activerecord 2.0.5 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +168 -6
- data/README +27 -22
- data/RUNNING_UNIT_TESTS +7 -4
- data/Rakefile +22 -25
- data/lib/active_record.rb +8 -2
- data/lib/active_record/aggregations.rb +21 -12
- data/lib/active_record/association_preload.rb +277 -0
- data/lib/active_record/associations.rb +481 -295
- data/lib/active_record/associations/association_collection.rb +162 -37
- data/lib/active_record/associations/association_proxy.rb +71 -7
- data/lib/active_record/associations/belongs_to_association.rb +5 -3
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +5 -6
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -64
- data/lib/active_record/associations/has_many_association.rb +8 -73
- data/lib/active_record/associations/has_many_through_association.rb +68 -117
- data/lib/active_record/associations/has_one_association.rb +7 -5
- data/lib/active_record/associations/has_one_through_association.rb +28 -0
- data/lib/active_record/attribute_methods.rb +69 -19
- data/lib/active_record/base.rb +496 -275
- data/lib/active_record/calculations.rb +28 -21
- data/lib/active_record/callbacks.rb +9 -38
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -2
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +232 -45
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +141 -27
- data/lib/active_record/connection_adapters/abstract_adapter.rb +9 -13
- data/lib/active_record/connection_adapters/mysql_adapter.rb +57 -24
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +143 -42
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +18 -10
- data/lib/active_record/dirty.rb +158 -0
- data/lib/active_record/fixtures.rb +121 -156
- data/lib/active_record/locking/optimistic.rb +14 -11
- data/lib/active_record/locking/pessimistic.rb +2 -2
- data/lib/active_record/migration.rb +157 -77
- data/lib/active_record/named_scope.rb +163 -0
- data/lib/active_record/observer.rb +19 -5
- data/lib/active_record/reflection.rb +34 -14
- data/lib/active_record/schema.rb +7 -14
- data/lib/active_record/schema_dumper.rb +4 -4
- data/lib/active_record/serialization.rb +5 -5
- data/lib/active_record/serializers/json_serializer.rb +37 -28
- data/lib/active_record/serializers/xml_serializer.rb +52 -29
- data/lib/active_record/test_case.rb +36 -0
- data/lib/active_record/timestamp.rb +4 -4
- data/lib/active_record/transactions.rb +3 -3
- data/lib/active_record/validations.rb +182 -248
- data/lib/active_record/version.rb +2 -2
- data/test/{fixtures → assets}/example.log +0 -0
- data/test/{fixtures → assets}/flowers.jpg +0 -0
- data/test/cases/aaa_create_tables_test.rb +24 -0
- data/test/cases/active_schema_test_mysql.rb +95 -0
- data/test/cases/active_schema_test_postgresql.rb +24 -0
- data/test/{adapter_test.rb → cases/adapter_test.rb} +15 -14
- data/test/{adapter_test_sqlserver.rb → cases/adapter_test_sqlserver.rb} +95 -95
- data/test/{aggregations_test.rb → cases/aggregations_test.rb} +20 -20
- data/test/{ar_schema_test.rb → cases/ar_schema_test.rb} +6 -6
- data/test/cases/associations/belongs_to_associations_test.rb +412 -0
- data/test/{associations → cases/associations}/callbacks_test.rb +24 -10
- data/test/{associations → cases/associations}/cascaded_eager_loading_test.rb +18 -17
- data/test/cases/associations/eager_load_nested_include_test.rb +83 -0
- data/test/{associations → cases/associations}/eager_singularization_test.rb +5 -5
- data/test/{associations → cases/associations}/eager_test.rb +216 -51
- data/test/{associations → cases/associations}/extension_test.rb +8 -8
- data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +684 -0
- data/test/cases/associations/has_many_associations_test.rb +932 -0
- data/test/cases/associations/has_many_through_associations_test.rb +190 -0
- data/test/cases/associations/has_one_associations_test.rb +323 -0
- data/test/cases/associations/has_one_through_associations_test.rb +74 -0
- data/test/{associations → cases/associations}/inner_join_association_test.rb +20 -20
- data/test/{associations → cases/associations}/join_model_test.rb +175 -35
- data/test/cases/associations_test.rb +262 -0
- data/test/{attribute_methods_test.rb → cases/attribute_methods_test.rb} +103 -11
- data/test/{base_test.rb → cases/base_test.rb} +338 -191
- data/test/{binary_test.rb → cases/binary_test.rb} +6 -4
- data/test/{calculations_test.rb → cases/calculations_test.rb} +35 -23
- data/test/{callbacks_test.rb → cases/callbacks_test.rb} +7 -7
- data/test/{class_inheritable_attributes_test.rb → cases/class_inheritable_attributes_test.rb} +3 -3
- data/test/{column_alias_test.rb → cases/column_alias_test.rb} +3 -3
- data/test/{connection_test_firebird.rb → cases/connection_test_firebird.rb} +2 -2
- data/test/{connection_test_mysql.rb → cases/connection_test_mysql.rb} +2 -2
- data/test/{copy_table_test_sqlite.rb → cases/copy_table_test_sqlite.rb} +13 -13
- data/test/{datatype_test_postgresql.rb → cases/datatype_test_postgresql.rb} +8 -8
- data/test/{date_time_test.rb → cases/date_time_test.rb} +5 -5
- data/test/{default_test_firebird.rb → cases/default_test_firebird.rb} +3 -3
- data/test/{defaults_test.rb → cases/defaults_test.rb} +8 -6
- data/test/{deprecated_finder_test.rb → cases/deprecated_finder_test.rb} +3 -3
- data/test/cases/dirty_test.rb +163 -0
- data/test/cases/finder_respond_to_test.rb +76 -0
- data/test/{finder_test.rb → cases/finder_test.rb} +266 -33
- data/test/{fixtures_test.rb → cases/fixtures_test.rb} +88 -72
- data/test/cases/helper.rb +47 -0
- data/test/{inheritance_test.rb → cases/inheritance_test.rb} +61 -17
- data/test/cases/invalid_date_test.rb +24 -0
- data/test/{json_serialization_test.rb → cases/json_serialization_test.rb} +36 -11
- data/test/{lifecycle_test.rb → cases/lifecycle_test.rb} +16 -13
- data/test/{locking_test.rb → cases/locking_test.rb} +17 -10
- data/test/{method_scoping_test.rb → cases/method_scoping_test.rb} +75 -39
- data/test/{migration_test.rb → cases/migration_test.rb} +420 -80
- data/test/{migration_test_firebird.rb → cases/migration_test_firebird.rb} +3 -3
- data/test/{mixin_test.rb → cases/mixin_test.rb} +7 -6
- data/test/{modules_test.rb → cases/modules_test.rb} +11 -6
- data/test/{multiple_db_test.rb → cases/multiple_db_test.rb} +5 -5
- data/test/cases/named_scope_test.rb +157 -0
- data/test/{pk_test.rb → cases/pk_test.rb} +10 -10
- data/test/{query_cache_test.rb → cases/query_cache_test.rb} +12 -10
- data/test/{readonly_test.rb → cases/readonly_test.rb} +11 -11
- data/test/{reflection_test.rb → cases/reflection_test.rb} +15 -14
- data/test/{reserved_word_test_mysql.rb → cases/reserved_word_test_mysql.rb} +4 -5
- data/test/{schema_authorization_test_postgresql.rb → cases/schema_authorization_test_postgresql.rb} +5 -5
- data/test/cases/schema_dumper_test.rb +138 -0
- data/test/cases/schema_test_postgresql.rb +102 -0
- data/test/{serialization_test.rb → cases/serialization_test.rb} +7 -7
- data/test/{synonym_test_oracle.rb → cases/synonym_test_oracle.rb} +5 -5
- data/test/{table_name_test_sqlserver.rb → cases/table_name_test_sqlserver.rb} +3 -3
- data/test/{threaded_connections_test.rb → cases/threaded_connections_test.rb} +7 -7
- data/test/{transactions_test.rb → cases/transactions_test.rb} +31 -5
- data/test/{unconnected_test.rb → cases/unconnected_test.rb} +2 -2
- data/test/{validations_test.rb → cases/validations_test.rb} +141 -39
- data/test/{xml_serialization_test.rb → cases/xml_serialization_test.rb} +12 -12
- data/test/config.rb +5 -0
- data/test/connections/native_db2/connection.rb +1 -1
- data/test/connections/native_firebird/connection.rb +1 -1
- data/test/connections/native_frontbase/connection.rb +1 -1
- data/test/connections/native_mysql/connection.rb +1 -1
- data/test/connections/native_openbase/connection.rb +1 -1
- data/test/connections/native_oracle/connection.rb +1 -1
- data/test/connections/native_postgresql/connection.rb +1 -3
- data/test/connections/native_sqlite/connection.rb +2 -2
- data/test/connections/native_sqlite3/connection.rb +2 -2
- data/test/connections/native_sqlite3/in_memory_connection.rb +3 -3
- data/test/connections/native_sybase/connection.rb +1 -1
- data/test/fixtures/author_addresses.yml +5 -0
- data/test/fixtures/authors.yml +2 -0
- data/test/fixtures/clubs.yml +6 -0
- data/test/fixtures/jobs.yml +7 -0
- data/test/fixtures/members.yml +4 -0
- data/test/fixtures/memberships.yml +20 -0
- data/test/fixtures/owners.yml +7 -0
- data/test/fixtures/people.yml +4 -1
- data/test/fixtures/pets.yml +14 -0
- data/test/fixtures/posts.yml +1 -0
- data/test/fixtures/price_estimates.yml +7 -0
- data/test/fixtures/readers.yml +5 -0
- data/test/fixtures/references.yml +17 -0
- data/test/fixtures/sponsors.yml +9 -0
- data/test/fixtures/subscribers.yml +7 -0
- data/test/fixtures/subscriptions.yml +12 -0
- data/test/fixtures/taggings.yml +4 -1
- data/test/fixtures/topics.yml +22 -2
- data/test/fixtures/warehouse-things.yml +3 -0
- data/test/{fixtures/migrations_with_decimal → migrations/decimal}/1_give_me_big_numbers.rb +0 -0
- data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/1_people_have_last_names.rb +1 -1
- data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/2_we_need_reminders.rb +1 -1
- data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/3_foo.rb +0 -0
- data/test/{fixtures/migrations → migrations/duplicate}/3_innocent_jointable.rb +0 -0
- data/test/migrations/duplicate_names/20080507052938_chunky.rb +7 -0
- data/test/migrations/duplicate_names/20080507053028_chunky.rb +7 -0
- data/test/{fixtures/migrations_with_duplicate → migrations/interleaved/pass_1}/3_innocent_jointable.rb +0 -0
- data/test/{fixtures/migrations → migrations/interleaved/pass_2}/1_people_have_last_names.rb +1 -1
- data/test/{fixtures/migrations_with_missing_versions/4_innocent_jointable.rb → migrations/interleaved/pass_2/3_innocent_jointable.rb} +0 -0
- data/test/{fixtures/migrations_with_missing_versions → migrations/interleaved/pass_3}/1_people_have_last_names.rb +1 -1
- data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +8 -0
- data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +12 -0
- data/test/{fixtures/migrations_with_missing_versions → migrations/missing}/1000_people_have_middle_names.rb +1 -1
- data/test/migrations/missing/1_people_have_last_names.rb +9 -0
- data/test/{fixtures/migrations_with_missing_versions → migrations/missing}/3_we_need_reminders.rb +1 -1
- data/test/migrations/missing/4_innocent_jointable.rb +12 -0
- data/test/migrations/valid/1_people_have_last_names.rb +9 -0
- data/test/{fixtures/migrations → migrations/valid}/2_we_need_reminders.rb +1 -1
- data/test/migrations/valid/3_innocent_jointable.rb +12 -0
- data/test/{fixtures → models}/author.rb +28 -4
- data/test/{fixtures → models}/auto_id.rb +0 -0
- data/test/{fixtures → models}/binary.rb +0 -0
- data/test/{fixtures → models}/book.rb +0 -0
- data/test/{fixtures → models}/categorization.rb +0 -0
- data/test/{fixtures → models}/category.rb +8 -5
- data/test/{fixtures → models}/citation.rb +0 -0
- data/test/models/club.rb +7 -0
- data/test/{fixtures → models}/column_name.rb +0 -0
- data/test/{fixtures → models}/comment.rb +5 -3
- data/test/{fixtures → models}/company.rb +15 -6
- data/test/{fixtures → models}/company_in_module.rb +5 -3
- data/test/{fixtures → models}/computer.rb +0 -1
- data/test/{fixtures → models}/contact.rb +1 -1
- data/test/{fixtures → models}/course.rb +0 -0
- data/test/{fixtures → models}/customer.rb +8 -8
- data/test/{fixtures → models}/default.rb +0 -0
- data/test/{fixtures → models}/developer.rb +14 -10
- data/test/{fixtures → models}/edge.rb +0 -0
- data/test/{fixtures → models}/entrant.rb +0 -0
- data/test/models/guid.rb +2 -0
- data/test/{fixtures → models}/item.rb +0 -0
- data/test/models/job.rb +5 -0
- data/test/{fixtures → models}/joke.rb +0 -0
- data/test/{fixtures → models}/keyboard.rb +0 -0
- data/test/{fixtures → models}/legacy_thing.rb +0 -0
- data/test/{fixtures → models}/matey.rb +0 -0
- data/test/models/member.rb +9 -0
- data/test/models/membership.rb +9 -0
- data/test/{fixtures → models}/minimalistic.rb +0 -0
- data/test/{fixtures → models}/mixed_case_monkey.rb +0 -0
- data/test/{fixtures → models}/movie.rb +0 -0
- data/test/{fixtures → models}/order.rb +2 -2
- data/test/models/owner.rb +4 -0
- data/test/{fixtures → models}/parrot.rb +0 -0
- data/test/models/person.rb +10 -0
- data/test/models/pet.rb +4 -0
- data/test/models/pirate.rb +9 -0
- data/test/{fixtures → models}/post.rb +23 -2
- data/test/models/price_estimate.rb +3 -0
- data/test/{fixtures → models}/project.rb +1 -0
- data/test/{fixtures → models}/reader.rb +0 -0
- data/test/models/reference.rb +4 -0
- data/test/{fixtures → models}/reply.rb +7 -5
- data/test/{fixtures → models}/ship.rb +0 -0
- data/test/models/sponsor.rb +4 -0
- data/test/{fixtures → models}/subject.rb +0 -0
- data/test/{fixtures → models}/subscriber.rb +2 -0
- data/test/models/subscription.rb +4 -0
- data/test/{fixtures → models}/tag.rb +0 -0
- data/test/{fixtures → models}/tagging.rb +0 -0
- data/test/{fixtures → models}/task.rb +0 -0
- data/test/{fixtures → models}/topic.rb +32 -4
- data/test/{fixtures → models}/treasure.rb +2 -0
- data/test/{fixtures → models}/vertex.rb +0 -0
- data/test/models/warehouse_thing.rb +5 -0
- data/test/schema/mysql_specific_schema.rb +12 -0
- data/test/schema/postgresql_specific_schema.rb +103 -0
- data/test/schema/schema.rb +421 -0
- data/test/schema/schema2.rb +6 -0
- data/test/schema/sqlite_specific_schema.rb +25 -0
- data/test/schema/sqlserver_specific_schema.rb +5 -0
- metadata +192 -176
- data/test/aaa_create_tables_test.rb +0 -72
- data/test/abstract_unit.rb +0 -84
- data/test/active_schema_test_mysql.rb +0 -46
- data/test/all.sh +0 -8
- data/test/association_inheritance_reload.rb +0 -14
- data/test/associations_test.rb +0 -2177
- data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +0 -1
- data/test/fixtures/bad_fixtures/attr_with_spaces +0 -1
- data/test/fixtures/bad_fixtures/blank_line +0 -3
- data/test/fixtures/bad_fixtures/duplicate_attributes +0 -3
- data/test/fixtures/bad_fixtures/missing_value +0 -1
- data/test/fixtures/db_definitions/db2.drop.sql +0 -33
- data/test/fixtures/db_definitions/db2.sql +0 -235
- data/test/fixtures/db_definitions/db22.drop.sql +0 -2
- data/test/fixtures/db_definitions/db22.sql +0 -5
- data/test/fixtures/db_definitions/firebird.drop.sql +0 -65
- data/test/fixtures/db_definitions/firebird.sql +0 -310
- data/test/fixtures/db_definitions/firebird2.drop.sql +0 -2
- data/test/fixtures/db_definitions/firebird2.sql +0 -6
- data/test/fixtures/db_definitions/frontbase.drop.sql +0 -33
- data/test/fixtures/db_definitions/frontbase.sql +0 -273
- data/test/fixtures/db_definitions/frontbase2.drop.sql +0 -1
- data/test/fixtures/db_definitions/frontbase2.sql +0 -4
- data/test/fixtures/db_definitions/openbase.drop.sql +0 -2
- data/test/fixtures/db_definitions/openbase.sql +0 -318
- data/test/fixtures/db_definitions/openbase2.drop.sql +0 -2
- data/test/fixtures/db_definitions/openbase2.sql +0 -7
- data/test/fixtures/db_definitions/oracle.drop.sql +0 -67
- data/test/fixtures/db_definitions/oracle.sql +0 -330
- data/test/fixtures/db_definitions/oracle2.drop.sql +0 -2
- data/test/fixtures/db_definitions/oracle2.sql +0 -6
- data/test/fixtures/db_definitions/postgresql.drop.sql +0 -44
- data/test/fixtures/db_definitions/postgresql.sql +0 -292
- data/test/fixtures/db_definitions/postgresql2.drop.sql +0 -2
- data/test/fixtures/db_definitions/postgresql2.sql +0 -4
- data/test/fixtures/db_definitions/schema.rb +0 -354
- data/test/fixtures/db_definitions/schema2.rb +0 -11
- data/test/fixtures/db_definitions/sqlite.drop.sql +0 -33
- data/test/fixtures/db_definitions/sqlite.sql +0 -219
- data/test/fixtures/db_definitions/sqlite2.drop.sql +0 -2
- data/test/fixtures/db_definitions/sqlite2.sql +0 -5
- data/test/fixtures/db_definitions/sybase.drop.sql +0 -35
- data/test/fixtures/db_definitions/sybase.sql +0 -222
- data/test/fixtures/db_definitions/sybase2.drop.sql +0 -4
- data/test/fixtures/db_definitions/sybase2.sql +0 -5
- data/test/fixtures/developers_projects/david_action_controller +0 -3
- data/test/fixtures/developers_projects/david_active_record +0 -3
- data/test/fixtures/developers_projects/jamis_active_record +0 -2
- data/test/fixtures/person.rb +0 -4
- data/test/fixtures/pirate.rb +0 -5
- data/test/fixtures/subscribers/first +0 -2
- data/test/fixtures/subscribers/second +0 -2
- data/test/schema_dumper_test.rb +0 -131
- data/test/schema_test_postgresql.rb +0 -64
@@ -0,0 +1,36 @@
|
|
1
|
+
require "active_support/test_case"
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class TestCase < ActiveSupport::TestCase #:nodoc:
|
5
|
+
self.fixture_path = FIXTURES_ROOT
|
6
|
+
self.use_instantiated_fixtures = false
|
7
|
+
self.use_transactional_fixtures = true
|
8
|
+
|
9
|
+
def create_fixtures(*table_names, &block)
|
10
|
+
Fixtures.create_fixtures(FIXTURES_ROOT, table_names, {}, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def assert_date_from_db(expected, actual, message = nil)
|
14
|
+
# SQL Server doesn't have a separate column type just for dates,
|
15
|
+
# so the time is in the string and incorrectly formatted
|
16
|
+
if current_adapter?(:SQLServerAdapter)
|
17
|
+
assert_equal expected.strftime("%Y/%m/%d 00:00:00"), actual.strftime("%Y/%m/%d 00:00:00")
|
18
|
+
elsif current_adapter?(:SybaseAdapter)
|
19
|
+
assert_equal expected.to_s, actual.to_date.to_s, message
|
20
|
+
else
|
21
|
+
assert_equal expected.to_s, actual.to_s, message
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def assert_queries(num = 1)
|
26
|
+
$query_count = 0
|
27
|
+
yield
|
28
|
+
ensure
|
29
|
+
assert_equal num, $query_count, "#{$query_count} instead of #{num} queries were executed."
|
30
|
+
end
|
31
|
+
|
32
|
+
def assert_no_queries(&block)
|
33
|
+
assert_queries(0, &block)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -5,7 +5,7 @@ module ActiveRecord
|
|
5
5
|
# Timestamping can be turned off by setting
|
6
6
|
# <tt>ActiveRecord::Base.record_timestamps = false</tt>
|
7
7
|
#
|
8
|
-
# Timestamps are in the local timezone by default but can use UTC by setting
|
8
|
+
# Timestamps are in the local timezone by default but you can use UTC by setting
|
9
9
|
# <tt>ActiveRecord::Base.default_timezone = :utc</tt>
|
10
10
|
module Timestamp
|
11
11
|
def self.included(base) #:nodoc:
|
@@ -29,13 +29,13 @@ module ActiveRecord
|
|
29
29
|
create_without_timestamps
|
30
30
|
end
|
31
31
|
|
32
|
-
def update_with_timestamps #:nodoc:
|
33
|
-
if record_timestamps
|
32
|
+
def update_with_timestamps(*args) #:nodoc:
|
33
|
+
if record_timestamps && (!partial_updates? || changed?)
|
34
34
|
t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
|
35
35
|
write_attribute('updated_at', t) if respond_to?(:updated_at)
|
36
36
|
write_attribute('updated_on', t) if respond_to?(:updated_on)
|
37
37
|
end
|
38
|
-
update_without_timestamps
|
38
|
+
update_without_timestamps(*args)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
@@ -28,11 +28,11 @@ module ActiveRecord
|
|
28
28
|
#
|
29
29
|
# This example will only take money from David and give to Mary if neither +withdrawal+ nor +deposit+ raises an exception.
|
30
30
|
# Exceptions will force a ROLLBACK that returns the database to the state before the transaction was begun. Be aware, though,
|
31
|
-
# that the objects
|
31
|
+
# that the objects will _not_ have their instance data returned to their pre-transactional state.
|
32
32
|
#
|
33
|
-
# == Different
|
33
|
+
# == Different Active Record classes in a single transaction
|
34
34
|
#
|
35
|
-
# Though the transaction class method is called on some
|
35
|
+
# Though the transaction class method is called on some Active Record class,
|
36
36
|
# the objects within the transaction block need not all be instances of
|
37
37
|
# that class.
|
38
38
|
# In this example a <tt>Balance</tt> record is transactionally saved even
|
@@ -201,7 +201,7 @@ module ActiveRecord
|
|
201
201
|
alias_method :count, :size
|
202
202
|
alias_method :length, :size
|
203
203
|
|
204
|
-
#
|
204
|
+
# Returns an XML representation of this error object.
|
205
205
|
#
|
206
206
|
# class Company < ActiveRecord::Base
|
207
207
|
# validates_presence_of :name, :address, :email
|
@@ -266,7 +266,7 @@ module ActiveRecord
|
|
266
266
|
# person.attributes = { "last_name" => "Heinemeier", "phone_number" => "555-555" }
|
267
267
|
# person.save # => true (and person is now saved in the database)
|
268
268
|
#
|
269
|
-
# An
|
269
|
+
# An Errors object is automatically created for every Active Record.
|
270
270
|
#
|
271
271
|
# Please do have a look at ActiveRecord::Validations::ClassMethods for a higher level of validations.
|
272
272
|
module Validations
|
@@ -279,11 +279,14 @@ module ActiveRecord
|
|
279
279
|
alias_method_chain :save!, :validation
|
280
280
|
alias_method_chain :update_attribute, :validation_skipping
|
281
281
|
end
|
282
|
+
|
283
|
+
base.send :include, ActiveSupport::Callbacks
|
284
|
+
base.define_callbacks *VALIDATIONS
|
282
285
|
end
|
283
286
|
|
284
287
|
# All of the following validations are defined in the class scope of the model that you're interested in validating.
|
285
288
|
# They offer a more declarative way of specifying when the model is valid and when it is not. It is recommended to use
|
286
|
-
# these over the low-level calls to validate and validate_on_create when possible.
|
289
|
+
# these over the low-level calls to +validate+ and +validate_on_create+ when possible.
|
287
290
|
module ClassMethods
|
288
291
|
DEFAULT_VALIDATION_OPTIONS = {
|
289
292
|
:on => :save,
|
@@ -298,7 +301,7 @@ module ActiveRecord
|
|
298
301
|
:odd => 'odd?', :even => 'even?' }.freeze
|
299
302
|
|
300
303
|
# Adds a validation method or block to the class. This is useful when
|
301
|
-
# overriding the
|
304
|
+
# overriding the +validate+ instance method becomes too unwieldly and
|
302
305
|
# you're looking for more descriptive declaration of your validations.
|
303
306
|
#
|
304
307
|
# This can be done with a symbol pointing to a method:
|
@@ -323,44 +326,7 @@ module ActiveRecord
|
|
323
326
|
# end
|
324
327
|
# end
|
325
328
|
#
|
326
|
-
# This usage applies to
|
327
|
-
def validate(*methods, &block)
|
328
|
-
methods << block if block_given?
|
329
|
-
write_inheritable_set(:validate, methods)
|
330
|
-
end
|
331
|
-
|
332
|
-
def validate_on_create(*methods, &block)
|
333
|
-
methods << block if block_given?
|
334
|
-
write_inheritable_set(:validate_on_create, methods)
|
335
|
-
end
|
336
|
-
|
337
|
-
def validate_on_update(*methods, &block)
|
338
|
-
methods << block if block_given?
|
339
|
-
write_inheritable_set(:validate_on_update, methods)
|
340
|
-
end
|
341
|
-
|
342
|
-
def condition_block?(condition)
|
343
|
-
condition.respond_to?("call") && (condition.arity == 1 || condition.arity == -1)
|
344
|
-
end
|
345
|
-
|
346
|
-
# Determine from the given condition (whether a block, procedure, method or string)
|
347
|
-
# whether or not to validate the record. See #validates_each.
|
348
|
-
def evaluate_condition(condition, record)
|
349
|
-
case condition
|
350
|
-
when Symbol; record.send(condition)
|
351
|
-
when String; eval(condition, record.send(:binding))
|
352
|
-
else
|
353
|
-
if condition_block?(condition)
|
354
|
-
condition.call(record)
|
355
|
-
else
|
356
|
-
raise(
|
357
|
-
ActiveRecordError,
|
358
|
-
"Validations need to be either a symbol, string (to be eval'ed), proc/method, or " +
|
359
|
-
"class implementing a static validation method"
|
360
|
-
)
|
361
|
-
end
|
362
|
-
end
|
363
|
-
end
|
329
|
+
# This usage applies to +validate_on_create+ and +validate_on_update+ as well.
|
364
330
|
|
365
331
|
# Validates each attribute against a block.
|
366
332
|
#
|
@@ -371,28 +337,25 @@ module ActiveRecord
|
|
371
337
|
# end
|
372
338
|
#
|
373
339
|
# Options:
|
374
|
-
# * <tt
|
375
|
-
# * <tt
|
376
|
-
# * <tt
|
377
|
-
# * <tt
|
378
|
-
# occur (e.g.
|
340
|
+
# * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
|
341
|
+
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+.
|
342
|
+
# * <tt>:allow_blank</tt> - Skip validation if attribute is blank.
|
343
|
+
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
344
|
+
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
|
345
|
+
# method, proc or string should return or evaluate to a true or false value.
|
346
|
+
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
|
347
|
+
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
379
348
|
# method, proc or string should return or evaluate to a true or false value.
|
380
|
-
# * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
|
381
|
-
# not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
|
382
|
-
# method, proc or string should return or evaluate to a true or false value.
|
383
349
|
def validates_each(*attrs)
|
384
350
|
options = attrs.extract_options!.symbolize_keys
|
385
351
|
attrs = attrs.flatten
|
386
352
|
|
387
353
|
# Declare the validation.
|
388
|
-
send(validation_method(options[:on] || :save)) do |record|
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
|
394
|
-
yield record, attr, value
|
395
|
-
end
|
354
|
+
send(validation_method(options[:on] || :save), options) do |record|
|
355
|
+
attrs.each do |attr|
|
356
|
+
value = record.send(attr)
|
357
|
+
next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
|
358
|
+
yield record, attr, value
|
396
359
|
end
|
397
360
|
end
|
398
361
|
end
|
@@ -410,21 +373,21 @@ module ActiveRecord
|
|
410
373
|
# <%= password_field "person", "password_confirmation" %>
|
411
374
|
#
|
412
375
|
# The added +password_confirmation+ attribute is virtual; it exists only as an in-memory attribute for validating the password.
|
413
|
-
# To achieve this, the validation adds
|
414
|
-
# only if +password_confirmation+ is not nil
|
376
|
+
# To achieve this, the validation adds accessors to the model for the confirmation attribute. NOTE: This check is performed
|
377
|
+
# only if +password_confirmation+ is not +nil+, and by default only on save. To require confirmation, make sure to add a presence
|
415
378
|
# check for the confirmation attribute:
|
416
379
|
#
|
417
380
|
# validates_presence_of :password_confirmation, :if => :password_changed?
|
418
381
|
#
|
419
382
|
# Configuration options:
|
420
|
-
# * <tt
|
421
|
-
# * <tt
|
422
|
-
# * <tt
|
423
|
-
# occur (e.g.
|
383
|
+
# * <tt>:message</tt> - A custom error message (default is: "doesn't match confirmation").
|
384
|
+
# * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
|
385
|
+
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
386
|
+
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
|
387
|
+
# method, proc or string should return or evaluate to a true or false value.
|
388
|
+
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
|
389
|
+
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
424
390
|
# method, proc or string should return or evaluate to a true or false value.
|
425
|
-
# * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
|
426
|
-
# not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
|
427
|
-
# method, proc or string should return or evaluate to a true or false value.
|
428
391
|
def validates_confirmation_of(*attr_names)
|
429
392
|
configuration = { :message => ActiveRecord::Errors.default_error_messages[:confirmation], :on => :save }
|
430
393
|
configuration.update(attr_names.extract_options!)
|
@@ -443,22 +406,22 @@ module ActiveRecord
|
|
443
406
|
# validates_acceptance_of :eula, :message => "must be abided"
|
444
407
|
# end
|
445
408
|
#
|
446
|
-
# If the database column does not exist, the terms_of_service attribute is entirely virtual. This check is
|
447
|
-
# performed only if terms_of_service is not nil and by default on save.
|
409
|
+
# If the database column does not exist, the +terms_of_service+ attribute is entirely virtual. This check is
|
410
|
+
# performed only if +terms_of_service+ is not +nil+ and by default on save.
|
448
411
|
#
|
449
412
|
# Configuration options:
|
450
|
-
# * <tt
|
451
|
-
# * <tt
|
452
|
-
# * <tt
|
453
|
-
# * <tt
|
454
|
-
# makes it easy to relate to an HTML checkbox. This should be set to
|
455
|
-
# column, since the attribute is typecast from "1" to
|
456
|
-
# * <tt
|
457
|
-
# occur (e.g.
|
413
|
+
# * <tt>:message</tt> - A custom error message (default is: "must be accepted").
|
414
|
+
# * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
|
415
|
+
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is true).
|
416
|
+
# * <tt>:accept</tt> - Specifies value that is considered accepted. The default value is a string "1", which
|
417
|
+
# makes it easy to relate to an HTML checkbox. This should be set to +true+ if you are validating a database
|
418
|
+
# column, since the attribute is typecast from "1" to +true+ before validation.
|
419
|
+
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
420
|
+
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
|
421
|
+
# method, proc or string should return or evaluate to a true or false value.
|
422
|
+
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
|
423
|
+
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
458
424
|
# method, proc or string should return or evaluate to a true or false value.
|
459
|
-
# * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
|
460
|
-
# not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
|
461
|
-
# method, proc or string should return or evaluate to a true or false value.
|
462
425
|
def validates_acceptance_of(*attr_names)
|
463
426
|
configuration = { :message => ActiveRecord::Errors.default_error_messages[:accepted], :on => :save, :allow_nil => true, :accept => "1" }
|
464
427
|
configuration.update(attr_names.extract_options!)
|
@@ -489,8 +452,8 @@ module ActiveRecord
|
|
489
452
|
# This is due to the way Object#blank? handles boolean values. false.blank? # => true
|
490
453
|
#
|
491
454
|
# Configuration options:
|
492
|
-
# * <tt>message</tt> - A custom error message (default is: "can't be blank")
|
493
|
-
# * <tt>on</tt> - Specifies when this validation is active (default is
|
455
|
+
# * <tt>message</tt> - A custom error message (default is: "can't be blank").
|
456
|
+
# * <tt>on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
|
494
457
|
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
495
458
|
# occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
|
496
459
|
# method, proc or string should return or evaluate to a true or false value.
|
@@ -498,28 +461,15 @@ module ActiveRecord
|
|
498
461
|
# not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
|
499
462
|
# method, proc or string should return or evaluate to a true or false value.
|
500
463
|
#
|
501
|
-
# === Warning
|
502
|
-
# Validate the presence of the foreign key, not the instance variable itself.
|
503
|
-
# Do this:
|
504
|
-
# validates_presence_of :invoice_id
|
505
|
-
#
|
506
|
-
# Not this:
|
507
|
-
# validates_presence_of :invoice
|
508
|
-
#
|
509
|
-
# If you validate the presence of the associated object, you will get
|
510
|
-
# failures on saves when both the parent object and the child object are
|
511
|
-
# new.
|
512
464
|
def validates_presence_of(*attr_names)
|
513
465
|
configuration = { :message => ActiveRecord::Errors.default_error_messages[:blank], :on => :save }
|
514
466
|
configuration.update(attr_names.extract_options!)
|
515
467
|
|
516
468
|
# can't use validates_each here, because it cannot cope with nonexistent attributes,
|
517
469
|
# while errors.add_on_empty can
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
end
|
522
|
-
end
|
470
|
+
send(validation_method(configuration[:on]), configuration) do |record|
|
471
|
+
record.errors.add_on_blank(attr_names, configuration[:message])
|
472
|
+
end
|
523
473
|
end
|
524
474
|
|
525
475
|
# Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time:
|
@@ -535,25 +485,25 @@ module ActiveRecord
|
|
535
485
|
# end
|
536
486
|
#
|
537
487
|
# Configuration options:
|
538
|
-
# * <tt
|
539
|
-
# * <tt
|
540
|
-
# * <tt
|
541
|
-
# * <tt
|
542
|
-
# * <tt
|
543
|
-
# * <tt
|
544
|
-
# * <tt
|
545
|
-
#
|
546
|
-
# * <tt
|
547
|
-
# * <tt
|
548
|
-
# * <tt
|
549
|
-
# * <tt
|
550
|
-
# * <tt
|
551
|
-
# * <tt
|
552
|
-
# occur (e.g.
|
488
|
+
# * <tt>:minimum</tt> - The minimum size of the attribute.
|
489
|
+
# * <tt>:maximum</tt> - The maximum size of the attribute.
|
490
|
+
# * <tt>:is</tt> - The exact size of the attribute.
|
491
|
+
# * <tt>:within</tt> - A range specifying the minimum and maximum size of the attribute.
|
492
|
+
# * <tt>:in</tt> - A synonym(or alias) for <tt>:within</tt>.
|
493
|
+
# * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
|
494
|
+
# * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
|
495
|
+
#
|
496
|
+
# * <tt>:too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is %d characters)").
|
497
|
+
# * <tt>:too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is %d characters)").
|
498
|
+
# * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method and the attribute is the wrong size (default is: "is the wrong length (should be %d characters)").
|
499
|
+
# * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>, <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
|
500
|
+
# * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
|
501
|
+
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
502
|
+
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
|
503
|
+
# method, proc or string should return or evaluate to a true or false value.
|
504
|
+
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
|
505
|
+
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
553
506
|
# method, proc or string should return or evaluate to a true or false value.
|
554
|
-
# * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
|
555
|
-
# not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
|
556
|
-
# method, proc or string should return or evaluate to a true or false value.
|
557
507
|
def validates_length_of(*attrs)
|
558
508
|
# Merge given options with defaults.
|
559
509
|
options = {
|
@@ -586,9 +536,10 @@ module ActiveRecord
|
|
586
536
|
too_long = options[:too_long] % option_value.end
|
587
537
|
|
588
538
|
validates_each(attrs, options) do |record, attr, value|
|
589
|
-
|
539
|
+
value = value.split(//) if value.kind_of?(String)
|
540
|
+
if value.nil? or value.size < option_value.begin
|
590
541
|
record.errors.add(attr, too_short)
|
591
|
-
elsif value.
|
542
|
+
elsif value.size > option_value.end
|
592
543
|
record.errors.add(attr, too_long)
|
593
544
|
end
|
594
545
|
end
|
@@ -602,11 +553,8 @@ module ActiveRecord
|
|
602
553
|
message = (options[:message] || options[message_options[option]]) % option_value
|
603
554
|
|
604
555
|
validates_each(attrs, options) do |record, attr, value|
|
605
|
-
if value.kind_of?(String)
|
606
|
-
|
607
|
-
else
|
608
|
-
record.errors.add(attr, message) unless !value.nil? and value.size.method(validity_checks[option])[option_value]
|
609
|
-
end
|
556
|
+
value = value.split(//) if value.kind_of?(String)
|
557
|
+
record.errors.add(attr, message) unless !value.nil? and value.size.method(validity_checks[option])[option_value]
|
610
558
|
end
|
611
559
|
end
|
612
560
|
end
|
@@ -632,63 +580,83 @@ module ActiveRecord
|
|
632
580
|
# attribute (that maps to a column). When the record is updated, the same check is made but disregarding the record itself.
|
633
581
|
#
|
634
582
|
# Because this check is performed outside the database there is still a chance that duplicate values
|
635
|
-
# will be inserted in two parallel transactions. To guarantee against this you should create a
|
583
|
+
# will be inserted in two parallel transactions. To guarantee against this you should create a
|
636
584
|
# unique index on the field. See +add_index+ for more information.
|
637
585
|
#
|
638
586
|
# Configuration options:
|
639
|
-
# * <tt
|
640
|
-
# * <tt
|
641
|
-
# * <tt
|
642
|
-
# * <tt
|
643
|
-
# * <tt
|
644
|
-
# * <tt
|
645
|
-
# occur (e.g.
|
587
|
+
# * <tt>:message</tt> - Specifies a custom error message (default is: "has already been taken").
|
588
|
+
# * <tt>:scope</tt> - One or more columns by which to limit the scope of the uniqueness constraint.
|
589
|
+
# * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by non-text columns (+false+ by default).
|
590
|
+
# * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
|
591
|
+
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
|
592
|
+
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
593
|
+
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
|
646
594
|
# method, proc or string should return or evaluate to a true or false value.
|
647
|
-
# * <tt
|
648
|
-
# not occur (e.g.
|
595
|
+
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
|
596
|
+
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
649
597
|
# method, proc or string should return or evaluate to a true or false value.
|
650
598
|
def validates_uniqueness_of(*attr_names)
|
651
599
|
configuration = { :message => ActiveRecord::Errors.default_error_messages[:taken], :case_sensitive => true }
|
652
600
|
configuration.update(attr_names.extract_options!)
|
653
601
|
|
654
602
|
validates_each(attr_names,configuration) do |record, attr_name, value|
|
655
|
-
|
656
|
-
|
603
|
+
# The check for an existing value should be run from a class that
|
604
|
+
# isn't abstract. This means working down from the current class
|
605
|
+
# (self), to the first non-abstract class. Since classes don't know
|
606
|
+
# their subclasses, we have to build the hierarchy between self and
|
607
|
+
# the record's class.
|
608
|
+
class_hierarchy = [record.class]
|
609
|
+
while class_hierarchy.first != self
|
610
|
+
class_hierarchy.insert(0, class_hierarchy.first.superclass)
|
611
|
+
end
|
612
|
+
|
613
|
+
# Now we can work our way down the tree to the first non-abstract
|
614
|
+
# class (which has a database table to query from).
|
615
|
+
finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? }
|
616
|
+
|
617
|
+
if value.nil? || (configuration[:case_sensitive] || !finder_class.columns_hash[attr_name.to_s].text?)
|
618
|
+
condition_sql = "#{record.class.quoted_table_name}.#{attr_name} #{attribute_condition(value)}"
|
657
619
|
condition_params = [value]
|
658
620
|
else
|
659
|
-
|
621
|
+
# sqlite has case sensitive SELECT query, while MySQL/Postgresql don't.
|
622
|
+
# Hence, this is needed only for sqlite.
|
623
|
+
condition_sql = "LOWER(#{record.class.quoted_table_name}.#{attr_name}) #{attribute_condition(value)}"
|
660
624
|
condition_params = [value.downcase]
|
661
625
|
end
|
662
626
|
|
663
627
|
if scope = configuration[:scope]
|
664
628
|
Array(scope).map do |scope_item|
|
665
629
|
scope_value = record.send(scope_item)
|
666
|
-
condition_sql << " AND #{record.class.
|
630
|
+
condition_sql << " AND #{record.class.quoted_table_name}.#{scope_item} #{attribute_condition(scope_value)}"
|
667
631
|
condition_params << scope_value
|
668
632
|
end
|
669
633
|
end
|
670
634
|
|
671
635
|
unless record.new_record?
|
672
|
-
condition_sql << " AND #{record.class.
|
636
|
+
condition_sql << " AND #{record.class.quoted_table_name}.#{record.class.primary_key} <> ?"
|
673
637
|
condition_params << record.send(:id)
|
674
638
|
end
|
675
639
|
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
640
|
+
results = finder_class.with_exclusive_scope do
|
641
|
+
connection.select_all(
|
642
|
+
construct_finder_sql(
|
643
|
+
:select => "#{connection.quote_column_name(attr_name)}",
|
644
|
+
:from => "#{finder_class.quoted_table_name}",
|
645
|
+
:conditions => [condition_sql, *condition_params]
|
646
|
+
)
|
647
|
+
)
|
684
648
|
end
|
685
649
|
|
686
|
-
|
687
|
-
|
688
|
-
finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? }
|
650
|
+
unless results.length.zero?
|
651
|
+
found = true
|
689
652
|
|
690
|
-
|
691
|
-
|
653
|
+
# As MySQL/Postgres don't have case sensitive SELECT queries, we try to find duplicate
|
654
|
+
# column in ruby when case sensitive option
|
655
|
+
if configuration[:case_sensitive] && finder_class.columns_hash[attr_name.to_s].text?
|
656
|
+
found = results.any? { |a| a[attr_name.to_s] == value }
|
657
|
+
end
|
658
|
+
|
659
|
+
record.errors.add(attr_name, configuration[:message]) if found
|
692
660
|
end
|
693
661
|
end
|
694
662
|
end
|
@@ -701,21 +669,21 @@ module ActiveRecord
|
|
701
669
|
# validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create
|
702
670
|
# end
|
703
671
|
#
|
704
|
-
# Note: use
|
672
|
+
# Note: use <tt>\A</tt> and <tt>\Z</tt> to match the start and end of the string, <tt>^</tt> and <tt>$</tt> match the start/end of a line.
|
705
673
|
#
|
706
674
|
# A regular expression must be provided or else an exception will be raised.
|
707
675
|
#
|
708
676
|
# Configuration options:
|
709
|
-
# * <tt
|
710
|
-
# * <tt
|
711
|
-
# * <tt
|
712
|
-
# * <tt
|
713
|
-
# * <tt
|
714
|
-
# * <tt
|
715
|
-
# occur (e.g.
|
677
|
+
# * <tt>:message</tt> - A custom error message (default is: "is invalid").
|
678
|
+
# * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
|
679
|
+
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
|
680
|
+
# * <tt>:with</tt> - The regular expression used to validate the format with (note: must be supplied!).
|
681
|
+
# * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
|
682
|
+
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
683
|
+
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
|
716
684
|
# method, proc or string should return or evaluate to a true or false value.
|
717
|
-
# * <tt
|
718
|
-
# not occur (e.g.
|
685
|
+
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
|
686
|
+
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
719
687
|
# method, proc or string should return or evaluate to a true or false value.
|
720
688
|
def validates_format_of(*attr_names)
|
721
689
|
configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save, :with => nil }
|
@@ -724,7 +692,7 @@ module ActiveRecord
|
|
724
692
|
raise(ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash") unless configuration[:with].is_a?(Regexp)
|
725
693
|
|
726
694
|
validates_each(attr_names, configuration) do |record, attr_name, value|
|
727
|
-
record.errors.add(attr_name, configuration[:message]) unless value.to_s =~ configuration[:with]
|
695
|
+
record.errors.add(attr_name, configuration[:message] % value) unless value.to_s =~ configuration[:with]
|
728
696
|
end
|
729
697
|
end
|
730
698
|
|
@@ -737,15 +705,15 @@ module ActiveRecord
|
|
737
705
|
# end
|
738
706
|
#
|
739
707
|
# Configuration options:
|
740
|
-
# * <tt
|
741
|
-
# * <tt
|
742
|
-
# * <tt
|
743
|
-
# * <tt
|
744
|
-
# * <tt
|
745
|
-
# occur (e.g.
|
708
|
+
# * <tt>:in</tt> - An enumerable object of available items.
|
709
|
+
# * <tt>:message</tt> - Specifies a custom error message (default is: "is not included in the list").
|
710
|
+
# * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
|
711
|
+
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
|
712
|
+
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
713
|
+
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
|
746
714
|
# method, proc or string should return or evaluate to a true or false value.
|
747
|
-
# * <tt
|
748
|
-
# not occur (e.g.
|
715
|
+
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
|
716
|
+
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
749
717
|
# method, proc or string should return or evaluate to a true or false value.
|
750
718
|
def validates_inclusion_of(*attr_names)
|
751
719
|
configuration = { :message => ActiveRecord::Errors.default_error_messages[:inclusion], :on => :save }
|
@@ -769,15 +737,15 @@ module ActiveRecord
|
|
769
737
|
# end
|
770
738
|
#
|
771
739
|
# Configuration options:
|
772
|
-
# * <tt
|
773
|
-
# * <tt
|
774
|
-
# * <tt
|
775
|
-
# * <tt
|
776
|
-
# * <tt
|
777
|
-
# occur (e.g.
|
740
|
+
# * <tt>:in</tt> - An enumerable object of items that the value shouldn't be part of.
|
741
|
+
# * <tt>:message</tt> - Specifies a custom error message (default is: "is reserved").
|
742
|
+
# * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
|
743
|
+
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
|
744
|
+
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
745
|
+
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
|
778
746
|
# method, proc or string should return or evaluate to a true or false value.
|
779
|
-
# * <tt
|
780
|
-
# not occur (e.g.
|
747
|
+
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
|
748
|
+
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
781
749
|
# method, proc or string should return or evaluate to a true or false value.
|
782
750
|
def validates_exclusion_of(*attr_names)
|
783
751
|
configuration = { :message => ActiveRecord::Errors.default_error_messages[:exclusion], :on => :save }
|
@@ -809,19 +777,19 @@ module ActiveRecord
|
|
809
777
|
# validates_associated :book
|
810
778
|
# end
|
811
779
|
#
|
812
|
-
#
|
780
|
+
# this would specify a circular dependency and cause infinite recursion.
|
813
781
|
#
|
814
782
|
# NOTE: This validation will not fail if the association hasn't been assigned. If you want to ensure that the association
|
815
|
-
# is both present and guaranteed to be valid, you also need to use validates_presence_of
|
783
|
+
# is both present and guaranteed to be valid, you also need to use +validates_presence_of+.
|
816
784
|
#
|
817
785
|
# Configuration options:
|
818
|
-
# * <tt
|
819
|
-
# * <tt
|
820
|
-
# * <tt
|
821
|
-
# occur (e.g.
|
786
|
+
# * <tt>:message</tt> - A custom error message (default is: "is invalid")
|
787
|
+
# * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
|
788
|
+
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
789
|
+
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
|
822
790
|
# method, proc or string should return or evaluate to a true or false value.
|
823
|
-
# * <tt
|
824
|
-
# not occur (e.g.
|
791
|
+
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
|
792
|
+
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
825
793
|
# method, proc or string should return or evaluate to a true or false value.
|
826
794
|
def validates_associated(*attr_names)
|
827
795
|
configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save }
|
@@ -834,30 +802,30 @@ module ActiveRecord
|
|
834
802
|
end
|
835
803
|
|
836
804
|
# Validates whether the value of the specified attribute is numeric by trying to convert it to
|
837
|
-
# a float with Kernel.Float (if <tt>
|
838
|
-
# <tt>/\A[\+\-]?\d+\Z/</tt> (if <tt>
|
805
|
+
# a float with Kernel.Float (if <tt>only_integer</tt> is false) or applying it to the regular expression
|
806
|
+
# <tt>/\A[\+\-]?\d+\Z/</tt> (if <tt>only_integer</tt> is set to true).
|
839
807
|
#
|
840
808
|
# class Person < ActiveRecord::Base
|
841
809
|
# validates_numericality_of :value, :on => :create
|
842
810
|
# end
|
843
811
|
#
|
844
812
|
# Configuration options:
|
845
|
-
# * <tt
|
846
|
-
# * <tt
|
847
|
-
# * <tt
|
848
|
-
# * <tt
|
849
|
-
# * <tt
|
850
|
-
# * <tt
|
851
|
-
# * <tt
|
852
|
-
# * <tt
|
853
|
-
# * <tt
|
854
|
-
# * <tt
|
855
|
-
# * <tt
|
856
|
-
# * <tt
|
857
|
-
# occur (e.g.
|
813
|
+
# * <tt>:message</tt> - A custom error message (default is: "is not a number").
|
814
|
+
# * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
|
815
|
+
# * <tt>:only_integer</tt> - Specifies whether the value has to be an integer, e.g. an integral value (default is +false+).
|
816
|
+
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is +false+). Notice that for fixnum and float columns empty strings are converted to +nil+.
|
817
|
+
# * <tt>:greater_than</tt> - Specifies the value must be greater than the supplied value.
|
818
|
+
# * <tt>:greater_than_or_equal_to</tt> - Specifies the value must be greater than or equal the supplied value.
|
819
|
+
# * <tt>:equal_to</tt> - Specifies the value must be equal to the supplied value.
|
820
|
+
# * <tt>:less_than</tt> - Specifies the value must be less than the supplied value.
|
821
|
+
# * <tt>:less_than_or_equal_to</tt> - Specifies the value must be less than or equal the supplied value.
|
822
|
+
# * <tt>:odd</tt> - Specifies the value must be an odd number.
|
823
|
+
# * <tt>:even</tt> - Specifies the value must be an even number.
|
824
|
+
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
825
|
+
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
|
858
826
|
# method, proc or string should return or evaluate to a true or false value.
|
859
|
-
# * <tt
|
860
|
-
# not occur (e.g.
|
827
|
+
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
|
828
|
+
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
861
829
|
# method, proc or string should return or evaluate to a true or false value.
|
862
830
|
def validates_numericality_of(*attr_names)
|
863
831
|
configuration = { :on => :save, :only_integer => false, :allow_nil => false }
|
@@ -895,7 +863,9 @@ module ActiveRecord
|
|
895
863
|
when :odd, :even
|
896
864
|
record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[option]) unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[]
|
897
865
|
else
|
898
|
-
|
866
|
+
message = configuration[:message] || ActiveRecord::Errors.default_error_messages[option]
|
867
|
+
message = message % configuration[option] if configuration[option]
|
868
|
+
record.errors.add(attr_name, message) unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]]
|
899
869
|
end
|
900
870
|
end
|
901
871
|
end
|
@@ -903,23 +873,18 @@ module ActiveRecord
|
|
903
873
|
|
904
874
|
# Creates an object just like Base.create but calls save! instead of save
|
905
875
|
# so an exception is raised if the record is invalid.
|
906
|
-
def create!(attributes = nil)
|
876
|
+
def create!(attributes = nil, &block)
|
907
877
|
if attributes.is_a?(Array)
|
908
|
-
attributes.collect { |attr| create!(attr) }
|
878
|
+
attributes.collect { |attr| create!(attr, &block) }
|
909
879
|
else
|
910
880
|
object = new(attributes)
|
881
|
+
yield(object) if block_given?
|
911
882
|
object.save!
|
912
883
|
object
|
913
884
|
end
|
914
885
|
end
|
915
886
|
|
916
|
-
|
917
887
|
private
|
918
|
-
def write_inheritable_set(key, methods)
|
919
|
-
existing_methods = read_inheritable_attribute(key) || []
|
920
|
-
write_inheritable_attribute(key, existing_methods | methods)
|
921
|
-
end
|
922
|
-
|
923
888
|
def validation_method(on)
|
924
889
|
case on
|
925
890
|
when :save then :validate
|
@@ -957,18 +922,18 @@ module ActiveRecord
|
|
957
922
|
save(false)
|
958
923
|
end
|
959
924
|
|
960
|
-
# Runs validate and validate_on_create or validate_on_update and returns true if no errors were added otherwise false.
|
925
|
+
# Runs +validate+ and +validate_on_create+ or +validate_on_update+ and returns true if no errors were added otherwise false.
|
961
926
|
def valid?
|
962
927
|
errors.clear
|
963
928
|
|
964
|
-
|
929
|
+
run_callbacks(:validate)
|
965
930
|
validate
|
966
931
|
|
967
932
|
if new_record?
|
968
|
-
|
933
|
+
run_callbacks(:validate_on_create)
|
969
934
|
validate_on_create
|
970
935
|
else
|
971
|
-
|
936
|
+
run_callbacks(:validate_on_update)
|
972
937
|
validate_on_update
|
973
938
|
end
|
974
939
|
|
@@ -981,7 +946,7 @@ module ActiveRecord
|
|
981
946
|
end
|
982
947
|
|
983
948
|
protected
|
984
|
-
# Overwrite this method for validation checks on all saves and use Errors.add(field, msg) for invalid attributes.
|
949
|
+
# Overwrite this method for validation checks on all saves and use <tt>Errors.add(field, msg)</tt> for invalid attributes.
|
985
950
|
def validate #:doc:
|
986
951
|
end
|
987
952
|
|
@@ -992,36 +957,5 @@ module ActiveRecord
|
|
992
957
|
# Overwrite this method for validation checks used only on updates.
|
993
958
|
def validate_on_update # :doc:
|
994
959
|
end
|
995
|
-
|
996
|
-
private
|
997
|
-
def run_validations(validation_method)
|
998
|
-
validations = self.class.read_inheritable_attribute(validation_method.to_sym)
|
999
|
-
if validations.nil? then return end
|
1000
|
-
validations.each do |validation|
|
1001
|
-
if validation.is_a?(Symbol)
|
1002
|
-
self.send(validation)
|
1003
|
-
elsif validation.is_a?(String)
|
1004
|
-
eval(validation, binding)
|
1005
|
-
elsif validation_block?(validation)
|
1006
|
-
validation.call(self)
|
1007
|
-
elsif validation_class?(validation, validation_method)
|
1008
|
-
validation.send(validation_method, self)
|
1009
|
-
else
|
1010
|
-
raise(
|
1011
|
-
ActiveRecordError,
|
1012
|
-
"Validations need to be either a symbol, string (to be eval'ed), proc/method, or " +
|
1013
|
-
"class implementing a static validation method"
|
1014
|
-
)
|
1015
|
-
end
|
1016
|
-
end
|
1017
|
-
end
|
1018
|
-
|
1019
|
-
def validation_block?(validation)
|
1020
|
-
validation.respond_to?("call") && (validation.arity == 1 || validation.arity == -1)
|
1021
|
-
end
|
1022
|
-
|
1023
|
-
def validation_class?(validation, validation_method)
|
1024
|
-
validation.respond_to?(validation_method)
|
1025
|
-
end
|
1026
960
|
end
|
1027
961
|
end
|