Understanding assert_difference in Ruby on Rails - ruby-on-rails

Could anyone please explain what this test code does? :
assert_difference('Post.count') do
post :create, :post => { :title => 'Hi', :body => 'This is my first post.'}
end
and:
assert_difference 'ActionMailer::Base.deliveries.size', +1 do
post :invite_friend, :email => 'friend#example.com'
end
I can't understand it even though I read the documentation.
Thanks!

assert_difference verifies that the result of evaluating its first argument (a String which can be passed to eval) changes by a certain amount after calling the block it was passed. The first example above could be "unrolled" to:
before = Post.count # technically, eval("Post.count")
post :create, :post => { :title => 'Hi', :body => 'This is my first post.'}
after = Post.count
assert_equal after, before + 1

This is just checking to make sure that the number of objects for whatever type was specified has increased by 1. (It is an easy way to check to see that an object was added to the DB)

This assertion is to verify the certain/specified difference in the first argument. 1st argument should be a string i.e "Post.count". Second argument has a default value 1, you can specify other numbers also, even negetive numbers. for more details visit:
http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html

This method is to verify that there is a numerical difference between first argument and the second argument. In Most cases first argument is a string which is like “Post.count” and second argument is a block. In rails this is mostly used in functional testing to check if an object can be saved in the database. Logic is that before a new is object being saved, number of records in that particular table must be different from the number of records after the object is saved ( from 1 to be precise).

Related

The thing with Ruby on Rails and Colons

I used to be more of a hobby Java guy and try to switch to Ruby on Rails right now.
But I'm having some difficulties, believe it or not, I like braces and semicolons..gives some orientation.
But here's the question:
Right now I'm taking an online course in RoR and it occured to me, that I'm always wrong on how to work with symbols, hashes etc.
Example 1:
Let's take a look at this single line of code for example:
form_for(:session, :html => {class: "form-horizontal", role: "form"}, url: login_path)
And this is how I read it:
Method / Function name is
form_for
Parameters parsed to this method are:
:session, :html => {class: "form-horizontal", role: "form"}, url: login_path
Let's break up those into
:session
:html => {class: "form-horizontal", role: "form"}
url: login_path
How the heck should I know how to declare those parameters?
Why are :session and :html passend in as keys and url not?
Is the :html symbol a Hashmap-symbol?
Example:
In an model File you declare an n:m relationship like this (for example users <-> stocks)
has_many :users, through: :user_stocks
Ok, I get that the first argument is :users and the second is the same as
:through => :user_stocks
correct?
But in the same way, let's look at an routes.rb config from the same project:
resources :user_stocks, except: [:show, :edit, :update]
Now we're using an array of keys on the except hash, correct?
It does get clearer when writing an question but still, is there a rule of thumb / convention on when to use
:name
name: value
:name => {values}?
name: [values]
Or is it just an personal preference? In that case I should hope that my online teacher stays consistent..
Generally speaking, I'm very confused on how the parameter syntax convention is and when to use what (what type of argument).
Is it just because I am starting with Ruby or did I miss some piece of convention.
I hope my problem is kind of understandable and excuse my english - non native speaker.
I really like to get along with RoR but right now watching the online course sometimes leaves me more confused than before because if I would've done it by myself, I would've used a completely different way.
How the heck should I know how to declare those parameters?
You look up the method in the docs and you read about it.
Parameters parsed to this method are:
:session,
:html => {class: "form-horizontal", role: "form"},
url: login_path
How the heck should I know how to declare those parameters? Why are
:session and :html passend in as keys and url not? Is the :html symbol
a Hashmap-symbol?
In ruby, if you pass in a series of key-value pairs at the end of the argument list, ruby gathers them all into a hash and passes them as one argument to the method. Here is an example:
def go(x, y)
p x
p y
end
go(:hello, a: 10, b: 20)
--output:--
:hello
{:a=>10, :b=>20}
Another example:
def go(x, y)
p x
p y
end
go(
:session,
:html => {class: "form-horizontal", role: "form"},
url: 'xyz'
)
--output:--
:session
{:html=>{:class=>"form-horizontal", :role=>"form"}, :url=>"xyz"}
has_many :users, through: :user_stocks
Ok, I get that the first argument is :users and the second is the same
as
:through => :user_stocks
correct?
Correct. In old ruby, key-value pairs in hashes were written like this:
'a' => 'hello'
If the value was a symbol, then it looked like this:
'a' => :hello
If the key was also a symbol, then you wrote:
:a => :hello
In modern ruby, if the key is a symbol you can write:
a: 'hello'
which is a shortcut for:
:a => 'hello'
and if the value is a symbol as well, in modern ruby it looks like this:
a: :hello
which is a shortcut for:
:a => :hello
resources :user_stocks, except: [:show, :edit, :update]
Now we're using an array of keys on the except hash, correct?
The hash isn't named except, but otherwise you are correct.
a rule of thumb / convention on when to use
:name # Single symbol argument
name: value # A key-value pair in a hash. The key is a symbol.
:name => {values}? #A key-value pair in a hash. The value looks like a hash, but the syntax is incorrect.
name: [values] #A key-value pair in a hash. The value is your notation for an array.
Or is it just an personal preference? In that case I should hope that my online teacher stays consistent..
Once again, a method can be defined to take any type of argument. Because ruby variables don't have types, you have to check the docs. If a method expects you to pass in a hash where the key :name has a value that is a hash, then you need to do that. On the other hand, if the method expects you to pass in a hash where the key :name has a value that is an array, then you need to do that.
Generally speaking, I'm very confused on how the parameter syntax
convention is and when to use what (what type of argument). Is it just
because I am starting with Ruby or did I miss some piece of
convention.
Ruby has a lot of shortcuts, which can be confusing to a beginner. Then there is the whole String v. Symbol concept. If you can understand the practical difference between a Symbol and a String, then you will be ahead of the game. A Symbol is like an integer. So when ruby has to compare whether Symbols are equal, ruby compares two integers, which is fast. If ruby has to compare Strings, then ruby has to compare the ascii code of each letter in one string to the ascii code of each letter in the other string until ruby finds a difference. For instance, in order for ruby to compare the following two Strings:
"helloX" v. "helloY"
ruby won't find a difference until after it has made six integer comparisons:
'h' v 'h' => equal
'e' v 'e' => equal
...
...
'X' v 'Y' => not equal
On the other hand, if ruby were comparing:
:helloX v. :helloY
the Symbols are essentially stored as single integers, something like:
341343 v. 134142 => not equal
To compare them takes only a single integer comparison, so it's faster. As someone is sure to point out, that isn't quite how Symbols are implemented, but the details don't matter. It's sufficient to know that Symbol comparisons are faster than String comparisons, and as to why that is true the example above is sufficient to demonstrate there is at least one implementation where it can be true.
Hashes: hashrockets vs literal
In Ruby hashes can use either "hashrockets" or the newer literal notation (since Ruby 1.9):
# hashrockets
{ :foo => "bar" }
# literal
{ foo: "bar" }
They both do the exact same thing. They create a hash with the symbol :foo as a key. The literal syntax is now generally preferered.
Hashrockets should only be used today if you have something other than symbols as keys (you can have numbers, strings or any object as keys):
{ 1 => 'a', 2 => 'b' }
# using literals won't work here since it will cast the keys to symbols:
{ 1: 'a', 2: 'b' } # => { :1 => 'a', :2 => 'b' }
As Ruby is loosly typed hashes can contain any kinds of values:
{ foo: "bar" }
{ foo: [:bar, :baz] }
{ foo: { bar: :baz } }
Hash options
In Ruby methods can recieve both ordinal arguments and a hash with options:
def foo(bar, hash = {})
#test= hash[:]
end
The hash options must come after the positional arguments in the arguments list.
# syntax error
foo(a: 2, "test")
# good
foo("test", a: 2)
When passing a hash options you can forgo the surrounding brackets as they are implied:
foo("test", { a: 1, b: 2 })
# same result but nicer to read
foo("test", a: 1, b: 2 )
Ruby 2.0 introduced keyword arguments which reduces the amount of boilerplate code needed to parse the options:
def foo(bar, test: nil)
#test= test
end
def foo(bar, test: nil, **kwargs)
#test= test
# all the options except test
puts kwargs.inspect
end
# will raise an error if the test key is not passed
def foo(bar, test:)
#test= test
end
Postitional arguments vs hash options
Postitional arguments are shorter and ideal where the order of the parameters is self explainatory:
class Number
def add(x)
#val += x
end
end
When you have more complex methods with a large number of arguments it can be tricky to keep track of the order:
def book_flight(destination, seats, airline_preference= nil, special_meals= nil)
end
thats where hash options come in and shine as you can have an arbirtrary number of options and the program does not blow up because you forgot a nil in the call.

What does :attribute => parameter actually do?

I have a hard time understanding the form :attribute => parameter
Can anyone give me some explanations for it? Is :attribute a field (variable) belonging to the class or something else? Why we can pass this pair as one parameter to methods?
If you're referring to something like this:
some_method(:foo => "bar", :baz => "abc")
then it's just shorthand which causes ruby to convert those things into a Hash. Please note that when using this form, that the hash must be the final argument to the method in order for this to work.
Based on the explanation above, this
some_method(:foo => "bar", :baz => "abc")
is ok, but this
some_method(:foo => "bar", :baz => "abc", moo)
is not.
Though you will see this commonly in Rails, it is not a Rails specific question. It is Ruby.
The answer to your question is that it is key/value pairs in a Hash, generally passed as an argument to a method.
You will see this as well when it is being assigned to a variable directly. But let me show you a sample method, and a sample usage, so that you can put them together:
def some_method(*args, name: 'Joe', amount: 42, **other_params )
puts "#{name}, #{amount}, glob of arguments = #{args.inspect}",
"other params #{other_params}"
end
some_method(:occupation => 'programmer', :phone => '123-456-7890', name: 'Jane')
This is Ruby 2.0.0 specific in the fact that you can provide for that last argument, which provides for unnamed parameters, in practice. Using the 1.9+ syntax for a Hash in the argument list, you are allowed to provide for other unnamed "parameters" which can appear after the hash argument.
Notice that if I had used the older syntax for Hash, namely the :key => 'value' syntax, I would not be allowed (at least as of this writing) to have the **other_params argument at the end of the argument list.
You could also provide the hash using the newer syntax in the calling code, though I left it as the Hash syntax when calling some_method.
The Hash still needs to be the last provided in the calling argument list, the same as indicated in the argument list for the method definition.

Why does Model.new in Rails 3 do an implicit conversion?

I'm facing a weird behavior in Rails 3 model instantiation.
So, I have a simple model :
class MyModel < ActiveRecord::Base
validates_format_of :val, :with => /^\d+$/, :message => 'Must be an integer value.'
end
Then a simple controller :
def create
#mod = MyModel.new(params[:my_model])
if #mod.save
...
end
end
first, params[:my_model].inspect returns :
{:val => 'coucou :)'}
But after calling #mod = MyModel.new(params[:my_model]) ...
Now, if I call #mod.val.inspect I will get :
0
Why am I not getting the original string ?
At the end the validates succeed because val is indeed an integer.
Is this because val is defined as an integer in the database ?
How do I avoid this behavior and let the validation do his job ?
If val is defined as an integer in your schema then calling #my_model.val will always return an integer because AR does typecasting. That's not new to rails 3, it's always worked that way. If you want the original string value assigned in the controller, try #my_model.val_before_type_cast. Note that validates_format_of performs its validation on this pre-typecast value, so you don't need to specify that there.
EDIT
Sorry I was wrong about the "performs its validation on this pre-typecast value" part. Looking at the code of the validation, it calls .to_s on the post-typecast value which in your case returns "0" and therefore passes validation.
I'd suggest not bothering with this validation to be honest. If 0 is not a valid value for this column then validate that directly, otherwise just rely on the typecasting. If the user enters 123 foo you'll end up with 123 in the database which is usually just fine.
There is also better fitting validator for your case:
http://guides.rubyonrails.org/active_record_validations_callbacks.html#validates_numericality_of

searching a model in rails for 2 values?

I wrote this retrieval statement to check if an appointment being saved or created dosent conflict with one thats already saved. but its not working, can someone please point me to where I'm going wrong?
#new_appointment = :appointment #which is the params of appointment being sent back from submit.
#appointments = Appointment.all(:conditions => { :date_of_appointment => #new_appointment.date_of_appointment, :trainer_id => #new_appointment.trainer_id}
)
the error is from the :date_of_appointment => #new_appointment.date_of_appointment this will always be false as:
thank you
At face value, there doesn't appear to be anything wrong with your syntax. My guess is that #new_appointment isn't containing the values you're expecting, and thus the database query is returning different values than you expect.
Try dumping out #new_appointment.inspect or check the logfiles to see what SQL the finder is producing, or use
Appointment.send(:construct_finder_sql, :conditions => {
:date_of_appointment => #new_appointment.date_of_appointment,
:trainer_id => #new_appointment.trainer_id
})
to see the SQL that will be generated (construct_finder_sql is a protected ActiveRecord::Base method).
Update based on your edit
#new_appointment = :appointment should be something like #new_appointment = Appointment.new(params[:appointment]). :appointment is just a symbol, it is not automatically related to your params unless you tell it to.

How can I add multiple should_receive expectations on an object using RSpec?

In my Rails controller, I'm creating multiple instances of the same model class. I want to add some RSpec expectations so I can test that it is creating the correct number with the correct parameters. So, here's what I have in my spec:
Bandmate.should_receive(:create).with(:band_id => #band.id, :user_id => #user.id, :position_id => 1, :is_leader => true)
Bandmate.should_receive(:create).with(:band_id => #band.id, :user_id => "2222", :position_id => 2)
Bandmate.should_receive(:create).with(:band_id => #band.id, :user_id => "3333", :position_id => 3)
Bandmate.should_receive(:create).with(:band_id => #band.id, :user_id => "4444", :position_id => 4)
This is causing problems because it seems that the Bandmate class can only have 1 "should_receive" expectation set on it. So, when I run the example, I get the following error:
Spec::Mocks::MockExpectationError in 'BandsController should create all the bandmates when created'
Mock 'Class' expected :create with ({:band_id=>1014, :user_id=>999, :position_id=>1, :is_leader=>true}) but received it with ({:band_id=>1014, :user_id=>"2222", :position_id=>"2"})
Those are the correct parameters for the second call to create, but RSpec is testing against the wrong parameters.
Does anyone know how I can set up my should_receive expectations to allow multiple different calls?
Multiple expectations are not a problem at all. What you're running into are ordering problems, given your specific args on unordered expectations. Check this page for details on ordering expectations.
The short story is that you should add .ordered to the end of each of your expectations.
Mock Receive Counts
my_mock.should_receive(:sym).once
my_mock.should_receive(:sym).twice
my_mock.should_receive(:sym).exactly(n).times
my_mock.should_receive(:sym).at_least(:once)
my_mock.should_receive(:sym).at_least(:twice)
my_mock.should_receive(:sym).at_least(n).times
my_mock.should_receive(:sym).at_most(:once)
my_mock.should_receive(:sym).at_most(:twice)
my_mock.should_receive(:sym).at_most(n).times
my_mock.should_receive(:sym).any_number_of_times
Works for rspec 2.5 too.

Resources