-
-
Notifications
You must be signed in to change notification settings - Fork 411
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Symbolize keys and freeze values when loading from YAML #583
Symbolize keys and freeze values when loading from YAML #583
Conversation
ea368bf
to
0090775
Compare
lib/i18n/backend/base.rb
Outdated
# are supported in the current version of Pysch. | ||
def yaml_load_options | ||
@yaml_load_options ||= YAML_LOAD_OPTIONS.select do |k, v| | ||
YAML.load_file('test/test_data/locales/en.yml', k => v) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
YAML.load_file('test/test_data/locales/en.yml', k => v) | |
YAML.load('', **{k => v}) |
Two things here:
- That
load_file
wouldn't work once loaded in an app because it is relative to the current working directory ($PWD)
). But you don't need to load a file anyway,load
take the same options. - It's best to use
**{}
to make sure it's passed as keyword arguments, not a positional hash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initially, I was trying to use #load
, but it turns out load's signature wasn't always a good proxy for the signature of load_file
.
Until this commit (by yourself), ruby/psych#463, not all kwargs on load
were available to load_file
.
This is problematic, as for instance on Ruby 2.7.4, load
accepts :symbolize_names
but load_file
doesn't.
Any suggestions? I certainly agree that using load
would be optimal, and didn't fully think through the consequence of using load_file
as you pointed out above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could accessing the methods args directly help here? Something along the lines of
YAML_LOAD_OPTIONS = YAML.method(:load_file).parameters.each_with_object({}) do |(context, param_name), args|
args[param_name] = true if context == :key && %i[symbolize_names freeze].include?(param_name)
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could accessing the methods args directly help here?
In this instance no, because for a very long time these methods weren't taking keyword args, but one big option hash.
0090775
to
2a15f69
Compare
lib/i18n/backend/base.rb
Outdated
@@ -236,14 +236,23 @@ def load_rb(filename) | |||
eval(IO.read(filename), binding, filename) | |||
end | |||
|
|||
# Feature tests YAML load options and only selects those that | |||
# are supported in the current version of Psych. | |||
YAML_LOAD_OPTIONS = { symbolize_names: true, freeze: true }.select do |k, v| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than rescuing on an ArgumentError
, I would rather a version check (either of Ruby, or of whatever's added the support for these options).
Pseudocode like:
if version >= 3.0.0
<symbolise code>
else
<old path>
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that bundled gem sometimes "lie" about their version because of https://bugs.ruby-lang.org/issues/18169, it happened to me previously hence I I now default to feature testing.
That being said, we could conflate this with the feature testing for unsafe_load_file
:
if YAML.respond_to?(:unsafe_load_file) # Psych 4.0 way
YAML.unsafe_load_file(filename, symbolize_names: true, freeze: true)
else
YAML.load_file(filename)
end
We'll miss a few versions but that's probably fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please do not rescue from ArgumentError.
b52fa23
to
0547768
Compare
@paarthmadan I took the liberty to update your PR. Now it only applies for |
Thanks @casperisfine, I appreciate it! FWIW, I agree with the proposed strategy 👍 We scope the change to a smaller (but incomplete) subset of versions to avoid needing to rescue from an |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
request change
@radar Just bumping this! Addressed your feedback, now we simply append the arguments when we're certain they're supported. |
A couple note (which doesn't mean they should be added to this PR): The same could be done for
That being said, I've never seen a As for performance, with the keys already parsed as symbols, The problem is that it's called in |
@@ -72,7 +72,11 @@ def setup | |||
|
|||
test "simple load_yml: loads data from a YAML file" do | |||
data = I18n.backend.send(:load_yml, "#{locales_dir}/en.yml") | |||
assert_equal({ 'en' => { 'foo' => { 'bar' => 'baz' } } }, data) | |||
if ::YAML.respond_to?(:unsafe_load_file) | |||
assert_equal({ :en => { :foo => { :bar => 'baz' } } }, data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like a check for frozen values here too, please. I think this might work here:
assert data.dig(:en, :foo, :bar).frozen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, added!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add check for frozen
0547768
to
5e5a092
Compare
Please fix the conflict on this branch. |
5e5a092
to
787bf0d
Compare
787bf0d
to
0fda789
Compare
Changes introduced
Pysch, Ruby's YAML parser, now supports configuring
freeze
andsymbolize_names
as options forYAML#unsafe_load_file
andYAML#load_file
.This PR sets both of these parameters to true.
Intended outcomes
In setting
freeze: true
, all values returned will now be frozen objects. Further, string values will be deduplicated.In setting
symbolize_names: true
, keys will be directly parsed as symbols. This is particularly beneficial, as we already eventually perform a call to#deep_symbolize_keys
. This way, they'll take on theirSymbol
representation at parse-time.