activerecord_json_validator 1.3.0 → 3.1.0

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
  SHA256:
3
- metadata.gz: d9dce365377ebd51d3f5b8156e3cf928507014ed1278c0ca794675b4fecd3d2c
4
- data.tar.gz: b1f58a7b215df70ff616dd80ee31dd24dd8fa0b0020189ee99318fb577a38621
3
+ metadata.gz: 90252b743670b3c94e3ac700b868c698cf9b94124f115fa7b39b27b63f66445a
4
+ data.tar.gz: 8de9a2eeb74864be691a63ecb9a27f286d669949221b78f70db62ecbb3f9b62c
5
5
  SHA512:
6
- metadata.gz: 3b0f0f8e654453f06fd16d31324b8d3cfa069411e206cbc3b58575987984bda9ece9156411121d5202c701fd4dcece6ed5ab7b40eac5d5116a02f3347f2e0a4c
7
- data.tar.gz: 355f0fec168526d3e881efb0b9ef5434089d2bc531adeb6f6dc9af61ea96dbb4a30600d744babd82dd57df2b6157d36a1d4e9ed837af902295d8419b7e5bfe91
6
+ metadata.gz: abdacc70526c1c9be8b5d3585283eaa594fe0c48116aab7eac9ca19a5553fcae69a409986e3793eb96c9ab80624d74f8556c69c0f3994a88987a5d83f9a84266
7
+ data.tar.gz: a170d66db92dcc1bc0e3eb9ebe75fb939ccecb85d952cc18b590aa8404562ddf0093edbe4993d1a16b11ba1efa5135e78e2b957f5ef114a41e8e0399cfcc08c5
@@ -0,0 +1,37 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request:
8
+ branches:
9
+ - '**'
10
+
11
+ jobs:
12
+ ci:
13
+ runs-on: ubuntu-latest
14
+
15
+ services:
16
+ db:
17
+ image: postgres:10.19
18
+ env:
19
+ POSTGRES_DB: activerecord_json_validator_test
20
+ POSTGRES_USER: postgres
21
+ POSTGRES_PASSWORD: postgres
22
+ ports: ['5432:5432']
23
+ options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
24
+
25
+ env:
26
+ CANONICAL_HOST: localhost
27
+ DATABASE_URL: postgres://postgres:postgres@localhost/activerecord_json_validator_test
28
+ DB_ADAPTER: postgresql
29
+
30
+ steps:
31
+ - uses: actions/checkout@v2
32
+ - uses: ruby/setup-ruby@v1
33
+ with:
34
+ ruby-version: '2.7.5'
35
+ bundler-cache: true
36
+ - run: bundle exec rubocop
37
+ - run: bundle exec rake spec
data/.rubocop.yml CHANGED
@@ -1,6 +1,11 @@
1
1
  AllCops:
2
+ Include:
3
+ - Rakefile
4
+ - Gemfile
5
+ - '**/*.rb'
2
6
  Exclude:
3
7
  - activerecord_json_validator.gemspec
8
+ - vendor/**/*
4
9
 
5
10
  Documentation:
6
11
  Enabled: false
data/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013-2016, Mirego
1
+ Copyright (c) 2013-2024, Mirego
2
2
  All rights reserved.
3
3
 
4
4
  Redistribution and use in source and binary forms, with or without
data/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  <p align="center">
2
2
  <a href="https://github.com/mirego/activerecord_json_validator">
3
- <img src="https://cloud.githubusercontent.com/assets/11348/6099354/cffcf35e-afc3-11e4-9a4d-d872941bbcf6.png" alt="" />
3
+ <img src="https://user-images.githubusercontent.com/11348/126779905-3468eb15-d554-46d5-925b-235f68169d86.png" alt="" />
4
4
  </a>
5
5
  <br />
6
- <code>ActiveRecord::JSONValidator</code> makes it easy to validate<br /> JSON attributes against a <a href="http://json-schema.org/">JSON schema</a>.
6
+ <code>ActiveRecord::JSONValidator</code> makes it easy to validate<br /> JSON attributes against a <a href="https://json-schema.org/">JSON schema</a>.
7
7
  <br /><br />
8
- <a href="https://rubygems.org/gems/activerecord_json_validator"><img src="http://img.shields.io/gem/v/activerecord_json_validator.svg" /></a>
9
- <a href="https://travis-ci.org/mirego/activerecord_json_validator"><img src="http://img.shields.io/travis/mirego/activerecord_json_validator.svg" /></a>
8
+ <a href="https://rubygems.org/gems/activerecord_json_validator"><img src="https://img.shields.io/gem/v/activerecord_json_validator.svg" /></a>
9
+ <a href="https://github.com/mirego/activerecord_json_validator/actions/workflows/ci.yaml"><img src="https://github.com/mirego/activerecord_json_validator/actions/workflows/ci.yaml/badge.svg" /></a>
10
10
  </p>
11
11
 
12
12
  ---
@@ -16,17 +16,19 @@
16
16
  Add this line to your application's Gemfile:
17
17
 
18
18
  ```ruby
19
- gem 'activerecord_json_validator'
19
+ gem 'activerecord_json_validator', '~> 3.0.0'
20
20
  ```
21
21
 
22
22
  ## Usage
23
23
 
24
24
  ### JSON Schema
25
25
 
26
+ Schemas should be a JSON file
27
+
26
28
  ```json
27
29
  {
28
30
  "type": "object",
29
- "$schema": "http://json-schema.org/draft-04/schema",
31
+ "$schema": "http://json-schema.org/draft-04/schema#",
30
32
  "properties": {
31
33
  "city": { "type": "string" },
32
34
  "country": { "type": "string" }
@@ -45,7 +47,7 @@ end
45
47
 
46
48
  class User < ActiveRecord::Base
47
49
  # Constants
48
- PROFILE_JSON_SCHEMA = Rails.root.join('config', 'schemas', 'profile.json_schema').to_s
50
+ PROFILE_JSON_SCHEMA = Rails.root.join('config', 'schemas', 'profile.json')
49
51
 
50
52
  # Validations
51
53
  validates :name, presence: true
@@ -65,17 +67,17 @@ user.profile_invalid_json # => '{invalid JSON":}'
65
67
 
66
68
  #### Options
67
69
 
68
- | Option | Description
69
- |------------|-----------------------------------------------------
70
- | `:schema` | The JSON schema to validate the data against (see **Schema** section)
71
- | `:message` | The ActiveRecord message added to the record errors (see **Message** section)
72
- | `:options` | A `Hash` of [`json-schema`](https://github.com/ruby-json-schema/json-schema)-supported options to pass to the validator
70
+ | Option | Description |
71
+ | ---------- | ------------------------------------------------------------------------------------------------------------------------------ |
72
+ | `:schema` | The JSON schema to validate the data against (see **Schema** section) |
73
+ | `:value` | The actual value to use when validating (see **Value** section) |
74
+ | `:message` | The ActiveRecord message added to the record errors (see **Message** section) |
75
+ | `:options` | A `Hash` of [`json_schemer`](https://github.com/davishmcclurg/json_schemer#options)-supported options to pass to the validator |
73
76
 
74
77
  ##### Schema
75
78
 
76
- `ActiveRecord::JSONValidator` uses the `json-schema` gem to validate the JSON
77
- data against a JSON schema. You can use [any value](https://github.com/ruby-json-schema/json-schema/tree/master#usage) that
78
- `JSON::Validator.validate` would take as the `schema` argument.
79
+ `ActiveRecord::JSONValidator` uses the [json_schemer](https://github.com/davishmcclurg/json_schemer) gem to validate the JSON
80
+ data against a JSON schema.
79
81
 
80
82
  Additionally, you can use a `Symbol` or a `Proc`. Both will be executed in the
81
83
  context of the validated record (`Symbol` will be sent as a method and the
@@ -84,8 +86,8 @@ context of the validated record (`Symbol` will be sent as a method and the
84
86
  ```ruby
85
87
  class User < ActiveRecord::Base
86
88
  # Constants
87
- PROFILE_REGULAR_JSON_SCHEMA = Rails.root.join('config', 'schemas', 'profile.json_schema').to_s
88
- PROFILE_ADMIN_JSON_SCHEMA = Rails.root.join('config', 'schemas', 'profile_admin.json_schema').to_s
89
+ PROFILE_REGULAR_JSON_SCHEMA = Rails.root.join('config', 'schemas', 'profile.json_schema')
90
+ PROFILE_ADMIN_JSON_SCHEMA = Rails.root.join('config', 'schemas', 'profile_admin.json_schema')
89
91
 
90
92
  # Validations
91
93
  validates :profile, presence: true, json: { schema: lambda { dynamic_profile_schema } } # `schema: :dynamic_profile_schema` would also work
@@ -96,6 +98,48 @@ class User < ActiveRecord::Base
96
98
  end
97
99
  ```
98
100
 
101
+ The schema is passed to the `JSONSchemer.schema` function, so it can be anything supported by it:
102
+
103
+ ```ruby
104
+ class User < ActiveRecord::Base
105
+ # Constants
106
+ JSON_SCHEMA = Rails.root.join('config', 'schemas', 'profile.json_schema')
107
+ # JSON_SCHEMA = { 'type' => 'object', 'properties' => { 'foo' => { 'type' => 'integer', 'minimum' => 3 } } }
108
+ # JSON_SCHEMA = '{"type":"object","properties":{"foo":{"type":"integer","minimum":3}}}'
109
+
110
+ # Validations
111
+ validates :profile, presence: true, json: { schema: JSON_SCHEMA }
112
+ end
113
+ ```
114
+
115
+ ##### Value
116
+
117
+ By default, the validator will use the “getter” method to the fetch attribute
118
+ value and validate the schema against it.
119
+
120
+ ```ruby
121
+ # Will validate `self.foo`
122
+ validates :foo, json: { schema: SCHEMA }
123
+ ```
124
+
125
+ But you can change this behavior if the getter method doesn’t return raw JSON data (a `Hash`):
126
+
127
+ ```ruby
128
+ # Will validate `self[:foo]`
129
+ validates :foo, json: { schema: SCHEMA, value: ->(record, _, _) { record[:foo] } }
130
+ ```
131
+
132
+ You could also implement a “raw getter” if you want to avoid the `value` option:
133
+
134
+ ```ruby
135
+ # Will validate `self[:foo]`
136
+ validates :raw_foo, json: { schema: SCHEMA }
137
+
138
+ def raw_foo
139
+ self[:foo]
140
+ end
141
+ ```
142
+
99
143
  ##### Message
100
144
 
101
145
  Like any other ActiveModel validation, you can specify either a `Symbol` or
@@ -120,14 +164,23 @@ user.errors.full_messages
120
164
  # ]
121
165
  ```
122
166
 
167
+ ## Development
168
+
169
+ The tests require a database. We've provided a simple `docker-compose.yml` that will make
170
+ it trivial to run the tests against PostgreSQL. Simply run `docker compose up -d`
171
+ followed by `rake spec`. When you're done, run `docker compose down` to stop the database.
172
+
173
+ In order to use another database, simply define the `DATABASE_URL` environment variable
174
+ appropriately.
175
+
123
176
  ## License
124
177
 
125
- `ActiveRecord::JSONValidator` is © 2013-2016 [Mirego](http://www.mirego.com) and may be freely distributed under the [New BSD license](http://opensource.org/licenses/BSD-3-Clause). See the [`LICENSE.md`](https://github.com/mirego/activerecord_json_validator/blob/master/LICENSE.md) file.
178
+ `ActiveRecord::JSONValidator` is © 2013-2024 [Mirego](https://www.mirego.com) and may be freely distributed under the [New BSD license](https://opensource.org/licenses/BSD-3-Clause). See the [`LICENSE.md`](https://github.com/mirego/activerecord_json_validator/blob/master/LICENSE.md) file.
126
179
 
127
- The tree logo is based on [this lovely icon](http://thenounproject.com/term/tree/51004/) by [Sara Quintana](http://thenounproject.com/sara.quintana.75), from The Noun Project. Used under a [Creative Commons BY 3.0](http://creativecommons.org/licenses/by/3.0/) license.
180
+ The tree logo is based on [this lovely icon](https://thenounproject.com/term/tree/51004/) by [Sara Quintana](https://thenounproject.com/sara.quintana.75), from The Noun Project. Used under a [Creative Commons BY 3.0](https://creativecommons.org/licenses/by/3.0/) license.
128
181
 
129
182
  ## About Mirego
130
183
 
131
- [Mirego](http://mirego.com) is a team of passionate people who believe that work is a place where you can innovate and have fun. We're a team of [talented people](http://life.mirego.com) who imagine and build beautiful Web and mobile applications. We come together to share ideas and [change the world](http://mirego.org).
184
+ [Mirego](https://www.mirego.com) is a team of passionate people who believe that work is a place where you can innovate and have fun. We're a team of [talented people](https://life.mirego.com) who imagine and build beautiful Web and mobile applications. We come together to share ideas and [change the world](https://www.mirego.org).
132
185
 
133
- We also [love open-source software](http://open.mirego.com) and we try to give back to the community as much as we can.
186
+ We also [love open-source software](https://open.mirego.com) and we try to give back to the community as much as we can.
@@ -18,15 +18,15 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ['lib']
20
20
 
21
- spec.add_development_dependency 'bundler', '~> 1.12'
21
+ spec.add_development_dependency 'bundler', '>= 1.12'
22
22
  spec.add_development_dependency 'rake'
23
23
  spec.add_development_dependency 'rspec', '~> 3.5'
24
24
  spec.add_development_dependency 'pg'
25
- spec.add_development_dependency 'mysql2'
26
- spec.add_development_dependency 'activesupport', '>= 4.2.0', '< 7'
27
- spec.add_development_dependency 'phare'
25
+ spec.add_development_dependency 'activesupport', '>= 4.2.0', '< 9'
28
26
  spec.add_development_dependency 'rubocop', '~> 0.28'
27
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.44'
28
+ spec.add_development_dependency 'rubocop-standard', '~> 6.0'
29
29
 
30
- spec.add_dependency 'json-schema', '~> 2.8'
31
- spec.add_dependency 'activerecord', '>= 4.2.0', '< 7'
30
+ spec.add_dependency 'json_schemer', '~> 2.2'
31
+ spec.add_dependency 'activerecord', '>= 4.2.0', '< 9'
32
32
  end
@@ -0,0 +1,11 @@
1
+ version: '3.2'
2
+
3
+ services:
4
+ postgres:
5
+ image: postgres:10
6
+ ports:
7
+ - 5432:5432
8
+ restart: on-failure
9
+ environment:
10
+ POSTGRES_DB: activerecord_json_validator_test
11
+ POSTGRES_HOST_AUTH_METHOD: trust # don't require password
@@ -5,6 +5,7 @@ class JsonValidator < ActiveModel::EachValidator
5
5
  options.reverse_merge!(message: :invalid_json)
6
6
  options.reverse_merge!(schema: nil)
7
7
  options.reverse_merge!(options: {})
8
+ options.reverse_merge!(value: ->(_record, _attribute, value) { value })
8
9
  @attributes = options[:attributes]
9
10
 
10
11
  super
@@ -14,15 +15,20 @@ class JsonValidator < ActiveModel::EachValidator
14
15
 
15
16
  # Validate the JSON value with a JSON schema path or String
16
17
  def validate_each(record, attribute, value)
17
- # Validate value with JSON::Validator
18
- errors = ::JSON::Validator.fully_validate(schema(record), validatable_value(value), options.fetch(:options))
18
+ # Get the _actual_ attribute value, not the getter method value
19
+ value = options.fetch(:value).call(record, attribute, value)
20
+
21
+ # Validate value with JSON Schemer
22
+ errors = JSONSchemer.schema(schema(record), **options.fetch(:options)).validate(value).to_a
19
23
 
20
24
  # Everything is good if we don’t have any errors and we got valid JSON value
21
25
  return if errors.empty? && record.send(:"#{attribute}_invalid_json").blank?
22
26
 
23
27
  # Add error message to the attribute
28
+ details = errors.map { |e| e.fetch('error') }
24
29
  message(errors).each do |error|
25
- record.errors.add(attribute, error, value: value)
30
+ error = error.fetch('error') if error.is_a?(Hash)
31
+ record.errors.add(attribute, error, errors: details, value: value)
26
32
  end
27
33
  end
28
34
 
@@ -31,25 +37,27 @@ protected
31
37
  # Redefine the setter method for the attributes, since we want to
32
38
  # catch JSON parsing errors.
33
39
  def inject_setter_method(klass, attributes)
40
+ return if klass.nil?
41
+
34
42
  attributes.each do |attribute|
35
- klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
43
+ klass.prepend(Module.new do
36
44
  attr_reader :"#{attribute}_invalid_json"
37
45
 
38
46
  define_method "#{attribute}=" do |args|
39
47
  begin
40
- @#{attribute}_invalid_json = nil
48
+ instance_variable_set("@#{attribute}_invalid_json", nil)
41
49
  args = ::ActiveSupport::JSON.decode(args) if args.is_a?(::String)
42
50
  super(args)
43
51
  rescue ActiveSupport::JSON.parse_error
44
- @#{attribute}_invalid_json = args
52
+ instance_variable_set("@#{attribute}_invalid_json", args)
45
53
  super({})
46
54
  end
47
55
  end
48
- RUBY
56
+ end)
49
57
  end
50
58
  end
51
59
 
52
- # Return a valid schema for JSON::Validator.fully_validate, recursively calling
60
+ # Return a valid schema, recursively calling
53
61
  # itself until it gets a non-Proc/non-Symbol value.
54
62
  def schema(record, schema = nil)
55
63
  schema ||= options.fetch(:schema)
@@ -61,18 +69,9 @@ protected
61
69
  end
62
70
  end
63
71
 
64
- def validatable_value(value)
65
- return value if value.is_a?(String)
66
-
67
- ::ActiveSupport::JSON.encode(value)
68
- end
69
-
70
72
  def message(errors)
71
73
  message = options.fetch(:message)
72
-
73
- case message
74
- when Proc then [message.call(errors)].flatten if message.is_a?(Proc)
75
- else [message]
76
- end
74
+ message = message.call(errors) if message.is_a?(Proc)
75
+ [message].flatten
77
76
  end
78
77
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module JSONValidator
5
- VERSION = '1.3.0'
5
+ VERSION = '3.1.0'
6
6
  end
7
7
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'active_record'
4
- require 'json-schema'
4
+ require 'json_schemer'
5
5
 
6
6
  require 'active_record/json_validator/version'
7
7
  require 'active_record/json_validator/validator'
@@ -3,106 +3,109 @@
3
3
  # rubocop:disable Metrics/BlockLength
4
4
  require 'spec_helper'
5
5
 
6
+ module CountryDefaulter
7
+ extend ActiveSupport::Concern
8
+
9
+ class_methods do
10
+ def default_country_attribute(name, country:)
11
+ define_method("#{name}=") do |value|
12
+ self[name] = { country: country }.merge(value)
13
+ end
14
+ end
15
+ end
16
+ end
17
+
6
18
  describe JsonValidator do
7
- describe :initialize do
8
- # NOTE: We do not explicitely call `JsonValidator.new` in the tests,
9
- # because we let Rails (ActiveModel::Validations) do that when we call
10
- # `validates … json: true` on the model.
11
- #
12
- # This allows us to test the constructor behavior when executed in
13
- # different Rails versions that do not pass the same arguments to it.
19
+ describe :validate_each do
14
20
  before do
15
21
  run_migration do
16
22
  create_table(:users, force: true) do |t|
17
- t.string :name
18
23
  t.text :data
24
+ t.json :smart_data
19
25
  end
20
26
  end
21
27
 
22
28
  spawn_model 'User' do
23
- serialize :data, JSON
24
- validates :data, json: true
29
+ include CountryDefaulter
30
+
31
+ schema = '
32
+ {
33
+ "type": "object",
34
+ "properties": {
35
+ "city": { "type": "string" },
36
+ "country": { "type": "string" }
37
+ },
38
+ "required": ["country"]
39
+ }
40
+ '
41
+
42
+ default_country_attribute :smart_data, country: 'Canada'
43
+
44
+ serialize :data, coder: JSON
45
+ serialize :other_data, coder: JSON
46
+ validates :data, json: { schema: schema, message: ->(errors) { errors } }
47
+ validates :other_data, json: { schema: schema, message: ->(errors) { errors.map { |error| error['details'].to_a.flatten.join(' ') } } }
48
+ validates :smart_data, json: { value: ->(record, _, _) { record[:smart_data] }, schema: schema, message: ->(errors) { errors } }
49
+
50
+ def smart_data
51
+ OpenStruct.new(self[:smart_data])
52
+ end
25
53
  end
26
-
27
- record.data = data
28
54
  end
29
55
 
30
- let(:record) { User.new }
56
+ context 'with valid JSON data but schema errors' do
57
+ let(:user) do
58
+ User.new(
59
+ data: '{"city":"Quebec City"}',
60
+ other_data: '{"city":"Quebec City"}',
61
+ smart_data: { country: 'Ireland', city: 'Dublin' }
62
+ )
63
+ end
31
64
 
32
- context 'with valid JSON data' do
33
- let(:data) { 'What? This is not JSON at all.' }
34
- it { expect(record.data_invalid_json).to eql(data) }
65
+ specify do
66
+ expect(user).not_to be_valid
67
+ expect(user.errors.full_messages).to eql(['Data object at root is missing required properties: country', 'Other data missing_keys country'])
68
+ expect(user.errors.group_by_attribute[:data].first).to have_attributes(
69
+ options: include(errors: ['object at root is missing required properties: country'])
70
+ )
71
+ expect(user.errors.group_by_attribute[:other_data].first).to have_attributes(
72
+ options: include(errors: ['object at root is missing required properties: country'])
73
+ )
74
+ expect(user.data).to eql({ 'city' => 'Quebec City' })
75
+ expect(user.data_invalid_json).to be_nil
76
+ expect(user.smart_data.city).to eql('Dublin')
77
+ expect(user.smart_data.country).to eql('Ireland')
78
+ end
35
79
  end
36
80
 
37
81
  context 'with invalid JSON data' do
38
- let(:data) { { foo: 'bar' } }
39
- it { expect(record.data_invalid_json).to be_nil }
40
- end
41
- end
42
-
43
- describe :validate_each do
44
- let(:validator) { JsonValidator.new(options) }
45
- let(:options) { { attributes: [attribute], options: { strict: true } } }
46
- let(:validate_each!) { validator.validate_each(record, attribute, value) }
47
-
48
- # Doubles
49
- let(:attribute) { double(:attribute, to_s: 'attribute_name') }
50
- let(:record) { double(:record, errors: record_errors) }
51
- let(:record_errors) { double(:errors) }
52
- let(:value) { double(:value) }
53
- let(:schema) { double(:schema) }
54
- let(:validatable_value) { double(:validatable_value) }
55
- let(:validator_errors) { double(:validator_errors) }
56
-
57
- before do
58
- expect(validator).to receive(:schema).with(record).and_return(schema)
59
- expect(validator).to receive(:validatable_value).with(value).and_return(validatable_value)
60
- expect(::JSON::Validator).to receive(:fully_validate).with(schema, validatable_value, options[:options]).and_return(validator_errors)
61
- end
62
-
63
- context 'with JSON::Validator errors' do
64
- before do
65
- expect(validator_errors).to receive(:empty?).and_return(false)
66
- expect(record).not_to receive(:"#{attribute}_invalid_json")
67
- expect(record_errors).to receive(:add).with(attribute, options[:message], value: value)
68
- end
82
+ let(:data) { 'What? This is not JSON at all.' }
83
+ let(:user) { User.new(data: data, smart_data: data) }
69
84
 
70
- specify { validate_each! }
71
- end
85
+ specify do
86
+ expect(user.data_invalid_json).to eql(data)
87
+ expect(user.data).to eql({})
72
88
 
73
- context 'without JSON::Validator errors but with invalid JSON data' do
74
- before do
75
- expect(validator_errors).to receive(:empty?).and_return(true)
76
- expect(record).to receive(:"#{attribute}_invalid_json").and_return('foo"{]')
77
- expect(record_errors).to receive(:add).with(attribute, options[:message], value: value)
89
+ # Ensure that both setters ran
90
+ expect(user.smart_data_invalid_json).to eql(data)
91
+ expect(user.smart_data).to eql(OpenStruct.new({ country: 'Canada' }))
78
92
  end
79
-
80
- specify { validate_each! }
81
93
  end
82
94
 
83
- context 'without JSON::Validator errors and valid JSON data' do
84
- before do
85
- expect(validator_errors).to receive(:empty?).and_return(true)
86
- expect(record).to receive(:"#{attribute}_invalid_json").and_return(nil)
87
- expect(record_errors).not_to receive(:add)
95
+ context 'with missing country in smart data' do
96
+ let(:user) do
97
+ User.new(
98
+ data: '{"city":"Quebec City","country":"Canada"}',
99
+ other_data: '{"city":"Quebec City","country":"Canada"}',
100
+ smart_data: { city: 'Quebec City' }
101
+ )
88
102
  end
89
103
 
90
- specify { validate_each! }
91
- end
92
-
93
- context 'with multiple error messages' do
94
- let(:options) { { attributes: [attribute], message: message, options: { strict: true } } }
95
- let(:message) { ->(errors) { errors.to_a } }
96
-
97
- before do
98
- expect(validator_errors).to receive(:empty?).and_return(false)
99
- expect(validator_errors).to receive(:to_a).and_return(%i[first_error second_error])
100
- expect(record).not_to receive(:"#{attribute}_invalid_json")
101
- expect(record_errors).to receive(:add).with(attribute, :first_error, value: value)
102
- expect(record_errors).to receive(:add).with(attribute, :second_error, value: value)
104
+ specify do
105
+ expect(user).to be_valid
106
+ expect(user.smart_data.city).to eql('Quebec City')
107
+ expect(user.smart_data.country).to eql('Canada') # Due to CountryDefaulter
103
108
  end
104
-
105
- specify { validate_each! }
106
109
  end
107
110
  end
108
111
 
@@ -155,22 +158,6 @@ describe JsonValidator do
155
158
  end
156
159
  end
157
160
 
158
- describe :validatable_value do
159
- let(:validator) { JsonValidator.new(options) }
160
- let(:options) { { attributes: [:foo] } }
161
- let(:validatable_value) { validator.send(:validatable_value, value) }
162
-
163
- context 'with non-String value' do
164
- let(:value) { { foo: 'bar' } }
165
- it { expect(validatable_value).to eql('{"foo":"bar"}') }
166
- end
167
-
168
- context 'with String value' do
169
- let(:value) { '{\"foo\":\"bar\"}' }
170
- it { expect(validatable_value).to eql(value) }
171
- end
172
- end
173
-
174
161
  describe :message do
175
162
  let(:validator) { JsonValidator.new(options) }
176
163
  let(:options) { { attributes: [:foo], message: message_option } }
data/spec/spec_helper.rb CHANGED
@@ -4,7 +4,6 @@ $LOAD_PATH.unshift File.expand_path('lib', __dir__)
4
4
 
5
5
  require 'active_support/all'
6
6
  require 'rspec'
7
- require 'mysql2'
8
7
  require 'pg'
9
8
 
10
9
  require 'activerecord_json_validator'
@@ -6,6 +6,6 @@ class DatabaseAdapter
6
6
  end
7
7
 
8
8
  def establish_connection!
9
- ActiveRecord::Base.establish_connection(database_configuration)
9
+ ActiveRecord::Base.establish_connection(ENV.fetch('DATABASE_URL', 'postgres://postgres@localhost/activerecord_json_validator_test'))
10
10
  end
11
11
  end
@@ -3,15 +3,6 @@
3
3
  require_relative 'database_adapter'
4
4
 
5
5
  class Mysql2Adapter < DatabaseAdapter
6
- def database_configuration
7
- {
8
- adapter: 'mysql2',
9
- database: @database,
10
- username: 'travis',
11
- encoding: 'utf8'
12
- }
13
- end
14
-
15
6
  def reset_database!
16
7
  ActiveRecord::Base.connection.execute("SELECT concat('DROP TABLE IF EXISTS ', table_name, ';') FROM information_schema.tables WHERE table_schema = '#{@database}';")
17
8
  end
@@ -3,15 +3,6 @@
3
3
  require_relative 'database_adapter'
4
4
 
5
5
  class PostgresqlAdapter < DatabaseAdapter
6
- def database_configuration
7
- {
8
- adapter: 'postgresql',
9
- database: @database,
10
- user: 'postgres',
11
- schema_search_path: 'public'
12
- }
13
- end
14
-
15
6
  def reset_database!
16
7
  ActiveRecord::Base.connection.execute('drop schema public cascade;')
17
8
  ActiveRecord::Base.connection.execute('create schema public;')
@@ -25,6 +25,6 @@ module DatabaseMacros
25
25
  adapter.reset_database!
26
26
 
27
27
  # Silence everything
28
- ActiveRecord::Base.logger = ActiveRecord::Migration.verbose = false
28
+ ActiveRecord::Base.logger = ActiveRecord::Migration.verbose = nil
29
29
  end
30
30
  end
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord_json_validator
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rémi Prévost
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-08-27 00:00:00.000000000 Z
11
+ date: 2025-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.12'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.12'
27
27
  - !ruby/object:Gem::Dependency
@@ -67,81 +67,81 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: mysql2
70
+ name: activesupport
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
75
+ version: 4.2.0
76
+ - - "<"
77
+ - !ruby/object:Gem::Version
78
+ version: '9'
76
79
  type: :development
77
80
  prerelease: false
78
81
  version_requirements: !ruby/object:Gem::Requirement
79
82
  requirements:
80
83
  - - ">="
81
84
  - !ruby/object:Gem::Version
82
- version: '0'
85
+ version: 4.2.0
86
+ - - "<"
87
+ - !ruby/object:Gem::Version
88
+ version: '9'
83
89
  - !ruby/object:Gem::Dependency
84
- name: activesupport
90
+ name: rubocop
85
91
  requirement: !ruby/object:Gem::Requirement
86
92
  requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: 4.2.0
90
- - - "<"
93
+ - - "~>"
91
94
  - !ruby/object:Gem::Version
92
- version: '7'
95
+ version: '0.28'
93
96
  type: :development
94
97
  prerelease: false
95
98
  version_requirements: !ruby/object:Gem::Requirement
96
99
  requirements:
97
- - - ">="
98
- - !ruby/object:Gem::Version
99
- version: 4.2.0
100
- - - "<"
100
+ - - "~>"
101
101
  - !ruby/object:Gem::Version
102
- version: '7'
102
+ version: '0.28'
103
103
  - !ruby/object:Gem::Dependency
104
- name: phare
104
+ name: rubocop-rspec
105
105
  requirement: !ruby/object:Gem::Requirement
106
106
  requirements:
107
- - - ">="
107
+ - - "~>"
108
108
  - !ruby/object:Gem::Version
109
- version: '0'
109
+ version: '1.44'
110
110
  type: :development
111
111
  prerelease: false
112
112
  version_requirements: !ruby/object:Gem::Requirement
113
113
  requirements:
114
- - - ">="
114
+ - - "~>"
115
115
  - !ruby/object:Gem::Version
116
- version: '0'
116
+ version: '1.44'
117
117
  - !ruby/object:Gem::Dependency
118
- name: rubocop
118
+ name: rubocop-standard
119
119
  requirement: !ruby/object:Gem::Requirement
120
120
  requirements:
121
121
  - - "~>"
122
122
  - !ruby/object:Gem::Version
123
- version: '0.28'
123
+ version: '6.0'
124
124
  type: :development
125
125
  prerelease: false
126
126
  version_requirements: !ruby/object:Gem::Requirement
127
127
  requirements:
128
128
  - - "~>"
129
129
  - !ruby/object:Gem::Version
130
- version: '0.28'
130
+ version: '6.0'
131
131
  - !ruby/object:Gem::Dependency
132
- name: json-schema
132
+ name: json_schemer
133
133
  requirement: !ruby/object:Gem::Requirement
134
134
  requirements:
135
135
  - - "~>"
136
136
  - !ruby/object:Gem::Version
137
- version: '2.8'
137
+ version: '2.2'
138
138
  type: :runtime
139
139
  prerelease: false
140
140
  version_requirements: !ruby/object:Gem::Requirement
141
141
  requirements:
142
142
  - - "~>"
143
143
  - !ruby/object:Gem::Version
144
- version: '2.8'
144
+ version: '2.2'
145
145
  - !ruby/object:Gem::Dependency
146
146
  name: activerecord
147
147
  requirement: !ruby/object:Gem::Requirement
@@ -151,7 +151,7 @@ dependencies:
151
151
  version: 4.2.0
152
152
  - - "<"
153
153
  - !ruby/object:Gem::Version
154
- version: '7'
154
+ version: '9'
155
155
  type: :runtime
156
156
  prerelease: false
157
157
  version_requirements: !ruby/object:Gem::Requirement
@@ -161,7 +161,7 @@ dependencies:
161
161
  version: 4.2.0
162
162
  - - "<"
163
163
  - !ruby/object:Gem::Version
164
- version: '7'
164
+ version: '9'
165
165
  description: ActiveRecord::JSONValidator makes it easy to validate JSON attributes
166
166
  with a JSON schema.
167
167
  email:
@@ -170,18 +170,16 @@ executables: []
170
170
  extensions: []
171
171
  extra_rdoc_files: []
172
172
  files:
173
+ - ".github/workflows/ci.yaml"
173
174
  - ".gitignore"
174
175
  - ".rspec"
175
176
  - ".rubocop.yml"
176
- - ".travis.yml"
177
177
  - Gemfile
178
178
  - LICENSE.md
179
179
  - README.md
180
180
  - Rakefile
181
181
  - activerecord_json_validator.gemspec
182
- - gemfiles/Gemfile.activerecord-4.2.x
183
- - gemfiles/Gemfile.activerecord-5.0.x
184
- - gemfiles/Gemfile.activerecord-6.0.x
182
+ - docker-compose.yml
185
183
  - lib/active_record/json_validator/validator.rb
186
184
  - lib/active_record/json_validator/version.rb
187
185
  - lib/activerecord_json_validator.rb
@@ -196,7 +194,7 @@ homepage: https://github.com/mirego/activerecord_json_validator
196
194
  licenses:
197
195
  - BSD 3-Clause
198
196
  metadata: {}
199
- post_install_message:
197
+ post_install_message:
200
198
  rdoc_options: []
201
199
  require_paths:
202
200
  - lib
@@ -211,8 +209,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
211
209
  - !ruby/object:Gem::Version
212
210
  version: '0'
213
211
  requirements: []
214
- rubygems_version: 3.0.3
215
- signing_key:
212
+ rubygems_version: 3.5.11
213
+ signing_key:
216
214
  specification_version: 4
217
215
  summary: ActiveRecord::JSONValidator makes it easy to validate JSON attributes with
218
216
  a JSON schema.
data/.travis.yml DELETED
@@ -1,33 +0,0 @@
1
- language: ruby
2
-
3
- rvm:
4
- - 2.4.6
5
- - 2.6.3
6
-
7
- gemfile:
8
- - gemfiles/Gemfile.activerecord-4.2.x
9
- - gemfiles/Gemfile.activerecord-5.0.x
10
- - gemfiles/Gemfile.activerecord-6.0.x
11
-
12
- matrix:
13
- exclude:
14
- - gemfile: gemfiles/Gemfile.activerecord-6.0.x
15
- rvm: 2.4.6
16
-
17
- sudo: false
18
-
19
- services:
20
- - mysql
21
- - postgresql
22
-
23
- env:
24
- - DB_ADAPTER=mysql2
25
- - DB_ADAPTER=postgresql
26
-
27
- before_script:
28
- - mysql -e 'create database activerecord_json_validator_test;'
29
- - psql -c 'create database activerecord_json_validator_test;' -U postgres
30
-
31
- script:
32
- - 'echo "Checking code style" && bundle exec phare'
33
- - 'echo "Running tests" && bundle exec rake spec'
@@ -1,6 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec path: '../'
4
-
5
- gem 'activerecord', '~> 4.2'
6
- gem 'pg', '~> 0.15'
@@ -1,5 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec path: '../'
4
-
5
- gem 'activerecord', '~> 5.0'
@@ -1,5 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec path: '../'
4
-
5
- gem 'activerecord', '~> 6.0'