I am working on a Ruby course and I came across an error when running one of the examples. Here is my Ruby class:
require 'json'
class User
attr_accessor :email, :name, :permissions
def initialize(*args)
@email = args[0]
@name = args[1]
@permissions = User.permisisons_from_template
end
def self.permisisons_from_template
file = File.read 'user_permissions_template.json'
JSON.load(file, nil, symbolize_names: true)
end
def save
self_json = {email: @email, name: @name, permissions: @permissions}.to_json
open('users.json', 'a') do |file|
file.puts self_json
end
end
end
My runner file code looks like this:
require 'pp'
require_relative 'user'
user = User.new '[email protected]', 'John Doe'
pp user
user.save
I get this error when I run this command "ruby runner.rb":
/usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/json/common.rb:156:in `initialize': options :symbolize_names and :create_additions cannot be used in conjunction (ArgumentError)
from /usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/json/common.rb:156:in `new'
from /usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/json/common.rb:156:in `parse'
from /usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/json/common.rb:335:in `load'
from /home/ec2-user/environment/section2_project/user.rb:15:in `permisisons_from_template'
from /home/ec2-user/environment/section2_project/user.rb:10:in `initialize'
from runner.rb:4:in `new'
from runner.rb:4:in `<main>'
I looked for help on the site and the suggested fix was to remove the nil parameter. Now, I am from a .Net background and I surmised that I could use proc: nil
and that would work as well, which it did. My assumption is that it didn't like the mixing of named parameters and positional parameters, but this isn't .Net, so I may just have gotten lucky with my guess. The moderator for the site wasn't sure why the code failed and why removing nil fixed the issue. So, my question is:
Why did the line JSON.load(file, nil, symbolize_names: true)
fail, but JSON.load(file, proc: nil, symbolize_names: true)
work? Thanks.
Wade
What's happening here is that the arguments aren't being parsed in the way you're expecting. There's a feature in Ruby where any key: value
at the end of the argument list are made into a Hash
without the need to put the {}
around them.
For example if you write a method:
def load(source, options = {})
end
This can be called as load(source)
in which case options
will be {}
, or as something like load(source, foo: 5, bar: true)
in which case options
will be {foo: 5, bar: true}
The other detail is that optional parameters with default values are filled left to right.
Why is this relevant?
Well, in the case of JSON.load(file, proc: nil, symbolize_names: true)
the proc: nil, symbolize_names: true
becomes the Hash {proc: nil, symbolize_names: true}
and this then fills the proc
position in the argument list, leaving the options
parameter with its default value. i.e. you aren't actually setting symbolize_names: true
when you thought you were.
In the case of JSON.load(file, nil, symbolize_names: true)
, the nil
fills the value of the proc
parameter, and symbolize_names: true
becomes options
. This gets combined with the default options in the JSON library to give the full set of options, {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true, :symbolize_names=>true}
which then contains the conflict that the error message is referencing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With