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 +4 -4
- data/README.md +284 -40
- data/lib/blockify.rb +232 -6
- data/lib/blockify/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4b73edc9fd1a25fe3131053307024a16de9f9a92
|
|
4
|
+
data.tar.gz: cf99a22f3fcd8a06c7ed740ba78f528b6afdda76
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
89
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
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
|
-
|
|
247
|
+
### instance.extractify(cls)
|
|
107
248
|
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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.
|
data/lib/blockify.rb
CHANGED
|
@@ -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.
|
|
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
|
+
|
data/lib/blockify/version.rb
CHANGED
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.
|
|
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:
|
|
11
|
+
date: 2018-01-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|