Skip to content

Commit eb503d8

Browse files
authored
Merge pull request rails#54172 from matthaigh27/main
JSON serialized attributes can return symbolized keys
2 parents d4acb7a + 65426af commit eb503d8

File tree

8 files changed

+54
-11
lines changed

8 files changed

+54
-11
lines changed

activerecord/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
* `ActiveRecord::Coder::JSON` can be instantiated
2+
3+
Options can now be passed to `ActiveRecord::Coder::JSON` when instantiating the coder. This allows:
4+
```ruby
5+
serialize :config, coder: ActiveRecord::Coder::JSON.new(symbolize_names: true)
6+
```
7+
*matthaigh27*
8+
19
* Deprecate using `insert_all`/`upsert_all` with unpersisted records in associations.
210

311
Using these methods on associations containing unpersisted records will now

activerecord/lib/active_record/attribute_methods/serialization.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,11 @@ def build_column_serializer(attr_name, coder, type, yaml = nil)
219219
# When ::JSON is used, force it to go through the Active Support JSON encoder
220220
# to ensure special objects (e.g. Active Record models) are dumped correctly
221221
# using the #as_json hook.
222-
coder = Coders::JSON if coder == ::JSON
223222

224223
if coder == ::YAML || coder == Coders::YAMLColumn
225224
Coders::YAMLColumn.new(attr_name, type, **(yaml || {}))
225+
elsif coder == ::JSON || coder == Coders::JSON
226+
Coders::JSON.new
226227
elsif coder.respond_to?(:new) && !coder.respond_to?(:load)
227228
coder.new(attr_name, type)
228229
elsif type && type != Object

activerecord/lib/active_record/coders/json.rb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22

33
module ActiveRecord
44
module Coders # :nodoc:
5-
module JSON # :nodoc:
6-
def self.dump(obj)
5+
class JSON # :nodoc:
6+
def initialize(options = {})
7+
@options = options
8+
end
9+
10+
def dump(obj)
711
ActiveSupport::JSON.encode(obj)
812
end
913

10-
def self.load(json)
11-
ActiveSupport::JSON.decode(json) unless json.blank?
14+
def load(json)
15+
ActiveSupport::JSON.decode(json, @options) unless json.blank?
1216
end
1317
end
1418
end

activerecord/test/cases/coders/json_test.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,18 @@ module ActiveRecord
66
module Coders
77
class JSONTest < ActiveRecord::TestCase
88
def test_returns_nil_if_empty_string_given
9-
assert_nil JSON.load("")
9+
coder = JSON.new
10+
assert_nil coder.load("")
1011
end
1112

1213
def test_returns_nil_if_nil_given
13-
assert_nil JSON.load(nil)
14+
coder = JSON.new
15+
assert_nil coder.load(nil)
16+
end
17+
18+
def test_coder_with_symbolize_names
19+
coder = JSON.new(symbolize_names: true)
20+
assert_equal({ foo: "bar" }, coder.load('{"foo":"bar"}'))
1421
end
1522
end
1623
end

activerecord/test/cases/serialized_attribute_test.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,17 @@ def test_json_read_db_null
149149
assert_nil t.content
150150
end
151151

152+
def test_json_symbolize_names_returns_symbolized_names
153+
Topic.serialize :content, coder: ActiveRecord::Coders::JSON.new(symbolize_names: true)
154+
my_post = posts(:welcome)
155+
156+
t = Topic.new(content: my_post)
157+
t.save!
158+
t.reload
159+
160+
assert_equal(t.content, t.content.deep_symbolize_keys)
161+
end
162+
152163
def test_serialized_attribute_declared_in_subclass
153164
hash = { "important1" => "value1", "important2" => "value2" }
154165
important_topic = ImportantTopic.create("important" => hash)

activesupport/CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
* `ActiveSupport::JSON` now accepts options
2+
3+
It is now possible to pass options to `ActiveSupport::JSON`:
4+
```ruby
5+
ActiveSupport::JSON.decode('{"key": "value"}', symbolize_names: true) # => { key: "value" }
6+
```
7+
8+
*matthaigh27*
9+
110
* `ActiveSupport::Testing::NotificationAssertions`'s `assert_notification` now matches against payload subsets by default.
211
312
Previously the following assertion would fail due to excess key vals in the notification payload. Now with payload subset matching, it will pass.

activesupport/lib/active_support/json/decoding.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ class << self
1919
#
2020
# ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}")
2121
# => {"team" => "rails", "players" => "36"}
22-
def decode(json)
23-
data = ::JSON.parse(json)
22+
def decode(json, options = {})
23+
data = ::JSON.parse(json, options)
2424

2525
if ActiveSupport.parse_json_times
2626
convert_dates_from(data)

activesupport/test/json/decoding_test.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,11 @@ def test_failed_json_decoding
110110
assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%()) }
111111
end
112112

113-
def test_cannot_pass_unsupported_options
114-
assert_raise(ArgumentError) { ActiveSupport::JSON.decode("", create_additions: true) }
113+
def test_symbolized_names_option
114+
json = '{"foo":"bar"}'
115+
assert_equal({ "foo" => "bar" }, ActiveSupport::JSON.decode(json))
116+
assert_equal({ foo: "bar" }, ActiveSupport::JSON.decode(json, symbolize_names: true))
117+
assert_equal({ foo: "bar" }, ActiveSupport::JSON.decode(json, { symbolize_names: true }))
115118
end
116119

117120
private

0 commit comments

Comments
 (0)