activerecord_json_validator 0.4.2 → 0.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a61f1c0fe63a28ef0a8959b9b42b6213f10b2272
4
- data.tar.gz: ed3d3daa9ca75775ff430ce38d9571f1c9903679
3
+ metadata.gz: e916b93c42c1f418b5138f0680f1f3c6da4caca6
4
+ data.tar.gz: 891634217d8bf1d6430a919500bc05e0559e058a
5
5
  SHA512:
6
- metadata.gz: b45bd69284796f7eaca82efb36c445184d35dd3c7c04229246ffbf4a6a21d485c075c8260f883b72193eea6472683430daefb9bec2a284e08eda99855913f7eb
7
- data.tar.gz: 91237f18346a8b3e7816996d3b9740428cccd9b89a8c5c1f6f9ec5c24faf312249be6f94ab87cee66dc79a114648892826dd4a96b1b61f27addacfa211286409
6
+ metadata.gz: dcb7b8ae91f3eca385eab1d8480b9ad60072c6f5ec9cf777565e5279db47946a17f8c280deae9972fdeec22e3e81caa7fc221dee89d19cc1a821b27d7729fd74
7
+ data.tar.gz: 3eeda53bedb220bd51ea5cacf5e476cb6875acb8ef83fc78222f74e436ca4c3d89cd119544c904edae98a53e76cddd643a339b5654a8335acbc4fd20872c9e77
@@ -29,5 +29,4 @@ Gem::Specification.new do |spec|
29
29
 
30
30
  spec.add_dependency 'json-schema', '~> 2.5'
31
31
  spec.add_dependency 'activerecord', '>= 4.1.0', '< 5'
32
- spec.add_dependency 'multi_json', '~> 1.10'
33
32
  end
@@ -2,6 +2,7 @@ class JsonValidator < ActiveModel::EachValidator
2
2
  def initialize(options)
3
3
  options.reverse_merge!(message: :invalid_json)
4
4
  options.reverse_merge!(schema: nil)
5
+ options.reverse_merge!(options: {})
5
6
  @attributes = options[:attributes]
6
7
 
7
8
  super
@@ -22,18 +23,11 @@ class JsonValidator < ActiveModel::EachValidator
22
23
 
23
24
  # Validate the JSON value with a JSON schema path or String
24
25
  def validate_each(record, attribute, value)
25
- begin
26
- json_value = JSON.dump(value)
27
- rescue JSON::GeneratorError
28
- json_value = ''
29
- end
30
-
31
26
  # Validate value with JSON::Validator
32
- schema = fetch_schema_for_record(record)
33
- errors = ::JSON::Validator.fully_validate(schema, json_value)
27
+ errors = ::JSON::Validator.fully_validate(schema(record), validatable_value(value), options.fetch(:options))
34
28
 
35
29
  # Everything is good if we don’t have any errors and we got valid JSON value
36
- return true if errors.empty? && record.send(:"#{attribute}_invalid_json").blank?
30
+ return if errors.empty? && record.send(:"#{attribute}_invalid_json").blank?
37
31
 
38
32
  # Add error message to the attribute
39
33
  record.errors.add(attribute, options.fetch(:message), value: value)
@@ -42,7 +36,7 @@ class JsonValidator < ActiveModel::EachValidator
42
36
  protected
43
37
 
44
38
  # Redefine the setter method for the attributes, since we want to
45
- # catch any MultiJson::LoadError errors.
39
+ # catch JSON parsing errors.
46
40
  def inject_setter_method(klass, attributes)
47
41
  attributes.each do |attribute|
48
42
  klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
@@ -53,7 +47,7 @@ protected
53
47
  @#{attribute}_invalid_json = nil
54
48
  args = ::ActiveSupport::JSON.decode(args) if args.is_a?(::String)
55
49
  super(args)
56
- rescue MultiJson::LoadError, JSON::ParserError
50
+ rescue ActiveSupport::JSON.parse_error
57
51
  @#{attribute}_invalid_json = args
58
52
  super({})
59
53
  end
@@ -62,12 +56,15 @@ protected
62
56
  end
63
57
  end
64
58
 
65
- def fetch_schema_for_record(record)
59
+ def schema(record)
66
60
  schema = options.fetch(:schema)
67
61
  return schema unless schema.is_a?(Proc)
68
62
 
69
63
  record.instance_exec(&schema)
70
64
  end
71
- end
72
65
 
73
- JSONValidator = JsonValidator
66
+ def validatable_value(value)
67
+ return value if value.is_a?(String)
68
+ ::ActiveSupport::JSON.encode(value)
69
+ end
70
+ end
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module JSONValidator
3
- VERSION = '0.4.2'
3
+ VERSION = '0.5'
4
4
  end
5
5
  end
@@ -1,6 +1,9 @@
1
1
  require 'active_record'
2
2
  require 'json-schema'
3
- require 'multi_json'
4
3
 
5
4
  require 'active_record/json_validator/version'
6
5
  require 'active_record/json_validator/validator'
6
+
7
+ # NOTE: In case `"JSON"` is treated as an acronym by `ActiveSupport::Inflector`,
8
+ # make `JSONValidator` available too.
9
+ JSONValidator = JsonValidator
@@ -1,100 +1,133 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe JsonValidator do
4
- before do
5
- run_migration do
6
- create_table(:users, force: true) do |t|
7
- t.string :name
8
- t.text :profile
4
+ describe :initialize do
5
+ # NOTE: We do not explicitely call `JsonValidator.new` in the tests,
6
+ # because we let Rails (ActiveModel::Validations) do that when we call
7
+ # `validates … json: true` on the model.
8
+ #
9
+ # This allows us to test the constructor behavior when executed in
10
+ # different Rails versions that do not pass the same arguments to it.
11
+ before do
12
+ run_migration do
13
+ create_table(:users, force: true) do |t|
14
+ t.string :name
15
+ t.text :data
16
+ end
9
17
  end
10
- end
11
18
 
12
- json_schema = schema
13
- spawn_model :User do
14
- serialize :profile, JSON
15
- validates :name, presence: true
16
- validates :profile, presence: true, json: { schema: json_schema }
17
-
18
- def dynamic_json_schema
19
- {
20
- type: 'object',
21
- :'$schema' => 'http://json-schema.org/draft-03/schema',
22
- properties: {
23
- foo: { type: 'string', required: false },
24
- bar: { type: 'string', required: true }
25
- }
26
- }
19
+ spawn_model 'User' do
20
+ serialize :data, JSON
21
+ validates :data, json: true
27
22
  end
28
- end
29
- end
30
23
 
31
- let(:user) { User.create(attributes) }
32
- let(:schema) do
33
- {
34
- type: 'object',
35
- :'$schema' => 'http://json-schema.org/draft-03/schema',
36
- properties: {
37
- city: { type: 'string', required: false },
38
- country: { type: 'string', required: true }
39
- }
40
- }
41
- end
24
+ record.data = data
25
+ end
42
26
 
43
- context 'with blank JSON value' do
44
- let(:attributes) { { name: 'Samuel Garneau', profile: {} } }
45
- it { expect(user).to_not be_valid }
46
- end
27
+ let(:record) { User.new }
47
28
 
48
- context 'with invalid JSON value' do
49
- context 'as Ruby Hash' do
50
- let(:attributes) { { name: 'Samuel Garneau', profile: { city: 'Quebec City' } } }
51
- it { expect(user).to_not be_valid }
29
+ context 'with valid JSON data' do
30
+ let(:data) { 'What? This is not JSON at all.' }
31
+ it { expect(record.data_invalid_json).to eql(data) }
52
32
  end
53
33
 
54
- context 'as JSON string' do
55
- let(:attributes) { { name: 'Samuel Garneau', profile: '{ "city": "Quebec City" }' } }
56
- it { expect(user).to_not be_valid }
34
+ context 'with invalid JSON data' do
35
+ let(:data) { { foo: 'bar' } }
36
+ it { expect(record.data_invalid_json).to be_nil }
57
37
  end
58
38
  end
59
39
 
60
- context 'with valid JSON value' do
61
- context 'as Ruby Hash' do
62
- let(:attributes) { { name: 'Samuel Garneau', profile: { country: 'CA' } } }
63
- it { expect(user).to be_valid }
40
+ describe :validate_each do
41
+ let(:validator) { JsonValidator.new(options) }
42
+ let(:options) { { attributes: [attribute], options: { strict: true } } }
43
+ let(:validate_each!) { validator.validate_each(record, attribute, value) }
44
+
45
+ # Doubles
46
+ let(:attribute) { double(:attribute, to_s: 'attribute_name') }
47
+ let(:record) { double(:record, errors: record_errors) }
48
+ let(:record_errors) { double(:errors) }
49
+ let(:value) { double(:value) }
50
+ let(:schema) { double(:schema) }
51
+ let(:validatable_value) { double(:validatable_value) }
52
+ let(:validator_errors) { double(:validator_errors) }
53
+
54
+ before do
55
+ expect(validator).to receive(:schema).with(record).and_return(schema)
56
+ expect(validator).to receive(:validatable_value).with(value).and_return(validatable_value)
57
+ expect(::JSON::Validator).to receive(:fully_validate).with(schema, validatable_value, options[:options]).and_return(validator_errors)
64
58
  end
65
59
 
66
- context 'as JSON string' do
67
- let(:attributes) { { name: 'Samuel Garneau', profile: '{ "country": "CA" }' } }
68
- it { expect(user).to be_valid }
60
+ context 'with JSON::Validator errors' do
61
+ before do
62
+ expect(validator_errors).to receive(:empty?).and_return(false)
63
+ expect(record).not_to receive(:"#{attribute}_invalid_json")
64
+ expect(record_errors).to receive(:add).with(attribute, options[:message], value: value)
65
+ end
66
+
67
+ specify { validate_each! }
69
68
  end
70
- end
71
69
 
72
- context 'with malformed JSON string' do
73
- let(:attributes) { { name: 'Samuel Garneau', profile: 'foo:}bar' } }
70
+ context 'without JSON::Validator errors but with invalid JSON data' do
71
+ before do
72
+ expect(validator_errors).to receive(:empty?).and_return(true)
73
+ expect(record).to receive(:"#{attribute}_invalid_json").and_return('foo"{]')
74
+ expect(record_errors).to receive(:add).with(attribute, options[:message], value: value)
75
+ end
74
76
 
75
- specify do
76
- expect(user).to_not be_valid
77
- expect(user.profile).to eql({})
78
- expect(user.profile_invalid_json).to eql('foo:}bar')
77
+ specify { validate_each! }
78
+ end
79
+
80
+ context 'without JSON::Validator errors and valid JSON data' do
81
+ before do
82
+ expect(validator_errors).to receive(:empty?).and_return(true)
83
+ expect(record).to receive(:"#{attribute}_invalid_json").and_return(nil)
84
+ expect(record_errors).not_to receive(:add)
85
+ end
86
+
87
+ specify { validate_each! }
79
88
  end
80
89
  end
81
90
 
82
- context 'with lambda schema option' do
83
- # The dynamic schema makes `country` and `city` keys mandatory
84
- let(:schema) { -> { dynamic_json_schema } }
91
+ describe :schema do
92
+ let(:validator) { JsonValidator.new(options) }
93
+ let(:options) { { attributes: [:foo], schema: schema_option } }
94
+ let(:schema) { validator.send(:schema, record) }
95
+
96
+ context 'with non-Proc schema' do
97
+ let(:schema_option) { double(:schema) }
98
+ let(:record) { double(:record) }
85
99
 
86
- context 'with valid JSON value' do
87
- let(:attributes) { { name: 'Samuel Garneau', profile: { foo: 'bar', bar: 'foo' } } }
88
- it { expect(user).to be_valid }
100
+ it { expect(schema).to eql(schema_option) }
89
101
  end
90
102
 
91
- context 'with invalid JSON value' do
92
- let(:attributes) { { name: 'Samuel Garneau', profile: {} } }
93
- it { expect(user).not_to be_valid }
103
+ context 'with Proc schema' do
104
+ let(:schema_option) { -> { dynamic_schema } }
105
+ let(:record) { record_class.new }
106
+ let(:record_class) do
107
+ Class.new do
108
+ def dynamic_schema
109
+ :yay
110
+ end
111
+ end
112
+ end
113
+
114
+ it { expect(schema).to eql(:yay) }
94
115
  end
95
116
  end
96
117
 
97
- context 'with JSON inflection' do
98
- it { expect(JSONValidator).to equal(JsonValidator) }
118
+ describe :validatable_value do
119
+ let(:validator) { JsonValidator.new(options) }
120
+ let(:options) { { attributes: [:foo] } }
121
+ let(:validatable_value) { validator.send(:validatable_value, value) }
122
+
123
+ context 'with non-String value' do
124
+ let(:value) { { foo: 'bar' } }
125
+ it { expect(validatable_value).to eql('{"foo":"bar"}') }
126
+ end
127
+
128
+ context 'with String value' do
129
+ let(:value) { "{\"foo\":\"bar\"}" }
130
+ it { expect(validatable_value).to eql(value) }
131
+ end
99
132
  end
100
133
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord_json_validator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: '0.5'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rémi Prévost
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-29 00:00:00.000000000 Z
11
+ date: 2015-02-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -162,20 +162,6 @@ dependencies:
162
162
  - - "<"
163
163
  - !ruby/object:Gem::Version
164
164
  version: '5'
165
- - !ruby/object:Gem::Dependency
166
- name: multi_json
167
- requirement: !ruby/object:Gem::Requirement
168
- requirements:
169
- - - "~>"
170
- - !ruby/object:Gem::Version
171
- version: '1.10'
172
- type: :runtime
173
- prerelease: false
174
- version_requirements: !ruby/object:Gem::Requirement
175
- requirements:
176
- - - "~>"
177
- - !ruby/object:Gem::Version
178
- version: '1.10'
179
165
  description: ActiveRecord::JSONValidator makes it easy to validate JSON attributes
180
166
  with a JSON schema.
181
167
  email: