activerecord 1.9.1 → 1.10.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 +78 -0
- data/README +1 -1
- data/install.rb +7 -42
- data/lib/active_record.rb +2 -0
- data/lib/active_record/acts/list.rb +28 -4
- data/lib/active_record/acts/nested_set.rb +212 -0
- data/lib/active_record/associations.rb +203 -21
- data/lib/active_record/associations/association_proxy.rb +10 -2
- data/lib/active_record/associations/belongs_to_association.rb +0 -1
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -9
- data/lib/active_record/associations/has_many_association.rb +25 -25
- data/lib/active_record/associations/has_one_association.rb +2 -2
- data/lib/active_record/base.rb +134 -110
- data/lib/active_record/connection_adapters/abstract_adapter.rb +9 -9
- data/lib/active_record/connection_adapters/mysql_adapter.rb +4 -0
- data/lib/active_record/connection_adapters/oci_adapter.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -2
- data/lib/active_record/deprecated_associations.rb +1 -19
- data/lib/active_record/deprecated_finders.rb +41 -0
- data/lib/active_record/fixtures.rb +24 -11
- data/lib/active_record/observer.rb +17 -11
- data/lib/active_record/reflection.rb +5 -1
- data/lib/active_record/transactions.rb +7 -0
- data/lib/active_record/validations.rb +32 -33
- data/rakefile +30 -6
- data/test/associations_go_eager_test.rb +55 -0
- data/test/associations_test.rb +72 -15
- data/test/base_test.rb +15 -21
- data/test/deprecated_associations_test.rb +0 -24
- data/test/deprecated_finder_test.rb +147 -0
- data/test/finder_test.rb +37 -37
- data/test/fixtures/author.rb +3 -0
- data/test/fixtures/authors.yml +7 -0
- data/test/fixtures/categories.yml +7 -0
- data/test/fixtures/categories_posts.yml +11 -0
- data/test/fixtures/category.rb +3 -0
- data/test/fixtures/comment.rb +5 -0
- data/test/fixtures/comments.yml +17 -0
- data/test/fixtures/company.rb +3 -0
- data/test/fixtures/courses.yml +4 -4
- data/test/fixtures/db_definitions/db2.drop.sql +6 -0
- data/test/fixtures/db_definitions/db2.sql +46 -0
- data/test/fixtures/db_definitions/mysql.drop.sql +6 -1
- data/test/fixtures/db_definitions/mysql.sql +60 -12
- data/test/fixtures/db_definitions/mysql2.sql +1 -1
- data/test/fixtures/db_definitions/oci.drop.sql +5 -0
- data/test/fixtures/db_definitions/oci.sql +45 -0
- data/test/fixtures/db_definitions/postgresql.drop.sql +6 -0
- data/test/fixtures/db_definitions/postgresql.sql +45 -0
- data/test/fixtures/db_definitions/sqlite.drop.sql +6 -1
- data/test/fixtures/db_definitions/sqlite.sql +46 -0
- data/test/fixtures/db_definitions/sqlserver.drop.sql +7 -1
- data/test/fixtures/db_definitions/sqlserver.sql +46 -0
- data/test/fixtures/fk_test_has_fk.yml +3 -0
- data/test/fixtures/fk_test_has_pk.yml +2 -0
- data/test/fixtures/mixin.rb +18 -0
- data/test/fixtures/mixins.yml +30 -0
- data/test/fixtures/post.rb +8 -0
- data/test/fixtures/posts.yml +20 -0
- data/test/fixtures/task.rb +3 -0
- data/test/fixtures/tasks.yml +7 -0
- data/test/fixtures_test.rb +34 -2
- data/test/mixin_nested_set_test.rb +184 -0
- data/test/mixin_test.rb +28 -3
- data/test/validations_test.rb +16 -0
- metadata +21 -5
- data/test/fixtures/db_definitions/drop_oracle_tables.sql +0 -35
- data/test/fixtures/db_definitions/drop_oracle_tables2.sql +0 -3
@@ -186,7 +186,7 @@ module ActiveRecord
|
|
186
186
|
when :time then string_to_dummy_time(value)
|
187
187
|
when :date then string_to_date(value)
|
188
188
|
when :binary then binary_to_string(value)
|
189
|
-
when :boolean then
|
189
|
+
when :boolean then value == true or (value =~ /^t(rue)?$/i) == 0 or value.to_s == '1'
|
190
190
|
else value
|
191
191
|
end
|
192
192
|
end
|
@@ -205,22 +205,22 @@ module ActiveRecord
|
|
205
205
|
|
206
206
|
private
|
207
207
|
def string_to_date(string)
|
208
|
-
return string
|
209
|
-
date_array = ParseDate.parsedate(string)
|
208
|
+
return string unless string.is_a?(String)
|
209
|
+
date_array = ParseDate.parsedate(string.to_s)
|
210
210
|
# treat 0000-00-00 as nil
|
211
211
|
Date.new(date_array[0], date_array[1], date_array[2]) rescue nil
|
212
212
|
end
|
213
213
|
|
214
214
|
def string_to_time(string)
|
215
|
-
return string
|
216
|
-
time_array = ParseDate.parsedate(string).compact
|
215
|
+
return string unless string.is_a?(String)
|
216
|
+
time_array = ParseDate.parsedate(string.to_s).compact
|
217
217
|
# treat 0000-00-00 00:00:00 as nil
|
218
218
|
Time.send(Base.default_timezone, *time_array) rescue nil
|
219
219
|
end
|
220
220
|
|
221
221
|
def string_to_dummy_time(string)
|
222
|
-
return string
|
223
|
-
time_array = ParseDate.parsedate(string)
|
222
|
+
return string unless string.is_a?(String)
|
223
|
+
time_array = ParseDate.parsedate(string.to_s)
|
224
224
|
# pad the resulting array with dummy date information
|
225
225
|
time_array[0] = 2000; time_array[1] = 1; time_array[2] = 1;
|
226
226
|
Time.send(Base.default_timezone, *time_array) rescue nil
|
@@ -378,8 +378,8 @@ module ActiveRecord
|
|
378
378
|
end
|
379
379
|
end
|
380
380
|
|
381
|
-
def create_table(name)
|
382
|
-
execute "CREATE TABLE #{name} (id #{native_database_types[:primary_key]})"
|
381
|
+
def create_table(name, options = "")
|
382
|
+
execute "CREATE TABLE #{name} (id #{native_database_types[:primary_key]}) #{options}"
|
383
383
|
table_definition = yield TableDefinition.new
|
384
384
|
table_definition.columns.each { |column_name, type, options| add_column(name, column_name, type, options) }
|
385
385
|
end
|
@@ -45,7 +45,7 @@ begin
|
|
45
45
|
return nil if value.nil? || value =~ /^\s*null\s*$/i
|
46
46
|
case type
|
47
47
|
when :string then value
|
48
|
-
when :integer then value.to_i
|
48
|
+
when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
|
49
49
|
when :float then value.to_f
|
50
50
|
when :datetime then cast_to_date_or_time(value)
|
51
51
|
when :time then cast_to_time(value)
|
@@ -261,4 +261,4 @@ begin
|
|
261
261
|
end
|
262
262
|
rescue LoadError
|
263
263
|
# OCI8 driver is unavailable.
|
264
|
-
end
|
264
|
+
end
|
@@ -203,7 +203,6 @@ module ActiveRecord
|
|
203
203
|
end
|
204
204
|
end
|
205
205
|
end
|
206
|
-
|
207
206
|
log(sql, name, @connection) do |conn|
|
208
207
|
conn.execute(sql)
|
209
208
|
select_one("SELECT @@IDENTITY AS Ident")["Ident"]
|
@@ -362,7 +361,7 @@ module ActiveRecord
|
|
362
361
|
end
|
363
362
|
|
364
363
|
def query_contains_identity_column(sql, col)
|
365
|
-
return sql =~ /[\
|
364
|
+
return sql =~ /[\[.,]\s*#{col}/
|
366
365
|
end
|
367
366
|
|
368
367
|
def get_order_by(sql)
|
@@ -84,25 +84,7 @@ module ActiveRecord
|
|
84
84
|
!#{association_name}(force_reload).nil?
|
85
85
|
end
|
86
86
|
end_eval
|
87
|
-
end
|
88
|
-
|
89
|
-
def deprecated_build_method(method_prefix, collection_name, collection_class_name, class_primary_key_name)# :nodoc:
|
90
|
-
module_eval <<-"end_eval", __FILE__, __LINE__
|
91
|
-
def #{method_prefix + collection_name}(attributes = {})
|
92
|
-
association = #{collection_class_name}.new
|
93
|
-
association.attributes = attributes.merge({ "#{class_primary_key_name}" => id})
|
94
|
-
association
|
95
|
-
end
|
96
|
-
end_eval
|
97
|
-
end
|
98
|
-
|
99
|
-
def deprecated_create_method(method_prefix, collection_name, collection_class_name, class_primary_key_name)# :nodoc:
|
100
|
-
module_eval <<-"end_eval", __FILE__, __LINE__
|
101
|
-
def #{method_prefix + collection_name}(attributes = nil)
|
102
|
-
#{collection_class_name}.create((attributes || {}).merge({ "#{class_primary_key_name}" => id}))
|
103
|
-
end
|
104
|
-
end_eval
|
105
|
-
end
|
87
|
+
end
|
106
88
|
end
|
107
89
|
end
|
108
90
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Base
|
3
|
+
class << self
|
4
|
+
# This method is deprecated in favor of find with the :conditions option.
|
5
|
+
#
|
6
|
+
# Works like find, but the record matching +id+ must also meet the +conditions+.
|
7
|
+
# +RecordNotFound+ is raised if no record can be found matching the +id+ or meeting the condition.
|
8
|
+
# Example:
|
9
|
+
# Person.find_on_conditions 5, "first_name LIKE '%dav%' AND last_name = 'heinemeier'"
|
10
|
+
def find_on_conditions(ids, conditions) # :nodoc:
|
11
|
+
find(ids, :conditions => conditions)
|
12
|
+
end
|
13
|
+
|
14
|
+
# This method is deprecated in favor of find(:first, options).
|
15
|
+
#
|
16
|
+
# Returns the object for the first record responding to the conditions in +conditions+,
|
17
|
+
# such as "group = 'master'". If more than one record is returned from the query, it's the first that'll
|
18
|
+
# be used to create the object. In such cases, it might be beneficial to also specify
|
19
|
+
# +orderings+, like "income DESC, name", to control exactly which record is to be used. Example:
|
20
|
+
# Employee.find_first "income > 50000", "income DESC, name"
|
21
|
+
def find_first(conditions = nil, orderings = nil, joins = nil) # :nodoc:
|
22
|
+
find(:first, :conditions => conditions, :order => orderings, :joins => joins)
|
23
|
+
end
|
24
|
+
|
25
|
+
# This method is deprecated in favor of find(:all, options).
|
26
|
+
#
|
27
|
+
# Returns an array of all the objects that could be instantiated from the associated
|
28
|
+
# table in the database. The +conditions+ can be used to narrow the selection of objects (WHERE-part),
|
29
|
+
# such as by "color = 'red'", and arrangement of the selection can be done through +orderings+ (ORDER BY-part),
|
30
|
+
# such as by "last_name, first_name DESC". A maximum of returned objects and their offset can be specified in
|
31
|
+
# +limit+ with either just a single integer as the limit or as an array with the first element as the limit,
|
32
|
+
# the second as the offset. Examples:
|
33
|
+
# Project.find_all "category = 'accounts'", "last_accessed DESC", 15
|
34
|
+
# Project.find_all ["category = ?", category_name], "created ASC", [15, 20]
|
35
|
+
def find_all(conditions = nil, orderings = nil, limit = nil, joins = nil) # :nodoc:
|
36
|
+
limit, offset = limit.is_a?(Array) ? limit : [ limit, nil ]
|
37
|
+
find(:all, :conditions => conditions, :order => orderings, :joins => joins, :limit => limit, :offset => offset)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -185,7 +185,10 @@ class Fixtures < Hash
|
|
185
185
|
DEFAULT_FILTER_RE = /\.ya?ml$/
|
186
186
|
|
187
187
|
def self.instantiate_fixtures(object, table_name, fixtures, load_instances=true)
|
188
|
-
|
188
|
+
old_logger_level = ActiveRecord::Base.logger.level
|
189
|
+
ActiveRecord::Base.logger.level = Logger::ERROR
|
190
|
+
|
191
|
+
object.instance_variable_set "@#{table_name.to_s.gsub('.','_')}", fixtures
|
189
192
|
if load_instances
|
190
193
|
fixtures.each do |name, fixture|
|
191
194
|
if model = fixture.find
|
@@ -193,12 +196,14 @@ class Fixtures < Hash
|
|
193
196
|
end
|
194
197
|
end
|
195
198
|
end
|
199
|
+
|
200
|
+
ActiveRecord::Base.logger.level = old_logger_level
|
196
201
|
end
|
197
202
|
|
198
203
|
def self.instantiate_all_loaded_fixtures(object, load_instances=true)
|
199
204
|
all_loaded_fixtures.each do |table_name, fixtures|
|
200
205
|
Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances)
|
201
|
-
end
|
206
|
+
end
|
202
207
|
end
|
203
208
|
|
204
209
|
cattr_accessor :all_loaded_fixtures
|
@@ -238,7 +243,7 @@ class Fixtures < Hash
|
|
238
243
|
pk = eval("#{table_class}::primary_key")
|
239
244
|
if pk == 'id'
|
240
245
|
connection.execute(
|
241
|
-
"SELECT setval('
|
246
|
+
"SELECT setval('#{table.to_s}_id_seq', (SELECT MAX(id) FROM #{table.to_s}), true)",
|
242
247
|
'Setting Sequence'
|
243
248
|
)
|
244
249
|
end
|
@@ -246,6 +251,8 @@ class Fixtures < Hash
|
|
246
251
|
end
|
247
252
|
end
|
248
253
|
|
254
|
+
attr_reader :table_name
|
255
|
+
|
249
256
|
def initialize(connection, table_name, fixture_path, file_filter = DEFAULT_FILTER_RE)
|
250
257
|
@connection, @table_name, @fixture_path, @file_filter = connection, table_name, fixture_path, file_filter
|
251
258
|
@class_name = Inflector.classify(@table_name)
|
@@ -342,7 +349,8 @@ class Fixture #:nodoc:
|
|
342
349
|
end
|
343
350
|
|
344
351
|
def key_list
|
345
|
-
@fixture.keys.
|
352
|
+
columns = @fixture.keys.collect{ |column_name| ActiveRecord::Base.connection.quote_column_name(column_name) }
|
353
|
+
columns.join(", ")
|
346
354
|
end
|
347
355
|
|
348
356
|
def value_list
|
@@ -464,30 +472,35 @@ module Test #:nodoc:
|
|
464
472
|
private
|
465
473
|
def load_fixtures
|
466
474
|
@loaded_fixtures = {}
|
467
|
-
|
468
|
-
|
475
|
+
fixtures = Fixtures.create_fixtures(fixture_path, fixture_table_names)
|
476
|
+
unless fixtures.nil?
|
477
|
+
if fixtures.instance_of?(Fixtures)
|
478
|
+
@loaded_fixtures[fixtures.table_name] = fixtures
|
479
|
+
else
|
480
|
+
fixtures.each { |f| @loaded_fixtures[f.table_name] = f }
|
481
|
+
end
|
469
482
|
end
|
470
483
|
end
|
471
|
-
|
484
|
+
|
472
485
|
# for pre_loaded_fixtures, only require the classes once. huge speed improvement
|
473
486
|
@@required_fixture_classes = false
|
474
|
-
|
487
|
+
|
475
488
|
def instantiate_fixtures
|
476
489
|
if pre_loaded_fixtures
|
477
490
|
raise RuntimeError, 'Load fixtures before instantiating them.' if Fixtures.all_loaded_fixtures.empty?
|
478
491
|
unless @@required_fixture_classes
|
479
|
-
self.class.require_fixture_classes Fixtures.all_loaded_fixtures.keys
|
492
|
+
self.class.require_fixture_classes Fixtures.all_loaded_fixtures.keys
|
480
493
|
@@required_fixture_classes = true
|
481
494
|
end
|
482
495
|
Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
|
483
|
-
else
|
496
|
+
else
|
484
497
|
raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
|
485
498
|
@loaded_fixtures.each do |table_name, fixtures|
|
486
499
|
Fixtures.instantiate_fixtures(self, table_name, fixtures, load_instances?)
|
487
500
|
end
|
488
501
|
end
|
489
502
|
end
|
490
|
-
|
503
|
+
|
491
504
|
def load_instances?
|
492
505
|
use_instantiated_fixtures != :no_instances
|
493
506
|
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
require 'singleton'
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
|
-
#
|
5
|
-
#
|
6
|
-
# normally comes when the model class is burdened with
|
7
|
-
# the core
|
4
|
+
# Observer classes respond to lifecycle callbacks to implement trigger-like
|
5
|
+
# behavior outside the original class. This is a great way to reduce the
|
6
|
+
# clutter that normally comes when the model class is burdened with
|
7
|
+
# functionality that doesn't pertain to the core responsibility of the
|
8
|
+
# class. Example:
|
8
9
|
#
|
9
10
|
# class CommentObserver < ActiveRecord::Observer
|
10
11
|
# def after_save(comment)
|
@@ -12,13 +13,13 @@ module ActiveRecord
|
|
12
13
|
# end
|
13
14
|
# end
|
14
15
|
#
|
15
|
-
# This Observer
|
16
|
+
# This Observer sends an email when a Comment#save is finished.
|
16
17
|
#
|
17
|
-
# == Observing a class that can't be
|
18
|
+
# == Observing a class that can't be inferred
|
18
19
|
#
|
19
20
|
# Observers will by default be mapped to the class with which they share a name. So CommentObserver will
|
20
21
|
# be tied to observing Comment, ProductManagerObserver to ProductManager, and so on. If you want to name your observer
|
21
|
-
#
|
22
|
+
# differently than the class you're interested in observing, you can use the Observer.observe class method:
|
22
23
|
#
|
23
24
|
# class AuditObserver < ActiveRecord::Observer
|
24
25
|
# observe Account
|
@@ -28,9 +29,7 @@ module ActiveRecord
|
|
28
29
|
# end
|
29
30
|
# end
|
30
31
|
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
# If the audit observer needs to watch more than one kind of object, this can be specified in an array, like this:
|
32
|
+
# If the audit observer needs to watch more than one kind of object, this can be specified with multiple arguments:
|
34
33
|
#
|
35
34
|
# class AuditObserver < ActiveRecord::Observer
|
36
35
|
# observe Account, Balance
|
@@ -42,7 +41,14 @@ module ActiveRecord
|
|
42
41
|
#
|
43
42
|
# The AuditObserver will now act on both updates to Account and Balance by treating them both as records.
|
44
43
|
#
|
44
|
+
# == Available callback methods
|
45
|
+
#
|
45
46
|
# The observer can implement callback methods for each of the methods described in the Callbacks module.
|
47
|
+
#
|
48
|
+
# == Triggering Observers
|
49
|
+
#
|
50
|
+
# In order to activate an observer, you need to call Observer.instance. In Rails, this can be done in controllers
|
51
|
+
# using the short-hand of for example observer :comment_observer.
|
46
52
|
class Observer
|
47
53
|
include Singleton
|
48
54
|
|
@@ -75,4 +81,4 @@ module ActiveRecord
|
|
75
81
|
self.class.name.scan(/(.*)Observer/)[0][0]
|
76
82
|
end
|
77
83
|
end
|
78
|
-
end
|
84
|
+
end
|
@@ -114,7 +114,11 @@ module ActiveRecord
|
|
114
114
|
# Holds all the meta-data about an association as it was specified in the Active Record class.
|
115
115
|
class AssociationReflection < MacroReflection #:nodoc:
|
116
116
|
def klass
|
117
|
-
active_record.send(:compute_type, (name_to_class_name(name.id2name)))
|
117
|
+
@klass ||= active_record.send(:compute_type, (name_to_class_name(name.id2name)))
|
118
|
+
end
|
119
|
+
|
120
|
+
def table_name
|
121
|
+
@table_name ||= klass.table_name
|
118
122
|
end
|
119
123
|
|
120
124
|
private
|
@@ -6,6 +6,9 @@ module ActiveRecord
|
|
6
6
|
module Transactions # :nodoc:
|
7
7
|
TRANSACTION_MUTEX = Mutex.new
|
8
8
|
|
9
|
+
class TransactionError < ActiveRecordError # :nodoc:
|
10
|
+
end
|
11
|
+
|
9
12
|
def self.append_features(base)
|
10
13
|
super
|
11
14
|
base.extend(ClassMethods)
|
@@ -78,6 +81,9 @@ module ActiveRecord
|
|
78
81
|
# Tribute: Object-level transactions are implemented by Transaction::Simple by Austin Ziegler.
|
79
82
|
module ClassMethods
|
80
83
|
def transaction(*objects, &block)
|
84
|
+
previous_handler = trap('TERM') do
|
85
|
+
raise TransactionError, "Transaction aborted"
|
86
|
+
end
|
81
87
|
lock_mutex
|
82
88
|
|
83
89
|
begin
|
@@ -93,6 +99,7 @@ module ActiveRecord
|
|
93
99
|
raise
|
94
100
|
ensure
|
95
101
|
unlock_mutex
|
102
|
+
trap('TERM', previous_handler)
|
96
103
|
end
|
97
104
|
end
|
98
105
|
|
@@ -1,4 +1,7 @@
|
|
1
1
|
module ActiveRecord
|
2
|
+
class RecordInvalid < ActiveRecordError #:nodoc:
|
3
|
+
end
|
4
|
+
|
2
5
|
# Active Record validation is reported to and from this object, which is used by Base#save to
|
3
6
|
# determine whether the object in a valid state to be saved. See usage example in Validations.
|
4
7
|
class Errors
|
@@ -291,16 +294,18 @@ module ActiveRecord
|
|
291
294
|
# Configuration options:
|
292
295
|
# * <tt>message</tt> - A custom error message (default is: "can't be empty")
|
293
296
|
# * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update)
|
297
|
+
# * <tt>accept</tt> - Specifies value that is considered accepted. The default value is a string "1", which
|
298
|
+
# makes it easy to relate to an HTML checkbox.
|
294
299
|
#
|
295
|
-
|
300
|
+
|
296
301
|
def validates_acceptance_of(*attr_names)
|
297
|
-
configuration = { :message => ActiveRecord::Errors.default_error_messages[:accepted], :on => :save, :allow_nil => true }
|
302
|
+
configuration = { :message => ActiveRecord::Errors.default_error_messages[:accepted], :on => :save, :allow_nil => true, :accept => "1" }
|
298
303
|
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
299
304
|
|
300
305
|
attr_accessor *attr_names
|
301
306
|
|
302
307
|
validates_each(attr_names,configuration) do |record, attr_name, value|
|
303
|
-
record.errors.add(attr_name, configuration[:message]) unless value ==
|
308
|
+
record.errors.add(attr_name, configuration[:message]) unless value == configuration[:accept]
|
304
309
|
end
|
305
310
|
end
|
306
311
|
|
@@ -354,12 +359,12 @@ module ActiveRecord
|
|
354
359
|
# Ensure that one and only one range option is specified.
|
355
360
|
range_options = ALL_RANGE_OPTIONS & options.keys
|
356
361
|
case range_options.size
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
362
|
+
when 0
|
363
|
+
raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.'
|
364
|
+
when 1
|
365
|
+
# Valid number of options; do nothing.
|
366
|
+
else
|
367
|
+
raise ArgumentError, 'Too many range options specified. Choose only one.'
|
363
368
|
end
|
364
369
|
|
365
370
|
# Get range option and value.
|
@@ -367,33 +372,21 @@ module ActiveRecord
|
|
367
372
|
option_value = options[range_options.first]
|
368
373
|
|
369
374
|
# Declare different validations per option.
|
370
|
-
|
375
|
+
|
376
|
+
validity_checks = { :is => "==", :minimum => ">=", :maximum => "<=" }
|
377
|
+
message_options = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }
|
378
|
+
|
379
|
+
case option
|
371
380
|
when :within, :in
|
372
|
-
raise ArgumentError, ':within must be a Range' unless option_value.is_a?(Range)
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
raise ArgumentError, ':is must be a nonnegative Integer' unless option_value.is_a?(Integer) and option_value >= 0
|
379
|
-
message = options[:message] || options[:wrong_length]
|
380
|
-
message = (message % option_value) rescue message
|
381
|
-
validates_each(attrs, options) do |record, attr, value|
|
382
|
-
record.errors.add(attr, message) if value.nil? or value.size != option_value
|
383
|
-
end
|
384
|
-
when :minimum
|
385
|
-
raise ArgumentError, ':minimum must be a nonnegative Integer' unless option_value.is_a?(Integer) and option_value >= 0
|
386
|
-
message = options[:message] || options[:too_short]
|
381
|
+
raise ArgumentError, ':within must be a Range' unless option_value.is_a?(Range) # '
|
382
|
+
validates_length_of attrs, :minimum => option_value.begin, :allow_nil => options[:allow_nil]
|
383
|
+
validates_length_of attrs, :maximum => option_value.end, :allow_nil => options[:allow_nil]
|
384
|
+
when :is, :minimum, :maximum
|
385
|
+
raise ArgumentError, ":#{option} must be a nonnegative Integer" unless option_value.is_a?(Integer) and option_value >= 0 # '
|
386
|
+
message = options[:message] || options[message_options[option]]
|
387
387
|
message = (message % option_value) rescue message
|
388
388
|
validates_each(attrs, options) do |record, attr, value|
|
389
|
-
record.errors.add(attr, message) if value.nil? or value.size
|
390
|
-
end
|
391
|
-
when :maximum
|
392
|
-
raise ArgumentError, ':maximum must be a nonnegative Integer' unless option_value.is_a?(Integer) and option_value >= 0
|
393
|
-
message = options[:message] || options[:too_long]
|
394
|
-
message = (message % option_value) rescue message
|
395
|
-
validates_each(attrs, options) do |record, attr, value|
|
396
|
-
record.errors.add(attr, message) if value.nil? or value.size > option_value
|
389
|
+
record.errors.add(attr, message) if value.nil? or !value.size.method(validity_checks[option])[option_value]
|
397
390
|
end
|
398
391
|
end
|
399
392
|
end
|
@@ -562,6 +555,12 @@ module ActiveRecord
|
|
562
555
|
if perform_validation && valid? || !perform_validation then save_without_validation else false end
|
563
556
|
end
|
564
557
|
|
558
|
+
# Attempts to save the record just like Base.save but will raise a RecordInvalid exception instead of returning false
|
559
|
+
# if the record is not valid.
|
560
|
+
def save!
|
561
|
+
valid? ? save_without_validation : raise(RecordInvalid)
|
562
|
+
end
|
563
|
+
|
565
564
|
# Updates a single attribute and saves the record without going through the normal validation procedure.
|
566
565
|
# This is especially useful for boolean flags on existing records. The regular +update_attribute+ method
|
567
566
|
# in Base is replaced with this when the validations module is mixed in, which it is by default.
|