abstract_mapper 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
#
|