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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG.md +34 -0
  4. data/Gemfile +1 -3
  5. data/README.md +29 -13
  6. data/Rakefile +4 -7
  7. data/abstract_mapper.gemspec +3 -2
  8. data/config/metrics/STYLEGUIDE +14 -15
  9. data/lib/abstract_mapper.rb +4 -1
  10. data/lib/abstract_mapper/attributes.rb +65 -0
  11. data/lib/abstract_mapper/branch.rb +17 -7
  12. data/lib/abstract_mapper/builder.rb +7 -3
  13. data/lib/abstract_mapper/command.rb +68 -0
  14. data/lib/abstract_mapper/commands.rb +11 -31
  15. data/lib/abstract_mapper/errors/unknown_command.rb +1 -1
  16. data/lib/abstract_mapper/errors/wrong_node.rb +1 -1
  17. data/lib/abstract_mapper/errors/wrong_rule.rb +2 -2
  18. data/lib/abstract_mapper/functions.rb +32 -5
  19. data/lib/abstract_mapper/node.rb +21 -10
  20. data/lib/abstract_mapper/optimizer.rb +3 -3
  21. data/lib/abstract_mapper/pair_rule.rb +2 -4
  22. data/lib/abstract_mapper/rspec.rb +1 -2
  23. data/lib/abstract_mapper/rule.rb +23 -2
  24. data/lib/abstract_mapper/rules.rb +3 -10
  25. data/lib/abstract_mapper/settings.rb +3 -3
  26. data/lib/abstract_mapper/sole_rule.rb +2 -4
  27. data/lib/abstract_mapper/version.rb +1 -1
  28. data/lib/rspec/doubles.rb +16 -0
  29. data/lib/rspec/nodes.rb +29 -16
  30. data/lib/rspec/rules.rb +3 -3
  31. data/spec/integration/faceter.rb +16 -7
  32. data/spec/integration/mapper_definition_spec.rb +1 -1
  33. data/spec/integration/rspec_examples_spec.rb +4 -30
  34. data/spec/spec_helper.rb +1 -1
  35. data/spec/unit/abstract_mapper/branch_spec.rb +91 -14
  36. data/spec/unit/abstract_mapper/builder_spec.rb +66 -48
  37. data/spec/unit/abstract_mapper/command_spec.rb +92 -0
  38. data/spec/unit/abstract_mapper/commands_spec.rb +38 -79
  39. data/spec/unit/abstract_mapper/dsl_spec.rb +60 -55
  40. data/spec/unit/abstract_mapper/errors/wrong_rule_spec.rb +1 -1
  41. data/spec/unit/abstract_mapper/functions/compact_spec.rb +3 -3
  42. data/spec/unit/abstract_mapper/functions/filter_spec.rb +1 -1
  43. data/spec/unit/abstract_mapper/functions/identity_spec.rb +21 -0
  44. data/spec/unit/abstract_mapper/functions/restrict_spec.rb +16 -0
  45. data/spec/unit/abstract_mapper/functions/subclass_spec.rb +1 -1
  46. data/spec/unit/abstract_mapper/node_spec.rb +119 -48
  47. data/spec/unit/abstract_mapper/optimizer_spec.rb +38 -32
  48. data/spec/unit/abstract_mapper/pair_rule_spec.rb +4 -11
  49. data/spec/unit/abstract_mapper/rule_spec.rb +31 -15
  50. data/spec/unit/abstract_mapper/rules_spec.rb +2 -2
  51. data/spec/unit/abstract_mapper/settings_spec.rb +80 -73
  52. data/spec/unit/abstract_mapper/sole_rule_spec.rb +3 -10
  53. data/spec/unit/abstract_mapper_spec.rb +13 -5
  54. metadata +33 -12
  55. data/lib/rspec/functions.rb +0 -25
  56. 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
- # Registry of DSL commands used by the builder
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.freeze
30
- freeze
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 [Array<Symbol, Class>] other
27
+ # @param [[Symbol, Class, Proc]] other
36
28
  #
37
29
  # @return [undefined]
38
30
  #
39
31
  def <<(other)
40
- name = other[0]
41
- node = other[1]
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
- # Builds a node by the name of DSL command
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
- # @param [Object, Array] args The command's arguments
51
- # @param [Proc] block
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, *args, &block)
58
- type = get(name)
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
@@ -15,7 +15,7 @@ class AbstractMapper
15
15
  # @private
16
16
  def initialize(name)
17
17
  super "'#{name}' is not a registered DSL command"
18
- freeze
18
+ IceNine.deep_freeze(self)
19
19
  end
20
20
 
21
21
  end # class UnknownCommand
@@ -13,7 +13,7 @@ class AbstractMapper
13
13
  # @private
14
14
  def initialize(node)
15
15
  super "#{node} is not a subclass of AbstractMapper::Node"
16
- freeze
16
+ IceNine.deep_freeze(self)
17
17
  end
18
18
 
19
19
  end # class WrongNode
@@ -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::SoleRule"
16
- freeze
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
- uses :map_array, from: Transproc::ArrayTransformations
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(array, fn).compact.flatten
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
@@ -17,11 +17,8 @@ class AbstractMapper
17
17
  #
18
18
  class Node
19
19
 
20
- # @!attribute [r] attributes
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(*attributes, &block)
34
- @attributes = attributes.freeze
30
+ def initialize(_ = {}, &block)
31
+ super
35
32
  @block = block
36
- freeze
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
- Transproc::Function.new(-> v { v }, {})
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
- "(#{attributes.map(&:inspect).join(", ")})" if attributes.any?
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
- freeze
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.rebuild { rules[tree] }
43
- new_tree.rebuild { new_tree.map(&method(:update)) }
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
- # Returns the name of transproc used to compose rules
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
  #
@@ -1,7 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  # Collection of shared examples for testing parts of DSL
4
- require_relative "../rspec/functions"
5
4
  require_relative "../rspec/nodes"
6
5
  require_relative "../rspec/rules"
7
- require_relative "../rspec/mapper"
6
+ require_relative "../rspec/doubles"
@@ -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.freeze
39
- freeze
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.freeze
28
- @transproc = ordered.map(&:transproc).inject(:>>)
29
- freeze
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
- freeze
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
- # Returns the name of transproc used to compose rules
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
  #