Skip to content
This repository was archived by the owner on Sep 22, 2025. It is now read-only.

Commit a4fe6e2

Browse files
authored
validator: prepend setter instead of overwriting (mirego#71)
1 parent e1d4e84 commit a4fe6e2

File tree

2 files changed

+47
-6
lines changed

2 files changed

+47
-6
lines changed

lib/active_record/json_validator/validator.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,23 @@ def validate_each(record, attribute, value)
3737
# Redefine the setter method for the attributes, since we want to
3838
# catch JSON parsing errors.
3939
def inject_setter_method(klass, attributes)
40+
return if klass.nil?
41+
4042
attributes.each do |attribute|
41-
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
43+
klass.prepend(Module.new do
4244
attr_reader :"#{attribute}_invalid_json"
4345

4446
define_method "#{attribute}=" do |args|
4547
begin
46-
@#{attribute}_invalid_json = nil
48+
instance_variable_set("@#{attribute}_invalid_json", nil)
4749
args = ::ActiveSupport::JSON.decode(args) if args.is_a?(::String)
4850
super(args)
4951
rescue ActiveSupport::JSON.parse_error
50-
@#{attribute}_invalid_json = args
52+
instance_variable_set("@#{attribute}_invalid_json", args)
5153
super({})
5254
end
5355
end
54-
RUBY
56+
end)
5557
end
5658
end
5759

spec/json_validator_spec.rb

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@
33
# rubocop:disable Metrics/BlockLength
44
require 'spec_helper'
55

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+
618
describe JsonValidator do
719
describe :validate_each do
820
before do
@@ -14,6 +26,8 @@
1426
end
1527

1628
spawn_model 'User' do
29+
include CountryDefaulter
30+
1731
schema = '
1832
{
1933
"type": "object",
@@ -24,6 +38,9 @@
2438
"required": ["country"]
2539
}
2640
'
41+
42+
default_country_attribute :smart_data, country: 'Canada'
43+
2744
serialize :data, JSON
2845
serialize :other_data, JSON
2946
validates :data, json: { schema: schema, message: ->(errors) { errors } }
@@ -41,7 +58,7 @@ def smart_data
4158
User.new(
4259
data: '{"city":"Quebec City"}',
4360
other_data: '{"city":"Quebec City"}',
44-
smart_data: { country: 'Canada', city: 'Quebec City' }
61+
smart_data: { country: 'Ireland', city: 'Dublin' }
4562
)
4663
end
4764

@@ -56,16 +73,38 @@ def smart_data
5673
)
5774
expect(user.data).to eql({ 'city' => 'Quebec City' })
5875
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')
5978
end
6079
end
6180

6281
context 'with invalid JSON data' do
6382
let(:data) { 'What? This is not JSON at all.' }
64-
let(:user) { User.new(data: data) }
83+
let(:user) { User.new(data: data, smart_data: data) }
6584

6685
specify do
6786
expect(user.data_invalid_json).to eql(data)
6887
expect(user.data).to eql({})
88+
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' }))
92+
end
93+
end
94+
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+
)
102+
end
103+
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
69108
end
70109
end
71110
end

0 commit comments

Comments
 (0)