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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -4
  3. data/CHANGELOG.md +26 -0
  4. data/README.md +5 -5
  5. data/Rakefile +1 -1
  6. data/abstract_mapper.gemspec +1 -0
  7. data/lib/abstract_mapper.rb +4 -15
  8. data/lib/abstract_mapper/ast.rb +16 -0
  9. data/lib/abstract_mapper/ast/branch.rb +121 -0
  10. data/lib/abstract_mapper/ast/node.rb +91 -0
  11. data/lib/abstract_mapper/builder.rb +2 -2
  12. data/lib/abstract_mapper/commands.rb +4 -2
  13. data/lib/abstract_mapper/commands/base.rb +69 -0
  14. data/lib/abstract_mapper/dsl.rb +2 -2
  15. data/lib/abstract_mapper/errors.rb +17 -0
  16. data/lib/abstract_mapper/errors/wrong_node.rb +1 -1
  17. data/lib/abstract_mapper/errors/wrong_rule.rb +1 -1
  18. data/lib/abstract_mapper/optimizer.rb +1 -1
  19. data/lib/abstract_mapper/rules.rb +10 -5
  20. data/lib/abstract_mapper/rules/base.rb +74 -0
  21. data/lib/abstract_mapper/rules/pair.rb +68 -0
  22. data/lib/abstract_mapper/rules/sole.rb +60 -0
  23. data/lib/abstract_mapper/settings.rb +22 -30
  24. data/lib/abstract_mapper/version.rb +1 -1
  25. data/lib/rspec/nodes.rb +2 -2
  26. data/spec/integration/faceter.rb +4 -4
  27. data/spec/integration/rspec_examples_spec.rb +0 -6
  28. data/spec/unit/abstract_mapper/{branch_spec.rb → ast/branch_spec.rb} +28 -61
  29. data/spec/unit/abstract_mapper/{node_spec.rb → ast/node_spec.rb} +16 -53
  30. data/spec/unit/abstract_mapper/builder_spec.rb +9 -24
  31. data/spec/unit/abstract_mapper/{command_spec.rb → commands/base_spec.rb} +10 -25
  32. data/spec/unit/abstract_mapper/commands_spec.rb +9 -14
  33. data/spec/unit/abstract_mapper/dsl_spec.rb +23 -15
  34. data/spec/unit/abstract_mapper/errors/unknown_command_spec.rb +1 -4
  35. data/spec/unit/abstract_mapper/errors/wrong_node_spec.rb +5 -4
  36. data/spec/unit/abstract_mapper/errors/wrong_rule_spec.rb +5 -5
  37. data/spec/unit/abstract_mapper/functions/compact_spec.rb +0 -2
  38. data/spec/unit/abstract_mapper/functions/filter_spec.rb +0 -2
  39. data/spec/unit/abstract_mapper/functions/identity_spec.rb +0 -2
  40. data/spec/unit/abstract_mapper/functions/restrict_spec.rb +0 -3
  41. data/spec/unit/abstract_mapper/functions/subclass_spec.rb +0 -2
  42. data/spec/unit/abstract_mapper/optimizer_spec.rb +13 -17
  43. data/spec/unit/abstract_mapper/{rule_spec.rb → rules/base_spec.rb} +17 -34
  44. data/spec/unit/abstract_mapper/{pair_rule_spec.rb → rules/pair_spec.rb} +8 -8
  45. data/spec/unit/abstract_mapper/{sole_rule_spec.rb → rules/sole_spec.rb} +5 -5
  46. data/spec/unit/abstract_mapper/rules_spec.rb +24 -37
  47. data/spec/unit/abstract_mapper/settings_spec.rb +38 -32
  48. data/spec/unit/abstract_mapper_spec.rb +9 -16
  49. metadata +37 -22
  50. data/lib/abstract_mapper/attributes.rb +0 -65
  51. data/lib/abstract_mapper/branch.rb +0 -120
  52. data/lib/abstract_mapper/command.rb +0 -68
  53. data/lib/abstract_mapper/node.rb +0 -87
  54. data/lib/abstract_mapper/pair_rule.rb +0 -64
  55. data/lib/abstract_mapper/rule.rb +0 -73
  56. data/lib/abstract_mapper/sole_rule.rb +0 -56
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0547716c6b580abf30fd1b16d2e42cb2656971da
4
- data.tar.gz: 7c793b44bebc3157b32cb754e76fd8e7c550a242
3
+ metadata.gz: a20fe042523fd0d4f3e37d3838e4e3a4c3770950
4
+ data.tar.gz: a3fe86965e9a2c223023a0fa447680d677d771d1
5
5
  SHA512:
6
- metadata.gz: 89772b4b3618585644e6224b93abd13276d1ed057be038ce38c677b752aefacc424fa3d05076810555afe976a45d4e059c82eb96cb7acc2b9f29b96fa66c7f15
7
- data.tar.gz: 688f0f055d98a12c7558948b5191c9f1e7fe0d43ac740929339fb3a1e7a700252273a535430c21d3676ecf229ba61257661890711a343f333c66bc9fdce2da5a
6
+ metadata.gz: fb3864254189282a0a0be1da50905c803cf4d8009c6b17893a74b3d262fe4ab5ec885687a61ce801ccf75100e7212c1515dc8eab91e0c91ec372c00292e3692b
7
+ data.tar.gz: 673c8268699b47ec511d63326b7aea6226115f50741137f8fdcff905f6d03fc89a86db52355eabb1e0482b1e06a6bda9d05b311f98407e25ff55861496a16580
@@ -4,14 +4,10 @@ cache: bundler
4
4
  script: "bundle exec rake test:coverage:run"
5
5
  bundler_args: --without metrics
6
6
  rvm:
7
- - '1.9.3'
8
- - '2.0'
9
7
  - '2.1'
10
8
  - '2.2'
11
9
  - ruby-head
12
- - rbx-2 --1.9
13
10
  - rbx-2 --2.1
14
- - jruby-19mode
15
11
  - jruby-20mode
16
12
  - jruby-head
17
13
  matrix:
@@ -1,3 +1,29 @@
1
+ ## v0.1.0 2015-09-18
2
+
3
+ This version rearranges some classes by adding namespaces and **extends the behaviour of configure**.
4
+ With a new support pre-defined mappers can be inherited deeply with updated set of commands and rules.
5
+
6
+ ### Added
7
+
8
+ * `AbstractMapper.configure` now updates the existing settings instead of rewriting them from scratch (nepalez)
9
+ with reference to issue #1 (faceter#2 by martinciu)
10
+
11
+ ### Changed (backward incompatible!)
12
+
13
+ * Rules and Nodes were namespaces and renamed (nepalez):
14
+ - `AbstractMapper::Node` -> `AbstractMapper::AST::Node`
15
+ - `AbstractMapper::Branch` -> `AbstractMapper::AST::Branch`
16
+ - `AbstractMapper::Rule` -> `AbstractMapper::Rules::Base`
17
+ - `AbstractMapper::SoleRule` -> `AbstractMapper::Rules::Sole`
18
+ - `AbstractMapper::PairRule` -> `AbstractMapper::Rules::Pair`
19
+
20
+ ### Internal
21
+
22
+ * Extracted `Attributes` to the external gem 'attributes_dsl' (nepalez)
23
+ * Renamed `Command` to `Commands::Base` (nepalez)
24
+
25
+ [Compare v0.0.2...v0.1.0](https://github.com/nepalez/abstract_mapper/compare/v0.0.2...v0.1.0)
26
+
1
27
  ## v0.0.2 2015-08-06
2
28
 
3
29
  ### Added
data/README.md CHANGED
@@ -94,19 +94,19 @@ end
94
94
 
95
95
  ### Define optimization rules
96
96
 
97
- AbstractMapper defines 2 rules `AbstractMapper::SoleRule` and `AbstractMapper::PairRule`. The first one is applicable to every single node to check if it can be optimized by itself, the second one takes two consecutive nodes and either return them unchanged, or merges them into more time-efficient node.
97
+ AbstractMapper defines 2 rules `AbstractMapper::Rules::Sole` and `AbstractMapper::Rules::Pair`. The first one is applicable to every single node to check if it can be optimized by itself, the second one takes two consecutive nodes and either return them unchanged, or merges them into more time-efficient node.
98
98
 
99
99
  For every rule you need to define two methods:
100
100
 
101
101
  * `#optimize?` that defines if the rule is applicable to given node (or pair of nodes)
102
102
  * `#optimize` that returns either array of changed nodes, or one node, or nothing when the node(s) should be removed from the tree
103
103
 
104
- Use `#nodes` method to access nodes to be optimized. Base class `AbstractMapper::SoleRule` also defines the `#node` method, while `AbstractMapper::PairRule` defines `#left` and `#right` for the corresponding parts of the pair.
104
+ Use `#nodes` method to access nodes to be optimized. Base class `AbstractMapper::Rules::Sole` also defines the `#node` method, while `AbstractMapper::Rules::Pair` defines `#left` and `#right` for the corresponding parts of the pair.
105
105
 
106
106
  ```ruby
107
107
  module Faceter
108
108
  # The empty lists are useless, because they does nothing at all
109
- class RemoveEmptyLists < AbstractMapper::SoleRule
109
+ class RemoveEmptyLists < AbstractMapper::Rules::Sole
110
110
  def optimize?
111
111
  node.is_a?(List) && node.empty?
112
112
  end
@@ -121,7 +121,7 @@ module Faceter
121
121
  #
122
122
  # That's why when we meet two consecutive lists, we have to merge them
123
123
  # into the one list, containing subnodes (entries) from both sources.
124
- class CompactLists < AbstractMapper::PairRule
124
+ class CompactLists < AbstractMapper::Rules::Pair
125
125
  def optimize?
126
126
  nodes.map { |n| n.is_a? List }.reduce(:&)
127
127
  end
@@ -132,7 +132,7 @@ module Faceter
132
132
  end
133
133
 
134
134
  # Two consecutive renames can be merged
135
- class CompactRenames < AbstractMapper::PairRule
135
+ class CompactRenames < AbstractMapper::Rules::Pair
136
136
  def optimize?
137
137
  nodes.map { |n| n.is_a? Rename }.reduce(:&)
138
138
  end
data/Rakefile CHANGED
@@ -15,7 +15,7 @@ rescue LoadError
15
15
  Hexx::RSpec.install_tasks
16
16
  end
17
17
 
18
- # Sets the Hexx::RSpec :test task to default
18
+ desc "Sets the Hexx::RSpec :test task to default"
19
19
  task :default do
20
20
  system "bundle exec rake test:coverage:run"
21
21
  end
@@ -19,6 +19,7 @@ Gem::Specification.new do |gem|
19
19
 
20
20
  gem.required_ruby_version = ">= 2.1"
21
21
 
22
+ gem.add_runtime_dependency "attributes_dsl", "~> 0.0"
22
23
  gem.add_runtime_dependency "ice_nine", "~> 0.11"
23
24
  gem.add_runtime_dependency "transproc", "~> 0.3", ">= 0.3.1"
24
25
 
@@ -1,27 +1,16 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require "attributes_dsl"
3
4
  require "ice_nine"
4
5
  require "transproc"
5
6
 
6
7
  require_relative "abstract_mapper/functions"
7
-
8
- require_relative "abstract_mapper/errors/unknown_command"
9
- require_relative "abstract_mapper/errors/wrong_node"
10
- require_relative "abstract_mapper/errors/wrong_rule"
11
-
12
- require_relative "abstract_mapper/attributes"
13
- require_relative "abstract_mapper/node"
14
- require_relative "abstract_mapper/branch"
15
- require_relative "abstract_mapper/command"
8
+ require_relative "abstract_mapper/errors"
9
+ require_relative "abstract_mapper/ast"
16
10
  require_relative "abstract_mapper/commands"
17
- require_relative "abstract_mapper/builder"
18
-
19
- require_relative "abstract_mapper/rule"
20
- require_relative "abstract_mapper/sole_rule"
21
- require_relative "abstract_mapper/pair_rule"
22
11
  require_relative "abstract_mapper/rules"
12
+ require_relative "abstract_mapper/builder"
23
13
  require_relative "abstract_mapper/optimizer"
24
-
25
14
  require_relative "abstract_mapper/settings"
26
15
  require_relative "abstract_mapper/dsl"
27
16
 
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+
3
+ class AbstractMapper
4
+
5
+ # The namespace for nodes of the abstract syntax tree
6
+ #
7
+ # @author Andrew Kozin <Andrew.Kozin@gmail.com>
8
+ #
9
+ module AST
10
+
11
+ require_relative "ast/node"
12
+ require_relative "ast/branch"
13
+
14
+ end # module AST
15
+
16
+ end # class AbstractMapper
@@ -0,0 +1,121 @@
1
+ # encoding: utf-8
2
+
3
+ class AbstractMapper
4
+
5
+ module AST
6
+
7
+ # A special type of the composed node, that describes transformation,
8
+ # applied to some level of nested input.
9
+ #
10
+ # Unlike the simple node, describing a transformation of data, the
11
+ # branch carries a collection of subnodes along with methods to [#update]
12
+ # itself with the same attributes and different subnodes.
13
+ #
14
+ # Tne branch only stores subnodes and composes transformations.
15
+ # Its has no access to DSL and knows neither how to build a tree
16
+ # (see [AbstractMapper::Builder]), nor how to optimize it later
17
+ # (see [AbstractMapper::Optimizer]).
18
+ #
19
+ # @api public
20
+ #
21
+ class Branch < Node
22
+
23
+ include Enumerable
24
+
25
+ # @!scope class
26
+ # @!method new(*attributes, &block)
27
+ # Creates a new branch
28
+ #
29
+ # @param [Object, Array<Object>] attributes
30
+ # The list of type-specific attributes of the branch
31
+ # @param [Proc] block
32
+ # The block that returns an array of subnodes for the branch
33
+ #
34
+ # @return [Branch::AST::Node]
35
+
36
+ # @private
37
+ def initialize(attributes = {})
38
+ @subnodes = block_given? ? yield : []
39
+ super(attributes, &nil)
40
+ end
41
+
42
+ # Returns a new branch of the same type, with the same attributes,
43
+ # but with a different collection of subnodes, transmitted by the block.
44
+ #
45
+ # @example
46
+ # branch = Branch.new(:foo)
47
+ # # => <Branch(:foo) []>
48
+ # branch.update { AST::Node.new(:bar) }
49
+ # # => <Branch(:foo) [<AST::Node(:bar)>]>
50
+ #
51
+ # @return [AbstractMapper::Branch]
52
+ #
53
+ # @yield block
54
+ #
55
+ def update
56
+ self.class.new(attributes) { yield }
57
+ end
58
+
59
+ # @!method each
60
+ # Returns the enumerator for subnodes
61
+ #
62
+ # @return [Enumerator]
63
+ #
64
+ def each(&block)
65
+ @subnodes.each(&block)
66
+ end
67
+
68
+ # Returns a new branch with the other node added to its subnodes
69
+ #
70
+ # @param [AbstractMapper::AST::Node] other
71
+ #
72
+ # @return [AbstractMapper::Branch]
73
+ #
74
+ def <<(other)
75
+ update { entries << other }
76
+ end
77
+
78
+ # The composition of transformations from all subnodes of the branch
79
+ #
80
+ # To be reloaded by the subclasses to apply the composition to
81
+ # a corresponding level of nested data.
82
+ #
83
+ # @return [Transproc::Function]
84
+ #
85
+ # @abstract
86
+ #
87
+ def transproc
88
+ map(&:transproc).inject(:>>)
89
+ end
90
+
91
+ # Adds subnodes to the default description of the branch
92
+ #
93
+ # @return [String]
94
+ #
95
+ def to_s
96
+ "#{super} [#{map(&:inspect).join(", ")}]"
97
+ end
98
+
99
+ # Checks equality of branches by type, attributes and subnodes
100
+ #
101
+ # @param [Other] other
102
+ #
103
+ # @return [Boolean]
104
+ #
105
+ def eql?(other)
106
+ super && entries.eql?(other.entries)
107
+ end
108
+
109
+ private
110
+
111
+ # Substitutes the name of the class by the special name "Root"
112
+ # to describe the root node of AST.
113
+ def __name__
114
+ instance_of?(Branch) ? "Root" : super
115
+ end
116
+
117
+ end # class Branch
118
+
119
+ end # module AST
120
+
121
+ end # class AbstractMapper
@@ -0,0 +1,91 @@
1
+ # encoding: utf-8
2
+
3
+ class AbstractMapper
4
+
5
+ module AST
6
+
7
+ # An immutable node of the abstract syntax tree (AST), that describes
8
+ # either some "end-up" transformation, or a level of nested input data.
9
+ #
10
+ # Every node is expected to accept attributes and (possibly) block, and
11
+ # implement `#transproc` that implements the `#call` method to
12
+ # transform input data to the output.
13
+ #
14
+ # AST::Nodes describe only the structure of AST, they know
15
+ # neither how to build the tree with DSL (see [AbstractMapper::Builder]),
16
+ # nor how to optimize it later (see [AbstractMapper::Optimizer]).
17
+ #
18
+ # @api public
19
+ #
20
+ class AST::Node
21
+
22
+ extend AttributesDSL
23
+ include Comparable
24
+
25
+ # @!attribute [r] block
26
+ #
27
+ # @return [Proc] The block given to initializer
28
+ #
29
+ attr_reader :block
30
+
31
+ # @private
32
+ def initialize(_ = {}, &block)
33
+ super
34
+ @block = block
35
+ IceNine.deep_freeze(self)
36
+ end
37
+
38
+ # @!method transproc
39
+ # The transformation function for the branch
40
+ #
41
+ # @return [Transproc::Function]
42
+ #
43
+ # @abstract
44
+ #
45
+ def transproc
46
+ Functions[:identity]
47
+ end
48
+
49
+ # Returns a human-readable string representating the node
50
+ #
51
+ # @return [String]
52
+ #
53
+ def inspect
54
+ "<#{self}>"
55
+ end
56
+
57
+ # Converts the node into string with its name and attributes
58
+ #
59
+ # @return [String]
60
+ #
61
+ def to_s
62
+ "#{__name__}#{__attributes__}"
63
+ end
64
+
65
+ # Compares the node to another one by type and attributes
66
+ #
67
+ # @param [Object] other
68
+ #
69
+ # @return [Boolean]
70
+ #
71
+ def ==(other)
72
+ other.instance_of?(self.class) && attributes.eql?(other.attributes)
73
+ end
74
+ alias_method :eql?, :==
75
+
76
+ private
77
+
78
+ def __name__
79
+ self.class.name.split("::").last
80
+ end
81
+
82
+ def __attributes__
83
+ return if attributes.empty?
84
+ "(#{attributes.map { |k, v| "#{k}: #{v.inspect}" }.join(", ")})"
85
+ end
86
+
87
+ end # class AST::Node
88
+
89
+ end # module AST
90
+
91
+ end # class AbstractMapper
@@ -39,7 +39,7 @@ class AbstractMapper
39
39
  #
40
40
  # @return (see #tree)
41
41
  #
42
- def self.update(node = Branch.new, &block)
42
+ def self.update(node = AST::Branch.new, &block)
43
43
  new(node, &block).tree
44
44
  end
45
45
 
@@ -72,7 +72,7 @@ class AbstractMapper
72
72
 
73
73
  def method_missing(name, *args, &block)
74
74
  node = @commands[name].call(*args, &block)
75
- @tree = tree << (node.is_a?(Branch) ? update(node, &block) : node)
75
+ @tree = tree << (node.is_a?(AST::Branch) ? update(node, &block) : node)
76
76
  end
77
77
 
78
78
  def respond_to_missing?(*)
@@ -2,6 +2,8 @@
2
2
 
3
3
  class AbstractMapper
4
4
 
5
+ require_relative "commands/base"
6
+
5
7
  # Collection of DSL commands used by the builder
6
8
  #
7
9
  # @api private
@@ -29,7 +31,7 @@ class AbstractMapper
29
31
  # @return [undefined]
30
32
  #
31
33
  def <<(other)
32
- command = Command.new(*other)
34
+ command = Base.new(*other)
33
35
  self.class.new @registry.merge(command.name => command)
34
36
  end
35
37
 
@@ -37,7 +39,7 @@ class AbstractMapper
37
39
  #
38
40
  # @param [#to_sym] name The name of the command
39
41
  #
40
- # @return [AbstractMapper::Command]
42
+ # @return [AbstractMapper::Commands::Base]
41
43
  #
42
44
  # @raise [AbstractMapper::Errors::UnknownCommand]
43
45
  # When unknown command is called
@@ -0,0 +1,69 @@
1
+ # encoding: utf-8
2
+
3
+ class AbstractMapper
4
+
5
+ class Commands
6
+
7
+ # Describes the command of the mapper
8
+ #
9
+ # Method `#call` builds a correspodning node for the AST.
10
+ #
11
+ # @api private
12
+ #
13
+ class Base
14
+
15
+ # @!attribute [r] name
16
+ #
17
+ # @return [Symbol] The name of the DSL command
18
+ #
19
+ attr_reader :name
20
+
21
+ # @!attribute [r] klass
22
+ #
23
+ # @return [Class] The class of the node to be created
24
+ #
25
+ attr_reader :klass
26
+
27
+ # @!attribute [r] converter
28
+ #
29
+ # @return [#call] The converter of the command's arguments into the node's
30
+ #
31
+ attr_reader :converter
32
+
33
+ # @!scope class
34
+ # @!method new(name, klass, converter)
35
+ # Creates the named command for node klass and arguments converter
36
+ #
37
+ # @param [#to_sym] name The name of the DSL command
38
+ # @param [Class] klass The klass of the node to be created
39
+ # @param [#call] converter The function that coerces attributes
40
+ #
41
+ # @return [AbstractMapper::Commands::Base]
42
+
43
+ # @private
44
+ def initialize(name, klass, converter = nil)
45
+ @name = name.to_sym
46
+ @klass = klass
47
+ @branch = Functions[:subclass?, AST::Branch][klass]
48
+ @converter = converter || proc { |args = {}| args }
49
+ IceNine.deep_freeze(self)
50
+ end
51
+
52
+ # Builds the AST node
53
+ #
54
+ # @param [Object, Array] args
55
+ # The argument of the command
56
+ # @param [Proc] block
57
+ #
58
+ # @return [AbstractMapper::AST::Node]
59
+ #
60
+ def call(*args, &block)
61
+ block = nil if @branch
62
+ klass.new(converter.call(*args), &block)
63
+ end
64
+
65
+ end # class Base
66
+
67
+ end # class Commands
68
+
69
+ end # class AbstractMapper