Rails Associations Not working and not recognizing classes - ruby-on-rails

I have a weird bug that just popped up with my rails app that I cannot figure out. I recently added a new association to an existing Model and now my previous associations do not want to work properly.
#=> self.user
#=> <# user.id => "1" ...
#=> self.transactions
#=> [<# transaction_id => "1"...
#=> self.credit_plan
#=> nil
So the first two associations work fine via, but for some reason credit_plan returns nil and is crashing all my existing working code. Here is the record associations I have.
class Order < ActiveRecord::Base
belongs_to :user
belongs_to :credit_plan
has_many :transactions, :class_name => "OrderTransaction"
.
class CreditPlan < ActiveRecord::Base
scope :active, where({:is_active => true})
scope :inactive, where({:is_active => false})
has_many :orders, :class_name => "Order"
.
class OrderTransaction < ActiveRecord::Base
belongs_to :order
serialize :params

Alright Guys, I figured it out. If I had posted more context of my files, I'm sure someone would have figured it out and helped me sooner.
So basically, when I was setting up my virtual attributes for the credit card form, I accidentally stomped on my own name space by adding :credit_plan as an attribute, which overrides the association.
class Order < ActiveRecord::Base
belongs_to :user
belongs_to :credit_plan
has_many :transactions, :class_name => "OrderTransaction"
validates_presence_of :credit_plan_id, :user
attr_accessor :first_name, :last_name, :card_type, :credit_card,
:number, :verification_value, :promotional_code, :expires_on,
:credit_plan # << This will override associations, delete to fix.
validate :validate_card, :on => :create

Related

how to create by default an instance of an object in a has_one relationship

Is there a DSL for creating an object in an AR relationship that would be the opposite of :dependent => destroy (in other words create an object so that it always exists). Say, for example, I have the following:
class Item < ActiveRecord::Base
#price
has_one :price, :as => :pricable, :dependent => :destroy
accepts_nested_attributes_for :price
....
class Price < ActiveRecord::Base
belongs_to :pricable, :polymorphic => true
attr_accessible :price, :price_comment
I'm thinking I'd like for a price to be created every time even if we don't specify a price? Is the only (or best) option to do this as a callback or is there a way to do this via a DSL (similar to :denpendent => :destroy)?
No, as there is virtually no use-case for this. If your record cannot exist without an associated record, you should probably be preventing the record from being saved, not shoe-horning in some kind of pseudo-null object to take its place.
The closest approximation would be a before_save callback:
class Item < ActiveRecord::Base
has_one :price, :as => :pricable, :dependent => :destroy
accepts_nested_attributes_for :price
before_save :create_default_price
def create_default_price
self.price ||= create_price
end
end
You should only run this code one time on create and use the convenience method create_price here:
class Item < ActiveRecord::Base
has_one :price, :as => :pricable, :dependent => :destroy
accepts_nested_attributes_for :price
after_validation :create_default_price, :on => :create
def create_default_price
self.create_price
end
end

Validation on associtions in rails with roles

So I have two ActiveRecord classes
class User < ActiveRecord::Base
has_many :buyer_deals, :class_name => "Deal", :foreign_key => :buyer_id
has_many :seller_deals, :class_name => "Deal", :foreign_key => :seller_id
validates_presence_of :name # THIS SHOULD ONLY BE RUN IF USER IS A SELLER
# IN THE DEAL
validates_presence_of :phone # THIS SHOULD ONLY BE RUN IF USER IS A BUYER
# IN THE DEAL
end
class Deal < ActiveRecord::Base
belongs_to :seller, :class_name => 'User'
belongs_to :buyer, :class_name => 'User'
validates_associated :seller
validates_associated :buyer
end
What I want to do is create a new deal with.
Deal.create(A NICE STRUCT WITH SELLER AND BUYER)
However I only want to run the name validation if the relation from the deal is a seller and the phone if the the relation from the deal is a seller, is this possible in rails, does not seem to find anything in the documentation.
You should be able to do this by adding a condition to you validation.
So, your User class would wind up looking something like...
class User < ActiveRecord::Base
has_many :buyer_deals, :class_name => "Deal", :foreign_key => :buyer_id
has_many :seller_deals, :class_name => "Deal", :foreign_key => :seller_id
validates_presence_of :name, :if => :has_an_active_seller_deal?
validates_presence_of :phone, :if => :has_an_active_buyer_deal?
def has_active_seller_deals?
seller_deals.count > 0
end
def has_active_buyer_deals?
buyer_deals.count > 0
end
end
An alternative to this would be to simply require all users to have a name and phone number on file at all times (no conditional validation), and only reveal it to other users with which they had active deals, and not as part of a user's public profile, thereby protecting the user's privacy when possible. This would probably be simpler.
You could put the validations in a callback:
before_save :check_user_type
private
def check_user_type
user_type = self.responds_to?(seller_id) ? :seller : :buyer
if user_type == :seller
validates_presence_of :name
else
validates_presence_of :phone
end

validates_presence_of and unsaved associations

Take the following code (Rails 3.0.10):
User < AR
has_many :providers
Provider < AR
belongs_to :user
validates_presence_of :user
user = User.new
user.providers.build
# so both models not yet saved but associated with each other
user.valid?
=> false
user.errors
=> {:providers=>["is invalid"]}
user.providers.first.errors
=> {:user_id=>["can't be blank"]}
Why can't Provider see that it has a not yet saved associated user model available? Or in other words - how can I deal with that so that the validation is still present? Or maybe I'm doing something wrong?
Note, that I'm looking for a clean solution, so suggesting a before validation callback in Provider model saving the User model to the database is a no-go.
Use :inverse_of
class User < ActiveRecord::Base
has_many :providers, :inverse_of => :user
end
class Provider < ActiveRecord::Base
belongs_to :user, :inverse_of => :providers
validates :user, :presence => true
end

Updating several records for same object

Ok I'm really feeling I'm missing the rails way on this one.
Following my last question rails parameters in form_for, I can correctly update the message contents but am struggling with updating the recipients
My Draft model
class Draft < ActiveRecord::Base
belongs_to :message
belongs_to :draft_recipient, :class_name => "User"
delegate :created_at, :subject, :user, :body, :draft_recipients, :to => :message
...
My Message Model
class Message < ActiveRecord::Base
belongs_to :user
has_many :recipients, :through => :message_copies
has_many :draft_recipients, :through => :drafts
has_many :message_copies
has_many :drafts, :class_name => "Draft", :foreign_key => :message_id
attr_accessor :to #array of people to send to
attr_accessible :subject, :body, :to, :recipients, :author, :user
...
In my controller I want to do something like
new_draft_recipients = params[:draft][:draft_recipients].split(",")
#draft.update_attributes(:draft_recipients => new_draft_recipients)
which obviously doesn't work. When I try update each record comparing old (from the database )and new recipients (passed through the form), the algorithm gets ridiculously complicated. I feel what is missing is proper associations, but I don't manage to understand which. I know this is really simple. Thanks for your help

Avoiding STI in Rails

class User < ActiveRecord::Base
has_one :location, :dependent => :destroy, :as => :locatable
has_one :ideal_location, :dependent => :destroy, :as => :locatable
has_one :birthplace, :dependent => :destroy, :as => :locatable
end
class Location < ActiveRecord::Base
belongs_to :locatable, :polymorphic => true
end
class IdealLocation < ActiveRecord::Base
end
class Birthplace < ActiveRecord::Base
end
I can't really see any reason to have subclasses in this situation. The behavior of the location objects are identical, the only point of them is to make the associations easy. I also would prefer to store the data as an int and not a string as it will allow the database indexes to be smaller.
I envision something like the following, but I can't complete the thought:
class User < ActiveRecord::Base
LOCATION_TYPES = { :location => 1, :ideal_location => 2, :birthplace => 3 }
has_one :location, :conditions => ["type = ?", LOCATION_TYPES[:location]], :dependent => :destroy, :as => :locatable
has_one :ideal_location, :conditions => ["type = ?", LOCATION_TYPES[:ideal_location]], :dependent => :destroy, :as => :locatable
has_one :birthplace, :conditions => ["type = ?", LOCATION_TYPES[:birthplace]], :dependent => :destroy, :as => :locatable
end
class Location < ActiveRecord::Base
belongs_to :locatable, :polymorphic => true
end
With this code the following fails, basically making it useless:
user = User.first
location = user.build_location
location.city = "Cincinnati"
location.state = "Ohio"
location.save!
location.type # => nil
This is obvious because there is no way to translate the :conditions options on the has_one declaration into the type equaling 1.
I could embed the id in the view anywhere these fields appear, but this seems wrong too:
<%= f.hidden_field :type, LOCATION_TYPES[:location] %>
Is there any way to avoid the extra subclasses or make the LOCATION_TYPES approach work?
In our particular case the application is very location aware and objects can have many different types of locations. Am I just being weird not wanting all those subclasses?
Any suggestions you have are appreciated, tell me I'm crazy if you want, but would you want to see 10+ different location models floating around app/models?
Why not use named_scopes?
Something like:
class User
has_many :locations
end
class Location
named_scope :ideal, :conditions => "type = 'ideal'"
named_scope :birthplace, :conditions => "type = 'birthplace" # or whatever
end
Then in your code:
user.locations.ideal => # list of ideal locations
user.locations.birthplace => # list of birthplace locations
You'd still have to handle setting the type on creation, I think.
As far as I can see it, a Location is a location is a Location. The different "subclasses" you're referring to (IdealLocation, Birthplace) seem to just be describing the location's relationship to a particular User. Stop me if I've got that part wrong.
Knowing that, I can see two solutions to this.
The first is to treat locations as value objects rather than entities. (For more on the terms: Value vs Entity objects (Domain Driven Design)). In the example above, you seem to be setting the location to "Cincinnati, OH", rather than finding a "Cincinnati, OH" object from the database. In that case, if many different users existed in Cincinnati, you'd have just as many identical "Cincinnati, OH" locations in your database, though there's only one Cincinnati, OH. To me, that's a clear sign that you're working with a value object, not an entity.
How would this solution look? Likely using a simple Location object like this:
class Location
attr_accessor :city, :state
def initialize(options={})
#city = options[:city]
#state = options[:state]
end
end
class User < ActiveRecord::Base
serialize :location
serialize :ideal_location
serialize :birthplace
end
#user.ideal_location = Location.new(:city => "Cincinnati", :state => "OH")
#user.birthplace = Location.new(:city => "Detroit", :state => "MI")
#user.save!
#user.ideal_location.state # => "OH"
The other solution I can see is to use your existing Location ActiveRecord model, but simply use the relationship with User to define the relationship "type", like so:
class User < ActiveRecord::Base
belongs_to :location, :dependent => :destroy
belongs_to :ideal_location, :class_name => "Location", :dependent => :destroy
belongs_to :birthplace, :class_name => "Location", :dependent => :destroy
end
class Location < ActiveRecord::Base
end
All you'd need to do to make this work is include location_id, ideal_location_id, and birthplace_id attributes in your User model.
Try adding before_save hooks
class Location
def before_save
self.type = 1
end
end
and likewise for the other types of location
You can encapsulate the behavior of Location objects using modules, and use some macro to create the relationship:
has_one <location_class>,: conditions => [ "type =?" LOCATION_TYPES [: location]],: dependent =>: destroy,: as =>: locatable
You can use something like this in your module:
module Orders
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def some_class_method(param)
end
def some_other_class_method(param)
end
module InstanceMethods
def some_instance_method
end
end
end
end
Rails guides: add-an-acts-as-method-to-active-record
Maybe I'm missing something important here, but I thought you could name your relationships like this:
class User < ActiveRecord::Base
has_one :location, :dependent => :destroy
#ideal_location_id
has_one :ideal_location, :class_name => "Location", :dependent => :destroy
#birthplace_id
has_one :birthplace, :class_name => "Location", :dependent => :destroy
end
class Location < ActiveRecord::Base
belongs_to :user # user_id
end

Resources