Virtual attributes and mass-assignment - ruby-on-rails

developers! I can't understand next situation
For Example I have model
class Pg::City < ActiveRecord::Base
belongs_to :country
#virtual accessors
attr_accessor :population
#attr_accessible :city, :isdisabled, :country_id
end
I can use code like this:
c = Pg::City.new({:population=>1000})
puts c.population
1000
But if I uncomment attr_accessible code above throw warning
WARNING: Can't mass-assign protected attributes: population
How can I use virtual attributes for mass-assigmnment together with model attributes?
Thanks!

Using attr_accessor to add a variable does not automatically add it to attr_accessible. If you are going to use attr_accessible, then you will need to add :population to the list:
attr_accessor :population
attr_accessible :city, :isdisabled, :country_id, :population

Related

Rails validations and belongs_to association

In my rails projects I have a lot of association tables. And I have some validations. Nothing really difficult, and it works almost every times.
But from time to time (like tonight), I have to switch from
validates_presence_of :project_id
validates_presence_of :tag_id
validates_uniqueness_of :project_id, :scope => [:tag_id]
to
validates_presence_of :project
validates_presence_of :tag
validates_uniqueness_of :project, :scope => [:tag]
Do you know the difference ? Do you if one is better than the other ?
From the Rails Guides: http://guides.rubyonrails.org/active_record_validations.html#presence
2.9 presence This helper validates that the specified attributes are not empty. It uses the blank? method to check if the value is either
nil or a blank string, that is, a string that is either empty or
consists of whitespace.
class Person < ActiveRecord::Base
validates :name, :login, :email, presence: true
end
If you want to be sure that an association is present, you'll need to
test whether the associated object itself is present, and not the
foreign key used to map the association.
class LineItem < ActiveRecord::Base
belongs_to :order
validates :order, presence: true
end
So, you should use the second example you gave, which tests if the associated object itself is present, and not the first example, which only tests if the foreign key used to map the association is present.

Using rails associations for models (undefined method `has_one')

I am trying to open up the registration page to create an account (which has a payment) and I get this error "undefined method `has_one' for Account:Class". I am not using a database so I am not using active record. Is there a way around this?
account.rb
class Account
include ActiveModel::Model
attr_accessor :company_name, :phone_number,
:card_number, :expiration_month, :expiration_year, :cvv
has_one :payment
end
payment.rb
class Payment
include ActiveModel::Model
attr_accessor :card_number, :expiration_month, :expiration_year, :cvv
belongs_to :account
validates :card_number, presence: true, allow_blank: false
validates :cvv, presence: true, allow_blank: false
end
account_controller.rb
class AccountController < ApplicationController
def register
#account = Account.new
end
end
has_one and belongs_to are not part of ActiveModel::Model. They are part of ActiveRecord as they specify how to get the objects from the relational database.
In your case I guess you should just have another attribute payment in Account model.
class Account
include ActiveModel::Model
attr_accessor :company_name, :phone_number,
:card_number, :expiration_month,
:expiration_year, :cvv,
:payment
end
and then in your controller do something like
class AccountController < ApplicationController
def register
#account = Account.new
#account.payment = Payment.new
end
end
or you could event initialize the payment in initializer of Account class. Also it seems like Payment does not need to know about Account.
Certainly this is a very old question, but I ran across it while trying to solve a similar problem and eventually found that solutions have arisen in the intervening years. So, given the only posted answer was never marked as "accepted," I thought I'd throw my hat in the ring.
Rails 5 introduced the ActiveRecord Attributes API, and by way of this post by Karol Galanciak describing it you may be interested to take a look at a gem created by the same author. If you took a look at the issues for that gem, you might be interested to read in issue #12 that the Attributes API functionality is now present in ActiveModel, though it is not publicly documented (the module Attributes is flagged with #:nodoc:, see attributes.rb) and perhaps should not be relied upon to be consistent from version to version, though the wind, soft as it may seem at times, certainly seems to be blowing in the direction of a "public" ActiveModel::Attributes API.
Nevertheless, if you were to throw caution to the wind and use the not-quite-public ActiveModel::Attributes you could do something like this (note: I cobbled this together for my own project and rewrote it a bit to fit your example, your needs may be different than mine):
class AccountPayment
include ActiveModel::Model
attr_accessor :card_number, :expiration_month, :expiration_year, :cvv
validates :card_number, presence: true, allow_blank: false
validates :cvv, presence: true, allow_blank: false
end
class AccountPaymentType < ActiveModel::Type::Value
def cast(value)
AccountPayment.new(value)
end
end
class Account
include ActiveModel::Model
include ActiveModel::Attributes #being bad and using private API!
attr_accessor :company_name, :phone_number, :card_number, :expiration_month, :expiration_year, :cvv
attribute :payment, :account_payment
end
And somewhere you must register the type - in rails it'd be in an initializer, but in my code I just stashed it at the top of the equivalent of your Account model:
ActiveModel::Type.register(:account_payment, AccountPaymentType)

Association Clarification Rails

This is probably so simple its stupid, but I'm having a brainfreeze moment and am just staring at the screen now going nowhere.
I have two models a member and a membership, each member can have one type of membership from a selection of many.
class Member < ActiveRecord::Base
attr_accessible :forename, :middlename, :surname, :house_no, :house_name, :street, :town, :postcode, :home_tel, :mobile_tel, :work_tel, :email
end
class Membership < ActiveRecord::Base
attr_accessible :membership_type
end
My membership model will have a few records pre populated so that a member can choose which type of membership they would like, ie Peak, Off Peak, Student
Am i correct in thinking that the member model will look like this
class Member < ActiveRecord::Base
**has_one :membership**(added this)
**accepts_nested_attributes_for :membership**
attr_accessible **:membership_attributes(Added This)**, :forename, :middlename, :surname, :house_no, :house_name, :street, :town, :postcode, :home_tel, :mobile_tel, :work_tel, :email
end
So i create a migration and add the membership_id column to the member model as the foreign key?
My Membership Model can look like this
class Membership < ActiveRecord::Base
**belongs_to :member** (Added This)
attr_accessible :membership_type
end
am i looking at this correctly here?
Thanks
So i create a migration and add the membership_id column as the
foreign key?
I think that in your migration you have to add a member_id column to the memberships table, as the foreign key.
(Active Record Associations has one)

Composite primary keys in ruby on rails

I am trying to use http://compositekeys.rubyforge.org/ in order to have composite primary keys in my activerecord models.
I already added gem 'composite_primary_keys', '=3.1.0' to my Gemfile.
Now I am trying to setup my first modelclass as follows.
class StringProperty < ActiveRecord::Base
self.primary_keys :entity_id, :property_id
set_table_name "problem.string_property"
attr_accessible :entity_id, :property_id, :value
end
But all I get is:
What am I doing wrong? :(
The following will work I think.
require 'composite_primary_keys'
class StringProperty < ActiveRecord::Base
self.primary_keys = :entity_id, :property_id
set_table_name "problem.string_property"
attr_accessible :entity_id, :property_id, :value
end
If it is only for unique constraint purposes use:
class Field < ActiveRecord::Base
validates :input, uniqueness: { scope: :user_id,
message: "one input per user" }
end
source: http://guides.rubyonrails.org/active_record_validations.html

Rails setting OR conditions in validate_presence_of in a model?

In a rails model, is it possible to do something like
class Example < ActiveRecord::Base
#associations
validates_presence_of :item_id, (:user_id OR :user_email)
#functions
end
Where the model has 3 columns of :item_id, :user_id, and :user_email?
I want the model to be valid as long as I have a :user_id or a :user_email.
Idea being that if the item is recommended to a person who isn't currently signed up, it can be associated via email address for when the recommended person signs up.
Or is there a different method that I can use instead?
One approach is to wrap those fields as a virtual attribute, say:
class Example < ActiveRecord::Base
validates_presence_of :referral
def referral
user_id || user_email
end
end
or you can just throw a custom validate validation method. See custom validations on the Rails API
If both user_id and user_email come from another model, perhaps it's better to add the association instead
class Example
belongs_to :user
validates_associated :user
before_validate :build_user_from_id_or_email
def build_user_from_id_or_email
# ... Find something with the parameters
end
end
validates_presence_of :item_id
validates_presence_of :user_id, :if => Proc.new{ |x| x.user_email.blank? }
validates_presence_of :user_email, :if => Proc.new{ |x| x.user_id.blank? }

Resources