mobj 1.6.9 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. data/README.md +157 -1
  2. data/lib/mobj.rb +31 -6
  3. metadata +1 -1
data/README.md CHANGED
@@ -3,14 +3,170 @@ mobj
3
3
 
4
4
  Some utilities that I keep using.
5
5
 
6
+ Of particular note may be the string tokenizer/path walker.
7
+
6
8
  Example
7
9
  ===
8
10
 
9
11
  Read the code.
10
12
 
13
+ No, really. Give me some examples, dork.
14
+ ===
15
+
16
+ Fine.
17
+
18
+ ### Toking Up
19
+
20
+ This is really the most useful part of the entire library, when you get right down to it.
21
+ String is given a method called "tokenize" that allows it's contents to be split up into
22
+ a structured path of tokens. This tokenized path can then be used to do some pretty amazing
23
+ things in terms of walking through arbitrarily complex trees of data.
24
+
25
+ For example, given this object:
26
+
27
+ ```ruby
28
+ obj = {
29
+ name: { first: "Joe", last: "Smith" },
30
+ ids: [ 1, 3, 5, 16, 941, 13, 100, 3, 0, 104 ],
31
+ auth_tokens: [ { provider: { name: "example.com", id:123 }, token: { auth: "123456", expire: "10-20-2012" } },
32
+ { provider: { name: "site.com", id:265 }, token: { authentication_token: "891011", date: "10-20-2013" } }
33
+ ],
34
+ primary_key: { path: "auth_tokens.provider" }
35
+ }
36
+ ```
37
+
38
+ ... Easily traverse it's data using rules embedded into a simple string, like so:
39
+
40
+ ```ruby
41
+ # Walk a simple object path:
42
+ "name.first".walk(obj)
43
+ #=> "Joe"
44
+
45
+ # Select multiple items out of an object:
46
+ "name.first,last".walk(obj)
47
+ #=> ["Joe", "Smith"]
48
+
49
+ # Or indexes (and ranges) in an array:
50
+ "ids[1, 3, 5 - 7, 9+]".walk(obj)
51
+ #=> [3, 16, 13, 100, 3, 104]
52
+
53
+ # Use regular expressions or method calls as selection keys:
54
+ "auth_tokens.token./^auth/.*to_i".walk(obj)
55
+ #=> [ 123456, 891011 ]
56
+
57
+ # Choose the first element that doesn't return nil:
58
+ "auth_tokens.token.expire|date".walk(obj)
59
+ #=> [ "10-20-2012", "10-20-2013" ]
60
+
61
+ # Provide default values when everything is nil:
62
+ "/auth/.provider,token.auth|authentication_token|~N/A".walk(obj)
63
+ #=> ["N/A", "N/A", "123456", "891011"]
64
+
65
+ # Even look up keys based on the values in other fields:
66
+ "{{primary_key.path}}.id".walk(obj)
67
+ #=> [123, 265]
68
+
69
+ ```
70
+
71
+ And much, much more, though you'd probably want to check out the tests to see how it really works.
72
+
73
+ ### Much Ado about Nulling
74
+
75
+ Every so often you find yourself in a situation where you want nil to behave like "null"
76
+ (i.e. you want it to silently allow unknown method calls to simply do nothing instead of throwing
77
+ and exception). All classes are now given a "null!" method that alters the behavior of nil for
78
+ the remainder of the line:
79
+
80
+ ```ruby
81
+ obj = FooObject.new
82
+
83
+ if obj.null!.foo.bar.baz
84
+ puts "Acts like normal"
85
+ end
86
+
87
+ obj = nil
88
+
89
+ if obj.null!.foo.bar.baz
90
+ puts "Won't throw and exception and evaluates to nil"
91
+ end
92
+
93
+ if obj.foo.bar.baz
94
+ puts "Will throw an exception as normal"
95
+ end
96
+ ```
97
+
98
+ ### When you just want to be contrary
99
+
100
+ Sometimes you just want to write everything in dot notation for no reason.
101
+ Or maybe you have a reason, and that reason is impressing your co workers with your
102
+ ruby tricks:
103
+
104
+ ```ruby
105
+ if foo && foo.bar
106
+ foo.baz
107
+ else
108
+ foo.biz
109
+ end
110
+ ```
111
+ ... becomes ...
112
+
113
+ ```ruby
114
+ foo.when.bar.then.baz.else.biz
115
+ ```
116
+
117
+ ### Sequestration, HashEx, MatchEx, etc
118
+
119
+ Found myself writing these things sooo many times that I just dumped them into my package of stuff:
120
+
121
+ ```ruby
122
+ foo.compact.size == 1 ? foo.compact.first : foo.compact
123
+ ```
124
+ ... becomes ...
125
+
126
+ ```ruby
127
+ foo.sequester
128
+ ```
129
+
130
+ Hash utils:
131
+
132
+ ```ruby
133
+ hash = { symbol: "sym", "string" => "string", :nilval => nil }
134
+
135
+ hash.symbol == hash[:symbol] == hash[:symbol]
136
+ #=> These are equvilent
137
+
138
+ hash.string == hash[:string] == hash["string"]
139
+ #=> These are also equvilent
140
+
141
+ hash.string?
142
+ #=> true
143
+
144
+ hash.nilval?
145
+ #=> false
146
+
147
+ hash.unknown?
148
+ #=> True
149
+ ```
150
+
151
+ Matching stuff
152
+
153
+ ```ruby
154
+ match = "Joe Bob".match(/(?<first_name>\w+) (?<first_name>)/)
155
+
156
+ match.to_hash
157
+ #=> Creates a hash of named captures
158
+
159
+ match.first_name if match.first_name?
160
+ #=> Or just use them directly as method names
161
+
162
+ "foo,bar,baz".matches(/([^,]+),/) do |match|
163
+ #=> same as "string#scan", but returns the actual MatchData
164
+ end
165
+ ```
166
+
11
167
  ---
12
168
 
13
169
  Caveats and such
14
170
  ===
15
171
 
16
- None. It's prefection.
172
+ All of them.
data/lib/mobj.rb CHANGED
@@ -5,6 +5,8 @@ module Mobj
5
5
  klass = class << self; self end
6
6
  klass.superclass
7
7
  end
8
+ def null!() self end
9
+ def nil!() self end
8
10
  end
9
11
 
10
12
  class ::Fixnum
@@ -244,15 +246,33 @@ module Mobj
244
246
  end
245
247
  elsif path.is_a?(Array)
246
248
  path.map { |pth| obj[pth.sym] }
249
+ elsif path[0] == '*' && obj.respond_to?(path[1..-1].sym)
250
+ obj.__send__(path[1..-1].sym)
251
+ elsif obj.respond_to? :[]
252
+ if obj.is_a?(Hash)
253
+ obj[path.sym]
254
+ else
255
+ obj[path.to_s.to_i] if path.to_s =~ /^\d+$/
256
+ end
247
257
  elsif obj.respond_to?(path.sym)
248
258
  obj.__send__(path.sym)
249
- elsif obj.respond_to? :[]
250
- obj[path.sym]
251
259
  else
252
260
  nil
253
261
  end
254
262
  end
255
263
 
264
+
265
+
266
+ def find(obj, match)
267
+ if obj.is_a?(Array)
268
+ obj.map do |child|
269
+ find(child, match)
270
+ end
271
+ elsif obj.respond_to?(:keys)
272
+ obj.keys.map { |key| key if key.match(match) }.compact.map{|key| obj[key] }
273
+ end
274
+ end
275
+
256
276
  def walk(obj, root = obj)
257
277
  obj, root = Circle.wrap(obj), Circle.wrap(root)
258
278
  val = case @type
@@ -261,7 +281,7 @@ module Mobj
261
281
  when :path
262
282
  extract(obj, @path)
263
283
  when :regex
264
- obj.keys.map { |key| key if key.match(@path) }.compact.map{|key| obj[key] }
284
+ find(obj, @path)
265
285
  when :up
266
286
  if obj.respond_to? :parent
267
287
  obj.__mobj__parent || obj.__mobj__parent
@@ -269,7 +289,11 @@ module Mobj
269
289
  obj.__mobj__parent
270
290
  end
271
291
  when :any
272
- @path.return_first { |token| token.walk(obj, root) }
292
+ if obj.is_a?(Array)
293
+ obj.map { |o| walk(o, root) }
294
+ else
295
+ @path.return_first { |token| token.walk(obj, root) }
296
+ end
273
297
  when :all
274
298
  matches = @path.map { |token| token.walk(obj, root) }
275
299
  matches.compact.size == @path.size ? matches : nil
@@ -277,6 +301,7 @@ module Mobj
277
301
  @path.map { |token| token.walk(obj, root) }
278
302
  when :lookup
279
303
  lookup = @path.walk(obj)
304
+ puts "lookup '#{lookup}':#{obj}:#{@path}"
280
305
  if lookup.is_a?(Array)
281
306
  lookup.flatten.map { |lu| lu.tokenize.walk(root) }.flatten(1)
282
307
  else
@@ -322,7 +347,7 @@ module Mobj
322
347
  lookup = /\{\{(?<lookup>.*?)\}\}/
323
348
  up = /(?<up>\^)/
324
349
  path = /(?<path>[^\.\[]+)/
325
- indexes = /(?<indexes>[\d\+\.,-]+)/
350
+ indexes = /(?<indexes>[\d\+\.,\s-]+)/
326
351
 
327
352
  matcher = /#{lit}|#{regex}|#{lookup}|#{up}|#{path}(?:\[#{indexes}\])?/
328
353
 
@@ -349,7 +374,7 @@ module Mobj
349
374
 
350
375
  unless ands.size + ors.size + eachs.size > 3
351
376
  options = {}
352
- index_matcher = /(?<low>\d+)(?:(?:\.\.(?<ex>\.)?|-?)(?<high>-?\d+|\+))?/
377
+ index_matcher = /\s*(?<low>\d+)\s*(?:(?:\.\s*\.\s*(?<ex>\.)?\s*|-?)\s*(?<high>-?\d+|\+))?\s*/
353
378
 
354
379
  options[:indexes] = match.indexes.matches(index_matcher).map do |index|
355
380
  if index.high?
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mobj
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.9
4
+ version: 1.7.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: