I'm going to be deprecating passing arguments on a method. I know how to use ActiveSupport::Deprecation for a method, but the method isn't going anywhere. Just passing arguments to it is going away.
This particular method is used a TON in testing, so I definitely don't want a warning every time! I've already removed the arguments from the tests, so I shouldn't see any warnings when I run the test suite unless someone adds the method with the arguments. How can I make this happen?
Here's a faked version of the method with the (*args) that will be deprecated:
def assert_in_tests(*args)
code_happens_here
end
The method (once the (*args) are deprecated) will be the following (and I don't want a deprecation warning when it's utilized):
def assert_in_tests
code_happens_here
end
the method isn't going anywhere. Just passing arguments to it is going away ... so I shouldn't see any warnings when I run the test suite unless someone adds the method with the arguments
A simple if statement should suffice would it not?
def assert_in_tests(*args)
ActiveSupport::Deprecation.warn("args to be deprecated ...") if args.present?
code_happens_here
end
Related
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 })
I'm trying to stub a method like so:
allow(Flipper).to receive(:enabled?).with(:premium_plus_features_beta).and_return(false)
but when it hits a different argument - it gives me an error like this:
#<Flipper (class)> received :enabled? with unexpected arguments
expected: (:premium_plus_features_beta)
got: (:non_advertiser_profile_amp, {:lawyer_id=>4469860})
Diff:
## -1,2 +1,2 ##
-[:premium_plus_features_beta]
+[:non_advertiser_profile_amp, {:lawyer_id=>4469860}]
I normally don't stub this much but why when I explicitly tell it the arguments, why is it erroring on different arguments? They are obviously not the same. Is this just some syntax issue?
edit 1
I tried this but doesn't work
https://makandracards.com/makandra/30543-rspec-only-stub-a-method-when-a-particular-argument-is-passed
Flipper.should_receive(:enabled?).and_call_original
Flipper.should_receive(:enabled?).with(:premium_plus_features_beta).and_return(false)
When stubbing methods with specific arguments, you are only stubbing that specific method call with those specific parameters. All other calls to the method will fail with the error:
#<Foo (class)> received :bar with unexpected arguments
As the OP discovered, the solution here is to first stub ALL calls to the object with the and_call_through method, then stub the specific calls with the specific arguments you wish to stub.
From the OP's answer, the first line stubs ALL calls to the Flipper object and allows them to call through to the underlying code, and the second line stubs the call that receives :premium_plus_features_beta and returns false:
allow(Flipper).to receive(:enabled?).and_call_original
allow(Flipper).to receive(:enabled?).with(:beta).and_return(false)
Also, there's one other point to be made here. The code in the OP question used the OLD RSpec expectation syntax. The code in the OP answer uses the NEW RSpec stub syntax. So, when the code said this:
Flipper.should_receive(:enabled?).and_call_original
Flipper.should_receive(:enabled?).with(:beta).and_return(false)
What it was doing was this:
expect(Flipper).to have_received(:enabled?).and_call_original
expect(Flipper).to have_received(:enabled?).with(:beta).and_return(false)
Which is entirely different than what I think the OP was really trying to do:
before do
allow(Flipper).to receive(:enabled?).and_call_original
allow(Flipper).to receive(:enabled?).with(:beta).and_return(enabled?)
end
context "when the beta is disabled" do
let(:enabled?) { false }
it "hides the beta" do
...
end
end
context "when the beta is enabled" do
let(:enabled?) { true }
it "shows the beta" do
...
end
end
Finally, for those who are curious about why RSpec changed the syntax... The old syntax required a monkey patch on Object in order to add the should_receive method. I think the RSpec team preferred the new syntax because it no longer required the monkey patch.
The working answer is:
allow(Flipper).to receive(:enabled?).and_call_original
allow(Flipper).to receive(:enabled?).with(:premium_plus_features_beta).and_return(false)
a bunch of bad info on the internet lol
How can I have a method be called on error when using Minitest with Rails, to generate some extra information about the error that just happened?
Have you considered using something like pry or debugger to get more information? I also find liberally sprinkling puts statements throughout the code helpful to ensure that methods I think are being called, are actually called and to show variable values.
This was as simple as:
def teardown
if !passed?
call_method_for_failure
end
end
I am trying execute test cases with rspec version 2.14 for which I am getting following error
undefined method `rspec_reset' for
I am trying to use rspec_reset on the class. The same test cases are working with rspec 2.13.1. So is it possible that rspec_reset method is not available after 2.13?
The reset method does not exist in RSpec 2.14.x. Instead, it is a helper method defined inside the spec_helper.rb file for the rspec-mocks project.
module VerifyAndResetHelpers
def verify(object)
RSpec::Mocks.proxy_for(object).verify
end
def reset(object)
RSpec::Mocks.proxy_for(object).reset
end
end
You can see that this method delegates the reset action to the underlying proxy instead of tacking it on to the class definition of the object in question.
Yes, in 2.14, rspec_reset is no longer available on all objects as it was previously, as discussed in https://github.com/rspec/rspec-mocks/pull/250.
Although I can't find any documentation on it, there now appears to be an RSpec class method reset which takes an object as an argument and will effectively "undo" any RSpec operations that have been done to that object.
There's an RSpec "example" at https://github.com/rspec/rspec-mocks/blob/cee433c89125a3984df33c87eb61985613adce9b/spec/rspec/mocks/mock_spec.rb which still uses the rspec_reset in the description of the example, but which now uses the aforementioned reset method to do the reset. In earlier versions of the example, the reset was done with rspec_reset.
I have a model named 'Task' in my project.
I upgraded from Rails 3.0 to Rails 3.1 and now I receive the following error. My code hasn't changed.
>> Task.new
WARNING: Deprecated reference to top-level constant 'Task' found at: /Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:2470:in `rakefile_location'
Use --classic-namespace on rake command
or 'require "rake/classic_namespace"' in Rakefile
ArgumentError: wrong number of arguments (0 for 2)
from (irb):1:in `initialize'
from (irb):1:in `new'
from (irb):1
I'm scared I've called my model something that should have been reserved, what should I do for best practice? Refactor and change the name? Or something else?
Update: I tried it's suggestion of updating the Rakefile, but this did not work.
In the end, it turns out 'Task' has been a reserved word for a long time. I used text mate's find and replace to do a refactor, and created migrations to update the database. It only took about an hour, and I feel it was worthwhile to avoid future problems.
I dug a bit deeper and found this out:
When my specs auto-load the Task constant, Rake's const_missing (source code) kicks in, issues that warning (source code) and returns Rake::Task. Which makes my specs fail because I'm now testing that instead of my model.
I then get a lot of these:
NoMethodError:
undefined method `enqueue' for Rake::Task:Class
Well, of course it doesn't implement enqueue — that's not my model!
So, in short, Rake tells me not to use their top-level Task (even though I didn't mean to), and provides me with a different constant, effectively breaking the auto-loading in Rails!
There's only way around that — I had to manually require 'task' in the spec. Now it's all ponies and rainbows.
Dear Jim Weinrich, if you read this: Next time you declare something deprecated, please ensure that you only warn people about this when they actually use a deprecated API!
Another way to handle this is to get rid of rake's deprecation warning. In your spec_helper, before any activity that would reference the model, do this:
# Rake defines a const_missing that, if you reference any of several
# top-level constants, issues a deprecation warning and then defines
# it. Since Rake defines it, rail's own const_missing doesn't
# happen, and our own class doesn't get loaded. The workaround is to
# remove rake's const_missing.
class Module
def const_missing(*args)
rake_original_const_missing(*args)
end
end
We could have renamed our model, but that was more work than this. This, however, might break with future versions of rake. Choose your poison.