How to filter out one method call from many others with `expect().to receive()` in RSpec - ruby-on-rails-3

I have such a code:
class ClassB
def print_letter(arg)
end
end
class ClassA
def self.my_method
ClassB.print_letter("a")
ClassB.print_letter("b")
end
end
RSpec.describe ClassA do
describe "self.my_method" do
it "prints a" do
allow(ClassB)
expect(ClassB).to receive(:print_letter).once.with("a")
described_class.my_method
end
end
end
And I'm getting a failure:
#<ClassB (class)> received :print_letter with unexpected arguments
expected: ("a")
got: ("b")
Can I do anything with that? Is there any way to force the receive method to analyze all method calls and pick the one where arguments match, not only the last one? BTW, this behavior seems confusing to me.

It's a good practice to give one responsability to one method.
In your case I guess you would like to test that your method return "A" and also "B".
I will recommend you to write a method which will return "A" And another one which return "B".
def print_a
ClassB.print_letter("a")
end
def print_b
ClassB.print_letter("b")
end
def self.my_method
print_a
print_b
end
And then just test your methods separatly, for example:
it " Print a" do
expect(print_a).to eq 'a'
end
In this way you don't need to test your self.my_method, it will Be overkill.

Related

How do "variables" work with recursion in Elixir?

I am an absolute beginner in Elixir and I tried toying around with recursion by making a function like this:
def player_choice() do
choice = IO.gets("Choose rock, paper or scissors: ") |> String.trim()
unless String.downcase(choice) in ["rock", "paper", "scissors"] do
IO.puts("\nPlease insert a valid move")
choice = player_choice()
end
choice
end
So if someone inputs a value thats not expected the function should continue asking for an input.
But if the first input is something unexpected like "no", and the next input is "rock" the function will return "no". Why does this happen, shouldn't "choice" be the reassigned value?
The problem is that you redefine a new variable choice in your internal unless, masking the outer one (in Elixir, you don't "reassign"). The compiler warns you:
variable "choice" is unused (there is a variable with the same name in
the context
A better solution would be something like:
def player_choice() do
choice = IO.gets("Choose rock, paper or scissors: ") |> String.trim() |> String.downcase
if choice in ["rock", "paper", "scissors"] do
choice
else
IO.puts("\nPlease insert a valid move")
player_choice()
end
end
Pattern matching seems an idiomatic way to approach this
defmodule Playground do
def player_choice(choice) when choice in ["rock", "paper", "scissors"], do: choice
def player_choice(_) do
IO.puts("\nPlease insert a valid move")
prompt()
end
def prompt() do
IO.gets("Choose rock, paper or scissors: ")
|> String.trim
|> String.downcase
|> Playground.player_choice
end
end

What runtime code can I write to extract the type notations as defined by Sorbet, to display to the user?

Is there a way to inspect the type notations in ruby/sorbet?
I actually want to display those values in a form somewhere and instead of hardcoding in values like "this can be nil or a String", I want to instead use the code I've defined by the sig { ... }.
Example pseudo-code:
class User
extend T::Sig
sig { returns(T.nilable(String)) }
def foo
...
end
end
Sorbet.return_type(User, :instance_method, :foo) # returns '[nil, String]' or similar
I believe this is what I am looking for:
T::Utils.signature_for_method
In action:
T::Utils.signature_for_method(User.new.method(:daily?)).return_type
=> #<T::Types::Union:0x0000000132198da8
#types=[#<T::Types::Simple:0x0000000132198f60 #name="TrueClass", #raw_type=TrueClass>, #<T::Types::Simple:0x0000000132198e98 #name="FalseClass", #raw_type=FalseClass>]>

Increment integer on save in Rails 3?

I have the following code in my Rails 3 application:
def like
#suggestion = Suggestion.find(params[:id])
Suggestion.update_all("votes = (votes + 1)")
redirect_to suggestions_url
end
def dislike
#suggestion = Suggestion.find(params[:id])
Suggestion.update_all("votes = (votes - 1)")
redirect_to suggestions_url
end
It's working, but rather than updating the current suggestion it's updating them all. So I changed it to:
def like
#suggestion = Suggestion.find(params[:id])
#suggestion.update_all("votes = (votes + 1)")
redirect_to suggestions_url
end
def dislike
#suggestion = Suggestion.find(params[:id])
#suggestion.update_all("votes = (votes - 1)")
redirect_to suggestions_url
end
but then I get:
undefined method `update_all' for #<Suggestion:0x007f87c2b918a0>
So then I tried #suggestion.update_attribute(:votes, '1') but that resets the value to 1 instead of incrementing it.
What's the correct way to achieve this? I just want the integer (votes) of the current suggestion to increment/decrease by 1 on each save.
I've also tried the following with no luck:
def like
#suggestion = Suggestion.find(params[:id])
#suggestion.increment(:votes)
redirect_to suggestions_url
end
This seems more suitable in the model. I suggest creating a like method inside the model like so:
def like
self.increment!(:votes)
end
Now you can do something like this:
#suggestion = Suggestion.find(params[:id])
#suggestion.like
Note: increment!, with the exclamation point also performs the save action
A couple things. It sounds like what you want is a controller action that increments an attribute by one. You were probably closest with the code
#suggestion.update_attribute(:votes, '1')
If you check the documentation for that method, it sets the value of the attribute votes to the second arguement, the string '1', on the object, #suggestion, and its corresponding row in the database. Instead of setting it to '1', you want to set it to the incremented value:
#suggestion.update_attribute(:votes, #suggestion.votes + 1)
Ethan suggested using the convenience method, increment!, which works just the same.
Now, if you wanted to actually auto-increment each time the object gets saved (as in something else about the object gets altered, you'd want to use the :before_save callback with increment without the bang.

Lua metatables and metamethod - How to call a different member function

I have the following Class
local PROGRESS = {}
PROGRESS.__index = function(self,key)
if key~="__group" and self.__group[key] then
return self.__group[key]
else
return rawget(self,key)
end
end
What this does is when You access table[key] it performs a lookup in table.__group (which is an object of another class) and returns table.__group[key] ,if it is not nil.
Now I am trying to do the same for member functions.
i.e If I call table:key() a lookup must be performed in table.__group and if the function is present, then table.__group:key() should be called.
How do I accomplish this?
I tried to do this.
local PROGRESS = {}
PROGRESS.__index = function(self,key)
if key~="__group" and self.__group[key] then
local val = self.__group[key]
if type(val) == "function" then
self.__group:val()
return function() end
end
return self.__group[key]
else
return rawget(self,key)
end
end
But there are 2 things wrong here.
I am unable to retrieve the original function's arguments
Event if I just ACCESS table[key].function without calling it, the function will be called
And I've got the feeling that I am trying to complicate things and the solution is way simpler.
Any help is appreciated.
UPDATE
#Mud
The problem with the original code is that the object passed as 'self' to the member function is an object of the new class. Not of the old class.
Consider this code
GROUP_CLASS = {}
GROUP_CLASS.__index = GROUP_CLASS
function GROUP_CLASS:showSum (a,b) print(self);print(a + b) end
group_object = setmetatable({},GROUP_CLASS)
group_object:showSum(1,2)
local PROGRESS_CLASS = {}
PROGRESS_CLASS.__index = function(self,key,value)
if key~="__group" and self.__group[key] then
return self.__group[key]
else
return rawget(self,key)
end
end
progress_object = setmetatable( {__group = group_object} , PROGRESS_CLASS)
progress_object:showSum(3,3)
--progress_object is passed as first argument to showSum. But i need group_object to be passed
In the above code, When progress_object:showSum(3,3) is called,
is it possible to pass group_object (or in other words progress_object.__group) as self instead of progress_object.
Hope that makes sense.
Response to updated post:
progress_object is passed as first argument to showSum. But i need group_object to be passed
If you're going to ignore the state of the object a method is called on, and substitute the state of some other object, why is it even a method on that object? That's like overriding the addition operator to do multiplication, a recipe for confusion.
In other words, you want this:
progress_object:method("foo")
To resolve, via bizarre internal machinery, into this:
group_object:method("foo")
Why not skip a step and just make the latter call?
If you must, you could achieve this by returning a proxy for the method which replaces self with __group
local PROGRESS_CLASS = {}
PROGRESS_CLASS.__index = function(self,key)
local groupval = self.__group[key]
if key == '__group' or not groupval then
return rawget(self,key)
elseif type(groupval) ~= 'function' then
return groupval
else
return function(...)
if self == ... then -- method call
-- replace self argument with __group
return groupval(self.__group,select(2,...))
else
return groupval(...)
end
end
end
end
Response to original post:
How I am trying to do the same for member functions. i.e If I call table:key() a lookup must be performed in table.__group and if the function is present, then table.__group:key() should be called.
How do I accomplish this?
Do nothing. Your original code handles this.
Lua doesn't know what a "member function" is. A member is a member (i.e. an element in a table), and whether the value of that member is a function is irrelevant.
Remember:
obj:method(a,b,c) is exactly equivalent to obj.method(obj,a,b,c)
obj.method is exactly equivalent to obj["method"].
Your code already resolves obj["method"] into obj.__group["method"]
So you're done.
For instance, say we have:
group = {}
function group:showSum (a,b) print(a + b) end
function group:showProduct(a,b) print(a * b) end
Using your first code, we can write:
foo = setmetatable({__group = group}, PROGRESS)
foo:showSum(3,3) -- 6
foo:showProduct(3,3) -- 9
That's it.
Now, as long as we're here, let's look at what your second function is doing:
local val = self.__group[key]
if type(val) == "function" then
self.__group:val()
return function() end
end
First you grab the function value from __group. At this point you're done. Simply return that value, and the caller is going to call that value (i.e. (...)). Instead, you call __group["val"] which is likely a totally different function from __group[key] (unless key=="val"), then you pass the caller a function which does nothing.

Flag invalid attribute in ActiveRecord

I am trying to implement functionality wherein an attribute, once set, cannot be changed on an ActiveRecord model. To this end, I have written the following methods:
def address
self[:address]
end
def address=(val)
if new_record?
self[:address] = val
else
errors.add(:address, "Cannot change address, once it is set")
return false # tried return nil here first, did not work
end
end
Am I doing something wrong here? I want the object to be invalid once I try to change an address, but I do not get any errors when I do obj.valid?
EDIT: The value is not changed once it is set, but I would like to get invalid object when I do the validation via obj.valid?
When you do obj.valid?, it clears all of your errors, and then runs each of the validations in turn. To have this produce an error on validation, you'll have to move that logic into a validation block.
Here's an example of one way to do that with an instance variable:
def address=(val)
if new_record?
self[:address] = val
else
#addr_change = true
return false # tried return nil here first, did not work
end
end
validate do |user|
errors.add(:address, "Cannot change address, once it is set") if #addr_change
end