Rails Model.where how do I get ID? - ruby-on-rails

I have the following where statement:
<% location = Location.where('locname' == client.locname) %>
How do I get the .id of the location record that it found?
This didn't work:
<% location = Location.where('locname' == client.locname).id %>
Thanks for the help!

<% location = Location.where("locname = ?", client.locname).first.id %>
The reason is that where will return an ActiveRecord::Relation, thus you can either loop through the elements or just grab the first one as I did above.

You may also use the find method provided by ActiveRecord like:
<% location = Location.find(:first, :conditions => ["locname = ?", client.locname]).id %>
be also aware that you need to paramterize your query properly to eliminate all possibilities of SQL injection.

The reason why your first code sample you provided doesn't allow you to obtain the id, is it isn't an instance of the Location class. Using some code from my own project:
1.9.2p290 :001 > ch = Character.where(name: 'Catharz')
Character Load (2.9ms) SELECT "characters".* FROM "characters" WHERE "characters"."name" = 'Catharz'
=> [#<Character id: 2, name: "Catharz", player_id: 2, archetype_id: 4, created_at: "2012-03-29 07:10:31", updated_at: "2012-11-26 05:36:11", char_type: "m", instances_count: 348, raids_count: 148, armour_rate: 5.1, jewellery_rate: 5.29, weapon_rate: 5.48>]
1.9.2p290 :002 > ch.class
=> ActiveRecord::Relation
This is because returns an instance of the ActiveRecord:Relation class which mimics your class. You can see this by calling #klass on the returned value.
1.9.2p290 :002 > ch.klass
=> Character(id: integer, name: string, player_id: integer, archetype_id: integer, created_at: datetime, updated_at: datetime, char_type: string, instances_count: integer, raids_count: integer, armour_rate: float, jewellery_rate: float, weapon_rate: float)
But if you try and get an id, you'll get the following exception:
1.9.2p290 :004 > ch.id
NoMethodError: undefined method `id' for #<ActiveRecord::Relation:0xce58344>
The ActiveRecord::Relation class allows you to chain together scopes, without executing the SQL until you need it to be executed. This is why Luis' answer above will work. Calling #first on the ActiveRecord::Relation will force the query to be executed.
As a pointer on design, you should probably be assigning your location as #location in your controller then using the instance variable in your view.

Related

Select some fileds from object in mongoid

Hi I need to select only particular fields from object.
something like this using mongoid
User.select(:name, :email).all
please help me.
I don’t know why only is not working for me if i use as_json on it.
User.only(:name, :email).all.as_json
is throwing attributes missing error.
The only method works for me. Are you certain that mongo is returning a value for the other parameters? The attributes should still exist for those extra parameters, but they should be assigned to nil instead.
1.9.3-p194 :011 > Retailer.only(:description).all.first
=> #<Retailer _id: 1, description: "WALMART", aliases: nil>
1.9.3-p194 :012 > Retailer.all.first
=> #<Retailer _id: 1, description: "WALMART", aliases: ["WALMART.COM", "WAL-MART"]>

Calling the .name method does not return the object's name

I am trying to get the category object to return the name "Comedy", but is returning "Category" instead when I call the name method. Not sure where to look to solve this issue so I would appreciate if someone can point me in the right direction.
pry response from command link:
[17] pry(#<QueueItem>)> category
=> [#<Category id: 1, name: "Comedy", created_at: "2014-03-17 19:21:34", updated_at: "2014-03-17 19:21:34">]
[18] pry(#<QueueItem>)> category.name
=> "Category"
Thanks!
From the above code and output, it seems like, category variable is storing an array of category ActiveRecord Objects. So we need to use the object by the following code.
category.first.name
Just to clarify, if we try on console category.class we should get:
> category.class
# ActiveRecord::Relation::ActiveRecord_Relation_Category
That's why it didn't throw Error, undefined method name for Array, simply because it is not an Array, and Rails Active Record Relations do have a name method
Actually, i never used pry but default rails console would have dumped the class name before the data:
> category
#<ActiveRecord::Relation [#<Category id: 1, ....>, #<Category id:2 ...>, .. etc]
Which is really useful.

Why is the "where" query in rails returning a different object?

I'm testing chats between users in my app. I'm using RSpec and FactoryGirl
The test that's not passing:
it "creates a chat if one does not exist" do
bob = create(:user, username: "bob")
dan = create(:user, username: "dan")
new_chat = Chat.create(user_id: #dan.id, chatted_user_id: bob.id)
expect(Chat.where("chatted_user_id = ?", bob.id).first).to equal(new_chat)
end
The failure message says:
Failure/Error: expect(Chat.where("chatted_user_id = ?", bob.id).first).to equal(new_chat)
expected #<Chat:70120833243920> => #<Chat id: 2, user_id: 2, chatted_user_id: 3>
got #<Chat:70120833276240> => #<Chat id: 2, user_id: 2, chatted_user_id: 3>
Compared using equal?, which compares object identity,
but expected and actual are not the same object. Use
`expect(actual).to eq(expected)` if you don't care about
object identity in this example.
Why is my query returning a different object id?
equal checks object identity. The objects you are testing are two objects (instances) referencing the same record, but they are actually different objects from a Ruby virtual machine point of view.
You should use
expect(Chat.where("chatted_user_id = ?", bob.id).first).to eq(new_chat)
To better understand the problem, look at the following example
2.0.0-p353 :001 > "foo".object_id
=> 70117320944040
2.0.0-p353 :002 > "foo".object_id
=> 70117320962820
Here I'm creating two identical strings. They are identical, but not equal because they are actually two different objects.
2.0.0-p353 :008 > "foo" == "foo"
=> true
2.0.0-p353 :009 > "foo".equal? "foo"
=> false
That's the same issue affecting your test. equal checks if two objects are actually the same at the object_id level. But what you really want to know is if they are the same record.

Sort Ruby array using the Post objects it contains

I have a Ruby array which contains Post objects (a Mongoid model) with a created_at method returning a Time object. What I want is to sort that array using that method (which is present on every element on the array). I tried #posts.sort_by {|post| post.created_at} but that didn't work. How could I achieve this? Thanks!
EDIT: Example of the array:
[#<Post _id: 4ffd5184e47512e60b000003, _type: nil, created_at: 2012-07-11 10:12:20 UTC, title: "TestSecond", description: "TestSecond", slug: "assa", user_id: 4ffc4fd8e47512aa14000003>, #<Post _id: 4ffd518ee47512e60b000004, _type: nil, created_at: 2012-07-11 10:12:30 UTC, title: "TestFirst", description: "TestFirst", slug: "assadd", user_id: 4ffc4fd8e47512aa14000003>].
EDIT #2: I'm getting that #posts arra with:
#posts = Array.new
#user.following.each do |user|
user.posts.each do |p|
#posts << p
end
end
I'm not sure why you have an array here. Normally, with Mongoid querying, you should have a Mongoid::Criteria instance, which you can desc(:created_by) or asc(:created_by) on, in order to sort.
Still, can't think of any reason sort_by doesn't work for properly for you, works great on my app (just tried at console).
UPD:
#user.following.each do |user|
user.posts.each do |p|
#posts << p
end
end
Well, to still have a mongoid collection there, something like this might be done:
#posts = #user.following.map(&:posts).inject { |posts, post| posts.concat post }
Now, you can #posts.desc(:created_at).
UPD2:
Also, for purpose of having a cleaner code, you could define a posts_from_following on User:
class User
def posts_from_following
following.map(&:posts).inject { |posts, post| posts.concat post }
end
end
and then, just do
#posts = #user.posts_from_following.desc(:created_at)
in your controller.
#posts.sort_by &:created_at
#posts.sort {|a, b| a.created_at <=> b.created_at }

How to convert from a string to object attribute name?

I am trying to convert a string value into a name of an attribute that belongs to an object. For example, in the following code, I need all the string values in the column_array turned into attribute names. The names "student_identification", "email", etc. are actual column names of my Student table. In the real scenario, column_array will be set by the user (by ticking check boxes). And new_array will be replaced by csv, as I want the data go into a csv file.
At the moment I am really struggling at the following line:
new_array << r."#{column_array[i]}"
I want "#{column_array[i]}" to be turned into the attribute name so I can access the data.
def exp_tst
#records = Student.find(:all, :conditions=> session[:selection_scope],
:order => sort_order('laboratory_id'))
column_array = ["student_identification", "laboratory_id", "email", "current_status"]
new_array = Array.new()
#records.each do |r|
(0..(column_array.size-1)).each do |i|
new_array << r."#{column_array[i]}"
end
end
end
Let's say column_array[i] = "foo", for an example.
If you want to call the method r.foo, use Object#send:
r.send(column_array[i], arg1, arg2, arg3, ...)
If you want to access r's instance variable #foo, use Object#instance_variable_get and Object#instance_variable_set:
r.instance_variable_get('#'+column_array[i])
r.instance_variable_set('#'+column_array[i], new_value)
In this case we have to prepend the given name with an # sigil, since that is required at the start of all instance variable names.
Since this is rails, and there's a whole lot of ActiveRecord magic going on with your models (and I'm guessing Student is a subclass of ActiveRecord::Base) you probably want to use the former, since ActiveRecord creates methods to access the database, and the values stored in instance variables may not be what you want or expect.
I'll use an example from some test data I've got lying around:
% script/console
Loading development environment (Rails 2.3.2)
irb> Customer
#=> Customer(id: integer, date_subscribed: datetime, rental_plan_id: integer, name: string, address: string, phone_number: string, credit_limit: decimal, last_bill_end_date: datetime, balance: decimal)
irb> example_customer = Customer.find(:all)[0]
#=> #<Customer id: 6, date_subscribed: "2007-12-24 05:00:00", rental_plan_id: 3, name: "Evagation Governessy", address: "803 Asbestous St, Uneradicated Stannous MP 37441", phone_number: "(433) 462-3416", credit_limit: #<BigDecimal:191edc0,'0.732E3',4(12)>, last_bill_end_date: "2009-05-15 04:00:00", balance: #<BigDecimal:191e870,'0.743E3',4(12)>>
irb> example_customer.name
#=> "Evagation Governessy"
irb> field = 'name'
#=> "name"
irb> example_customer.instance_variable_get(field)
NameError: `name` is not allowed as an instance variable name
from (irb):8:in `instance_variable_get`
from (irb):8
irb> example_customer.instance_variable_get('#'+field)
#=> nil
irb> example_customer.send(field)
#=> "Evagation Governessy"
irb> example_customer.send(field+'=', "Evagation Governessy Jr.")
#=> "Evagation Governessy Jr."
irb> example_customer.send(field)
#=> "Evagation Governessy Jr."
irb> example_customer.name
#=> "Evagation Governessy Jr."
So you can see how #send(field) accesses the record information, and trying to access the attributes doesn't.
Also, we can use #send(field+'=') to change record information.
Look at instance_eval method ...
if you have 'attribute' and need do
object.attribute = 'ololo'
you can do:
object.instance_eval('attribute') = 'ololo'

Resources