How to mock an instance method of an already mocked object? - ruby

I need to mock the following:
Class User
def facebook
#returns an instance of a facebook gem
end
end
So in my User tests, to access the User's facebook info I need to call user.facebook.me.info to retrieve its info. If I want to mock this, I'm currently using the following:
#user = Factory(:user)
facebook = mock()
me = mock()
me.expects(:info).returns({"name" => "John Doe"})
facebook.expects(:me).returns(me)
#user.expects(:facebook).returns(facebook)
assert_equal "John Doe", #user.facebook.me.info["name"]
This works but seems a bit unwieldy, is there a better way to do this ?
[edit] I'm using mocha as mocking framework

You could try something like this :-
user = Factory(:user)
user.stubs(:facebook => stub(:me => stub(:info => {:name => "John Doe"})))
If you really want to check that all these methods are called (which I suspect you don't), you could do the following :-
user = Factory(:user)
user.expects(:facebook => mock(:me => mock(:info => {:name => "John Doe"})))
It's a bit more verbose, but it's usually worthwhile giving each mock object a name :-
user = Factory(:user)
user.stubs(:facebook => stub('facebook', :me => stub('me', :info => {:name => "John Doe"})))
I hope that helps.

If you don't want to check that all the methods are called, you can also use different alternatives to mocking. For instance, you can use an OpenStruct.
#user = Factory(:user)
facebook = mock()
me = mock()
me.expects(:info).returns({"name" => "John Doe"})
facebook.expects(:me).returns(me)
#user.expects(:facebook).returns(facebook)
assert_equal "John Doe", #user.facebook.me.info["name"]
becomes
#user = Factory(:user)
#facebook = OpenStruct.new(:me => OpenStruct.new(:info => {:name => "John Doe"}))
#user.expects(:facebook).returns(#facebook)
This solution also offers you the advantage that you can change the #facebook properties in your tests, if you need to test different conditions.

Related

Mandrill template merge fail

I'm using mandrill to send emails from my API (in ruby with the mandrill-api gem), like signup confirmation email.
I have a merge tag in the template to put the username :
Hello *|USERNAME|*,
Thank you very much for installing the app! ...
...
My ruby code looks like that :
m = Mandrill::API.new ENV['MANDRILL_KEY']
template_name = "app-registration-welcome-email"
template_content = [{}]
message = {
:from_name=> "From Name",
:to=>[
{
:email => user.email,
:name => user.name,
}
],
:global_merge_vars => [{
:name => "username",
:content => user.name
}],
:merge_language => "mailchimp",
:merge => true,
:merge_vars => [{
:rcpt => user.email,
:vars => [{
:name => "username",
:content => user.name
}],
}],
:track_opens => true,
}
m.messages.send_template template_name, template_content, message
Unfortunately, when i receive the email, all is fine (to, name, from, ...) but the merge tag isn't replaced in the body and i still have *|USERNAME|* displayed.
What am I missing here?
You should only need to set USERNAME once, either in :global_merge_vars or :merge_vars.
If you're sending to just one recipient, it doesn't matter which you do.
If there are multiple recipients, use :merge_vars.
Try building message and then puts message.to_json and drop it into the debugger at https://mandrillapp.com/api/docs/messages.JSON.html#method=send-template (Click the 'Try it' button.) See if that gives you any clues.
You might also try using "USERNAME" instead of "username" as your variable name. The docs say merge vars are case-insensitive, but it's worth removing one more possible mismatch.

Update activerecord relation given a hash of multiple entries

I'm quite new to Rails, so be gentle :)
I have the following models set-up:
class User
has_many :it_certificates, :class_name => 'UserCertificate'
class UserCertificate
belongs_to :skill
Given the following input (in JSON)
{
"certificates":[
{ // update
"id":1,
"name":"Agile Web Dev 2",
"entity":"Agile Masters!",
"non_it":false,
"date_items":{
"month":10,
"year":2012
},
"skill": {
"id":57
}
},
{ // create
"name":"Agile Web Dev 1",
"entity":"Agile Masters!",
"non_it":false,
"date_items":{
"month":10,
"year":2011
},
"skill": {
"id":58
}
}
]
}
How's the easiest way to update the information for the relation it_certificates?
I've been looking to update_all but it doesn't match my needs (it only updates given fields with the same value).
So I've been struggling around with the approach of iterating over each of these records and then update them one-by-one.
I mean struggling because it looks to me there are lots of things I have to care of when the idea of Rails is the opposite.
Thanks in advance!
So, here's my solution for now:
def self.update_from_hash(data, user_id)
self.transaction do
data.each do |certificate|
if certificate[:id] == nil
# create
if !self.create(
:name => certificate[:name],
:entity => certificate[:entity],
:user_id => user_id,
:non_it => certificate[:non_it],
:skill_id => certificate[:skill][:id],
:date => self.build_date_from_items(certificate[:date_items][:month], certificate[:date_items][:year])
)
raise ActiveRecord::Rollback
end
else
# update
if !self.update(certificate[:id], {
:name => certificate[:name],
:entity => certificate[:entity],
:non_it => certificate[:non_it],
:skill_id => certificate[:skill][:id],
:date => self.build_date_from_items(certificate[:date_items][:month], certificate[:date_items][:year])
})
raise ActiveRecord::Rollback
end
end
end
end
return true
end
It works, but I'm still expecting a more elegant solution :)

How to stop DataMapper from double query when limiting columns/fields?

I'm not sure if I'm at fault here or if my approach is wrong with this.
I want to fetch a user (limiting columns/fields only to name, email, id):
#user = User.first(:api_key => request.env["HTTP_API_KEY"], :fields => [:id, :name, :email])
The output in the command line is correct as follows:
SELECT "id", "name", "email" FROM "users" WHERE "api_key" = '90e20c4838ba3e1772ace705c2f51d4146656cc5' ORDER BY "id" LIMIT 1
Directly after the above query, I have this code:
render_json({
:success => true,
:code => 200,
:user => #user
})
render_json() looks like this, nothing special:
def render_json(p)
status p[:code] if p.has_key?(:code)
p.to_json
end
The problem at this point is that the #user variable contains the full user object (all other fields included) and DataMapper has made an additional query to the database to fetch the fields not included in the :fields constraint, from the logs:
SELECT "id", "password", "api_key", "premium", "timezone", "verified", "notify_me", "company", "updated_at" FROM "users" WHERE "id" = 1 ORDER BY "id"
My question is this: how do I stop DM from performing the additional query? I know it has to do with it's lazy loading architecture and that returning the #user variable in JSON assumes that I want the whole user object. I particularly don't want the password field to be visible in any output representation of the user object.
The same behaviour can be seen when using DM's own serialisation module.
I think you should use an intermediate object for json rendering.
First, query the user from database :
db_user = User.first(:api_key => request.env["HTTP_API_KEY"], :fields => [:id, :name, :email])
Then, create a "json object" to manipulate this user :
#user = { id: db_user.id, name: db_user.name, email: db_user.email }

Passing post data to other pages in Sinatra

This seems to work fine:
views/index.haml:
%form{:method => 'POST' :action => '/'}
%label{:for => 'name'} Name:
%input{:type => 'text, :value => #values[:name] || ""}
%input{:type => 'submit'}
app.rb:
post '/' do
#values = params
haml :review
end
views/review.rb
Hello #{params[:name]}!
However, when I try to send my post-data to the same view on a different URL I get an error, or in other words:
app.rb:
post '/' do
#values = params
redirect '/review'
end
get '/review' do
#values = params
haml :review
end
The data is not going through, but no error is raised.
How do I send the post-data across pages like this? Ideally, I do not want to create a database.
You can store the parameters in a session or specify the query string explicitly. Browser Redirect from Sinatra Documentation
As specified in the documentation, you may use sessions or convert the POST params to a query string and use it in the redirect method. A crude example would be:
Say the POST params hash inside the '/' block is:
{
:name => "Whatever",
:address => "Wherever"
}
This hash can be made into a string like so:
query = params.map{|key, value| "#{key}=#{value}"}.join("&")
# The "query" string now is: "name=Whatever&address=Wherever"
Now use this inside the post '/' do
redirect to("/review?#{query}")

How to add a Person using the Highrise Ruby gem?

I am trying to use https://github.com/tapajos/highrise/ to update user accounts when people sign up to an app. However I am not getting very far.
In console I am doing:
person = Highrise::Person.create(:name => "charlie")
Which saves fine, but if I do something like
person = Highrise::Person.create(:name => "charlie", :email => "charlie#222.com")
then I get:
Unprocessable Entity
I can not get my head around this, I essentially want to add a full record:
person = Highrise::Person.create(:name => "charlie", :params => {:contact_data => {:email_addresses => "charlie#222.com"}})
but still i get the same error and can not find any examples online
You were on the right track with that last attempt. Give this a try:
person = Highrise::Person.create(
:first_name => "Charlie", :last_name => "Bravo",
:contact_data => {
:email_addresses => [{
:email_address => {:address => "charlie#222.com"}
}]
}
)
This matches the structure of the create a person request, as defined in the Highrise API. https://github.com/37signals/highrise-api/blob/master/sections/people.md#create-person
Also you can refer to ruby api's test spec for more examples https://github.com/tapajos/highrise/blob/f44cb3212c6d49549330c46454fe440ac117fa1b/spec/highrise/person_spec.rb#L40

Resources