Reputation: 45
I'm working on a Rails app that's built largely on Michael Hartl's Rails Tutorial. I'm getting an error in Logentries that I can't quite track down.
ActionView::Template::Error (wrong number of arguments (1 for 2)):
31: <div class="inner">
32: <h1 id="fadeThis" style="display: none;">The Lake County Bar Association</h1>
33:
heroku router - - at=info method=GET path="/" host=www.example.org request_id="REMOVED" fwd="xx.xx.xx.xx" dyno=web.1 connect=1ms service=10ms status=500 bytes=1669 protocol=http
34: <% if logged_in? %>
35: <br>
36: <h2 class="margin-top-0 wow fadeIn" id="welcomeText"> Welcome back, <%= current_user.first_name %>! </h2>
37: <% else %>
app/models/user.rb:46:in `authenticated?'
app/helpers/sessions_helper.rb:11:in `current_user'
app/helpers/sessions_helper.rb:21:in `logged_in?'
app/views/static_pages/home.html.erb:34:in `_app_views_static_pages_home_html_erb__118074299580396133_69921140988760'
Here are the authenticated? methods in user.rb:
def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
And the current_user and logged_in? methods from the sessions_helper.rb:
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(cookies[:remember_token])
log_in user
@current_user = user
end
end
end
def logged_in?
!current_user.nil?
end
And the relevant section from home.html.erb:
<% if logged_in? %>
<br>
<h2 class="margin-top-0 wow fadeIn" id="welcomeText"> Welcome back, <%= current_user.first_name %>! </h2>
<% else %>
<h3> <span class="element"> <br></span></h3>
<hr>
<br>
Upvotes: 2
Views: 893
Reputation:
As mentioned, Ruby does not support method overloading based on parameters. To be able to do what you want to do, you can use arguments, keyword arguments or options hash.
My preference is for the keyword arguments since I've seen that in Python a lot and really like how clear it is. Your preference or the project preference may be different.
def authenticated?(*args)
case args.count
when 1
remember_token = args[0]
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
when 2
attribute, token = args
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
end
Calling the method:
authenticated?(false)
authenticated?('whatever', 'cool')
You could also check the types of the arguments or use parameter ordering (for example authenticated?(nil, nil, true)
where the first two arguments are the attribute and token, and the third argument is remember_token).
Keyword arguments are under-used in Ruby but they have been available since Ruby 2.0.
def authenticated?(remember_token: nil, attribute: nil, token: nil)
if remember_token
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
elsif attribute && token
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
end
Calling the method:
authenticated?(attribute: 'whatever', token: 'cool')
authenticated?(remember_token: false)
Very common pattern/idiom in Ruby to use an options hash:
def authenticated?(options = {})
if options['remember_token']
return false if options['remember_token'].nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
elsif options['attribute'] && options['token']
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
end
Calling the method:
authenticated?(:attribute => 'whatever', :token => 'cool')
authenticated?(:remember_token => false)
Upvotes: 1
Reputation: 33552
Ruby doesn't really supports method overloading. From this doc
You want to create two different versions of a method with the same name: two methods that differ in the arguments they take. However, a Ruby class can have only one method with a given name (if you define a method with the same name twice, the latter method definition prevails
So in your case only authenticated?(attribute, token)
gets called
Upvotes: 1