right_support 2.8.28 → 2.8.29
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/VERSION +1 -1
- data/lib/right_support/data/hash_tools.rb +81 -3
- data/right_support.gemspec +3 -3
- data/spec/data/hash_tools_spec.rb +62 -7
- metadata +8 -13
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d244fe9a56d4712f420a8e3aef63841427de950c
|
4
|
+
data.tar.gz: 9ea699ca1dd5bc23bb23c0085ed03d3ea1a23968
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2fa6ca5181f6040eae44dec1fdb8bbb2980c9faa39b80de0cd2b87057078b4c6b1c0df3a54a1e0c6627d86ca0726a5023eec91a7ee697834666a12bd82dc36c2
|
7
|
+
data.tar.gz: d636fe0c0f2eec50ce62c1a91131631db306ce67777b2b2af825be726e19413439e42c87255a32fa26b7b2a54165e881098de0cbb73f3461025d105de1ffef2c
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.8.
|
1
|
+
2.8.29
|
@@ -25,7 +25,13 @@ module RightSupport::Data
|
|
25
25
|
# various tools for manipulating hash-like classes.
|
26
26
|
module HashTools
|
27
27
|
|
28
|
-
|
28
|
+
# require checks
|
29
|
+
HAS_EXTLIB = require_succeeds?('extlib')
|
30
|
+
HAS_JSON = require_succeeds?('json')
|
31
|
+
|
32
|
+
# exceptions
|
33
|
+
class NoExtlib < ::StandardError; end
|
34
|
+
class NoJson < ::StandardError; end
|
29
35
|
|
30
36
|
# Determines if given object is hashable (i.e. object responds to hash methods).
|
31
37
|
#
|
@@ -112,6 +118,8 @@ module RightSupport::Data
|
|
112
118
|
|
113
119
|
# Creates a deep clone of the given hash.
|
114
120
|
#
|
121
|
+
# @deprecated in favor of more robust deep_clone2
|
122
|
+
#
|
115
123
|
# note that not all objects are clonable in Ruby even though all respond to clone
|
116
124
|
# (which is completely counter-intuitive and contrary to all other managed languages).
|
117
125
|
# Java, for example, has the built-in Cloneable marker interface which we will simulate
|
@@ -135,6 +143,9 @@ module RightSupport::Data
|
|
135
143
|
result = original.clone
|
136
144
|
result.each do |k, v|
|
137
145
|
if hashable?(v)
|
146
|
+
# HACK: we should have passed &leaf_callback here but never did before
|
147
|
+
# so it is techincally a bug. we are not going to change it due to not
|
148
|
+
# wanting to break legacy code so use deep_clone2 instead.
|
138
149
|
result[k] = deep_clone(v)
|
139
150
|
elsif leaf_callback
|
140
151
|
result[k] = leaf_callback.call(v)
|
@@ -147,6 +158,75 @@ module RightSupport::Data
|
|
147
158
|
result
|
148
159
|
end
|
149
160
|
|
161
|
+
# Deeply duplicates (clones) a hashable object containing other hashes or
|
162
|
+
# arrays of hashes but passing other types through.
|
163
|
+
#
|
164
|
+
# Optionally changes the target hashable type to 'normalize' to a specific
|
165
|
+
# hashable class (such as Mash).
|
166
|
+
#
|
167
|
+
# Optionally traverses arrays (default) in an attempt to deep-clone any
|
168
|
+
# sub-hashes instead of simply associating them.
|
169
|
+
#
|
170
|
+
# === Parameters
|
171
|
+
# @param [Object] any kind of object
|
172
|
+
# @param [Hash] options
|
173
|
+
# @option [Class] :class for cloned hashables or nil for same as source
|
174
|
+
#
|
175
|
+
# === Block
|
176
|
+
# @yieldparam [Object] value of leaf
|
177
|
+
# @yieldreturn [Object] cloned value of leaf or original value
|
178
|
+
#
|
179
|
+
# === Return
|
180
|
+
# @return [Object] depends on input type
|
181
|
+
def self.deep_clone2(any, options = {}, &leaf_callback)
|
182
|
+
if hashable?(any)
|
183
|
+
# clone to a new instance of hashable class with deep cloning.
|
184
|
+
any.inject((options[:class] || any.class).new) do |m, (k, v)|
|
185
|
+
m[k] = deep_clone2(v, options, &leaf_callback)
|
186
|
+
m
|
187
|
+
end
|
188
|
+
elsif any.kind_of?(::Array)
|
189
|
+
# traverse arrays
|
190
|
+
any.map { |e| deep_clone2(e, options, &leaf_callback) }
|
191
|
+
elsif leaf_callback
|
192
|
+
leaf_callback.call(any)
|
193
|
+
elsif any.respond_to?(:duplicable?)
|
194
|
+
# see #deep_clone for remarks
|
195
|
+
any.duplicable? ? any.clone : any
|
196
|
+
else
|
197
|
+
any # whatever
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# Deeply mashes and duplicates (clones) a hashable using deep_clone2 after
|
202
|
+
# checking that extlib is available.
|
203
|
+
#
|
204
|
+
# The advantage of Mash over Hash is, of course, to be able to use either
|
205
|
+
# a String or Symbol as a key for the same value.
|
206
|
+
#
|
207
|
+
# Note that Mash.new(my_mash) will convert child hashes to mashes but not
|
208
|
+
# with the guarantee of cloning and detaching the deep mash. In other words.
|
209
|
+
# if any part of the hash is already a mash then it is not cloned by
|
210
|
+
# invoking Mash.new()
|
211
|
+
#
|
212
|
+
# === Parameters
|
213
|
+
# @param [Object] any kind of object
|
214
|
+
#
|
215
|
+
# === Block
|
216
|
+
# @yieldparam [Object] value of leaf
|
217
|
+
# @yieldreturn [Object] cloned value of leaf or original value
|
218
|
+
#
|
219
|
+
# === Return
|
220
|
+
# @return [Object] depends on input type
|
221
|
+
def self.deep_mash(any, &leaf_callback)
|
222
|
+
if HAS_EXTLIB
|
223
|
+
options = { :class => ::Mash }
|
224
|
+
deep_clone2(any, options, &leaf_callback)
|
225
|
+
else
|
226
|
+
raise NoExtlib, "extlib is unavailable"
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
150
230
|
# Performs a deep merge (but not a deep clone) of one hash into another.
|
151
231
|
#
|
152
232
|
# === Parameters
|
@@ -327,8 +407,6 @@ module RightSupport::Data
|
|
327
407
|
end
|
328
408
|
end
|
329
409
|
|
330
|
-
class NoJson < Exception; end
|
331
|
-
|
332
410
|
# Generates JSON from the given hash (of hashes) that is sorted by key at
|
333
411
|
# all levels. Does not handle case of hash to array of hashes, etc.
|
334
412
|
#
|
data/right_support.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: right_support 2.8.
|
5
|
+
# stub: right_support 2.8.29 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "right_support"
|
9
|
-
s.version = "2.8.
|
9
|
+
s.version = "2.8.29"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Tony Spataro", "Sergey Sergyenko", "Ryan Williamson", "Lee Kirchhoff", "Alexey Karpik", "Scott Messier"]
|
14
|
-
s.date = "2014-08-
|
14
|
+
s.date = "2014-08-26"
|
15
15
|
s.description = "A toolkit of useful, reusable foundation code created by RightScale."
|
16
16
|
s.email = "support@rightscale.com"
|
17
17
|
s.extra_rdoc_files = [
|
@@ -175,20 +175,75 @@ describe RightSupport::Data::HashTools do
|
|
175
175
|
data[:tree][:branch][:a].duplicable?.should be_true
|
176
176
|
data[:tree][:branch][:b].duplicable?.should be_true
|
177
177
|
end
|
178
|
-
deep_check_object_id(
|
178
|
+
deep_check_object_id(data, actual)
|
179
179
|
end
|
180
180
|
end
|
181
181
|
|
182
182
|
it 'should deep clone leaves using callback when given' do
|
183
|
-
initial = { :x =>
|
184
|
-
actual = subject.deep_clone(initial) { |value| value.
|
183
|
+
initial = { :x => 'a', :y => { :a => ['b', 'c'], :b => ['d', ['e', 'f']] } }
|
184
|
+
actual = subject.deep_clone(initial) { |value| value.clone }
|
185
185
|
actual.should == initial
|
186
186
|
deep_check_object_id(actual, initial) do |a, b|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
187
|
+
a.object_id.should_not == b.object_id
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# also tests deep_clone2
|
193
|
+
context '#deep_mash' do
|
194
|
+
def deep_check_object_id(a, b, &leaf_callback)
|
195
|
+
a.object_id.should_not == b.object_id
|
196
|
+
if subject.hashable?(a)
|
197
|
+
b.class.should == ::Mash
|
198
|
+
a.each do |k, v|
|
199
|
+
other = b[k]
|
200
|
+
if subject.hashable?(v)
|
201
|
+
deep_check_object_id(v, other)
|
202
|
+
elsif v.kind_of?(::Array)
|
203
|
+
v.object_id.should_not == other.object_id
|
204
|
+
other.class.should == ::Array
|
205
|
+
v.size.should == other.size
|
206
|
+
v.each_with_index do |e, idx|
|
207
|
+
deep_check_object_id(e, other[idx])
|
208
|
+
end
|
209
|
+
elsif leaf_callback
|
210
|
+
leaf_callback.call(v, other)
|
211
|
+
elsif v.respond_to?(:duplicable?) && v.duplicable?
|
212
|
+
v.object_id.should_not == other.object_id
|
213
|
+
else
|
214
|
+
v.object_id.should == other.object_id
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
{
|
221
|
+
:empty => {},
|
222
|
+
:shallow => { :x => 1, :y => 2 },
|
223
|
+
:deep => { :x => 1, :y => { :a => 'A' }, :z => { :b => 'B', :c => { :foo => :bar } } },
|
224
|
+
:arrayed => [ { :x => 1 }, { :y => { :a => 'A' } }, :z => { :b => 'B', :c => { :foo => :bar } } ],
|
225
|
+
:duplicable => {
|
226
|
+
:tree => {
|
227
|
+
:branch => {
|
228
|
+
:a => ::RightSupport::Data::HashToolsSpec::DuplicableValue.new(1),
|
229
|
+
:b => ::RightSupport::Data::HashToolsSpec::DuplicableValue.new('hi there') } } }
|
230
|
+
}.each do |kind, data|
|
231
|
+
it "should deep mash values in #{kind} hashes" do
|
232
|
+
actual = subject.deep_mash(data)
|
233
|
+
if :duplicable == kind
|
234
|
+
# verify that leaves are duplicable
|
235
|
+
data[:tree][:branch][:a].duplicable?.should be_true
|
236
|
+
data[:tree][:branch][:b].duplicable?.should be_true
|
191
237
|
end
|
238
|
+
deep_check_object_id(data, actual)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'should deep clone leaves using callback when given' do
|
243
|
+
initial = { :x => 'a', :y => { :a => ['b', 'c'], :b => ['d', ['e', 'f']] } }
|
244
|
+
actual = subject.deep_mash(initial) { |value| value.clone }
|
245
|
+
deep_check_object_id(initial, actual) do |a, b|
|
246
|
+
a.object_id.should_not == b.object_id
|
192
247
|
end
|
193
248
|
end
|
194
249
|
end
|
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: right_support
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.8.
|
5
|
-
prerelease:
|
4
|
+
version: 2.8.29
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Tony Spataro
|
@@ -14,7 +13,7 @@ authors:
|
|
14
13
|
autorequire:
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
|
-
date: 2014-
|
16
|
+
date: 2014-09-15 00:00:00.000000000 Z
|
18
17
|
dependencies: []
|
19
18
|
description: A toolkit of useful, reusable foundation code created by RightScale.
|
20
19
|
email: support@rightscale.com
|
@@ -24,7 +23,7 @@ extra_rdoc_files:
|
|
24
23
|
- LICENSE
|
25
24
|
- README.rdoc
|
26
25
|
files:
|
27
|
-
- .rspec
|
26
|
+
- ".rspec"
|
28
27
|
- CHANGELOG.rdoc
|
29
28
|
- Gemfile
|
30
29
|
- Gemfile.lock
|
@@ -146,29 +145,25 @@ files:
|
|
146
145
|
homepage: https://github.com/rightscale/right_support
|
147
146
|
licenses:
|
148
147
|
- MIT
|
148
|
+
metadata: {}
|
149
149
|
post_install_message:
|
150
150
|
rdoc_options: []
|
151
151
|
require_paths:
|
152
152
|
- lib
|
153
153
|
required_ruby_version: !ruby/object:Gem::Requirement
|
154
|
-
none: false
|
155
154
|
requirements:
|
156
|
-
- -
|
155
|
+
- - ">="
|
157
156
|
- !ruby/object:Gem::Version
|
158
157
|
version: '0'
|
159
|
-
segments:
|
160
|
-
- 0
|
161
|
-
hash: 1570969451733484161
|
162
158
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
163
|
-
none: false
|
164
159
|
requirements:
|
165
|
-
- -
|
160
|
+
- - ">="
|
166
161
|
- !ruby/object:Gem::Version
|
167
162
|
version: '0'
|
168
163
|
requirements: []
|
169
164
|
rubyforge_project:
|
170
|
-
rubygems_version:
|
165
|
+
rubygems_version: 2.2.2
|
171
166
|
signing_key:
|
172
|
-
specification_version:
|
167
|
+
specification_version: 4
|
173
168
|
summary: Reusable foundation code.
|
174
169
|
test_files: []
|