aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record')
-rw-r--r--activerecord/lib/active_record/associations/association.rb13
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb5
-rw-r--r--activerecord/lib/active_record/autosave_association.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb39
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/core.rb2
-rw-r--r--activerecord/lib/active_record/counter_cache.rb2
-rw-r--r--activerecord/lib/active_record/fixtures.rb4
-rw-r--r--activerecord/lib/active_record/identity_map.rb144
-rw-r--r--activerecord/lib/active_record/inheritance.rb23
-rw-r--r--activerecord/lib/active_record/model.rb1
-rw-r--r--activerecord/lib/active_record/model_schema.rb3
-rw-r--r--activerecord/lib/active_record/persistence.rb16
-rw-r--r--activerecord/lib/active_record/railtie.rb5
-rw-r--r--activerecord/lib/active_record/relation.rb15
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb10
-rw-r--r--activerecord/lib/active_record/test_case.rb10
-rw-r--r--activerecord/lib/active_record/transactions.rb2
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb20
19 files changed, 55 insertions, 267 deletions
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index 512c52338e..fb0ca15c23 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -45,7 +45,6 @@ module ActiveRecord
# Resets the \loaded flag to +false+ and sets the \target to +nil+.
def reset
@loaded = false
- IdentityMap.remove(target) if IdentityMap.enabled? && target
@target = nil
end
@@ -135,17 +134,7 @@ module ActiveRecord
# ActiveRecord::RecordNotFound is rescued within the method, and it is
# not reraised. The proxy is \reset and +nil+ is the return value.
def load_target
- if find_target?
- begin
- if IdentityMap.enabled? && association_class && association_class.respond_to?(:base_class)
- @target = IdentityMap.get(association_class, owner[reflection.foreign_key])
- end
- rescue NameError
- nil
- ensure
- @target ||= find_target
- end
- end
+ @target ||= find_target if find_target?
loaded! unless loaded?
target
rescue ActiveRecord::RecordNotFound
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index 3a737e5b35..11c63591e3 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -22,8 +22,6 @@ module ActiveRecord
if status = super
@previously_changed = changes
@changed_attributes.clear
- elsif IdentityMap.enabled?
- IdentityMap.remove(self)
end
status
end
@@ -34,9 +32,6 @@ module ActiveRecord
@previously_changed = changes
@changed_attributes.clear
end
- rescue
- IdentityMap.remove(self) if IdentityMap.enabled?
- raise
end
# <tt>reload</tt> the record and clears changed attributes.
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index eb3ae7014e..be0af081d1 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -328,7 +328,6 @@ module ActiveRecord
autosave = reflection.options[:autosave]
if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
- begin
records.each do |record|
next if record.destroyed?
@@ -348,11 +347,6 @@ module ActiveRecord
raise ActiveRecord::Rollback unless saved
end
- rescue
- records.each {|x| IdentityMap.remove(x) } if IdentityMap.enabled?
- raise
- end
-
end
# reconstruct the scope now that we know the owner's id
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 767c99de4c..1d713e472b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -284,26 +284,25 @@ module ActiveRecord
protected
- def log(sql, name = "SQL", binds = [])
- @instrumenter.instrument(
- "sql.active_record",
- :sql => sql,
- :name => name,
- :connection_id => object_id,
- :binds => binds) { yield }
- rescue Exception => e
- message = "#{e.class.name}: #{e.message}: #{sql}"
- @logger.debug message if @logger
- exception = translate_exception(e, message)
- exception.set_backtrace e.backtrace
- raise exception
- end
-
- def translate_exception(e, message)
- # override in derived class
- ActiveRecord::StatementInvalid.new(message)
- end
-
+ def log(sql, name = "SQL", binds = [])
+ @instrumenter.instrument(
+ "sql.active_record",
+ :sql => sql,
+ :name => name,
+ :connection_id => object_id,
+ :binds => binds) { yield }
+ rescue Exception => e
+ message = "#{e.class.name}: #{e.message}: #{sql}"
+ @logger.error message if @logger
+ exception = translate_exception(e, message)
+ exception.set_backtrace e.backtrace
+ raise exception
+ end
+
+ def translate_exception(e, message)
+ # override in derived class
+ ActiveRecord::StatementInvalid.new(message)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 5b7fa029da..10a178e369 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -1006,7 +1006,7 @@ module ActiveRecord
# This should be not be called manually but set in database.yml.
def schema_search_path=(schema_csv)
if schema_csv
- execute "SET search_path TO #{schema_csv}"
+ execute("SET search_path TO #{schema_csv}", 'SCHEMA')
@schema_search_path = schema_csv
end
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 46aababfb6..9a2f859fc7 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -176,7 +176,7 @@ module ActiveRecord
assign_attributes(attributes, options) if attributes
yield self if block_given?
- run_callbacks :initialize
+ run_callbacks :initialize if _initialize_callbacks.any?
end
# Initialize an empty model object from +coder+. +coder+ must contain
diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb
index 8d5388e1f3..f52979ebd9 100644
--- a/activerecord/lib/active_record/counter_cache.rb
+++ b/activerecord/lib/active_record/counter_cache.rb
@@ -69,8 +69,6 @@ module ActiveRecord
"#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
end
- IdentityMap.remove_by_id(symbolized_base_class, id) if IdentityMap.enabled?
-
update_all(updates.join(', '), primary_key => id)
end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index b82d5b5621..5c42e8f719 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -796,9 +796,7 @@ module ActiveRecord
@fixture_cache[fixture_name].delete(fixture) if force_reload
if @loaded_fixtures[fixture_name][fixture.to_s]
- ActiveRecord::IdentityMap.without do
- @fixture_cache[fixture_name][fixture] ||= @loaded_fixtures[fixture_name][fixture.to_s].find
- end
+ @fixture_cache[fixture_name][fixture] ||= @loaded_fixtures[fixture_name][fixture.to_s].find
else
raise StandardError, "No entry named '#{fixture}' found for fixture collection '#{fixture_name}'"
end
diff --git a/activerecord/lib/active_record/identity_map.rb b/activerecord/lib/active_record/identity_map.rb
deleted file mode 100644
index d9777bb2f6..0000000000
--- a/activerecord/lib/active_record/identity_map.rb
+++ /dev/null
@@ -1,144 +0,0 @@
-module ActiveRecord
- # = Active Record Identity Map
- #
- # Ensures that each object gets loaded only once by keeping every loaded
- # object in a map. Looks up objects using the map when referring to them.
- #
- # More information on Identity Map pattern:
- # http://www.martinfowler.com/eaaCatalog/identityMap.html
- #
- # == Configuration
- #
- # In order to enable IdentityMap, set <tt>config.active_record.identity_map = true</tt>
- # in your <tt>config/application.rb</tt> file.
- #
- # IdentityMap is disabled by default and still in development (i.e. use it with care).
- #
- # == Associations
- #
- # Active Record Identity Map does not track associations yet. For example:
- #
- # comment = @post.comments.first
- # comment.post = nil
- # @post.comments.include?(comment) #=> true
- #
- # Ideally, the example above would return false, removing the comment object from the
- # post association when the association is nullified. This may cause side effects, as
- # in the situation below, if Identity Map is enabled:
- #
- # Post.has_many :comments, :dependent => :destroy
- #
- # comment = @post.comments.first
- # comment.post = nil
- # comment.save
- # Post.destroy(@post.id)
- #
- # Without using Identity Map, the code above will destroy the @post object leaving
- # the comment object intact. However, once we enable Identity Map, the post loaded
- # by Post.destroy is exactly the same object as the object @post. As the object @post
- # still has the comment object in @post.comments, once Identity Map is enabled, the
- # comment object will be accidently removed.
- #
- # This inconsistency is meant to be fixed in future Rails releases.
- #
- module IdentityMap
-
- class << self
- def enabled=(flag)
- Thread.current[:identity_map_enabled] = flag
- end
-
- def enabled
- Thread.current[:identity_map_enabled]
- end
- alias enabled? enabled
-
- def repository
- Thread.current[:identity_map] ||= Hash.new { |h,k| h[k] = {} }
- end
-
- def use
- old, self.enabled = enabled, true
-
- yield if block_given?
- ensure
- self.enabled = old
- clear
- end
-
- def without
- old, self.enabled = enabled, false
-
- yield if block_given?
- ensure
- self.enabled = old
- end
-
- def get(klass, primary_key)
- record = repository[klass.symbolized_sti_name][primary_key]
-
- if record.is_a?(klass)
- ActiveSupport::Notifications.instrument("identity.active_record",
- :line => "From Identity Map (id: #{primary_key})",
- :name => "#{klass} Loaded",
- :connection_id => object_id)
-
- record
- else
- nil
- end
- end
-
- def add(record)
- repository[record.class.symbolized_sti_name][record.id] = record
- end
-
- def remove(record)
- repository[record.class.symbolized_sti_name].delete(record.id)
- end
-
- def remove_by_id(symbolized_sti_name, id)
- repository[symbolized_sti_name].delete(id)
- end
-
- def clear
- repository.clear
- end
- end
-
- # Reinitialize an Identity Map model object from +coder+.
- # +coder+ must contain the attributes necessary for initializing an empty
- # model object.
- def reinit_with(coder)
- @attributes_cache = {}
- dirty = @changed_attributes.keys
- attributes = self.class.initialize_attributes(coder['attributes'].except(*dirty))
- @attributes.update(attributes)
- @changed_attributes.update(coder['attributes'].slice(*dirty))
- @changed_attributes.delete_if{|k,v| v.eql? @attributes[k]}
-
- run_callbacks :find
-
- self
- end
-
- class Middleware
- def initialize(app)
- @app = app
- end
-
- def call(env)
- enabled = IdentityMap.enabled
- IdentityMap.enabled = true
-
- response = @app.call(env)
- response[2] = Rack::BodyProxy.new(response[2]) do
- IdentityMap.enabled = enabled
- IdentityMap.clear
- end
-
- response
- end
- end
- end
-end
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index 2c766411a0..ebe244c6a6 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -63,26 +63,9 @@ module ActiveRecord
# single-table inheritance model that makes it possible to create
# objects of different types from the same table.
def instantiate(record, column_types = {})
- sti_class = find_sti_class(record[inheritance_column])
- record_id = sti_class.primary_key && record[sti_class.primary_key]
-
- if ActiveRecord::IdentityMap.enabled? && record_id
- if (column = sti_class.columns_hash[sti_class.primary_key]) && column.number?
- record_id = record_id.to_i
- end
- if instance = IdentityMap.get(sti_class, record_id)
- instance.reinit_with('attributes' => record)
- else
- instance = sti_class.allocate.init_with('attributes' => record)
- IdentityMap.add(instance)
- end
- else
- column_types = sti_class.decorate_columns(column_types)
- instance = sti_class.allocate.init_with('attributes' => record,
- 'column_types' => column_types)
- end
-
- instance
+ sti_class = find_sti_class(record[inheritance_column])
+ column_types = sti_class.decorate_columns(column_types)
+ sti_class.allocate.init_with('attributes' => record, 'column_types' => column_types)
end
# For internal use.
diff --git a/activerecord/lib/active_record/model.rb b/activerecord/lib/active_record/model.rb
index 86de5ab2fa..105d1e0e2b 100644
--- a/activerecord/lib/active_record/model.rb
+++ b/activerecord/lib/active_record/model.rb
@@ -60,7 +60,6 @@ module ActiveRecord
include AttributeMethods
include Callbacks, ActiveModel::Observing, Timestamp
include Associations
- include IdentityMap
include ActiveModel::SecurePassword
include AutosaveAssociation, NestedAttributes
include Aggregations, Transactions, Reflection, Serialization, Store
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index 99847ac161..c85d590ce1 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -160,6 +160,7 @@ module ActiveRecord
# Sets the value of inheritance_column
def inheritance_column=(value)
@inheritance_column = value.to_s
+ @explicit_inheritance_column = true
end
def sequence_name
@@ -303,7 +304,7 @@ module ActiveRecord
@column_types = nil
@content_columns = nil
@dynamic_methods_hash = nil
- @inheritance_column = nil
+ @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
@relation = nil
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 09502bb52c..35c922e979 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -115,10 +115,7 @@ module ActiveRecord
# callbacks, Observer methods, or any <tt>:dependent</tt> association
# options, use <tt>#destroy</tt>.
def delete
- if persisted?
- self.class.delete(id)
- IdentityMap.remove(self) if IdentityMap.enabled?
- end
+ self.class.delete(id) if persisted?
@destroyed = true
freeze
end
@@ -129,7 +126,6 @@ module ActiveRecord
destroy_associations
if persisted?
- IdentityMap.remove(self) if IdentityMap.enabled?
pk = self.class.primary_key
column = self.class.columns_hash[pk]
substitute = connection.substitute_at(column, 0)
@@ -284,11 +280,9 @@ module ActiveRecord
clear_aggregation_cache
clear_association_cache
- IdentityMap.without do
- fresh_object = self.class.unscoped { self.class.find(id, options) }
- @attributes.update(fresh_object.instance_variable_get('@attributes'))
- @columns_hash = fresh_object.instance_variable_get('@columns_hash')
- end
+ fresh_object = self.class.unscoped { self.class.find(id, options) }
+ @attributes.update(fresh_object.instance_variable_get('@attributes'))
+ @columns_hash = fresh_object.instance_variable_get('@columns_hash')
@attributes_cache = {}
self
@@ -363,10 +357,8 @@ module ActiveRecord
attributes_values = arel_attributes_with_values_for_create(!id.nil?)
new_id = self.class.unscoped.insert attributes_values
-
self.id ||= new_id if self.class.primary_key
- IdentityMap.add(self) if IdentityMap.enabled?
@new_record = false
id
end
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 516c450f85..ee3a6bf8c0 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -53,11 +53,6 @@ module ActiveRecord
ActiveSupport.on_load(:active_record) { self.logger ||= ::Rails.logger }
end
- initializer "active_record.identity_map" do |app|
- config.app_middleware.insert_after "::ActionDispatch::Callbacks",
- "ActiveRecord::IdentityMap::Middleware" if config.active_record.delete(:identity_map)
- end
-
initializer "active_record.set_configs" do |app|
ActiveSupport.on_load(:active_record) do
if app.config.active_record.delete(:whitelist_attributes)
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 7531e1fe6f..ae2dc6872a 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -168,13 +168,7 @@ module ActiveRecord
default_scoped = with_default_scope
if default_scoped.equal?(self)
- @records = if @readonly_value.nil? && !@klass.locking_enabled?
- eager_loading? ? find_with_associations : @klass.find_by_sql(arel, @bind_values)
- else
- IdentityMap.without do
- eager_loading? ? find_with_associations : @klass.find_by_sql(arel, @bind_values)
- end
- end
+ @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, @bind_values)
preload = @preload_values
preload += @includes_values unless eager_loading?
@@ -274,7 +268,6 @@ module ActiveRecord
# # The same idea applies to limit and order
# Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(:author => 'David')
def update_all(updates, conditions = nil, options = {})
- IdentityMap.repository[symbolized_base_class].clear if IdentityMap.enabled?
if conditions || options.present?
where(conditions).apply_finder_options(options.slice(:limit, :order)).update_all(updates)
else
@@ -404,7 +397,6 @@ module ActiveRecord
# If you need to destroy dependent associations or call your <tt>before_*</tt> or
# +after_destroy+ callbacks, use the +destroy_all+ method instead.
def delete_all(conditions = nil)
- IdentityMap.repository[symbolized_base_class] = {} if IdentityMap.enabled?
if conditions
where(conditions).delete_all
else
@@ -437,7 +429,6 @@ module ActiveRecord
# # Delete multiple rows
# Todo.delete([2,3,4])
def delete(id_or_array)
- IdentityMap.remove_by_id(self.symbolized_base_class, id_or_array) if IdentityMap.enabled?
where(primary_key => id_or_array).delete_all
end
@@ -516,6 +507,10 @@ module ActiveRecord
end
end
+ def blank?
+ to_a.blank?
+ end
+
private
def references_eager_loaded_tables?
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index adfacf37ee..52e67ecc6e 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -318,17 +318,7 @@ module ActiveRecord
def find_one(id)
id = id.id if ActiveRecord::Base === id
- if IdentityMap.enabled? && where_values.blank? &&
- limit_value.blank? && order_values.blank? &&
- includes_values.blank? && preload_values.blank? &&
- readonly_value.nil? && joins_values.blank? &&
- !@klass.locking_enabled? &&
- record = IdentityMap.get(@klass, id)
- return record
- end
-
column = columns_hash[primary_key]
-
substitute = connection.substitute_at(column, @bind_values.length)
relation = where(table[primary_key].eq(substitute))
relation.bind_values += [[column, id]]
diff --git a/activerecord/lib/active_record/test_case.rb b/activerecord/lib/active_record/test_case.rb
index 4d881f0f7d..fcaa4b74a6 100644
--- a/activerecord/lib/active_record/test_case.rb
+++ b/activerecord/lib/active_record/test_case.rb
@@ -7,20 +7,10 @@ module ActiveRecord
#
# Defines some test assertions to test against SQL queries.
class TestCase < ActiveSupport::TestCase #:nodoc:
- setup :cleanup_identity_map
-
- def setup
- cleanup_identity_map
- end
-
def teardown
SQLCounter.log.clear
end
- def cleanup_identity_map
- ActiveRecord::IdentityMap.clear
- end
-
def assert_date_from_db(expected, actual, message = nil)
# SybaseAdapter doesn't have a separate column type just for dates,
# so the time is in the string and incorrectly formatted
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index b492377d18..743dfc5a38 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -251,7 +251,6 @@ module ActiveRecord
remember_transaction_record_state
yield
rescue Exception
- IdentityMap.remove(self) if IdentityMap.enabled?
restore_transaction_record_state
raise
ensure
@@ -270,7 +269,6 @@ module ActiveRecord
def rolledback!(force_restore_state = false) #:nodoc:
run_callbacks :rollback
ensure
- IdentityMap.remove(self) if IdentityMap.enabled?
restore_transaction_record_state(force_restore_state)
end
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 9556878f63..db618f617f 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -35,8 +35,14 @@ module ActiveRecord
relation = relation.and(table[scope_item].eq(scope_value))
end
- if finder_class.unscoped.where(relation).exists?
- record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
+ relation = finder_class.unscoped.where(relation)
+
+ if options[:conditions]
+ relation = relation.merge(options[:conditions])
+ end
+
+ if relation.exists?
+ record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope, :conditions).merge(:value => value))
end
end
@@ -102,6 +108,14 @@ module ActiveRecord
# validates_uniqueness_of :teacher_id, :scope => [:semester_id, :class_id]
# end
#
+ # It is also possible to limit the uniqueness constraint to a set of records matching certain conditions.
+ # In this example archived articles are not being taken into consideration when validating uniqueness
+ # of the title attribute:
+ #
+ # class Article < ActiveRecord::Base
+ # validates_uniqueness_of :title, :conditions => where('status != ?', 'archived')
+ # end
+ #
# When the record is created, a check is performed to make sure that no record exists in the database
# with the given value for the specified attribute (that maps to a column). When the record is updated,
# the same check is made but disregarding the record itself.
@@ -109,6 +123,8 @@ module ActiveRecord
# Configuration options:
# * <tt>:message</tt> - Specifies a custom error message (default is: "has already been taken").
# * <tt>:scope</tt> - One or more columns by which to limit the scope of the uniqueness constraint.
+ # * <tt>:conditions</tt> - Specify the conditions to be included as a <tt>WHERE</tt> SQL fragment to limit
+ # the uniqueness constraint lookup. (e.g. <tt>:conditions => where('status = ?', 'active')</tt>)
# * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by non-text columns (+true+ by default).
# * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).