right_support 2.8.28 → 2.8.29
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/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: []
|