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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f6cf348b789ab8f2b2dea3a478b14ed8e641aff1
4
- data.tar.gz: 5aa67c8330bd05d27a33f1dbc03bd4a85a273de1
3
+ metadata.gz: 3811178544221641ca9241597598ee9a9bacdc85
4
+ data.tar.gz: 951f34c4c7cebf51a944732d5f2bbb2f267ce545
5
5
  SHA512:
6
- metadata.gz: 91cfa3a79393b29cee0238f98b99939174b5337df5b4f4ff8b3984cd87bc54a481e26ed399bbbabb6bb49eed3437b58ae96b88cac0e3e6a8c0691bce31468e83
7
- data.tar.gz: 2af4cf496ca4e34b1962829335af462f983b747958b797128f407f428b36446874bad80efee49c5a71b8520b4a62d164ff4377ff5d95499d8515347a23483212
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
- In a model with a JSON column (ex - `billing_fields`) and serialize it:
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 does leave the column open to any keys so it is great for development while equally terrible for security. You can utilize the `HashSerializer::Helpers` to add key validation:
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
- serialize :billing_fields, HashSerializer
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.empty?
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
- Since some JSON keys may be best served with conflicting names to the housed 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.
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
- serialize :billing_fields, HashSerializer
76
+ include HashSerializer::Helpers
77
+
78
+ serialize :billing_fields, HashSerializer::Serializer
69
79
 
70
- store_accessor_with_prefix :billing_fields, 'billing', %w(name)
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.
@@ -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
- # Validates a Postgres JSON hash on an ActiveRecord model does not include extra keys. It prevents user created
5
- # data on JSON column types.
6
- #
7
- # Example:
8
- # >> validate_hash_serializer :billing_hash, %w(name address city state)
9
- #
10
- # @param hash_name [Symbol, String]
11
- # @param valid_keys [Array]
12
- #
13
- # @return [Array] a sorted Array of the invalid keys
14
- def validate_hash_serializer_keys(hash_name, valid_keys)
15
- return if send(hash_name).nil? # || !send("#{hash_name}_changed?")
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
- invalid_keys = send(hash_name).keys.map(&:to_s) - valid_keys.map(&:to_s)
18
- return if invalid_keys.empty?
22
+ invalid_keys = send(hash_name).keys.map(&:to_s) - valid_keys.map(&:to_s)
23
+ return if invalid_keys.empty?
19
24
 
20
- invalid_keys.sort
25
+ invalid_keys.sort
26
+ end
21
27
  end
22
28
 
23
- # Creates ActiveRecord model methods for a Postgres JSON hash
24
- #
25
- # Example:
26
- # >> store_accessor_with_prefix :stripe_oauth_fields, 'stripe_connect', VALID_STRIPE_OAUTH_FIELDS
27
- #
28
- # @param hash [Hash]
29
- # @param prefix [String] prefix for the generated methods
30
- # @param keys [Array] array of strings to create methods
31
- def hash_accessor_with_prefix(hash, prefix, *keys)
32
- Array(keys).flatten.each do |key|
33
- prefixed_key = "#{prefix}_#{key}"
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
- # Ex - billing_token=
36
- create_setter_methods(hash, prefixed_key, key)
43
+ # Ex - billing_token=
44
+ create_setter_methods(hash_name, prefixed_key, key)
37
45
 
38
- # Ex - billing_token
39
- create_getters(hash, prefixed_key, key)
46
+ # Ex - billing_token
47
+ create_getters(hash_name, prefixed_key, key)
40
48
 
41
- # Ex - billing_token_changed?
42
- create_changed_methods(prefixed_key)
49
+ # Ex - billing_token_changed?
50
+ create_changed_methods(prefixed_key)
51
+ end
43
52
  end
44
- end
45
53
 
46
- protected
54
+ private
47
55
 
48
- def create_method(name, &block)
49
- self.class.send(:define_method, name, &block)
50
- end
56
+ def create_method(name, &block)
57
+ send(:define_method, name, &block)
58
+ end
59
+
60
+ protected
51
61
 
52
- # Creates setter methods with the prefixed name and adds @*_changed properties, if the value changed,
53
- # and calls [hash]_will_change! to signify the hash has updated
54
- #
55
- # @param hash [Hash]
56
- # @param prefixed_key [String] the hash key with the desired prefix
57
- # @param key [String] the non-prefixed hash key
58
- def create_setter_methods(hash, prefixed_key, key)
59
- create_method("#{prefixed_key}=") do |value|
60
- # Set a variable to track whether the value changed
61
- instance_variable_set("@#{prefixed_key}_changed", true) if send(hash)[key] != value
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
- # Store the value
64
- send(hash)[key] = value
65
- send("#{hash}_will_change!") if respond_to? "#{hash}_will_change!".to_sym
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
- # Creates prefixed getter methods to access the hash value
70
- #
71
- # @param hash [Hash]
72
- # @param prefixed_key [String] the hash key with the desired prefix
73
- # @param key [String] the non-prefixed hash key
74
- def create_getters(hash, prefixed_key, key)
75
- create_method(prefixed_key) do
76
- send(hash)[key]
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
- # Creates *_changed? methods referencing the @*_changed property created in create_setter_methods
81
- #
82
- # @param prefixed_key [String] the hash key with the desired prefix
83
- def create_changed_methods(prefixed_key)
84
- create_method("#{prefixed_key}_changed?") do
85
- instance_variable_get("@#{prefixed_key}_changed") == true
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
@@ -1,5 +1,5 @@
1
1
  # HashSerializer Version
2
2
  module HashSerializer
3
3
  # Current version
4
- VERSION = '0.1.1'.freeze
4
+ VERSION = '0.2.0'.freeze
5
5
  end
@@ -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
@@ -0,0 +1,9 @@
1
+ require 'active_record'
2
+
3
+ ActiveRecord::Schema.define do
4
+ self.verbose = false
5
+
6
+ create_table :customer_with_hashes, force: true do |t|
7
+ t.string :text
8
+ end
9
+ end
@@ -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 { HelpersTestModel.new }
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
- expect(subject.validate_hash_serializer_keys(:billing, valid_keys)).to eq(invalid_keys)
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, valid_keys)).to be_nil
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
- let(:prefix) { 'billing_stuff' }
46
-
47
- context 'not called' do
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
- context 'called' do
65
- before(:each) { subject.hash_accessor_with_prefix(:billing, prefix, valid_keys) }
66
-
67
- it 'setters exist' do
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
- it 'changed? methods exist' do
78
- expect(subject.respond_to?(:billing_stuff_name_changed?)).to be_truthy
79
- expect(subject.respond_to?(:billing_stuff_zipcode_changed?)).to be_truthy
80
- end
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
- it 'methods works' do
83
- new_name = 'John Bland III'
84
- new_zipcode = 85_008
41
+ it 'methods works' do
42
+ new_name = 'John Bland III'
43
+ new_zipcode = 85_008
85
44
 
86
- expect(subject).to receive(:billing_will_change!).twice
45
+ expect(subject).to receive(:billing_will_change!).twice
87
46
 
88
- subject.billing_stuff_name = new_name
89
- subject.billing_stuff_zipcode = new_zipcode
47
+ subject.billing_stuff_name = new_name
48
+ subject.billing_stuff_zipcode = new_zipcode
90
49
 
91
- expect(subject.billing_stuff_name).to eq(new_name)
92
- expect(subject.billing_stuff_name_changed?).to be_truthy
93
- expect(subject.billing_stuff_zipcode).to eq(new_zipcode)
94
- expect(subject.billing_stuff_zipcode_changed?).to be_truthy
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
@@ -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.1.1
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-04-28 00:00:00.000000000 Z
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