abstractivator 0.0.15
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 +7 -0
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/abstractivator.gemspec +25 -0
- data/lib/abstractivator.rb +2 -0
- data/lib/abstractivator/collections.rb +22 -0
- data/lib/abstractivator/cons.rb +58 -0
- data/lib/abstractivator/enumerable_ext.rb +94 -0
- data/lib/abstractivator/proc_ext.rb +52 -0
- data/lib/abstractivator/trees.rb +220 -0
- data/lib/abstractivator/trees/block_collector.rb +34 -0
- data/lib/abstractivator/version.rb +3 -0
- data/lib/enumerable_ext.rb +11 -0
- data/spec/lib/abstractivator/collections_spec.rb +20 -0
- data/spec/lib/abstractivator/cons_spec.rb +48 -0
- data/spec/lib/abstractivator/enumerable_ext_spec.rb +144 -0
- data/spec/lib/abstractivator/proc_ext_spec.rb +76 -0
- data/spec/lib/abstractivator/tree_visitor_spec.rb +320 -0
- data/spec/lib/enumerable_ext_spec.rb +20 -0
- metadata +129 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 47647350eb409b2ff52995a8e4f7349134cebb60
|
4
|
+
data.tar.gz: c2a3f24a61450361a6a8ccd6fc1265005970be91
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9f0c2f1ad23e80a9e3633717456c3338b494b38591b557c58eecab7db713f4cdbc470367dd44d9c7e098d1f35c14e6e1c85e1a41b87ed83952036512343b52be
|
7
|
+
data.tar.gz: 407f9d10c8c0b83ec44ec33375a11cd2d6b1d8369ce664b0041b4dff8a4cf597ca5fcfd58d3dd249de68de8681dba4a86080360cf75caa5e118b4307cd2c6ddf
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
.idea
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Peter Winton
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Abstractivator
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'abstractivator'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install abstractivator
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it ( https://github.com/[my-github-username]/abstractivator/fork )
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'abstractivator/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'abstractivator'
|
8
|
+
spec.version = Abstractivator::VERSION
|
9
|
+
spec.authors = ['Peter Winton']
|
10
|
+
spec.email = ['pwinton@indigobio.com']
|
11
|
+
spec.summary = %q{Utilities}
|
12
|
+
spec.description = %q{Utilities}
|
13
|
+
spec.homepage = ''
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
spec.add_runtime_dependency 'activesupport', '~> 4.0'
|
24
|
+
spec.add_runtime_dependency 'sourcify', '> 0'
|
25
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Abstractivator
|
2
|
+
module Collections
|
3
|
+
def multizip(enumerables, pad_value=nil)
|
4
|
+
es = enumerables.map(&:each)
|
5
|
+
result = []
|
6
|
+
fail_count = 0
|
7
|
+
while fail_count < es.size do
|
8
|
+
fail_count = 0
|
9
|
+
heads = es.map do |e|
|
10
|
+
begin
|
11
|
+
e.next
|
12
|
+
rescue StopIteration
|
13
|
+
fail_count += 1
|
14
|
+
pad_value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
result << heads if fail_count < es.size
|
18
|
+
end
|
19
|
+
result
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Abstractivator
|
2
|
+
module Cons
|
3
|
+
Nil = Object.new
|
4
|
+
|
5
|
+
def empty_list
|
6
|
+
Nil
|
7
|
+
end
|
8
|
+
|
9
|
+
def cons(h, t)
|
10
|
+
Cell.new(h, t)
|
11
|
+
end
|
12
|
+
|
13
|
+
def head(p)
|
14
|
+
p.first
|
15
|
+
end
|
16
|
+
|
17
|
+
def tail(p)
|
18
|
+
p.last
|
19
|
+
end
|
20
|
+
|
21
|
+
def list_to_enum(list)
|
22
|
+
Enumerator.new do |y|
|
23
|
+
while list != Nil
|
24
|
+
y << head(list)
|
25
|
+
list = tail(list)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def enum_to_list(enum)
|
31
|
+
e = enum.reverse.each
|
32
|
+
result = Nil
|
33
|
+
begin
|
34
|
+
while true
|
35
|
+
result = cons(e.next, result)
|
36
|
+
end
|
37
|
+
rescue StopIteration
|
38
|
+
result
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Cell < Array
|
43
|
+
def initialize(h, t)
|
44
|
+
super([h, t])
|
45
|
+
end
|
46
|
+
|
47
|
+
def head
|
48
|
+
first
|
49
|
+
end
|
50
|
+
|
51
|
+
def tail
|
52
|
+
last
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
extend self
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Enumerable
|
4
|
+
|
5
|
+
# joins items from left with items from right based on their keys.
|
6
|
+
# get_{left,right}_key are callables which, given an item, return the item's key.
|
7
|
+
# the defaults are used to form a pair for items which have no match.
|
8
|
+
# returns an array of 2-element arrays, each of which is a left/right pair.
|
9
|
+
def self.outer_join(left, right, get_left_key, get_right_key, left_default, right_default)
|
10
|
+
|
11
|
+
ls = left.hash_map(get_left_key)
|
12
|
+
rs = right.hash_map(get_right_key)
|
13
|
+
|
14
|
+
raise 'duplicate left keys' if ls.size < left.size
|
15
|
+
raise 'duplicate right keys' if rs.size < right.size
|
16
|
+
|
17
|
+
result = []
|
18
|
+
|
19
|
+
ls.each_pair do |k, l|
|
20
|
+
r = rs[k]
|
21
|
+
if r
|
22
|
+
rs.delete(k)
|
23
|
+
else
|
24
|
+
r = get_default(right_default, l)
|
25
|
+
end
|
26
|
+
result.push [l, r]
|
27
|
+
end
|
28
|
+
|
29
|
+
rs.each_pair do |_, r|
|
30
|
+
result.push [get_default(left_default, r), r]
|
31
|
+
end
|
32
|
+
|
33
|
+
result
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.inner_join(left, right, get_left_key, get_right_key)
|
37
|
+
sentinel = Object.new
|
38
|
+
result = self.outer_join(left, right, get_left_key, get_right_key, sentinel, sentinel)
|
39
|
+
result.reject { |pair| pair.first == sentinel || pair.last == sentinel }
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.get_default(default, other_side_value)
|
43
|
+
proc?(default) ? default.(other_side_value) : default
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.proc?(x)
|
47
|
+
x.respond_to?(:call)
|
48
|
+
end
|
49
|
+
|
50
|
+
def hash_map(get_key, &get_value)
|
51
|
+
Hash[self.map{|x| [get_key.(x), get_value ? get_value.(x) : x]}]
|
52
|
+
end
|
53
|
+
|
54
|
+
def outer_join(right, get_left_key, get_right_key, default_value)
|
55
|
+
Enumerable.outer_join(self, right, get_left_key, get_right_key, default_value, default_value)
|
56
|
+
end
|
57
|
+
|
58
|
+
def inner_join(right, get_left_key, get_right_key)
|
59
|
+
Enumerable.inner_join(self, right, get_left_key, get_right_key)
|
60
|
+
end
|
61
|
+
|
62
|
+
def uniq?
|
63
|
+
seen = Set.new
|
64
|
+
each_with_index do |x, i|
|
65
|
+
seen << (block_given? ? yield(x) : x)
|
66
|
+
return false if seen.size < i + 1
|
67
|
+
end
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
orig_detect = instance_method(:detect)
|
72
|
+
define_method :detect do |*args, &block|
|
73
|
+
detect = orig_detect.bind(self)
|
74
|
+
|
75
|
+
if args.size == 1 && !args.first.respond_to?(:call) && block
|
76
|
+
value = args.first
|
77
|
+
detect.call {|x| block.call(x) == value}
|
78
|
+
elsif args.size == 2 && !block
|
79
|
+
attr_name, value = args
|
80
|
+
detect.call {|x| x.send(attr_name) == value}
|
81
|
+
else
|
82
|
+
detect.call(*args, &block)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def inject_right(*args, &block)
|
87
|
+
self.reverse_each.inject(*args, &block) # reverse_each to avoid duplicating the enumerable, when possible
|
88
|
+
end
|
89
|
+
|
90
|
+
def pad_right(n, value=nil, &block)
|
91
|
+
block ||= proc { value }
|
92
|
+
self + ([n-self.size, 0].max).times.map(&block)
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module MethodAndProcExtensions
|
2
|
+
def loosen_args
|
3
|
+
proc do |*args, &block|
|
4
|
+
Proc.loose_call(self, args, &block)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Proc
|
10
|
+
include MethodAndProcExtensions
|
11
|
+
|
12
|
+
def compose(other)
|
13
|
+
proc{|x| self.call(other.call(x))}
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.compose(*procs)
|
17
|
+
procs.inject_right(identity) { |inner, p| p.compose(inner) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.identity
|
21
|
+
proc {|x| x}
|
22
|
+
end
|
23
|
+
|
24
|
+
def reverse_args
|
25
|
+
proc do |*args, &block|
|
26
|
+
self.call(*args.reverse, &block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.loose_call(x, args, &block)
|
31
|
+
x.respond_to?(:call) ? x.call(*args.take(x.arity).pad_right(x.arity), &block) : x
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Method
|
36
|
+
include MethodAndProcExtensions
|
37
|
+
end
|
38
|
+
|
39
|
+
class UnboundMethod
|
40
|
+
def explicit_receiver
|
41
|
+
proc do |receiver, *args, &block|
|
42
|
+
self.bind(receiver).call(*args, &block)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Array
|
48
|
+
def to_proc
|
49
|
+
raise 'size must be exactly one' unless size == 1
|
50
|
+
proc{|x| x[first]}
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
require 'active_support/core_ext/object/deep_dup'
|
2
|
+
require 'abstractivator/trees/block_collector'
|
3
|
+
require 'sourcify'
|
4
|
+
require 'delegate'
|
5
|
+
require 'set'
|
6
|
+
|
7
|
+
module Abstractivator
|
8
|
+
module Trees
|
9
|
+
|
10
|
+
SetMask = Struct.new(:items, :get_key)
|
11
|
+
def set_mask(items, get_key)
|
12
|
+
SetMask.new(items, get_key)
|
13
|
+
end
|
14
|
+
|
15
|
+
def tree_compare(tree, mask, path=[], index=nil)
|
16
|
+
if mask == [:*] && tree.is_a?(Enumerable)
|
17
|
+
[]
|
18
|
+
elsif mask == :+ && tree != :__missing__
|
19
|
+
[]
|
20
|
+
elsif mask == :- && tree != :__missing__
|
21
|
+
[diff(path, tree, :__absent__)]
|
22
|
+
elsif mask.respond_to?(:call)
|
23
|
+
comparable = mask.call(tree)
|
24
|
+
comparable ? [] : [diff(path, tree, mask)]
|
25
|
+
else
|
26
|
+
case mask
|
27
|
+
when Hash
|
28
|
+
if tree.is_a?(Hash)
|
29
|
+
mask.each_pair.flat_map do |k, v|
|
30
|
+
tree_compare(tree.fetch(k, :__missing__), v, push_path(path, k))
|
31
|
+
end
|
32
|
+
else
|
33
|
+
[diff(path, tree, mask)]
|
34
|
+
end
|
35
|
+
when SetMask # must check this before Enumerable because Structs are enumerable
|
36
|
+
if tree.is_a?(Enumerable)
|
37
|
+
# convert the enumerables to hashes, then compare those hashes
|
38
|
+
tree_items = tree
|
39
|
+
mask_items = mask.items.dup
|
40
|
+
get_key = mask.get_key
|
41
|
+
|
42
|
+
be_strict = !mask_items.delete(:*)
|
43
|
+
new_tree = hashify_set(tree_items, get_key)
|
44
|
+
new_mask = hashify_set(mask_items, get_key)
|
45
|
+
tree_keys = Set.new(new_tree.keys)
|
46
|
+
mask_keys = Set.new(new_mask.keys)
|
47
|
+
tree_only = tree_keys - mask_keys
|
48
|
+
|
49
|
+
# report duplicate keys
|
50
|
+
if new_tree.size < tree_items.size
|
51
|
+
diff(path, [:__duplicate_keys__, duplicates(tree_items.map(&get_key))], nil)
|
52
|
+
elsif new_mask.size < mask_items.size
|
53
|
+
diff(path, nil, [:__duplicate_keys__, duplicates(mask_items.map(&get_key))])
|
54
|
+
# hash comparison allows extra values in the tree.
|
55
|
+
# report extra values in the tree unless there was a :* in the mask
|
56
|
+
elsif be_strict && tree_only.any?
|
57
|
+
tree_only.map{|k| diff(push_path(path, k), new_tree[k], :__absent__)}
|
58
|
+
else # compare as hashes
|
59
|
+
tree_compare(new_tree, new_mask, path, index)
|
60
|
+
end
|
61
|
+
else
|
62
|
+
[diff(path, tree, mask.items)]
|
63
|
+
end
|
64
|
+
when Enumerable
|
65
|
+
if tree.is_a?(Enumerable)
|
66
|
+
index ||= 0
|
67
|
+
if !tree.any? && !mask.any?
|
68
|
+
[]
|
69
|
+
elsif !tree.any?
|
70
|
+
[diff(push_path(path, index.to_s), :__missing__, mask)]
|
71
|
+
elsif !mask.any?
|
72
|
+
[diff(push_path(path, index.to_s), tree, :__absent__)]
|
73
|
+
else
|
74
|
+
# if the mask is programmatically generated (unlikely), then
|
75
|
+
# the mask might be really big and this could blow the stack.
|
76
|
+
# don't support this case for now.
|
77
|
+
tree_compare(tree.first, mask.first, push_path(path, index.to_s)) +
|
78
|
+
tree_compare(tree.drop(1), mask.drop(1), path, index + 1)
|
79
|
+
end
|
80
|
+
else
|
81
|
+
[diff(path, tree, mask)]
|
82
|
+
end
|
83
|
+
else
|
84
|
+
tree == mask ? [] : [diff(path, tree, mask)]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def hashify_set(items, get_key)
|
92
|
+
Hash[items.map{|x| [get_key.call(x), x] }]
|
93
|
+
end
|
94
|
+
|
95
|
+
def duplicates(xs)
|
96
|
+
xs.group_by{|x| x}.each_pair.select{|_k, v| v.size > 1}.map(&:first)
|
97
|
+
end
|
98
|
+
|
99
|
+
def push_path(path, name)
|
100
|
+
path + [name]
|
101
|
+
end
|
102
|
+
|
103
|
+
def path_string(path)
|
104
|
+
path.join('/')
|
105
|
+
end
|
106
|
+
|
107
|
+
def diff(path, tree, mask)
|
108
|
+
{path: path_string(path), tree: tree, mask: massage_mask_for_diff(mask)}
|
109
|
+
end
|
110
|
+
|
111
|
+
def massage_mask_for_diff(mask)
|
112
|
+
if mask.respond_to?(:call)
|
113
|
+
massaged = :__predicate__
|
114
|
+
begin
|
115
|
+
massaged = mask.to_source
|
116
|
+
rescue Exception => e
|
117
|
+
raise unless e.class.name.start_with?('Sourcify')
|
118
|
+
end
|
119
|
+
massaged
|
120
|
+
else
|
121
|
+
mask
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
public
|
126
|
+
|
127
|
+
def tree_map(h)
|
128
|
+
raise ArgumentError.new('Must provide a transformer block') unless block_given?
|
129
|
+
config = BlockCollector.new
|
130
|
+
yield(config)
|
131
|
+
TransformTreeClosure.new.do_obj(h, config.get_path_tree)
|
132
|
+
end
|
133
|
+
|
134
|
+
class TransformTreeClosure
|
135
|
+
def initialize
|
136
|
+
@bias = 0 # symbol = +, string = -
|
137
|
+
end
|
138
|
+
|
139
|
+
def do_obj(obj, path_tree)
|
140
|
+
case obj
|
141
|
+
when nil; nil
|
142
|
+
when Array; do_array(obj, path_tree)
|
143
|
+
else; do_hash(obj, path_tree)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def do_hash(h, path_tree)
|
150
|
+
h = h.dup
|
151
|
+
path_tree.each_pair do |name, path_tree|
|
152
|
+
if path_tree.respond_to?(:call)
|
153
|
+
if (hash_name = try_get_hash_name(name))
|
154
|
+
hash_name, old_fh = get_key_and_value(h, hash_name)
|
155
|
+
unless old_fh.nil?
|
156
|
+
h[hash_name] = old_fh.each_with_object(old_fh.dup) do |(key, value), fh|
|
157
|
+
fh[key] = path_tree.call(value.deep_dup)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
elsif (array_name = try_get_array_name(name))
|
161
|
+
array_name, value = get_key_and_value(h, array_name)
|
162
|
+
unless value.nil?
|
163
|
+
h[array_name] = value.map(&:deep_dup).map(&path_tree)
|
164
|
+
end
|
165
|
+
else
|
166
|
+
name, value = get_key_and_value(h, name)
|
167
|
+
h[name] = path_tree.call(value.deep_dup)
|
168
|
+
end
|
169
|
+
else
|
170
|
+
name, value = get_key_and_value(h, name)
|
171
|
+
h[name] = do_obj(value, path_tree)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
h
|
175
|
+
end
|
176
|
+
|
177
|
+
def get_key_and_value(h, string_key)
|
178
|
+
tried_symbol = @bias >= 0
|
179
|
+
trial_key = tried_symbol ? string_key.to_sym : string_key
|
180
|
+
value = h[trial_key]
|
181
|
+
|
182
|
+
if value.nil? # failed
|
183
|
+
@bias += (tried_symbol ? -1 : 1)
|
184
|
+
key = tried_symbol ? string_key : string_key.to_sym
|
185
|
+
[key, h[key]]
|
186
|
+
else
|
187
|
+
@bias += (tried_symbol ? 1 : -1)
|
188
|
+
[trial_key, value]
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def do_array(a, path_tree)
|
193
|
+
a.map{|x| do_obj(x, path_tree)}
|
194
|
+
end
|
195
|
+
|
196
|
+
def try_get_hash_name(p)
|
197
|
+
p =~ /(.+)\{\}$/ ? $1 : nil
|
198
|
+
end
|
199
|
+
|
200
|
+
def try_get_array_name(p)
|
201
|
+
p =~ /(.+)\[\]$/ ? $1 : nil
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
public
|
206
|
+
|
207
|
+
def recursive_delete!(hash, keys)
|
208
|
+
x = hash # hash is named 'hash' for documentation purposes but may be anything
|
209
|
+
case x
|
210
|
+
when Hash
|
211
|
+
keys.each{|k| x.delete(k)}
|
212
|
+
x.each_value{|v| recursive_delete!(v, keys)}
|
213
|
+
when Array
|
214
|
+
x.each{|v| recursive_delete!(v, keys)}
|
215
|
+
end
|
216
|
+
x
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
220
|
+
end
|