Custom Rails Validation - multiple fields simultaneously - ruby-on-rails

I'm looking to create a custom validation in Rails. I need to validate that a POSTed start_date_time and end_date_time (together) do not overlap that combination in the database.
Example:
In the database:
start_date
05/15/2000
end_date
05/30/2000
POSTed:
start_date
05/10/2000
end_date
05/20/2000
FAILS!
Here's the rub:
1) I want to send both start and end fields into the function
2) I want to get the values of both POSTed fields to use in building a query.
3) Bonus: I want to use a scope (like say, for a given [:user_id, :event] -- but, again, I want that to be passed in.
How do I get the values of the fields?
Let's say my function looks like this:
def self.validates_datetime_not_overlapping(start, finish, scope_attr=[], conf={})
config = {
:message => 'some default message'
}
config.update(conf)
# Now what?
end
I'm sort of stuck at this point. I've scoured the net, and can't figure it out.... I can get the value of either start or finish, but not both at the same time by using validate_each ...
Any help would be great!
Thanks :)

What about custom validation methods?
You can add:
validate :check_dates
def check_dates
do.whatever.you.want.with.any.field
end
EDIT:
So maybe validate_with?
Modified example from RoR Guides:
class Person < ActiveRecord::Base
validates_with DatesValidator, :start => :your_start_date, :stop => :your_stop_date
end
class DatesValidator < ActiveRecord::Validator
def validate
start = record.send(options[:start])
stop = record.send(options[:stop])
...
end
end

Related

Rails validation / scope on 24 hour period

I'm trying to put a validation on a record. The validation will check that the record can't be created if the ip_address and post_id are the same. This works good.
I am trying to add another condition that will allow this duplication only if it is after a 24 hour period, if so, allow it to save, but all future saves will be disabled again until the 24 period is over.
Here has been my best attempt so far:
validates_uniqueness_of :ip_address, scope: :impressionable_id,
conditions: -> {where('created_at < ?', Time.now - 24.hours)}
So the validation should somehow only check the "latest" record of the group it finds to do the validation to be accurate
Thank you!
It might be easier to just make an explicit validator method for this:
class Foo < ActiveRecord::Base
validate :ip_address_uniqueness, on: :create
# ...
def ip_address_uniqueness
existing = Foo.where(impressionable_id: self.impressionable_id)
.where(ip_address: self.ip_address)
.where(created_at: Time.current.all_day)
errors.add(:ip_address, 'cannot be used again today') if existing
end
end

Creating a Rails change log

I am pretty new to rails (and development) and have a requirement to create a change log. Let's say you have an employees table. On that table you have an employee reference number, a first name, and a last name. When either the first name or last name changes, I need to log it to a table somewhere for later reporting. I only need to log the change, so if employee ref 1 changes from Bill to Bob, then I need to put the reference number and first name into a table. The change table can have all the columns that mnight change, but most only be populated with the reference number and the changed field. I don't need the previous value either, just the new one. hope that makes sense.
Looked at gems such as paper trail, but they seem very complicated for what I need. I don't ever need to manipulate the model or move versions etc, I just need to track which fields have changed, when, and by whom.
I'd appreciate your recommendations.
If you insist on building your own changelog, based on your requirements you can do so using a few callbacks. First create your log table:
def up
create_table :employee_change_logs do |t|
t.references :employee
# as per your spec - copy all column definitions from your employees table
end
end
In your Employee model:
class Employee < ActiveRecord::Base
has_many :employee_change_logs
before_update :capture_changed_columns
after_update :log_changed_columns
# capture the changes before the update occurs
def capture_changed_columns
#changed_columns = changed
end
def log_changed_columns
return if #changed_columns.empty?
log_entry = employee_change_logs.build
#changed_columns.each{|c| log_entry.send(:"#{c}=", self.send(c))}
log_entry.save!
end
end
I recommend the gem vestal_versions.
To version an ActiveRecord model, simply add versioned to your class like so:
class User < ActiveRecord::Base
versioned
validates_presence_of :first_name, :last_name
def name
"#{first_name} #{last_name}"
end
end
And use like this:
#user.update_attributes(:last_name => "Jobs", :updated_by => "Tyler")
#user.version # => 2
#user.versions.last.user # => "Tyler"
The first thing we did was put an around filter in the application controller. This was how I get the current_employee into the employee model, which was the challenge, especially for a newbie like me!
around_filter :set_employee_for_log, :if => Proc.new { #current_account &&
#current_account.log_employee_changes? && #current_employee }
def set_employee_for_log
Thread.current[:current_employee] = #current_employee.id
begin
yield
ensure
Thread.current[:current_employee ] = nil
end
end
end
Next, in the employee model I defined which fields I was interested in monitoring
CHECK_FIELDS = ['first_name', 'last_name', 'middle_name']
then I added some hooks to actually capture the changes IF logging is enabled at the account level
before_update :capture_changed_columns
after_update :log_changed_columns, :if => Proc.new { self.account.log_employee_changes? }
def capture_changed_columns
#changed_columns = changed
#changes = changes
end
def log_changed_columns
e = EmployeeChangeLog.new
Employee::CHECK_FIELDS.each do |field|
if self.send("#{field}_changed?")
e.send("#{field}=", self.send(field))
end
end
if e.changed?
e.update_attribute(:account_id, self.account.id)
e.update_attribute(:employee_id, self.id)
e.update_attribute(:employee_ref, self.employee_ref)
e.update_attribute(:user_id, Thread.current[:current_employee])
e.save
else return
end
end
And that;s it. If the account enables it, the app keeps an eye on specific fields and then all changes to those fields are logged to a table, creating an simple audit trail.

Advice on "Dynamic" Model validation

I have a model named Calendar.
The validations that will be applied to it varies from the selections made by the user.
I know that I can use custom validation + conditional validation to do this, but doesn't look very clean to me.
I wonder if I can store it on a database column and pass it to a "generic" validator method.
What do you think?
Explaining further:
A user has a calendar.
Other users that have access to this calendar, can schedule appointments.
To schedule an appointment the app should validate according to the rules defined by the calendar's owner.
There are many combinations, so what I came to is:
Create custom validator classes to each of the possible validations and make then conditional.
class Calendar
validate_allowed_in_hollydays :appointment_date if :allowedinhollydays?
(tenths of other cases)
...
end
This works, but feels wrong.
I'm thinking about storing somewhere which rules should be applied to that calendar and then doing something like:
validate_stored_rules :appointment_date
It seems a little backwards to save the data in the database and then validate it.
I think your initial thought of going with some custom validation is the best bet. validates_with looks like your best option. You could then create a separate class and build all the validation inside that to keep it separate from your main model.
class Person < ActiveRecord::Base
validates_with GoodnessValidator
end
class GoodnessValidator < ActiveModel::Validator
def validate
if record.first_name == "Evil"
record.errors[:base] << "This person is evil"
end
end
end
Code lifted straight from the Rails Validation Guide
you should use with_options it allows to put default options into your code:
class User < ActiveRecord::Base
with_options :if => :is_admin do |admin|
admin.validates_length_of :password, :minimum => 10
end
end
in the example is_admin might be an database column, attr_accessor or an method
Thank you all for your help.
I've got it working like this:
def after_initialize
singleton = class << self; self; end
validations = eval(calendar.cofig)
validations.each do |val|
singleton.class_eval(val)
end
end

In Rails, How do I validates_uniqueness_of :field with a scope of last 6 months

First Item
I Want to validate a field to make sure it is unique (in the last 6 months) before saving it to the database.
I am thinking I should use validates_uniqueness_of :field, case_sensitive => false, Scope => ...
For my application it only has to be unique if, it was used <6 months ago.
Thinking to compare it to created_at, but don't really know how to go about it.
Second Item
I think I should somehow use .strip to remove any spaces before or after the text that the use may have put in accidentally (I know that these extra spaces are used by default in rails and if they are there can make a filed unique.)
If anyone has any hints on how this should be done correctly I really would appreciate it.
validates_uniqueness_of works by checking if a record already exists with the same value of the given field within the given scope. :scope lets you define the scope (obviously) of the uniqueness; for instance, if I was creating blog software and wanted to only allow a post title to be used once per blog, I could say validates_uniqueness_of :title, :scope => :blog_id -- without the scope, I'd only be allowing each title to be used once across the entire system. :scope won't let you do a complex check, like that which you desire.
What you're probably need to do is create your own validation function to check the uniqueness of the field in question within the given timeframe (code goes within the model):
validate :field_must_be_unique_within_six_months
def field_must_be_unique_within_six_months
return if field.blank?
num_duplicates = self.class.count(:conditions => ["field = ? AND created_at < ?", self.field, 6.months.ago])
if num_duplicates > 0
errors.add(:field, :taken)
end
end
The field_must_be_unique_within_six_months method will work similarly to validates_uniqueness_of, in that it will add an error message if there is already a record with the same given field, but with the added condition that it will also check the date. The validate :field_must_be_unique_within_six_months will add the method to the validation process when a record is saved.
To validate multiple fields at the same time without violating DRY, you could use validates_each to do something like the following:
validates_each :field1, :field2 do |record, attr, value|
if record.class.exists?(["#{attr.to_s} = ? AND created_at < ?", value, 6.months.ago])
errors.add(attr, :taken)
end
end
In the above block, record is the record being validated, attr is the attribute (so field1, field2, etc.) and value is the value of that attribute.
You can probably do something like this:
def validate
errors.add(:field, 'blah blah') if is_used_recently && !has_unique_field?
end
def has_unique_field?
Model.exists?(['field = ? and created_at > ?', self.field, 6.months.ago])
end
def is_used_recently
self.created_at < 6.months.ago || self.new? # i don't know if created_at would be set by this point
end
Alternatively you might want to create a new validation handler, or extend the existing one to pass in a :within option if that's something you're going to be doing often.
To get rid of leading and trailing white space the method you want is 'strip'. You can run this on all your fields by doing something like:
before_validation :clean_up_whitespace
def clean_up_whitespace
self.some_field.strip! # this does the strip in place
end
I hope this helps, let me know if I've made any mistakes!

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