activerecord 1.8.0 → 1.9.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 CHANGED
@@ -1,3 +1,37 @@
1
+ *1.9.0* (22th March, 2005)
2
+
3
+ * Added adapter independent limit clause as a two-element array with the first being the limit, the second being the offset #795 [Sam Stephenson]. Example:
4
+
5
+ Developer.find_all nil, 'id ASC', 5 # return the first five developers
6
+ Developer.find_all nil, 'id ASC', [3, 8] # return three developers, starting from #8 and forward
7
+
8
+ This doesn't yet work with the DB2 or MS SQL adapters. Patches to make that happen are encouraged.
9
+
10
+ * Added alias_method :to_param, :id to Base, such that Active Record objects to be used as URL parameters in Action Pack automatically #812 [Nicholas Seckar/Sam Stephenson]
11
+
12
+ * Improved the performance of the OCI8 adapter for Oracle #723 [pilx/gjenkins]
13
+
14
+ * Added type conversion before saving a record, so string-based values like "10.0" aren't left for the database to convert #820 [dave@cherryville.org]
15
+
16
+ * Added with additional settings for working with transactional fixtures and pre-loaded test databases #865 [mindel]
17
+
18
+ * Fixed acts_as_list to trigger remove_from_list on destroy after the fact, not before, so a unique position can be maintained #871 [Alisdair McDiarmid]
19
+
20
+ * Added the possibility of specifying fixtures in multiple calls #816 [kim@tinker.com]
21
+
22
+ * Added Base.exists?(id) that'll return true if an object of the class with the given id exists #854 [stian@grytoyr.net]
23
+
24
+ * Added optionally allow for nil or empty strings with validates_numericality_of #801 [Sebastian Kanthak]
25
+
26
+ * Fixed problem with using slashes in validates_format_of regular expressions #801 [Sebastian Kanthak]
27
+
28
+ * Fixed that SQLite3 exceptions are caught and reported properly #823 [yerejm]
29
+
30
+ * Added that all types of after_find/after_initialized callbacks are triggered if the explicit implementation is present, not only the explicit implementation itself
31
+
32
+ * Fixed that symbols can be used on attribute assignment, like page.emails.create(:subject => data.subject, :body => data.body)
33
+
34
+
1
35
  *1.8.0* (7th March, 2005)
2
36
 
3
37
  * Added ActiveRecord::Base.colorize_logging to control whether to use colors in logs or not (on by default)
@@ -60,7 +60,7 @@ module ActiveRecord
60
60
 
61
61
  #{scope_condition_method}
62
62
 
63
- before_destroy :remove_from_list
63
+ after_destroy :remove_from_list
64
64
  before_create :add_to_list_bottom
65
65
  EOV
66
66
  end
@@ -326,6 +326,13 @@ module ActiveRecord #:nodoc:
326
326
  end
327
327
  end
328
328
 
329
+ # Returns true if the given +id+ represents the primary key of a record in the database, false otherwise.
330
+ # Example:
331
+ # Person.exists?(5)
332
+ def exists?(id)
333
+ !find_first("#{primary_key} = #{sanitize(id)}").nil?
334
+ end
335
+
329
336
  # This method is deprecated in favor of find with the :conditions option.
330
337
  # Works like find, but the record matching +id+ must also meet the +conditions+.
331
338
  # +RecordNotFound+ is raised if no record can be found matching the +id+ or meeting the condition.
@@ -339,16 +346,18 @@ module ActiveRecord #:nodoc:
339
346
  # table in the database. The +conditions+ can be used to narrow the selection of objects (WHERE-part),
340
347
  # such as by "color = 'red'", and arrangement of the selection can be done through +orderings+ (ORDER BY-part),
341
348
  # such as by "last_name, first_name DESC". A maximum of returned objects and their offset can be specified in
342
- # +limit+ (LIMIT...OFFSET-part). Examples:
349
+ # +limit+ with either just a single integer as the limit or as an array with the first element as the limit,
350
+ # the second as the offset. Examples:
343
351
  # Project.find_all "category = 'accounts'", "last_accessed DESC", 15
344
- # Project.find_all ["category = ?", category_name], "created ASC", ["? OFFSET ?", 15, 20]
352
+ # Project.find_all ["category = ?", category_name], "created ASC", [15, 20]
345
353
  def find_all(conditions = nil, orderings = nil, limit = nil, joins = nil)
346
354
  sql = "SELECT * FROM #{table_name} "
347
355
  sql << "#{joins} " if joins
348
356
  add_conditions!(sql, conditions)
349
357
  sql << "ORDER BY #{orderings} " unless orderings.nil?
350
358
 
351
- connection.add_limit!(sql, sanitize_sql(limit)) unless limit.nil?
359
+ limit = sanitize_sql(limit) if limit.is_a? Array and limit.first.is_a? String
360
+ connection.add_limit!(sql, limit) if limit
352
361
 
353
362
  find_by_sql(sql)
354
363
  end
@@ -522,7 +531,7 @@ module ActiveRecord #:nodoc:
522
531
  # "mice" table. Example:
523
532
  #
524
533
  # class Mouse < ActiveRecord::Base
525
- # table_name "mice"
534
+ # set_table_name "mice"
526
535
  # end
527
536
  def table_name
528
537
  table_name_prefix + undecorated_table_name(class_name_of_active_record_descendant(self)) + table_name_suffix
@@ -889,6 +898,9 @@ module ActiveRecord #:nodoc:
889
898
  read_attribute(self.class.primary_key)
890
899
  end
891
900
 
901
+ # Enables Active Record objects to be used as URL parameters in Action Pack automatically.
902
+ alias_method :to_param, :id
903
+
892
904
  def id_before_type_cast #:nodoc:
893
905
  read_attribute_before_type_cast(self.class.primary_key)
894
906
  end
@@ -1012,6 +1024,7 @@ module ActiveRecord #:nodoc:
1012
1024
  # attributes not included in that won't be allowed to be mass-assigned.
1013
1025
  def attributes=(attributes)
1014
1026
  return if attributes.nil?
1027
+ attributes.stringify_keys!
1015
1028
 
1016
1029
  multi_parameter_attributes = []
1017
1030
  remove_attributes_protected_from_mass_assignment(attributes).each do |k, v|
@@ -1233,7 +1246,7 @@ module ActiveRecord #:nodoc:
1233
1246
  def attributes_with_quotes(include_primary_key = true)
1234
1247
  columns_hash = self.class.columns_hash
1235
1248
 
1236
- attrs_quoted = @attributes.inject({}) do |attrs_quoted, pair|
1249
+ attrs_quoted = attributes.inject({}) do |attrs_quoted, pair|
1237
1250
  attrs_quoted[pair.first] = quote(pair.last, columns_hash[pair.first]) unless !include_primary_key && pair.first == self.class.primary_key
1238
1251
  attrs_quoted
1239
1252
  end
@@ -78,6 +78,10 @@ module ActiveRecord
78
78
  # you want to ensure that a certain callback is called for the entire hierarchy and the regular overwriteable methods when you
79
79
  # want to leave it up to each descendent to decide whether they want to call +super+ and trigger the inherited callbacks.
80
80
  #
81
+ # *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the callbacks before specifying the
82
+ # associations. Otherwise, you might trigger the loading of a child before the parent has registered the callbacks and they won't
83
+ # be inherited.
84
+ #
81
85
  # == Types of callbacks
82
86
  #
83
87
  # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
@@ -117,7 +121,7 @@ module ActiveRecord
117
121
  # record.credit_card_number = decrypt(record.credit_card_number)
118
122
  # end
119
123
  #
120
- # alias_method :after_initialize, :after_save
124
+ # alias_method :after_find, :after_save
121
125
  #
122
126
  # private
123
127
  # def encrypt(value)
@@ -151,8 +155,8 @@ module ActiveRecord
151
155
  #
152
156
  # Because after_find and after_initialize is called for each object instantiated found by a finder, such as Base.find_all, we've had
153
157
  # to implement a simple performance constraint (50% more speed on a simple test case). Unlike all the other callbacks, after_find and
154
- # after_initialize can only be declared using an explicit implementation. So using the inheritable callback queue for after_find and
155
- # after_initialize won't work.
158
+ # after_initialize will only be run if an explicit implementation is defined (<tt>def after_find</tt>). In that case, all of the
159
+ # callback types will be called.
156
160
  #
157
161
  # == Cancelling callbacks
158
162
  #
@@ -209,8 +213,19 @@ module ActiveRecord
209
213
  module ClassMethods #:nodoc:
210
214
  def instantiate_with_callbacks(record)
211
215
  object = instantiate_without_callbacks(record)
212
- object.send(:invoke_and_notify, :after_find)
213
- object.send(:invoke_and_notify, :after_initialize)
216
+
217
+ if object.send(:respond_to_without_attributes?, :after_find)
218
+ object.send(:callback, :after_find)
219
+ else
220
+ object.send(:invoke_and_notify, :after_find)
221
+ end
222
+
223
+ if object.send(:respond_to_without_attributes?, :after_initialize)
224
+ object.send(:callback, :after_initialize)
225
+ else
226
+ object.send(:invoke_and_notify, :after_initialize)
227
+ end
228
+
214
229
  object
215
230
  end
216
231
  end
@@ -223,7 +238,7 @@ module ActiveRecord
223
238
  def initialize_with_callbacks(attributes = nil) #:nodoc:
224
239
  initialize_without_callbacks(attributes)
225
240
  result = yield self if block_given?
226
- invoke_and_notify(:after_initialize)
241
+ respond_to_without_attributes?(:after_initialize) ? callback(:after_initialize) : invoke_and_notify(:after_initialize)
227
242
  result
228
243
  end
229
244
 
@@ -179,7 +179,7 @@ module ActiveRecord
179
179
  case type
180
180
  when :string then value
181
181
  when :text then value
182
- when :integer then value.to_i
182
+ when :integer then value.to_i rescue value ? 1 : 0
183
183
  when :float then value.to_f
184
184
  when :datetime then string_to_time(value)
185
185
  when :timestamp then string_to_time(value)
@@ -353,13 +353,25 @@ module ActiveRecord
353
353
  def structure_dump() end
354
354
 
355
355
  def add_limit!(sql, limit)
356
+ if limit.is_a? Array
357
+ limit, offset = *limit
358
+ add_limit_with_offset!(sql, limit.to_i, offset.to_i)
359
+ else
360
+ add_limit_without_offset!(sql, limit)
361
+ end
362
+ end
363
+
364
+ def add_limit_with_offset!(sql, limit, offset)
365
+ sql << " LIMIT #{limit} OFFSET #{offset}"
366
+ end
367
+
368
+ def add_limit_without_offset!(sql, limit)
356
369
  sql << " LIMIT #{limit}"
357
370
  end
358
371
 
359
-
360
372
  def initialize_schema_information
361
373
  begin
362
- execute "CREATE TABLE schema_info (version #{native_database_types[:integer]})"
374
+ execute "CREATE TABLE schema_info (version #{native_database_types[:integer][:name]}#{native_database_types[:integer][:limit]})"
363
375
  insert "INSERT INTO schema_info (version) VALUES(0)"
364
376
  rescue ActiveRecord::StatementInvalid
365
377
  # Schema has been intialized
@@ -377,8 +389,9 @@ module ActiveRecord
377
389
  end
378
390
 
379
391
  def add_column(table_name, column_name, type, options = {})
380
- add_column_sql = "ALTER TABLE #{table_name} ADD #{column_name} #{native_database_types[type]}"
381
- add_column_sql << "(#{limit})" if options[:limit]
392
+ native_type = native_database_types[type]
393
+ add_column_sql = "ALTER TABLE #{table_name} ADD #{column_name} #{native_type[:name]}"
394
+ add_column_sql << "(#{options[:limit] || native_type[:limit]})" if options[:limit] || native_type[:limit]
382
395
  add_column_sql << " DEFAULT '#{options[:default]}'" if options[:default]
383
396
  execute(add_column_sql)
384
397
  end
@@ -404,7 +417,7 @@ module ActiveRecord
404
417
  log_info(sql, name, 0)
405
418
  nil
406
419
  end
407
- rescue => e
420
+ rescue Exception => e
408
421
  log_info("#{e.message}: #{sql}", name, 0)
409
422
  raise ActiveRecord::StatementInvalid, "#{e.message}: #{sql}"
410
423
  end
@@ -452,4 +465,4 @@ module ActiveRecord
452
465
  end
453
466
  end
454
467
  end
455
- end
468
+ end
@@ -89,7 +89,11 @@ begin
89
89
  s.gsub(/'/, "''") # ' (for ruby-mode)
90
90
  end
91
91
 
92
- def add_limit!(sql, limit)
92
+ def add_limit_with_offset!(sql, limit, offset)
93
+ raise ArgumentError, 'add_limit_with_offset! not implemented'
94
+ end
95
+
96
+ def add_limit_without_offset!(sql, limit)
93
97
  sql << " FETCH FIRST #{limit} ROWS ONLY"
94
98
  end
95
99
 
@@ -67,16 +67,16 @@ module ActiveRecord
67
67
  def native_database_types
68
68
  {
69
69
  :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
70
- :string => "varchar(255)",
71
- :text => "text",
72
- :integer => "int(11)",
73
- :float => "float",
74
- :datetime => "datetime",
75
- :timestamp => "datetime",
76
- :time => "datetime",
77
- :date => "date",
78
- :binary => "blob",
79
- :boolean => "tinyint(1)"
70
+ :string => { :name => "varchar", :limit => 255 },
71
+ :text => { :name => "text" },
72
+ :integer => { :name => "int", :limit => 11 },
73
+ :float => { :name => "float" },
74
+ :datetime => { :name => "datetime" },
75
+ :timestamp => { :name => "datetime" },
76
+ :time => { :name => "datetime" },
77
+ :date => { :name => "date" },
78
+ :binary => { :name => "blob" },
79
+ :boolean => { :name => "tinyint", :limit => 1 }
80
80
  }
81
81
  end
82
82
 
@@ -172,7 +172,11 @@ module ActiveRecord
172
172
  structure += select_one("SHOW CREATE TABLE #{table.to_a.first.last}")["Create Table"] + ";\n\n"
173
173
  end
174
174
  end
175
-
175
+
176
+ def add_limit_with_offset!(sql, limit, offset)
177
+ sql << " LIMIT #{offset}, #{limit}"
178
+ end
179
+
176
180
  def recreate_database(name)
177
181
  drop_database(name)
178
182
  create_database(name)
@@ -2,15 +2,15 @@
2
2
  # 1. I had to redefine a method in ActiveRecord to make it possible to implement an autonumbering
3
3
  # solution for oracle. It's implemented in a way that is intended to not break other adapters.
4
4
  # 2. Default value support needs a patch to the OCI8 driver, to enable it to read LONG columns.
5
- # LONG is deprecated, and so may never be properly added to driver.
6
- # A similar patch is needed for TIMESTAMP.
7
- # This is dangerous because it may break with newer versions of the driver.
5
+ # The driver-author has said he will add this in a future release.
6
+ # A similar patch is needed for TIMESTAMP. This should be replaced with the 0.2 version of the
7
+ # driver, which will support TIMESTAMP properly.
8
8
  # 3. Large Object support works by an after_save callback added to the ActiveRecord. This is not
9
9
  # a problem - you can add other (chained) after_save callbacks.
10
- # 4. LIMIT and OFFSET work by scrolling through a cursor - no rownum select from select required.
11
- # It does mean that large OFFSETs will have to scroll through the intervening records. To keep
12
- # consistency with other adapters I've allowed the LIMIT and OFFSET clauses to be included in
13
- # the sql string and later extracted them by parsing the string.
10
+ # 4. LIMIT and OFFSET now work using a select from select from select. This pattern enables
11
+ # the middle select to limit downwards as much as possible, before the outermost select
12
+ # limits upwards. The extra rownum column is stripped from the results.
13
+ # See http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:127412348064
14
14
  #
15
15
  # Do what you want with this code, at your own peril, but if any significant portion of my code
16
16
  # remains then please acknowledge my contribution.
@@ -118,13 +118,17 @@ begin
118
118
  end
119
119
 
120
120
  def select_all(sql, name = nil)
121
- offset = sql =~ /OFFSET (\d+)$/ ? $1.to_i : -1
121
+ offset = sql =~ /OFFSET (\d+)$/ ? $1.to_i : 0
122
122
  sql, limit = $1, $2.to_i if sql =~ /(.*)(?: LIMIT[= ](\d+))(\s*OFFSET \d+)?$/
123
+ if limit
124
+ sql = "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset+limit}) where raw_rnum_ > #{offset}"
125
+ elsif offset > 0
126
+ sql = "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_) where raw_rnum_ > #{offset}"
127
+ end
123
128
  cursor = log(sql, name, @connection) { @connection.exec sql }
124
129
  cols = cursor.get_col_names.map { |x| x.downcase }
125
130
  rows = []
126
131
  while row = cursor.fetch
127
- next if cursor.row_count <= offset
128
132
  hash = Hash.new
129
133
  cols.each_with_index { |col, i|
130
134
  hash[col] = case row[i]
@@ -134,10 +138,9 @@ begin
134
138
  (row[i].hour == 0 and row[i].minute == 0 and row[i].second == 0) ?
135
139
  row[i].to_date : row[i].to_time
136
140
  else row[i]
137
- end
141
+ end unless col == 'raw_rnum_'
138
142
  }
139
143
  rows << hash
140
- break if rows.size == limit
141
144
  end
142
145
  rows
143
146
  ensure
@@ -179,10 +182,6 @@ begin
179
182
  alias :update :execute
180
183
  alias :delete :execute
181
184
 
182
- def add_limit!(sql, limit)
183
- sql << "LIMIT=" << limit.to_s
184
- end
185
-
186
185
  def begin_db_transaction()
187
186
  @connection.autocommit = false
188
187
  end
@@ -253,19 +253,12 @@ EOL
253
253
  "[#{name}]"
254
254
  end
255
255
 
256
- def add_limit!(sql, limit)
257
- if sql =~ /LIMIT/i
258
- limit = sql.slice!(/LIMIT.*/).gsub(/LIMIT.(.*)$/, '\1')
259
- end
260
- if !limit.nil?
261
- limit_amount = limit.to_s.include?("OFFSET") ? get_offset_amount(limit) : Array.new([limit])
262
- order_by = sql.include?("ORDER BY") ? get_order_by(sql.sub(/.*ORDER\sBY./, "")) : nil
263
- if limit_amount.size == 2
264
- sql.gsub!(/SELECT/i, "SELECT * FROM ( SELECT TOP #{limit_amount[0]} * FROM ( SELECT TOP #{limit_amount[1]}")<<" ) AS tmp1 ORDER BY #{order_by[1]} ) AS tmp2 ORDER BY #{order_by[0]}"
265
- else
266
- sql.gsub!(/SELECT/i, "SELECT TOP #{limit_amount[0]}")
267
- end
268
- end
256
+ def add_limit_with_offset!(sql, limit, offset)
257
+ raise ArgumentError, 'add_limit_with_offset! not implemented'
258
+ end
259
+
260
+ def add_limit_without_offset!(sql, limit)
261
+ raise ArgumentError, 'add_limit_without_offset! not implemented'
269
262
  end
270
263
 
271
264
  def recreate_database(name)
@@ -121,6 +121,14 @@ require 'csv'
121
121
  # from a CSV fixture file would be accessible via @web_sites["web_site_1"]["name"] == "Ruby on Rails" and have the individual
122
122
  # fixtures available as instance variables @web_site_1 and @web_site_2.
123
123
  #
124
+ # If you do not wish to use instantiated fixtures (usually for performance reasons) there are two options.
125
+ #
126
+ # - to completely disable instantiated fixtures:
127
+ # self.use_instantiated_fixtures = false
128
+ #
129
+ # - to keep the fixture instance (@web_sites) available, but do not automatically 'find' each instance:
130
+ # self.use_instantiated_fixtures = :no_instances
131
+ #
124
132
  # = Dynamic fixtures with ERb
125
133
  #
126
134
  # Some times you don't care about the content of the fixtures as much as you care about the volume. In these cases, you can
@@ -164,6 +172,9 @@ require 'csv'
164
172
  # If you preload your test database with all fixture data (probably in the Rakefile task) and use transactional fixtures,
165
173
  # then you may omit all fixtures declarations in your test cases since all the data's already there and every case rolls back its changes.
166
174
  #
175
+ # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to true. This will provide
176
+ # access to fixture data for every table that has been loaded through fixtures (depending on the value of +use_instantiated_fixtures+)
177
+ #
167
178
  # When *not* to use transactional fixtures:
168
179
  # 1. You're testing whether a transaction works correctly. Nested transactions don't commit until all parent transactions commit,
169
180
  # particularly, the fixtures transaction which is begun in setup and rolled back in teardown. Thus, you won't be able to verify
@@ -173,14 +184,25 @@ require 'csv'
173
184
  class Fixtures < Hash
174
185
  DEFAULT_FILTER_RE = /\.ya?ml$/
175
186
 
176
- def self.instantiate_fixtures(object, table_name, fixtures)
187
+ def self.instantiate_fixtures(object, table_name, fixtures, load_instances=true)
177
188
  object.instance_variable_set "@#{table_name}", fixtures
178
- fixtures.each do |name, fixture|
179
- if model = fixture.find
180
- object.instance_variable_set "@#{name}", model
189
+ if load_instances
190
+ fixtures.each do |name, fixture|
191
+ if model = fixture.find
192
+ object.instance_variable_set "@#{name}", model
193
+ end
181
194
  end
182
195
  end
183
196
  end
197
+
198
+ def self.instantiate_all_loaded_fixtures(object, load_instances=true)
199
+ all_loaded_fixtures.each do |table_name, fixtures|
200
+ Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances)
201
+ end
202
+ end
203
+
204
+ cattr_accessor :all_loaded_fixtures
205
+ self.all_loaded_fixtures = {}
184
206
 
185
207
  def self.create_fixtures(fixtures_directory, *table_names)
186
208
  connection = block_given? ? yield : ActiveRecord::Base.connection
@@ -189,15 +211,17 @@ class Fixtures < Hash
189
211
  begin
190
212
  ActiveRecord::Base.logger.level = Logger::ERROR
191
213
 
214
+ fixtures_map = {}
192
215
  fixtures = table_names.flatten.map do |table_name|
193
- Fixtures.new(connection, File.split(table_name.to_s).last, File.join(fixtures_directory, table_name.to_s))
194
- end
195
-
216
+ fixtures_map[table_name] = Fixtures.new(connection, File.split(table_name.to_s).last, File.join(fixtures_directory, table_name.to_s))
217
+ end
218
+ all_loaded_fixtures.merge! fixtures_map
219
+
196
220
  connection.transaction do
197
221
  fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures }
198
222
  fixtures.each { |fixture| fixture.insert_fixtures }
199
223
  end
200
-
224
+
201
225
  reset_sequences(connection, table_names) if connection.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
202
226
 
203
227
  return fixtures.size > 1 ? fixtures : fixtures.first
@@ -360,19 +384,21 @@ module Test #:nodoc:
360
384
  cattr_accessor :fixture_path
361
385
  class_inheritable_accessor :fixture_table_names
362
386
  class_inheritable_accessor :use_transactional_fixtures
363
- class_inheritable_accessor :use_instantiated_fixtures
387
+ class_inheritable_accessor :use_instantiated_fixtures # true, false, or :no_instances
388
+ class_inheritable_accessor :pre_loaded_fixtures
364
389
 
365
390
  self.fixture_table_names = []
366
391
  self.use_transactional_fixtures = false
367
392
  self.use_instantiated_fixtures = true
393
+ self.pre_loaded_fixtures = false
368
394
 
369
395
  def self.fixtures(*table_names)
370
- self.fixture_table_names = table_names.flatten
396
+ self.fixture_table_names |= table_names.flatten
371
397
  require_fixture_classes
372
398
  end
373
399
 
374
- def self.require_fixture_classes
375
- fixture_table_names.each do |table_name|
400
+ def self.require_fixture_classes(table_names=nil)
401
+ (table_names || fixture_table_names).each do |table_name|
376
402
  begin
377
403
  require Inflector.singularize(table_name.to_s)
378
404
  rescue LoadError
@@ -382,6 +408,10 @@ module Test #:nodoc:
382
408
  end
383
409
 
384
410
  def setup_with_fixtures
411
+ if pre_loaded_fixtures && !use_transactional_fixtures
412
+ raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
413
+ end
414
+
385
415
  # Load fixtures once and begin transaction.
386
416
  if use_transactional_fixtures
387
417
  load_fixtures unless @already_loaded_fixtures
@@ -438,13 +468,29 @@ module Test #:nodoc:
438
468
  @loaded_fixtures[table_name] = Fixtures.create_fixtures(fixture_path, table_name)
439
469
  end
440
470
  end
441
-
471
+
472
+ # for pre_loaded_fixtures, only require the classes once. huge speed improvement
473
+ @@required_fixture_classes = false
474
+
442
475
  def instantiate_fixtures
443
- raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
444
- @loaded_fixtures.each do |table_name, fixtures|
445
- Fixtures.instantiate_fixtures(self, table_name, fixtures)
476
+ if pre_loaded_fixtures
477
+ raise RuntimeError, 'Load fixtures before instantiating them.' if Fixtures.all_loaded_fixtures.empty?
478
+ unless @@required_fixture_classes
479
+ self.class.require_fixture_classes Fixtures.all_loaded_fixtures.keys
480
+ @@required_fixture_classes = true
481
+ end
482
+ Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
483
+ else
484
+ raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
485
+ @loaded_fixtures.each do |table_name, fixtures|
486
+ Fixtures.instantiate_fixtures(self, table_name, fixtures, load_instances?)
487
+ end
446
488
  end
447
489
  end
490
+
491
+ def load_instances?
492
+ use_instantiated_fixtures != :no_instances
493
+ end
448
494
  end
449
495
 
450
496
  end
@@ -1,8 +1,8 @@
1
1
  module ActiveRecord
2
- class IrreversibleMigration < ActiveRecordError
2
+ class IrreversibleMigration < ActiveRecordError#:nodoc:
3
3
  end
4
4
 
5
- class Migration
5
+ class Migration #:nodoc:
6
6
  class << self
7
7
  def up() end
8
8
  def down() end
@@ -14,7 +14,7 @@ module ActiveRecord
14
14
  end
15
15
  end
16
16
 
17
- class Migrator
17
+ class Migrator#:nodoc:
18
18
  class << self
19
19
  def up(migrations_path, target_version = nil)
20
20
  new(:up, migrations_path, target_version).migrate
@@ -270,11 +270,11 @@ module ActiveRecord
270
270
  def validates_confirmation_of(*attr_names)
271
271
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:confirmation], :on => :save }
272
272
  configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
273
- configuration[:message].gsub!(/\"/, '\\\\\"')
274
273
 
275
- for attr_name in attr_names
276
- attr_accessor "#{attr_name}_confirmation"
277
- class_eval(%(#{validation_method(configuration[:on])} %{errors.add('#{attr_name}', "#{configuration[:message]}") unless #{attr_name}_confirmation.nil? or #{attr_name} == #{attr_name}_confirmation}))
274
+ attr_accessor *(attr_names.map { |n| "#{n}_confirmation" })
275
+
276
+ validates_each(attr_names, configuration) do |record, attr_name, value|
277
+ record.errors.add(attr_name, configuration[:message]) unless record.send("#{attr_name}_confirmation").nil? or value == record.send("#{attr_name}_confirmation")
278
278
  end
279
279
  end
280
280
 
@@ -294,13 +294,13 @@ module ActiveRecord
294
294
  #
295
295
  # NOTE: The agreement is considered valid if it's set to the string "1". This makes it easy to relate it to an HTML checkbox.
296
296
  def validates_acceptance_of(*attr_names)
297
- configuration = { :message => ActiveRecord::Errors.default_error_messages[:accepted], :on => :save }
297
+ configuration = { :message => ActiveRecord::Errors.default_error_messages[:accepted], :on => :save, :allow_nil => true }
298
298
  configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
299
- configuration[:message].gsub!(/\"/, '\\\\\"')
300
299
 
301
- for attr_name in attr_names
302
- attr_accessor(attr_name)
303
- class_eval(%(#{validation_method(configuration[:on])} %{errors.add('#{attr_name}', "#{configuration[:message]}") unless #{attr_name}.nil? or #{attr_name} == "1"}))
300
+ attr_accessor *attr_names
301
+
302
+ validates_each(attr_names,configuration) do |record, attr_name, value|
303
+ record.errors.add(attr_name, configuration[:message]) unless value == "1"
304
304
  end
305
305
  end
306
306
 
@@ -312,10 +312,13 @@ module ActiveRecord
312
312
  def validates_presence_of(*attr_names)
313
313
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:empty], :on => :save }
314
314
  configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
315
- configuration[:message].gsub!(/\"/, '\\\\\"')
316
315
 
317
- for attr_name in attr_names
318
- class_eval(%(#{validation_method(configuration[:on])} %{errors.add_on_empty('#{attr_name}', "#{configuration[:message]}")}))
316
+ # can't use validates_each here, because it cannot cope with non-existant attributes,
317
+ # while errors.add_on_empty can
318
+ attr_names.each do |attr_name|
319
+ send(validation_method(configuration[:on])) do |record|
320
+ record.errors.add_on_empty(attr_name,configuration[:message])
321
+ end
319
322
  end
320
323
  end
321
324
 
@@ -414,13 +417,14 @@ module ActiveRecord
414
417
  def validates_uniqueness_of(*attr_names)
415
418
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:taken] }
416
419
  configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
417
- configuration[:message].gsub!(/\"/, '\\\\\"')
418
420
 
419
- for attr_name in attr_names
420
- if scope = configuration[:scope]
421
- class_eval(%(validate %{errors.add('#{attr_name}', "#{configuration[:message]}") if self.class.find_first(new_record? ? ['#{attr_name} = ? AND #{scope} = ?', #{attr_name}, #{scope}] : ["#{attr_name} = ? AND \\\#{self.class.primary_key} <> ? AND #{scope} = ?", #{attr_name}, id, #{scope}])}))
422
- else
423
- class_eval(%(validate %{errors.add('#{attr_name}', "#{configuration[:message]}") if self.class.find_first(new_record? ? ['#{attr_name} = ?', #{attr_name}] : ["#{attr_name} = ? AND \\\#{self.class.primary_key} <> ?", #{attr_name}, id])}))
421
+ if scope = configuration[:scope]
422
+ validates_each(attr_names,configuration) do |record, attr_name, value|
423
+ record.errors.add(attr_name, configuration[:message]) if record.class.find_first(record.new_record? ? ["#{attr_name} = ? AND #{scope} = ?", record.send(attr_name), record.send(scope)] : ["#{attr_name} = ? AND #{record.class.primary_key} <> ? AND #{scope} = ?", record.send(attr_name), record.send(:id), record.send(scope)])
424
+ end
425
+ else
426
+ validates_each(attr_names,configuration) do |record, attr_name, value|
427
+ record.errors.add(attr_name, configuration[:message]) if record.class.find_first(record.new_record? ? ["#{attr_name} = ?", record.send(attr_name)] : ["#{attr_name} = ? AND #{record.class.primary_key} <> ?", record.send(attr_name), record.send(:id) ] )
424
428
  end
425
429
  end
426
430
  end
@@ -441,12 +445,11 @@ module ActiveRecord
441
445
  def validates_format_of(*attr_names)
442
446
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save, :with => nil }
443
447
  configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
444
- configuration[:message].gsub!(/\"/, '\\\\\"')
445
448
 
446
449
  raise(ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash") unless configuration[:with].is_a?(Regexp)
447
450
 
448
- for attr_name in attr_names
449
- class_eval(%(#{validation_method(configuration[:on])} %{errors.add("#{attr_name}", "#{configuration[:message]}") unless #{attr_name} and #{attr_name}.to_s.match(/#{Regexp.quote(configuration[:with].source)}/)}))
451
+ validates_each(attr_names, configuration) do |record, attr_name, value|
452
+ record.errors.add(attr_name, configuration[:message]) unless value.to_s =~ configuration[:with]
450
453
  end
451
454
  end
452
455
 
@@ -464,19 +467,13 @@ module ActiveRecord
464
467
  def validates_inclusion_of(*attr_names)
465
468
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:inclusion], :on => :save }
466
469
  configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
467
- configuration[:message].gsub!(/\"/, '\\\\\"')
468
-
470
+
469
471
  enum = configuration[:in] || configuration[:within]
470
- allow_nil = configuration[:allow_nil]
471
-
472
+
472
473
  raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?("include?")
473
474
 
474
- for attr_name in attr_names
475
- if allow_nil
476
- class_eval(%(#{validation_method(configuration[:on])} %{errors.add("#{attr_name}", "#{configuration[:message]}") unless #{attr_name}.nil? or (#{enum.inspect}).include?(#{attr_name}) }))
477
- else
478
- class_eval(%(#{validation_method(configuration[:on])} %{errors.add("#{attr_name}", "#{configuration[:message]}") unless (#{enum.inspect}).include?(#{attr_name}) }))
479
- end
475
+ validates_each(attr_names, configuration) do |record, attr_name, value|
476
+ record.errors.add(attr_name, configuration[:message]) unless enum.include?(value)
480
477
  end
481
478
  end
482
479
 
@@ -504,13 +501,10 @@ module ActiveRecord
504
501
  def validates_associated(*attr_names)
505
502
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save }
506
503
  configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
507
- configuration[:message].gsub!(/\"/, '\\\\\"')
508
-
509
- for attr_name in attr_names
510
- class_eval(%(#{validation_method(configuration[:on])} %{
511
- errors.add("#{attr_name}", "#{configuration[:message]}") unless
512
- (#{attr_name}.is_a?(Array) ? #{attr_name} : [#{attr_name}]).inject(true){ |memo, record| (record.nil? or record.valid?) and memo }
513
- }))
504
+
505
+ validates_each(attr_names, configuration) do |record, attr_name, value|
506
+ record.errors.add(attr_name, configuration[:message]) unless
507
+ (value.is_a?(Array) ? value : [value]).inject(true) { |memo, r| (r.nil? or r.valid?) and memo }
514
508
  end
515
509
  end
516
510
 
@@ -526,29 +520,25 @@ module ActiveRecord
526
520
  # * <tt>message</tt> - A custom error message (default is: "is not a number")
527
521
  # * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
528
522
  # * <tt>only_integer</tt> Specifies whether the value has to be an integer, e.g. an integral value (default is false)
523
+ # * <tt>allow_nil</tt> Skip validation if attribute is nil (default is false). Notice that for fixnum and float columsn empty strings are converted to nil
529
524
  def validates_numericality_of(*attr_names)
530
525
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:not_a_number], :on => :save,
531
- :integer => false }
526
+ :only_integer => false, :allow_nil => false }
532
527
  configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
533
528
 
534
- for attr_name in attr_names
535
- if configuration[:only_integer]
536
- # we have to use a regexp here, because Kernel.Integer accepts nil and "0xdeadbeef", but does not
537
- # accept "099" and String#to_i accepts everything. The string containing the regexp is evaluated twice
538
- # so we have to escape everything properly
539
- class_eval(%(#{validation_method(configuration[:on])} %{
540
- errors.add("#{attr_name}", "#{configuration[:message]}") unless #{attr_name}_before_type_cast.to_s =~ /^[\\\\+\\\\-]?\\\\d+$/
541
- }))
542
- else
543
- class_eval(%(#{validation_method(configuration[:on])} %{
544
- begin
545
- Kernel.Float(#{attr_name}_before_type_cast)
546
- rescue ArgumentError, TypeError
547
- errors.add("#{attr_name}", "#{configuration[:message]}")
548
- end
549
- }))
529
+ if configuration[:only_integer]
530
+ validates_each(attr_names,configuration) do |record, attr_name,value|
531
+ record.errors.add(attr_name, configuration[:message]) unless record.send("#{attr_name}_before_type_cast").to_s =~ /^[+-]?\d+$/
550
532
  end
551
- end
533
+ else
534
+ validates_each(attr_names,configuration) do |record, attr_name,value|
535
+ begin
536
+ Kernel.Float(record.send("#{attr_name}_before_type_cast").to_s)
537
+ rescue ArgumentError, TypeError
538
+ record.errors.add(attr_name, configuration[:message])
539
+ end
540
+ end
541
+ end
552
542
  end
553
543
 
554
544
  private
data/rakefile CHANGED
@@ -8,7 +8,7 @@ require 'rake/contrib/rubyforgepublisher'
8
8
 
9
9
  PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
10
10
  PKG_NAME = 'activerecord'
11
- PKG_VERSION = '1.8.0' + PKG_BUILD
11
+ PKG_VERSION = '1.9.0' + PKG_BUILD
12
12
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
13
13
 
14
14
  PKG_FILES = FileList[
@@ -117,7 +117,7 @@ spec = Gem::Specification.new do |s|
117
117
  s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
118
118
  end
119
119
 
120
- s.add_dependency('activesupport', '= 1.0.1' + PKG_BUILD)
120
+ s.add_dependency('activesupport', '= 1.0.2' + PKG_BUILD)
121
121
 
122
122
  s.files.delete "test/fixtures/fixture_database.sqlite"
123
123
  s.files.delete "test/fixtures/fixture_database_2.sqlite"
@@ -0,0 +1,31 @@
1
+ require 'abstract_unit'
2
+
3
+ class ActiveSchemaTest < Test::Unit::TestCase
4
+ def setup
5
+ ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
6
+ alias_method :real_execute, :execute
7
+ def execute(sql, name = nil) return sql end
8
+ end
9
+ end
10
+
11
+ def teardown
12
+ ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:alias_method, :execute, :real_execute)
13
+ end
14
+
15
+ def test_drop_table
16
+ assert_equal "DROP TABLE people", drop_table(:people)
17
+ end
18
+
19
+ def test_add_column
20
+ assert_equal "ALTER TABLE people ADD last_name varchar(255)", add_column(:people, :last_name, :string)
21
+ end
22
+
23
+ def test_add_column_with_limit
24
+ assert_equal "ALTER TABLE people ADD key varchar(32)", add_column(:people, :key, :string, :limit => 32)
25
+ end
26
+
27
+ private
28
+ def method_missing(method_symbol, *arguments)
29
+ ActiveRecord::Base.connection.send(method_symbol, *arguments)
30
+ end
31
+ end
@@ -41,14 +41,11 @@ class CallbackDeveloper < ActiveRecord::Base
41
41
  @history ||= []
42
42
  end
43
43
 
44
- # after_initialize and after_find may not be declared using class methods.
45
- # They are invoked only if instance methods have been defined.
44
+ # after_initialize and after_find are invoked only if instance methods have been defined.
46
45
  def after_initialize
47
- history << [:after_initialize, :method]
48
46
  end
49
47
 
50
48
  def after_find
51
- history << [:after_find, :method]
52
49
  end
53
50
  end
54
51
 
@@ -61,15 +58,24 @@ class CallbacksTest < Test::Unit::TestCase
61
58
  def test_initialize
62
59
  david = CallbackDeveloper.new
63
60
  assert_equal [
64
- [ :after_initialize, :method ]
61
+ [ :after_initialize, :string ],
62
+ [ :after_initialize, :proc ],
63
+ [ :after_initialize, :object ],
64
+ [ :after_initialize, :block ],
65
65
  ], david.history
66
66
  end
67
67
 
68
68
  def test_find
69
69
  david = CallbackDeveloper.find(1)
70
70
  assert_equal [
71
- [ :after_find, :method ],
72
- [ :after_initialize, :method ]
71
+ [ :after_find, :string ],
72
+ [ :after_find, :proc ],
73
+ [ :after_find, :object ],
74
+ [ :after_find, :block ],
75
+ [ :after_initialize, :string ],
76
+ [ :after_initialize, :proc ],
77
+ [ :after_initialize, :object ],
78
+ [ :after_initialize, :block ],
73
79
  ], david.history
74
80
  end
75
81
 
@@ -77,7 +83,10 @@ class CallbacksTest < Test::Unit::TestCase
77
83
  david = CallbackDeveloper.new
78
84
  david.valid?
79
85
  assert_equal [
80
- [ :after_initialize, :method ],
86
+ [ :after_initialize, :string ],
87
+ [ :after_initialize, :proc ],
88
+ [ :after_initialize, :object ],
89
+ [ :after_initialize, :block ],
81
90
  [ :before_validation, :string ],
82
91
  [ :before_validation, :proc ],
83
92
  [ :before_validation, :object ],
@@ -101,8 +110,14 @@ class CallbacksTest < Test::Unit::TestCase
101
110
  david = CallbackDeveloper.find(1)
102
111
  david.valid?
103
112
  assert_equal [
104
- [ :after_find, :method ],
105
- [ :after_initialize, :method ],
113
+ [ :after_find, :string ],
114
+ [ :after_find, :proc ],
115
+ [ :after_find, :object ],
116
+ [ :after_find, :block ],
117
+ [ :after_initialize, :string ],
118
+ [ :after_initialize, :proc ],
119
+ [ :after_initialize, :object ],
120
+ [ :after_initialize, :block ],
106
121
  [ :before_validation, :string ],
107
122
  [ :before_validation, :proc ],
108
123
  [ :before_validation, :object ],
@@ -125,7 +140,10 @@ class CallbacksTest < Test::Unit::TestCase
125
140
  def test_create
126
141
  david = CallbackDeveloper.create('name' => 'David', 'salary' => 1000000)
127
142
  assert_equal [
128
- [ :after_initialize, :method ],
143
+ [ :after_initialize, :string ],
144
+ [ :after_initialize, :proc ],
145
+ [ :after_initialize, :object ],
146
+ [ :after_initialize, :block ],
129
147
  [ :before_validation, :string ],
130
148
  [ :before_validation, :proc ],
131
149
  [ :before_validation, :object ],
@@ -165,8 +183,14 @@ class CallbacksTest < Test::Unit::TestCase
165
183
  david = CallbackDeveloper.find(1)
166
184
  david.save
167
185
  assert_equal [
168
- [ :after_find, :method ],
169
- [ :after_initialize, :method ],
186
+ [ :after_find, :string ],
187
+ [ :after_find, :proc ],
188
+ [ :after_find, :object ],
189
+ [ :after_find, :block ],
190
+ [ :after_initialize, :string ],
191
+ [ :after_initialize, :proc ],
192
+ [ :after_initialize, :object ],
193
+ [ :after_initialize, :block ],
170
194
  [ :before_validation, :string ],
171
195
  [ :before_validation, :proc ],
172
196
  [ :before_validation, :object ],
@@ -206,8 +230,14 @@ class CallbacksTest < Test::Unit::TestCase
206
230
  david = CallbackDeveloper.find(1)
207
231
  david.destroy
208
232
  assert_equal [
209
- [ :after_find, :method ],
210
- [ :after_initialize, :method ],
233
+ [ :after_find, :string ],
234
+ [ :after_find, :proc ],
235
+ [ :after_find, :object ],
236
+ [ :after_find, :block ],
237
+ [ :after_initialize, :string ],
238
+ [ :after_initialize, :proc ],
239
+ [ :after_initialize, :object ],
240
+ [ :after_initialize, :block ],
211
241
  [ :before_destroy, :string ],
212
242
  [ :before_destroy, :proc ],
213
243
  [ :before_destroy, :object ],
@@ -223,8 +253,14 @@ class CallbacksTest < Test::Unit::TestCase
223
253
  david = CallbackDeveloper.find(1)
224
254
  CallbackDeveloper.delete(david.id)
225
255
  assert_equal [
226
- [ :after_find, :method ],
227
- [ :after_initialize, :method ]
256
+ [ :after_find, :string ],
257
+ [ :after_find, :proc ],
258
+ [ :after_find, :object ],
259
+ [ :after_find, :block ],
260
+ [ :after_initialize, :string ],
261
+ [ :after_initialize, :proc ],
262
+ [ :after_initialize, :object ],
263
+ [ :after_initialize, :block ],
228
264
  ], david.history
229
265
  end
230
266
 
@@ -234,8 +270,14 @@ class CallbacksTest < Test::Unit::TestCase
234
270
  CallbackDeveloper.before_validation proc { |model| model.history << [:before_validation, :should_never_get_here] }
235
271
  david.save
236
272
  assert_equal [
237
- [ :after_find, :method ],
238
- [ :after_initialize, :method ],
273
+ [ :after_find, :string ],
274
+ [ :after_find, :proc ],
275
+ [ :after_find, :object ],
276
+ [ :after_find, :block ],
277
+ [ :after_initialize, :string ],
278
+ [ :after_initialize, :proc ],
279
+ [ :after_initialize, :object ],
280
+ [ :after_initialize, :block ],
239
281
  [ :before_validation, :string ],
240
282
  [ :before_validation, :proc ],
241
283
  [ :before_validation, :object ],
@@ -13,7 +13,7 @@ sqlite_test_db2 = "#{BASE_DIR}/fixture_database_2.sqlite"
13
13
  def make_connection(clazz, db_file, db_definitions_file)
14
14
  unless File.exist?(db_file)
15
15
  puts "SQLite database not found at #{db_file}. Rebuilding it."
16
- sqlite_command = "sqlite #{db_file} 'create table a (a integer); drop table a;'"
16
+ sqlite_command = %Q{sqlite #{db_file} "create table a (a integer); drop table a;"}
17
17
  puts "Executing '#{sqlite_command}'"
18
18
  raise SqliteError.new("Seems that there is no sqlite executable available") unless system(sqlite_command)
19
19
  clazz.establish_connection(
@@ -13,7 +13,7 @@ sqlite_test_db2 = "#{BASE_DIR}/fixture_database_2.sqlite3"
13
13
  def make_connection(clazz, db_file, db_definitions_file)
14
14
  unless File.exist?(db_file)
15
15
  puts "SQLite3 database not found at #{db_file}. Rebuilding it."
16
- sqlite_command = "sqlite3 #{db_file} 'create table a (a integer); drop table a;'"
16
+ sqlite_command = %Q{sqlite3 #{db_file} "create table a (a integer); drop table a;"}
17
17
  puts "Executing '#{sqlite_command}'"
18
18
  raise SqliteError.new("Seems that there is no sqlite3 executable available") unless system(sqlite_command)
19
19
  clazz.establish_connection(
@@ -4,12 +4,19 @@ require 'fixtures/topic'
4
4
  require 'fixtures/entrant'
5
5
 
6
6
  class FinderTest < Test::Unit::TestCase
7
- fixtures :companies, :topics, :entrants
7
+ fixtures :companies, :topics, :entrants, :developers
8
8
 
9
9
  def test_find
10
10
  assert_equal(@topics["first"]["title"], Topic.find(1).title)
11
11
  end
12
12
 
13
+ def test_exists
14
+ assert (Topic.exists?(1))
15
+ assert !(Topic.exists?(45))
16
+ assert !(Topic.exists?("foo"))
17
+ assert !(Topic.exists?([1,2]))
18
+ end
19
+
13
20
  def test_find_by_array_of_one_id
14
21
  assert_kind_of(Array, Topic.find([ 1 ]))
15
22
  assert_equal(1, Topic.find([ 1 ]).length)
@@ -233,6 +240,37 @@ class FinderTest < Test::Unit::TestCase
233
240
  assert_equal "Mary", topics[0].author_name
234
241
  end
235
242
 
243
+ def test_find_with_bad_sql
244
+ assert_raises(ActiveRecord::StatementInvalid) { Topic.find_by_sql "select 1 from badtable" }
245
+ end
246
+
247
+ def test_find_all_with_limit
248
+ first_five_developers = Developer.find_all nil, 'id ASC', 5
249
+ assert_equal 5, first_five_developers.length
250
+ assert_equal 'David', first_five_developers.first.name
251
+ assert_equal 'fixture_5', first_five_developers.last.name
252
+
253
+ no_developers = Developer.find_all nil, 'id ASC', 0
254
+ assert_equal 0, no_developers.length
255
+
256
+ assert_equal first_five_developers, Developer.find_all(nil, 'id ASC', [5])
257
+ assert_equal no_developers, Developer.find_all(nil, 'id ASC', [0])
258
+ end
259
+
260
+ def test_find_all_with_limit_and_offset
261
+ first_three_developers = Developer.find_all nil, 'id ASC', [3, 0]
262
+ second_three_developers = Developer.find_all nil, 'id ASC', [3, 3]
263
+ last_two_developers = Developer.find_all nil, 'id ASC', [3, 8]
264
+
265
+ assert_equal 3, first_three_developers.length
266
+ assert_equal 3, second_three_developers.length
267
+ assert_equal 2, last_two_developers.length
268
+
269
+ assert_equal 'David', first_three_developers.first.name
270
+ assert_equal 'fixture_4', second_three_developers.first.name
271
+ assert_equal 'fixture_9', last_two_developers.first.name
272
+ end
273
+
236
274
  protected
237
275
  def bind(statement, *vars)
238
276
  if vars.first.is_a?(Hash)
@@ -119,7 +119,7 @@ CREATE TABLE people (
119
119
 
120
120
  CREATE TABLE binaries (
121
121
  id int NOT NULL IDENTITY(1, 1),
122
- data blob NULL,
122
+ data image NULL,
123
123
  PRIMARY KEY (id)
124
124
  );
125
125
 
@@ -113,8 +113,10 @@ class FixturesWithoutInstantiationTest < Test::Unit::TestCase
113
113
  fixtures :topics, :developers, :accounts
114
114
 
115
115
  def test_without_complete_instantiation
116
- assert_nil @topics
117
116
  assert_nil @first
117
+ assert_nil @topics
118
+ assert_nil @developers
119
+ assert_nil @accounts
118
120
  end
119
121
 
120
122
  def test_fixtures_from_root_yml_without_instantiation
@@ -123,6 +125,19 @@ class FixturesWithoutInstantiationTest < Test::Unit::TestCase
123
125
  end
124
126
 
125
127
 
128
+ class FixturesWithoutInstanceInstantiationTest < Test::Unit::TestCase
129
+ self.use_instantiated_fixtures = :no_instances
130
+ fixtures :topics, :developers, :accounts
131
+
132
+ def test_without_instance_instantiation
133
+ assert_nil @first
134
+ assert_not_nil @topics
135
+ assert_not_nil @developers
136
+ assert_not_nil @accounts
137
+ end
138
+ end
139
+
140
+
126
141
  class TransactionalFixturesTest < Test::Unit::TestCase
127
142
  self.use_transactional_fixtures = true
128
143
  fixtures :topics
@@ -136,3 +151,23 @@ class TransactionalFixturesTest < Test::Unit::TestCase
136
151
  assert_not_nil @first
137
152
  end
138
153
  end
154
+
155
+
156
+ class MultipleFixturesTest < Test::Unit::TestCase
157
+ fixtures :topics
158
+ fixtures :developers, :accounts
159
+
160
+ def test_fixture_table_names
161
+ assert_equal([:topics, :developers, :accounts], fixture_table_names)
162
+ end
163
+ end
164
+
165
+
166
+ class OverlappingFixturesTest < Test::Unit::TestCase
167
+ fixtures :topics, :developers
168
+ fixtures :developers, :accounts
169
+
170
+ def test_fixture_table_names
171
+ assert_equal([:topics, :developers, :accounts], fixture_table_names)
172
+ end
173
+ end
@@ -606,40 +606,40 @@ class ValidationsTest < Test::Unit::TestCase
606
606
  end
607
607
 
608
608
  def test_validates_numericality_of_with_string
609
- Topic.validates_numericality_of( :replies_count )
609
+ Topic.validates_numericality_of( :approved )
610
610
  ["not a number","42 not a number","0xdeadbeef","00-1","-+019.0","12.12.13.12",nil].each do |v|
611
- t = Topic.create("title" => "numeric test", "content" => "whatever", "replies_count" => "not a number")
611
+ t = Topic.create("title" => "numeric test", "content" => "whatever", "approved" => "not a number")
612
612
  assert !t.valid?, "#{v} not rejected as a number"
613
- assert t.errors.on(:replies_count)
613
+ assert t.errors.on(:approved)
614
614
  end
615
615
  end
616
616
 
617
617
  def test_validates_numericality_of
618
- Topic.validates_numericality_of( :replies_count )
619
- ["10", "10.0", "10.5", "-10.5", "-0.0001","0090","-090","-090.1"].each do |v|
620
- t = Topic.create("title" => "numeric test", "content" => "whatever", "replies_count" => v)
618
+ Topic.validates_numericality_of( :approved, :allow_nil => true )
619
+ ["10", "10.0", "10.5", "-10.5", "-0.0001","0090","-090","-090.1",nil,""].each do |v|
620
+ t = Topic.create("title" => "numeric test", "content" => "whatever", "approved" => v)
621
621
  assert t.valid?, "#{v} not recognized as a number"
622
- # we cannot check this as replies_count is actually an integer field
623
- #assert_in_delta v.to_f, t.replies_count, 0.0000001
622
+ # we cannot check this as approved is actually an integer field
623
+ #assert_in_delta v.to_f, t.approved, 0.0000001
624
624
  end
625
625
  end
626
626
 
627
627
  def test_validates_numericality_of_int_with_string
628
- Topic.validates_numericality_of( :replies_count, :only_integer => true )
628
+ Topic.validates_numericality_of( :approved, :only_integer => true )
629
629
  ["not a number","42 not a number","0xdeadbeef","0-1","--3","+-3","+3-1",nil].each do |v|
630
- t = Topic.create("title" => "numeric test", "content" => "whatever", "replies_count" => v)
630
+ t = Topic.create("title" => "numeric test", "content" => "whatever", "approved" => v)
631
631
  assert !t.valid?, "#{v} not rejected as integer"
632
- assert t.errors.on(:replies_count)
632
+ assert t.errors.on(:approved)
633
633
  end
634
634
  end
635
635
 
636
636
  def test_validates_numericality_of_int
637
- Topic.validates_numericality_of( :replies_count, :only_integer => true )
638
- ["42", "+42", "-42", "042", "0042", "-042", 42].each do |v|
639
- t = Topic.create("title" => "numeric test", "content" => "whatever", "replies_count" => v)
637
+ Topic.validates_numericality_of( :approved, :only_integer => true, :allow_nil => true )
638
+ ["42", "+42", "-42", "042", "0042", "-042", 42, nil,""].each do |v|
639
+ t = Topic.create("title" => "numeric test", "content" => "whatever", "approved" => v)
640
640
  assert t.valid?, "#{v} not recognized as integer"
641
- assert_equal v.to_i, t.replies_count
641
+ assert_equal((v.nil? or v == "")? nil : v.to_i, t.approved)
642
642
  end
643
643
  end
644
-
644
+
645
645
  end
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.6
2
+ rubygems_version: 0.8.8
3
3
  specification_version: 1
4
4
  name: activerecord
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.8.0
7
- date: 2005-03-07
6
+ version: 1.9.0
7
+ date: 2005-03-22
8
8
  summary: Implements the ActiveRecord pattern for ORM.
9
9
  require_paths:
10
10
  - lib
@@ -77,6 +77,7 @@ files:
77
77
  - lib/active_record/wrappers/yaml_wrapper.rb
78
78
  - test/aaa_create_tables_test.rb
79
79
  - test/abstract_unit.rb
80
+ - test/active_schema_mysql.rb
80
81
  - test/aggregations_test.rb
81
82
  - test/all.sh
82
83
  - test/association_inheritance_reload.rb
@@ -226,5 +227,5 @@ dependencies:
226
227
  -
227
228
  - "="
228
229
  - !ruby/object:Gem::Version
229
- version: 1.0.1
230
+ version: 1.0.2
230
231
  version: