abstract_mapper 0.0.1
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 +7 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +9 -0
- data/.metrics +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +2 -0
- data/.travis.yml +19 -0
- data/.yardopts +3 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +9 -0
- data/Guardfile +14 -0
- data/LICENSE +21 -0
- data/README.md +239 -0
- data/Rakefile +34 -0
- data/abstract_mapper.gemspec +26 -0
- data/config/metrics/STYLEGUIDE +230 -0
- data/config/metrics/cane.yml +5 -0
- data/config/metrics/churn.yml +6 -0
- data/config/metrics/flay.yml +2 -0
- data/config/metrics/metric_fu.yml +15 -0
- data/config/metrics/reek.yml +1 -0
- data/config/metrics/roodi.yml +24 -0
- data/config/metrics/rubocop.yml +76 -0
- data/config/metrics/saikuro.yml +3 -0
- data/config/metrics/simplecov.yml +6 -0
- data/config/metrics/yardstick.yml +37 -0
- data/lib/abstract_mapper/branch.rb +110 -0
- data/lib/abstract_mapper/builder.rb +84 -0
- data/lib/abstract_mapper/commands.rb +71 -0
- data/lib/abstract_mapper/dsl.rb +62 -0
- data/lib/abstract_mapper/errors/unknown_command.rb +25 -0
- data/lib/abstract_mapper/errors/wrong_node.rb +23 -0
- data/lib/abstract_mapper/errors/wrong_rule.rb +23 -0
- data/lib/abstract_mapper/functions.rb +76 -0
- data/lib/abstract_mapper/node.rb +76 -0
- data/lib/abstract_mapper/optimizer.rb +48 -0
- data/lib/abstract_mapper/pair_rule.rb +66 -0
- data/lib/abstract_mapper/rspec.rb +7 -0
- data/lib/abstract_mapper/rule.rb +52 -0
- data/lib/abstract_mapper/rules.rb +61 -0
- data/lib/abstract_mapper/settings.rb +95 -0
- data/lib/abstract_mapper/sole_rule.rb +58 -0
- data/lib/abstract_mapper/version.rb +9 -0
- data/lib/abstract_mapper.rb +82 -0
- data/lib/rspec/functions.rb +25 -0
- data/lib/rspec/mapper.rb +40 -0
- data/lib/rspec/nodes.rb +58 -0
- data/lib/rspec/rules.rb +59 -0
- data/spec/integration/faceter.rb +62 -0
- data/spec/integration/mapper_definition_spec.rb +33 -0
- data/spec/integration/rspec_examples_spec.rb +77 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/unit/abstract_mapper/branch_spec.rb +126 -0
- data/spec/unit/abstract_mapper/builder_spec.rb +78 -0
- data/spec/unit/abstract_mapper/commands_spec.rb +105 -0
- data/spec/unit/abstract_mapper/dsl_spec.rb +82 -0
- data/spec/unit/abstract_mapper/errors/unknown_command_spec.rb +21 -0
- data/spec/unit/abstract_mapper/errors/wrong_node_spec.rb +21 -0
- data/spec/unit/abstract_mapper/errors/wrong_rule_spec.rb +23 -0
- data/spec/unit/abstract_mapper/functions/compact_spec.rb +25 -0
- data/spec/unit/abstract_mapper/functions/filter_spec.rb +26 -0
- data/spec/unit/abstract_mapper/functions/subclass_spec.rb +66 -0
- data/spec/unit/abstract_mapper/node_spec.rb +84 -0
- data/spec/unit/abstract_mapper/optimizer_spec.rb +48 -0
- data/spec/unit/abstract_mapper/pair_rule_spec.rb +59 -0
- data/spec/unit/abstract_mapper/rule_spec.rb +83 -0
- data/spec/unit/abstract_mapper/rules_spec.rb +88 -0
- data/spec/unit/abstract_mapper/settings_spec.rb +113 -0
- data/spec/unit/abstract_mapper/sole_rule_spec.rb +51 -0
- data/spec/unit/abstract_mapper_spec.rb +36 -0
- metadata +171 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class AbstractMapper
|
4
|
+
|
5
|
+
# Optimizes the immutable abstract syntax tree (AST) using comfigurable
|
6
|
+
# collection of applicable rules.
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
#
|
10
|
+
class Optimizer
|
11
|
+
|
12
|
+
# @!attribute [r] rules
|
13
|
+
#
|
14
|
+
# @return [AbstractMapper::Rules]
|
15
|
+
# The collection of applicable optimization rules
|
16
|
+
#
|
17
|
+
attr_reader :rules
|
18
|
+
|
19
|
+
# @!scope class
|
20
|
+
# @!method new(rules = Rules.new)
|
21
|
+
# Creates the optimizer
|
22
|
+
#
|
23
|
+
# @param [AbstractMapper::Rules] rules
|
24
|
+
# The collection of applicable optimization rules
|
25
|
+
#
|
26
|
+
# @return [AbstractMapper::Optimizer]
|
27
|
+
|
28
|
+
# @private
|
29
|
+
def initialize(rules)
|
30
|
+
@rules = rules
|
31
|
+
freeze
|
32
|
+
end
|
33
|
+
|
34
|
+
# Recursively optimizes the AST from root to leafs
|
35
|
+
#
|
36
|
+
# @param [AbstractMapper::Branch] tree
|
37
|
+
#
|
38
|
+
# @return [AbstractMapper::Branch]
|
39
|
+
#
|
40
|
+
def update(tree)
|
41
|
+
return tree unless tree.is_a? Branch
|
42
|
+
new_tree = tree.rebuild { rules[tree] }
|
43
|
+
new_tree.rebuild { new_tree.map(&method(:update)) }
|
44
|
+
end
|
45
|
+
|
46
|
+
end # class Optimizer
|
47
|
+
|
48
|
+
end # class AbstractMapper
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class AbstractMapper
|
4
|
+
|
5
|
+
# The base class for rules to be applied to pairs of nodes.
|
6
|
+
#
|
7
|
+
# The subclass should implement two instance methods:
|
8
|
+
#
|
9
|
+
# * `#optimize?` to check if the optimization should be applied to the nodes
|
10
|
+
# * `#optimize` that should return the merged node
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# class MergeConsecutiveLists < AbstractMapper::PairRule
|
14
|
+
# def optimize?
|
15
|
+
# left.is_a?(List) & right.is_a?(List)
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# def optimize
|
19
|
+
# List.new { left.entries + right.entries }
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# @abstract
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
#
|
27
|
+
class PairRule < Rule
|
28
|
+
|
29
|
+
# Returns the name of transproc used to compose rules
|
30
|
+
#
|
31
|
+
# @return [Symbol]
|
32
|
+
#
|
33
|
+
def self.composer
|
34
|
+
:compact
|
35
|
+
end
|
36
|
+
|
37
|
+
# @!attribute [r] node
|
38
|
+
#
|
39
|
+
# @return [AbstractMapper::Node] The left node in the pair to be optimized
|
40
|
+
#
|
41
|
+
attr_reader :left
|
42
|
+
|
43
|
+
# @!attribute [r] node
|
44
|
+
#
|
45
|
+
# @return [AbstractMapper::Node] The right node in the pair to be optimized
|
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::Node] node
|
54
|
+
#
|
55
|
+
# @return [AbstractMapper::Rule]
|
56
|
+
|
57
|
+
# @private
|
58
|
+
def initialize(left, right)
|
59
|
+
@left = left
|
60
|
+
@right = right
|
61
|
+
super
|
62
|
+
end
|
63
|
+
|
64
|
+
end # class PairRule
|
65
|
+
|
66
|
+
end # class AbstractMapper
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class AbstractMapper
|
4
|
+
|
5
|
+
# Base class for nodes optimization rules
|
6
|
+
#
|
7
|
+
# @abstract
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
#
|
11
|
+
class Rule
|
12
|
+
|
13
|
+
# The transformation function that applies the rule to the array of nodes
|
14
|
+
#
|
15
|
+
# @return [Transproc::Function]
|
16
|
+
#
|
17
|
+
def self.transproc
|
18
|
+
Functions[composer, proc { |*nodes| new(*nodes).call }]
|
19
|
+
end
|
20
|
+
|
21
|
+
# @!attribute [r] nodes
|
22
|
+
#
|
23
|
+
# @return [Array<AbstractMapper::Node>]
|
24
|
+
# Either one or two nodes to be optimized
|
25
|
+
#
|
26
|
+
attr_reader :nodes
|
27
|
+
|
28
|
+
# @!scope class
|
29
|
+
# @!method new(*nodes)
|
30
|
+
# Creates the rule for a sole node, or a pair of consecutive nodes
|
31
|
+
#
|
32
|
+
# @param [Array<AbstractMapper::Node>] nodes
|
33
|
+
#
|
34
|
+
# @return [AbstractMapper::Rule]
|
35
|
+
|
36
|
+
# @private
|
37
|
+
def initialize(*nodes)
|
38
|
+
@nodes = nodes.freeze
|
39
|
+
freeze
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns the result of the rule applied to the initialized [#nodes]
|
43
|
+
#
|
44
|
+
# @return [Array<AbstractMapper::Node>]
|
45
|
+
#
|
46
|
+
def call
|
47
|
+
optimize? ? [optimize].flatten.compact : nodes
|
48
|
+
end
|
49
|
+
|
50
|
+
end # class Rule
|
51
|
+
|
52
|
+
end # class AbstractMapper
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class AbstractMapper
|
4
|
+
|
5
|
+
# Registry of DSL rules applicable to nodes of AST
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
#
|
9
|
+
class Rules
|
10
|
+
|
11
|
+
# @!attribute [r] registry
|
12
|
+
#
|
13
|
+
# @return [Array<AbstractMapper::SoleRule>] list of rules applicable to AST
|
14
|
+
#
|
15
|
+
attr_reader :registry
|
16
|
+
|
17
|
+
# @!scope class
|
18
|
+
# @!method new(registry)
|
19
|
+
# Creates a registry with array of rules
|
20
|
+
#
|
21
|
+
# @param [Array<AbstractMapper::SoleRule>] registry Array of rules
|
22
|
+
#
|
23
|
+
# @return [AbstractMapper::Rules]
|
24
|
+
|
25
|
+
# @private
|
26
|
+
def initialize(registry = [])
|
27
|
+
@registry = registry.dup.freeze
|
28
|
+
@transproc = ordered.map(&:transproc).inject(:>>)
|
29
|
+
freeze
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the copy of current registry with the new rule added
|
33
|
+
#
|
34
|
+
# @param [AbstractMapper::SoleRule] other
|
35
|
+
#
|
36
|
+
# @return (see #new)
|
37
|
+
#
|
38
|
+
def <<(other)
|
39
|
+
self.class.new(registry + [other])
|
40
|
+
end
|
41
|
+
|
42
|
+
# Applies all the registered rules to the array of nodes
|
43
|
+
#
|
44
|
+
# @param [Array<AbstractMapper::Node>] nodes
|
45
|
+
#
|
46
|
+
# @return [Array<AbstractMapper::Node>] The optimized array of nodes
|
47
|
+
#
|
48
|
+
def [](nodes)
|
49
|
+
@transproc ? @transproc[nodes] : nodes
|
50
|
+
end
|
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
|
+
end # class Rules
|
60
|
+
|
61
|
+
end # class AbstractMapper
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class AbstractMapper
|
4
|
+
|
5
|
+
# The configurable container of domain-specific DSL commands and rules
|
6
|
+
# along with corresponding AST builder and optimizer
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
#
|
10
|
+
class Settings
|
11
|
+
|
12
|
+
# @!attribute [r] commands
|
13
|
+
#
|
14
|
+
# @return [AbstractMapper::Commands]
|
15
|
+
# The collection of registered DSL commands
|
16
|
+
#
|
17
|
+
attr_reader :commands
|
18
|
+
|
19
|
+
# @!attribute [r] rules
|
20
|
+
#
|
21
|
+
# @return [AbstractMapper::Rules]
|
22
|
+
# The collection of registered optimization rules
|
23
|
+
#
|
24
|
+
attr_reader :rules
|
25
|
+
|
26
|
+
# @!attribute [r] optimizer
|
27
|
+
#
|
28
|
+
# @return [AbstractMapper::Optimizer]
|
29
|
+
# The optimizer with domain-specific rules
|
30
|
+
#
|
31
|
+
attr_reader :optimizer
|
32
|
+
|
33
|
+
# @!attribute [r] builder
|
34
|
+
#
|
35
|
+
# @return [Class] The builder class with domain-specific commands
|
36
|
+
#
|
37
|
+
attr_reader :builder
|
38
|
+
|
39
|
+
# @!scope class
|
40
|
+
# @!method new(&block)
|
41
|
+
# Creates a domain-specific settings with commands and rules initialized
|
42
|
+
# from inside a block
|
43
|
+
#
|
44
|
+
# @param [Proc] block
|
45
|
+
#
|
46
|
+
# @return [AbstractMapper::Settings]
|
47
|
+
|
48
|
+
# @private
|
49
|
+
def initialize(&block)
|
50
|
+
__set_rules__
|
51
|
+
__set_commands__
|
52
|
+
__configure__(&block)
|
53
|
+
__set_builder__
|
54
|
+
__set_optimizer__
|
55
|
+
freeze
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def rule(value)
|
61
|
+
fn = Functions[:subclass?, Rule]
|
62
|
+
fail Errors::WrongRule.new(value) unless fn[value]
|
63
|
+
@rules = rules << value
|
64
|
+
end
|
65
|
+
|
66
|
+
def command(name, node)
|
67
|
+
fn = Functions[:subclass?, Node]
|
68
|
+
fail Errors::WrongNode.new(node) unless fn[node]
|
69
|
+
@commands = commands << [name, node]
|
70
|
+
end
|
71
|
+
|
72
|
+
def __set_rules__
|
73
|
+
@rules = Rules.new
|
74
|
+
end
|
75
|
+
|
76
|
+
def __set_commands__
|
77
|
+
@commands = Commands.new
|
78
|
+
end
|
79
|
+
|
80
|
+
def __configure__(&block)
|
81
|
+
instance_eval(&block) if block_given?
|
82
|
+
end
|
83
|
+
|
84
|
+
def __set_builder__
|
85
|
+
@builder = Class.new(Builder)
|
86
|
+
builder.commands = commands
|
87
|
+
end
|
88
|
+
|
89
|
+
def __set_optimizer__
|
90
|
+
@optimizer = Optimizer.new(rules)
|
91
|
+
end
|
92
|
+
|
93
|
+
end # class Settings
|
94
|
+
|
95
|
+
end # class AbstractMapper
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class AbstractMapper
|
4
|
+
|
5
|
+
# The abstract class that describes the rule to be applied to sole nodes.
|
6
|
+
#
|
7
|
+
# The subclass should implement two instance methods:
|
8
|
+
#
|
9
|
+
# * `#optimize?` to check if the optimization should be applied to the node
|
10
|
+
# * `#optimize` that should return the optimized node
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# class RemoveEmptyList < AbstractMapper::SoleRule
|
14
|
+
# def optimize?
|
15
|
+
# node.is_a?(List) && node.empty?
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# def optimize
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# @abstract
|
23
|
+
#
|
24
|
+
# @api public
|
25
|
+
#
|
26
|
+
class SoleRule < Rule
|
27
|
+
|
28
|
+
# Returns the name of transproc used to compose rules
|
29
|
+
#
|
30
|
+
# @return [Symbol]
|
31
|
+
#
|
32
|
+
def self.composer
|
33
|
+
:filter
|
34
|
+
end
|
35
|
+
|
36
|
+
# @!attribute [r] node
|
37
|
+
#
|
38
|
+
# @return [AbstractMapper::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::Node] node
|
47
|
+
#
|
48
|
+
# @return [AbstractMapper::Rule]
|
49
|
+
|
50
|
+
# @private
|
51
|
+
def initialize(node)
|
52
|
+
@node = node
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
end # class SoleRule
|
57
|
+
|
58
|
+
end # class AbstractMapper
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "transproc"
|
4
|
+
|
5
|
+
require_relative "abstract_mapper/functions"
|
6
|
+
|
7
|
+
require_relative "abstract_mapper/errors/unknown_command"
|
8
|
+
require_relative "abstract_mapper/errors/wrong_node"
|
9
|
+
require_relative "abstract_mapper/errors/wrong_rule"
|
10
|
+
|
11
|
+
require_relative "abstract_mapper/node"
|
12
|
+
require_relative "abstract_mapper/branch"
|
13
|
+
require_relative "abstract_mapper/commands"
|
14
|
+
require_relative "abstract_mapper/builder"
|
15
|
+
|
16
|
+
require_relative "abstract_mapper/rule"
|
17
|
+
require_relative "abstract_mapper/sole_rule"
|
18
|
+
require_relative "abstract_mapper/pair_rule"
|
19
|
+
require_relative "abstract_mapper/rules"
|
20
|
+
require_relative "abstract_mapper/optimizer"
|
21
|
+
|
22
|
+
require_relative "abstract_mapper/settings"
|
23
|
+
require_relative "abstract_mapper/dsl"
|
24
|
+
|
25
|
+
# The configurable base class for mappers
|
26
|
+
#
|
27
|
+
# The mapper DSL is configured by assigning it a specific settings:
|
28
|
+
#
|
29
|
+
# class BaseMapper < AbstractMapper::Mapper
|
30
|
+
# configure do
|
31
|
+
# command :list, List # domain-specific command
|
32
|
+
# command :rename, Rename # domain-specific command
|
33
|
+
#
|
34
|
+
# rule MergeLists # domain-specific rule
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# Then a configured mapper can use a corresponding DSL commands
|
39
|
+
#
|
40
|
+
# class ConcreteMapper < BaseMapper
|
41
|
+
# list do
|
42
|
+
# rename :foo, to: :bar
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# @api public
|
47
|
+
#
|
48
|
+
class AbstractMapper
|
49
|
+
|
50
|
+
extend DSL # configurable DSL container
|
51
|
+
|
52
|
+
# @!attribute [r] tree
|
53
|
+
#
|
54
|
+
# @return [AbstractMapper::Branch] AST
|
55
|
+
#
|
56
|
+
attr_reader :tree
|
57
|
+
|
58
|
+
# @!scope class
|
59
|
+
# @!method new
|
60
|
+
# Creates a mapper instance with the optimized AST
|
61
|
+
#
|
62
|
+
# @return [AbstractMapper::Mapper]
|
63
|
+
|
64
|
+
# @private
|
65
|
+
def initialize
|
66
|
+
@tree = self.class.finalize
|
67
|
+
@transproc = @tree.transproc
|
68
|
+
freeze
|
69
|
+
end
|
70
|
+
|
71
|
+
# Maps the input data to some output using the transformation,
|
72
|
+
# described by the optimized [#tree]
|
73
|
+
#
|
74
|
+
# @param [Object] input
|
75
|
+
#
|
76
|
+
# @return [Object]
|
77
|
+
#
|
78
|
+
def call(input)
|
79
|
+
@transproc.call(input)
|
80
|
+
end
|
81
|
+
|
82
|
+
end # class AbstractMapper
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# ==============================================================================
|
4
|
+
# Examples for testing transproc functions
|
5
|
+
# ==============================================================================
|
6
|
+
|
7
|
+
shared_examples :transforming_immutable_data do
|
8
|
+
|
9
|
+
let(:fn) { described_class[*arguments] }
|
10
|
+
|
11
|
+
subject { fn[input.dup.freeze] }
|
12
|
+
|
13
|
+
it do
|
14
|
+
is_expected.to eql(output), <<-REPORT.gsub(/.+\|/, "")
|
15
|
+
|
|
16
|
+
|fn = #{described_class}[{arguments}]
|
17
|
+
|
|
18
|
+
|fn#{Array[*input]}
|
19
|
+
|
|
20
|
+
| expected: #{output}
|
21
|
+
| got: #{subject}
|
22
|
+
REPORT
|
23
|
+
end
|
24
|
+
|
25
|
+
end # shared examples
|
data/lib/rspec/mapper.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# ==============================================================================
|
4
|
+
# Examples for testing DSL registry
|
5
|
+
# ==============================================================================
|
6
|
+
|
7
|
+
shared_examples :defining_rule do
|
8
|
+
|
9
|
+
subject { described_class.settings.rules.registry }
|
10
|
+
it do
|
11
|
+
is_expected.to include(rule), <<-REPORT.gsub(/.+\|/, "")
|
12
|
+
|
|
13
|
+
|#{described_class} optimization rules
|
14
|
+
|
|
15
|
+
|expected to include #{rule.inspect}
|
16
|
+
| got rules:
|
17
|
+
|#{subject.map { |rule| "#{" " * 9}- #{rule.inspect}" }.join("\n")}
|
18
|
+
REPORT
|
19
|
+
end
|
20
|
+
|
21
|
+
end # shared examples
|
22
|
+
|
23
|
+
shared_examples :defining_command do
|
24
|
+
|
25
|
+
subject { described_class.settings.commands.registry }
|
26
|
+
it "registers the command" do
|
27
|
+
expect(subject[name]).to eql(node), <<-REPORT.gsub(/.+\|/, "")
|
28
|
+
|
|
29
|
+
|#{described_class} DSL commands
|
30
|
+
|
|
31
|
+
|expected to include '#{name}' adding #{node.inspect}
|
32
|
+
| got commands:
|
33
|
+
|#{subject
|
34
|
+
.map { |name, node| "#{" " * 9}- #{name}: #{node.inspect}" }
|
35
|
+
.sort
|
36
|
+
.join("\n")}
|
37
|
+
REPORT
|
38
|
+
end
|
39
|
+
|
40
|
+
end # shared examples
|
data/lib/rspec/nodes.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# ==============================================================================
|
4
|
+
# Examples for testing nodes
|
5
|
+
# ==============================================================================
|
6
|
+
|
7
|
+
shared_context :node do
|
8
|
+
|
9
|
+
def node
|
10
|
+
attrs__ = defined?(attributes) ? attributes : []
|
11
|
+
block__ = defined?(block) ? block : nil
|
12
|
+
described_class.new(*attrs__, &block__)
|
13
|
+
end
|
14
|
+
|
15
|
+
end # shared context
|
16
|
+
|
17
|
+
shared_examples :creating_immutable_node do
|
18
|
+
|
19
|
+
include_context :node
|
20
|
+
|
21
|
+
subject { node }
|
22
|
+
|
23
|
+
it { is_expected.to be_kind_of AbstractMapper::Node }
|
24
|
+
it { is_expected.to be_frozen, "expected #{subject.inspect} to be immutable" }
|
25
|
+
|
26
|
+
end # shared examples
|
27
|
+
|
28
|
+
shared_examples :creating_immutable_branch do
|
29
|
+
|
30
|
+
include_context :node
|
31
|
+
|
32
|
+
subject { node }
|
33
|
+
|
34
|
+
it { is_expected.to be_kind_of AbstractMapper::Branch }
|
35
|
+
it { is_expected.to be_frozen, "expected #{subject.inspect} to be immutable" }
|
36
|
+
|
37
|
+
end # shared examples
|
38
|
+
|
39
|
+
shared_examples :mapping_immutable_input do
|
40
|
+
|
41
|
+
include_context :node
|
42
|
+
|
43
|
+
subject { node.transproc.call(input.dup.freeze) }
|
44
|
+
|
45
|
+
it do
|
46
|
+
is_expected.to eql(output), <<-REPORT.gsub(/.+\|/, "")
|
47
|
+
|
|
48
|
+
|#{node}
|
49
|
+
|
|
50
|
+
|Input: #{input}
|
51
|
+
|
|
52
|
+
|Output:
|
53
|
+
| expected: #{output}
|
54
|
+
| got: #{subject}
|
55
|
+
REPORT
|
56
|
+
end
|
57
|
+
|
58
|
+
end # shared examples
|
data/lib/rspec/rules.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# ==============================================================================
|
4
|
+
# Examples for testing rules
|
5
|
+
# ==============================================================================
|
6
|
+
|
7
|
+
shared_context :rule do
|
8
|
+
|
9
|
+
def nodes
|
10
|
+
defined?(input) ? [input].flatten : []
|
11
|
+
end
|
12
|
+
|
13
|
+
def optimized
|
14
|
+
defined?(output) ? [output].flatten : []
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:rule) { described_class }
|
18
|
+
|
19
|
+
subject { rule.new(*nodes).call.inspect }
|
20
|
+
|
21
|
+
end # shared context
|
22
|
+
|
23
|
+
shared_examples :skipping_nodes do
|
24
|
+
|
25
|
+
include_context :rule
|
26
|
+
|
27
|
+
it do
|
28
|
+
is_expected.to eql(nodes.inspect), <<-REPORT.gsub(/.+\|/, "")
|
29
|
+
|
|
30
|
+
|#{rule}
|
31
|
+
|
|
32
|
+
|Input: #{nodes.inspect}
|
33
|
+
|
|
34
|
+
|Output:
|
35
|
+
| expected: #{nodes.inspect}
|
36
|
+
| got: #{subject}
|
37
|
+
REPORT
|
38
|
+
end
|
39
|
+
|
40
|
+
end # shared examples
|
41
|
+
|
42
|
+
shared_examples :optimizing_nodes do
|
43
|
+
|
44
|
+
include_context :rule
|
45
|
+
|
46
|
+
it do
|
47
|
+
is_expected.to eql(optimized.inspect), <<-REPORT.gsub(/.+\|/, "")
|
48
|
+
|
|
49
|
+
|#{rule}
|
50
|
+
|
|
51
|
+
|Input: #{nodes.inspect}
|
52
|
+
|
|
53
|
+
|Output:
|
54
|
+
| expected: #{optimized.inspect}
|
55
|
+
| got: #{subject}
|
56
|
+
REPORT
|
57
|
+
end
|
58
|
+
|
59
|
+
end # shared examples
|