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

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 }

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.

Batch insert multiple records with Mongoid?

I am reading through this Stackoverflow answer about how to insert multiple documents in Mongoid in one query. From the answer I read:
batch = [{:name => "mongodb"}, {:name => "mongoid"}]
Article.collection.insert(batch)
I need an example to understand how this works. Say we have the Article class:
class Article
include Mongoid::Document
include Mongoid::Timestamps
field :subject, type: String
field :body, type: String
field :remote_id, type: String
validates_uniqueness_of :remote_id
belongs_to :news_paper, :inverse_of => :articles
end
And the I e.g. create an array of articles:
[ {subject: "Mongoid rocks", body: "It really does", remote_id: "1234", news_paper_id: "abc"},
{subject: "Ruby rocks", body: "It really does", remote_id: "1234", news_paper_id: "abc"},
{subject: "Rails rocks", body: "It really does", remote_id: "5678", news_paper_id: "abc"} ]
How do I create them, and at the same time make sure the validation catches that I have 2 remote_id's that are the same?
If you add a unique indexing for remote_id field, MongoDB will take care the uniqueness of this field
index({ remote_id: 1 }, { unique: true })
Don't forget to run create_indexes: rake db:mongoid:create_indexes
After that, you are free to use Article.collection.insert(batch).

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 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

On MongoMapper, what is the difference between a Document and an EmbeddedDocument?

This example makes it seem like both are used (included) in order to make a class a persistent model, but it is not clear when I should use one or the other.
A MongoMapper::Document is saved to the database as a top-level record. A MongoMapper::EmbeddedDocument is saved within another document. For instance, let's say I have a blogging application. I have a Post and Comment model. They might look something like this:
require 'mongo_mapper'
MongoMapper.database = 'test'
class Post
include MongoMapper::Document
key :title
key :body
key :comments
many :comments
end
class Comment
include MongoMapper::EmbeddedDocument
key :author
key :body
end
p = Post.new(:title => 'Some post', :body => 'About something')
p.comments << Comment.new(:author => 'Emily', :body => 'A comment')
p.save
puts Post.all.map(&:inspect)
Will produce a document in your mongo database that looks like this:
{ "_id" : ObjectId("4c4dcf4b712337464e000001"),
"title" : "Some post",
"body" : "About something",
"comments" : [
{
"body" : "A comment",
"author" : "Emily",
"_id" : ObjectId("4c4dcf4b712337464e000002")
}
] }
In terms of interacting with them through MongoMapper, only a MongoMapper::Document response to methods like find and save. A MongoMapper::EmbeddedDocument can only be accessed through its parent document. The implication of this is that you should only use MongoMapper::EmbeddedDocument for models that are clearly subsidiary to their parent models and will only be used in the context of that parent.

Resources