abstract_mapper 0.0.2 → 0.1.0
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 +0 -4
- data/CHANGELOG.md +26 -0
- data/README.md +5 -5
- data/Rakefile +1 -1
- data/abstract_mapper.gemspec +1 -0
- data/lib/abstract_mapper.rb +4 -15
- data/lib/abstract_mapper/ast.rb +16 -0
- data/lib/abstract_mapper/ast/branch.rb +121 -0
- data/lib/abstract_mapper/ast/node.rb +91 -0
- data/lib/abstract_mapper/builder.rb +2 -2
- data/lib/abstract_mapper/commands.rb +4 -2
- data/lib/abstract_mapper/commands/base.rb +69 -0
- data/lib/abstract_mapper/dsl.rb +2 -2
- data/lib/abstract_mapper/errors.rb +17 -0
- data/lib/abstract_mapper/errors/wrong_node.rb +1 -1
- data/lib/abstract_mapper/errors/wrong_rule.rb +1 -1
- data/lib/abstract_mapper/optimizer.rb +1 -1
- data/lib/abstract_mapper/rules.rb +10 -5
- data/lib/abstract_mapper/rules/base.rb +74 -0
- data/lib/abstract_mapper/rules/pair.rb +68 -0
- data/lib/abstract_mapper/rules/sole.rb +60 -0
- data/lib/abstract_mapper/settings.rb +22 -30
- data/lib/abstract_mapper/version.rb +1 -1
- data/lib/rspec/nodes.rb +2 -2
- data/spec/integration/faceter.rb +4 -4
- data/spec/integration/rspec_examples_spec.rb +0 -6
- data/spec/unit/abstract_mapper/{branch_spec.rb → ast/branch_spec.rb} +28 -61
- data/spec/unit/abstract_mapper/{node_spec.rb → ast/node_spec.rb} +16 -53
- data/spec/unit/abstract_mapper/builder_spec.rb +9 -24
- data/spec/unit/abstract_mapper/{command_spec.rb → commands/base_spec.rb} +10 -25
- data/spec/unit/abstract_mapper/commands_spec.rb +9 -14
- data/spec/unit/abstract_mapper/dsl_spec.rb +23 -15
- data/spec/unit/abstract_mapper/errors/unknown_command_spec.rb +1 -4
- data/spec/unit/abstract_mapper/errors/wrong_node_spec.rb +5 -4
- data/spec/unit/abstract_mapper/errors/wrong_rule_spec.rb +5 -5
- data/spec/unit/abstract_mapper/functions/compact_spec.rb +0 -2
- data/spec/unit/abstract_mapper/functions/filter_spec.rb +0 -2
- data/spec/unit/abstract_mapper/functions/identity_spec.rb +0 -2
- data/spec/unit/abstract_mapper/functions/restrict_spec.rb +0 -3
- data/spec/unit/abstract_mapper/functions/subclass_spec.rb +0 -2
- data/spec/unit/abstract_mapper/optimizer_spec.rb +13 -17
- data/spec/unit/abstract_mapper/{rule_spec.rb → rules/base_spec.rb} +17 -34
- data/spec/unit/abstract_mapper/{pair_rule_spec.rb → rules/pair_spec.rb} +8 -8
- data/spec/unit/abstract_mapper/{sole_rule_spec.rb → rules/sole_spec.rb} +5 -5
- data/spec/unit/abstract_mapper/rules_spec.rb +24 -37
- data/spec/unit/abstract_mapper/settings_spec.rb +38 -32
- data/spec/unit/abstract_mapper_spec.rb +9 -16
- metadata +37 -22
- data/lib/abstract_mapper/attributes.rb +0 -65
- data/lib/abstract_mapper/branch.rb +0 -120
- data/lib/abstract_mapper/command.rb +0 -68
- data/lib/abstract_mapper/node.rb +0 -87
- data/lib/abstract_mapper/pair_rule.rb +0 -64
- data/lib/abstract_mapper/rule.rb +0 -73
- data/lib/abstract_mapper/sole_rule.rb +0 -56
data/lib/abstract_mapper/dsl.rb
CHANGED
@@ -31,7 +31,7 @@ class AbstractMapper
|
|
31
31
|
# @yield a block
|
32
32
|
#
|
33
33
|
def configure(&block)
|
34
|
-
@settings = Settings.new(&block)
|
34
|
+
@settings = settings ? settings.update(&block) : Settings.new(&block)
|
35
35
|
self
|
36
36
|
end
|
37
37
|
|
@@ -46,7 +46,7 @@ class AbstractMapper
|
|
46
46
|
private # DSL commands
|
47
47
|
|
48
48
|
def tree
|
49
|
-
@tree ||= Branch.new
|
49
|
+
@tree ||= AST::Branch.new
|
50
50
|
end
|
51
51
|
|
52
52
|
def respond_to_missing?(*)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class AbstractMapper
|
4
|
+
|
5
|
+
# The namespace for the gem-specific exceptions
|
6
|
+
#
|
7
|
+
# @author Andrew Kozin <Andrew.Kozin@gmail.com>
|
8
|
+
#
|
9
|
+
module Errors
|
10
|
+
|
11
|
+
require_relative "errors/unknown_command"
|
12
|
+
require_relative "errors/wrong_node"
|
13
|
+
require_relative "errors/wrong_rule"
|
14
|
+
|
15
|
+
end # module Errors
|
16
|
+
|
17
|
+
end # class AbstractMapper
|
@@ -38,7 +38,7 @@ class AbstractMapper
|
|
38
38
|
# @return [AbstractMapper::Branch]
|
39
39
|
#
|
40
40
|
def update(tree)
|
41
|
-
return tree unless tree.is_a? Branch
|
41
|
+
return tree unless tree.is_a? AST::Branch
|
42
42
|
new_tree = tree.update { rules[tree] }
|
43
43
|
new_tree.update { new_tree.map(&method(:update)) }
|
44
44
|
end
|
@@ -2,6 +2,10 @@
|
|
2
2
|
|
3
3
|
class AbstractMapper
|
4
4
|
|
5
|
+
require_relative "rules/base"
|
6
|
+
require_relative "rules/sole"
|
7
|
+
require_relative "rules/pair"
|
8
|
+
|
5
9
|
# Registry of DSL rules applicable to nodes of AST
|
6
10
|
#
|
7
11
|
# @api private
|
@@ -10,7 +14,8 @@ class AbstractMapper
|
|
10
14
|
|
11
15
|
# @!attribute [r] registry
|
12
16
|
#
|
13
|
-
# @return [Array<AbstractMapper::
|
17
|
+
# @return [Array<AbstractMapper::Rules::Base>]
|
18
|
+
# the list of rules applicable to AST
|
14
19
|
#
|
15
20
|
attr_reader :registry
|
16
21
|
|
@@ -18,7 +23,7 @@ class AbstractMapper
|
|
18
23
|
# @!method new(registry)
|
19
24
|
# Creates a registry with array of rules
|
20
25
|
#
|
21
|
-
# @param [Array<AbstractMapper::
|
26
|
+
# @param [Array<AbstractMapper::Rules::Base>] registry Array of rules
|
22
27
|
#
|
23
28
|
# @return [AbstractMapper::Rules]
|
24
29
|
|
@@ -31,7 +36,7 @@ class AbstractMapper
|
|
31
36
|
|
32
37
|
# Returns the copy of current registry with the new rule added
|
33
38
|
#
|
34
|
-
# @param [AbstractMapper::
|
39
|
+
# @param [AbstractMapper::Rules::Base] other
|
35
40
|
#
|
36
41
|
# @return (see #new)
|
37
42
|
#
|
@@ -41,9 +46,9 @@ class AbstractMapper
|
|
41
46
|
|
42
47
|
# Applies all the registered rules to the array of nodes
|
43
48
|
#
|
44
|
-
# @param [Array<AbstractMapper::Node>] nodes
|
49
|
+
# @param [Array<AbstractMapper::AST::Node>] nodes
|
45
50
|
#
|
46
|
-
# @return [Array<AbstractMapper::Node>] The optimized array of nodes
|
51
|
+
# @return [Array<AbstractMapper::AST::Node>] The optimized array of nodes
|
47
52
|
#
|
48
53
|
def [](nodes)
|
49
54
|
@transproc ? @transproc[nodes] : nodes
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class AbstractMapper
|
4
|
+
|
5
|
+
class Rules
|
6
|
+
|
7
|
+
# Base class for optimization rules
|
8
|
+
#
|
9
|
+
# @abstract
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
#
|
13
|
+
class Base
|
14
|
+
|
15
|
+
# @private
|
16
|
+
def self.composer
|
17
|
+
:identity
|
18
|
+
end
|
19
|
+
private_class_method :composer
|
20
|
+
|
21
|
+
# The transformation function that applies the rule to the array of nodes
|
22
|
+
#
|
23
|
+
# @return [Transproc::Function]
|
24
|
+
#
|
25
|
+
def self.transproc
|
26
|
+
Functions[composer, proc { |*nodes| new(*nodes).call }]
|
27
|
+
end
|
28
|
+
|
29
|
+
# @!attribute [r] nodes
|
30
|
+
#
|
31
|
+
# @return [Array<AbstractMapper::AST::Node>]
|
32
|
+
# Either one or two nodes to be optimized
|
33
|
+
#
|
34
|
+
attr_reader :nodes
|
35
|
+
|
36
|
+
# Initializes the rule for a sole node, or a pair of consecutive nodes
|
37
|
+
#
|
38
|
+
# @param [Array<AbstractMapper::AST::Node>] nodes
|
39
|
+
#
|
40
|
+
# @return [AbstractMapper::Rules::Base]
|
41
|
+
#
|
42
|
+
def initialize(*nodes)
|
43
|
+
@nodes = nodes
|
44
|
+
IceNine.deep_freeze(self)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Checks if optimization is needed for the node(s)
|
48
|
+
#
|
49
|
+
# @return [Boolean]
|
50
|
+
#
|
51
|
+
def optimize?
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the optimized node(s)
|
55
|
+
#
|
56
|
+
# @return [Object]
|
57
|
+
#
|
58
|
+
def optimize
|
59
|
+
nodes
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns the result of the rule applied to the initialized [#nodes]
|
63
|
+
#
|
64
|
+
# @return [Array<AbstractMapper::AST::Node>]
|
65
|
+
#
|
66
|
+
def call
|
67
|
+
optimize? ? [optimize].flatten.compact : nodes
|
68
|
+
end
|
69
|
+
|
70
|
+
end # class Base
|
71
|
+
|
72
|
+
end # class Rules
|
73
|
+
|
74
|
+
end # class AbstractMapper
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class AbstractMapper
|
4
|
+
|
5
|
+
class Rules
|
6
|
+
|
7
|
+
# The base class for rules to be applied to pairs of nodes.
|
8
|
+
#
|
9
|
+
# The subclass should implement two instance methods:
|
10
|
+
#
|
11
|
+
# * `#optimize?` to check if the optimization should be applied to the nodes
|
12
|
+
# * `#optimize` that should return the merged node
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# class MergeConsecutiveLists < AbstractMapper::Rules::Pair
|
16
|
+
# def optimize?
|
17
|
+
# left.is_a?(List) & right.is_a?(List)
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# def optimize
|
21
|
+
# List.new { left.entries + right.entries }
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# @abstract
|
26
|
+
#
|
27
|
+
# @api public
|
28
|
+
#
|
29
|
+
class Pair < Base
|
30
|
+
|
31
|
+
# @private
|
32
|
+
def self.composer
|
33
|
+
:compact
|
34
|
+
end
|
35
|
+
private_class_method :composer
|
36
|
+
|
37
|
+
# @!attribute [r] node
|
38
|
+
#
|
39
|
+
# @return [AbstractMapper::AST::Node] The left node in the pair
|
40
|
+
#
|
41
|
+
attr_reader :left
|
42
|
+
|
43
|
+
# @!attribute [r] node
|
44
|
+
#
|
45
|
+
# @return [AbstractMapper::AST::Node] The right node in the pair
|
46
|
+
#
|
47
|
+
attr_reader :right
|
48
|
+
|
49
|
+
# @!scope class
|
50
|
+
# @!method new(node)
|
51
|
+
# Creates a rule applied to the sole node
|
52
|
+
#
|
53
|
+
# @param [AbstractMapper::AST::Node] node
|
54
|
+
#
|
55
|
+
# @return [AbstractMapper::Rules::Base]
|
56
|
+
|
57
|
+
# @private
|
58
|
+
def initialize(left, right)
|
59
|
+
@left = left
|
60
|
+
@right = right
|
61
|
+
super
|
62
|
+
end
|
63
|
+
|
64
|
+
end # class Pair
|
65
|
+
|
66
|
+
end # class Rule
|
67
|
+
|
68
|
+
end # class AbstractMapper
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class AbstractMapper
|
4
|
+
|
5
|
+
class Rules
|
6
|
+
|
7
|
+
# The abstract class that describes the rule to be applied to sole nodes.
|
8
|
+
#
|
9
|
+
# The subclass should implement two instance methods:
|
10
|
+
#
|
11
|
+
# * `#optimize?` to check if the optimization should be applied to the node
|
12
|
+
# * `#optimize` that should return the optimized node
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# class RemoveEmptyList < AbstractMapper::Rules::Sole
|
16
|
+
# def optimize?
|
17
|
+
# node.is_a?(List) && node.empty?
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# def optimize
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# @abstract
|
25
|
+
#
|
26
|
+
# @api public
|
27
|
+
#
|
28
|
+
class Sole < Base
|
29
|
+
|
30
|
+
# @private
|
31
|
+
def self.composer
|
32
|
+
:filter
|
33
|
+
end
|
34
|
+
private_class_method :composer
|
35
|
+
|
36
|
+
# @!attribute [r] node
|
37
|
+
#
|
38
|
+
# @return [AbstractMapper::AST::Node] The node to be optimized
|
39
|
+
#
|
40
|
+
attr_reader :node
|
41
|
+
|
42
|
+
# @!scope class
|
43
|
+
# @!method new(node)
|
44
|
+
# Creates a rule applied to the sole node
|
45
|
+
#
|
46
|
+
# @param [AbstractMapper::AST::Node] node
|
47
|
+
#
|
48
|
+
# @return [AbstractMapper::Rules::Base]
|
49
|
+
|
50
|
+
# @private
|
51
|
+
def initialize(node)
|
52
|
+
@node = node
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
end # class Sole
|
57
|
+
|
58
|
+
end # class Rule
|
59
|
+
|
60
|
+
end # class AbstractMapper
|
@@ -36,57 +36,49 @@ class AbstractMapper
|
|
36
36
|
#
|
37
37
|
attr_reader :builder
|
38
38
|
|
39
|
-
#
|
40
|
-
#
|
41
|
-
# Creates a domain-specific settings with commands and rules initialized
|
42
|
-
# from inside a block
|
39
|
+
# Initializes a domain-specific settings with commands and rules
|
40
|
+
# being set in the block.
|
43
41
|
#
|
44
42
|
# @param [Proc] block
|
45
43
|
#
|
46
|
-
# @
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
__configure__(&block)
|
53
|
-
__set_builder__
|
54
|
-
__set_optimizer__
|
44
|
+
# @yield the block with settings for commands and rules
|
45
|
+
#
|
46
|
+
def initialize(rules = Rules.new, commands = Commands.new, &block)
|
47
|
+
@rules = rules
|
48
|
+
@commands = commands
|
49
|
+
configure(&block)
|
55
50
|
IceNine.deep_freeze(self)
|
56
51
|
end
|
57
52
|
|
53
|
+
# Returns a new class with rules and commands added from the block
|
54
|
+
# to the existing ones
|
55
|
+
#
|
56
|
+
# @return [AbstractMapper::Settings]
|
57
|
+
#
|
58
|
+
# @yield the block with settings for commands and rules
|
59
|
+
#
|
60
|
+
def update(&block)
|
61
|
+
self.class.new(rules, commands, &block)
|
62
|
+
end
|
63
|
+
|
58
64
|
private
|
59
65
|
|
60
66
|
def rule(value)
|
61
|
-
fn = Functions[:subclass?,
|
67
|
+
fn = Functions[:subclass?, Rules::Base]
|
62
68
|
fail Errors::WrongRule.new(value) unless fn[value]
|
63
69
|
@rules = rules << value
|
64
70
|
end
|
65
71
|
|
66
72
|
def command(name, node, &block)
|
67
|
-
fn = Functions[:subclass?, Node]
|
73
|
+
fn = Functions[:subclass?, AST::Node]
|
68
74
|
fail Errors::WrongNode.new(node) unless fn[node]
|
69
75
|
@commands = commands << [name, node, block]
|
70
76
|
end
|
71
77
|
|
72
|
-
def
|
73
|
-
@rules = Rules.new
|
74
|
-
end
|
75
|
-
|
76
|
-
def __set_commands__
|
77
|
-
@commands = Commands.new
|
78
|
-
end
|
79
|
-
|
80
|
-
def __configure__(&block)
|
78
|
+
def configure(&block)
|
81
79
|
instance_eval(&block) if block_given?
|
82
|
-
end
|
83
|
-
|
84
|
-
def __set_builder__
|
85
80
|
@builder = Class.new(Builder)
|
86
81
|
builder.commands = commands
|
87
|
-
end
|
88
|
-
|
89
|
-
def __set_optimizer__
|
90
82
|
@optimizer = Optimizer.new(rules)
|
91
83
|
end
|
92
84
|
|
data/lib/rspec/nodes.rb
CHANGED
@@ -20,7 +20,7 @@ shared_examples :creating_immutable_node do
|
|
20
20
|
|
21
21
|
subject { node }
|
22
22
|
|
23
|
-
it { is_expected.to be_kind_of AbstractMapper::Node }
|
23
|
+
it { is_expected.to be_kind_of AbstractMapper::AST::Node }
|
24
24
|
it { is_expected.to be_frozen, "expected #{subject.inspect} to be immutable" }
|
25
25
|
|
26
26
|
end # shared examples
|
@@ -31,7 +31,7 @@ shared_examples :creating_immutable_branch do
|
|
31
31
|
|
32
32
|
subject { node }
|
33
33
|
|
34
|
-
it { is_expected.to be_kind_of AbstractMapper::Branch }
|
34
|
+
it { is_expected.to be_kind_of AbstractMapper::AST::Branch }
|
35
35
|
it { is_expected.to be_frozen, "expected #{subject.inspect} to be immutable" }
|
36
36
|
|
37
37
|
end # shared examples
|
data/spec/integration/faceter.rb
CHANGED
@@ -14,13 +14,13 @@ shared_context "Faceter" do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
# Nodes
|
17
|
-
class List < AbstractMapper::Branch
|
17
|
+
class List < AbstractMapper::AST::Branch
|
18
18
|
def transproc
|
19
19
|
Functions[:map_array, super]
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
class Rename < AbstractMapper::Node
|
23
|
+
class Rename < AbstractMapper::AST::Node
|
24
24
|
attribute :keys
|
25
25
|
|
26
26
|
def transproc
|
@@ -29,7 +29,7 @@ shared_context "Faceter" do
|
|
29
29
|
end
|
30
30
|
|
31
31
|
# Rules
|
32
|
-
class CompactLists < AbstractMapper::
|
32
|
+
class CompactLists < AbstractMapper::Rules::Pair
|
33
33
|
def optimize?
|
34
34
|
left.is_a?(List) && right.is_a?(List)
|
35
35
|
end
|
@@ -39,7 +39,7 @@ shared_context "Faceter" do
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
class CompactRenames < AbstractMapper::
|
42
|
+
class CompactRenames < AbstractMapper::Rules::Pair
|
43
43
|
def optimize?
|
44
44
|
left.is_a?(Rename) && right.is_a?(Rename)
|
45
45
|
end
|