README.md in abstract_mapper-0.0.1 vs README.md in abstract_mapper-0.0.2
- old
+ new
@@ -60,11 +60,11 @@
### Define transformations (specific nodes of AST)
Every node should implement the `#transproc` method that transforms some input data to the output.
-When you need attributes, assign them via initializer:
+When you need attributes, assign them using [virtus] method `attribute`:
```ruby
require "abstract_mapper"
module Faceter
@@ -77,25 +77,23 @@
def transproc
Transproc::ArrayTransformations[:map_array, super]
end
end
- # The node to define a renaming of key in a tuple
+ # The node to define a renaming of keys in a tuple
class Rename < AbstractMapper::Node
- def initialize(key, **options)
- @key = key
- @new_key = options.fetch(:to)
- super
- end
+ attribute :keys
def transproc
- Transproc::HashTransformations[:rename_keys, @key => @new_key]
+ Transproc::HashTransformations[:rename_keys, keys]
end
end
end
```
+[virtus]: https://github.com/solnic/virtus
+
### Define optimization rules
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.
For every rule you need to define two methods:
@@ -123,33 +121,51 @@
#
# That's why when we meet two consecutive lists, we have to merge them
# into the one list, containing subnodes (entries) from both sources.
class CompactLists < AbstractMapper::PairRule
def optimize?
- nodes.join(:|) { |n| n.is_a? List }
+ nodes.map { |n| n.is_a? List }.reduce(:&)
end
def optimize
List.new { nodes.map(:entries).flatten }
end
end
+
+ # Two consecutive renames can be merged
+ class CompactRenames < AbstractMapper::PairRule
+ def optimize?
+ nodes.map { |n| n.is_a? Rename }.reduce(:&)
+ end
+
+ def optimize
+ Rename.new nodes.map(&:attributes).reduce(:merge)
+ end
+ end
end
```
### Register commands and rules
-Now that both the nodes (transformers) and optimization rules are defined, its time to register them for the mapper:
+Now that both the nodes (transformers) and optimization rules are defined, its time to register them for the mapper.
+You can coerce command argumets into node attributes. The coercer is expected to return a hash:
+
```ruby
module Faceter
class Mapper < AbstractMapper
configure do
- command :list, List
- command :rename, Rename
+ command :list, List
+ # `:foo, to: :bar` becomes `{ keys: { foo: :bar } }`
+ command :rename, Rename do |name, opts|
+ { keys: { name => opts.fetch(:to) } }
+ end
+
rule RemoveEmptyLists
rule CompactLists
+ rule CompactRenames
end
end
end
```
@@ -182,10 +198,10 @@
All the rules are applied before initializing `my_mapper`, so the AST will be the following:
```ruby
my_mapper.tree
-# => <Root <List [<Rename(foo:, to: :bar)>, <Rename(:baz, to: :qux)>]>
+# => <Root [<List [<Rename(foo: :bar, baz: :qux)>]>]>
```
Testing
-------