I quickly ran into problems when trying to create an ActiveRecord instance that overrode initialize like this:
class Email < ActiveRecord::Base
belongs_to :lead
def initialize(email = nil)
self.email = email unless email.nil?
end
end
I found this post which cleared up why it is happening.
Is there anyway that I can avoid creation code like this:
e = Email.new
e.email = "info#info.com"
I would like to create and initialise my objects in one line of code preferably.
Is this possible?
e = Email.new(:email => "info#info.com")
ActiveRecord::Base#new also takes a handy block variation
email = Email.new do |e|
e.email = params[:email] unless params[:email].blank?
end
The suggestions of using the hash version in prior answers is how i typically do it if I don't want to put any logic on the actual assignment.
Related
I am trying to make it available to repost a post in my app...I see in a Youtube video this code..
class Pin < ActiveRecord::Base
validates_presence_of :bio
belongs_to :user
mount_uploader :image, PictureUploader
def repost
repost_pin = self.dup
repost_pin.save
end
is it correct to use self like here? and would it be possible to change to this:
def repost
dub
end
in the end repost will be called on the instance variable #post(#post = Post.find(params[:id]) ) .... maybe I misunderstanding something here...anyone can help?
You can skip self when it is clear from context that you are reading a current instance's attribute or calling its method.
When writing you should be more careful as this:
def write
test = 'test'
end
will create a local variable test even if there is an attribute with the same name. Alternatively, this:
def write
self.test = 'test'
end
will assign the value to current instance's attribute named test.
In your example you could skip self since dup is a method of Object and therefore is available in current context as a valid identifier:
def repost
repost_pin = dup
repost_pin.save
end
That said, it is not an error to explicitly use self e.g., to mark your intent to use the object's attribute or method. Ruby style guide does not recommend it though.
I want some of my model attributes to predefined dynamically. I have various models.And now I want My Bill model to create objects using other model instances.
Models :
leave.rb # belongs_to :residents
resident.rb # has_many:leaves,has_many:bills,has_one:account
bill.rb # belongs_to:residents
rate_card.rb # belongs_to:hostel
account.rb # belongs_to:resident
hostel.rb
now here is my bills controller create method :
def create
#bill = Resident.all.each { |resident| resident.bills.create(?) }
if #bill.save
flash[:success]="Bills successfully generated"
else
flash[:danger]="Something went wrong please try again !"
end
end
I want to build bill using all of the models eg:
resident.bills.create(is_date:using form,to_date:using form,expiry_date:using form,amount:30*(resident.rate_card.diet)+resident.rate_card.charge1+resident.rate_card.charge2)+(resident.account.leaves)*10+resident.account.fine)
///////Is this possible ?
And how to use strong params here ?
Pls help me out thxx..
I think the Rails way for this logic you want is with callbacks if you want calculated attributes either on create, update or delete, meaning attributes that depend on other models. For instance:
class Bill < ActiveRecord::Base
...
before_create :set_amount
...
protected
def set_amount
self.amount = 30 * self.resident.rate_card.diet + self.resident.rate_card.charge1 + self.resident.rate_card.charge2 + (self.resident.account.leaves) * 10 + self.resident.account.fine
end
end
If you want this logic to be used when updating the record also, then you should use before_save instead of before_create.
After you do this, you should accept the usual params (strong) of Bill model, as in:
def bill_params
params.require(:bill).permit(:is_date, :to_date, :expiry_date)
end
So your create call would be like:
resident.bills.create(bill_params)
Also, be wary of your create action, you should probably create a method either on your Bill or your Resident model that uses transactions to create all bills at the same time because you probably want either every bill created or none. This way you won't have the Resident.all.each logic in your BillsController.
create takes a hash, you can:
create_params = { amount: 30*(resident.rate_card.diet) }
create_params[:some_field] = params[:some_field]
# and so on
resident.bills.create(create_params)
or:
obj = resident.bills.build(your_strong_parameters_as_usual)
obj.amount = # that calculation
obj.save!
I'm confused at your syntax of your controller. #bill is being set to the value of a loop, which feels off. Each loops return the enumerable you cycle through, so you'll end up with #bill = Resident.all with some bills being created on the side.
What your controller really wants to know is, did my many new bills save correctly?
This seems like a perfect place to use a ruby object (or, colloquially, a Plain Old Ruby Object, as opposed to an ActiveRecord object) to encapsulate the specifics of this bill-generator.
If I'm reading this right, it appears that you are generating many bills at once, based on form-inputted data like:
is_date
to_date
expiry_date
...as well as some data about each individual resident.
Here's the model I'd create:
app/models/bill_generator.rb
class BillGenerator
include ActiveModel::Model
# This lets you do validations
attr_accessor :is_date, :to_date, :expiry_date
# This lets your form builder see these attributes when you go form.input
attr_accessor :bills
# ...for the bills we'll be generating in a sec
validates_presence_of :is_date, :to_date, :expiry_date
# You can do other validations here. Just an example.
validate :bills_are_valid?
def initialize(attributes = {})
super # This calls the Active Model initializer
build_new_bills # Called as soon as you do BillGenerator.new
end
def build_new_bills
#bills = []
Resident.all.each do |r|
#bills << r.bills.build(
# Your logic goes here. Not sure what goes into a bill-building...
# Note that I'm building (which means not-yet-saved), not creating
)
end
def save
if valid?
#bills.each { |b| b.save }
true
else
false
end
end
private
def bills_are_valid?
bill_validity = true
#bills.each do |b|
bill_validity = false unless b.valid?
end
bill_validity
end
end
Why all this mess? Because in your controller you can do...
app/controllers/bill_controller.rb
def create
#bill_generator = BillGenerator.new(bill_generator_params)
if #bill_generator.save?
# Redirect to somewhere with a flash?
else
# Re-render the form with a flash?
end
end
def bill_generator_params
params.require(:bill_generator).permit(:is_date, :to_date, :expiry_date)
# No extra garbage. No insecurity by letting all kinds of crud through!
end
...like a BillGenerator is any old object. Did it save? Great. It didn't, show the form again.
Now, my BillGenerator won't just be copy-and-paste. Your 'build_new_bills' probably will have some of that math you alluded to, which I'll leave to you.
Let me know what you think!
you can do it by using params.permit! as this allows any parameters to be passed. here's an example:
def create
...
#bill = Resident.all.each { |resident| resident.bills.create(any_params) }
end
private
def any_params
params.permit!
end
be careful with this of course, as you are opening this up to potential exploits.
I'm newbie in Ruby, so need help, because can not find answer :(
I have Rails application, which has model Event like this:
class Event < ActiveRecord::Base
before_validation :clean_input
....
protected
def clean_input
fields = %w[title preview content]
fields.each do |field|
eval "self.#{field} = ActionController::Base.helpers.sanitize(self.#{field})"
end
end
end
The method purpose is cleaning input from dangerous data before validation and before storing it in DB.
Before I wrote this method it looked like the one below (with lot of duplication, that is not DRY at all). This code is very clear, but when adding new field I'll have to add new line instead of adding new element to an array:
def clean_input
self.title = ActionController::Base.helpers.sanitize(self.title)
self.preview = ActionController::Base.helpers.sanitize(self.preview)
self.content = ActionController::Base.helpers.sanitize(self.content)
end
So my questions are:
1) is it possible to omit eval ... in favor of call or send somehow (all my attemps were useless)?
2) is it possible to declare before_validation :clean_input like this before_validation clean_input: fields: { :title, :preview, :content}?
1) Sure:
def clean_input
%w(title preview content).each do |field|
self.send("#{field}=", ActionController::Base.helpers.sanitize(self.send(field)))
end
end
2) No, and your current implementation is ok
Since you are updating an active record model, there are a few other ways of updating attributes, for example:
def clean_input
%i(title preview content).each do |field|
self[field] = ActionController::Base.helpers.sanitize(self[field])
end
end
I have a strange issue setting data inside an active record..
When I attempt to set the data inside a method, it doesn't seem to affect anything.
Here's my class
class Option < ActiveRecord::Base
serialize :returns_policy_refunds, Array
def reloadRefundOptions!
#returns_policy_refunds = WebSite.get_refund_options #options array
end
end
Simple as' class eh?
To test serialization I'm just spitting out the data on a screen..
-#options.each do |option|
- option.returns_policy_refunds = ["wtf"] #just to reset things
<b>BLOCK 1</b>
= option.reloadRefundOptions!
= option.returns_policy_refunds
<br>
<b>BLOCK 2</b>
= option.returns_policy_refunds = WebSite.get_refund_options
= option.returns_policy_refunds
Now.. I'd expect to see the same in BLOCK1 as in BLOCK2..
The method sets the return policy..
What I actually see in the first option.returns_policy_refunds is ["wtf"]
What am I missing? I must be doing something wrong, but I have no idea what.
Any thoughts?
Leave out the # in your attribute assignment:
class Option < ActiveRecord::Base
serialize :returns_policy_refunds, Array
def reloadRefundOptions!
self.returns_policy_refunds = WebSite.get_refund_options #options array
end
end
Haven't tried it yet but I would say that option.returns_policy_refunds gets the data from the attributes hash defined by ActiveRecord. If you assign a class variable using # it's just defined there and may only be accessed with an attribute reader or a direct send.
For example, if I have a user model and I need to validate login only (which can happen when validating a form via ajax), it would be great if I use the same model validations defined in the User model without actually instantiating a User instance.
So in the controller I'd be able to write code like
User.valid_attribute?(:login, "login value")
Is there anyway I can do this?
Since validations operate on instances (and they use the errors attribute of an instance as a container for error messages), you can't use them without having the object instantiated. Having said that, you can hide this needed behaviour into a class method:
class User < ActiveRecord::Base
def self.valid_attribute?(attr, value)
mock = self.new(attr => value)
unless mock.valid?
return mock.errors.has_key?(attr)
end
true
end
end
Now, you can call
User.valid_attribute?(:login, "login value")
just as you intended.
(Ideally, you'd include that class method directly into the ActiveRecord::Base so it would be available to every model.)
Thank you Milan for your suggestion. Inspired by it I created a simple module one can use to add this functionality to any class. Note that the original Milans suggestion has a logic error as line:
return mock.errors.has_key?(attr)
should clearly be:
return (not mock.errors.has_key?(attr))
I've tested my solution and it should work, but ofc I give no guarantees. And here's my glorious solution. Basically a 2-liner if you take away the module stuff.. It accepts method names as stings or symbols.
module SingleAttributeValidation
def self.included(klass)
klass.extend(ClassMethods)
end
module ClassMethods
def valid_attribute?(attr, value)
mock = self.new(attr => value)
(not mock.valid?) && (not mock.errors.has_key?(attr.class == Symbol ? attr : attr.to_sym))
end
end
end
To use your standard validation routines:
User.new(:login => 'login_value').valid?
If that does not work for you, build a custom class method for this:
class User < ActiveRecord::Base
validate do |user|
user.errors.add('existing') unless User.valid_login?(user.login)
end
def self.valid_login?(login)
# your validation here
!User.exist?(:login=> login)
end
end
I had a hell of a time getting this to work in Rails 3.1. This finally worked. (Not sure if it's the best way to do it, I'm kind of a newb.). The problem I was having was that value was being set to type ActiveSupport::SafeBuffer, and was failing validation.
def self.valid_attribute?(attr, value)
mock = User.new(attr => "#{value}") # Rails3 SafeBuffer messes up validation
unless mock.valid?
return (not mock.errors.messages.has_key?(attr))
end
return true
end
I have gone with the custom class solution but I just wanted to make sure there was no better way
class ModelValidator
def self.validate_atrribute(klass, attribute, value)
obj = Klass.new
obj.send("#{attribute}=", value)
obj.valid?
errors = obj.errors.on(attribute).to_a
return (errors.length > 0), errors
end
end
and I can use it like
valid, errors = ModelValidator.validate_attribute(User, "login", "humanzz")
class User < ActiveRecord::Base
validates_each :login do |record, attr, value|
record.errors.add attr, 'error message here' unless User.valid_login?(value)
end
def self.valid_login?(login)
# do validation
end
end
Just call User.valid_login?(login) to see if login itself is valid
An implementation of the 'valid_attribute' method you are suggesting:
class ActiveRecord:Base
def self.valid_attribute?(attribute, value)
instance = new
instance[attribute] = value
instance.valid?
list_of_errors = instance.errors.instance_variable_get('#errors')[attribute]
list_of_errors && list_of_errors.size == 0
end
end
How about:
User.columns_hash.has_key?('login')