abstract_mapper 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|