Rails ActiveRecord : scope incoherence - ruby-on-rails

In my model I have :
#models/friend.rb
scope :approved_friend, where(:approved => true)
And the Rails console outputs :
User.find(2).friends
=> [#<Friend id: 18, user_id: 2, approved: true, created_at: "2013-04-23 09:18:59", updated_at: "2013-04-23 09:18:59", friend_id: 1>]
User.find(2).friends.approved_friend
=> []
Notice that approved is true in the output ...
Where it gets crazy is here :
User.find(1).friends.approved_friend
=> [#<Friend id: 19, user_id: 1, approved: true, created_at: "2013-04-23 09:19:36", updated_at: "2013-04-23 09:19:36", friend_id: 2>]
Am-I missing something ?
EDIT :
On one hand you have this query :
SELECT "friends".* FROM "friends" WHERE "friends"."user_id" = 2
=> [#<Friend id: 18, user_id: 2, approved: true, created_at: "2013-04-23 09:18:59", updated_at: "2013-04-23 09:18:59", friend_id: 1>]
On the other hand, you've got this (query through scope) :
SELECT "friends".* FROM "friends" WHERE "friends"."user_id" = 2 AND "friends"."approved" = 't'
=> []

Since the :status field is in the Friend model, you might have to change the scope to this
scope :approved_friend, where('friends.approved' => true).includes(:friend)

Related

How to save in database only "full" unique objects?

guys.
I have a model Order with attrbutes :name, :phone, :product_id
How to save in database only unique objects of Order - with unique combination of :name, :phone and :product?
In example:
already in db:
Order name: 'Bob', phone: '123', product_id: '4'
must not been saved:
Order name: 'Bob', phone: '123', product_id: '4'
must be saved:
Order name: 'Bob', phone: '123', product_id: '5'
must be saved:
Order name: 'Bob', phone: '1234', product_id: '4'
Try to set a unique validation with scope. Docs are here
class Order < ActiveRecord::Base
validates :name, uniqueness: { scope: [:phone, :product_id], message: "Not UNIQ" }
end
Here is a result:
[29] pry(main)> Order.all
Order Load (0.3ms) SELECT "orders".* FROM "orders"
=> [#<Order:0x007fd07cc6fd00 id: 1, name: "Bob", phone: "1234", product_id: 4, created_at: Thu, 11 Aug 2016 02:56:22 UTC +00:00, updated_at: Thu, 11 Aug 2016 02:56:22 UTC +00:00>]
[30] pry(main)> o1 = Order.new(name:"Bob", phone:"1234", product_id: 4)
=> #<Order:0x007fd07d5f3390 id: nil, name: "Bob", phone: "1234", product_id: 4, created_at: nil, updated_at: nil>
[31] pry(main)> o1.valid?
Order Exists (0.4ms) SELECT 1 AS one FROM "orders" WHERE ("orders"."name" = 'Bob' AND "orders"."phone" = '1234' AND "orders"."product_id" = 4) LIMIT 1
=> false
[32] pry(main)> o1.errors
=> #<ActiveModel::Errors:0x007fd07d65b580 #base=#<Order:0x007fd07d5f3390 id: nil, name: "Bob", phone: "1234", product_id: 4, created_at: nil, updated_at: nil>, #messages={:name=>["Not UNIQ"]}>
[33] pry(main)> o2 = Order.new(name:"Bob", phone:"12345", product_id: 4)
=> #<Order:0x007fd07cc7f3e0 id: nil, name: "Bob", phone: "12345", product_id: 4, created_at: nil, updated_at: nil>
[34] pry(main)> o2.valid?
Order Exists (0.4ms) SELECT 1 AS one FROM "orders" WHERE ("orders"."name" = 'Bob' AND "orders"."phone" = '12345' AND "orders"."product_id" = 4) LIMIT 1
=> true

Associations Relationships in Console

Ok, trying to learn rails commands on associations relationships in console
So if I have User that has_many posts, and Posts that belongs_to user ...
That means all this should work, correct? And these are all the the methods added automagically for this particular relationship, correct?
1. user.posts
2. user.posts=(posts)
3. user.posts << post
4. user.posts.delete(post)
5. user.posts.empty?
6. user.posts.size
7. user.post_ids
8. user.posts.clear
9. user.posts.find
10. user.posts.build(attributes={})
11. user.posts.create(attributes={})
user is: user = User.create(:name => "Michael")
result is: #<User id: 3, name: "Michael", created_at: "2016-06-21 16:52:22", updated_at: "2016-06-21 16:52:22">
&
post is: post = Post.create(:body => "body of txt")
result is: #<Post id: 3, user_id: 3, body: "body of txt", created_at: "2016-06-21 16:53:51", updated_at: "2016-06-21 16:58:38">
1. For user.posts, I get this:
#<ActiveRecord::Associations::CollectionProxy [#<Post id: 3, user_id: 3, body: "body of txt", created_at: "2016-06-21 16:53:51", updated_at: "2016-06-21 16:58:38">, #<Post id: nil, user_id: 3, body: "txt of body2", created_at: nil, updated_at: nil>]>
2. For user.posts=(posts), I get this:
NameError: undefined local variable or method posts' for main:Object and a whole bunch more of rbenv stuff
3. For user.posts << post, I get this:
#<ActiveRecord::Associations::CollectionProxy [#<Post id: 3, user_id: 3, body: "body of txt", created_at: "2016-06-21 16:53:51", updated_at: "2016-06-21 16:58:38">, #<Post id: nil, user_id: 3, body: "txt of body2", created_at: nil, updated_at: nil>, #<Post id: 3, user_id: 3, body: "body of txt", created_at: "2016-06-21 16:53:51", updated_at: "2016-06-21 16:58:38">]>
5. For user.posts.empty?, I get this:
false
6. For user.posts.size, I get this:
3
7. For user.post_ids, I get this:
[3, nil, 3]
9. For user.posts.find, I get this:
ActiveRecord::RecordNotFound: Couldn't find Post without an ID
10. For user.posts.build(attributes={:body => "random body of txt"}), I get this:
#<Post id: nil, user_id: 3, body: "random body of txt", created_at: nil, updated_at: nil>
Interesting thing about the one above is that when I do Post.all, that doesn't show up! But if I do user.posts, it does show up in that ...
11. For user.posts.create(attributes={}), I get this:
=> #<Post id: 4, user_id: 3, body: "body of text via create method", created_at: "2016-06-21 17:49:50", updated_at: "2016-06-21 17:49:50">
4. For user.posts.delete(post), I get this:
(0.3ms) begin transaction
SQL (0.6ms) DELETE FROM "posts" WHERE "posts"."id" = ? [["id", 3]]
(1.8ms) commit transaction
=> [#<Post id: 3, user_id: 3, body: "body of txt", created_at: "2016-06-21 16:53:51", updated_at: "2016-06-21 16:58:38">]
8. For user.posts.clear, I get this:
DELETE FROM "posts" WHERE "posts"."user_id" = ? [["user_id", 3]]
Which means now, when I run this: user.posts, I get this:
#<ActiveRecord::Associations::CollectionProxy []>
So, as you can see from above #2 doesn't work ... as well #9. Why not?
Thanks in advance for your help!
EDIT in response to answer (easier for clarification)
ok, I did posts = user.posts which resulted:
#<ActiveRecord::Associations::CollectionProxy [#<Post id: 5, user_id: 3, body: "body of txt", created_at: "2016-06-21 18:00:57", updated_at: "2016-06-21 18:01:11">, #<Post id: 6, user_id: 3, body: "body of txt2", created_at: "2016-06-21 18:22:02", updated_at: "2016-06-21 18:22:36">]>
then I did user.posts=(posts) which resulted the same answer as above.
But when I ran posts = Post.where(user: user), I got this:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: posts.user: SELECT "posts".* FROM "posts" WHERE "posts"."user" = 3
Also, when I ran user.posts = posts, I got this:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: posts.user: SELECT "posts".* FROM "posts" WHERE "posts"."user" = 3
In regards to user.posts.find(Post.last.id), it worked if I did this:
user.posts.find(Post.last) but if I did something like this:
user.posts.find(Post.last.6), I got this:
SyntaxError: (irb):121: no .<digit> floating literal anymore; put 0 before dotuser.posts.find(Post.last.6)
For #2
You need to define what "posts" is.
If you are trying to "set" posts
posts = user.posts
If you want to do a comparison on the two you would do:
posts = Post.where(user: user)
user.posts == posts #should be true
For #9
You need to pass something into your find method:
user.posts.find(Post.last) #this returns an object if "user" created Post.last
Hope this helps!
Edit:
You can't do user.posts=(posts) because you set data with an array, hash, string, integer, etc.
An equals comparison operator should have 2 ='s. That was my mistake and I have corrected the post.

Why is my after_save callback stopping my ActiveRecord association from saving properly?

When I comment out my after_save call back, my ActiveRecord associations work just fine. In Rails Console, you'd see:
> #report = Report.create :name => "foo"
=> #<Report id: 9, name: "foo", created_at: "2013-03-05 09:51:55", updated_at: "2013-03-05 09:51:55">
> #question = #report.questions.create :description => "bar"
=> #<Question id: 18, standard_id: nil, description: "bar", element_id: nil, condition_id: nil, blueprint_name: nil, blueprint_url: nil, created_at: "2013-03-05 09:52:32", updated_at: "2013-03-05 09:52:32", additive: false, instructions: nil>
> #report.questions
=> [#<Question id: 18, standard_id: nil, description: "bar", element_id: nil, condition_id: nil, blueprint_name: nil, blueprint_url: nil, created_at: "2013-03-05 09:52:32", updated_at: "2013-03-05 09:52:32", additive: false, instructions: nil>]
> #question.reports
=> [#<Report id: 9, name: "foo", created_at: "2013-03-05 09:51:55", updated_at: "2013-03-05 09:51:55">]
However, the associations stop working when I add the following after_save callback to question.rb:
def create_matching_surveys
self.reports.each do |report|
report.reviews.each do |review|
review.competitors.each do |competitor|
competitor.surveys.find_or_create_by_question_id(self.id)
end
end
end
end
Then, in Rails Console, you get:
> #report = Report.create :name => "foo"
=> #<Report id: 13, name: "foo", created_at: "2013-03-05 10:20:51", updated_at: "2013-03-05 10:20:51">
> #question = #report.questions.create :description => "bar"
=> #<Question id: 24, standard_id: nil, description: "bar", element_id: nil, condition_id: nil, blueprint_name: nil, blueprint_url: nil, created_at: "2013-03-05 10:21:02", updated_at: "2013-03-05 10:21:02", additive: false, instructions: nil>
> #report.questions
=> [#<Question id: 24, standard_id: nil, description: "bar", element_id: nil, condition_id: nil, blueprint_name: nil, blueprint_url: nil, created_at: "2013-03-05 10:21:02", updated_at: "2013-03-05 10:21:02", additive: false, instructions: nil>]
> #question.reports
=> []
This happens whether or not the report has reviews that have competitors.
The strange thing is I thought the callback was meant to happen after the question was saved? So by rights the association should save too before any of this happens, right?
How do I fix it?
UPDATE
I think I have to call the callback in the right spot in the object's life cycle, but I can't find that spot. Here's why I think this:
> #report = Report.create :name => "foo"
=> #<Report id: 20, name: "foo", created_at: "2013-03-05 12:29:35", updated_at: "2013-03-05 12:29:35">
> #question = #report.questions.create :description => "bar"
=> #<Question id: 31, standard_id: nil, description: "bar", element_id: nil, condition_id: nil, blueprint_name: nil, blueprint_url: nil, created_at: "2013-03-05 12:30:14", updated_at: "2013-03-05 12:30:14", additive: false, instructions: nil>
> #question.reports
=> []
> #question.update_attributes :description => "foo"
=> true
> #question.reports
=> [#<Report id: 20, name: "foo", created_at: "2013-03-05 12:29:35", updated_at: "2013-03-05 12:29:35">]
BTW, the method is now in question_observer.rb:
class QuestionObserver < ActiveRecord::Observer
def after_save(model)
model.reload
model.reports.reload
model.reports.each do |report|
report.reviews.each do |review|
review.competitors.each do |competitor|
competitor.surveys.find_or_create_by_question_id(model.id)
end
end
end
return true
end
end
The answer was to use a neat new callback hook called after_commit which was introduced with Rails 3.
See http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#method-i-after_commit.
The only issue is after_commit doesn't work "out of the box" with transactional fixtures, but there are plenty of solutions out there, and I found this one worked well for me: https://supportbee.com/devblog/2012/01/14/testing-after_commitafter_transaction-with-rspec/

Rails how to query for a record based on two fields

I have a Friend model:
user_id, friend_id, status (approved, pending, ignored)
I want to query if user1 & user 2 have a record.
user1.id = 10
user2.id = 9
The Friend Model could have either
10,9,x
9,10,x
How can I write a rails query that will see if a record exists, for either combination? Thanks
How about:
Friend.where(:user_id => [user.id, self.id], :friend_id => [user.id, self.id], :status => 'x')
Converted to SQL would be:
"SELECT \"friends\".* FROM \"friends\" WHERE \"friends\".\"user_id\" IN (10, 9) AND \"friends\".\"friend_id\" IN (10, 9) AND \"friends\".\"status\" = 'x'"
It would return both cases with the one query:
[#<Friend id: 1, user_id: 10, friend_id: 9, status: 'x', created_at: "2011-06-25 15:09:00", updated_at: "2011-11-01 18:28:50">, #<Friend id: 2, user_id: 9, friend_id: 10, status: 'x', created_at: "2011-06-25 15:11:06", updated_at: "2011-11-01 18:28:50">]

.first returning wrong object type

If you look at the four method calls below, Service.first returns a Service object, Salon.first returns a Salon object, etc. But TransactionItem.first returns a Service object. Why could this be?
ruby-1.8.7-p334 :001 > Service.first
=> #<Service id: 147, name: "Fub", salon_id: 2, created_at: "2011-08-10 18:00:07", updated_at: "2011-08-10 18:00:12", price: nil, active: true, archived: true>
ruby-1.8.7-p334 :002 > Salon.first
=> #<Salon id: 1, name: "The Cheeky Strut", created_at: nil, updated_at: nil, address_id: nil, email: nil>
ruby-1.8.7-p334 :003 > Product.first
=> #<Product id: 1, name: "Herbal Essences Shampoo", retail_price: #<BigDecimal:10305f1f0,'0.1E2',9(18)>, wholesale_price: nil, sku: "", salon_id: 2, created_at: "2011-07-08 01:35:48", updated_at: "2011-07-08 01:35:48", archived: false>
ruby-1.8.7-p334 :004 > TransactionItem.first
=> #<Service id: 63, created_at: "2011-08-30 20:05:57", updated_at: "2011-08-30 20:05:57", price: #<BigDecimal:10303eba8,'0.18E2',9(18)>>
ruby-1.8.7-p334 :005 >
This is what my app/models/transaction_item.rb looks like:
class TransactionItem < ActiveRecord::Base
belongs_to :transaction
belongs_to :stylist
end
I blew away the TransactionItem table via a migration, then created a brand new migration to re-create it. That seems to have fixed the problem.

Resources