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