Calling a method with keywords arguments - ruby-on-rails

Ruby 2.6.3
Rails 5.2
def test_method(param1:, param2:, param3:)
end
test_method(param1: "food")
It is giving me the following error message:
Traceback (most recent call last):
2: from (irb):11
1: from (irb):8:in `test_method'
ArgumentError (missing keywords: param2, param3)
I thought the point of using method parameters, is for me to be able to specify the params I want to use, when calling this method. What am I missing? I am using this in a Rails 5.2 application, if that makes a difference.

There are requried keyword arguments (use something:) and optional keyword arguments (use something: default_value). I think required keyword parameters should go before optional ones.

Related

How do I troubleshoot "wrong number of arguments" in solidus affirm

Struggling to get solidus_affirm working and hoping anyone might have some ideas. I always have trouble with "wrong number of arguments" errors like this.
Can get through checkout all the way to the confirm step, where it throws a 500.
ArgumentError in Spree::CheckoutController#update
wrong number of arguments (given 2, expected 1)
Extracted source (around line #28):
def post(path, **data)
connection.post(normalized_path(path), data)
end
affirm-ruby (1.1.2) lib/affirm/client.rb:28:in 'post'
affirm-ruby (1.1.2) lib/affirm/client.rb:10:in 'public_send'
affirm-ruby (1.1.2) lib/affirm/client.rb:10:in 'request'
affirm-ruby (1.1.2) lib/affirm/charge.rb:7:in 'authorize'
solidus_affirm (4f8d9ee63345) lib/solidus_affirm/affirm_client.rb:18:in 'authorize'
solidus_core (3.1.5) app/models/spree/payment_method.rb:40:in 'authorize'
if I put a binding.pry in lib/affirm/charge.rb:7:in 'authorize' of the affirm gem, and try the respond Client.request method there, I get a faraday deprecation warning, not sure if that's the cause or unrelated. Seems like it's injecting an auth header or something?
pry(Affirm::Charge)> respond Client.request(:post, "charges", checkout_token: token)
WARNING: 'Faraday::Connection#basic_auth' is deprecated; it will be removed in version 2.0.
While initializing your connection, use '#request(:basic_auth, ...)' instead.
See https://lostisland.github.io/faraday/middleware/authentication for more usage info.
In that same binding.pry, I tried a few things to see what came back:
[2] pry(Affirm::Charge)> respond Client.request(:post)
ArgumentError: wrong number of arguments (given 1, expected 2)
pry(Affirm::Charge)> respond Client.request(:post, "charges")
WARNING: 'Faraday::Connection#basic_auth' is deprecated; it will be removed in version 2.0.
While initializing your connection, use '#request(:basic_auth, ...)' instead.
See https://lostisland.github.io/faraday/middleware/authentication for more usage info.
ArgumentError: wrong number of arguments (given 2, expected 1)
Any help troubleshooting the error would be much appreciated.
Apparently the error was caused by a change in the behavior of the double splat operator in Ruby 3 and needs to be fixed in the affirm-ruby gem. I submitted a PR and overrode the methods in an initializer.
In Ruby 3.0, positional arguments and keyword arguments will be separated.
Ruby 2.7 will warn for behaviors that will change in Ruby 3.0. If you
see the following warnings, you need to update your code:
Using the last argument as keyword parameters is deprecated, or
Passing the keyword argument as the last hash parameter is deprecated,
or Splitting the last argument into positional and keyword parameters
is deprecated In most cases, you can avoid the incompatibility by
adding the double splat operator. It explicitly specifies passing
keyword arguments instead of a Hash object. Likewise, you may add
braces {} to explicitly pass a Hash object, instead of keyword
arguments.
Source: https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/
The splat operator was breaking things: request(method, path, **data)
I created an initializer at config/initializers/affirm-ruby.rb to override the gem's methods in our Solidus app.
module Affirm
class Client
class << self
def request(method, path, data={})
new.public_send(method, path, data)
end
end
def get(path, data={})
connection.get(normalized_path(path), data)
end
def post(path, data={})
connection.post(normalized_path(path), data)
end
end
end
The problem is that the call to request() is ambiguous.
respond Client.request(:post, "charges", checkout_token: token)
Looking at the source for the request() method, it takes two specific positional arguments, followed by any number of positional arguments. That's the problem: it only takes positional arguments. Your call includes a keyword argument of checkout_token, but the request() method doesn't know what that is, because there's no checkout_token keyword parameter on the method definition.
If you need to give a method a hash, you should give it exactly that, just like the article you linked suggests:
respond Client.request(:post, "charges", { checkout_token: token })

ArgumentError in FactoryBot build method in Ruby On Rails

Good morning all,
I am following an online course from Udemy, in the latter portion of the course, we learned to use FactoryBot. At this moment, I'm having an error in irb on FactoryBot.build(:book) method
irb(main):001:0> FactoryBot.build(:book)
Traceback (most recent call last):
1: from (irb):1
ArgumentError (missing keywords: from, to)
I don't find out why, because, first of all, I followed the course to the letter (I came back to it several times). Secondly, I have the same version as the trainer:
2.5.1

NoMethodError in controller that has methods defined

I finished implementing and testing a controller, then switched back and forth between Git branches and did some merges here and there, etc.
Now I'm unable to use the methods I've defined for the controller, and also very confused, as I'm getting NoMethodError when trying to call them.
Added an edit and solution at the end of the post.
Using Rails version 5.2.3 -
Here I've got my controller defined: app/controllers/paypal_access_token_controller.rb:
class PaypalAccessTokenController < ApplicationController
before_action :authenticate_admin!, only: [:show]
def njurf
puts "#######################"
puts "why can't i call these methods dangit"
puts "#######################"
end
def show
# doesn't really matter
end
def update
# doesn't really matter either
# no syntax errors, I promise
end
end
I'd like the update method to be called when I start the Rails application.
I added PaypalAccessToken.update() to the file config/environments/development.rb - this worked.
Now that I've implemented stuff in other seemingly unrelated parts of the application, I can't call on methods from this controller anymore.
I removed the line from config/environments/development.rb so that I could run the Rails console and added the njurf method to the controller. From the console, I tried calling PaypalAccessTokenController.njurf, and PaypalAccessTokenController.update, but both give me the bespoke NoMethodError.
Here's some proof of concept
Loading development environment (Rails 5.2.3)
irb(main):001:0> PaypalAccessTokenController.update
Traceback (most recent call last):
1: from (irb):1
NoMethodError (undefined method `update' for PaypalAccessTokenController:Class)
irb(main):002:0> PaypalAccessTokenController.update()
Traceback (most recent call last):
1: from (irb):2
NoMethodError (undefined method `update' for PaypalAccessTokenController:Class)
irb(main):003:0> PaypalAccessTokenController.njurf()
Traceback (most recent call last):
1: from (irb):3
NoMethodError (undefined method `njurf' for PaypalAccessTokenController:Class)
So the controller class exists, at least, but I don't know why I'm getting this error - nor do I know how to go about fixing it.
Any help would be appreciated.
edit: This controller only had routes for the show method. I removed this route when I had implemented and tested the update method, because I no longer needed/wanted these methods to be accessible via URLs.
This is what made the methods inaccessible from console.
solution: Either I leave in a route to one of the controller's methods - or, like Ricky Spanish and amit_saxena pointed out below, properly declare the methods as class methods.
Thanks for the replies
You can call it, when you define it with self
def self.njurf
puts "#######################"
puts "why can't i call these methods dangit"
puts "#######################"
end
PaypalAccessTokenController.njurf
#######################
why can't i call these methods dangit
#######################
=> nil
It might look strange, but are you sure you have defined the proper resources on routes?
You cannot access controller methods like that from console as they aren't class methods. You can instead try this:
PaypalAccessTokenController.new.update
but that doesn't mean it's going to work (it probably needs params to work on), but you will not get a NoMethodError. Most probably you messed something in your routes file. You can check the routes using:
bundle exec rake routes
which will tell you exactly which route points to which controller#action and should give you some pointers about what the problem is. Paste the relevant routes here is you are unable to figure it out. Also pay attention to HTTP method (GET/POST/PATCH). You might be using a GET instead of PATCH and getting the error as a result.

TypeError: wrong argument type String (expected Array)

I am trying to execute following line in rails console (bundle exec rails c):
query = select("product.id").where("admin_id = ? and account_id = ?", 3, 4)
But I get following error:
TypeError: wrong argument type String (expected Array)
from (irb):83:in `select'
This code seems to be working fine in the application as such. Any clue on why it is failing in irb?
ruby version -> 1.9.3p545
Rails version -> 3.2.8
This code is originally defined in a ActiveRecord class (Product) in a scope.
How to execute it through rails console ?
You haven't specified the receiver for select, hence it tries to execute select method defined on Kernel module. In your application this call is wrapped within some class, which becomes a default receiver. You need to add this receiver to your call in irb, most likely:
query = Product.select("product.id").where("admin_id = ? and account_id = ?", 3, 4)
Update:
Since this is defined as a scope, you can just use scope name to execute it:
query = Product.scope_name

Rails/Ruby send method can't build paths with params?

So i found this strange anomaly while working in a gem that we are using internally.
We have this private method
private
def redirect_to_element(element, next_upload)
send("scorecard_#{element.base_class_name.underscore}_path", current_scorecard, current_tab(element, next_upload))
end
Which just builds a path dynamically depending on what element is passed to it. What i would like to do is have those dynamic paths pass some params. But i get this error
undefined method `scorecard_enterprise_development_path(ignore_tracking: true)' for #<#<Class:0x007ff767a702e0>:0x007ff767899a20>
so in the console i tried several things and this is what i found.
>> scorecard_enterprise_development_path
=> "/scorecards/338/enterprise_development"
>> send('scorecard_enterprise_development_path')
=> "/scorecards/338/enterprise_development"
>> scorecard_enterprise_development_path(ignore_tracking: true)
=> "/scorecards/338/enterprise_development?ignore_tracking=true"
>> send('scorecard_enterprise_development_path(ignore_tracking: true)')
!! #<NoMethodError: undefined method `scorecard_enterprise_development_path(ignore_tracking: true)' for #<#<Class:0x007ff767a702e0>:0x007ff767899a20>>
That using the send method to build a path with params will fail. Can anyone explain why this happens?
I am using, ruby -v 1.9.3p327 and rails -v 3.2.16
#send will invoke the method identified by the first argument and pass it any arguments specified.
So you should use the method this way:
send('scorecard_enterprise_development_path', ignore_tracking: true)
See the send documentation

Resources