patrun 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +22 -0
  3. data/README.md +317 -0
  4. metadata +13 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 26be7b15694fcdb3af30b2cf505e1f5446e75094
4
- data.tar.gz: 8d6c257f618b6c0acdbfa7ff47a2cb02c0225014
3
+ metadata.gz: 0a552276944c28770e97035adabe76cb38332489
4
+ data.tar.gz: 651c5a0732b33481159413c8e74c91985092708b
5
5
  SHA512:
6
- metadata.gz: e08f11b282afa711d2b25cff516780804d1647cb0b51960edcca2768ddefc289647f401b36839f17e9b0c4ad9c5e116219beb2eaacf3c8627e1bae572fa2b2e4
7
- data.tar.gz: f4903d54e56315f32e43964d746d07a1b5c7ce45f2954286f1f285128268369dfeb0770ec22530a6256c35d8531551a1b09406a345ff257e3c6b7d5f3589dd2b
6
+ metadata.gz: 4afd8163e39836f303d037928e2c891dbe9528223ee2bdad1ec31bd0f9f22a708790e393aba63fe3eb1895e5176dc0e674c7e24e7dacfe704498e2b03d149290
7
+ data.tar.gz: 4b3dcae0a76b89bcbb2efa3db29352e0243a35547fa16489d967ceb918fcde4347e910e0a91c974f01c616c9037644c4cb3551d103c1148037d2a4ed97a1fa60
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,317 @@
1
+ # patrun-ruby
2
+
3
+ ### A fast pattern matcher on Ruby object properties.
4
+
5
+ Need to pick out an object based on a subset of its properties? Say you've got:
6
+
7
+ ```Ruby
8
+ { :x => 1 } -> A
9
+ { :x => 1, :y => 1 } -> B
10
+ { :x => 1, :y => 2 } -> C
11
+ ```
12
+
13
+ Then patrun can give you the following results:
14
+
15
+ ```Ruby
16
+ { :x => 1 } -> A
17
+ { :x => 2 } -> no match
18
+ { :x => 1, :y => 1 } -> B
19
+ { :x => 1, :y => 2 } -> C
20
+ { :x => 2, :y => 2 } -> no match
21
+ { :y => 1 } -> no match
22
+ ```
23
+
24
+ It's basically _query-by-example_ for property sets.
25
+
26
+
27
+ ### Support
28
+
29
+ If you're using this library, feel free to contact me on twitter if you have any questions! :) [@colmharte](http://twitter.com/colmharte)
30
+
31
+
32
+ Current Version: 0.1.1
33
+
34
+ Tested on: Ruby 2.0.0p481
35
+
36
+
37
+ ### Quick example
38
+
39
+ Here's how you register some patterns, and then search for matches:
40
+
41
+ ```Ruby
42
+ require('patrun')
43
+
44
+ pm = Patrun.new()
45
+ pm.add({:a => 1},'A').add({:b => 2},'B')
46
+
47
+ # prints A
48
+ puts pm.find({:a => 1})
49
+
50
+ # prints nil
51
+ puts pm.find({:a => 2})
52
+
53
+ # prints A, :b => 1 is ignored, it was never registered
54
+ puts pm.find({:a => 1, :b => 1})
55
+
56
+ # prints B, :c => 3 is ignored, it was never registered
57
+ puts pm.find({:b => 2, :c => 3})
58
+ ```
59
+
60
+ You're matching a subset, so your input can contain any number of other properties.
61
+
62
+
63
+ ## Install
64
+
65
+ ```sh
66
+ gem install patrun
67
+ ```
68
+
69
+
70
+ # The Why
71
+
72
+ This module lets you build a simple decision tree so you can avoid
73
+ writing _if_ statements. It tries to make the minimum number of
74
+ comparisons necessary to pick out the most specific match.
75
+
76
+ This is very useful for handling situations where you have lots of
77
+ "cases", some of which have "sub-cases", and even "sub-sub-sub-cases".
78
+
79
+ For example, here are some sales tax rules:
80
+
81
+ * default: no sales tax
82
+ * here's a list of countries with known rates: Ireland: 23%, UK: 20%, Germany: 19%, ...
83
+ * but wait, that's only standard rates, here's [the other rates](http://www.vatlive.com/vat-rates/european-vat-rates/eu-vat-rates/)
84
+ * Oh, and we also have the USA, where we need to worry about each state...
85
+
86
+ Do this:
87
+
88
+ ```Ruby
89
+ # queries return a Proc, in case there is some
90
+ # really custom logic (and there is, see US, NY below)
91
+ # in the normal case, just pass the rate back out with
92
+ # an identity function
93
+ # also record the rate for custom printing later
94
+ I = Proc.new { | val |
95
+ rate = Proc.new {
96
+ val
97
+ }
98
+ rate
99
+ }
100
+
101
+ salestax = Patrun.new()
102
+ salestax
103
+ .add({}, I.call(0.0) )
104
+ .add({ :country => 'IE' }, I.call(0.25) )
105
+ .add({ :country => 'UK' }, I.call(0.20) )
106
+ .add({ :country => 'DE' }, I.call(0.19) )
107
+ .add({ :country => 'IE', :type => 'reduced' }, I.call(0.135) )
108
+ .add({ :country => 'IE', :type => 'food' }, I.call(0.048) )
109
+ .add({ :country => 'UK', :type => 'food' }, I.call(0.0) )
110
+ .add({ :country => 'DE', type:'reduced' }, I.call(0.07) )
111
+ .add({ :country => 'US' }, I.call(0.0) ) # no federeal rate (yet!)
112
+ .add({ :country => 'US', :state => 'AL' }, I.call(0.04) )
113
+ .add({ :country => 'US', :state => 'AL', city:'Montgomery' }, I.call(0.10) )
114
+ .add({ :country => 'US', :state => 'NY' }, I.call(0.07) )
115
+
116
+ under110 = Proc.new { | net |
117
+ net < 110 ? 0.0 : salestax.find( {:country => 'US', :state => 'NY'}).call(net)
118
+ }
119
+
120
+ salestax.add({ :country => 'US', :state => 'NY', :type => 'reduced' }, under110)
121
+
122
+ puts "Default rate: #{salestax.find({}).call(99)}"
123
+
124
+ puts "Standard rate in Ireland on E99: #{salestax.find({country:'IE'}).call(99)}"
125
+
126
+ puts "Food rate in Ireland on E99: #{salestax.find({country:'IE',type:'food'}).call(99)}"
127
+
128
+ puts "Reduced rate in Germany on E99: #{salestax.find({country:'IE',type:'reduced'}).call(99)}"
129
+
130
+ puts "Standard rate in Alabama on $99: #{salestax.find({country:'US',state:'AL'}).call(99)}"
131
+
132
+ puts "Standard rate in Montgomery, Alabama on $99: #{salestax.find({country:'US',state:'AL',city:'Montgomery'}).call(99)}"
133
+
134
+ puts "Reduced rate in New York for clothes on $99: #{salestax.find({country:'US',state:'NY',type:'reduced'}).call(199)}"
135
+
136
+
137
+ # prints:
138
+ # Default rate: 0
139
+ # Standard rate in Ireland on E99: 0.25
140
+ # Food rate in Ireland on E99: 0.048
141
+ # Reduced rate in Germany on E99: 0.135
142
+ # Standard rate in Alabama on $99: 0.04
143
+ # Standard rate in Montgomery, Alabama on $99: 0.1
144
+ # Reduced rate in New York for clothes on $99: 0.0
145
+ ```
146
+
147
+ You can take a look a the decision tree at any time:
148
+
149
+ ```Ruby
150
+
151
+ # print out patterns, using a custom format function
152
+ puts salestax.toString( Proc.new { | f | ":#{f.call(99)}"})
153
+
154
+
155
+ # prints:
156
+ -> :0.0
157
+ city=Montgomery, country=US, state=AL -> :0.1
158
+ country=IE -> :0.25
159
+ country=IE, type=reduced -> :0.135
160
+ country=IE, type=food -> :0.048
161
+ country=UK -> :0.2
162
+ country=UK, type=food -> :0.0
163
+ country=DE -> :0.19
164
+ country=DE, type=reduced -> :0.07
165
+ country=US -> :0.0
166
+ country=US, state=AL -> :0.04
167
+ country=US, state=NY -> :0.07
168
+ country=US, state=NY, type=reduced -> :0.0
169
+ ```
170
+
171
+
172
+ # The Rules
173
+
174
+ * 1: More specific matches beat less specific matches. That is, more property values beat fewer.
175
+ * 2: Property names are checked in alphabetical order.
176
+
177
+ And that's it.
178
+
179
+
180
+ # Customization
181
+
182
+ You can customize the way that data is stored. For example, you might want to add a constant property to each pattern.
183
+
184
+ To do this, you provide a custom function when you create the _patrun_ object:
185
+
186
+ ```Ruby
187
+ alwaysAddFoo = Patrun.new( Proc.new{ | pm, pat, data |
188
+ pat['foo'] = true
189
+ })
190
+
191
+ alwaysAddFoo.add( {:a => 1}, "bar" )
192
+
193
+ alwaysAddFoo.find( {:a => 1} ) # nothing!
194
+ alwaysAddFoo.find( {:a => 1, :foo => true} ) # == "bar"
195
+ ```
196
+
197
+ Your custom function can also return a modifer function for found
198
+ data, and optionally a modifier for removing data.
199
+
200
+ Here's an example that modifies found data:
201
+
202
+ ```Ruby
203
+ upperify = Patrun.new( Proc.new { | pm, pat, data |
204
+ Proc.new { | pm, pat, data |
205
+ data.to_s.upcase()
206
+ }
207
+ })
208
+
209
+ upperify.add( {:a => 1}, "bar" )
210
+
211
+ upperify.find( {:a => 1} ) # BAR
212
+ ```
213
+
214
+ Finally, here's an example that allows you to add multiple matches for a given pattern:
215
+
216
+ ```Ruby
217
+ many = Patrun.new( Proc.new { | pm, pat, data |
218
+ items = pm.find(pat,true) || []
219
+ items.push(data)
220
+
221
+ {:find => Proc.new { | pm, args, data |
222
+ 0 < items.length ? items : nil
223
+ },
224
+ :remove => Proc.new { | pm, args, data |
225
+ items.pop()
226
+ 0 == items.length
227
+ }
228
+ }
229
+ })
230
+
231
+ many.add( {:a => 1}, 'A' )
232
+ many.add( {:a => 1}, 'B' )
233
+ many.add( {:b => 1}, 'C' )
234
+
235
+ many.find( {:a => 1} ) # [ 'A', 'B' ]
236
+ many.find( {:b => 1} ) # [ 'C' ]
237
+
238
+ many.remove( {:a => 1} )
239
+ many.find( {:a => 1} ) # [ 'A' ]
240
+
241
+ many.remove( {:b => 1} )
242
+ many.find( {:b => 1} ) # nil
243
+ ```
244
+
245
+
246
+ # API
247
+
248
+ ## patrun( custom )
249
+
250
+ Generates a new pattern matcher instance. Optionally provide a customisation Proc.
251
+
252
+
253
+ ## .add( {...pattern...}, object )
254
+
255
+ Register a pattern, and the object that will be returned if an input
256
+ matches. Both keys and values are considered to be strings. Other
257
+ types are converted to strings.
258
+
259
+ ## .find( {...subject...}[, exact] )
260
+
261
+ Return the unique match for this subject, or nil if not found. The
262
+ properties of the subject are matched against the patterns previously
263
+ added, and the most specifc pattern wins. Unknown properties in the
264
+ subject are ignored. You can optionally provide a second boolean
265
+ parameter, _exact_. If true, then all properties of the subject must
266
+ match.
267
+
268
+
269
+ ## .list( {...pattern-partial...}[, exact] )
270
+
271
+ Return the list of registered patterns that contain this partial
272
+ pattern. You can use wildcards for property values. Omitted values
273
+ are *not* equivalent to a wildcard of _"*"_, you must specify each
274
+ property explicitly. You can optionally provide a second boolean
275
+ parameter, _exact_. If true, then only those patterns matching the
276
+ pattern-partial exactly are returned.
277
+
278
+ ```Ruby
279
+ pm = Patrun.new()
280
+ .add({:a => 1, :b => 1},'B1')
281
+ .add({:a => 1, :b => 2},'B2')
282
+
283
+ # finds nothing: []
284
+ puts pm.list({:a => 1})
285
+
286
+ # finds:
287
+ # [ { match: { :a => '1', :b => '1' }, :data => 'B1' },
288
+ # { match: { :a => '1', :b => '2' }, :data => 'B2' } ]
289
+ puts pm.list({:a => 1, :b => '*'})
290
+ ```
291
+
292
+ If you provide no pattern argument at all, _list_ will list all patterns that have been added.
293
+ ```Ruby
294
+ # finds everything
295
+ puts pm.list()
296
+ ```
297
+
298
+ ## .remove( {...pattern...} )
299
+
300
+ Remove this pattern, and it's object, from the matcher.
301
+
302
+
303
+ ## .toString( [proc][, tree] )
304
+
305
+ Generate a string representation of the decision tree for debugging. Optionally provide a formatting Proc for objects.
306
+
307
+ * proc: format proc for data, optional
308
+ * tree: boolean flag, if true, print an indented tree rather than a list of patterns, default: false
309
+
310
+ ## .toJSON()
311
+
312
+ Generate JSON representation of the tree.
313
+
314
+
315
+ # Development
316
+
317
+ From the Irish patr&uacute;n: [pattern](http://www.focloir.ie/en/dictionary/ei/pattern). Pronounced _pah-troon_.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: patrun
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Colm Harte
@@ -14,15 +14,24 @@ description: A fast pattern matcher on Ruby object properties.
14
14
  email: colm.harte@nearform.com
15
15
  executables: []
16
16
  extensions: []
17
- extra_rdoc_files: []
17
+ extra_rdoc_files:
18
+ - README.md
19
+ - LICENSE
18
20
  files:
19
21
  - lib/patrun.rb
20
- homepage: http://rubygems.org/gems/patrun
22
+ - README.md
23
+ - LICENSE
24
+ homepage: https://github.com/colmharte/patrun-ruby
21
25
  licenses:
22
26
  - MIT
23
27
  metadata: {}
24
28
  post_install_message:
25
- rdoc_options: []
29
+ rdoc_options:
30
+ - --main
31
+ - README.md
32
+ - --title
33
+ - Fast Pattern Matcher
34
+ - --line-numbers
26
35
  require_paths:
27
36
  - lib
28
37
  required_ruby_version: !ruby/object:Gem::Requirement