jcconnell
jcconnell

Reputation: 45

ActionView::Template::Error (wrong number of arguments (1 for 2)):

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

Answers (2)

user9903
user9903

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.

method overloading via argument count checking

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).

method overloading via keyword arguments (Ruby 2.0+)

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)

method overloading via options hash (Ruby 1.9)

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

Pavan
Pavan

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

Related Questions