mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Fix ruby-master test suite (Psych 4.0.0)
Ruby master ships with Psych 4.0.0 which makes `YAML.load` defaults to safe mode (https://github.com/ruby/psych/pull/487). However since these YAML files are trustworthy sources we can parse them with `unsafe_load`.
This commit is contained in:
parent
811f7471d1
commit
1e56b1d115
18 changed files with 95 additions and 41 deletions
|
@ -23,30 +23,33 @@ class ParametersSerializationTest < ActiveSupport::TestCase
|
|||
|
||||
test "yaml deserialization" do
|
||||
params = ActionController::Parameters.new(key: :value)
|
||||
roundtripped = YAML.load(YAML.dump(params))
|
||||
payload = YAML.dump(params)
|
||||
roundtripped = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(payload) : YAML.load(payload)
|
||||
|
||||
assert_equal params, roundtripped
|
||||
assert_not_predicate roundtripped, :permitted?
|
||||
end
|
||||
|
||||
test "yaml backwardscompatible with psych 2.0.8 format" do
|
||||
params = YAML.load <<~end_of_yaml
|
||||
payload = <<~end_of_yaml
|
||||
--- !ruby/hash:ActionController::Parameters
|
||||
key: :value
|
||||
end_of_yaml
|
||||
params = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(payload) : YAML.load(payload)
|
||||
|
||||
assert_equal :value, params[:key]
|
||||
assert_not_predicate params, :permitted?
|
||||
end
|
||||
|
||||
test "yaml backwardscompatible with psych 2.0.9+ format" do
|
||||
params = YAML.load(<<~end_of_yaml)
|
||||
payload = <<~end_of_yaml
|
||||
--- !ruby/hash-with-ivars:ActionController::Parameters
|
||||
elements:
|
||||
key: :value
|
||||
ivars:
|
||||
:@permitted: false
|
||||
end_of_yaml
|
||||
params = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(payload) : YAML.load(payload)
|
||||
|
||||
assert_equal :value, params[:key]
|
||||
assert_not_predicate params, :permitted?
|
||||
|
|
|
@ -821,7 +821,7 @@ class ErrorsTest < ActiveModel::TestCase
|
|||
messages: {}
|
||||
CODE
|
||||
|
||||
errors = YAML.load(yaml)
|
||||
errors = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(yaml) : YAML.load(yaml)
|
||||
errors.add(:name, :invalid)
|
||||
assert_equal({ name: ["is invalid"] }, errors.messages)
|
||||
assert_equal({ name: [{ error: :invalid }] }, errors.details)
|
||||
|
@ -847,7 +847,7 @@ class ErrorsTest < ActiveModel::TestCase
|
|||
- :error: :invalid
|
||||
CODE
|
||||
|
||||
errors = YAML.load(yaml)
|
||||
errors = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(yaml) : YAML.load(yaml)
|
||||
assert_equal({ name: ["is invalid"] }, errors.messages)
|
||||
assert_equal({ name: [{ error: :invalid }] }, errors.details)
|
||||
|
||||
|
@ -872,7 +872,7 @@ class ErrorsTest < ActiveModel::TestCase
|
|||
options: {}
|
||||
CODE
|
||||
|
||||
errors = YAML.load(yaml)
|
||||
errors = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(yaml) : YAML.load(yaml)
|
||||
assert_equal({ name: ["is invalid"] }, errors.messages)
|
||||
assert_equal({ name: [{ error: :invalid }] }, errors.details)
|
||||
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
* Fix compatibility with `psych >= 4`.
|
||||
|
||||
Starting in Psych 4.0.0 `YAML.load` behaves like `YAML.safe_load`. To preserve compatibility
|
||||
Active Record's schema cache loader and `YAMLColumn` now uses `YAML.unsafe_load` if available.
|
||||
|
||||
*Jean Boussier*
|
||||
|
||||
* `ActiveRecord::Base.logger` is now a `class_attribute`.
|
||||
|
||||
This means it can no longer be accessed directly through `@@logger`, and that setting `logger =`
|
||||
|
|
|
@ -23,7 +23,7 @@ module ActiveRecord
|
|||
def load(yaml)
|
||||
return object_class.new if object_class != Object && yaml.nil?
|
||||
return yaml unless yaml.is_a?(String) && yaml.start_with?("---")
|
||||
obj = YAML.load(yaml)
|
||||
obj = yaml_load(yaml)
|
||||
|
||||
assert_valid_value(obj, action: "load")
|
||||
obj ||= object_class.new if object_class != Object
|
||||
|
@ -44,6 +44,16 @@ module ActiveRecord
|
|||
rescue ArgumentError
|
||||
raise ArgumentError, "Cannot serialize #{object_class}. Classes passed to `serialize` must have a 0 argument constructor."
|
||||
end
|
||||
|
||||
if YAML.respond_to?(:unsafe_load)
|
||||
def yaml_load(payload)
|
||||
YAML.unsafe_load(payload)
|
||||
end
|
||||
else
|
||||
def yaml_load(payload)
|
||||
YAML.load(payload)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,7 +9,15 @@ module ActiveRecord
|
|||
return unless File.file?(filename)
|
||||
|
||||
read(filename) do |file|
|
||||
filename.include?(".dump") ? Marshal.load(file) : YAML.load(file)
|
||||
if filename.include?(".dump")
|
||||
Marshal.load(file)
|
||||
else
|
||||
if YAML.respond_to?(:unsafe_load)
|
||||
YAML.unsafe_load(file)
|
||||
else
|
||||
YAML.load(file)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -148,7 +148,8 @@ class PostgresqlHstoreTest < ActiveRecord::PostgreSQLTestCase
|
|||
assert_equal "fr", x.language
|
||||
assert_equal "GMT", x.timezone
|
||||
|
||||
y = YAML.load(YAML.dump(x))
|
||||
payload = YAML.dump(x)
|
||||
y = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(payload) : YAML.load(payload)
|
||||
assert_equal "fr", y.language
|
||||
assert_equal "GMT", y.timezone
|
||||
end
|
||||
|
|
|
@ -789,7 +789,8 @@ class AttributeMethodsTest < ActiveRecord::TestCase
|
|||
in_time_zone "Pacific Time (US & Canada)" do
|
||||
record = Topic.new(id: 1)
|
||||
record.written_on = "Jan 01 00:00:00 2014"
|
||||
assert_equal record, YAML.load(YAML.dump(record))
|
||||
payload = YAML.dump(record)
|
||||
assert_equal record, YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(payload) : YAML.load(payload)
|
||||
end
|
||||
ensure
|
||||
# NOTE: Reset column info because global topics
|
||||
|
|
|
@ -52,7 +52,9 @@ module ActiveRecord
|
|||
cache.dump_to(tempfile.path)
|
||||
|
||||
# Unzip and load manually.
|
||||
cache = Zlib::GzipReader.open(tempfile.path) { |gz| YAML.load(gz.read) }
|
||||
cache = Zlib::GzipReader.open(tempfile.path) do |gz|
|
||||
YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(gz.read) : YAML.load(gz.read)
|
||||
end
|
||||
|
||||
# Give it a connection. Usually the connection
|
||||
# would get set on the cache when it's retrieved
|
||||
|
|
|
@ -147,7 +147,8 @@ module JSONSharedTestCases
|
|||
x = klass.new(resolution: "320×480")
|
||||
assert_equal "320×480", x.resolution
|
||||
|
||||
y = YAML.load(YAML.dump(x))
|
||||
payload = YAML.dump(x)
|
||||
y = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(payload) : YAML.load(payload)
|
||||
assert_equal "320×480", y.resolution
|
||||
end
|
||||
|
||||
|
|
|
@ -550,7 +550,8 @@ class OptimisticLockingTest < ActiveRecord::TestCase
|
|||
|
||||
def test_yaml_dumping_with_lock_column
|
||||
t1 = LockWithoutDefault.new
|
||||
t2 = YAML.load(YAML.dump(t1))
|
||||
payload = YAML.dump(t1)
|
||||
t2 = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(payload) : YAML.load(payload)
|
||||
|
||||
assert_equal t1.attributes, t2.attributes
|
||||
end
|
||||
|
|
|
@ -276,11 +276,12 @@ class StoreTest < ActiveRecord::TestCase
|
|||
|
||||
test "dump, load and dump again a model" do
|
||||
dumped = YAML.dump(@john)
|
||||
loaded = YAML.load(dumped)
|
||||
loaded = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(dumped) : YAML.load(dumped)
|
||||
assert_equal @john, loaded
|
||||
|
||||
second_dump = YAML.dump(loaded)
|
||||
assert_equal @john, YAML.load(second_dump)
|
||||
second_loaded = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(second_dump) : YAML.load(second_dump)
|
||||
assert_equal @john, second_loaded
|
||||
end
|
||||
|
||||
test "read store attributes through accessors with default suffix" do
|
||||
|
|
|
@ -19,26 +19,26 @@ class YamlSerializationTest < ActiveRecord::TestCase
|
|||
def test_roundtrip
|
||||
topic = Topic.first
|
||||
assert topic
|
||||
t = YAML.load YAML.dump topic
|
||||
t = yaml_load YAML.dump topic
|
||||
assert_equal topic, t
|
||||
end
|
||||
|
||||
def test_roundtrip_serialized_column
|
||||
topic = Topic.new(content: { omg: :lol })
|
||||
assert_equal({ omg: :lol }, YAML.load(YAML.dump(topic)).content)
|
||||
assert_equal({ omg: :lol }, yaml_load(YAML.dump(topic)).content)
|
||||
end
|
||||
|
||||
def test_psych_roundtrip
|
||||
topic = Topic.first
|
||||
assert topic
|
||||
t = Psych.load Psych.dump topic
|
||||
t = yaml_load Psych.dump topic
|
||||
assert_equal topic, t
|
||||
end
|
||||
|
||||
def test_psych_roundtrip_new_object
|
||||
topic = Topic.new
|
||||
assert topic
|
||||
t = Psych.load Psych.dump topic
|
||||
t = yaml_load Psych.dump topic
|
||||
assert_equal topic.attributes, t.attributes
|
||||
end
|
||||
|
||||
|
@ -49,30 +49,30 @@ class YamlSerializationTest < ActiveRecord::TestCase
|
|||
def test_raw_types_are_not_changed_on_round_trip
|
||||
topic = Topic.new(parent_id: "123")
|
||||
assert_equal "123", topic.parent_id_before_type_cast
|
||||
assert_equal "123", YAML.load(YAML.dump(topic)).parent_id_before_type_cast
|
||||
assert_equal "123", yaml_load(YAML.dump(topic)).parent_id_before_type_cast
|
||||
end
|
||||
|
||||
def test_cast_types_are_not_changed_on_round_trip
|
||||
topic = Topic.new(parent_id: "123")
|
||||
assert_equal 123, topic.parent_id
|
||||
assert_equal 123, YAML.load(YAML.dump(topic)).parent_id
|
||||
assert_equal 123, yaml_load(YAML.dump(topic)).parent_id
|
||||
end
|
||||
|
||||
def test_new_records_remain_new_after_round_trip
|
||||
topic = Topic.new
|
||||
|
||||
assert topic.new_record?, "Sanity check that new records are new"
|
||||
assert YAML.load(YAML.dump(topic)).new_record?, "Record should be new after deserialization"
|
||||
assert yaml_load(YAML.dump(topic)).new_record?, "Record should be new after deserialization"
|
||||
|
||||
topic.save!
|
||||
|
||||
assert_not topic.new_record?, "Saved records are not new"
|
||||
assert_not YAML.load(YAML.dump(topic)).new_record?, "Saved record should not be new after deserialization"
|
||||
assert_not yaml_load(YAML.dump(topic)).new_record?, "Saved record should not be new after deserialization"
|
||||
|
||||
topic = Topic.select("title").last
|
||||
|
||||
assert_not topic.new_record?, "Loaded records without ID are not new"
|
||||
assert_not YAML.load(YAML.dump(topic)).new_record?, "Record should not be new after deserialization"
|
||||
assert_not yaml_load(YAML.dump(topic)).new_record?, "Record should not be new after deserialization"
|
||||
end
|
||||
|
||||
def test_types_of_virtual_columns_are_not_changed_on_round_trip
|
||||
|
@ -80,7 +80,7 @@ class YamlSerializationTest < ActiveRecord::TestCase
|
|||
.joins(:posts)
|
||||
.group("authors.id")
|
||||
.first
|
||||
dumped = YAML.load(YAML.dump(author))
|
||||
dumped = yaml_load(YAML.dump(author))
|
||||
|
||||
assert_equal 5, author.posts_count
|
||||
assert_equal 5, dumped.posts_count
|
||||
|
@ -94,7 +94,7 @@ class YamlSerializationTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_deserializing_rails_v2_yaml
|
||||
topic = YAML.load(yaml_fixture("rails_v2"))
|
||||
topic = yaml_load(yaml_fixture("rails_v2"))
|
||||
|
||||
assert_not_predicate topic, :new_record?
|
||||
assert_equal 1, topic.id
|
||||
|
@ -103,7 +103,7 @@ class YamlSerializationTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_deserializing_rails_v1_mysql_yaml
|
||||
topic = YAML.load(yaml_fixture("rails_v1_mysql"))
|
||||
topic = yaml_load(yaml_fixture("rails_v1_mysql"))
|
||||
|
||||
assert_not_predicate topic, :new_record?
|
||||
assert_equal 1, topic.id
|
||||
|
@ -113,7 +113,7 @@ class YamlSerializationTest < ActiveRecord::TestCase
|
|||
|
||||
def test_deserializing_rails_41_yaml
|
||||
topic = assert_deprecated do
|
||||
YAML.load(yaml_fixture("rails_4_1"))
|
||||
yaml_load(yaml_fixture("rails_4_1"))
|
||||
end
|
||||
|
||||
assert_predicate topic, :new_record?
|
||||
|
@ -124,7 +124,7 @@ class YamlSerializationTest < ActiveRecord::TestCase
|
|||
|
||||
def test_deserializing_rails_4_2_0_yaml
|
||||
topic = assert_deprecated do
|
||||
YAML.load(yaml_fixture("rails_4_2_0"))
|
||||
yaml_load(yaml_fixture("rails_4_2_0"))
|
||||
end
|
||||
|
||||
assert_not_predicate topic, :new_record?
|
||||
|
@ -136,7 +136,7 @@ class YamlSerializationTest < ActiveRecord::TestCase
|
|||
def test_yaml_encoding_keeps_mutations
|
||||
author = Author.first
|
||||
author.name = "Sean"
|
||||
dumped = YAML.load(YAML.dump(author))
|
||||
dumped = yaml_load(YAML.dump(author))
|
||||
|
||||
assert_equal "Sean", dumped.name
|
||||
assert_equal author.name_was, dumped.name_was
|
||||
|
@ -146,12 +146,16 @@ class YamlSerializationTest < ActiveRecord::TestCase
|
|||
def test_yaml_encoding_keeps_false_values
|
||||
topic = Topic.first
|
||||
topic.approved = false
|
||||
dumped = YAML.load(YAML.dump(topic))
|
||||
dumped = yaml_load(YAML.dump(topic))
|
||||
|
||||
assert_equal false, dumped.approved
|
||||
end
|
||||
|
||||
private
|
||||
def yaml_load(payload)
|
||||
YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(payload) : YAML.load(payload)
|
||||
end
|
||||
|
||||
def yaml_fixture(file_name)
|
||||
path = File.expand_path(
|
||||
"support/yaml_compatibility_fixtures/#{file_name}.yml",
|
||||
|
|
|
@ -20,9 +20,9 @@ module ActiveSupport
|
|||
|
||||
def parse(context: nil, **options)
|
||||
source = render(context)
|
||||
begin
|
||||
YAML.load(source, aliases: true, **options) || {}
|
||||
rescue ArgumentError
|
||||
if YAML.respond_to?(:unsafe_load)
|
||||
YAML.unsafe_load(source, **options) || {}
|
||||
else
|
||||
YAML.load(source, **options) || {}
|
||||
end
|
||||
rescue Psych::SyntaxError => error
|
||||
|
|
|
@ -713,7 +713,8 @@ class DurationTest < ActiveSupport::TestCase
|
|||
end
|
||||
|
||||
def test_durations_survive_yaml_serialization
|
||||
d1 = YAML.load(YAML.dump(10.minutes))
|
||||
payload = YAML.dump(10.minutes)
|
||||
d1 = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(payload) : YAML.load(payload)
|
||||
assert_equal 600, d1.to_i
|
||||
assert_equal 660, (d1 + 60).to_i
|
||||
end
|
||||
|
|
|
@ -217,7 +217,8 @@ class TimeWithZoneTest < ActiveSupport::TestCase
|
|||
time: 1999-12-31 19:00:00.000000000 Z
|
||||
EOF
|
||||
|
||||
assert_equal(@twz, YAML.load(yaml))
|
||||
loaded = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(yaml) : YAML.load(yaml)
|
||||
assert_equal(@twz, loaded)
|
||||
end
|
||||
|
||||
def test_ruby_yaml_load
|
||||
|
@ -230,7 +231,8 @@ class TimeWithZoneTest < ActiveSupport::TestCase
|
|||
time: 1999-12-31 19:00:00.000000000 Z
|
||||
EOF
|
||||
|
||||
assert_equal({ "twz" => @twz }, YAML.load(yaml))
|
||||
loaded = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(yaml) : YAML.load(yaml)
|
||||
assert_equal({ "twz" => @twz }, loaded)
|
||||
end
|
||||
|
||||
def test_httpdate
|
||||
|
|
|
@ -854,6 +854,8 @@ class TimeZoneTest < ActiveSupport::TestCase
|
|||
end
|
||||
|
||||
def test_yaml_load
|
||||
assert_equal(ActiveSupport::TimeZone["Pacific/Honolulu"], YAML.load("--- !ruby/object:ActiveSupport::TimeZone\nname: Pacific/Honolulu\n"))
|
||||
payload = "--- !ruby/object:ActiveSupport::TimeZone\nname: Pacific/Honolulu\n"
|
||||
loaded = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(payload) : YAML.load(payload)
|
||||
assert_equal(ActiveSupport::TimeZone["Pacific/Honolulu"], loaded)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
* Fix compatibility with `psych >= 4`.
|
||||
|
||||
Starting in Psych 4.0.0 `YAML.load` behaves like `YAML.safe_load`. To preserve compatibility
|
||||
`Rails.application.config_for` now uses `YAML.unsafe_load` if available.
|
||||
|
||||
*Jean Boussier*
|
||||
|
||||
* Allow loading nested locales in engines.
|
||||
|
||||
*Gannon McGibbon*
|
||||
|
|
|
@ -275,10 +275,13 @@ module Rails
|
|||
if path = paths["config/database"].existent.first
|
||||
require "rails/application/dummy_erb_compiler"
|
||||
|
||||
yaml = Pathname.new(path)
|
||||
erb = DummyERB.new(yaml.read)
|
||||
yaml = DummyERB.new(Pathname.new(path).read).result
|
||||
|
||||
YAML.load(erb.result) || {}
|
||||
if YAML.respond_to?(:unsafe_load)
|
||||
YAML.unsafe_load(yaml) || {}
|
||||
else
|
||||
YAML.load(yaml) || {}
|
||||
end
|
||||
else
|
||||
{}
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue