key_path 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5bc45e3830cd4c5a9bbf20765cf382874a890927
4
+ data.tar.gz: fa82506141b7515dbc9b6f19cbf99f1dffb9f272
5
+ SHA512:
6
+ metadata.gz: 5f1817be54a72fce97109507cb259f1a19392efb7adc6dc4e492ee6c986126c869c6a387ac753b80ef3b604f8051813c19f372592f4c93f6f2827778d8daeeeb
7
+ data.tar.gz: 4d1aa2f3f87fb7973c6ac2f3296516d70bc38d6ddbbd9436d45a3e4dff60bccceed8c0f84af2e4e88121bdfe64e5998ca2c228bcfa5ac2300aa91c01422a2d31
data/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ service_name: travis-ci
data/.gitignore ADDED
@@ -0,0 +1,17 @@
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
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in keypath.gemspec
4
+ gemspec
5
+
6
+ gem 'rake'
7
+ gem 'coveralls', require: false
8
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Nick Charlton
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,85 @@
1
+ # keypath-based collection access extensions for Ruby.
2
+
3
+ [![Build Status](https://secure.travis-ci.org/nickcharlton/keypath-ruby.png?branch=master)](http://travis-ci.org/nickcharlton/keypath-ruby) [![Code Climate](https://codeclimate.com/github/nickcharlton/keypath-ruby.png)](https://codeclimate.com/github/nickcharlton/keypath-ruby) [![Test Coverage](https://coveralls.io/repos/nickcharlton/keypath-ruby/badge.png?branch=master)](https://coveralls.io/r/nickcharlton/keypath-ruby)
4
+
5
+ This gem allows you to access nested Ruby collections (`Hash`, `Array`, etc) using
6
+ keypaths.
7
+
8
+ For example, say you had a nested data structure like:
9
+
10
+ ```ruby
11
+ data = {
12
+ :item_one => {:id => 1, :url => 'http://nickcharlton.net/'},
13
+ :something_else => [
14
+ {
15
+ :id => 1,
16
+ :url => 'https://github.com/'
17
+ }
18
+ ]
19
+ }
20
+ ```
21
+
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
24
+ you might get back from a JSON document.
25
+
26
+ ## Installation
27
+
28
+ Add this line to your application's Gemfile:
29
+
30
+ gem 'keypath'
31
+
32
+ And then execute:
33
+
34
+ $ bundle
35
+
36
+ Or install it yourself as:
37
+
38
+ $ gem install keypath
39
+
40
+ ## Usage
41
+
42
+ `KeyPath` is at least two things. First, it's a class (actually, `KeyPath::Path`)
43
+ 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
45
+ `String` which allow you to use the native collection classes with keypaths.
46
+
47
+ ```ruby
48
+ require 'key_path'
49
+
50
+ data = {
51
+ :item => {
52
+ :url => 'http://nickcharlton.net'
53
+ }
54
+ }
55
+
56
+ # fetching a path
57
+ path = KeyPath::Path.new 'item.url'
58
+ data.value_at_keypath(path) #=> 'http://nickcharlton.net'
59
+
60
+ # finding all `:url` paths in a collection
61
+ data.keypaths_for_nested_key(:url) #=> {item.url => 'http://nickcharlton.net'}
62
+
63
+ # going back and forth from a string
64
+ path.to_s #=> 'item.url'
65
+ 'item.url'.to_keypath #=> #<KeyPath:70096895112220 path=item.url>
66
+
67
+ # get the parent of a keypath (or nil, if there isn't one)
68
+ path.parent #=> #<KeyPath:70096895221110 path=item>
69
+
70
+ # setting a path
71
+ data.set_keypath(path, 'http://github.com/')
72
+ ```
73
+
74
+ ## Contributing
75
+
76
+ 1. Fork it
77
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
78
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
79
+ 4. Push to the branch (`git push origin my-new-feature`)
80
+ 5. Create new Pull Request
81
+
82
+ ## Author
83
+
84
+ Copyright (c) 2013 Nick Charlton (<nick@nickcharlton.net>)
85
+
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ ##
2
+ # Initialise Bundler, catch errors.
3
+ ##
4
+ require 'bundler'
5
+ require 'bundler/gem_tasks'
6
+
7
+ begin
8
+ Bundler.setup(:default, :development)
9
+ rescue Bundler::BundlerError => e
10
+ $stderr.puts e.message
11
+ $stderr.puts "Run `bundle install` to install missing gems."
12
+ exit e.status_code
13
+ end
14
+
15
+ ##
16
+ # Configure the test suite.
17
+ ##
18
+ require 'rake/testtask'
19
+
20
+ Rake::TestTask.new :spec do |t|
21
+ t.test_files = Dir['spec/*_spec.rb']
22
+ end
23
+
24
+ ##
25
+ # By default, just run the tests.
26
+ ##
27
+ task :default => :spec
28
+
data/key_path.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'key_path/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'key_path'
8
+ spec.version = KeyPath::VERSION
9
+ spec.authors = ['Nick Charlton']
10
+ spec.email = ['nick@nickcharlton.net']
11
+ spec.description = %q{Keypath-based collection access extensions for Ruby.}
12
+ spec.summary = %q{This gem allows you to access nested Ruby collections
13
+ (Hash, Array, etc) using keypaths. E.g.: 'something.item.0'}
14
+ spec.homepage = 'https://github.com/nickcharlton/keypath-ruby'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency 'activesupport', '~> 4.0'
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.3'
25
+ spec.add_development_dependency 'rake'
26
+ spec.add_development_dependency 'pry'
27
+ end
@@ -0,0 +1,52 @@
1
+ module Enumerable
2
+ # see: http://stackoverflow.com/a/7139631/83386
3
+ def value_at_keypath(keypath)
4
+ if keypath.is_a?(KeyPath::Path)
5
+ keypath = keypath.to_s
6
+ end
7
+
8
+ parts = keypath.split '.', 2
9
+
10
+ # if it's an array, call the index
11
+ if self[parts[0].to_i]
12
+ match = self[parts[0].to_i]
13
+ else
14
+ match = self[parts[0]] || self[parts[0].to_sym]
15
+ end
16
+
17
+ if !parts[1] or match.nil?
18
+ return match
19
+ else
20
+ return match.value_at_keypath(parts[1])
21
+ end
22
+ end
23
+
24
+ def set_keypath(keypath, value)
25
+ # handle both string and KeyPath::Path forms
26
+ if keypath.is_a?(String)
27
+ keypath = keypath.to_keypath
28
+ end
29
+
30
+ # create a collection at the keypath
31
+ collection = keypath.to_collection
32
+
33
+ # set the value in the collection
34
+ depth = ''
35
+ keypath.to_a.each do |e|
36
+ # walk down set and make up the right place to assign
37
+ if e.is_number?
38
+ key = "#{e}"
39
+ else
40
+ key = ":#{e}"
41
+ end
42
+ depth << "[#{key}]"
43
+ end
44
+
45
+ # assign it
46
+ eval "collection#{depth} = #{value}"
47
+
48
+ # merge the new collection into self
49
+ self.deep_merge!(collection)
50
+ end
51
+ end
52
+
@@ -0,0 +1,73 @@
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
+ module KeyPath
16
+ module HashDeepMerge
17
+ #
18
+ # Recursive version of Hash#merge!
19
+ #
20
+ # Adds the contents of +other_hash+ to +hsh+,
21
+ # merging entries in +hsh+ with duplicate keys with those from +other_hash+.
22
+ #
23
+ # Compared with Hash#merge!, this method supports nested hashes.
24
+ # When both +hsh+ and +other_hash+ contains an entry with the same key,
25
+ # it merges and returns the values from both arrays.
26
+ #
27
+ # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
28
+ # h2 = {"b" => 254, "c" => 300, "c" => {"c1" => 16, "c3" => 94}}
29
+ # h1.rmerge!(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
30
+ #
31
+ # Simply using Hash#merge! would return
32
+ #
33
+ # h1.merge!(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
34
+ #
35
+ def deep_merge!(other_hash)
36
+ merge!(other_hash) do |key, oldval, newval|
37
+ oldval.class == self.class ? oldval.deep_merge!(newval) : newval
38
+ end
39
+ end
40
+
41
+ #
42
+ # Recursive version of Hash#merge
43
+ #
44
+ # Compared with Hash#merge!, this method supports nested hashes.
45
+ # When both +hsh+ and +other_hash+ contains an entry with the same key,
46
+ # it merges and returns the values from both arrays.
47
+ #
48
+ # Compared with Hash#merge, this method provides a different approch
49
+ # for merging nasted hashes.
50
+ # If the value of a given key is an Hash and both +other_hash+ abd +hsh
51
+ # includes the same key, the value is merged instead replaced with
52
+ # +other_hash+ value.
53
+ #
54
+ # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
55
+ # h2 = {"b" => 254, "c" => 300, "c" => {"c1" => 16, "c3" => 94}}
56
+ # h1.rmerge(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
57
+ #
58
+ # Simply using Hash#merge would return
59
+ #
60
+ # h1.merge(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
61
+ #
62
+ def deep_merge(other_hash)
63
+ r = {}
64
+ merge(other_hash) do |key, oldval, newval|
65
+ r[key] = oldval.class == self.class ? oldval.deep_merge(newval) : newval
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ class Hash
72
+ include KeyPath::HashDeepMerge
73
+ end
@@ -0,0 +1,30 @@
1
+ module KeyPath
2
+ module HashExtensions
3
+ def keypaths_for_nested_key(nested_key = '', nested_hash=self, path=[], all_values={})
4
+ nested_hash.each do |k, v|
5
+ path << k.to_s # assemble the path from the key
6
+ case v
7
+ when Array then
8
+ v.each_with_index do |item, i|
9
+ path << "#{i}" # add the array key
10
+ keypaths_for_nested_key(nested_key, item, path, all_values)
11
+ end
12
+ path.pop # remove the array key
13
+ when Hash then keypaths_for_nested_key(nested_key, v, path, all_values)
14
+ else
15
+ if k == nested_key
16
+ all_values.merge!({"#{path.join('.')}" => "#{v}"})
17
+ end
18
+ path.pop
19
+ end
20
+ end
21
+ path.pop
22
+
23
+ return all_values
24
+ end
25
+ end
26
+ end
27
+
28
+ class Hash
29
+ include KeyPath::HashExtensions
30
+ end
@@ -0,0 +1,59 @@
1
+ module KeyPath
2
+ class Path
3
+ def initialize(path='')
4
+ @path = path
5
+ end
6
+
7
+ def parent
8
+ s = self.to_a
9
+ s.pop
10
+
11
+ # there's no parent if it's empty
12
+ if s == []
13
+ return nil
14
+ end
15
+
16
+ # otherwise, join them back together and pass back a path
17
+ s.join('.').to_keypath
18
+ end
19
+
20
+ def to_s
21
+ @path
22
+ end
23
+
24
+ def to_a
25
+ @path.split('.')
26
+ end
27
+
28
+ def to_collection
29
+ collection = {}
30
+ s = self.to_a
31
+ depth = ''
32
+
33
+ s.each_with_index do |e, i|
34
+ # assemble the key
35
+ if e.is_number?
36
+ key = "#{e}"
37
+ else
38
+ key = ":#{e}"
39
+ end
40
+ depth << "[#{key}]"
41
+
42
+ # figure out the correct type to push
43
+ type = {}
44
+ if e.is_plural?
45
+ type = []
46
+ end
47
+
48
+ # evaluate this stage
49
+ eval "collection#{depth} = #{type}"
50
+ end
51
+
52
+ collection
53
+ end
54
+
55
+ def inspect
56
+ "#<#{self.class.name}:#{self.object_id} path=#{@path}>"
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,25 @@
1
+ require 'active_support/inflector'
2
+
3
+ module KeyPath
4
+ module StringExtensions
5
+ def to_keypath
6
+ KeyPath::Path.new self
7
+ end
8
+
9
+ def is_singular?
10
+ self.pluralize != self and self.singularize == self
11
+ end
12
+
13
+ def is_plural?
14
+ self.singularize != self and self.pluralize == self
15
+ end
16
+
17
+ def is_number?
18
+ true if Float(self) rescue false
19
+ end
20
+ end
21
+ end
22
+
23
+ class String
24
+ include KeyPath::StringExtensions
25
+ end
@@ -0,0 +1,3 @@
1
+ module KeyPath
2
+ VERSION = '1.0.0'
3
+ end
data/lib/key_path.rb ADDED
@@ -0,0 +1,14 @@
1
+ # gem bits
2
+ require 'key_path/version'
3
+ require 'key_path/path'
4
+
5
+ # extensions
6
+ require 'key_path/hash/deep_merge'
7
+ require 'key_path/hash/extensions'
8
+ require 'key_path/enumerable/extensions'
9
+ require 'key_path/string/extensions'
10
+
11
+
12
+ module KeyPath
13
+ # Your code goes here...
14
+ end
@@ -0,0 +1,88 @@
1
+ # test helpers
2
+ require File.expand_path 'spec_helper.rb', __dir__
3
+
4
+ describe 'EnumerableExtensions' do
5
+ it 'adds methods to collections' do
6
+ hash = {:id => 1}
7
+
8
+ hash.must_respond_to 'value_at_keypath'
9
+ hash.must_respond_to 'set_keypath'
10
+ end
11
+
12
+ it 'can fetch simple path values' do
13
+ hash = {:id => 1}
14
+
15
+ hash.value_at_keypath('id').must_equal 1
16
+ end
17
+
18
+ it 'can accept KeyPath::Path objects for keypaths' do
19
+ hash = {:id => 1}
20
+ keypath = KeyPath::Path.new('id')
21
+
22
+ hash.value_at_keypath(keypath).must_equal 1
23
+ end
24
+
25
+
26
+ it 'can fetch with a nested hash key path' do
27
+ hash = {:item => {:id => 1}}
28
+
29
+ hash.value_at_keypath('item.id').must_equal 1
30
+ end
31
+
32
+ it 'can fetch a nested array object' do
33
+ hash = {:items => [{:id => 1}]}
34
+
35
+ hash.value_at_keypath('items.0').must_equal({:id => 1})
36
+ end
37
+
38
+ it 'can set a simple value using a keypath string' do
39
+ source = {:item => {:id => {}}}
40
+ keypath = 'item.id'
41
+ value = 1
42
+
43
+ source.set_keypath(keypath, value)
44
+
45
+ source.value_at_keypath(keypath).must_equal(value)
46
+ end
47
+
48
+ it 'can set a simple value using a KeyPath::Path object' do
49
+ source = {:item => {:id => {}}}
50
+ keypath = KeyPath::Path.new('item.id')
51
+ value = 1
52
+
53
+ source.set_keypath(keypath, value)
54
+
55
+ source.value_at_keypath(keypath).must_equal(value)
56
+ end
57
+
58
+ it 'can set a string value using a KeyPath::Path object' do
59
+ source = {:item => {:id => {}}}
60
+ keypath = KeyPath::Path.new('item.id')
61
+ value = 'value'
62
+
63
+ source.set_keypath(keypath, value)
64
+
65
+ source.value_at_keypath(keypath).must_equal(value)
66
+ end
67
+
68
+
69
+ it 'can set a hash for a path' do
70
+ source = {:item => {:id => {}}}
71
+ keypath = KeyPath::Path.new('item')
72
+ value = {:id => 1}
73
+
74
+ source.set_keypath(keypath, value)
75
+
76
+ source.value_at_keypath(keypath).must_equal(value)
77
+ end
78
+
79
+ it 'can set a value in a nested array' do
80
+ source = {:items => [{:id => 1}]}
81
+ keypath = KeyPath::Path.new('items.1')
82
+ value = {:id => 2}
83
+
84
+ source.set_keypath(keypath, value)
85
+
86
+ source.value_at_keypath(keypath).must_equal(value)
87
+ end
88
+ end
@@ -0,0 +1,58 @@
1
+ # test helpers
2
+ require File.expand_path 'spec_helper.rb', __dir__
3
+
4
+ describe 'HashDeepMerge' do
5
+ it 'adds methods to the Hash class' do
6
+ hash = {:id => 1}
7
+
8
+ hash.must_respond_to 'deep_merge'
9
+ hash.must_respond_to 'deep_merge!'
10
+ end
11
+
12
+ it 'recursively adds two hashes' do
13
+ one = {:one => {:id => 1}}
14
+ two = {:one => {:url => 'http://nickcharlton.net'}}
15
+ three = {:one => {:id => 1, :url => 'http://nickcharlton.net'}}
16
+
17
+ output = one.deep_merge(two)
18
+
19
+ output.must_be_kind_of Hash
20
+ output.must_equal three
21
+ end
22
+
23
+ it 'recursively adds two hashes in place' do
24
+ one = {:one => {:id => 1}}
25
+ two = {:one => {:url => 'http://nickcharlton.net'}}
26
+ three = {:one => {:id => 1, :url => 'http://nickcharlton.net'}}
27
+
28
+ one.deep_merge!(two)
29
+
30
+ one.must_be_kind_of Hash
31
+ one.must_equal three
32
+ end
33
+ end
34
+
35
+ describe 'HashNormalMerge' do
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}}
40
+
41
+ output = one.merge(two)
42
+
43
+ output.must_be_kind_of Hash
44
+ output.must_equal expected
45
+ end
46
+
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}}
51
+
52
+ one.merge!(two)
53
+
54
+ one.must_be_kind_of Hash
55
+ one.must_equal expected
56
+ end
57
+
58
+ end
@@ -0,0 +1,64 @@
1
+ # test helpers
2
+ require File.expand_path 'spec_helper.rb', __dir__
3
+
4
+ describe 'HashExtensions' do
5
+ it 'adds methods to the Hash class' do
6
+ hash = {:id => 1}
7
+
8
+ hash.must_respond_to 'keypaths_for_nested_key'
9
+ end
10
+
11
+ it 'can find keys in a nested hash' do
12
+ data = {
13
+ :id => 1,
14
+ :item => {
15
+ :id => 2,
16
+ :name => 'an item'
17
+ }
18
+ }
19
+
20
+ keypaths = data.keypaths_for_nested_key(:id)
21
+
22
+ keypaths.wont_be_nil
23
+ keypaths.must_include 'id'
24
+ keypaths.must_include 'item.id'
25
+ end
26
+
27
+ it 'can find keys in nested array' do
28
+ data = {
29
+ :id => 1,
30
+ :items => [{
31
+ :id => 2,
32
+ :name => 'an item'
33
+ },
34
+ {
35
+ :id => 3,
36
+ :name => 'another item'
37
+ }]
38
+ }
39
+
40
+ keypaths = data.keypaths_for_nested_key(:id)
41
+
42
+ keypaths.wont_be_nil
43
+ keypaths.must_include 'id'
44
+ keypaths.must_include 'items.0.id'
45
+ keypaths.must_include 'items.1.id'
46
+ end
47
+
48
+ it 'can handle walking into an otherwise unknown object' do
49
+ class ExampleClass
50
+ def initialize
51
+ @name = 'Example Class'
52
+ end
53
+
54
+ example = ExampleClass.new
55
+
56
+ data = {:items => example}
57
+
58
+ keypaths = data.keypaths_for_nested_key(:items)
59
+
60
+ keypaths.wont_be_nil
61
+ keypaths.must_include 'items'
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,3 @@
1
+ # test helpers
2
+ require File.expand_path 'spec_helper.rb', __dir__
3
+
data/spec/path_spec.rb ADDED
@@ -0,0 +1,67 @@
1
+ # test helpers
2
+ require File.expand_path 'spec_helper.rb', __dir__
3
+
4
+ describe 'KeyPath::Path main methods' do
5
+ it 'creates a KeyPath instance from a string' do
6
+ path = KeyPath::Path.new('item.url')
7
+
8
+ path.wont_be_nil
9
+ path.to_s.must_be_kind_of String
10
+ path.to_s.must_equal 'item.url'
11
+ end
12
+
13
+ it 'outputs an array of items in the path' do
14
+ path = KeyPath::Path.new('item.url')
15
+
16
+ path.to_a.must_be_kind_of Array
17
+ path.to_a.must_equal ['item', 'url']
18
+ end
19
+
20
+ it 'spits out a useful inspect string' do
21
+ path = KeyPath::Path.new('item.url')
22
+
23
+ path.inspect.must_be_kind_of String
24
+ path.inspect.wont_be_nil
25
+ end
26
+
27
+ it 'returns a parent if one exists' do
28
+ path = KeyPath::Path.new('item.url')
29
+
30
+ path.parent.wont_be_nil
31
+ path.parent.must_be_kind_of KeyPath::Path
32
+ end
33
+
34
+ it 'returns nil if a parent doesnt exist' do
35
+ path = KeyPath::Path.new('item')
36
+
37
+ path.parent.must_be_nil
38
+ end
39
+ end
40
+
41
+ describe 'KeyPath::Path collections generation' do
42
+ it 'returns an empty hash with an empty path' do
43
+ path = KeyPath::Path.new('')
44
+ path.to_collection.must_equal({})
45
+ end
46
+
47
+ it 'returns a nested hash for a single path unit' do
48
+ path = KeyPath::Path.new('item')
49
+ path.to_collection.must_equal({:item => {}})
50
+ end
51
+
52
+ it 'returns a nested array when the key is plural' do
53
+ path = KeyPath::Path.new('items')
54
+ path.to_collection.must_equal({:items => []})
55
+ end
56
+
57
+ it 'returns a double nested array with a two set keypath' do
58
+ path = KeyPath::Path.new('item.id')
59
+ path.to_collection.must_equal({:item => {:id => {}}})
60
+ end
61
+
62
+ it 'returns a nested array with an item' do
63
+ path = KeyPath::Path.new('items.0.id')
64
+ path.to_collection.must_equal({:items => [{:id => {}}]})
65
+ end
66
+ end
67
+
@@ -0,0 +1,13 @@
1
+ # test coverage
2
+ require 'coveralls'
3
+
4
+ # enable coveralls
5
+ Coveralls.wear!
6
+
7
+ # test framework
8
+ require 'minitest/autorun'
9
+ require 'minitest/pride'
10
+
11
+ # pull in the library
12
+ require File.expand_path '../lib/key_path.rb', __dir__
13
+
@@ -0,0 +1,41 @@
1
+ # test helpers
2
+ require File.expand_path 'spec_helper.rb', __dir__
3
+
4
+ describe 'StringExtensions' do
5
+ it 'can create a KeyPath instance from itself' do
6
+ example_string = 'item.url'
7
+
8
+ example_string.to_keypath.wont_be_nil
9
+ example_string.to_keypath.must_be_kind_of KeyPath::Path
10
+
11
+ # around and around we go
12
+ example_string.to_keypath.to_s.must_equal example_string
13
+ end
14
+
15
+ it 'can make a set of strings plural' do
16
+ %w(word rail dress business).each do |v|
17
+ v.is_plural?.must_equal false
18
+ end
19
+
20
+ %w(words rails dresses businesses).each do |v|
21
+ v.is_plural?.must_equal true
22
+ end
23
+ end
24
+
25
+ it 'can make a set of string singular' do
26
+ %w(word rail dress business).each do |v|
27
+ v.is_singular?.must_equal true
28
+ end
29
+
30
+ %w(words rails dresses businesses).each do |v|
31
+ v.is_singular?.must_equal false
32
+ end
33
+ end
34
+
35
+ it 'can test if a string is actually a number' do
36
+ '0'.is_number?.must_equal true
37
+ '1234567890'.is_number?.must_equal true
38
+
39
+ 'item'.is_number?.must_equal false
40
+ end
41
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: key_path
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Nick Charlton
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Keypath-based collection access extensions for Ruby.
70
+ email:
71
+ - nick@nickcharlton.net
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .coveralls.yml
77
+ - .gitignore
78
+ - .travis.yml
79
+ - Gemfile
80
+ - LICENSE
81
+ - README.md
82
+ - Rakefile
83
+ - key_path.gemspec
84
+ - lib/key_path.rb
85
+ - lib/key_path/enumerable/extensions.rb
86
+ - lib/key_path/hash/deep_merge.rb
87
+ - lib/key_path/hash/extensions.rb
88
+ - lib/key_path/path.rb
89
+ - lib/key_path/string/extensions.rb
90
+ - lib/key_path/version.rb
91
+ - spec/enumerable_extensions_spec.rb
92
+ - spec/hash_deep_merge_spec.rb
93
+ - spec/hash_extensions_spec.rb
94
+ - spec/key_path_spec.rb
95
+ - spec/path_spec.rb
96
+ - spec/spec_helper.rb
97
+ - spec/string_extensions_spec.rb
98
+ homepage: https://github.com/nickcharlton/keypath-ruby
99
+ licenses:
100
+ - MIT
101
+ metadata: {}
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubyforge_project:
118
+ rubygems_version: 2.0.14
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: 'This gem allows you to access nested Ruby collections (Hash, Array, etc)
122
+ using keypaths. E.g.: ''something.item.0'''
123
+ test_files:
124
+ - spec/enumerable_extensions_spec.rb
125
+ - spec/hash_deep_merge_spec.rb
126
+ - spec/hash_extensions_spec.rb
127
+ - spec/key_path_spec.rb
128
+ - spec/path_spec.rb
129
+ - spec/spec_helper.rb
130
+ - spec/string_extensions_spec.rb
131
+ has_rdoc: