Best way to ensure a Model attribute is consistently downcased and stripped? - ruby-on-rails

I'd like to know what the best way to ensure a user supplied parameter is downcased and stripped in all situations.
I would like to achieve the following:
Guarantee that the attribute will not be saved to the DB unless stripped/downcased
Queries against the db should always downcase/strip the attribute
Validations are run against a downcased/stripped version of user supplied params
Models return downcase/stripped attribute (which shouldn't be a problem given item #1)

you need to write a before_save callback method, within which you downcase and strip the attributes set by the user.
For eg:
class User < ActiveRecord::Base
before_save :format_values
def format_values
self.name = self.name.downcase
end
end
EDIT
I had missed your 3rd point about validations. So if you need to also run validations on these values. You'd need to use the before_validation callback instead.

class User < ActiveRecord::Base
before_validation :format_values
def format_values
self.name = self.name.strip.downcase if name
end
end

Updated the answer based on the comment.
No need to get fancy with callbacks (don't use callbacks, anyway). Just override the setter for your attribute.
class MyModel
def some_attribute=(value)
value = value.strip.downcase if value
write_attribute(:some_attribute, value)
end
end

You would do that in a before_validation callback:
# in your model
before_validation :normalize_attribute
private
def normalize_attribute
# change `attribute` to your actual attribute's name
self.attribute = attribute.strip.downcase if attribute
end
Or you could do that with a custom setter:
# change `attribute` to your actual attribute's name
def attribute=(value)
write_attribute(:attribute, value.strip.downcase) if value
end
The first option will sanitize the attribute's value every time the object is saved, even if the value has not changed. This might be helpful if you introduce this sanitize method when records in the database already exist, because this allow to sanitize all existing record with just one line of code in the Rails console: Model.find_each(&:save). The second option will only sanitize values when they are set. This is a bit more performant.
I both cases I suggest to check for if attribute otherwise you might call strip.downcase on nil values what would lead to an exception.

Related

Set non-database attribute for rails model without `attr_accessor`

In PHP, I can set an attribute (that is not a column in database) to a model. E.g.(PHP code),
$user = new User;
$user->flag = true;
But in rails, when I set any attribute that doesn't exist in database, it will throw an error undefined method flag. There is attr_accessor method, but what will happen if I need about ten temp attributes?
but what will happen if I need about ten temp attributes?
#app/models/user.rb
class User < ActiveRecord::Base
attr_accessor :flag, :other_attribute, :other_attribute2, :etc...
end
attr_accessor creates "virtual" attributes in Rails -- they don't exist in the database, but are present in the model.
As with attributes from the db, attr_accessor just creates a set of setter & getter (instance) methods in your class, which you can call & interact with when the class is initialized:
#app/models/user.rb
class User < ActiveRecord::Base
attr_accessor :flag
# getter
def flag
#flag
end
# setter
def flag=(val)
#flag = val
end
end
This is expected because it's how ActiveRecord works by design. If you need to set arbitrary attributes, then you have to use a different kind of objects.
For example, Ruby provides a library called OpenStruct that allows you to create objects where you can assign arbitrary key/values. You may want to use such library and then convert the object into a corresponding ActiveRecord instance only if/when you need to save to the database.
Don't try to model ActiveRecord to behave as you just described because it was simply not designed to behave in that way. That would be a cargo culting error from your current PHP knowledge.
As the guys explained, attr_accessor is just a quick setter and getter.
We can set our Model attr_accessor on record initializing to be a Ruby#Hash for example using ActiveRecord#After_initilize method so we get more flexibility on temporarily storing values (idea credit to this answer).
Something like:
class User < ActiveRecord::Base
attr_accessor :vars
after_initialize do |user|
self.vars = Hash.new
end
end
Now you could do:
user = User.new
#set
user.vars['flag'] = true
#get
user.vars['flag']
#> true
All that attr_accessor does is add getter and setter methods which use an instance variable, eg this
attr_accessor :flag
will add these methods:
def flag
#flag
end
def flag=(val)
#flag = val
end
You can write these methods yourself if you want, and have them do something more interesting than just storing the value in an instance var, if you want.
If you need temp attributes you can add them to the singleton object.
instance = Model.new
class << instance
attr_accessor :name
end

setting mandatory field automatically in Rails from Model callback, when and how?

I have this model which has a mandatory field which needs to be automatically set just before save. I'm struggling with the correct way to implement this:
build the logic in the controller before the save (and have validates rule in model)
build the logic in a before_save callback and have validates rule in model, but this seems to late in the flow? I do get validation errors this way.
build the logic in a before_save callback and don't define validation for this particular field
do it any of the ways above and don't assign a validates rule for the particular field
I was working on 2 since this seems like the correct way to implement this. Was considering the usage of before_validation, but I don't know what would happen when my other fields don't get validated... this could cause double assignment of the same value..
code for 2 which gives a basic idea of what I'm trying to achieve:
#category.rb
class Category < ActiveRecord::Base
before_create :set_position_number
def set_position_number
highest = Category.maximum(:position)
self.position = highest.to_i + 1
end
end
I'm struggling with the correct way to implement this
The most efficient way will be to use an ActiveRecord callback hook, such as you've posted:
#app/models/category.rb
class Category < ActiveRecord::Base
before_create :your_action
private
def your_action
#fires before create
end
end
but this seems to late in the flow
As mentioned in the comments, you can see the order of the callbacks (and thus their order in the flow):
Thus, if you want to populate some data before you validate, and then validate that data, you'll be best using the before_validation callback:
#app/models/category.rb
class Category < ActiveRecord::Base
before_validation :set_position_number, on: :create
validates :position, ______________
private
def set_position_number
highest = Category.maximum(:position)
self.position = highest.to_i + 1
end
end
Remember, a Rails model just populates certain attributes which are then to be either saved to the db, or validated. Rails does not care where those attributes come from; populating them before_validation is a good a source as the controller.
If you are setting a value automatically and don't take user input, you don't need validation. Write a unit test.
If the field is something like a position value, then you should indeed set it in a before_create callback.

how to validate a record in table before saving in ruby on rails

I am new to Ruby on Rails
I have a scenario in which I have a form which has some fields. One of the field values I need to validate against a table which has the data .
I want to restrict the user from saving any data unless the field is validated with the table records.
Initially I added the code in controller to validate that but I have other fields which I need to validate as empty so it did not work .
Also I want the the validation error to be part of other errors.
I tried the below code in the model file
before_create :validate_company_id
def validate_company_id
cp = Company.find_by_company_id(self.company)
if #cp != nil
return
else
self.status ||= "Invalid"
end
end
But its not validating , could you help me how I can validate it .
regards
Surjan
The guys answered correctly, but provided the other way for solution. You could ask yourself: "Why doesn't my code get executed?"
First of all, you have errors in your code - #cp is undefined. Also, I don't know what are you trying to achieve with self.status ||= "Invalid".
You also don't have to use self when you're calling an attribute, but you do have to call it when you're assignig a new attribute value. So self.company is unnecessary, you can just use company.
I've also noticed you have the company_id attribute in your companies table. That's not neccessary, common convention is using just an id instead. If you don't want to alter your table you can set the id field on your model like so:
class Company < ActiveRecord::Base
set_primary_key :company_id
# ... the rest of your model code ...
end
After that you can use Company.find instead of Company.find_by_company_id.
Okay, let's say you have the following code after the fixes:
before_create :validate_company_id
def validate_company_id
cp = Company.find(company)
if cp != nil
return
else
self.status ||= "Invalid"
end
end
First of all I would like to use ternary operator here
before_create :validate_company_id
def validate_company_id
Company.find(company) ? return : self.status ||= "Invalid"
end
Isn't this cleaner? It does the exact same thing.
Now about that self.status of yours. If you would like to invalidate the object in ActiveModel you have to set some values in errors hash. You're in misconception if you think that a model with the status attribute of "Invalid" is invalid. It's still perfectly valid model in Rails.
So how do you invalidate?
You put some values into errors hash. You can also specify a message and the attribute you're validation error refers to.
So let's do it on your model
before_create :validate_company_id
def validate_company_id
Company.find(company) ? return : errors.add(:company,"Invalid Company ID")
end
Now if you try to save your model with invalid company_id it will still pass and get saved to the DB. Why is that?
It's because of the ActiveModels lifecycle. Your method gets called too late.
Here are all the callback methods you can use
Creating
before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
after_save
Updating
before_validation
after_validation
before_save
around_save
before_update
around_update
after_update
after_save
Destroying
before_destroy
around_destroy
after_destroy
Notice how your method gets called long after the validation cycle. So you should not use before_create, but after_validation or before_validation callbacks instead.
And here we are with the working validation method of your model.
after_validation :validate_company_id
def validate_company_id
Company.find(company) ? return : errors.add(:company,"Invalid Company ID")
end
instead of using before_create. You can tell the model to use a custom method for validation as follows
validate :validate_company_id
def validate_company_id
cp = Company.find_by_company_id(self.company)
if cp.nil?
errors.add(:company, 'Invalid Company ID')
end
end
Inside you model, you can add custom validations as below:
validate :validate_company_id
def validate_company_id
Your validations
Add error messages can be added as below
errors.add(:company, "is invalid")
end

How to set some field in model as readonly when a condition is met?

I have models like this:
class Person
has_many :phones
...
end
class Phone
belongs_to :person
end
I want to forbid changing phones associated to person when some condition is met. Forbidden field is set to disabled in html form. When I added a custom validation to check it, it caused save error even when phone doesn't change. I think it is because a hash with attributes is passed to
#person.update_attributes(params[:person])
and there is some data with phone number (because form include fields for phone). How to update only attributes that changed? Or how to create validation that ignore saves when a field isn't changing? Or maybe I'm doing something wrong?
You might be able to use the
changed # => []
changed? # => true|false
changes # => {}
methods that are provided.
The changed method will return an array of changed attributes which you might be able to do an include?(...) against to build the functionality you are looking for.
Maybe something like
validate :check_for_changes
def check_for_changes
errors.add_to_base("Field is not changed") unless changed.include?("field")
end
def validate
errors.add :phone_number, "can't be updated" if phone_number_changed?
end
-- don't know if this works with associations though
Other way would be to override update_attributes, find values that haven't changed and remove them from params hash and finally call original update_attributes.
Why don't you use before_create, before_save callbacks in model to restrict create/update/save/delete or virtually any such operation. I think hooking up observers to decide whether you want to restrict the create or allow; would be a good approach. Following is a short example.
class Person < ActiveRecord::Base
#These callbacks are run every time a save/create is done.
before_save :ensure_my_condition_is_met
before_create :some_other_condition_check
protected
def some_other_condition_check
#checks here
end
def ensure_my_condition_is_met
# checks here
end
end
More information for callbacks can be obtained here:
http://guides.rubyonrails.org/activerecord_validations_callbacks.html#callbacks-overview
Hope it helps.

Rails: How can I set default values in ActiveRecord?

How can I set default value in ActiveRecord?
I see a post from Pratik that describes an ugly, complicated chunk of code: http://m.onkey.org/2007/7/24/how-to-set-default-values-in-your-model
class Item < ActiveRecord::Base
def initialize_with_defaults(attrs = nil, &block)
initialize_without_defaults(attrs) do
setter = lambda { |key, value| self.send("#{key.to_s}=", value) unless
!attrs.nil? && attrs.keys.map(&:to_s).include?(key.to_s) }
setter.call('scheduler_type', 'hotseat')
yield self if block_given?
end
end
alias_method_chain :initialize, :defaults
end
I have seen the following examples googling around:
def initialize
super
self.status = ACTIVE unless self.status
end
and
def after_initialize
return unless new_record?
self.status = ACTIVE
end
I've also seen people put it in their migration, but I'd rather see it defined in the model code.
Is there a canonical way to set default value for fields in ActiveRecord model?
There are several issues with each of the available methods, but I believe that defining an after_initialize callback is the way to go for the following reasons:
default_scope will initialize values for new models, but then that will become the scope on which you find the model. If you just want to initialize some numbers to 0 then this is not what you want.
Defining defaults in your migration also works part of the time... As has already been mentioned this will not work when you just call Model.new.
Overriding initialize can work, but don't forget to call super!
Using a plugin like phusion's is getting a bit ridiculous. This is ruby, do we really need a plugin just to initialize some default values?
Overriding after_initialize is deprecated as of Rails 3. When I override after_initialize in rails 3.0.3 I get the following warning in the console:
DEPRECATION WARNING: Base#after_initialize has been deprecated, please use Base.after_initialize :method instead. (called from /Users/me/myapp/app/models/my_model:15)
Therefore I'd say write an after_initialize callback, which lets you default attributes in addition to letting you set defaults on associations like so:
class Person < ActiveRecord::Base
has_one :address
after_initialize :init
def init
self.number ||= 0.0 #will set the default value only if it's nil
self.address ||= build_address #let's you set a default association
end
end
Now you have just one place to look for initialization of your models. I'm using this method until someone comes up with a better one.
Caveats:
For boolean fields do:
self.bool_field = true if self.bool_field.nil?
See Paul Russell's comment on this answer for more details
If you're only selecting a subset of columns for a model (ie; using select in a query like Person.select(:firstname, :lastname).all) you will get a MissingAttributeError if your init method accesses a column that hasn't been included in the select clause. You can guard against this case like so:
self.number ||= 0.0 if self.has_attribute? :number
and for a boolean column...
self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?
Also note that the syntax is different prior to Rails 3.2 (see Cliff Darling's comment below)
Rails 5+
You can use the attribute method within your models, eg.:
class Account < ApplicationRecord
attribute :locale, :string, default: 'en'
end
You can also pass a lambda to the default parameter. Example:
attribute :uuid, :string, default: -> { SecureRandom.uuid }
The second argument is the type and it can also be a custom type class instance, for example:
attribute :uuid, UuidType.new, default: -> { SecureRandom.uuid }
We put the default values in the database through migrations (by specifying the :default option on each column definition) and let Active Record use these values to set the default for each attribute.
IMHO, this approach is aligned with the principles of AR : convention over configuration, DRY, the table definition drives the model, not the other way around.
Note that the defaults are still in the application (Ruby) code, though not in the model but in the migration(s).
Some simple cases can be handled by defining a default in the database schema but that doesn't handle a number of trickier cases including calculated values and keys of other models. For these cases I do this:
after_initialize :defaults
def defaults
unless persisted?
self.extras||={}
self.other_stuff||="This stuff"
self.assoc = [OtherModel.find_by_name('special')]
end
end
I've decided to use the after_initialize but I don't want it to be applied to objects that are found only those new or created. I think it is almost shocking that an after_new callback isn't provided for this obvious use case but I've made do by confirming whether the object is already persisted indicating that it isn't new.
Having seen Brad Murray's answer this is even cleaner if the condition is moved to callback request:
after_initialize :defaults, unless: :persisted?
# ":if => :new_record?" is equivalent in this context
def defaults
self.extras||={}
self.other_stuff||="This stuff"
self.assoc = [OtherModel.find_by_name('special')]
end
The after_initialize callback pattern can be improved by simply doing the following
after_initialize :some_method_goes_here, :if => :new_record?
This has a non-trivial benefit if your init code needs to deal with associations, as the following code triggers a subtle n+1 if you read the initial record without including the associated.
class Account
has_one :config
after_initialize :init_config
def init_config
self.config ||= build_config
end
end
The Phusion guys have some nice plugin for this.
An even better/cleaner potential way than the answers proposed is to overwrite the accessor, like this:
def status
self['status'] || ACTIVE
end
See "Overwriting default accessors" in the ActiveRecord::Base documentation and more from StackOverflow on using self.
I use the attribute-defaults gem
From the documentation:
run sudo gem install attribute-defaults and add require 'attribute_defaults' to your app.
class Foo < ActiveRecord::Base
attr_default :age, 18
attr_default :last_seen do
Time.now
end
end
Foo.new() # => age: 18, last_seen => "2014-10-17 09:44:27"
Foo.new(:age => 25) # => age: 25, last_seen => "2014-10-17 09:44:28"
Similar questions, but all have slightly different context:
- How do I create a default value for attributes in Rails activerecord's model?
Best Answer: Depends on What You Want!
If you want every object to start with a value: use after_initialize :init
You want the new.html form to have a default value upon opening the page? use https://stackoverflow.com/a/5127684/1536309
class Person < ActiveRecord::Base
has_one :address
after_initialize :init
def init
self.number ||= 0.0 #will set the default value only if it's nil
self.address ||= build_address #let's you set a default association
end
...
end
If you want every object to have a value calculated from user input: use before_save :default_values
You want user to enter X and then Y = X+'foo'? use:
class Task < ActiveRecord::Base
before_save :default_values
def default_values
self.status ||= 'P'
end
end
I've also seen people put it in their migration, but I'd rather see it
defined in the model code.
Is there a canonical way to set default value for fields in
ActiveRecord model?
The canonical Rails way, before Rails 5, was actually to set it in the migration, and just look in the db/schema.rb for whenever wanting to see what default values are being set by the DB for any model.
Contrary to what #Jeff Perrin answer states (which is a bit old), the migration approach will even apply the default when using Model.new, due to some Rails magic. Verified working in Rails 4.1.16.
The simplest thing is often the best. Less knowledge debt and potential points of confusion in the codebase. And it 'just works'.
class AddStatusToItem < ActiveRecord::Migration
def change
add_column :items, :scheduler_type, :string, { null: false, default: "hotseat" }
end
end
Or, for column change without creating a new one, then do either:
class AddStatusToItem < ActiveRecord::Migration
def change
change_column_default :items, :scheduler_type, "hotseat"
end
end
Or perhaps even better:
class AddStatusToItem < ActiveRecord::Migration
def change
change_column :items, :scheduler_type, :string, default: "hotseat"
end
end
Check the official RoR guide for options in column change methods.
The null: false disallows NULL values in the DB, and, as an added benefit, it also updates so that all pre-existing DB records that were previously null is set with the default value for this field as well. You may exclude this parameter in the migration if you wish, but I found it very handy!
The canonical way in Rails 5+ is, as #Lucas Caton said:
class Item < ActiveRecord::Base
attribute :scheduler_type, :string, default: 'hotseat'
end
This is what constructors are for! Override the model's initialize method.
Use the after_initialize method.
Sup guys, I ended up doing the following:
def after_initialize
self.extras||={}
self.other_stuff||="This stuff"
end
Works like a charm!
Rails 6.1+
You can now use the attribute method on your model without setting a type.
attribute :status, default: ACTIVE
or
class Account < ApplicationRecord
attribute :locale, default: 'en'
end
Note that feeding a default to attribute cannot reference the instance of the class (a lambda will execute in the context of the class, not the instance). So, if you need to set the default to a value dynamically based on the instance or associations, you're still going to have to use an alternative, such as an after_initialize callback. As stated previously, it's recommended to limit this to new records only to avoid n+1 queries if you reference associations.
after_initialize :do_something_that_references_instance_or_associations, if: :new_record?
This has been answered for a long time, but I need default values frequently and prefer not to put them in the database. I create a DefaultValues concern:
module DefaultValues
extend ActiveSupport::Concern
class_methods do
def defaults(attr, to: nil, on: :initialize)
method_name = "set_default_#{attr}"
send "after_#{on}", method_name.to_sym
define_method(method_name) do
if send(attr)
send(attr)
else
value = to.is_a?(Proc) ? to.call : to
send("#{attr}=", value)
end
end
private method_name
end
end
end
And then use it in my models like so:
class Widget < ApplicationRecord
include DefaultValues
defaults :category, to: 'uncategorized'
defaults :token, to: -> { SecureRandom.uuid }
end
I ran into problems with after_initialize giving ActiveModel::MissingAttributeError errors when doing complex finds:
eg:
#bottles = Bottle.includes(:supplier, :substance).where(search).order("suppliers.name ASC").paginate(:page => page_no)
"search" in the .where is hash of conditions
So I ended up doing it by overriding initialize in this way:
def initialize
super
default_values
end
private
def default_values
self.date_received ||= Date.current
end
The super call is necessary to make sure the object initializing correctly from ActiveRecord::Base before doing my customize code, ie: default_values
after_initialize method is deprecated, use the callback instead.
after_initialize :defaults
def defaults
self.extras||={}
self.other_stuff||="This stuff"
end
however, using :default in your migrations is still the cleanest way.
The problem with the after_initialize solutions is that you have to add an after_initialize to every single object you look up out of the DB, regardless of whether you access this attribute or not. I suggest a lazy-loaded approach.
The attribute methods (getters) are of course methods themselves, so you can override them and provide a default. Something like:
Class Foo < ActiveRecord::Base
# has a DB column/field atttribute called 'status'
def status
(val = read_attribute(:status)).nil? ? 'ACTIVE' : val
end
end
Unless, like someone pointed out, you need to do Foo.find_by_status('ACTIVE'). In that case I think you'd really need to set the default in your database constraints, if the DB supports it.
class Item < ActiveRecord::Base
def status
self[:status] or ACTIVE
end
before_save{ self.status ||= ACTIVE }
end
I strongly suggest using the "default_value_for" gem: https://github.com/FooBarWidget/default_value_for
There are some tricky scenarios that pretty much require overriding the initialize method, which that gem does.
Examples:
Your db default is NULL, your model/ruby-defined default is "some string", but you actually want to set the value to nil for whatever reason: MyModel.new(my_attr: nil)
Most solutions here will fail to set the value to nil, and will instead set it to the default.
OK, so instead of taking the ||= approach, you switch to my_attr_changed?...
BUT now imagine your db default is "some string", your model/ruby-defined default is "some other string", but under a certain scenario, you want to set the value to "some string" (the db default): MyModel.new(my_attr: 'some_string')
This will result in my_attr_changed? being false because the value matches the db default, which in turn will fire your ruby-defined default code and set the value to "some other string" -- again, not what you desired.
For those reasons I don't think this can properly be accomplished with just an after_initialize hook.
Again, I think the "default_value_for" gem is taking the right approach: https://github.com/FooBarWidget/default_value_for
Although doing that for setting default values is confusing and awkward in most cases, you can use :default_scope as well. Check out squil's comment here.
I've found that using a validation method provides a lot of control over setting defaults. You can even set defaults (or fail validation) for updates. You even set a different default value for inserts vs updates if you really wanted to.
Note that the default won't be set until #valid? is called.
class MyModel
validate :init_defaults
private
def init_defaults
if new_record?
self.some_int ||= 1
elsif some_int.nil?
errors.add(:some_int, "can't be blank on update")
end
end
end
Regarding defining an after_initialize method, there could be performance issues because after_initialize is also called by each object returned by :find :
http://guides.rubyonrails.org/active_record_validations_callbacks.html#after_initialize-and-after_find
If the column happens to be a 'status' type column, and your model lends itself to the use of state machines, consider using the aasm gem, after which you can simply do
aasm column: "status" do
state :available, initial: true
state :used
# transitions
end
It still doesn't initialize the value for unsaved records, but it's a bit cleaner than rolling your own with init or whatever, and you reap the other benefits of aasm such as scopes for all your statuses.
https://github.com/keithrowell/rails_default_value
class Task < ActiveRecord::Base
default :status => 'active'
end
Here's a solution I've used that I was a little surprised hasn't been added yet.
There are two parts to it. First part is setting the default in the actual migration, and the second part is adding a validation in the model ensuring that the presence is true.
add_column :teams, :new_team_signature, :string, default: 'Welcome to the Team'
So you'll see here that the default is already set. Now in the validation you want to ensure that there is always a value for the string, so just do
validates :new_team_signature, presence: true
What this will do is set the default value for you. (for me I have "Welcome to the Team"), and then it will go one step further an ensure that there always is a value present for that object.
Hope that helps!
# db/schema.rb
create_table :store_listings, force: true do |t|
t.string :my_string, default: "original default"
end
StoreListing.new.my_string # => "original default"
# app/models/store_listing.rb
class StoreListing < ActiveRecord::Base
attribute :my_string, :string, default: "new default"
end
StoreListing.new.my_string # => "new default"
class Product < ActiveRecord::Base
attribute :my_default_proc, :datetime, default: -> { Time.now }
end
Product.new.my_default_proc # => 2015-05-30 11:04:48 -0600
sleep 1
Product.new.my_default_proc # => 2015-05-30 11:04:49 -0600
I had a similar challenge when working on a Rails 6 application.
Here's how I solved it:
I have a Users table and a Roles table. The Users table belongs to the Roles table. I also have an Admin and Student Models that inherit from the Users table.
It then required that I set a default value for the role whenever a user is created, say admin role that has an id = 1 or student role that has an id = 2.
class User::Admin < User
before_save :default_values
def default_values
# set role_id to '1' except if role_id is not empty
return self.role_id = '1' unless role_id.nil?
end
end
This means that before an admin user is created/saved in the database the role_id is set to a default of 1 if it is not empty.
return self.role_id = '1' unless role_id.nil?
is the same as:
return self.role_id = '1' unless self.role_id.nil?
and the same as:
self.role_id = '1' if role_id.nil?
but the first one is cleaner and more precise.
That's all.
I hope this helps
Been using this for a while.
# post.rb
class Post < ApplicationRecord
attribute :country, :string, default: 'ID'
end
use default_scope in rails 3
api doc
ActiveRecord obscures the difference between defaulting defined in the database (schema) and defaulting done in the application (model). During initialization, it parses the database schema and notes any default values specified there. Later, when creating objects, it assigns those schema-specified default values without touching the database.
discussion
From the api docs http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
Use the before_validation method in your model, it gives you the options of creating specific initialisation for create and update calls
e.g. in this example (again code taken from the api docs example) the number field is initialised for a credit card. You can easily adapt this to set whatever values you want
class CreditCard < ActiveRecord::Base
# Strip everything but digits, so the user can specify "555 234 34" or
# "5552-3434" or both will mean "55523434"
before_validation(:on => :create) do
self.number = number.gsub(%r[^0-9]/, "") if attribute_present?("number")
end
end
class Subscription < ActiveRecord::Base
before_create :record_signup
private
def record_signup
self.signed_up_on = Date.today
end
end
class Firm < ActiveRecord::Base
# Destroys the associated clients and people when the firm is destroyed
before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
end
Surprised that his has not been suggested here

Resources