rails has_many through on a new element - ruby-on-rails

I have the following model:
class Party < ActiveRecord::Base
has_many :party_characters
has_many :characters, :through => :party_characters
...
And on my controller I have the following code:
def new
#party = Party.new
p #party.characters.any?
p #party.characters
p #party.characters.any?
...
That writes the following to the console:
true
[]
false
Why does the any? method returns true before the print and false after?

Tried the same thing on the console and got the following results
1.9.3p0 :003 > p = Party.new
=> #<Party id: nil, name: nil, created_at: nil, updated_at: nil>
1.9.3p0 :004 > p.characters.any?
(17.9ms) SELECT COUNT(*) FROM "characters" INNER JOIN "party_characters" ON "characters"."id" = "party_characters"."character_id" WHERE "party_characters"."party_id" IS NULL
=> true
1.9.3p0 :005 > p.characters
=> []
1.9.3p0 :006 > p.characters.any?
=> false
So I decided to check what I had on the party_characters table and found entries with a valid character_id but with a nil party id. I removed those and everything works as supposed.

Related

Sidekiq + Rails Model Generating Extra Records

I'm having an issue with my scheduled text messages. I run a rake task that checks to see if a text message should be put into a Sidekiq queue. The record is processed (the text is sent) but a new empty record is generated and the sentstatus is not updated to "true".
send_scheduled_text.rake
require_relative '../../app/workers/send_text_worker'
namespace :send_scheduled_text do
task:texts => :environment do
TextMessage.all.each do |text_message|
if text_message.sentstatus == false
if (Date.today == text_message.scheduled_date) && (Time.now.hour >= text_message.scheduled_time.hour)
# Sidekiq code:
SendTextWorker.perform_async(text_message.id)
end
end
end
end
end
send_text_worker.rb
class SendTextWorker
include Sidekiq::Worker
def perform(text_message_id)
text = TextMessage.find(text_message_id)
text.send_text_message(text.content, text.phone)
end
end
text_message.rb
require 'twilio-ruby'
require 'date'
class TextMessage < ActiveRecord::Base
belongs_to :client, dependent: :destroy
belongs_to :step, dependent: :destroy
has_many :coach_emails
before_save :grab_phone
def grab_phone
self.phone = phone
end
def send_text_message(message, phone)
twilio_sid = ENV["TWILIO_ACCT_SID"]
twilio_token = ENV["TWILIO_AUTH_TOKEN"]
twilio_phone_number = ENV["TWILIO_PHONE_NUMBER"]
begin
#twilio_client = Twilio::REST::Client.new(twilio_sid, twilio_token)
#twilio_client.account.sms.messages.create(
:from => "+1#{twilio_phone_number}",
:to => phone,
:body => message)
rescue Twilio::REST::RequestError => e
puts e.message
end
if e != "400" || e != "500"
self.sentstatus = true
end
self.save!
send
send
Rails console: before rake task is called
(sentstatus is false)
irb(main):001:0> TextMessage.all
TextMessage Load (0.5ms) SELECT "text_messages".* FROM "text_messages"
=> #<ActiveRecord::Relation [#<TextMessage id: 164, client_id: nil, content: "Testing Sidekiq processing", incoming_message: false, created_at: "2015-02-02 04:43:29", updated_at: "2015-02-02 04:43:29", scheduled_date: "2015-02-01", sentstatus: false, step_id: 4, phone: "+14127364161", scheduled_time: "2000-01-01 14:00:00">]>
Rails console: After rake task is called
(sentstatus is false, should be true. I also have this new bizarre empty record)
irb(main):001:0> TextMessage.all
TextMessage Load (0.5ms) SELECT "text_messages".* FROM "text_messages"
=> #<ActiveRecord::Relation [#<TextMessage id: 164, client_id: nil, content: "Testing Sidekiq processing", incoming_message: false, created_at: "2015-02-02 04:43:29", updated_at: "2015-02-02 04:43:29", scheduled_date: "2015-02-01", sentstatus: false, step_id: 4, phone: "+14127364161", scheduled_time: "2000-01-01 14:00:00">,
#<TextMessage id: 165, client_id: nil, content: nil, incoming_message: nil, created_at: "2015-02-02 04:45:24", updated_at: "2015-02-02 04:45:24", scheduled_date: nil, sentstatus: true, step_id: nil, phone: nil, scheduled_time: nil>]>
I have a feeling this is a Sidekiq nuance that I'm missing. Thanks for any thoughts!
I ended up moving my model logic to my worker. Presto -- timing works and I'm not generating any extra nil records.

Create instance through self referential association

Am I missing something here?
Discourse model:
class Discourse < ActiveRecord::Base
#<Discourse id:, user_id: , sub_discourse_id: , title: , body: , deleted: , delete_date: , created_at:, updated_at: >
has_many :discourse_replies
has_many :replies, through: :discourse_replies
end
DiscourseReply model:
class DiscourseReply < ActiveRecord::Base
belongs_to :discourse
belongs_to :reply, class_name: 'Discourse'
end
console:
Loading development environment (Rails 4.0.2)
2.0.0p247 :001 > fd = Discourse.create(title: 'first', body: 'first')
=> #<Discourse id: 5, user_id: nil, sub_discourse_id: nil, title: "first", body: "first", deleted: nil, delete_date: nil, created_at: "2014-04-04 23:32:13", updated_at: "2014-04-04 23:32:13">
2.0.0p247 :002 > fd.discourse_replies
=> #<ActiveRecord::Associations::CollectionProxy []>
2.0.0p247 :004 > fd.create_discourse_reply
NoMethodError: undefined method 'create_discourse_reply' for #<Discourse:0x00000003396450>
2.0.0p247 :004 > fd.discourse_replies.build(title: "reply to first", body: "reply to first")
ActiveRecord::UnknownAttributeError: unknown attribute: title
In a nutshell, why is create_discourse_reply an undefined method?
You've defined has_many association for discourse_replies so to create a associated object you need to do
fd.discourse_replies.create
fd.create_discourse_reply would have been created the object if you'd have associated it with has_one or belongs_to association.

One to many relationship behavior

I have a behavior about the one to many relationship that I don't get and it definitely turns me nuts.
Here is model 1:
class Account < ActiveRecord::Base
belongs_to :organization, :autosave => true
validates :organization, :presence => true
end
Here is model 2:
class Organization < ActiveRecord::Base
has_many :accounts, :autosave => true
validates :accounts, :presence => true
end
Now, in a rails console:
>> acc = Account.new
>> org = Organization.new
>> org.accounts << acc
>> org.accounts
[#<Account id: nil, organization_id: nil, created_at: nil, updated_at: nil>]
>> acc.organization
nil
or the other way around:
>> acc = Account.new
>> org = Organization.new
>> acc.organization = org
>> acc.organization
#<Organization id: nil, created_at: nil, updated_at: nil>
>> organization.accounts
[]
Is this normal behavior? Should I manually update both sides of the relationship?!
the answer is simple just save the object first
acc = Account.new
org = Organization.new
acc.organization = org
acc.save
Ref this use build
org = Organization.new
acc = org.build_account.new
org.save

query embedded document matching several attributes

I have the following structure:
class User
include Mongoid::Document
end
class Resource
include Mongoid::Document
embeds_many :permissions
end
class Permission
include Mongoid::Document
embedded_in :resource
field :read, type: Boolean
field :write, type: Boolean
field :user_id, type: BSON::ObjectId
end
Now let's suppose I have the following data:
1.9.3p194 :001 > a = User.create
=> #<User _id: 4ff46818f83222daf9000001, _type: nil>
1.9.3p194 :002 > b = User.create
=> #<User _id: 4ff4681bf83222daf9000002, _type: nil>
1.9.3p194 :003 > r = Resource.create
=> #<Resource _id: 4ff46822f83222daf9000003, _type: nil>
1.9.3p194 :004 > r.permissions.create(read: true, user_id: a.id)
=> #<Permission _id: 4ff46835f83222daf9000004, _type: nil, read: true, write: nil, user_id: 4ff46818f83222daf9000001>
1.9.3p194 :005 > r.permissions.create(read: true, write: true, user_id: b.id)
=> #<Permission _id: 4ff4684af83222daf9000005, _type: nil, read: true, write: true, user_id: 4ff4681bf83222daf9000002>
Now I want to find all Resources for which User A has write access (which should be none)
1.9.3p194 :007 > Resource.where('permissions.write' => true).where('permissions.user_id' => a.id).all.entries
=> [#<Resource _id: 4ff46822f83222daf9000003, _type: nil>]
This obviously fails because for each where clause there is a match.
What would be the correct solution for this? Is there a way to do an and for embedded document queries?
Try this
Resource.where(:permissions.matches => {:write => true, :user_id => a.id}).all
Try this one:
Resource.Permission.where(write: true,user_id: a.id)

How to create associated objects (you have accepted parameters) for after saving in Rails?

The problem I am having with this is Product is trying to create variants before the product is even created and there are certain callbacks for variants that require the product to exist. So how can I rewrite this so that v.save doesn't execute till the object is created or whatever.
Product.class_eval do
validates_presence_of [:style_no, :market_price, :designer, :short_description, :description]
validates_numericality_of [:size_47_quantity,
:size_46_quantity,
:size_45_quantity,
:size_44_quantity,
:size_43_quantity,
:size_42_quantity,
:size_41_quantity,
:size_40_quantity,
:size_39_quantity]
for i in 39..47
define_method:"size_#{i}_quantity" do
if v = self.variants.find_by_size(i)
v.count_on_hand
else
0
end
end
define_method:"size_#{i}_quantity=" do |amount|
# if only there is some method that can postpone all the following if this product hasn't been created yet!
self.id = Product.last.id + 1 unless self.id
v = self.variants.find_by_size(i) || self.variants.new(:size => i)
v.count_on_hand = amount
v.save
end
end
end
You can try this solution:
Product class
class Product < ActiveRecord::Base
validates_presence_of [:style_no, :market_price, :designer, :short_description, :description]
has_many :variants
# This method would check if variant was created or loaded.
#
# So many sequantial calls to it will return same object
def variant_with_size(size)
self.variants.select{|v| v.size == size}.first || self.variants.where('size = ?', size).first
end
module ClassExtensions
def self.included(base)
(39..47).each do |i|
method = "size_#{i}_quantity".to_sym
included_module = Module.new
included_module.module_eval <<EOF
def #{method}
if v = self.variant_with_size(#{i})
v.count_on_hand
else
0
end
end
def #{method}=(amount)
v = self.variant_with_size(#{i}) || self.variants.build(:size => #{i})
v.count_on_hand = amount
v
end
EOF
base.send :include, included_module
end
end
end
include ClassExtensions
end
Variant class
class Variant < ActiveRecord::Base
belongs_to :product
validates :count_on_hand, :numericality => true
end
Usage
Usage example with correct variant amount:
ruby-1.9.2-p180 :001 > p = Product.new
=> #<Product id: nil, style_no: nil, market_price: nil, designer: nil, short_description: nil, description: nil, created_at: nil, updated_at: nil>
ruby-1.9.2-p180 :002 > p.size_39_quantity
=> 0
ruby-1.9.2-p180 :003 > p.size_39_quantity = 2
=> 2
ruby-1.9.2-p180 :004 > p.variants
=> [#<Variant id: nil, product_id: nil, size: 39, count_on_hand: 2, created_at: nil, updated_at: nil>]
ruby-1.9.2-p180 :005 > p.save
=> true
ruby-1.9.2-p180 :006 > p.variants
=> [#<Variant id: 3, product_id: 3, size: 39, count_on_hand: 2, created_at: "2011-04-06 06:34:46", updated_at: "2011-04-06 06:34:46">]
Usage with incorrect variant amount:
ruby-1.9.2-p180 :007 > p1 = Product.new
=> #<Product id: nil, style_no: nil, market_price: nil, designer: nil, short_description: nil, description: nil, created_at: nil, updated_at: nil>
ruby-1.9.2-p180 :008 > p1.size_39_quantity = 'A'
=> "A"
ruby-1.9.2-p180 :009 > p1.save
=> false
ruby-1.9.2-p180 :010 > p1.errors
=> {:variants=>["is invalid"]}
ruby-1.9.2-p180 :011 > p1.variants[0].errors
=> {:count_on_hand=>["is not a number"]}
At a glance, I'd consider using an after_save callback on Product to create product variants.
Something like:
class Product < ActiveRecord::Base
has_many :variants
after_save :create_variants! if :not_a_variant?
OPTIONS = [:size_1_qty, :size_2_qty] # TODO: move to a OptionType model associated with Product
def not_a_variant?
size.nil? # or however you might distinguish a Product from a Variant
end
private
def create_variants!
# OPTIONS could instead be related option_types. perhaps a 'size' option type with values of 40, 41, 42, etc.
OPTIONS.each do |size|
variants.build(...)
end
save!
end
end
I was just reviewing the Spree shopping cart project by Rails Dog and they handle product variants in a similar fashion. You might check it out.

Resources