key_path 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +1 -3
- data/README.md +15 -15
- data/Rakefile +2 -3
- data/key_path.gemspec +8 -7
- data/lib/key_path/enumerable/extensions.rb +12 -13
- data/lib/key_path/hash/deep_merge.rb +40 -35
- data/lib/key_path/hash/extensions.rb +8 -6
- data/lib/key_path/path.rb +8 -11
- data/lib/key_path/string/extensions.rb +4 -2
- data/lib/key_path/version.rb +2 -1
- data/lib/key_path.rb +0 -2
- data/spec/enumerable_extensions_spec.rb +22 -14
- data/spec/hash_deep_merge_spec.rb +13 -14
- data/spec/hash_extensions_spec.rb +15 -15
- data/spec/key_path_spec.rb +0 -1
- data/spec/path_spec.rb +5 -6
- data/spec/spec_helper.rb +2 -6
- data/spec/string_extensions_spec.rb +1 -1
- metadata +19 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b3b105137df50dc9ec27395c52fc2416ad3c275
|
4
|
+
data.tar.gz: 07009e35a342fa252077d2e230fa9b7a8556a388
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8fc198e9bc7a21908039c10342656edc0bd9fa97de3732a1d2fadb1eb06f9e5a089b3899d80e76686064641d61864925b5a0584c8c75536229943cddf83cf003
|
7
|
+
data.tar.gz: db56e6b5359f71521d6fd43439a5b009ea9573ea8353f7331450d2178c5313fb4ed3f2df5a74e39193ee0488c70c6608dde81c27505f159dbefe0d51800fb39b
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.2.2
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
# keypath-based collection access extensions for Ruby.
|
2
2
|
|
3
|
-
[![Build Status](https://
|
3
|
+
[![Build Status](https://travis-ci.org/nickcharlton/keypath-ruby.svg?branch=master)](https://travis-ci.org/nickcharlton/keypath-ruby)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/nickcharlton/keypath-ruby/badges/gpa.svg)](https://codeclimate.com/github/nickcharlton/keypath-ruby)
|
5
|
+
[![Test Coverage](https://codeclimate.com/github/nickcharlton/keypath-ruby/badges/coverage.svg)](https://codeclimate.com/github/nickcharlton/keypath-ruby)
|
6
|
+
[![Dependency Status](https://gemnasium.com/nickcharlton/keypath-ruby.svg)](https://gemnasium.com/nickcharlton/keypath-ruby)
|
4
7
|
|
5
|
-
This gem allows you to access nested Ruby collections (`Hash`, `Array`, etc) using
|
8
|
+
This gem allows you to access nested Ruby collections (`Hash`, `Array`, etc) using
|
6
9
|
keypaths.
|
7
10
|
|
8
11
|
For example, say you had a nested data structure like:
|
@@ -19,29 +22,24 @@ data = {
|
|
19
22
|
}
|
20
23
|
```
|
21
24
|
|
22
|
-
You could access "https://github.com/" through: `something_else.0.url`. Basically,
|
23
|
-
this is intended to allow you to manipulate/transform large nested structures that
|
25
|
+
You could access "https://github.com/" through: `something_else.0.url`. Basically,
|
26
|
+
this is intended to allow you to manipulate/transform large nested structures that
|
24
27
|
you might get back from a JSON document.
|
25
28
|
|
26
29
|
## Installation
|
27
30
|
|
28
|
-
|
31
|
+
`keypath-ruby` is on [RubyGems][] as `key_path`. But you can also add the repo
|
32
|
+
to your [Gemfile][]:
|
29
33
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
$ bundle
|
35
|
-
|
36
|
-
Or install it yourself as:
|
37
|
-
|
38
|
-
$ gem install keypath
|
34
|
+
```ruby
|
35
|
+
gem 'key_path', :git => 'https://github.com/nickcharlton/keypath-ruby.git'
|
36
|
+
```
|
39
37
|
|
40
38
|
## Usage
|
41
39
|
|
42
40
|
`KeyPath` is at least two things. First, it's a class (actually, `KeyPath::Path`)
|
43
41
|
which represents a path (this is just a string, and has methods to go back and
|
44
|
-
forth from it) and secondly a set of class extensions for `Enumerable`, `Hash` and
|
42
|
+
forth from it) and secondly a set of class extensions for `Enumerable`, `Hash` and
|
45
43
|
`String` which allow you to use the native collection classes with keypaths.
|
46
44
|
|
47
45
|
```ruby
|
@@ -83,3 +81,5 @@ data.set_keypath(path, 'http://github.com/')
|
|
83
81
|
|
84
82
|
Copyright (c) 2013 Nick Charlton (<nick@nickcharlton.net>)
|
85
83
|
|
84
|
+
[RubyGems]: http://rubygems.org/
|
85
|
+
[Gemfile]: http://bundler.io/
|
data/Rakefile
CHANGED
@@ -8,7 +8,7 @@ begin
|
|
8
8
|
Bundler.setup(:default, :development)
|
9
9
|
rescue Bundler::BundlerError => e
|
10
10
|
$stderr.puts e.message
|
11
|
-
$stderr.puts
|
11
|
+
$stderr.puts 'Run `bundle install` to install missing gems.'
|
12
12
|
exit e.status_code
|
13
13
|
end
|
14
14
|
|
@@ -24,5 +24,4 @@ end
|
|
24
24
|
##
|
25
25
|
# By default, just run the tests.
|
26
26
|
##
|
27
|
-
task :
|
28
|
-
|
27
|
+
task default: :spec
|
data/key_path.gemspec
CHANGED
@@ -8,16 +8,17 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = KeyPath::VERSION
|
9
9
|
spec.authors = ['Nick Charlton']
|
10
10
|
spec.email = ['nick@nickcharlton.net']
|
11
|
-
spec.description =
|
12
|
-
spec.summary =
|
13
|
-
|
11
|
+
spec.description = 'Keypath-based collection access extensions for Ruby.'
|
12
|
+
spec.summary = 'This gem allows you to access nested Ruby' \
|
13
|
+
'collections (Hash, Array, etc) using keypaths.' \
|
14
|
+
"e.g.: 'something.item.0'"
|
14
15
|
spec.homepage = 'https://github.com/nickcharlton/keypath-ruby'
|
15
16
|
spec.license = 'MIT'
|
16
17
|
|
17
|
-
spec.files = `git ls-files`.split(
|
18
|
-
spec.executables = spec.files.grep(
|
19
|
-
spec.test_files = spec.files.grep(
|
20
|
-
spec.require_paths = [
|
18
|
+
spec.files = `git ls-files`.split($RS)
|
19
|
+
spec.executables = spec.files.grep(/^bin/) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(/^(test|spec|features)/)
|
21
|
+
spec.require_paths = ['lib']
|
21
22
|
|
22
23
|
spec.add_dependency 'activesupport', '~> 4.0'
|
23
24
|
|
@@ -1,20 +1,18 @@
|
|
1
1
|
module Enumerable
|
2
2
|
# see: http://stackoverflow.com/a/7139631/83386
|
3
3
|
def value_at_keypath(keypath)
|
4
|
-
if keypath.is_a?(KeyPath::Path)
|
5
|
-
keypath = keypath.to_s
|
6
|
-
end
|
4
|
+
keypath = keypath.to_s if keypath.is_a?(KeyPath::Path)
|
7
5
|
|
8
6
|
parts = keypath.split '.', 2
|
9
|
-
|
7
|
+
|
10
8
|
# if it's an array, call the index
|
11
9
|
if self[parts[0].to_i]
|
12
10
|
match = self[parts[0].to_i]
|
13
11
|
else
|
14
12
|
match = self[parts[0]] || self[parts[0].to_sym]
|
15
13
|
end
|
16
|
-
|
17
|
-
if !parts[1]
|
14
|
+
|
15
|
+
if !parts[1] || match.nil?
|
18
16
|
return match
|
19
17
|
else
|
20
18
|
return match.value_at_keypath(parts[1])
|
@@ -23,13 +21,11 @@ module Enumerable
|
|
23
21
|
|
24
22
|
def set_keypath(keypath, value)
|
25
23
|
# handle both string and KeyPath::Path forms
|
26
|
-
if keypath.is_a?(String)
|
27
|
-
keypath = keypath.to_keypath
|
28
|
-
end
|
24
|
+
keypath = keypath.to_keypath if keypath.is_a?(String)
|
29
25
|
|
30
26
|
# create a collection at the keypath
|
31
27
|
collection = keypath.to_collection
|
32
|
-
|
28
|
+
|
33
29
|
# set the value in the collection
|
34
30
|
depth = ''
|
35
31
|
keypath.to_a.each do |e|
|
@@ -43,10 +39,13 @@ module Enumerable
|
|
43
39
|
end
|
44
40
|
|
45
41
|
# assign it
|
46
|
-
|
47
|
-
|
42
|
+
if value.is_a? String
|
43
|
+
eval "collection#{depth} = '#{value}'"
|
44
|
+
else
|
45
|
+
eval "collection#{depth} = #{value}"
|
46
|
+
end
|
47
|
+
|
48
48
|
# merge the new collection into self
|
49
49
|
self.deep_merge!(collection)
|
50
50
|
end
|
51
51
|
end
|
52
|
-
|
@@ -1,73 +1,78 @@
|
|
1
|
-
#
|
2
|
-
# = Hash Deep Merge
|
3
|
-
#
|
4
|
-
# Merges a Ruby Hash recursively, Also known as deep merge.
|
5
|
-
# Recursive version of Hash#merge and Hash#merge!.
|
6
|
-
#
|
7
|
-
# Category:: Ruby
|
8
|
-
# Package:: Hash
|
9
|
-
# Author:: Simone Carletti <weppos@weppos.net>
|
10
|
-
# Copyright:: 2007-2008 The Authors
|
11
|
-
# License:: MIT License
|
12
|
-
# Link:: http://www.simonecarletti.com/
|
13
|
-
# Source:: https://gist.github.com/weppos/6391
|
14
|
-
#
|
15
1
|
module KeyPath
|
2
|
+
#
|
3
|
+
# = Hash Deep Merge
|
4
|
+
#
|
5
|
+
# Merges a Ruby Hash recursively, Also known as deep merge.
|
6
|
+
# Recursive version of Hash#merge and Hash#merge!.
|
7
|
+
#
|
8
|
+
# Category:: Ruby
|
9
|
+
# Package:: Hash
|
10
|
+
# Author:: Simone Carletti <weppos@weppos.net>
|
11
|
+
# Copyright:: 2007-2008 The Authors
|
12
|
+
# License:: MIT License
|
13
|
+
# Link:: http://www.simonecarletti.com/
|
14
|
+
# Source:: https://gist.github.com/weppos/6391
|
15
|
+
#
|
16
16
|
module HashDeepMerge
|
17
17
|
#
|
18
18
|
# Recursive version of Hash#merge!
|
19
|
-
#
|
20
|
-
# Adds the contents of +other_hash+ to +hsh+,
|
19
|
+
#
|
20
|
+
# Adds the contents of +other_hash+ to +hsh+,
|
21
21
|
# merging entries in +hsh+ with duplicate keys with those from +other_hash+.
|
22
|
-
#
|
22
|
+
#
|
23
23
|
# Compared with Hash#merge!, this method supports nested hashes.
|
24
24
|
# When both +hsh+ and +other_hash+ contains an entry with the same key,
|
25
25
|
# it merges and returns the values from both arrays.
|
26
|
-
#
|
26
|
+
#
|
27
27
|
# h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
|
28
28
|
# h2 = {"b" => 254, "c" => 300, "c" => {"c1" => 16, "c3" => 94}}
|
29
|
-
# h1.rmerge!(h2) #=> {"a" => 100, "b" => 254,
|
30
|
-
#
|
29
|
+
# h1.rmerge!(h2) #=> {"a" => 100, "b" => 254,
|
30
|
+
# "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
|
31
|
+
#
|
31
32
|
# Simply using Hash#merge! would return
|
32
|
-
#
|
33
|
-
# h1.merge!(h2) #=> {"a" => 100, "b" = >254,
|
34
|
-
#
|
33
|
+
#
|
34
|
+
# h1.merge!(h2) #=> {"a" => 100, "b" = >254,
|
35
|
+
# "c" => {"c1" => 16, "c3" => 94}}
|
36
|
+
#
|
35
37
|
def deep_merge!(other_hash)
|
36
|
-
merge!(other_hash) do |
|
37
|
-
|
38
|
+
merge!(other_hash) do |_key, oldval, newval|
|
39
|
+
oldval.class == self.class ? oldval.deep_merge!(newval) : newval
|
38
40
|
end
|
39
41
|
end
|
40
|
-
|
42
|
+
|
41
43
|
#
|
42
44
|
# Recursive version of Hash#merge
|
43
|
-
#
|
45
|
+
#
|
44
46
|
# Compared with Hash#merge!, this method supports nested hashes.
|
45
47
|
# When both +hsh+ and +other_hash+ contains an entry with the same key,
|
46
48
|
# it merges and returns the values from both arrays.
|
47
|
-
#
|
49
|
+
#
|
48
50
|
# Compared with Hash#merge, this method provides a different approch
|
49
51
|
# for merging nasted hashes.
|
50
52
|
# If the value of a given key is an Hash and both +other_hash+ abd +hsh
|
51
53
|
# includes the same key, the value is merged instead replaced with
|
52
54
|
# +other_hash+ value.
|
53
|
-
#
|
55
|
+
#
|
54
56
|
# h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
|
55
57
|
# h2 = {"b" => 254, "c" => 300, "c" => {"c1" => 16, "c3" => 94}}
|
56
|
-
# h1.rmerge(h2) #=> {"a" => 100, "b" => 254,
|
57
|
-
#
|
58
|
+
# h1.rmerge(h2) #=> {"a" => 100, "b" => 254,
|
59
|
+
# "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
|
60
|
+
#
|
58
61
|
# Simply using Hash#merge would return
|
59
|
-
#
|
60
|
-
# h1.merge(h2) #=> {"a" => 100, "b" = >254,
|
61
|
-
#
|
62
|
+
#
|
63
|
+
# h1.merge(h2) #=> {"a" => 100, "b" = >254,
|
64
|
+
# "c" => {"c1" => 16, "c3" => 94}}
|
65
|
+
#
|
62
66
|
def deep_merge(other_hash)
|
63
67
|
r = {}
|
64
|
-
merge(other_hash) do |key, oldval, newval|
|
68
|
+
merge(other_hash) do |key, oldval, newval|
|
65
69
|
r[key] = oldval.class == self.class ? oldval.deep_merge(newval) : newval
|
66
70
|
end
|
67
71
|
end
|
68
72
|
end
|
69
73
|
end
|
70
74
|
|
75
|
+
# Mixin HashDeepMerge into the standard Hash.
|
71
76
|
class Hash
|
72
77
|
include KeyPath::HashDeepMerge
|
73
78
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module KeyPath
|
2
|
+
# A Mixin for Hash which allows us to create a path from a key.
|
2
3
|
module HashExtensions
|
3
|
-
def keypaths_for_nested_key(nested_key = '', nested_hash=self,
|
4
|
+
def keypaths_for_nested_key(nested_key = '', nested_hash = self,
|
5
|
+
path = [], all_values = {})
|
4
6
|
nested_hash.each do |k, v|
|
5
7
|
path << k.to_s # assemble the path from the key
|
6
8
|
case v
|
@@ -12,19 +14,19 @@ module KeyPath
|
|
12
14
|
path.pop # remove the array key
|
13
15
|
when Hash then keypaths_for_nested_key(nested_key, v, path, all_values)
|
14
16
|
else
|
15
|
-
if k == nested_key
|
16
|
-
|
17
|
-
end
|
17
|
+
all_values.merge!("#{path.join('.')}" => "#{v}") if k == nested_key
|
18
|
+
|
18
19
|
path.pop
|
19
20
|
end
|
20
21
|
end
|
21
22
|
path.pop
|
22
|
-
|
23
|
-
|
23
|
+
|
24
|
+
all_values
|
24
25
|
end
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
29
|
+
# Mix `HashExtensions` into `Hash`.
|
28
30
|
class Hash
|
29
31
|
include KeyPath::HashExtensions
|
30
32
|
end
|
data/lib/key_path/path.rb
CHANGED
@@ -1,17 +1,16 @@
|
|
1
1
|
module KeyPath
|
2
|
+
# The class representing a Path in a collection object.
|
2
3
|
class Path
|
3
|
-
def initialize(path='')
|
4
|
+
def initialize(path = '')
|
4
5
|
@path = path
|
5
6
|
end
|
6
7
|
|
7
8
|
def parent
|
8
|
-
s =
|
9
|
+
s = to_a
|
9
10
|
s.pop
|
10
11
|
|
11
12
|
# there's no parent if it's empty
|
12
|
-
if s == []
|
13
|
-
return nil
|
14
|
-
end
|
13
|
+
return nil if s == []
|
15
14
|
|
16
15
|
# otherwise, join them back together and pass back a path
|
17
16
|
s.join('.').to_keypath
|
@@ -27,10 +26,10 @@ module KeyPath
|
|
27
26
|
|
28
27
|
def to_collection
|
29
28
|
collection = {}
|
30
|
-
s =
|
29
|
+
s = to_a
|
31
30
|
depth = ''
|
32
31
|
|
33
|
-
s.
|
32
|
+
s.each do |e|
|
34
33
|
# assemble the key
|
35
34
|
if e.is_number?
|
36
35
|
key = "#{e}"
|
@@ -41,9 +40,7 @@ module KeyPath
|
|
41
40
|
|
42
41
|
# figure out the correct type to push
|
43
42
|
type = {}
|
44
|
-
if e.is_plural?
|
45
|
-
type = []
|
46
|
-
end
|
43
|
+
type = [] if e.is_plural?
|
47
44
|
|
48
45
|
# evaluate this stage
|
49
46
|
eval "collection#{depth} = #{type}"
|
@@ -53,7 +50,7 @@ module KeyPath
|
|
53
50
|
end
|
54
51
|
|
55
52
|
def inspect
|
56
|
-
"#<#{self.class.name}:#{
|
53
|
+
"#<#{self.class.name}:#{object_id} path=#{@path}>"
|
57
54
|
end
|
58
55
|
end
|
59
56
|
end
|
@@ -1,17 +1,18 @@
|
|
1
1
|
require 'active_support/inflector'
|
2
2
|
|
3
3
|
module KeyPath
|
4
|
+
# Extensions to `String`.
|
4
5
|
module StringExtensions
|
5
6
|
def to_keypath
|
6
7
|
KeyPath::Path.new self
|
7
8
|
end
|
8
9
|
|
9
10
|
def is_singular?
|
10
|
-
|
11
|
+
pluralize != self && singularize == self
|
11
12
|
end
|
12
13
|
|
13
14
|
def is_plural?
|
14
|
-
|
15
|
+
singularize != self && pluralize == self
|
15
16
|
end
|
16
17
|
|
17
18
|
def is_number?
|
@@ -20,6 +21,7 @@ module KeyPath
|
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
24
|
+
# Mix `StringExtensions` into `String`.
|
23
25
|
class String
|
24
26
|
include KeyPath::StringExtensions
|
25
27
|
end
|
data/lib/key_path/version.rb
CHANGED
data/lib/key_path.rb
CHANGED
@@ -3,40 +3,39 @@ require File.expand_path 'spec_helper.rb', __dir__
|
|
3
3
|
|
4
4
|
describe 'EnumerableExtensions' do
|
5
5
|
it 'adds methods to collections' do
|
6
|
-
hash = {:
|
6
|
+
hash = { id: 1 }
|
7
7
|
|
8
8
|
hash.must_respond_to 'value_at_keypath'
|
9
9
|
hash.must_respond_to 'set_keypath'
|
10
10
|
end
|
11
11
|
|
12
12
|
it 'can fetch simple path values' do
|
13
|
-
hash = {:
|
13
|
+
hash = { id: 1 }
|
14
14
|
|
15
15
|
hash.value_at_keypath('id').must_equal 1
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'can accept KeyPath::Path objects for keypaths' do
|
19
|
-
hash = {:
|
19
|
+
hash = { id: 1 }
|
20
20
|
keypath = KeyPath::Path.new('id')
|
21
21
|
|
22
22
|
hash.value_at_keypath(keypath).must_equal 1
|
23
23
|
end
|
24
24
|
|
25
|
-
|
26
25
|
it 'can fetch with a nested hash key path' do
|
27
|
-
hash = {:
|
26
|
+
hash = { item: { id: 1 } }
|
28
27
|
|
29
28
|
hash.value_at_keypath('item.id').must_equal 1
|
30
29
|
end
|
31
30
|
|
32
31
|
it 'can fetch a nested array object' do
|
33
|
-
hash = {:
|
32
|
+
hash = { items: [{ id: 1 }] }
|
34
33
|
|
35
|
-
hash.value_at_keypath('items.0').must_equal(
|
34
|
+
hash.value_at_keypath('items.0').must_equal(id: 1)
|
36
35
|
end
|
37
36
|
|
38
37
|
it 'can set a simple value using a keypath string' do
|
39
|
-
source = {:
|
38
|
+
source = { item: { id: {} } }
|
40
39
|
keypath = 'item.id'
|
41
40
|
value = 1
|
42
41
|
|
@@ -46,7 +45,7 @@ describe 'EnumerableExtensions' do
|
|
46
45
|
end
|
47
46
|
|
48
47
|
it 'can set a simple value using a KeyPath::Path object' do
|
49
|
-
source = {:
|
48
|
+
source = { item: { id: {} } }
|
50
49
|
keypath = KeyPath::Path.new('item.id')
|
51
50
|
value = 1
|
52
51
|
|
@@ -56,7 +55,7 @@ describe 'EnumerableExtensions' do
|
|
56
55
|
end
|
57
56
|
|
58
57
|
it 'can set a string value using a KeyPath::Path object' do
|
59
|
-
source = {:
|
58
|
+
source = { item: { id: {} } }
|
60
59
|
keypath = KeyPath::Path.new('item.id')
|
61
60
|
value = 'value'
|
62
61
|
|
@@ -65,11 +64,20 @@ describe 'EnumerableExtensions' do
|
|
65
64
|
source.value_at_keypath(keypath).must_equal(value)
|
66
65
|
end
|
67
66
|
|
67
|
+
it 'can set a string with capital letters' do
|
68
|
+
source = { item: { id: {} } }
|
69
|
+
keypath = KeyPath::Path.new('item.id')
|
70
|
+
value = 'VALUE'
|
71
|
+
|
72
|
+
source.set_keypath(keypath, value)
|
73
|
+
|
74
|
+
source.value_at_keypath(keypath).must_equal(value)
|
75
|
+
end
|
68
76
|
|
69
77
|
it 'can set a hash for a path' do
|
70
|
-
source = {:
|
78
|
+
source = { item: { id: {} } }
|
71
79
|
keypath = KeyPath::Path.new('item')
|
72
|
-
value = {:
|
80
|
+
value = { id: 1 }
|
73
81
|
|
74
82
|
source.set_keypath(keypath, value)
|
75
83
|
|
@@ -77,9 +85,9 @@ describe 'EnumerableExtensions' do
|
|
77
85
|
end
|
78
86
|
|
79
87
|
it 'can set a value in a nested array' do
|
80
|
-
source = {:
|
88
|
+
source = { items: [{ id: 1 }] }
|
81
89
|
keypath = KeyPath::Path.new('items.1')
|
82
|
-
value = {:
|
90
|
+
value = { id: 2 }
|
83
91
|
|
84
92
|
source.set_keypath(keypath, value)
|
85
93
|
|
@@ -3,16 +3,16 @@ require File.expand_path 'spec_helper.rb', __dir__
|
|
3
3
|
|
4
4
|
describe 'HashDeepMerge' do
|
5
5
|
it 'adds methods to the Hash class' do
|
6
|
-
hash = {:
|
6
|
+
hash = { id: 1 }
|
7
7
|
|
8
8
|
hash.must_respond_to 'deep_merge'
|
9
9
|
hash.must_respond_to 'deep_merge!'
|
10
10
|
end
|
11
11
|
|
12
12
|
it 'recursively adds two hashes' do
|
13
|
-
one = {:
|
14
|
-
two = {:
|
15
|
-
three = {:
|
13
|
+
one = { one: { id: 1 } }
|
14
|
+
two = { one: { url: 'https://nickcharlton.net' } }
|
15
|
+
three = { one: { id: 1, url: 'https://nickcharlton.net' } }
|
16
16
|
|
17
17
|
output = one.deep_merge(two)
|
18
18
|
|
@@ -21,9 +21,9 @@ describe 'HashDeepMerge' do
|
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'recursively adds two hashes in place' do
|
24
|
-
one = {:
|
25
|
-
two = {:
|
26
|
-
three = {:
|
24
|
+
one = { one: { id: 1 } }
|
25
|
+
two = { one: { url: 'https://nickcharlton.net' } }
|
26
|
+
three = { one: { id: 1, url: 'https://nickcharlton.net' } }
|
27
27
|
|
28
28
|
one.deep_merge!(two)
|
29
29
|
|
@@ -34,9 +34,9 @@ end
|
|
34
34
|
|
35
35
|
describe 'HashNormalMerge' do
|
36
36
|
it 'combines nested hashes without decending into them' do
|
37
|
-
one = {'a' => 100, 'b' => 200, 'c' => {'c1' => 12, 'c2' => 14}}
|
38
|
-
two = {'b' => 254, 'c' => 300, 'c' => {'c1' => 16, 'c3' => 94}}
|
39
|
-
expected = {'a' => 100, 'b' => 254, 'c' => {'c1' => 16, 'c3' => 94}}
|
37
|
+
one = { 'a' => 100, 'b' => 200, 'c' => { 'c1' => 12, 'c2' => 14 } }
|
38
|
+
two = { 'b' => 254, 'c' => 300, 'c' => { 'c1' => 16, 'c3' => 94 } }
|
39
|
+
expected = { 'a' => 100, 'b' => 254, 'c' => { 'c1' => 16, 'c3' => 94 } }
|
40
40
|
|
41
41
|
output = one.merge(two)
|
42
42
|
|
@@ -45,14 +45,13 @@ describe 'HashNormalMerge' do
|
|
45
45
|
end
|
46
46
|
|
47
47
|
it 'combines nested hashes without decending into them in place' do
|
48
|
-
one = {'a' => 100, 'b' => 200, 'c' => {'c1' => 12, 'c2' => 14}}
|
49
|
-
two = {'b' => 254, 'c' => 300, 'c' => {'c1' => 16, 'c3' => 94}}
|
50
|
-
expected = {'a' => 100, 'b' => 254, 'c' => {'c1' => 16, 'c3' => 94}}
|
48
|
+
one = { 'a' => 100, 'b' => 200, 'c' => { 'c1' => 12, 'c2' => 14 } }
|
49
|
+
two = { 'b' => 254, 'c' => 300, 'c' => { 'c1' => 16, 'c3' => 94 } }
|
50
|
+
expected = { 'a' => 100, 'b' => 254, 'c' => { 'c1' => 16, 'c3' => 94 } }
|
51
51
|
|
52
52
|
one.merge!(two)
|
53
53
|
|
54
54
|
one.must_be_kind_of Hash
|
55
55
|
one.must_equal expected
|
56
56
|
end
|
57
|
-
|
58
57
|
end
|
@@ -3,20 +3,20 @@ require File.expand_path 'spec_helper.rb', __dir__
|
|
3
3
|
|
4
4
|
describe 'HashExtensions' do
|
5
5
|
it 'adds methods to the Hash class' do
|
6
|
-
hash = {:
|
6
|
+
hash = { id: 1 }
|
7
7
|
|
8
8
|
hash.must_respond_to 'keypaths_for_nested_key'
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'can find keys in a nested hash' do
|
12
12
|
data = {
|
13
|
-
:
|
14
|
-
:
|
15
|
-
:
|
16
|
-
:
|
13
|
+
id: 1,
|
14
|
+
item: {
|
15
|
+
id: 2,
|
16
|
+
name: 'an item'
|
17
17
|
}
|
18
18
|
}
|
19
|
-
|
19
|
+
|
20
20
|
keypaths = data.keypaths_for_nested_key(:id)
|
21
21
|
|
22
22
|
keypaths.wont_be_nil
|
@@ -26,14 +26,13 @@ describe 'HashExtensions' do
|
|
26
26
|
|
27
27
|
it 'can find keys in nested array' do
|
28
28
|
data = {
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
32
|
-
:
|
33
|
-
},
|
34
|
-
|
35
|
-
:
|
36
|
-
:name => 'another item'
|
29
|
+
id: 1,
|
30
|
+
items: [{
|
31
|
+
id: 2,
|
32
|
+
name: 'an item'
|
33
|
+
}, {
|
34
|
+
id: 3,
|
35
|
+
name: 'another item'
|
37
36
|
}]
|
38
37
|
}
|
39
38
|
|
@@ -46,6 +45,7 @@ describe 'HashExtensions' do
|
|
46
45
|
end
|
47
46
|
|
48
47
|
it 'can handle walking into an otherwise unknown object' do
|
48
|
+
# Example class to demonstrate an unknown object.
|
49
49
|
class ExampleClass
|
50
50
|
def initialize
|
51
51
|
@name = 'Example Class'
|
@@ -53,7 +53,7 @@ describe 'HashExtensions' do
|
|
53
53
|
|
54
54
|
example = ExampleClass.new
|
55
55
|
|
56
|
-
data = {:
|
56
|
+
data = { items: example }
|
57
57
|
|
58
58
|
keypaths = data.keypaths_for_nested_key(:items)
|
59
59
|
|
data/spec/key_path_spec.rb
CHANGED
data/spec/path_spec.rb
CHANGED
@@ -14,7 +14,7 @@ describe 'KeyPath::Path main methods' do
|
|
14
14
|
path = KeyPath::Path.new('item.url')
|
15
15
|
|
16
16
|
path.to_a.must_be_kind_of Array
|
17
|
-
path.to_a.must_equal
|
17
|
+
path.to_a.must_equal %w(item url)
|
18
18
|
end
|
19
19
|
|
20
20
|
it 'spits out a useful inspect string' do
|
@@ -46,22 +46,21 @@ describe 'KeyPath::Path collections generation' do
|
|
46
46
|
|
47
47
|
it 'returns a nested hash for a single path unit' do
|
48
48
|
path = KeyPath::Path.new('item')
|
49
|
-
path.to_collection.must_equal(
|
49
|
+
path.to_collection.must_equal(item: {})
|
50
50
|
end
|
51
51
|
|
52
52
|
it 'returns a nested array when the key is plural' do
|
53
53
|
path = KeyPath::Path.new('items')
|
54
|
-
path.to_collection.must_equal(
|
54
|
+
path.to_collection.must_equal(items: [])
|
55
55
|
end
|
56
56
|
|
57
57
|
it 'returns a double nested array with a two set keypath' do
|
58
58
|
path = KeyPath::Path.new('item.id')
|
59
|
-
path.to_collection.must_equal(
|
59
|
+
path.to_collection.must_equal(item: { id: {} })
|
60
60
|
end
|
61
61
|
|
62
62
|
it 'returns a nested array with an item' do
|
63
63
|
path = KeyPath::Path.new('items.0.id')
|
64
|
-
path.to_collection.must_equal(
|
64
|
+
path.to_collection.must_equal(items: [{ id: {} }])
|
65
65
|
end
|
66
66
|
end
|
67
|
-
|
data/spec/spec_helper.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
# enable coveralls
|
5
|
-
Coveralls.wear!
|
1
|
+
require 'codeclimate-test-reporter'
|
2
|
+
CodeClimate::TestReporter.start
|
6
3
|
|
7
4
|
# test framework
|
8
5
|
require 'minitest/autorun'
|
@@ -10,4 +7,3 @@ require 'minitest/pride'
|
|
10
7
|
|
11
8
|
# pull in the library
|
12
9
|
require File.expand_path '../lib/key_path.rb', __dir__
|
13
|
-
|
@@ -12,7 +12,7 @@ describe 'StringExtensions' do
|
|
12
12
|
example_string.to_keypath.to_s.must_equal example_string
|
13
13
|
end
|
14
14
|
|
15
|
-
it 'can make a set of strings plural' do
|
15
|
+
it 'can make a set of strings plural' do
|
16
16
|
%w(word rail dress business).each do |v|
|
17
17
|
v.is_plural?.must_equal false
|
18
18
|
end
|
metadata
CHANGED
@@ -1,69 +1,69 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: key_path
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Charlton
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-04-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '4.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '4.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '1.3'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.3'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: pry
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
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
68
|
version: '0'
|
69
69
|
description: Keypath-based collection access extensions for Ruby.
|
@@ -73,9 +73,10 @@ executables: []
|
|
73
73
|
extensions: []
|
74
74
|
extra_rdoc_files: []
|
75
75
|
files:
|
76
|
-
- .coveralls.yml
|
77
|
-
- .gitignore
|
78
|
-
- .
|
76
|
+
- ".coveralls.yml"
|
77
|
+
- ".gitignore"
|
78
|
+
- ".ruby-version"
|
79
|
+
- ".travis.yml"
|
79
80
|
- Gemfile
|
80
81
|
- LICENSE
|
81
82
|
- README.md
|
@@ -105,21 +106,21 @@ require_paths:
|
|
105
106
|
- lib
|
106
107
|
required_ruby_version: !ruby/object:Gem::Requirement
|
107
108
|
requirements:
|
108
|
-
- -
|
109
|
+
- - ">="
|
109
110
|
- !ruby/object:Gem::Version
|
110
111
|
version: '0'
|
111
112
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
113
|
requirements:
|
113
|
-
- -
|
114
|
+
- - ">="
|
114
115
|
- !ruby/object:Gem::Version
|
115
116
|
version: '0'
|
116
117
|
requirements: []
|
117
118
|
rubyforge_project:
|
118
|
-
rubygems_version: 2.
|
119
|
+
rubygems_version: 2.4.5
|
119
120
|
signing_key:
|
120
121
|
specification_version: 4
|
121
|
-
summary: 'This gem allows you to access nested
|
122
|
-
using keypaths.
|
122
|
+
summary: 'This gem allows you to access nested Rubycollections (Hash, Array, etc)
|
123
|
+
using keypaths.e.g.: ''something.item.0'''
|
123
124
|
test_files:
|
124
125
|
- spec/enumerable_extensions_spec.rb
|
125
126
|
- spec/hash_deep_merge_spec.rb
|
@@ -128,4 +129,3 @@ test_files:
|
|
128
129
|
- spec/path_spec.rb
|
129
130
|
- spec/spec_helper.rb
|
130
131
|
- spec/string_extensions_spec.rb
|
131
|
-
has_rdoc:
|