dotted_hash 0.8
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/.gitignore +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/MIT-LICENSE +20 -0
- data/README.md +89 -0
- data/Rakefile +20 -0
- data/dotted_hash.gemspec +34 -0
- data/lib/dotted_hash.rb +131 -0
- data/lib/dotted_hash/version.rb +3 -0
- data/test/active_model_lint_unit.rb +13 -0
- data/test/dotted_hash_spec.rb +196 -0
- data/test/test_helper.rb +27 -0
- metadata +147 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 442525c1060bd5bbfcbd29eab42eacd400bcd21a
|
|
4
|
+
data.tar.gz: 64330ffcb55a2db31f48dad429ecc329bd086a74
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: eb8fdb956dcbfc3b5687e50250554a037ea9228803e4068bb20d174d3704e511eab959aa986f00c4b4fc6937c54bcfa77a777fc96601541e4ba8ec1b1e1dc8a7
|
|
7
|
+
data.tar.gz: 37d877e03ad53c404f82b5d5f7fe0fdb432ad8d7d4da174bb37a86a70a05c567d3a34d7c0d73dc6018906051f4745c2e8c37aa2dad6abf89bd4d61e8a7461b76
|
data/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Gemfile.lock
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2013 Ivan Stana
|
|
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/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2011 Karel Minarik
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# DottedHash
|
|
2
|
+
|
|
3
|
+
Recursive OpenStruct-like or Hash-like object. Uses ActiveModel.
|
|
4
|
+
|
|
5
|
+
Based on *Tire::Result::Item* with addition of writing attributes.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add this line to your application's Gemfile:
|
|
10
|
+
|
|
11
|
+
gem 'dotted_hash'
|
|
12
|
+
|
|
13
|
+
And then execute:
|
|
14
|
+
|
|
15
|
+
$ bundle
|
|
16
|
+
|
|
17
|
+
Or install it yourself as:
|
|
18
|
+
|
|
19
|
+
$ gem install dotted_hash
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
### Create
|
|
24
|
+
|
|
25
|
+
> DottedHash.new(id: 'duke', content: "Why I'm so great")
|
|
26
|
+
|
|
27
|
+
> document = DottedHash.new(id: 'duke', content: "Why I'm so great",
|
|
28
|
+
quotes: {
|
|
29
|
+
'Duke Nukem Forever' => ['Always bet on Duke!', 'Hail to the King, baby!'],
|
|
30
|
+
'Manhattan Project' => ['Confucius say... DIE!', 'Crouching mutant, hidden pipebomb!'] }
|
|
31
|
+
)
|
|
32
|
+
=> <DottedHash id: "duke", content: "Why I'm so great", quotes: <DottedHash Duke Nukem Forever:
|
|
33
|
+
["Always bet on Duke!", "Hail to the King, baby!"], Manhattan Project: ["Confucius say... DIE!", "Crouching mutant, hidden pipebomb!"]>>
|
|
34
|
+
|
|
35
|
+
### Read
|
|
36
|
+
|
|
37
|
+
> document.content
|
|
38
|
+
=> "Why I'm so great"
|
|
39
|
+
|
|
40
|
+
if key has spaces
|
|
41
|
+
|
|
42
|
+
> document.quotes['Duke Nukem Forever']
|
|
43
|
+
> document.quotes['Duke Nukem Forever']
|
|
44
|
+
|
|
45
|
+
nested
|
|
46
|
+
|
|
47
|
+
> documents.authors.creator.name
|
|
48
|
+
|
|
49
|
+
### Write
|
|
50
|
+
|
|
51
|
+
> document.name = 'Duke Nukem'
|
|
52
|
+
> document.quotes = ["I've got balls of steel.", 'Who wants some?']
|
|
53
|
+
|
|
54
|
+
recursively (also creates sub-DottedHashes if they don't exist)
|
|
55
|
+
|
|
56
|
+
> document.recursive_assign('authors.creator.name', 'Duke Nukem')
|
|
57
|
+
|
|
58
|
+
### Delete
|
|
59
|
+
|
|
60
|
+
> document.name = nil
|
|
61
|
+
|
|
62
|
+
### Get document in nice form
|
|
63
|
+
|
|
64
|
+
> document.to_hash
|
|
65
|
+
> document.to_json
|
|
66
|
+
|
|
67
|
+
### Deceive class name based on *_type* attribute if using *Rails* (if class exists)
|
|
68
|
+
|
|
69
|
+
> d = DottedHash.new(_type: 'cannon', name: 'BFG')
|
|
70
|
+
> d.class
|
|
71
|
+
=> Cannon
|
|
72
|
+
|
|
73
|
+
See source and tests for some more details.
|
|
74
|
+
|
|
75
|
+
## Contributing
|
|
76
|
+
|
|
77
|
+
1. Fork it
|
|
78
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
79
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
80
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
81
|
+
5. Create new Pull Request
|
|
82
|
+
|
|
83
|
+
## About
|
|
84
|
+
|
|
85
|
+
Original project: [https://github.com/karmi/tire](https://github.com/karmi/tire)
|
|
86
|
+
|
|
87
|
+
Author of modificated version: *Ivan Stana*
|
|
88
|
+
|
|
89
|
+
License: *MIT*
|
data/Rakefile
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
|
|
3
|
+
desc 'Run tests'
|
|
4
|
+
task :test do
|
|
5
|
+
puts
|
|
6
|
+
puts '****** Test::Unit'
|
|
7
|
+
|
|
8
|
+
Dir[File.join('test', "*unit.rb")].each do |unit|
|
|
9
|
+
puts unit
|
|
10
|
+
system("ruby #{unit}")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
puts
|
|
14
|
+
puts '****** RSpec'
|
|
15
|
+
|
|
16
|
+
Dir[File.join('test', "*spec.rb")].each do |unit|
|
|
17
|
+
puts unit
|
|
18
|
+
system("rspec #{unit}")
|
|
19
|
+
end
|
|
20
|
+
end
|
data/dotted_hash.gemspec
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'dotted_hash/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "dotted_hash"
|
|
8
|
+
spec.version = DottedHash::VERSION
|
|
9
|
+
spec.authors = ["Ivan Stana"]
|
|
10
|
+
spec.email = ["stiipa@centrum.sk"]
|
|
11
|
+
spec.description = %q{Recursive OpenStruct-like or Hash-like object. Based on Tire::Result::Item with addition of writing attributes.}
|
|
12
|
+
spec.summary = %q{Recursive OpenStruct-like object.}
|
|
13
|
+
spec.homepage = "http://github.com/istana/dotted_hash"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files`.split($/)
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.extra_rdoc_files = [ "README.md", "MIT-LICENSE" ]
|
|
22
|
+
spec.rdoc_options = [ "--charset=UTF-8" ]
|
|
23
|
+
|
|
24
|
+
spec.add_dependency "activemodel", ">= 3.0"
|
|
25
|
+
spec.add_dependency "activesupport"
|
|
26
|
+
|
|
27
|
+
unless defined?(JRUBY_VERSION)
|
|
28
|
+
spec.add_development_dependency "turn"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
|
32
|
+
spec.add_development_dependency "rake"
|
|
33
|
+
spec.add_development_dependency "rspec", "~> 2.13"
|
|
34
|
+
end
|
data/lib/dotted_hash.rb
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Taken from Tire::Results::Item
|
|
2
|
+
# but modified
|
|
3
|
+
require_relative "dotted_hash/version"
|
|
4
|
+
require 'active_model'
|
|
5
|
+
require 'active_support/core_ext/string/inflections'
|
|
6
|
+
require 'active_support/json'
|
|
7
|
+
|
|
8
|
+
class DottedHash
|
|
9
|
+
extend ActiveModel::Naming
|
|
10
|
+
include ActiveModel::Conversion
|
|
11
|
+
#include ActiveModel::Model
|
|
12
|
+
# Create new instance, recursively converting all Hashes to Item
|
|
13
|
+
# and leaving everything else alone.
|
|
14
|
+
#
|
|
15
|
+
# TODO: track depth
|
|
16
|
+
def initialize(args={})
|
|
17
|
+
raise ArgumentError, "Please pass a Hash-like object" unless args.respond_to?(:each_pair)
|
|
18
|
+
@attributes = {}
|
|
19
|
+
args.each_pair do |key, value|
|
|
20
|
+
assign_value(key, value)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def assign_value(key, value)
|
|
25
|
+
if value.is_a?(Array)
|
|
26
|
+
@attributes[key.to_sym] = value.map { |item| @attributes[key.to_sym] = item.is_a?(Hash) ? DottedHash.new(item.to_hash) : item }
|
|
27
|
+
else
|
|
28
|
+
@attributes[key.to_sym] = value.is_a?(Hash) ? DottedHash.new(value.to_hash) : value
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
private :assign_value
|
|
32
|
+
|
|
33
|
+
# Delegate method to a key in underlying hash, if present, otherwise return +nil+.
|
|
34
|
+
#
|
|
35
|
+
def method_missing(method_name, *arguments)
|
|
36
|
+
if method_name.to_s[-1] == '=' && arguments.size == 1
|
|
37
|
+
attribute = method_name.to_s.chop
|
|
38
|
+
value = arguments.first
|
|
39
|
+
assign_value(attribute, value)
|
|
40
|
+
else
|
|
41
|
+
@attributes[method_name.to_sym]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def respond_to?(method_name, include_private = false)
|
|
46
|
+
# answer to any write method
|
|
47
|
+
if method_name.to_s[-1] == '='
|
|
48
|
+
true
|
|
49
|
+
else
|
|
50
|
+
@attributes.has_key?(method_name.to_sym) || super
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def recursive_assign(key, value)
|
|
55
|
+
return nil if key.blank?
|
|
56
|
+
keys = key.split('.')
|
|
57
|
+
if keys.size > 1
|
|
58
|
+
key = keys.shift.to_sym
|
|
59
|
+
|
|
60
|
+
if !self.send(key)
|
|
61
|
+
self.send("#{key}=", DottedHash.new)
|
|
62
|
+
end
|
|
63
|
+
sub = self.send(key)
|
|
64
|
+
sub.send(:recursive_assign, keys.join('.'), value)
|
|
65
|
+
elsif keys.size == 1
|
|
66
|
+
self.send("#{keys.shift}=", value)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def [](key)
|
|
71
|
+
@attributes[key.to_sym]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def id
|
|
75
|
+
@attributes[:_id] || @attributes[:id]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def type
|
|
79
|
+
@attributes[:_type] || @attributes[:type]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def persisted?
|
|
83
|
+
!!id
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def errors
|
|
87
|
+
ActiveModel::Errors.new(self)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def valid?
|
|
91
|
+
true
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def to_key
|
|
95
|
+
persisted? ? [id] : nil
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def to_hash
|
|
99
|
+
@attributes.reduce({}) do |sum, item|
|
|
100
|
+
sum[ item.first ] = item.last.respond_to?(:to_hash) ? item.last.to_hash : item.last
|
|
101
|
+
sum
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
alias_method :to_h, :to_hash
|
|
106
|
+
|
|
107
|
+
def as_json(options=nil)
|
|
108
|
+
hash = to_hash
|
|
109
|
+
hash.respond_to?(:with_indifferent_access) ? hash.with_indifferent_access.as_json(options) : hash.as_json(options)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def to_json(options=nil)
|
|
113
|
+
as_json.to_json(options)
|
|
114
|
+
end
|
|
115
|
+
alias_method :to_indexed_json, :to_json
|
|
116
|
+
|
|
117
|
+
# Let's pretend we're someone else in Rails
|
|
118
|
+
#
|
|
119
|
+
def class
|
|
120
|
+
begin
|
|
121
|
+
defined?(::Rails) && @attributes[:_type] ? @attributes[:_type].camelize.constantize : super
|
|
122
|
+
rescue NameError
|
|
123
|
+
super
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def inspect
|
|
128
|
+
s = []; @attributes.each { |k,v| s << "#{k}: #{v.inspect}" }
|
|
129
|
+
%Q|<DottedHash#{self.class.to_s == 'DottedHash' ? '' : " (#{self.class})"} #{s.join(', ')}>|
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require_relative 'test_helper'
|
|
2
|
+
require 'test/unit'
|
|
3
|
+
|
|
4
|
+
class DottedHashTest < Test::Unit::TestCase
|
|
5
|
+
# ActiveModel compatibility tests
|
|
6
|
+
#
|
|
7
|
+
def setup
|
|
8
|
+
super
|
|
9
|
+
begin; Object.send(:remove_const, :Rails); rescue; end
|
|
10
|
+
@model = DottedHash.new :title => 'Test'
|
|
11
|
+
end
|
|
12
|
+
include ActiveModel::Lint::Tests
|
|
13
|
+
end
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
require_relative 'test_helper'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
describe DottedHash do
|
|
5
|
+
before :all do
|
|
6
|
+
begin; Object.send(:remove_const, :Rails); rescue; end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
context 'Basic' do
|
|
10
|
+
before :each do
|
|
11
|
+
@document = DottedHash.new(title: 'Test', author: { name: 'Kafka' },
|
|
12
|
+
awards: { best_fiction: {year: '1925' } })
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should be initialized with a Hash or Hash like object" do
|
|
16
|
+
expect{ DottedHash.new('FUUUUUUU') }.to raise_error(ArgumentError)
|
|
17
|
+
expect{ DottedHash.new(id: 1) }.not_to raise_error
|
|
18
|
+
DottedHash.new(id: 1).should_be_instance_of :N
|
|
19
|
+
expect(DottedHash.new(id: 1)).to be_instance_of(DottedHash)
|
|
20
|
+
|
|
21
|
+
expect do
|
|
22
|
+
class AlmostHash < Hash; end
|
|
23
|
+
DottedHash.new(AlmostHash.new(id: 1))
|
|
24
|
+
end.not_to raise_error
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should have an 'id' method" do
|
|
28
|
+
a = DottedHash.new(_id: 1)
|
|
29
|
+
b = DottedHash.new(id: 1)
|
|
30
|
+
expect(a.id).to eq(1)
|
|
31
|
+
expect(b.id).to eq(1)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "should have a 'type' method" do
|
|
35
|
+
a = DottedHash.new(_type: 'foo')
|
|
36
|
+
b = DottedHash.new(type: 'foo')
|
|
37
|
+
expect(a.type).to eq('foo')
|
|
38
|
+
expect(b.type).to eq('foo')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should respond to :to_indexed_json" do
|
|
42
|
+
expect(DottedHash.new).to respond_to(:to_indexed_json)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "should retrieve simple values from underlying hash" do
|
|
46
|
+
expect(@document[:title]).to eq('Test')
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "should retrieve hash values from underlying hash" do
|
|
50
|
+
expect(@document[:author][:name]).to eq('Kafka')
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should allow to retrieve value by methods" do
|
|
54
|
+
expect(@document.title).not_to be_nil
|
|
55
|
+
expect(@document.title).to eq('Test')
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "should implement respond_to? for proxied methods" do
|
|
59
|
+
expect(@document).to respond_to(:title)
|
|
60
|
+
expect(@document.respond_to?(:title)).to eql(true)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "should return nil for non-existing keys/methods" do
|
|
64
|
+
expect{ @document.whatever }.not_to raise_error
|
|
65
|
+
expect(@document.whatever).to be_nil
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "should not care about symbols or strings in keys" do
|
|
69
|
+
@document = DottedHash.new('title' => 'Test')
|
|
70
|
+
expect(@document.title).not_to be_nil
|
|
71
|
+
expect(@document.title).to eq('Test')
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "should not care about symbols or strings in composite keys" do
|
|
75
|
+
@document = DottedHash.new(highlight: { 'name.ngrams' => 'abc' })
|
|
76
|
+
|
|
77
|
+
expect(@document.highlight['name.ngrams']).not_to be_nil
|
|
78
|
+
expect(@document.highlight['name.ngrams']).to eq('abc')
|
|
79
|
+
expect(@document.highlight['name.ngrams']).to eql(@document.highlight['name.ngrams'.to_sym])
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "should allow to retrieve values from nested hashes" do
|
|
83
|
+
expect(@document.author.name).not_to be_nil
|
|
84
|
+
expect(@document.author.name).to eq('Kafka')
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "should wrap arrays" do
|
|
88
|
+
@document = DottedHash.new(stats: [1, 2, 3])
|
|
89
|
+
expect(@document.stats).to eq([1, 2, 3])
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "should wrap hashes in arrays" do
|
|
93
|
+
@document = DottedHash.new(comments: [{title: 'one'}, {title: 'two'}])
|
|
94
|
+
expect(@document.comments.size).to eql(2)
|
|
95
|
+
expect(@document.comments.first).to be_instance_of(DottedHash)
|
|
96
|
+
expect(@document.comments.first.title).to eq('one')
|
|
97
|
+
expect(@document.comments.last.title).to eq('two')
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "should allow simple writes" do
|
|
101
|
+
@document.geralt = 'of Rivia'
|
|
102
|
+
expect(@document.geralt).to eq('of Rivia')
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "should allow writes of arrays" do
|
|
106
|
+
@document.characters = ['Amazon', 'Necromancer', 'Barbarian']
|
|
107
|
+
expect(@document.characters).to eq(['Amazon', 'Necromancer', 'Barbarian'])
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "should wrap hashes in arrays by attribute write" do
|
|
111
|
+
@document.classes = [{vanilla: 'Sorceress'}, {expansion: 'Druid'}]
|
|
112
|
+
expect(@document.classes.size).to eql(2)
|
|
113
|
+
expect(@document.classes.first).to be_instance_of(DottedHash)
|
|
114
|
+
expect(@document.classes.first.vanilla).to eq('Sorceress')
|
|
115
|
+
expect(@document.classes.last.expansion).to eq('Druid')
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it "#recursive_assign" do
|
|
119
|
+
x = @document.recursive_assign(" ", "bar")
|
|
120
|
+
expect(x).to eq(nil)
|
|
121
|
+
|
|
122
|
+
expect{ @document.recursive_assign('authors.creator.name', 'Triss') }.not_to raise_error
|
|
123
|
+
expect(@document.authors).to be_instance_of(DottedHash)
|
|
124
|
+
expect(@document.authors.creator).to be_instance_of(DottedHash)
|
|
125
|
+
|
|
126
|
+
expect{ @document.authors.creator.name }.not_to raise_error
|
|
127
|
+
expect(@document.authors.creator.name).to eq('Triss')
|
|
128
|
+
|
|
129
|
+
expect{ @document.recursive_assign('authors.contributors', ['foo', 'bar']) }.not_to raise_error
|
|
130
|
+
|
|
131
|
+
expect(@document.authors.creator.name).to eq('Triss')
|
|
132
|
+
expect(@document.authors.contributors).to eq(['foo', 'bar'])
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it "should be an DottedHash instance" do
|
|
136
|
+
expect(@document).to be_instance_of(DottedHash)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it "should be convertible to hash" do
|
|
140
|
+
expect(@document.to_hash).to be_instance_of(Hash)
|
|
141
|
+
expect(@document.to_hash[:author]).to be_instance_of(Hash)
|
|
142
|
+
expect(@document.to_hash[:awards][:best_fiction]).to be_instance_of(Hash)
|
|
143
|
+
|
|
144
|
+
expect(@document.to_hash[:author][:name]).to eq('Kafka')
|
|
145
|
+
expect(@document.to_hash[:awards][:best_fiction][:year]).to eq('1925')
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it "should be convertible to JSON" do
|
|
149
|
+
expect(@document.as_json).to be_instance_of(Hash)
|
|
150
|
+
|
|
151
|
+
expect(@document.as_json(only: :title)['title']).to eq('Test')
|
|
152
|
+
expect(@document.as_json(only: :title)['author']).to be_nil
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it "should be inspectable" do
|
|
156
|
+
expect(@document.inspect).to match(/<DottedHash .* title|DottedHash .* author/)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
context "within Rails" do
|
|
160
|
+
|
|
161
|
+
before :each do
|
|
162
|
+
module ::Rails
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
class ::FakeRailsModel
|
|
166
|
+
extend ActiveModel::Naming
|
|
167
|
+
include ActiveModel::Conversion
|
|
168
|
+
def self.find(id, options); new; end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
@document = DottedHash.new(id: 1, _type: 'fake_rails_model', title: 'Test')
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it "should be an instance of model, based on _type" do
|
|
175
|
+
expect(@document.class).to eql(FakeRailsModel)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
it "should be inspectable with masquerade" do
|
|
179
|
+
expect(@document.inspect).to match(/<DottedHash \(FakeRailsModel\)/)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
it "should return proper singular and plural forms" do
|
|
183
|
+
expect(ActiveModel::Naming.singular(@document)).to eq('fake_rails_model')
|
|
184
|
+
expect(ActiveModel::Naming.plural(@document)).to eq('fake_rails_models')
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
it "should instantiate itself for deep hashes, not a Ruby class corresponding to type" do
|
|
188
|
+
document = DottedHash.new(_type: 'my_model', title: 'Test', author: { name: 'John' })
|
|
189
|
+
|
|
190
|
+
expect(document.class).to eq(DottedHash)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
ENV['DEBUG'] = 'true'
|
|
2
|
+
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'bundler/setup'
|
|
5
|
+
|
|
6
|
+
require 'pathname'
|
|
7
|
+
|
|
8
|
+
JRUBY = defined?(JRUBY_VERSION)
|
|
9
|
+
|
|
10
|
+
if ENV['JSON_LIBRARY']
|
|
11
|
+
puts "Using '#{ENV['JSON_LIBRARY']}' JSON library"
|
|
12
|
+
require ENV['JSON_LIBRARY']
|
|
13
|
+
else
|
|
14
|
+
require 'json'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
require_relative '../lib/dotted_hash'
|
|
18
|
+
|
|
19
|
+
if Module.const_defined?(:RSpec)
|
|
20
|
+
RSpec.configure do |config|
|
|
21
|
+
config.expect_with :rspec do |c|
|
|
22
|
+
c.syntax = :expect
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
|
metadata
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: dotted_hash
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: '0.8'
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ivan Stana
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2013-05-01 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activemodel
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - '>='
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '3.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - '>='
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '3.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: activesupport
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - '>='
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - '>='
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: turn
|
|
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: bundler
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ~>
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '1.3'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ~>
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '1.3'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rake
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - '>='
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - '>='
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: rspec
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ~>
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '2.13'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ~>
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '2.13'
|
|
97
|
+
description: Recursive OpenStruct-like or Hash-like object. Based on Tire::Result::Item
|
|
98
|
+
with addition of writing attributes.
|
|
99
|
+
email:
|
|
100
|
+
- stiipa@centrum.sk
|
|
101
|
+
executables: []
|
|
102
|
+
extensions: []
|
|
103
|
+
extra_rdoc_files:
|
|
104
|
+
- README.md
|
|
105
|
+
- MIT-LICENSE
|
|
106
|
+
files:
|
|
107
|
+
- .gitignore
|
|
108
|
+
- Gemfile
|
|
109
|
+
- LICENSE.txt
|
|
110
|
+
- MIT-LICENSE
|
|
111
|
+
- README.md
|
|
112
|
+
- Rakefile
|
|
113
|
+
- dotted_hash.gemspec
|
|
114
|
+
- lib/dotted_hash.rb
|
|
115
|
+
- lib/dotted_hash/version.rb
|
|
116
|
+
- test/active_model_lint_unit.rb
|
|
117
|
+
- test/dotted_hash_spec.rb
|
|
118
|
+
- test/test_helper.rb
|
|
119
|
+
homepage: http://github.com/istana/dotted_hash
|
|
120
|
+
licenses:
|
|
121
|
+
- MIT
|
|
122
|
+
metadata: {}
|
|
123
|
+
post_install_message:
|
|
124
|
+
rdoc_options:
|
|
125
|
+
- --charset=UTF-8
|
|
126
|
+
require_paths:
|
|
127
|
+
- lib
|
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
129
|
+
requirements:
|
|
130
|
+
- - '>='
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: '0'
|
|
133
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - '>='
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '0'
|
|
138
|
+
requirements: []
|
|
139
|
+
rubyforge_project:
|
|
140
|
+
rubygems_version: 2.0.3
|
|
141
|
+
signing_key:
|
|
142
|
+
specification_version: 4
|
|
143
|
+
summary: Recursive OpenStruct-like object.
|
|
144
|
+
test_files:
|
|
145
|
+
- test/active_model_lint_unit.rb
|
|
146
|
+
- test/dotted_hash_spec.rb
|
|
147
|
+
- test/test_helper.rb
|