blockify 0.1.0 → 0.2.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8af64338fc8a469e61f4286ef0703d40d3368365
4
- data.tar.gz: 667d48278b272ed5910fb3fe8afc777d13bcc2c9
3
+ metadata.gz: 4b73edc9fd1a25fe3131053307024a16de9f9a92
4
+ data.tar.gz: cf99a22f3fcd8a06c7ed740ba78f528b6afdda76
5
5
  SHA512:
6
- metadata.gz: f6b6f7afec83d58d134b771598b9cb69d9424842d9b1f5eaa7038dfd765757186127843300c287cfbb39dc6c1015337525281ae155473e40b4d0af9eac76ee13
7
- data.tar.gz: e06d7a2a90c69ab54f032da75392a260abc49b34d61a139227a9e6a60404c2483d99f3c0506e42915d8f1bc40470f11ccc38c66b8f80112a6334bb20a2fe2514
6
+ metadata.gz: d7d64123af21e5470a94613e56aa319e1fc096702a63dd0a4a7ddcebf638ec76b80745c9abf189adda392c205314afae7cf16874d8544b01b1329f7981870158
7
+ data.tar.gz: 51a82f92c05408e016ab9a311e53297298b061e52d604c3a1a5078caba631ab9eed679bb651807a5fc2d63ced5271600802895247437fcab0605c84493b240d6
data/README.md CHANGED
@@ -24,8 +24,8 @@ Or install it yourself as:
24
24
 
25
25
  ## Usage
26
26
 
27
- This first release includes a module that inserts itself with the Array and Hash classes.
28
- This same module could be used to service custom container classes as well so long as they look Array-like or Hash-like.
27
+ Blockify includes a module that inserts itself with the Array and Hash classes.
28
+ This same module could be used to service custom container classes as well, so long as they look Array-like or Hash-like.
29
29
  As a practical point of view, examine the following code:
30
30
 
31
31
  ```ruby
@@ -46,9 +46,53 @@ The `blockify` gem uses recursion to step through the entire structure. If we pe
46
46
  ```
47
47
  The `#blockify_elements` method travels through the structure and calls our block whenever it encounters an element that is not an Array or a Hash.
48
48
  Whatever the block returns, the element in question will be modified with that result. In this case, each element is converted to a string.
49
- All the recursion magic is performed by the gem.
49
+ All the recursion magic is performed by the gem. There are many other tools as well as will be detailed in the following sections.
50
50
 
51
- Another tool is an element locator. This is best shown by example as follows:
51
+ ### instance.blockify_elements { |element| expression_to_replace_element }
52
+ The `#blockify` method is included in both Hash and Array. This method returns an identical structure of nested Hash and Array elements
53
+ and allows each element to be modified on an element by element basis.
54
+ The following example searches the structure and replaces nil elements with empty strings:
55
+
56
+ ```ruby
57
+ require 'blockify'
58
+
59
+ my_structure = [0,nil,nil,"string",{a:nil, b:"a string", c:[nil,nil,"good",nil,"bad"]}]
60
+ new_structure = my_structure.blockify_elements { |elm| elm.nil? ? "" : elm }
61
+ # new_structure == [0, "", "", "string", {:a=>"", :b=>"a string", :c=>["", "", "good", "", "bad"]}]
62
+ ```
63
+
64
+ ### instance.blockify_elements! { |element| expression_to_replace_element }
65
+ This method works identically to `#blockify` except it self-modifies. It also returns what it changes.
66
+
67
+ ### instance.scan_elements { |key,obj| statements }
68
+ This method scans the entire Hash-Array structure and provides a means of examining elements in each substructure.
69
+ Unlike the `#blockify` method, `#scan_elements` does not attempt to create a new structure;
70
+ instead, you have the opportunity to either read, or update each element in place.
71
+
72
+ In the passed block, the parameter `key` is either the `Hash` key or the `Array` index depending on what type `obj` happens to be.
73
+ The expression `obj[key]` gives us the element. We could also update the element by assignment such as: `obj[key]=expr`.
74
+ We can also find out what type our sub-structure currently is with: `obj.class`.
75
+ In our block, we do not know and don't care about the structure; instead, we just want to work with each element in the current substructure.
76
+ We can modify the element in place or extract its content into some external object, or a combination of the two.
77
+ Although we have access to the sub-structure, we should not delete or add elements on this sub-structure as unexpected things might happen.
78
+ We could implement a self-modifying version of `stringify_elements` using `scan_elements` this way:
79
+
80
+ ```ruby
81
+ require 'blockify'
82
+
83
+ my_structure = [0,1,[{a:{a:88}},[6,{a:[1,2,3]},[4,5],4],3],2,1]
84
+ my_structure.scan_elements { |key,obj| obj[key] = obj[key].to_s }
85
+ # my_structure == ["0", "1", [{:a=>{:a=>"88"}}, ["6", {:a=>["1", "2", "3"]}, ["4", "5"], "4"], "3"], "2", "1"]
86
+ ```
87
+
88
+ If we wanted to create a non-destructive version of `stringify` using the `#scan_elements` method, we would first have to duplicate the structure
89
+ and return the modified duplicate.
90
+
91
+ ### instance.find_element_path { |elm| boolean_statement }
92
+
93
+ This method returns the location path of the first occurrence of when the block returns true.
94
+ A path is defined as a sequence of indexes that access the underlying Hash-Array structure.
95
+ This is best seen by example as follows:
52
96
 
53
97
  ```ruby
54
98
  require 'blockify'
@@ -65,12 +109,14 @@ old = nested_thingee.path_put "Fred" path # old == 17
65
109
  elm = nested_thingee.path_get path # elm =="Fred"
66
110
  # nested_thingee == [0,1,2,[3,4,5,[6,7,8,[9,10,11,12,[13,{:a=>[16,"Fred"],:b=>[18,19,20]}]]]],14,15]
67
111
  ```
112
+
68
113
  It is up to your imagination what you put in the block. When the block returns true, the recursion is done, and you get the path.
69
- Each method in the `blockify` gem is imported into both `Hash` and `Array`.
70
- The gem also includes three access methods that utilize the returned path. The above example shows two of them called `#path_put` and `#path_get`.
71
- Instead of searching for the first find, we have a means of finding every match as well; this is called: `#find_element_paths`.
72
- This methods returns an array of paths where each path is an array of indicies.
73
- We can access everything with `#paths_get` which will return an array of every found element.
114
+ As a spoiler alert, we see two path related access methods which will be explained later.
115
+
116
+ ### instance.find_element_paths { |elm| boolean_statement }
117
+
118
+ This method works similarly as `#find_element_path` except that an array of paths are found.
119
+ Instead of stopping on the first find, the search considers the entire structure.
74
120
  See the example below:
75
121
 
76
122
  ```ruby
@@ -82,56 +128,254 @@ paths.first # [3,0]
82
128
  paths.last # [5]
83
129
  paths[5] # [3, 3, 3, 0]
84
130
  nested_thingee.path_get paths[5] # 9
131
+
132
+ # access elements from all paths that match the criteria:
85
133
  nested_thingee.paths_get paths # [13, 15, 6, 7, 8, 9, 10, 11, 12, 14, 5]
86
134
  ```
87
135
 
88
- One of the methods of of the `blockify` gem utilizes the `#find_element_path` is called `#includify?`.
89
- This is defined as follows:
136
+ And another spoiler alert from the above example comprises the `#paths_get` method.
137
+
138
+ ### instance.path_get(index_list)
139
+
140
+ This method is a short-handed way of accessing the Hash-Array structure with a list of indexes rather than a chain of access operators.
141
+ This is typically used to access an element from the `#find_element_path` method. It can also be used to return part of the structure itself.
142
+ If we pass an empty array, we get the entire structure. The `index_list` is simply an array of indexes where an index is either an Integer, or a Hash key.
143
+
144
+ ### instance.path_put(data, index_list)
145
+
146
+ This method replaces the element found at the `#index_list` with new data. This could also be used to replace the Hash-Array substructure with
147
+ either data, or another substructure.
148
+ Unlike `#path_get` passing an empty array as the index_list is not legal.
149
+
150
+ ### instance.paths_get(index_lists)
151
+
152
+ This method returns an element or a sub-structure for each `index_list`.
153
+ The parameter `index_lists` is defined as an array of path indexes. Structurally, this is an array of an array of indexes.
154
+ It is typically used in conjunction with the `#find_element_paths` method. See the example below:
155
+
156
+ ```ruby
157
+ nested_thingee = [0,1,2,[13,4,15,[6,7,8,[9,10,11,12,[3,{:a=>[16,17],:b=>[18,19,20]}]]]],14,5]
158
+ index_lists = []
159
+ index_lists.push [3,0]
160
+ index_lists.push [5]
161
+ index_lists.push [3, 3, 3, 0]
162
+ index_lists.push [3, 3, 3, 4, 1, :b, 2]
163
+ data = nested_thingee.paths_get index_lists # data == [13, 5, 9, 20]
164
+ ```
165
+
166
+ ### instance.circular?(path=[])
167
+
168
+ A circular Hash-Array structure contains paths that point back to itself in an never-ending loop.
169
+ Using such a structure with some of the methods above will cause a stack overload to occur.
170
+ This method detects such anomalies. You can also get the first violation path by passing an array.
171
+ If there are multiple violations, you must remove all violations one-by-one before calling
172
+ one of the recursive methods above. The example below demonstrates this as follows:
90
173
 
91
174
  ```ruby
92
- def includify?(search_string)
93
- path = find_element_path { |elm| elm.to_s.include? search_string }
94
- !path.empty?
95
- end
175
+
176
+ abc = [:a, :b, :c]
177
+ xyz = [1,2,3]
178
+ example = [0, {first: abc, second: xyz }]
179
+
180
+ example.circular? # false
181
+
182
+ abc.push example # create a circular structure
183
+
184
+ example.circular? # true
185
+ abc.circular? # true
186
+
187
+ path = []
188
+ abc.circular? path # true ... path == [3, 1]
189
+ path = []
190
+ example.circular? path # true ... path == [1, :first, 3]
191
+
192
+ xyz.push abc # now a second violation
193
+ xyz.circular? # true
194
+ path = []
195
+ xyz.circular? path # true ... path == [3, 3, 1]
196
+
197
+ example[0] = [[xyz,abc]] # third violation
198
+ path = []
199
+ example.circular? path # true ... path == [3, 3, 0, 0, 0]
96
200
  ```
97
201
 
98
- When you have a deeply nested array_hash system, the standard `#include?` method is no help.
99
- Instead of looking one-level deep for an exact match, `#includify?` examines each element
100
- until it finds what it is looking for. Internally, we are looking for a substring on one of the elements.
202
+ ### instance.unloopify
203
+
204
+ This method repeatedly calls `#circular?` and replaces the offending substructure with a container object
205
+ that sort of looks the the offending substructure. This container is called `CircularObjectContainer`.
206
+ If the contained element happens to be a `Hash` object, then the container will appear `Hash` like
207
+ and will include the methods such as `#each_pair` and `#each`. The `Array` like instance will implement the method `#each`.
208
+ All instances of `CircularObjectContainer` are frozen read-only entities. All container elements of a `CircularObjectContainer`
209
+ instance will be empty; this effectively breaks the loop. Also, the `#[]` access operator is available, but `#[]=` is forbidden.
210
+ This object is detailed later in this document.
211
+
212
+ ### instance.stringify_elements
213
+
214
+ This method creates a new structure with every element converted to strings. It is the same as: `instance.blockify_elements {|elm| elm.to_s}`.
215
+
216
+ ### instance.stringify_elements!
217
+
218
+ This bang version of `#stringify_elements` self-modifies the current structure; additionally, it returns the modified structure.
219
+
220
+ ### instance.inspectify_elements
221
+
222
+ This method creates a new structure with every element replaced with `#inspect` called on that element.
223
+ It is the same as: `instance.blockify_elements {|elm| elm.inspect}`.
224
+
225
+ ### instance.inspectify_elements!
226
+
227
+ This is a self-modifying version of `#inspectify_elements` which also returns the modified self.
228
+
229
+ ### instance.deep_duplify
230
+
231
+ This creates an identical deep copy of the original Hash-Array structure where each sub-structure is reconstructed from a new Hash or Array.
232
+ Each element in the structure additionally calls `#dup` if permitted.
233
+ This will covert internal `CircularObjectContainer` objects back into Hash or Array objects.
234
+ It is a good idea to call this method after you call `unloopify` because `CircularObjectContainer` objects cannot be modified.
235
+ This method internally calls `#blockify_elements`.
236
+
237
+ ### instance.flattenify
238
+
239
+ This method extracts every element from the Hash-Array structure and places each element in a one-level deep array.
101
240
  See the example below:
102
241
 
103
242
  ```ruby
104
- require 'blockify'
243
+ structure = ["zero",{m:[{t:["one", "two"],v:["three","four"]}],z:"five"}]
244
+ flat = structure.flattenify # flat == ["zero", "one", "two", "three", "four", "five"]
245
+ ```
105
246
 
106
- nasty_array_hash = [{:quick=>{:a=>"tool", :b=>["xray","tent"]}, :quicker=>{:a=>:mess}, "green"=>"fox in socks", :empty=>[{}]},"fred"]
247
+ ### instance.extractify(cls)
107
248
 
108
- nasty_array_hash.includify? "tool" # true
109
- nasty_array_hash.includify? "mess" # true
110
- nasty_array_hash.includify? "quick" # false, :quick is a key, not an element
111
- nasty_array_hash.includify? "green" # false, "green" is a key, not an element
112
- nasty_array_hash.includify? "fox" # true, "fox" is a substring of "fox in socks"
113
- ep = nasty_array_hash.find_element_path { |elm| elm.to_s.include? "fox"} # [0, "green"]
114
- str = nasty_array_hash.path_get ep # str == "fox in socks"
249
+ This method extracts elements that belong to the type of its parent container; the parameter `cls` is either `Array` or `Hash`.
250
+ See the example below:
115
251
 
252
+ ```ruby
253
+ structure = [1,3,5,{a:2,b:4,c:6,d:[55,777],e:8},{t:[999],a:"even"}]
254
+ even = structure.extractify(Hash) # even == {:a=>"even", :b=>4, :c=>6, :e=>8}
255
+ odd = structure.extractify(Array) # odd == [999, 777, 5]
116
256
  ```
117
257
 
118
- The current methods for this first release are as follows:
258
+ Note that if a Hash key is repeated, or an Array index is repeated, the returned element will replace previous elements at that key or that index.
259
+ The returned structure is always flat.
260
+
261
+ ### instance.extractifirstify(cls)
262
+
263
+ Unlike `#extractify` which returns the last found element in the returned flatten structure,
264
+ `#extractifirstify` fills the return structure with the first found element. See the example below:
119
265
 
120
266
  ```ruby
121
- # new_array_hash = ary_hash.blockify_elements { |elm| some_expression_using_elm_that_gets_saved_to_elm }
122
- # ary_hash.blockify_elements! { your_block_here } # ... self-modified ary_hash
123
- # path_array = ary_hash.find_element_path { |elm| some_true_false_method_here_operating_on_elm }
124
- # result = ary_hash.stringify_elements # creates same structure with elements converted to strings
125
- # ary_hash.stringify_elements! # self-modified version of the above
126
- # result = ary_hash.inspectify_elements # all elements replaced with a call to #inspect
127
- # ary_hash.inspectify_elements! # self-modified version of the above
128
- # ary_hash.includify? substring # true if substring is found in one of the elements
129
- # ary_hash.find_element_paths { ... } # returns array of found paths
130
- # ary_hash.path_get path_array # returns the element or structure found by the path array ... path_get([]) returns the entire structure
131
- # ary_hash.paths_get array_paths # paths looks like this: [[],[],[],[],...] returns an array of each path_get
132
- # ary_hash.path_put(data, path) # replaces current path element with data, where path is an ordered list of keys
267
+ structure = [1,3,5,{a:2,b:4,c:6,d:[55,777],e:8},{t:[999],a:"even"}]
268
+ even = structure.extractifirstify(Hash) # even == {:a=>2, :b=>4, :c=>6, :e=>8}
269
+ odd = structure.extractifirstify(Array) # odd == [1, 3, 5]
133
270
  ```
134
271
 
272
+ ### instance.extractistacktify(cls)
273
+
274
+ This method returns a two-level structure and places all compatible data into the second-level structure.
275
+ The secondary structure is always an array. The primary structure is either `Array` or `Hash` depending on the parameter `cls`
276
+ This is best seen by example as follows:
277
+
278
+ ```ruby
279
+ structure = [1,3,5,{a:2,b:4,c:6,d:[55,777],e:8},{t:[999],a:"even"}]
280
+ even = structure.extractistacktify(Hash) # even == {:a=>[2, "even"], :b=>[4], :c=>[6], :e=>[8]}
281
+ odd = structure.extractistacktify(Array) # odd == [[1, 55, 999], [3, 777], [5]]
282
+ ```
283
+
284
+ From the example above, we have effectively captured and reorganized all the elements into two bins.
285
+
286
+ ### instance.includify?(search_string)
287
+ This method searches the Hash-Array structure's elements for a matching substring.
288
+ The element is first converted to a string before examining the match.
289
+ See the example below:
290
+
291
+ ```ruby
292
+ haystack = [555,"123",{a:"apple", b:"banana", c:[567,"needle",:fred]}]
293
+ haystack.includify? "needle" # true
294
+ haystack.includify? "need" # true
295
+ haystack.includify? "6" # true
296
+ haystack.includify? "red" # true
297
+ haystack.includify? "24" # false
298
+ haystack.includify? ":" # false ... :fred.to_s == "fred"
299
+ ```
300
+
301
+ ### CircularObjectContainer
302
+
303
+ CircularObjectContainer instances are created automatically within the Hash-Array structure when `#unloopify` encounters a circular construct.
304
+ This object is further detailed the the following subsections:
305
+
306
+ #### construction
307
+
308
+ CircularObjectContainer can contain any object, but its purpose is to contain Hash-like or Array-like entities.
309
+ It takes this form: `CircularObjectContainer.new(item_to_be_contained)`.
310
+ The constructor then exposes methods such as `#each`, `#each_pair`, and `#[]` depending on the contained type.
311
+ The internal object is not saved, but instead saves a reconstructed version of the original. Only the original `object_id` is preserved.
312
+ Non-container objects will use the original if `#dup` is not permitted. The internal reconstructed object calls `#freeze` as well as the
313
+ newly constructed returned object.
314
+
315
+ #### [] array access postfix operator
316
+
317
+ This method is added as a singleton method if and only if the contained entity is Hash-like or Array-like.
318
+ Note that `:[]=` is never implemented. This method calls the internal access operator of the contained object.
319
+
320
+ #### each
321
+ This method is added as a singleton method if and only if the contained entity implements `#each`.
322
+ This method calls the contained object's `#each` method.
323
+
324
+ #### each_pair
325
+ This method is added as a singleton method if and only if the contained entity implements `#each_pair`.
326
+ This method calls the contained object's `#each_pair` method.
327
+
328
+ #### id
329
+
330
+ This method returns the original `object_id` during construction.
331
+
332
+ #### type
333
+
334
+ This method returns the class of the contained reconstructed entity.
335
+
336
+ #### dup
337
+
338
+ Instead of duplicating a CircularObjectContainer instance, the internal element is duplicated and returned.
339
+ This will most likely be a Hash or an Array.
340
+
341
+ #### taboo?
342
+
343
+ This always returns true.
344
+
345
+ #### def circular?
346
+
347
+ This returns false and allows `CircularObjectContainer` objects to be used within a structure and still be able to call `#circular?`.
348
+
349
+ #### blockify_elements
350
+
351
+ This calls `#blockify_elements` from the contained entity.
352
+
353
+ #### find_element_path
354
+
355
+ This calls `#find_element_path` from the contained entity.
356
+
357
+ #### find_element_paths
358
+
359
+ This calls `#find_element_paths` from the contained entity.
360
+
361
+ #### scan_elements
362
+
363
+ This calls `#scan_elements` from the contained entity.
364
+
365
+ ### Incidental Methods Added to Standard Objects
366
+ The main class `Object` has the method `#taboo?` added which returns false. The object `Hash` has two methods added:
367
+ `incrementify(key_name)` and `decrementify(key_name)`. These add or subtract the `Integer` `1` to the indexed element.
368
+
369
+ ### Adding blockify to custom objects
370
+ If your custom object is Hash-like, or Array-like, it can have the methods of `blockify` included. The following attributes must be present:
371
+
372
+ 1. Must be able to be constructed as a blank-slate (`#new` without parameters).
373
+ 2. Must implement `:[]` and `:[]=` access operators.
374
+ 3. Must implement `#each`
375
+ 4. If `#each_pair` is implemented, `#each` must behave the way `Hash` behaves.
376
+ 5. If array-like (`#each_pair` not implemented), must implement `#push` and `#pop`
377
+ 6. Call this: `MyCustomClass.include Blockify`
378
+
135
379
  ## Development
136
380
 
137
381
  I need to control this for the time being, so stay tuned! I will add more goodies in later releases.
@@ -32,13 +32,13 @@ module Blockify
32
32
  if val.respond_to? :each
33
33
  path.push key
34
34
  val.find_element_path(path, done, &block)
35
- return path if done.first
35
+ return path.self_or_empty_as_nil if done.first
36
36
  path.pop
37
37
  else
38
38
  if block.call(val)
39
39
  path.push key
40
40
  done[0]=true
41
- return path
41
+ return path.self_or_empty_as_nil
42
42
  end
43
43
  end
44
44
  end
@@ -48,19 +48,19 @@ module Blockify
48
48
  if val.respond_to? :each
49
49
  path.push idx
50
50
  val.find_element_path(path, done, &block)
51
- return path if done.first
51
+ return path.self_or_empty_as_nil if done.first
52
52
  path.pop
53
53
  else
54
54
  if block.call(val)
55
55
  path.push idx
56
56
  done[0]=true
57
- return path
57
+ return path.self_or_empty_as_nil
58
58
  end
59
59
  end
60
60
  idx += 1
61
61
  end
62
62
  end # if-else
63
- return path
63
+ return path.self_or_empty_as_nil
64
64
  end
65
65
 
66
66
  def find_element_paths(path=[],paths=[], &block)
@@ -123,9 +123,119 @@ module Blockify
123
123
  return rtn
124
124
  end
125
125
 
126
+ # read elements and yield everything
127
+ def scan_elements(&block)
128
+ if self.respond_to? :each_pair
129
+ self.each_pair do |key,val|
130
+ if val.respond_to? :each
131
+ val.scan_elements(&block)
132
+ else
133
+ block.call(key,self)
134
+ end
135
+ end
136
+ elsif self.respond_to? :each
137
+ ii = 0
138
+ self.each do |val|
139
+ if val.respond_to? :each
140
+ val.scan_elements(&block)
141
+ else
142
+ block.call(ii,self)
143
+ end
144
+ ii += 1
145
+ end
146
+ end
147
+ end
148
+
149
+ # add path later to see where things break down
150
+ def circular? (path = [], stack = {}, rtn = [false])
151
+ my_id = self.object_id
152
+ stack.incrementify(my_id)
153
+ if self.respond_to? :each_pair
154
+ self.each_pair do |key,val|
155
+ if val.respond_to? :each
156
+ x_id = val.object_id
157
+ if stack[x_id]
158
+ rtn[0]=true
159
+ return rtn.first
160
+ else
161
+ path.push key
162
+ return rtn.first if val.circular?(path, stack, rtn)
163
+ path.pop
164
+ end
165
+ end
166
+ end
167
+ elsif self.respond_to? :each
168
+ ii = 0
169
+ self.each do |val|
170
+ if val.respond_to? :each
171
+ x_id = val.object_id
172
+ if stack[x_id]
173
+ rtn[0]=true
174
+ path.push ii
175
+ return rtn.first
176
+ else
177
+ path.push ii
178
+ return rtn.first if val.circular?(path, stack, rtn)
179
+ path.pop
180
+ end
181
+ end
182
+ ii += 1
183
+ end
184
+ end
185
+ stack.decrementify(my_id)
186
+ rtn.first
187
+ end
188
+
189
+ def unloopify
190
+ path = []
191
+ while circular? path do
192
+ obj = path_get path
193
+ nobj = CircularObjectContainer.new obj
194
+ path_put(nobj, path)
195
+ path = []
196
+ end
197
+ end
198
+
199
+ def flattenify
200
+ rtn = []
201
+ scan_elements { |key, inst| rtn.push inst[key] }
202
+ return rtn
203
+ end
204
+
205
+ def extractify(cls)
206
+ col = cls.new
207
+ scan_elements do |key, inst|
208
+ col[key] = inst[key] if (inst.kind_of? cls)
209
+ end
210
+ return col
211
+ end
212
+
213
+ def extractifirstify(cls)
214
+ col = cls.new
215
+ scan_elements do |key, inst|
216
+ if (inst.kind_of? cls)
217
+ if col[key].nil?
218
+ col[key] = inst[key]
219
+ end
220
+ end
221
+ end
222
+ return col
223
+ end
224
+
225
+ def extractistacktify(cls)
226
+ col = cls.new
227
+ scan_elements do |key, inst|
228
+ if (inst.kind_of? cls)
229
+ col[key] ||= []
230
+ col[key].push inst[key]
231
+ end
232
+ end
233
+ return col
234
+ end
235
+
126
236
  def includify?(search_string)
127
237
  path = find_element_path { |elm| elm.to_s.include? search_string }
128
- !path.empty?
238
+ !path.nil?
129
239
  end
130
240
 
131
241
  def blockify_elements!(&block)
@@ -143,8 +253,124 @@ module Blockify
143
253
  def inspectify_elements!
144
254
  replace inspectify_elements
145
255
  end
256
+
257
+ def deep_duplify
258
+ blockify_elements do |t|
259
+ t.dup rescue t
260
+ end
261
+ end
262
+
263
+ def self_or_empty_as_nil
264
+ return nil if empty?
265
+ self
266
+ end
146
267
 
147
268
  end # module
148
269
 
149
270
  Array.include Blockify
150
271
  Hash.include Blockify
272
+
273
+ class Hash
274
+ def incrementify(kv) #values assumed to be integers
275
+ if self[kv].nil?
276
+ self[kv] = 0
277
+ else
278
+ self[kv] += 1
279
+ end
280
+ end
281
+ def decrementify(kv) #values assumed to be integers
282
+ if self[kv].nil?
283
+ self[kv] = nil
284
+ elsif self[kv]==0
285
+ self[kv] = nil
286
+ else
287
+ self[kv] -= 1
288
+ end
289
+ end
290
+ end
291
+
292
+ class Object
293
+ def taboo?
294
+ false
295
+ end
296
+ end
297
+
298
+ class CircularObjectContainer
299
+ def initialize(obj)
300
+ @id = obj.object_id
301
+ if obj.respond_to? :each_pair
302
+ @obj = obj.class.new # create a blank slate
303
+ obj.each_pair do |key,val|
304
+ if val.respond_to? :each
305
+ @obj[key] = val.class.new # go no further
306
+ else
307
+ tval = val.dup rescue val
308
+ @obj[key] = tval
309
+ end
310
+ end
311
+ self.instance_eval do # yes we want a singleton method here!
312
+ def each_pair(&block)
313
+ @obj.each_pair &block
314
+ end
315
+ def each(&block)
316
+ @obj.each &block
317
+ end
318
+ def [](key)
319
+ @obj[key]
320
+ end
321
+ end
322
+ elsif obj.respond_to? :each
323
+ @obj = obj.class.new # create a blank slate
324
+ obj.each do |val|
325
+ if val.respond_to? :each
326
+ @obj.push val.class.new
327
+ else
328
+ tval = val.dup rescue val
329
+ @obj.push tval
330
+ end
331
+ end
332
+ self.instance_eval do # yes we want a singleton method here!
333
+ def each(&block)
334
+ @obj.each &block
335
+ end
336
+ def [](idx)
337
+ @obj[idx]
338
+ end
339
+ end
340
+ else
341
+ @obj = obj.dup rescue obj
342
+ end
343
+ @obj.freeze
344
+ self.freeze
345
+ self
346
+ end
347
+ def id # get the original object_id
348
+ @id
349
+ end
350
+ def type
351
+ @obj.class
352
+ end
353
+ def dup # get the innards and discard container
354
+ @obj.dup rescue @obj
355
+ end
356
+ def taboo?
357
+ true
358
+ end
359
+ def circular? (path = [], stack = {}, rtn = [false])
360
+ return rtn.first
361
+ end
362
+ def blockify_elements(&block)
363
+ @obj.dup.blockify_elements(&block)
364
+ end
365
+ def find_element_path(path=[],done=[false], &block)
366
+ @obj.dup.find_element_path(path,done, &block)
367
+ end
368
+ def find_element_paths(path=[],paths=[], &block)
369
+ @obj.dup.find_element_paths(path,paths, &block)
370
+ end
371
+ def scan_elements(&block)
372
+ @obj.dup.scan_elements(&block)
373
+ end
374
+ end
375
+
376
+
@@ -1,3 +1,3 @@
1
1
  module Blockify
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blockify
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bryan Colvin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-12-27 00:00:00.000000000 Z
11
+ date: 2018-01-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler