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 -------