abstract_mapper 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +34 -0
- data/Gemfile +1 -3
- data/README.md +29 -13
- data/Rakefile +4 -7
- data/abstract_mapper.gemspec +3 -2
- data/config/metrics/STYLEGUIDE +14 -15
- data/lib/abstract_mapper.rb +4 -1
- data/lib/abstract_mapper/attributes.rb +65 -0
- data/lib/abstract_mapper/branch.rb +17 -7
- data/lib/abstract_mapper/builder.rb +7 -3
- data/lib/abstract_mapper/command.rb +68 -0
- data/lib/abstract_mapper/commands.rb +11 -31
- data/lib/abstract_mapper/errors/unknown_command.rb +1 -1
- data/lib/abstract_mapper/errors/wrong_node.rb +1 -1
- data/lib/abstract_mapper/errors/wrong_rule.rb +2 -2
- data/lib/abstract_mapper/functions.rb +32 -5
- data/lib/abstract_mapper/node.rb +21 -10
- data/lib/abstract_mapper/optimizer.rb +3 -3
- data/lib/abstract_mapper/pair_rule.rb +2 -4
- data/lib/abstract_mapper/rspec.rb +1 -2
- data/lib/abstract_mapper/rule.rb +23 -2
- data/lib/abstract_mapper/rules.rb +3 -10
- data/lib/abstract_mapper/settings.rb +3 -3
- data/lib/abstract_mapper/sole_rule.rb +2 -4
- data/lib/abstract_mapper/version.rb +1 -1
- data/lib/rspec/doubles.rb +16 -0
- data/lib/rspec/nodes.rb +29 -16
- data/lib/rspec/rules.rb +3 -3
- data/spec/integration/faceter.rb +16 -7
- data/spec/integration/mapper_definition_spec.rb +1 -1
- data/spec/integration/rspec_examples_spec.rb +4 -30
- data/spec/spec_helper.rb +1 -1
- data/spec/unit/abstract_mapper/branch_spec.rb +91 -14
- data/spec/unit/abstract_mapper/builder_spec.rb +66 -48
- data/spec/unit/abstract_mapper/command_spec.rb +92 -0
- data/spec/unit/abstract_mapper/commands_spec.rb +38 -79
- data/spec/unit/abstract_mapper/dsl_spec.rb +60 -55
- data/spec/unit/abstract_mapper/errors/wrong_rule_spec.rb +1 -1
- data/spec/unit/abstract_mapper/functions/compact_spec.rb +3 -3
- data/spec/unit/abstract_mapper/functions/filter_spec.rb +1 -1
- data/spec/unit/abstract_mapper/functions/identity_spec.rb +21 -0
- data/spec/unit/abstract_mapper/functions/restrict_spec.rb +16 -0
- data/spec/unit/abstract_mapper/functions/subclass_spec.rb +1 -1
- data/spec/unit/abstract_mapper/node_spec.rb +119 -48
- data/spec/unit/abstract_mapper/optimizer_spec.rb +38 -32
- data/spec/unit/abstract_mapper/pair_rule_spec.rb +4 -11
- data/spec/unit/abstract_mapper/rule_spec.rb +31 -15
- data/spec/unit/abstract_mapper/rules_spec.rb +2 -2
- data/spec/unit/abstract_mapper/settings_spec.rb +80 -73
- data/spec/unit/abstract_mapper/sole_rule_spec.rb +3 -10
- data/spec/unit/abstract_mapper_spec.rb +13 -5
- metadata +33 -12
- data/lib/rspec/functions.rb +0 -25
- data/lib/rspec/mapper.rb +0 -40
@@ -0,0 +1,68 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class AbstractMapper
|
4
|
+
|
5
|
+
# Describes the command of the mapper
|
6
|
+
#
|
7
|
+
# Every command has a name, a node that is added to the AST and the function,
|
8
|
+
# that converts command attributes into the node's.
|
9
|
+
#
|
10
|
+
# Method `#call` builds a correspodning node for the AST.
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
class Command
|
15
|
+
|
16
|
+
# @!attribute [r] name
|
17
|
+
#
|
18
|
+
# @return [Symbol] The name of the DSL command
|
19
|
+
#
|
20
|
+
attr_reader :name
|
21
|
+
|
22
|
+
# @!attribute [r] klass
|
23
|
+
#
|
24
|
+
# @return [Class] The class of the node to be created
|
25
|
+
#
|
26
|
+
attr_reader :klass
|
27
|
+
|
28
|
+
# @!attribute [r] converter
|
29
|
+
#
|
30
|
+
# @return [#call] The converter of the command's arguments into the node's
|
31
|
+
#
|
32
|
+
attr_reader :converter
|
33
|
+
|
34
|
+
# @!scope class
|
35
|
+
# @!method new(name, klass, converter)
|
36
|
+
# Creates the named command for node klass and arguments converter
|
37
|
+
#
|
38
|
+
# @param [#to_sym] name The name of the DSL command
|
39
|
+
# @param [Class] klass The klass of the node to be created
|
40
|
+
# @param [#call] converter The function that coerces attributes
|
41
|
+
#
|
42
|
+
# @return [AbstractMapper::Command]
|
43
|
+
|
44
|
+
# @private
|
45
|
+
def initialize(name, klass, converter = nil)
|
46
|
+
@name = name.to_sym
|
47
|
+
@klass = klass
|
48
|
+
@branch = Functions[:subclass?, Branch][klass]
|
49
|
+
@converter = converter || proc { |args = {}| args }
|
50
|
+
IceNine.deep_freeze(self)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Builds the AST node
|
54
|
+
#
|
55
|
+
# @param [Object, Array] args
|
56
|
+
# The argument of the command that should be converted to node attributes
|
57
|
+
# @param [Proc] block
|
58
|
+
#
|
59
|
+
# @return [AbstractMapper::Node]
|
60
|
+
#
|
61
|
+
def call(*args, &block)
|
62
|
+
block = nil if @branch
|
63
|
+
klass.new(converter.call(*args), &block)
|
64
|
+
end
|
65
|
+
|
66
|
+
end # class Command
|
67
|
+
|
68
|
+
end # class AbstractMapper
|
@@ -2,20 +2,12 @@
|
|
2
2
|
|
3
3
|
class AbstractMapper
|
4
4
|
|
5
|
-
#
|
5
|
+
# Collection of DSL commands used by the builder
|
6
6
|
#
|
7
7
|
# @api private
|
8
8
|
#
|
9
9
|
class Commands
|
10
10
|
|
11
|
-
# @!attribute [r] registry
|
12
|
-
#
|
13
|
-
# @return [Hash<Symbol => Class>]
|
14
|
-
# The registry of command names, pointing to types of the nodes,
|
15
|
-
# that should be built for adding to AST
|
16
|
-
#
|
17
|
-
attr_reader :registry
|
18
|
-
|
19
11
|
# @!scope class
|
20
12
|
# @!method new(registry = {})
|
21
13
|
# Creates an immutable collection of commands
|
@@ -26,44 +18,32 @@ class AbstractMapper
|
|
26
18
|
|
27
19
|
# @private
|
28
20
|
def initialize(registry = {})
|
29
|
-
@registry = registry.dup
|
30
|
-
|
21
|
+
@registry = registry.dup
|
22
|
+
IceNine.deep_freeze(self)
|
31
23
|
end
|
32
24
|
|
33
25
|
# Returns a new immutable registry with added command name and type
|
34
26
|
#
|
35
|
-
# @param [
|
27
|
+
# @param [[Symbol, Class, Proc]] other
|
36
28
|
#
|
37
29
|
# @return [undefined]
|
38
30
|
#
|
39
31
|
def <<(other)
|
40
|
-
|
41
|
-
|
42
|
-
self.class.new registry.merge(name.to_sym => node)
|
32
|
+
command = Command.new(*other)
|
33
|
+
self.class.new @registry.merge(command.name => command)
|
43
34
|
end
|
44
35
|
|
45
|
-
#
|
46
|
-
#
|
47
|
-
# Skips the block if a registered node is a branch
|
36
|
+
# Returns the registered command by name
|
48
37
|
#
|
49
38
|
# @param [#to_sym] name The name of the command
|
50
|
-
#
|
51
|
-
# @
|
52
|
-
# The block to be passed to the constructor of the simple node
|
39
|
+
#
|
40
|
+
# @return [AbstractMapper::Command]
|
53
41
|
#
|
54
42
|
# @raise [AbstractMapper::Errors::UnknownCommand]
|
55
43
|
# When unknown command is called
|
56
44
|
#
|
57
|
-
def [](name
|
58
|
-
|
59
|
-
branch = Functions[:subclass?, Branch][type]
|
60
|
-
branch ? type.new(*args) : type.new(*args, &block)
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
|
65
|
-
def get(name)
|
66
|
-
registry.fetch(name.to_sym) { fail(Errors::UnknownCommand.new name) }
|
45
|
+
def [](name)
|
46
|
+
@registry.fetch(name.to_sym) { fail(Errors::UnknownCommand.new name) }
|
67
47
|
end
|
68
48
|
|
69
49
|
end # class Commands
|
@@ -12,8 +12,8 @@ class AbstractMapper
|
|
12
12
|
|
13
13
|
# @private
|
14
14
|
def initialize(node)
|
15
|
-
super "#{node} is not a subclass of AbstractMapper::
|
16
|
-
|
15
|
+
super "#{node} is not a subclass of AbstractMapper::Rule"
|
16
|
+
IceNine.deep_freeze(self)
|
17
17
|
end
|
18
18
|
|
19
19
|
end # class WrongRule
|
@@ -10,7 +10,21 @@ class AbstractMapper
|
|
10
10
|
|
11
11
|
extend Transproc::Registry
|
12
12
|
|
13
|
-
|
13
|
+
import :map_array, from: Transproc::ArrayTransformations
|
14
|
+
|
15
|
+
# Returns the unchanged value whatever parameters are given
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# fn = Functions[:identity, :foo]
|
19
|
+
# fn[1] # => 1
|
20
|
+
#
|
21
|
+
# @param [Object] value
|
22
|
+
#
|
23
|
+
# @return [Object]
|
24
|
+
#
|
25
|
+
def self.identity(value, *)
|
26
|
+
value
|
27
|
+
end
|
14
28
|
|
15
29
|
# Applies the function to every element of array and removes empty values
|
16
30
|
#
|
@@ -24,8 +38,8 @@ class AbstractMapper
|
|
24
38
|
#
|
25
39
|
# @return [Array]
|
26
40
|
#
|
27
|
-
def filter(array, fn)
|
28
|
-
map_array
|
41
|
+
def self.filter(array, fn)
|
42
|
+
t(:map_array, fn)[array].compact.flatten
|
29
43
|
end
|
30
44
|
|
31
45
|
# Applies the function to every consecutive pair of array elements,
|
@@ -44,7 +58,7 @@ class AbstractMapper
|
|
44
58
|
#
|
45
59
|
# @return [Array]
|
46
60
|
#
|
47
|
-
def compact(array, fn)
|
61
|
+
def self.compact(array, fn)
|
48
62
|
array.each_with_object([]) do |i, a|
|
49
63
|
if a.empty?
|
50
64
|
a << i
|
@@ -67,10 +81,23 @@ class AbstractMapper
|
|
67
81
|
#
|
68
82
|
# @return [Boolean]
|
69
83
|
#
|
70
|
-
def subclass?(subling, ancestor)
|
84
|
+
def self.subclass?(subling, ancestor)
|
71
85
|
subling.ancestors.include?(ancestor)
|
72
86
|
end
|
73
87
|
|
88
|
+
# Restricts the hash by keys and values of the default one
|
89
|
+
#
|
90
|
+
# @param [Hash] hash
|
91
|
+
# @param [Hash] default_hash
|
92
|
+
#
|
93
|
+
# @return [Hash] <description>
|
94
|
+
#
|
95
|
+
def self.restrict(hash, default_hash)
|
96
|
+
keys = default_hash.keys
|
97
|
+
values = default_hash.merge(hash).values
|
98
|
+
Hash[keys.zip(values)]
|
99
|
+
end
|
100
|
+
|
74
101
|
end # module Functions
|
75
102
|
|
76
103
|
end # class AbstractMapper
|
data/lib/abstract_mapper/node.rb
CHANGED
@@ -17,11 +17,8 @@ class AbstractMapper
|
|
17
17
|
#
|
18
18
|
class Node
|
19
19
|
|
20
|
-
#
|
21
|
-
|
22
|
-
# @return [Array] The list of node-specific attributes
|
23
|
-
#
|
24
|
-
attr_reader :attributes
|
20
|
+
include Attributes # adds attributes and their DSL
|
21
|
+
include Comparable
|
25
22
|
|
26
23
|
# @!attribute [r] block
|
27
24
|
#
|
@@ -30,19 +27,21 @@ class AbstractMapper
|
|
30
27
|
attr_reader :block
|
31
28
|
|
32
29
|
# @private
|
33
|
-
def initialize(
|
34
|
-
|
30
|
+
def initialize(_ = {}, &block)
|
31
|
+
super
|
35
32
|
@block = block
|
36
|
-
|
33
|
+
IceNine.deep_freeze(self)
|
37
34
|
end
|
38
35
|
|
39
36
|
# @!method transproc
|
40
37
|
# The transformation function for the branch
|
41
38
|
#
|
39
|
+
# @return [Transproc::Function]
|
40
|
+
#
|
42
41
|
# @abstract
|
43
42
|
#
|
44
43
|
def transproc
|
45
|
-
|
44
|
+
Functions[:identity]
|
46
45
|
end
|
47
46
|
|
48
47
|
# Returns a human-readable string representating the node
|
@@ -61,6 +60,17 @@ class AbstractMapper
|
|
61
60
|
"#{__name__}#{__attributes__}"
|
62
61
|
end
|
63
62
|
|
63
|
+
# Compares the node to another one by type and attributes
|
64
|
+
#
|
65
|
+
# @param [Object] other
|
66
|
+
#
|
67
|
+
# @return [Boolean]
|
68
|
+
#
|
69
|
+
def ==(other)
|
70
|
+
other.instance_of?(self.class) && attributes.eql?(other.attributes)
|
71
|
+
end
|
72
|
+
alias_method :eql?, :==
|
73
|
+
|
64
74
|
private
|
65
75
|
|
66
76
|
def __name__
|
@@ -68,7 +78,8 @@ class AbstractMapper
|
|
68
78
|
end
|
69
79
|
|
70
80
|
def __attributes__
|
71
|
-
|
81
|
+
return if attributes.empty?
|
82
|
+
"(#{attributes.map { |k, v| "#{k}: #{v.inspect}" }.join(", ")})"
|
72
83
|
end
|
73
84
|
|
74
85
|
end # class Node
|
@@ -28,7 +28,7 @@ class AbstractMapper
|
|
28
28
|
# @private
|
29
29
|
def initialize(rules)
|
30
30
|
@rules = rules
|
31
|
-
|
31
|
+
IceNine.deep_freeze(self)
|
32
32
|
end
|
33
33
|
|
34
34
|
# Recursively optimizes the AST from root to leafs
|
@@ -39,8 +39,8 @@ class AbstractMapper
|
|
39
39
|
#
|
40
40
|
def update(tree)
|
41
41
|
return tree unless tree.is_a? Branch
|
42
|
-
new_tree = tree.
|
43
|
-
new_tree.
|
42
|
+
new_tree = tree.update { rules[tree] }
|
43
|
+
new_tree.update { new_tree.map(&method(:update)) }
|
44
44
|
end
|
45
45
|
|
46
46
|
end # class Optimizer
|
@@ -26,13 +26,11 @@ class AbstractMapper
|
|
26
26
|
#
|
27
27
|
class PairRule < Rule
|
28
28
|
|
29
|
-
#
|
30
|
-
#
|
31
|
-
# @return [Symbol]
|
32
|
-
#
|
29
|
+
# @private
|
33
30
|
def self.composer
|
34
31
|
:compact
|
35
32
|
end
|
33
|
+
private_class_method :composer
|
36
34
|
|
37
35
|
# @!attribute [r] node
|
38
36
|
#
|
data/lib/abstract_mapper/rule.rb
CHANGED
@@ -10,6 +10,12 @@ class AbstractMapper
|
|
10
10
|
#
|
11
11
|
class Rule
|
12
12
|
|
13
|
+
# @private
|
14
|
+
def self.composer
|
15
|
+
:identity
|
16
|
+
end
|
17
|
+
private_class_method :composer
|
18
|
+
|
13
19
|
# The transformation function that applies the rule to the array of nodes
|
14
20
|
#
|
15
21
|
# @return [Transproc::Function]
|
@@ -35,8 +41,23 @@ class AbstractMapper
|
|
35
41
|
|
36
42
|
# @private
|
37
43
|
def initialize(*nodes)
|
38
|
-
@nodes = nodes
|
39
|
-
|
44
|
+
@nodes = nodes
|
45
|
+
IceNine.deep_freeze(self)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Checks if optimization is needed for the node(s)
|
49
|
+
#
|
50
|
+
# @return [Boolean]
|
51
|
+
#
|
52
|
+
def optimize?
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the optimized node(s)
|
56
|
+
#
|
57
|
+
# @return [Object]
|
58
|
+
#
|
59
|
+
def optimize
|
60
|
+
nodes
|
40
61
|
end
|
41
62
|
|
42
63
|
# Returns the result of the rule applied to the initialized [#nodes]
|
@@ -24,9 +24,9 @@ class AbstractMapper
|
|
24
24
|
|
25
25
|
# @private
|
26
26
|
def initialize(registry = [])
|
27
|
-
@registry = registry.dup
|
28
|
-
@transproc =
|
29
|
-
|
27
|
+
@registry = registry.dup
|
28
|
+
@transproc = registry.map(&:transproc).inject(:>>)
|
29
|
+
IceNine.deep_freeze(self)
|
30
30
|
end
|
31
31
|
|
32
32
|
# Returns the copy of current registry with the new rule added
|
@@ -49,13 +49,6 @@ class AbstractMapper
|
|
49
49
|
@transproc ? @transproc[nodes] : nodes
|
50
50
|
end
|
51
51
|
|
52
|
-
private
|
53
|
-
|
54
|
-
# Calls all sole rules first
|
55
|
-
def ordered
|
56
|
-
registry.sort_by { |rule| Functions[:subclass?, PairRule][rule].to_s }
|
57
|
-
end
|
58
|
-
|
59
52
|
end # class Rules
|
60
53
|
|
61
54
|
end # class AbstractMapper
|
@@ -52,7 +52,7 @@ class AbstractMapper
|
|
52
52
|
__configure__(&block)
|
53
53
|
__set_builder__
|
54
54
|
__set_optimizer__
|
55
|
-
|
55
|
+
IceNine.deep_freeze(self)
|
56
56
|
end
|
57
57
|
|
58
58
|
private
|
@@ -63,10 +63,10 @@ class AbstractMapper
|
|
63
63
|
@rules = rules << value
|
64
64
|
end
|
65
65
|
|
66
|
-
def command(name, node)
|
66
|
+
def command(name, node, &block)
|
67
67
|
fn = Functions[:subclass?, Node]
|
68
68
|
fail Errors::WrongNode.new(node) unless fn[node]
|
69
|
-
@commands = commands << [name, node]
|
69
|
+
@commands = commands << [name, node, block]
|
70
70
|
end
|
71
71
|
|
72
72
|
def __set_rules__
|
@@ -25,13 +25,11 @@ class AbstractMapper
|
|
25
25
|
#
|
26
26
|
class SoleRule < Rule
|
27
27
|
|
28
|
-
#
|
29
|
-
#
|
30
|
-
# @return [Symbol]
|
31
|
-
#
|
28
|
+
# @private
|
32
29
|
def self.composer
|
33
30
|
:filter
|
34
31
|
end
|
32
|
+
private_class_method :composer
|
35
33
|
|
36
34
|
# @!attribute [r] node
|
37
35
|
#
|