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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e916b93c42c1f418b5138f0680f1f3c6da4caca6
|
4
|
+
data.tar.gz: 891634217d8bf1d6430a919500bc05e0559e058a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dcb7b8ae91f3eca385eab1d8480b9ad60072c6f5ec9cf777565e5279db47946a17f8c280deae9972fdeec22e3e81caa7fc221dee89d19cc1a821b27d7729fd74
|
7
|
+
data.tar.gz: 3eeda53bedb220bd51ea5cacf5e476cb6875acb8ef83fc78222f74e436ca4c3d89cd119544c904edae98a53e76cddd643a339b5654a8335acbc4fd20872c9e77
|
@@ -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
|
-
|
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
|
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
|
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
|
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
|
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
|
-
|
66
|
+
def validatable_value(value)
|
67
|
+
return value if value.is_a?(String)
|
68
|
+
::ActiveSupport::JSON.encode(value)
|
69
|
+
end
|
70
|
+
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
|
data/spec/json_validator_spec.rb
CHANGED
@@ -1,100 +1,133 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe JsonValidator do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
32
|
-
|
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
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
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 '
|
55
|
-
let(:
|
56
|
-
it { expect(
|
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
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
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 '
|
67
|
-
|
68
|
-
|
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
|
-
|
73
|
-
|
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
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
83
|
-
|
84
|
-
let(:
|
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
|
-
|
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
|
92
|
-
let(:
|
93
|
-
|
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
|
-
|
98
|
-
|
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
|
+
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-
|
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:
|