perforated 0.8.2 → 0.9.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/.gitignore +1 -0
- data/CHANGELOG.md +16 -0
- data/Gemfile +2 -2
- data/README.md +3 -3
- data/bench/giant.rb +11 -11
- data/bench/rebuilder.rb +23 -0
- data/lib/perforated.rb +1 -5
- data/lib/perforated/cache.rb +27 -29
- data/lib/perforated/compatibility/find_in_batches.rb +11 -0
- data/lib/perforated/rebuilder.rb +50 -0
- data/lib/perforated/strategy.rb +9 -0
- data/lib/perforated/version.rb +1 -1
- data/perforated.gemspec +1 -1
- data/spec/perforated/cache_spec.rb +27 -38
- data/spec/perforated/compatibility/find_in_batches_spec.rb +25 -0
- data/spec/perforated/rebuilder_spec.rb +18 -0
- metadata +12 -8
- data/lib/perforated/rooted.rb +0 -33
- data/lib/perforated/strategy/default.rb +0 -11
- data/spec/perforated/rooted_spec.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af43ff72681ac3aa03057419afb02db11d89dcec
|
4
|
+
data.tar.gz: 52f9abfc9c9239e34fbde54958e22f8bdbf3d9e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c84edc00c26382975abd7a028687c54e9cc2f99c1717324f95a60aa2fc3bb9471acff847a1b65cead2aaa62a03509c4af0a95f7083b6c4eecf49d46b201cec3
|
7
|
+
data.tar.gz: 01eba26e7cb2e63d9ac66cedebed88b2a2b7943aacbf9a928d3bfef8d6ebeaed53db168f748581cececc235c5f51643d86e1aac845c3a71ff9275ccd0204da8e
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
## Version 0.9.0
|
2
|
+
|
3
|
+
* `as_json` and `to_json` now take a block. The block will be applied to each
|
4
|
+
model as it is being cached, allowing for serializers or presenters without
|
5
|
+
much additional memory overhead.
|
6
|
+
* `as_json` has been removed. The overhead from marshalling/unmarshalling had
|
7
|
+
enough overhead that it negated the caching benefits.
|
8
|
+
* Strategies no longer expect a `suffix` property, as anything cached is
|
9
|
+
expected to be a JSON string.
|
10
|
+
* Caching is performed in batches, which can be controlled by passing
|
11
|
+
`batch_size` through to `to_json`. Arrays are refined to support
|
12
|
+
`find_in_batches` in order to maintain compatibility with ActiveRecord
|
13
|
+
Relations. For especially large collections this can provide significant
|
14
|
+
memory savings (particularly when paired with passing a block through for
|
15
|
+
custom serialization).
|
16
|
+
|
1
17
|
## Version 0.8.2
|
2
18
|
|
3
19
|
* Really force the use of custom `fetch_multi` when using `NullStore`, not just
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -117,12 +117,12 @@ cache method you can provide a custom key caching strategy.
|
|
117
117
|
|
118
118
|
```ruby
|
119
119
|
module CustomStrategy
|
120
|
-
def self.expand_cache_key(object
|
121
|
-
[object.id, object.updated_at
|
120
|
+
def self.expand_cache_key(object)
|
121
|
+
[object.id, object.updated_at].join('/')
|
122
122
|
end
|
123
123
|
end
|
124
124
|
|
125
|
-
perforated = Perforated
|
125
|
+
perforated = Perforated.new(array, CustomStrategy)
|
126
126
|
```
|
127
127
|
|
128
128
|
## Installation
|
data/bench/giant.rb
CHANGED
@@ -3,10 +3,8 @@ require 'bundler'
|
|
3
3
|
Bundler.setup
|
4
4
|
|
5
5
|
require 'benchmark'
|
6
|
-
require 'dalli'
|
7
6
|
require 'perforated'
|
8
7
|
require 'active_support/core_ext/object'
|
9
|
-
require 'active_support/cache/dalli_store'
|
10
8
|
require 'redis'
|
11
9
|
require 'redis-activesupport'
|
12
10
|
|
@@ -17,7 +15,7 @@ Structure = Struct.new(:id) do
|
|
17
15
|
end
|
18
16
|
|
19
17
|
module Strategy
|
20
|
-
def self.expand_cache_key(object
|
18
|
+
def self.expand_cache_key(object)
|
21
19
|
"perf-#{object}"
|
22
20
|
end
|
23
21
|
end
|
@@ -25,24 +23,26 @@ end
|
|
25
23
|
perforated = Perforated::Cache.new((0..20_000).map { |i| Structure.new(i) }, Strategy)
|
26
24
|
|
27
25
|
Benchmark.bm do |x|
|
26
|
+
GC.disable
|
27
|
+
puts "Total Objects: #{ObjectSpace.count_objects[:TOTAL]}"
|
28
|
+
|
28
29
|
x.report('memory-1') { perforated.to_json }
|
29
30
|
x.report('memory-2') { perforated.to_json }
|
30
31
|
|
32
|
+
puts "Total Objects: #{ObjectSpace.count_objects[:TOTAL]}"
|
33
|
+
|
31
34
|
Perforated.configure do |config|
|
32
35
|
config.cache = ActiveSupport::Cache::RedisStore.new(host: 'localhost', db: 5)
|
33
36
|
end
|
34
37
|
|
35
38
|
Perforated.cache.clear
|
36
39
|
|
40
|
+
GC.enable
|
41
|
+
GC.start
|
42
|
+
GC.disable
|
43
|
+
|
37
44
|
x.report('redis-1') { perforated.to_json }
|
38
45
|
x.report('redis-2') { perforated.to_json }
|
39
46
|
|
40
|
-
|
41
|
-
config.cache = ActiveSupport::Cache::DalliStore.new('localhost')
|
42
|
-
end
|
43
|
-
|
44
|
-
Perforated.cache.clear
|
45
|
-
|
46
|
-
x.report('dalli-1') { perforated.to_json }
|
47
|
-
x.report('dalli-2') { perforated.to_json }
|
47
|
+
puts "Total Objects: #{ObjectSpace.count_objects[:TOTAL]}"
|
48
48
|
end
|
data/bench/rebuilder.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
|
3
|
+
Bundler.setup
|
4
|
+
|
5
|
+
require 'benchmark'
|
6
|
+
require 'perforated'
|
7
|
+
require 'oj'
|
8
|
+
|
9
|
+
keys = %w[notes tags comments]
|
10
|
+
objects = (0..10_000).map do |num|
|
11
|
+
keys = keys.rotate
|
12
|
+
|
13
|
+
{ keys.first => num }
|
14
|
+
end
|
15
|
+
|
16
|
+
strings = objects.map { |obj| JSON.dump(obj) }
|
17
|
+
rebuilder = Perforated::Rebuilder.new(strings, Oj)
|
18
|
+
|
19
|
+
N = 100
|
20
|
+
|
21
|
+
Benchmark.bmbm do |x|
|
22
|
+
x.report('rebuild') { N.times { rebuilder.rebuild(rooted: true) } }
|
23
|
+
end
|
data/lib/perforated.rb
CHANGED
@@ -1,16 +1,12 @@
|
|
1
1
|
require 'active_support/cache'
|
2
2
|
require 'json'
|
3
3
|
require 'perforated/cache'
|
4
|
-
require 'perforated/compatibility/fetch_multi'
|
5
|
-
require 'perforated/rooted'
|
6
|
-
require 'perforated/strategy/default'
|
7
|
-
require 'perforated/version'
|
8
4
|
|
9
5
|
module Perforated
|
10
6
|
extend self
|
11
7
|
|
12
8
|
def new(*args)
|
13
|
-
Perforated::Cache.new(args)
|
9
|
+
Perforated::Cache.new(*args)
|
14
10
|
end
|
15
11
|
|
16
12
|
def cache=(new_cache)
|
data/lib/perforated/cache.rb
CHANGED
@@ -1,40 +1,42 @@
|
|
1
|
+
require 'perforated/rebuilder'
|
2
|
+
require 'perforated/strategy'
|
3
|
+
require 'perforated/compatibility/find_in_batches'
|
4
|
+
require 'perforated/compatibility/fetch_multi'
|
5
|
+
|
1
6
|
module Perforated
|
2
7
|
class Cache
|
3
|
-
|
4
|
-
|
5
|
-
def initialize(enumerable, key_strategy = Perforated::Strategy::Default)
|
6
|
-
@enumerable = enumerable
|
7
|
-
@key_strategy = key_strategy
|
8
|
-
end
|
8
|
+
using Perforated::Compatibility::FindInBatches
|
9
9
|
|
10
|
-
|
11
|
-
keyed = keyed_enumerable('as-json')
|
12
|
-
objects = fetch_multi(keyed) { |key| keyed[key].as_json }.values
|
10
|
+
attr_accessor :enumerable, :strategy
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
objects
|
18
|
-
end
|
12
|
+
def initialize(enumerable, strategy = Perforated::Strategy)
|
13
|
+
@enumerable = enumerable
|
14
|
+
@strategy = strategy
|
19
15
|
end
|
20
16
|
|
21
|
-
def to_json(
|
22
|
-
|
23
|
-
objects = fetch_multi(keyed) { |key| keyed[key].to_json }
|
24
|
-
concat = concatenate(objects)
|
17
|
+
def to_json(rooted: false, batch_size: 1000, &block)
|
18
|
+
results = []
|
25
19
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
20
|
+
enumerable.find_in_batches(batch_size: batch_size) do |subset|
|
21
|
+
keyed = key_mapped(subset)
|
22
|
+
|
23
|
+
results << fetch_multi(keyed) do |key|
|
24
|
+
if block_given?
|
25
|
+
(yield keyed[key]).to_json
|
26
|
+
else
|
27
|
+
keyed[key].to_json
|
28
|
+
end
|
29
|
+
end.values
|
30
30
|
end
|
31
|
+
|
32
|
+
Perforated::Rebuilder.new(results).rebuild(rooted: rooted)
|
31
33
|
end
|
32
34
|
|
33
35
|
private
|
34
36
|
|
35
|
-
def
|
36
|
-
|
37
|
-
memo[
|
37
|
+
def key_mapped(subset)
|
38
|
+
subset.each_with_object({}) do |object, memo|
|
39
|
+
memo[strategy.expand_cache_key(object)] = object
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
@@ -43,9 +45,5 @@ module Perforated
|
|
43
45
|
|
44
46
|
Perforated::Compatibility.fetch_multi(*keys, &block)
|
45
47
|
end
|
46
|
-
|
47
|
-
def concatenate(objects)
|
48
|
-
"[#{objects.values.join(',')}]"
|
49
|
-
end
|
50
48
|
end
|
51
49
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Perforated
|
4
|
+
class Rebuilder
|
5
|
+
attr_reader :parser, :strings
|
6
|
+
|
7
|
+
def initialize(strings, parser = Perforated.json)
|
8
|
+
@strings = strings
|
9
|
+
@parser = parser
|
10
|
+
end
|
11
|
+
|
12
|
+
def rebuild(rooted: false)
|
13
|
+
if rooted
|
14
|
+
parser.dump(merge(parser.load(concatenated)))
|
15
|
+
else
|
16
|
+
concatenated
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def concatenated
|
21
|
+
"[#{strings.join(',')}]"
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def merge(objects)
|
27
|
+
merged = objects.each_with_object({}) do |object, memo|
|
28
|
+
object.each do |key, value|
|
29
|
+
memo[key] ||= Set.new
|
30
|
+
|
31
|
+
if value.is_a?(Array)
|
32
|
+
memo[key].merge(value)
|
33
|
+
else
|
34
|
+
memo[key].add(value)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
sets_to_arrays(merged)
|
40
|
+
end
|
41
|
+
|
42
|
+
def sets_to_arrays(merged)
|
43
|
+
merged.each do |key, value|
|
44
|
+
merged[key] = value.to_a
|
45
|
+
end
|
46
|
+
|
47
|
+
merged
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/perforated/version.rb
CHANGED
data/perforated.gemspec
CHANGED
@@ -4,12 +4,8 @@ describe Perforated::Cache do
|
|
4
4
|
after { Perforated.cache.clear }
|
5
5
|
|
6
6
|
Language = Struct.new(:name) do
|
7
|
-
def as_json
|
8
|
-
{ name: name }
|
9
|
-
end
|
10
|
-
|
11
7
|
def to_json
|
12
|
-
|
8
|
+
{ name: name }.to_json
|
13
9
|
end
|
14
10
|
|
15
11
|
def cache_key
|
@@ -18,12 +14,8 @@ describe Perforated::Cache do
|
|
18
14
|
end
|
19
15
|
|
20
16
|
Family = Struct.new(:name, :languages) do
|
21
|
-
def as_json
|
22
|
-
{ languages: languages }
|
23
|
-
end
|
24
|
-
|
25
17
|
def to_json
|
26
|
-
|
18
|
+
{ languages: languages }.to_json
|
27
19
|
end
|
28
20
|
|
29
21
|
def cache_key
|
@@ -31,54 +23,51 @@ describe Perforated::Cache do
|
|
31
23
|
end
|
32
24
|
end
|
33
25
|
|
34
|
-
describe '#
|
35
|
-
it 'constructs
|
26
|
+
describe '#to_json' do
|
27
|
+
it 'constructs a stringified json array of underlying values' do
|
36
28
|
ruby = Language.new('Ruby')
|
37
29
|
elixir = Language.new('Elixir')
|
38
30
|
cache = Perforated::Cache.new([ruby, elixir])
|
39
31
|
|
40
|
-
expect(cache.
|
41
|
-
|
42
|
-
expect(Perforated.cache.
|
43
|
-
expect(Perforated.cache.read('Language/Elixir/as-json')).to eq(elixir.as_json)
|
32
|
+
expect(cache.to_json).to eq(%([{"name":"Ruby"},{"name":"Elixir"}]))
|
33
|
+
expect(Perforated.cache.exist?('Language/Ruby')).to be_truthy
|
34
|
+
expect(Perforated.cache.exist?('Language/Elixir')).to be_truthy
|
44
35
|
end
|
45
36
|
|
46
37
|
it 'does not overwrite existing key values' do
|
47
38
|
erlang = Language.new('Erlang')
|
48
|
-
Perforated.cache.write('Language/Erlang
|
49
|
-
|
50
|
-
Perforated::Cache.new([erlang]).as_json
|
51
|
-
|
52
|
-
expect(Perforated.cache.read('Language/Erlang/as-json')).to eq(name: 'Elixir')
|
53
|
-
end
|
39
|
+
Perforated.cache.write('Language/Erlang', JSON.dump(name: 'Elixir'))
|
54
40
|
|
55
|
-
|
56
|
-
lisps = Family.new('Lisp', ['scheme', 'clojure'])
|
57
|
-
scripts = Family.new('Script', ['perl', 'ruby'])
|
58
|
-
cache = Perforated::Cache.new([lisps, scripts])
|
41
|
+
Perforated::Cache.new([erlang]).to_json
|
59
42
|
|
60
|
-
expect(cache.
|
61
|
-
languages: %w[scheme clojure perl ruby]
|
62
|
-
)
|
43
|
+
expect(Perforated.cache.read('Language/Erlang')).to eq(JSON.dump(name: 'Elixir'))
|
63
44
|
end
|
64
45
|
|
65
46
|
it 'safely returns an empty enumerable when empty' do
|
66
47
|
cache = Perforated::Cache.new([])
|
67
48
|
|
68
|
-
expect(cache.
|
69
|
-
expect(cache.
|
49
|
+
expect(cache.to_json).to eq('[]')
|
50
|
+
expect(cache.to_json(rooted: true)).to eq('{}')
|
70
51
|
end
|
71
|
-
end
|
72
52
|
|
73
|
-
|
74
|
-
|
75
|
-
cache = Perforated::Cache.new([
|
53
|
+
it 'applies a provided block to the object before caching' do
|
54
|
+
ruby = Language.new('Ruby')
|
55
|
+
cache = Perforated::Cache.new([ruby])
|
76
56
|
|
77
|
-
|
78
|
-
|
79
|
-
|
57
|
+
serializer = Struct.new(:lang) do
|
58
|
+
def to_json
|
59
|
+
{ name: lang.name.upcase }.to_json
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
results = cache.to_json do |lang|
|
64
|
+
serializer.new(lang)
|
65
|
+
end
|
66
|
+
|
67
|
+
expect(results).to eq(JSON.dump([{ name: 'RUBY' }]))
|
80
68
|
end
|
81
69
|
|
70
|
+
|
82
71
|
it 'reconstructs rooted objects into a single merged object' do
|
83
72
|
lisps = Family.new('Lisp', ['scheme', 'clojure'])
|
84
73
|
scripts = Family.new('Script', ['perl', 'ruby'])
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'perforated/compatibility/find_in_batches'
|
2
|
+
|
3
|
+
describe Perforated::Compatibility::FindInBatches do
|
4
|
+
FindBatches = Struct.new(:array) do
|
5
|
+
using Perforated::Compatibility::FindInBatches
|
6
|
+
|
7
|
+
def perform(&block)
|
8
|
+
array.find_in_batches(batch_size: 50, &block)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#find_in_batches' do
|
13
|
+
it 'iterates over an enumerable in batches' do
|
14
|
+
enumerable = [1] * 100
|
15
|
+
eachable = FindBatches.new(enumerable)
|
16
|
+
results = []
|
17
|
+
|
18
|
+
eachable.perform do |arr|
|
19
|
+
results << arr.reduce(&:+)
|
20
|
+
end
|
21
|
+
|
22
|
+
expect(results).to eq([50, 50])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'perforated/rebuilder'
|
3
|
+
|
4
|
+
describe Perforated::Rebuilder do
|
5
|
+
describe '#rebuild' do
|
6
|
+
it 'merges stringified json' do
|
7
|
+
string_a = JSON.dump(families: { name: 'lang' }, languages: [{ name: 'scheme' }])
|
8
|
+
string_b = JSON.dump(families: { name: 'lang' }, languages: [{ name: 'clojure' }])
|
9
|
+
string_c = JSON.dump(families: { name: 'lang' })
|
10
|
+
|
11
|
+
rooted = Perforated::Rebuilder.new([string_a, string_b], JSON)
|
12
|
+
|
13
|
+
expect(rooted.rebuild(rooted: true)).to eq(
|
14
|
+
'{"families":[{"name":"lang"}],"languages":[{"name":"scheme"},{"name":"clojure"}]}'
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: perforated
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Parker Selbert
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-10-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '3'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '3'
|
69
69
|
description: Intellgent json collection caching
|
70
70
|
email:
|
71
71
|
- parker@sorentwo.com
|
@@ -82,16 +82,19 @@ files:
|
|
82
82
|
- README.md
|
83
83
|
- Rakefile
|
84
84
|
- bench/giant.rb
|
85
|
+
- bench/rebuilder.rb
|
85
86
|
- lib/perforated.rb
|
86
87
|
- lib/perforated/cache.rb
|
87
88
|
- lib/perforated/compatibility/fetch_multi.rb
|
88
|
-
- lib/perforated/
|
89
|
-
- lib/perforated/
|
89
|
+
- lib/perforated/compatibility/find_in_batches.rb
|
90
|
+
- lib/perforated/rebuilder.rb
|
91
|
+
- lib/perforated/strategy.rb
|
90
92
|
- lib/perforated/version.rb
|
91
93
|
- perforated.gemspec
|
92
94
|
- spec/perforated/cache_spec.rb
|
93
95
|
- spec/perforated/compatibility/fetch_multi_spec.rb
|
94
|
-
- spec/perforated/
|
96
|
+
- spec/perforated/compatibility/find_in_batches_spec.rb
|
97
|
+
- spec/perforated/rebuilder_spec.rb
|
95
98
|
- spec/perforated_spec.rb
|
96
99
|
- spec/spec_helper.rb
|
97
100
|
homepage: https://github.com/sorentwo/perforated
|
@@ -124,6 +127,7 @@ summary: 'The most expensive part of serving a JSON request is converting the se
|
|
124
127
|
test_files:
|
125
128
|
- spec/perforated/cache_spec.rb
|
126
129
|
- spec/perforated/compatibility/fetch_multi_spec.rb
|
127
|
-
- spec/perforated/
|
130
|
+
- spec/perforated/compatibility/find_in_batches_spec.rb
|
131
|
+
- spec/perforated/rebuilder_spec.rb
|
128
132
|
- spec/perforated_spec.rb
|
129
133
|
- spec/spec_helper.rb
|
data/lib/perforated/rooted.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
require 'set'
|
2
|
-
|
3
|
-
module Perforated
|
4
|
-
module Rooted
|
5
|
-
def self.merge(objects)
|
6
|
-
merged = objects.each_with_object({}) do |object, memo|
|
7
|
-
object.each do |key, value|
|
8
|
-
memo[key] ||= Set.new
|
9
|
-
|
10
|
-
if value.is_a?(Array)
|
11
|
-
memo[key].merge(value)
|
12
|
-
else
|
13
|
-
memo[key].add(value)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
sets_to_arrays(merged)
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.reconstruct(objects, parser = Perforated.json)
|
22
|
-
parser.dump(merge(parser.load(objects)))
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.sets_to_arrays(object)
|
26
|
-
object.each do |key, value|
|
27
|
-
object[key] = value.to_a
|
28
|
-
end
|
29
|
-
|
30
|
-
object
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
require 'perforated'
|
2
|
-
|
3
|
-
describe Perforated::Rooted do
|
4
|
-
describe '.merge' do
|
5
|
-
it 'merges all nested objects' do
|
6
|
-
obj_a = { languages: [{ name: 'scheme' }] }
|
7
|
-
obj_b = { languages: [{ name: 'clojure' }] }
|
8
|
-
|
9
|
-
expect(Perforated::Rooted.merge([obj_a, obj_b])).to eq(
|
10
|
-
languages: [{ name: 'scheme' }, { name: 'clojure' }]
|
11
|
-
)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
describe '.reconstruct' do
|
16
|
-
it 'merges stringified json' do
|
17
|
-
obj_a = JSON.dump({ languages: [{ name: 'scheme' }] })
|
18
|
-
obj_b = JSON.dump({ languages: [{ name: 'clojure' }] })
|
19
|
-
concat = "[#{obj_a},#{obj_b}]"
|
20
|
-
|
21
|
-
expect(Perforated::Rooted.reconstruct(concat)).to eq(
|
22
|
-
'{"languages":[{"name":"scheme"},{"name":"clojure"}]}'
|
23
|
-
)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|