README.md in dio-0.0.2 vs README.md in dio-0.0.3

- old
+ new

@@ -13,11 +13,169 @@ treating the pattern match keys as method calls to "dive into" an object to match against it. ## Usage -Further usage instructions will be documented at a later date, see -`spec/dio_spec.rb` for ideas in the mean time. +There are three core types of Forwarders, the center of how Dio works: + +1. **Dynamic** - Uses `public_send` for `Hash` matches, and `Array` method coercion for `Array` matches +2. **Attribute** - Uses `attr_*` methods as source of match data +3. **String Hash** - Treats `String` Hashes like `Symbol` ones for the purpose of matching. + +Let's take a look at each of them. + +### Dynamic Forwarder + +Used with `Dio.dynamic` or `Dio[]`, the default forwarder. This uses `public_send` to extract attributes for pattern matching. + +#### With Hash Matching + +With an `Integer` this might look like this: + +```ruby +Dio[1] in { succ: { succ: { succ: 4 } } } +# => true +``` + +This has the same result as calling `1.succ.succ.succ` with each nested `Hash` in the pattern match working on the next value. That means it can also be used to do this: + +```ruby +Dio[1] in { + succ: { chr: "\x02", to_s: '2' }, + to_s: '1' +} +``` + +#### With Array Matching + +If the object under the wrapper provides a method that can be used to coerce the value into an `Array` it can be used for an `Array` match. + +Those methods are: `to_a`, `to_ary`, and `map`. + +Given a `Node` class with a value and a set of children: + +```ruby +Node = Struct.new(:value, :children) +``` + +We can match against it as if it were capable of natively pattern matching: + +```ruby +tree = Node[1, + Node[2, Node[3, Node[4]]], + Node[5], + Node[6, Node[7], Node[8]] +] + +case Dio.dynamic(tree) +in [1, [*, [5, _], *]] + true +else + false +end +``` + +### Attribute Forwarder + +Attribute Forwarders are more conservative than Dynamic ones as they only work on public attributes, or those that are defined with `attr_*`. In the case of this class: + +```ruby +class Person + attr_reader :name, :age, :children + + def initialize(name:, age:, children: []) + @name = name + @age = age + @children = children + end +end +``` + +...the attributes available would be `name`, `age`, and `children`. This also means that you can dive into `children` as well. + +#### With Hash Matching + +Let's say we had Alice here: + +```ruby +Person.new( + name: 'Alice', + age: 40, + children: [ + Person.new(name: 'Jim', age: 10), + Person.new(name: 'Jill', age: 10) + ] +) +``` + +With Hash style matching we can do this: + +```ruby +case Dio.attribute(alice) +in { name: /^A/, age: 30..50 } + true +else + false +end +``` + +...which, as pattern matches use `===` lets us use a lot of other fun things. We can even go deeper into searching through the `children` attribute: + +```ruby +case Dio.attribute(alice) +in { children: [*, { name: /^J/ }, *] } + true +else + false +end +``` + +#### With Array Matching + +This one is a bit more spurious, as it applies the attributes in the name it sees them. For something like our `Node` above with two attributes it will work the same as `dynamic`. + +### String Hash Forwarder + +Pattern Matching cannot apply to `String` keys, which can be annoying when working with data and not wanting to deep transform it into `Symbol` keys. The String Hash Forwarder tries to address this. + +#### With Hash Matching + +Let's say we had the following `Hash`: + +```ruby +hash = { + 'a' => 1, + 'b' => 2, + 'c' => { + 'd' => 3, + 'e' => { + 'f' => 4 + } + } +} +``` + +We can match against it by using the `Symbol` equivalents of our `String` keys: + +```ruby +case Dio.string_hash(hash) +in { a: 1, b: 2 } + true +else + false +end +``` + +...and because of the nature of Dio you can continue to dive deeper: + +```ruby +case Dio.string_hash(hash) +in { a: 1, b: 2, c: { d: 1..10, e: { f: 3.. } } } + true +else + false +end +``` ## Installation Add this line to your application's Gemfile: