metahash-rb 1.0.4 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +15 -0
- data/README.md +34 -5
- data/lib/metahash.rb +17 -10
- data/lib/metahash/extensions/hash/to_metadata.rb +9 -0
- data/lib/metahash/metaclass.rb +19 -7
- data/lib/metahash/metadata.rb +25 -15
- data/lib/metahash/version.rb +1 -1
- data/spec/hash_spec.rb +9 -0
- data/spec/metadata_spec.rb +96 -0
- data/spec/spec_helper.rb +8 -1
- data/spec/support/sample_hashes.rb +9 -0
- metadata +7 -4
- data/tddium.yml +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c50e809216c3a73ef2046daea65464fa70523202
|
4
|
+
data.tar.gz: 24b5731caac076b56137652e1aef1811eb7af6e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 229d38b0eb040f5bc90e6eed6f2e173411647b1195d5544c0aeb1f3dc632f0f81dfb7d7a637b5b879f2de74c7d8dece3f71bf1e53e981df03799a962b024397d
|
7
|
+
data.tar.gz: d4c9b695e64e7ebb188997f1b0491e5e27268a838c9e1f7b902c8837f6801faa31c14cfed2727a4bb068209ffa3fda762996b33df5caed284bb328236144046a
|
data/.travis.yml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
language: ruby
|
2
|
+
bundler_args: --without guard
|
3
|
+
rvm:
|
4
|
+
- "2.0"
|
5
|
+
- "2.1"
|
6
|
+
- "2.1.1"
|
7
|
+
- ruby-head
|
8
|
+
script: "bundle exec rspec"
|
9
|
+
addons:
|
10
|
+
code_climate:
|
11
|
+
repo_token: 1d1b1a31bb3137d986297ad8a1ad5a3a1adbd70f0e8583d7eaf1dd4c0ab0bbe1
|
12
|
+
branches:
|
13
|
+
only: master
|
14
|
+
notifications:
|
15
|
+
email: false
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@ MetaHash
|
|
2
2
|
========
|
3
3
|
|
4
4
|
[![Gem Version](https://badge.fury.io/rb/metahash-rb.svg)](http://badge.fury.io/rb/metahash-rb)
|
5
|
-
[![](https://ci.
|
5
|
+
[![Build Status](https://travis-ci.org/NullVoxPopuli/MetaHash.svg)](https://travis-ci.org/NullVoxPopuli/MetaHash)
|
6
6
|
[![Code Climate](https://codeclimate.com/github/NullVoxPopuli/MetaHash/badges/gpa.svg)](https://codeclimate.com/github/NullVoxPopuli/MetaHash)
|
7
7
|
[![Dependency Status](https://gemnasium.com/NullVoxPopuli/MetaHash.svg)](https://gemnasium.com/NullVoxPopuli/MetaHash)
|
8
8
|
[![Test Coverage](https://codeclimate.com/github/NullVoxPopuli/MetaHash/badges/coverage.svg)](https://codeclimate.com/github/NullVoxPopuli/MetaHash)
|
@@ -14,18 +14,45 @@ Provides a subclass of Hash and a wrapper around Rails' serialize attribute for
|
|
14
14
|
## Examples
|
15
15
|
#### Access nested hashes using method / object syntax
|
16
16
|
|
17
|
+
Arand new Metadata objects act just like hashes:
|
18
|
+
|
17
19
|
h = Metadata.new
|
18
|
-
h
|
19
|
-
|
20
|
+
h
|
21
|
+
=> {}
|
22
|
+
|
23
|
+
Accessing nested data requires no wrapping conditions checking for existence of the rquested data:
|
24
|
+
|
25
|
+
h.outer.inner
|
26
|
+
=> {}
|
27
|
+
|
28
|
+
Possible real-word example:
|
29
|
+
|
30
|
+
if (min_numbers = h.password_rules.formats.numbers.minimum).present?
|
31
|
+
# some code using min_numbers
|
32
|
+
else
|
33
|
+
# data doesn't exist in h
|
34
|
+
|
35
|
+
h.password_rules.formats.numbers.minimum = 1
|
36
|
+
# h
|
37
|
+
# => { password_rules: { formats: { numbers: { minimum: 1 } } } }
|
38
|
+
end
|
20
39
|
|
21
40
|
#### Access to values stored in nested hashes via method call syntax
|
22
41
|
|
23
42
|
h = Metadata.new( { outer: { inner: { hash_key: "value" } } } )
|
24
|
-
|
43
|
+
|
44
|
+
h.outer.inner.hash_key
|
45
|
+
=> "value"
|
25
46
|
|
26
47
|
#### Set values for nested hash structures without the nested hashes having to be initially defined
|
27
48
|
|
28
|
-
h = Metadata.new
|
49
|
+
h = Metadata.new
|
50
|
+
h
|
51
|
+
=> {}
|
52
|
+
|
53
|
+
h.outer.inner = 2
|
54
|
+
h
|
55
|
+
=> { outer: { inner: 2 } }
|
29
56
|
|
30
57
|
## Using with ActiveRecord
|
31
58
|
|
@@ -54,3 +81,5 @@ This gem has been tested with Ruby 2.0, and rails 3.2, 4.1
|
|
54
81
|
3. Add Test(s)!
|
55
82
|
4. Commit your proposed changes
|
56
83
|
5. Submit a pull request
|
84
|
+
|
85
|
+
[![Analytics](https://ga-beacon.appspot.com/UA-54618821-1/your-repo/page-name)](https://github.com/igrigorik/ga-beacon)
|
data/lib/metahash.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "metahash/metaclass"
|
2
2
|
require "metahash/metadata"
|
3
|
+
require "metahash/extensions/hash/to_metadata"
|
3
4
|
require "metahash/version"
|
4
5
|
require 'active_record'
|
5
6
|
|
@@ -10,7 +11,7 @@ module MetaHash
|
|
10
11
|
# the hash by creating class Metadata < Hash; end
|
11
12
|
#
|
12
13
|
# @param [Symbol] serialized_field name of the field to convert to Metadata
|
13
|
-
def has_metadata(serialized_field = "metadata", serializer: JSON)
|
14
|
+
def has_metadata( serialized_field = "metadata", serializer: JSON )
|
14
15
|
# tell Active Record that the field is going to be JSON
|
15
16
|
# serialized, because JSON > YAML
|
16
17
|
serialize serialized_field, serializer
|
@@ -19,33 +20,39 @@ module MetaHash
|
|
19
20
|
# first check the type of the field
|
20
21
|
# proceed if hash, abort if Metadata
|
21
22
|
if record.has_attribute?(serialized_field)
|
22
|
-
if [Hash, NilClass].include?(record.send(serialized_field).class)
|
23
|
+
if [ Hash, NilClass ].include?( record.send( serialized_field ).class )
|
23
24
|
# alias the old method / field
|
24
25
|
backup_name = "#{serialized_field}_original".to_sym
|
26
|
+
|
25
27
|
# alias_method backup_name, serialized_field
|
26
|
-
record.define_singleton_method backup_name, record.method(serialized_field)
|
28
|
+
record.define_singleton_method( backup_name, record.method( serialized_field ) )
|
29
|
+
|
27
30
|
# name the metadata accessor the same as the original field
|
28
31
|
# rails should automatically serialize this on save
|
29
|
-
initial_value = record.send(backup_name) || {}
|
30
|
-
record.send("#{serialized_field}=", Metadata.new(initial_value))
|
32
|
+
initial_value = record.send( backup_name) || {}
|
33
|
+
record.send( "#{serialized_field}=", Metadata.new( initial_value ) )
|
31
34
|
end
|
32
35
|
end
|
33
36
|
end
|
34
37
|
|
38
|
+
build_save_hooks( serialized_field )
|
39
|
+
end
|
35
40
|
|
41
|
+
private
|
42
|
+
|
43
|
+
def build_save_hooks( serialized_field )
|
36
44
|
# create a before_save hook to store a pure Hash in the DB
|
37
45
|
before_save do |record|
|
38
|
-
@temp_metadata = record.send(serialized_field)
|
39
|
-
record.send("#{serialized_field}=", @temp_metadata.to_hash) if @temp_metadata
|
46
|
+
@temp_metadata = record.send( serialized_field )
|
47
|
+
record.send( "#{serialized_field}=", @temp_metadata.to_hash ) if @temp_metadata
|
40
48
|
end
|
41
49
|
|
42
50
|
# restore the metadata to the field
|
43
51
|
after_save do |record|
|
44
|
-
record.send("#{serialized_field}=", @temp_metadata) if @temp_metadata
|
52
|
+
record.send( "#{serialized_field}=", @temp_metadata ) if @temp_metadata
|
45
53
|
end
|
46
|
-
|
47
54
|
end
|
48
55
|
|
49
56
|
end
|
50
57
|
|
51
|
-
ActiveRecord::Base.send :extend, MetaHash
|
58
|
+
ActiveRecord::Base.send( :extend, MetaHash )
|
data/lib/metahash/metaclass.rb
CHANGED
@@ -4,16 +4,28 @@
|
|
4
4
|
# particular class
|
5
5
|
module Metaclass
|
6
6
|
# The hidden singleton lurks behind everyone
|
7
|
-
def metaclass
|
8
|
-
|
7
|
+
def metaclass
|
8
|
+
class << self
|
9
|
+
self
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def meta_eval(&block)
|
14
|
+
metaclass.instance_eval(&block)
|
15
|
+
end
|
9
16
|
|
10
17
|
# Adds methods to a metaclass
|
11
|
-
def meta_def(
|
12
|
-
meta_eval {
|
18
|
+
def meta_def(name, &block)
|
19
|
+
meta_eval {
|
20
|
+
define_method(name, &block)
|
21
|
+
}
|
13
22
|
end
|
14
23
|
|
24
|
+
# unused
|
15
25
|
# Defines an instance method within a class
|
16
|
-
def class_def(
|
17
|
-
|
18
|
-
|
26
|
+
# def class_def(name, &block)
|
27
|
+
# class_eval {
|
28
|
+
# define_method(name, &block)
|
29
|
+
# }
|
30
|
+
# end
|
19
31
|
end
|
data/lib/metahash/metadata.rb
CHANGED
@@ -44,7 +44,7 @@ class Metadata < Hash
|
|
44
44
|
# recursively create nested metadata objects
|
45
45
|
hash.each do |key, value|
|
46
46
|
|
47
|
-
self[key] = (
|
47
|
+
self[ key ] = (
|
48
48
|
if value.is_a?(Hash)
|
49
49
|
Metadata.new(value)
|
50
50
|
elsif value.is_a?(Array)
|
@@ -70,20 +70,7 @@ class Metadata < Hash
|
|
70
70
|
if (key = method_name.to_s).include?("=")
|
71
71
|
key = key.chop.to_sym
|
72
72
|
|
73
|
-
|
74
|
-
if not @empty_nested_hashes.empty?
|
75
|
-
@empty_nested_hashes.each do |key|
|
76
|
-
deepest_metadata = deepest_metadata[key] = Metadata.new
|
77
|
-
end
|
78
|
-
@empty_nested_hashes = []
|
79
|
-
deepest_metadata[key] = args[0]
|
80
|
-
# override any existing method with the key
|
81
|
-
deepest_metadata.meta_def(key){ self[key]}
|
82
|
-
else
|
83
|
-
self[key] = args[0]
|
84
|
-
# override any existing method with the key
|
85
|
-
self.meta_def(key){ args[0] }
|
86
|
-
end
|
73
|
+
assign_value(key, args[0])
|
87
74
|
else
|
88
75
|
value = self[method_name]
|
89
76
|
if not value
|
@@ -134,4 +121,27 @@ class Metadata < Hash
|
|
134
121
|
def to_ary
|
135
122
|
self.to_hash.to_a
|
136
123
|
end
|
124
|
+
|
125
|
+
alias_method :to_a, :to_ary
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def assign_value(key, value)
|
130
|
+
deepest_metadata = self
|
131
|
+
|
132
|
+
if not @empty_nested_hashes.empty?
|
133
|
+
@empty_nested_hashes.each do |key|
|
134
|
+
deepest_metadata = deepest_metadata[key] = Metadata.new
|
135
|
+
end
|
136
|
+
|
137
|
+
@empty_nested_hashes = []
|
138
|
+
deepest_metadata[key] = value
|
139
|
+
# override any existing method with the key
|
140
|
+
deepest_metadata.meta_def(key){ self[key] }
|
141
|
+
else
|
142
|
+
self[key] = value
|
143
|
+
# override any existing method with the key
|
144
|
+
self.meta_def(key){ value }
|
145
|
+
end
|
146
|
+
end
|
137
147
|
end
|
data/lib/metahash/version.rb
CHANGED
data/spec/hash_spec.rb
ADDED
data/spec/metadata_spec.rb
CHANGED
@@ -6,6 +6,95 @@ describe Metadata do
|
|
6
6
|
expect(Metadata.new).to be_kind_of(Hash)
|
7
7
|
end
|
8
8
|
|
9
|
+
context 'instance methods' do
|
10
|
+
let(:m){ Metadata.new(h) }
|
11
|
+
|
12
|
+
describe "[]" do
|
13
|
+
let(:key){h.keys.first}
|
14
|
+
|
15
|
+
it "retrieves with a string key" do
|
16
|
+
expect(m[key.to_s]).to eq h[key]
|
17
|
+
end
|
18
|
+
|
19
|
+
it "retrieves with a symbol key" do
|
20
|
+
expect(m[key.to_sym]).to eq h[key]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "[]=" do
|
25
|
+
|
26
|
+
it 'sets with a string key' do
|
27
|
+
m["a"] = 2
|
28
|
+
expect(m[:a]).to eq 2
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'sets with a symbol key' do
|
32
|
+
m[:b] = 2
|
33
|
+
expect(m["b"]).to eq 2
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "valid_key?" do
|
39
|
+
|
40
|
+
it "is true for keys that aren't methods" do
|
41
|
+
expect(m.valid_key?(:b)).to eq true
|
42
|
+
end
|
43
|
+
|
44
|
+
it "is false for existing methods" do
|
45
|
+
expect(m.valid_key?(:to_hash)).to eq false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "to_hash" do
|
50
|
+
|
51
|
+
it "converts to a hash" do
|
52
|
+
expect(m.to_hash).to_not be_kind_of Metadata
|
53
|
+
end
|
54
|
+
|
55
|
+
it "converts arrays" do
|
56
|
+
h = hash_with_array
|
57
|
+
m = Metadata.new(h)
|
58
|
+
expect(m.to_hash).to_not respond_to :array
|
59
|
+
expect(m.to_hash[:array]).to be_a Array
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "to_ary or to_a" do
|
65
|
+
|
66
|
+
it "converts the hash to an array" do
|
67
|
+
expect(m.to_a).to eq m.to_hash.to_a
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "initialize" do
|
73
|
+
|
74
|
+
it "copies hash contents" do
|
75
|
+
expect(Metadata.new(h)).to eq h
|
76
|
+
end
|
77
|
+
|
78
|
+
it "returns the argument" do
|
79
|
+
m = Metadata.new
|
80
|
+
expect(Metadata.new(m)).to eq m
|
81
|
+
end
|
82
|
+
|
83
|
+
it "creates metadata objects in an array" do
|
84
|
+
h = hash_with_array
|
85
|
+
m = Metadata.new(h)
|
86
|
+
|
87
|
+
expect(m.array.first.a).to eq 2
|
88
|
+
expect(m.array.last.b).to eq 3
|
89
|
+
end
|
90
|
+
|
91
|
+
it "raises an error with invalid input" do
|
92
|
+
expect{Metadata.new(2)}.to raise_error
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
9
98
|
describe "pruning empty hashes" do
|
10
99
|
let(:m){Metadata.new}
|
11
100
|
|
@@ -56,6 +145,13 @@ describe Metadata do
|
|
56
145
|
expect(m.a.keys).to eq 3
|
57
146
|
end
|
58
147
|
|
148
|
+
it "can overrid min and max of deeply nested hashes" do
|
149
|
+
m.password_rules.formats.digits.max = 3
|
150
|
+
m.password_rules.formats.digits.min = 2
|
151
|
+
|
152
|
+
expect(m.password_rules.formats.digits.min).to eq 2
|
153
|
+
expect(m.password_rules.formats.digits.max).to eq 3
|
154
|
+
end
|
59
155
|
end
|
60
156
|
|
61
157
|
|
data/spec/spec_helper.rb
CHANGED
@@ -1,11 +1,18 @@
|
|
1
1
|
|
2
2
|
require "rubygems"
|
3
3
|
require "bundler/setup"
|
4
|
-
|
4
|
+
|
5
5
|
require "pry-byebug" # binding.pry to debug!
|
6
|
+
|
7
|
+
# Coverage
|
6
8
|
require "codeclimate-test-reporter"
|
9
|
+
ENV['CODECLIMATE_REPO_TOKEN'] = "1d1b1a31bb3137d986297ad8a1ad5a3a1adbd70f0e8583d7eaf1dd4c0ab0bbe1"
|
7
10
|
CodeClimate::TestReporter.start
|
8
11
|
|
12
|
+
# This Gem
|
13
|
+
require "metahash"
|
14
|
+
|
15
|
+
|
9
16
|
ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
|
10
17
|
load File.dirname(__FILE__) + '/support/schema.rb'
|
11
18
|
Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each {|file| require file }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metahash-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- L. Preston Sego III
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -103,22 +103,24 @@ extra_rdoc_files: []
|
|
103
103
|
files:
|
104
104
|
- ".gitignore"
|
105
105
|
- ".rspec"
|
106
|
+
- ".travis.yml"
|
106
107
|
- Gemfile
|
107
108
|
- LICENSE
|
108
109
|
- README.md
|
109
110
|
- Rakefile
|
110
111
|
- lib/metahash.rb
|
112
|
+
- lib/metahash/extensions/hash/to_metadata.rb
|
111
113
|
- lib/metahash/metaclass.rb
|
112
114
|
- lib/metahash/metadata.rb
|
113
115
|
- lib/metahash/version.rb
|
114
116
|
- metahash.gemspec
|
117
|
+
- spec/hash_spec.rb
|
115
118
|
- spec/metadata_spec.rb
|
116
119
|
- spec/metahash_spec.rb
|
117
120
|
- spec/spec_helper.rb
|
118
121
|
- spec/support/models/test_object.rb
|
119
122
|
- spec/support/sample_hashes.rb
|
120
123
|
- spec/support/schema.rb
|
121
|
-
- tddium.yml
|
122
124
|
homepage: https://github.com/NullVoxPopuli/MetaHash
|
123
125
|
licenses:
|
124
126
|
- MIT
|
@@ -142,8 +144,9 @@ rubyforge_project:
|
|
142
144
|
rubygems_version: 2.4.1
|
143
145
|
signing_key:
|
144
146
|
specification_version: 4
|
145
|
-
summary: MetaHash-1.0
|
147
|
+
summary: MetaHash-1.1.0
|
146
148
|
test_files:
|
149
|
+
- spec/hash_spec.rb
|
147
150
|
- spec/metadata_spec.rb
|
148
151
|
- spec/metahash_spec.rb
|
149
152
|
- spec/spec_helper.rb
|
data/tddium.yml
DELETED