hash_serializer 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +18 -10
- data/hash_serializer.gemspec +3 -0
- data/lib/hash_serializer/helpers.rb +77 -66
- data/lib/hash_serializer/version.rb +1 -1
- data/spec/db/models.rb +24 -0
- data/spec/db/schema.rb +9 -0
- data/spec/hash_serializer/helpers_spec.rb +27 -69
- data/spec/spec_helper.rb +8 -0
- metadata +48 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3811178544221641ca9241597598ee9a9bacdc85
|
4
|
+
data.tar.gz: 951f34c4c7cebf51a944732d5f2bbb2f267ce545
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 661653f86ee26f9ef0e1d88adcb71610a2c77a87cd16e6fc2c72367d86070ff40f87e0ebce6a3b9f69ae06192c037913b9160b87571effa25daf30a00b636cbb
|
7
|
+
data.tar.gz: b0ac3ca415d0d2eb24e210ff7a687aee1d57161ec724407c5de7090cf73adff2a8568b8a2c7b25eaf846236ae393506de92420ce9459fdb5ed181793fa4f08f1
|
data/README.md
CHANGED
@@ -29,11 +29,13 @@ Or install it yourself as:
|
|
29
29
|
|
30
30
|
## Usage
|
31
31
|
|
32
|
-
|
32
|
+
### HashSerializer::Serializer
|
33
|
+
|
34
|
+
In a model with a JSON column (ex - `billing_fields`), use `HashSerializer::Serializer` serialize a Hash to JSON.
|
33
35
|
|
34
36
|
```ruby
|
35
37
|
class Customer < ActiveRecord::Base
|
36
|
-
serialize :billing_fields, HashSerializer
|
38
|
+
serialize :billing_fields, HashSerializer::Serializer
|
37
39
|
end
|
38
40
|
```
|
39
41
|
|
@@ -41,17 +43,23 @@ end
|
|
41
43
|
$ customer.billing_fields[:name]
|
42
44
|
=> John C. Bland II
|
43
45
|
|
44
|
-
This
|
46
|
+
This leaves the column open to any structure you want so it is great for development while equally terrible for data integrity.
|
47
|
+
|
48
|
+
### HashSerializer::Helpers
|
49
|
+
|
50
|
+
You can utilize the `HashSerializer::Helpers` to add JSON key validation.
|
45
51
|
|
46
52
|
```ruby
|
47
53
|
class Customer < ActiveRecord::Base
|
48
|
-
|
54
|
+
include HashSerializer::Helpers
|
55
|
+
|
56
|
+
serialize :billing_fields, HashSerializer::Serializer
|
49
57
|
|
50
58
|
validate :validate_billing_fields
|
51
59
|
|
52
60
|
def validate_billing_fields
|
53
61
|
invalid_keys = validate_hash_serializer :billing_hash, %w(name)
|
54
|
-
errors.add(:billing_fields, 'has invalid keys: #{invalid_fields.join(', ')}') unless invalid_keys.
|
62
|
+
errors.add(:billing_fields, 'has invalid keys: #{invalid_fields.join(', ')}') unless invalid_keys.blank?
|
55
63
|
end
|
56
64
|
end
|
57
65
|
```
|
@@ -61,13 +69,15 @@ end
|
|
61
69
|
$ customer.valid?
|
62
70
|
=> false
|
63
71
|
|
64
|
-
|
72
|
+
Some JSON keys may conflict with names on the including model. You can also generate custom methods for each key for direct access to the hash without using hash syntax. It also allows for determining if a value has changed.
|
65
73
|
|
66
74
|
```ruby
|
67
75
|
class Customer < ActiveRecord::Base
|
68
|
-
|
76
|
+
include HashSerializer::Helpers
|
77
|
+
|
78
|
+
serialize :billing_fields, HashSerializer::Serializer
|
69
79
|
|
70
|
-
|
80
|
+
hash_accessor_with_prefix :billing_fields, 'billing', %w(name)
|
71
81
|
end
|
72
82
|
```
|
73
83
|
|
@@ -88,8 +98,6 @@ end
|
|
88
98
|
$ customer.billing_fields_changed?
|
89
99
|
=> true
|
90
100
|
|
91
|
-
**NOTE:** To hide the methods, you can include them in a Concern and call the included methods within the `included` block.
|
92
|
-
|
93
101
|
## Development
|
94
102
|
|
95
103
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/hash_serializer.gemspec
CHANGED
@@ -30,4 +30,7 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.add_development_dependency 'rubocop', '~> 0.39.0'
|
31
31
|
spec.add_development_dependency 'simplecov', '~> 0.11.2'
|
32
32
|
spec.add_development_dependency 'yard', '~> 0.8.7.6'
|
33
|
+
spec.add_development_dependency 'sqlite3', '~> 1.3.11'
|
34
|
+
spec.add_development_dependency 'activerecord', '~> 4.2.6'
|
35
|
+
spec.add_development_dependency 'activerecord-nulldb-adapter', '~> 0.3.2'
|
33
36
|
end
|
@@ -1,88 +1,99 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
|
1
3
|
module HashSerializer
|
2
4
|
# Helper methods for generating methods from hash keys and validating keys
|
3
5
|
module Helpers
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
# Validates a Postgres JSON hash on an ActiveRecord model does not include extra keys. It prevents user created
|
10
|
+
# data on JSON column types.
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
# >> validate_hash_serializer :billing_hash, %w(name address city state)
|
14
|
+
#
|
15
|
+
# @param hash_name [Symbol, String]
|
16
|
+
# @param valid_keys [Array]
|
17
|
+
#
|
18
|
+
# @return [Array] a sorted Array of the invalid keys
|
19
|
+
def validate_hash_serializer_keys(hash_name, valid_keys)
|
20
|
+
return if send(hash_name).nil? # || !send("#{hash_name}_changed?")
|
16
21
|
|
17
|
-
|
18
|
-
|
22
|
+
invalid_keys = send(hash_name).keys.map(&:to_s) - valid_keys.map(&:to_s)
|
23
|
+
return if invalid_keys.empty?
|
19
24
|
|
20
|
-
|
25
|
+
invalid_keys.sort
|
26
|
+
end
|
21
27
|
end
|
22
28
|
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
# Helper methods available on the class
|
30
|
+
module ClassMethods
|
31
|
+
# Creates ActiveRecord model methods for a Postgres JSON hash
|
32
|
+
#
|
33
|
+
# Example:
|
34
|
+
# >> hash_accessor_with_prefix :stripe_oauth_fields, 'stripe_connect', VALID_STRIPE_OAUTH_FIELDS
|
35
|
+
#
|
36
|
+
# @param hash_name [String | Symbol]
|
37
|
+
# @param prefix [String] prefix for the generated methods
|
38
|
+
# @param keys [Array] array of strings to create methods
|
39
|
+
def hash_accessor_with_prefix(hash_name, prefix, *keys)
|
40
|
+
Array(keys).flatten.each do |key|
|
41
|
+
prefixed_key = "#{prefix}_#{key}"
|
34
42
|
|
35
|
-
|
36
|
-
|
43
|
+
# Ex - billing_token=
|
44
|
+
create_setter_methods(hash_name, prefixed_key, key)
|
37
45
|
|
38
|
-
|
39
|
-
|
46
|
+
# Ex - billing_token
|
47
|
+
create_getters(hash_name, prefixed_key, key)
|
40
48
|
|
41
|
-
|
42
|
-
|
49
|
+
# Ex - billing_token_changed?
|
50
|
+
create_changed_methods(prefixed_key)
|
51
|
+
end
|
43
52
|
end
|
44
|
-
end
|
45
53
|
|
46
|
-
|
54
|
+
private
|
47
55
|
|
48
|
-
|
49
|
-
|
50
|
-
|
56
|
+
def create_method(name, &block)
|
57
|
+
send(:define_method, name, &block)
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
51
61
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
+
# Creates setter methods with the prefixed name and adds @*_changed properties, if the value changed,
|
63
|
+
# and calls [hash]_will_change! to signify the hash has updated
|
64
|
+
#
|
65
|
+
# @param hash_name [String | Symbol]
|
66
|
+
# @param prefixed_key [String] the hash key with the desired prefix
|
67
|
+
# @param key [String] the non-prefixed hash key
|
68
|
+
def create_setter_methods(hash_name, prefixed_key, key)
|
69
|
+
create_method("#{prefixed_key}=") do |value|
|
70
|
+
# Set a variable to track whether the value changed
|
71
|
+
instance_variable_set("@#{prefixed_key}_changed", true) if send(prefixed_key) != value
|
62
72
|
|
63
|
-
|
64
|
-
|
65
|
-
|
73
|
+
# Store the value
|
74
|
+
send(hash_name)[key] = value
|
75
|
+
send("#{hash_name}_will_change!") if respond_to? "#{hash_name}_will_change!".to_sym
|
76
|
+
end
|
66
77
|
end
|
67
|
-
end
|
68
78
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
79
|
+
# Creates prefixed getter methods to access the hash value
|
80
|
+
#
|
81
|
+
# @param hash_name [String | Symbol]
|
82
|
+
# @param prefixed_key [String] the hash key with the desired prefix
|
83
|
+
# @param key [String] the non-prefixed hash key
|
84
|
+
def create_getters(hash_name, prefixed_key, key)
|
85
|
+
create_method(prefixed_key) do
|
86
|
+
send(hash_name)[key]
|
87
|
+
end
|
77
88
|
end
|
78
|
-
end
|
79
89
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
90
|
+
# Creates *_changed? methods referencing the @*_changed property created in create_setter_methods
|
91
|
+
#
|
92
|
+
# @param prefixed_key [String] the hash key with the desired prefix
|
93
|
+
def create_changed_methods(prefixed_key)
|
94
|
+
create_method("#{prefixed_key}_changed?") do
|
95
|
+
instance_variable_get("@#{prefixed_key}_changed") == true
|
96
|
+
end
|
86
97
|
end
|
87
98
|
end
|
88
99
|
end
|
data/spec/db/models.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
# Helper model for testing purposes
|
4
|
+
class CustomerWithHash < ActiveRecord::Base
|
5
|
+
include HashSerializer::Helpers
|
6
|
+
|
7
|
+
VALID_KEYS = %w(name zipcode).freeze
|
8
|
+
INVALID_KEYS = %w(invalid keys).freeze
|
9
|
+
HASH_PREFIX = 'billing_stuff'.freeze
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
super
|
13
|
+
|
14
|
+
@billing = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_writer(:billing)
|
18
|
+
attr_reader(:billing)
|
19
|
+
|
20
|
+
hash_accessor_with_prefix :billing, HASH_PREFIX, VALID_KEYS
|
21
|
+
|
22
|
+
def billing_will_change!
|
23
|
+
end
|
24
|
+
end
|
data/spec/db/schema.rb
ADDED
@@ -1,31 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
# Helper model for testing purposes
|
4
|
-
class HelpersTestModel
|
5
|
-
include HashSerializer::Helpers
|
6
|
-
|
7
|
-
def initialize(hash = {})
|
8
|
-
@hash = hash
|
9
|
-
end
|
10
|
-
|
11
|
-
def billing=(hash)
|
12
|
-
@hash = hash
|
13
|
-
end
|
14
|
-
|
15
|
-
def billing
|
16
|
-
@hash
|
17
|
-
end
|
18
|
-
|
19
|
-
def billing_will_change!
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
3
|
describe HashSerializer::Helpers do
|
24
4
|
let(:hash) { { name: 'John C. Bland II', zipcode: 78_377 } }
|
25
5
|
|
26
|
-
subject {
|
27
|
-
let(:valid_keys) { %w(name zipcode) }
|
28
|
-
let(:invalid_keys) { %w(invalid keys) }
|
6
|
+
subject { CustomerWithHash.new }
|
29
7
|
|
30
8
|
context 'validate_hash_serializer_keys' do
|
31
9
|
it 'should return invalid keys' do
|
@@ -33,66 +11,46 @@ describe HashSerializer::Helpers do
|
|
33
11
|
hash['keys'] = false
|
34
12
|
|
35
13
|
subject.billing = hash
|
36
|
-
|
14
|
+
|
15
|
+
errors = subject.validate_hash_serializer_keys(:billing, CustomerWithHash::VALID_KEYS)
|
16
|
+
|
17
|
+
expect(errors).to eq(CustomerWithHash::INVALID_KEYS)
|
37
18
|
end
|
38
19
|
|
39
20
|
it 'should return nil if no invalid keys exist' do
|
40
|
-
expect(subject.validate_hash_serializer_keys(:billing,
|
21
|
+
expect(subject.validate_hash_serializer_keys(:billing, CustomerWithHash::VALID_KEYS)).to be_nil
|
41
22
|
end
|
42
23
|
end
|
43
24
|
|
44
25
|
context 'hash_accessor_with_prefix' do
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
it 'setters do not exist' do
|
49
|
-
expect(subject.respond_to?(:billing_stuff_name=)).to be_falsey
|
50
|
-
expect(subject.respond_to?(:billing_stuff_zipcode=)).to be_falsey
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'getters do not exist' do
|
54
|
-
expect(subject.respond_to?(:billing_stuff_name)).to be_falsey
|
55
|
-
expect(subject.respond_to?(:billing_stuff_zipcode)).to be_falsey
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'changed? methods exist' do
|
59
|
-
expect(subject.respond_to?(:billing_stuff_name_changed?)).to be_falsey
|
60
|
-
expect(subject.respond_to?(:billing_stuff_zipcode_changed?)).to be_falsey
|
61
|
-
end
|
26
|
+
it 'setters exist' do
|
27
|
+
expect(subject.respond_to?(:billing_stuff_name=)).to be_truthy
|
28
|
+
expect(subject.respond_to?(:billing_stuff_zipcode=)).to be_truthy
|
62
29
|
end
|
63
30
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
expect(subject.respond_to?(:billing_stuff_name=)).to be_truthy
|
69
|
-
expect(subject.respond_to?(:billing_stuff_zipcode=)).to be_truthy
|
70
|
-
end
|
71
|
-
|
72
|
-
it 'getters exist' do
|
73
|
-
expect(subject.respond_to?(:billing_stuff_name)).to be_truthy
|
74
|
-
expect(subject.respond_to?(:billing_stuff_zipcode)).to be_truthy
|
75
|
-
end
|
31
|
+
it 'getters exist' do
|
32
|
+
expect(subject.respond_to?(:billing_stuff_name)).to be_truthy
|
33
|
+
expect(subject.respond_to?(:billing_stuff_zipcode)).to be_truthy
|
34
|
+
end
|
76
35
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
36
|
+
it 'changed? methods exist' do
|
37
|
+
expect(subject.respond_to?(:billing_stuff_name_changed?)).to be_truthy
|
38
|
+
expect(subject.respond_to?(:billing_stuff_zipcode_changed?)).to be_truthy
|
39
|
+
end
|
81
40
|
|
82
|
-
|
83
|
-
|
84
|
-
|
41
|
+
it 'methods works' do
|
42
|
+
new_name = 'John Bland III'
|
43
|
+
new_zipcode = 85_008
|
85
44
|
|
86
|
-
|
45
|
+
expect(subject).to receive(:billing_will_change!).twice
|
87
46
|
|
88
|
-
|
89
|
-
|
47
|
+
subject.billing_stuff_name = new_name
|
48
|
+
subject.billing_stuff_zipcode = new_zipcode
|
90
49
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
end
|
50
|
+
expect(subject.billing_stuff_name).to eq(new_name)
|
51
|
+
expect(subject.billing_stuff_name_changed?).to be_truthy
|
52
|
+
expect(subject.billing_stuff_zipcode).to eq(new_zipcode)
|
53
|
+
expect(subject.billing_stuff_zipcode_changed?).to be_truthy
|
96
54
|
end
|
97
55
|
end
|
98
56
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'simplecov'
|
2
|
+
require 'active_record'
|
2
3
|
|
3
4
|
SimpleCov.start
|
4
5
|
SimpleCov.minimum_coverage 99
|
@@ -11,3 +12,10 @@ end
|
|
11
12
|
|
12
13
|
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
13
14
|
require 'hash_serializer'
|
15
|
+
|
16
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3',
|
17
|
+
database: ':memory:',
|
18
|
+
schema: 'spec/db/schema.rb')
|
19
|
+
|
20
|
+
load 'db/schema.rb'
|
21
|
+
require 'db/models.rb'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hash_serializer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John C. Bland II
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-05-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -148,6 +148,48 @@ dependencies:
|
|
148
148
|
- - "~>"
|
149
149
|
- !ruby/object:Gem::Version
|
150
150
|
version: 0.8.7.6
|
151
|
+
- !ruby/object:Gem::Dependency
|
152
|
+
name: sqlite3
|
153
|
+
requirement: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - "~>"
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 1.3.11
|
158
|
+
type: :development
|
159
|
+
prerelease: false
|
160
|
+
version_requirements: !ruby/object:Gem::Requirement
|
161
|
+
requirements:
|
162
|
+
- - "~>"
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
version: 1.3.11
|
165
|
+
- !ruby/object:Gem::Dependency
|
166
|
+
name: activerecord
|
167
|
+
requirement: !ruby/object:Gem::Requirement
|
168
|
+
requirements:
|
169
|
+
- - "~>"
|
170
|
+
- !ruby/object:Gem::Version
|
171
|
+
version: 4.2.6
|
172
|
+
type: :development
|
173
|
+
prerelease: false
|
174
|
+
version_requirements: !ruby/object:Gem::Requirement
|
175
|
+
requirements:
|
176
|
+
- - "~>"
|
177
|
+
- !ruby/object:Gem::Version
|
178
|
+
version: 4.2.6
|
179
|
+
- !ruby/object:Gem::Dependency
|
180
|
+
name: activerecord-nulldb-adapter
|
181
|
+
requirement: !ruby/object:Gem::Requirement
|
182
|
+
requirements:
|
183
|
+
- - "~>"
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
version: 0.3.2
|
186
|
+
type: :development
|
187
|
+
prerelease: false
|
188
|
+
version_requirements: !ruby/object:Gem::Requirement
|
189
|
+
requirements:
|
190
|
+
- - "~>"
|
191
|
+
- !ruby/object:Gem::Version
|
192
|
+
version: 0.3.2
|
151
193
|
description: A simple Hash to JSON serializer with some helpers to improve JSON model
|
152
194
|
columns.
|
153
195
|
email:
|
@@ -177,6 +219,8 @@ files:
|
|
177
219
|
- lib/hash_serializer/helpers.rb
|
178
220
|
- lib/hash_serializer/serializer.rb
|
179
221
|
- lib/hash_serializer/version.rb
|
222
|
+
- spec/db/models.rb
|
223
|
+
- spec/db/schema.rb
|
180
224
|
- spec/hash_serializer/helpers_spec.rb
|
181
225
|
- spec/hash_serializer/serializer_spec.rb
|
182
226
|
- spec/hash_serializer/version_spec.rb
|
@@ -208,6 +252,8 @@ specification_version: 4
|
|
208
252
|
summary: A simple Hash to JSON serializer with some helpers to improve JSON model
|
209
253
|
columns.
|
210
254
|
test_files:
|
255
|
+
- spec/db/models.rb
|
256
|
+
- spec/db/schema.rb
|
211
257
|
- spec/hash_serializer/helpers_spec.rb
|
212
258
|
- spec/hash_serializer/serializer_spec.rb
|
213
259
|
- spec/hash_serializer/version_spec.rb
|