I have a library that has an #execute method like this
def execute(query, **args)
# ...
end
I have a class that generates the data for args (which has a lot of logic depending on user abilities)
class Abilities
def to_h
{ user: user } # and a lot more data
end
end
Now when I'm using #execute I always have to remember to use #to_h, which is pretty annoying and leads to mistakes when someone forgets it:
execute(query, abilities.to_h)
So I was wondering if my Abilities class could somehow respond to the ** (double splat) operator, so that I can simply pass the object:
execute(query, abilities)
When I try to call it like this, it throws an error:
ArgumentError: wrong number of arguments (given 2, expected 1)
So, is there any way to make my Abilities class behave like a Hash? I could derive it like this Abilities < Hash but then I have all the Hash logic on it, which seems pretty dirty.
You can implement to_hash: (or define it as an alias for to_h)
class MyClass
def to_hash
{ a: 1, b: 2 }
end
end
def foo(**kwargs)
p kwargs: kwargs
end
foo(MyClass.new)
#=> {:kwargs=>{:a=>1, :b=>2}}
If you specify the API to execute in such a way that it accepts anything that supports to_h, then you have a solution:
def execute(query, args = {})
args = args.to_h
...
end
Related
I want to know, what method calls another method (I'm just trying to create simple expect("string").to eq("string") model (just like in RSpect, but more easier).
But i get "main", what is that? (I see that "main" for first time)
public
def expect(message)
message.to_s
end
def to
caller_method = caller_locations.first.label
puts caller_method
end
expect("test").to #=> <main>
#what output i expected:
expect("test").to #=> expect
My goal:
#first i need to do something like that:
expect("test").to eq("test") #=> true
#final must look like this:
expect(expect("test").to eq("test")).to eq(true) #=> true
I would recommend against using caller_method in this case. Rather, make a class whose methods return self - that way they will be chainable:
module Expectation
attr_accessor :caller_method
def expect(arg)
self.caller_method = "expect"
self
end
def to
caller_method
end
end
include Expectation
expect("foo").to
# => "expect"
Obviously this is only a starting point, and this doesn't actually do any comparisons / validations yet. But hopefully you can understand this pattern. The key thing is returning self to make a chainable API, and storing internal state using something like attr_accessor
I am trying to write this inside my class:
class << self
def steps
#steps.call
end
def transitions
#transitions.call
end
def steps(&steps)
#steps = steps
end
def transitions(&transitions)
#transitions = transitions
end
end
That won't work since in Ruby, I can't do this kind of method overloading. Is there a way around this?
You can kind of do this with method aliasing and mixins, but the way you handle methods with different signatures in Ruby is with optional arguments:
def steps(&block)
block.present? ? #steps = block : #steps.call
end
This sort of delegation is a code smell, though. It usually means there's something awkward about the interface you've designed. In this case, something like this is probably better:
def steps
#steps.call
end
def steps=(&block)
#steps = block
end
This makes it clear to other objects in the system how to use this interface since it follows convention. It also allows for other cases, like passing a block into the steps method for some other use:
def steps(&block)
#steps.call(&block)
end
Ruby does not support method overloading (see "Why doesn't ruby support method overloading?" for the reason). You can, however, do something like:
def run(args*)
puts args
end
args will then be an array of the arguments passed in.
You can also pass in a hash of options to handle arguments, or you can pass in nil when you don't want to supply arguments and handle nil in your method body.
I have a function let's say A whose output and functionality I have to test, A calls another function B which takes a lot of time to compute the output. So I am trying to use stubs to mimic all the values that B returns.
def A
#do something
output = B
#do something with output
end
Now the test files
describe "check what A returns" do
ClassName.stub(:B) do
[0, 1]
end
test_values = TestClass.A(input parameters)
#checks on test values
end
My aim is to pass the expected output of B to function A. I am using RSpec. How do I go about it?
With RSpec you can do:
allow(ClassName).to receive(:B).and_return([1,2,3,4,5])
After this you can call B function and it will return [1,2,3,4,5]
You can find more info at RSpec documentation: https://relishapp.com/rspec/rspec-mocks/v/3-4/docs/configuring-responses/returning-a-value
I've attempted to write some classes and test cases for what it seems like you want to test. The key here is to use allow to stub out the return value for a method.
Just note here that I've changed the methods in your class to be class methods to fit what seems to be your test case, but you can obviously change them back to instance methods to fit your purpose. Also, accepted Ruby style is to have lowercase method names.
class ClassName
def self.B
# returns something that we're going to stub out
end
end
class TestClass
def self.A
# do something
output = ClassName.B
# do something with output
# eg in this case, add a value to it
output << 2
end
end
describe TestClass do
describe '.A' do
before do
allow(ClassName).to receive(:B).and_return([0, 1])
end
it 'does something with output' do
expect(described_class.A).to eq([0, 1, 2])
end
end
end
There's ways as mentioned in other posts but I'll give you another: you might want to make that dependency explicit.
Here's how it could look like:
# test_class.rb
class TestClass
# The default will be automatically setup to be an object of type ClassName
def initialize(some_collaborator: ClassName.new)
self.some_collaborator = some_collaborator # Some people will probably also insert some guard (to make sure it responds to `b`
end
def a
# your code calling `some_collaborator.b`
end
private
attr_accessor :some_collaborator
end
# test_class_spec.rb
describe TestClass do
let(:stub_b) { stub("Some instance of ClassName", b: [...] }
subject { TestClass.new(some_collaborator: stub_b) }
it "whatever" do
expect(subject.a).to ...
end
end
The default collaborator should be a sensible default (and if you can't instantiate it there's ways to encapsulate it anyways). Not only it will be easier to read, but it will be easier to maintain.
In the following code:
def main
someArray.all? { |item| checkSomething(item) }
end
private
def checkSomething(arg)
...
end
How do I shorten the all? statement in order to ged rid of the redundant item variable?
I'm looking for something like someArray.all?(checkSomething) which gives a "wrong number of arguments" error.
You could have a slightly shorter code if checkSomething was a method on your object class. Don't know what it is, so, I'm guessing, you're working with primitives (numbers, strings, etc.). So something like this should work:
class Object
def check_something
# check self
end
end
some_array.all?(&:check_something)
But this is, of course, a horrible, horrible way of going about it. Saving a few keystrokes at the cost of such global pollution - absolutely not worth it. Moreover, even this trick will not be available as soon as you will need to pass additional parameters to the check method.
Besides, the original code is quite readable too.
You could use Object#method and Method#to_proc (i.e. &method) to get rid of the item variable, although it is slower:
def main(array)
array.all?(&method(:check_something))
end
def check_something(arg)
arg.odd?
end
main [1,3,5] #=> true
main [1,3,6] #=> false
If checkSomething is an item method (i.e. defined in the class of the 'i' object) you could do symbol to proc...
def main
someArray.all?(&:checkSomething)
end
A method only has access to passed arguments, or to selfso to bypass passing arguments you need to make the method an instance method of the object class (so it can use self)
The way you have it... where checkSomething is external to the i class... you can't do that.
Considering you want to keep your object's checkSomething private, I think this would be a good work around :
class Something
def main
someArray.all?(&checkSomething)
end
private
def checkSomething
->(item) do
# Checking part.
end
end
end
For block that executes a method with arguments, Checkout this way...
def main
someArray.all? &checkSomething(arg1, arg2, ...)
end
private
def checkSomething(arg1, arg2, ...)
Proc.new { |item| ..... }
end
could you not use a Ruby's collection method 'any?' instead?
def main
#students is an array of students
students.any?(&:passed)
end
class Student
def passed
#code to check if student passed
end
end
Ref http://ruby-doc.org/core-2.2.2/Enumerable.html#method-i-any-3F
I have a class method, call it PetSearch. I want to initialize a search with options via a class variable, ##options. I can pass in either an array or a hash. Chose array as such:
def self.init_options(options=['dog', 94123, 'Young', 'F'])
##options[:animal] = options[0]
##options[:location] = options[1]
##options[:age] = options[2]
##options[:sex] = options[3]
end
However, I want to be able to pass in options like:
def self.init_options(dog, Young)
##options[:dog] = dog
##options[:age] = Young
end
Notice that I would like to pass in a non-string "variable" like dog - not 'dog', and I am passing in the variables indiscriminately without regard to order. I'm assuming there's a meta-programming block/proc/etc. sort of way to do this, but I am still learning how to harness that power. Can someone help me out? I will receive my undying gratitude and major up-votes.
I would never recommend doing this, and it doesn't really make sense: most of the ways you would collect user input would be as strings (e.g., script arguments, form values, etc.).
You can abuse method_missing and const_missing to pass in your arguments as non-strings.
So in your class or module you could have something like:
def self.init_options(*args)
##options[:dog] = args.grep(/[Dd]og|etc|etc/).first
##options[:age] = args.grep(/[Oo]ld|[Yy]oung/).first
end
def self.options
##options
end
And then, in the context where your user is doing the "initializing":
def method_missing(m)
m.to_s
end
def Object.const_missing(c)
c.to_s
end
Testmm.init_options(dog, Young)
puts Testmm.options.inspect
#=> {:dog=>"dog", :age=>"Young"}
Not sure if that is the behavior you are looking for, but it sounds like it. Also note that this won't work in IRB, but I've confirmed that it works as a script.