activerecord 1.3.0 → 1.4.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.

Files changed (75) hide show
  1. data/CHANGELOG +77 -2
  2. data/install.rb +5 -0
  3. data/lib/active_record.rb +6 -2
  4. data/lib/active_record/acts/list.rb +56 -45
  5. data/lib/active_record/acts/tree.rb +3 -2
  6. data/lib/active_record/associations.rb +10 -62
  7. data/lib/active_record/associations/association_collection.rb +20 -23
  8. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +36 -10
  9. data/lib/active_record/associations/has_many_association.rb +50 -25
  10. data/lib/active_record/base.rb +118 -80
  11. data/lib/active_record/callbacks.rb +51 -50
  12. data/lib/active_record/connection_adapters/abstract_adapter.rb +33 -12
  13. data/lib/active_record/connection_adapters/db2_adapter.rb +129 -0
  14. data/lib/active_record/connection_adapters/sqlite_adapter.rb +23 -1
  15. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +80 -90
  16. data/lib/active_record/fixtures.rb +1 -1
  17. data/lib/active_record/locking.rb +57 -0
  18. data/lib/active_record/support/class_attribute_accessors.rb +19 -5
  19. data/lib/active_record/support/class_inheritable_attributes.rb +4 -3
  20. data/lib/active_record/support/dependencies.rb +71 -0
  21. data/lib/active_record/support/inflector.rb +1 -0
  22. data/lib/active_record/support/misc.rb +29 -3
  23. data/lib/active_record/support/module_attribute_accessors.rb +57 -0
  24. data/lib/active_record/transactions.rb +18 -11
  25. data/lib/active_record/validations.rb +3 -3
  26. data/lib/active_record/vendor/db2.rb +357 -0
  27. data/lib/active_record/vendor/mysql.rb +1 -1
  28. data/rakefile +17 -5
  29. data/test/associations_test.rb +39 -4
  30. data/test/base_test.rb +13 -4
  31. data/test/binary_test.rb +43 -0
  32. data/test/callbacks_test.rb +230 -0
  33. data/test/connections/native_db2/connection.rb +24 -0
  34. data/test/connections/native_sqlserver/connection.rb +9 -3
  35. data/test/deprecated_associations_test.rb +16 -10
  36. data/test/finder_test.rb +65 -13
  37. data/test/fixtures/associations.png +0 -0
  38. data/test/fixtures/binary.rb +2 -0
  39. data/test/fixtures/companies.yml +21 -0
  40. data/test/fixtures/courses.yml +7 -0
  41. data/test/fixtures/customers.yml +7 -0
  42. data/test/fixtures/db_definitions/db2.sql +124 -0
  43. data/test/fixtures/db_definitions/db22.sql +4 -0
  44. data/test/fixtures/db_definitions/mysql.sql +12 -0
  45. data/test/fixtures/db_definitions/postgresql.sql +13 -0
  46. data/test/fixtures/db_definitions/sqlite.sql +9 -0
  47. data/test/fixtures/db_definitions/sqlserver.sql +13 -0
  48. data/test/fixtures/developers_projects.yml +13 -0
  49. data/test/fixtures/entrants.yml +14 -0
  50. data/test/fixtures/fixture_database.sqlite +0 -0
  51. data/test/fixtures/movies.yml +7 -0
  52. data/test/fixtures/people.yml +3 -0
  53. data/test/fixtures/person.rb +1 -0
  54. data/test/fixtures/projects.yml +7 -0
  55. data/test/fixtures/topics.yml +21 -0
  56. data/test/locking_test.rb +34 -0
  57. data/test/mixin_test.rb +6 -0
  58. data/test/validations_test.rb +1 -1
  59. metadata +33 -29
  60. data/test/fixtures/companies/first_client +0 -6
  61. data/test/fixtures/companies/first_firm +0 -4
  62. data/test/fixtures/companies/second_client +0 -6
  63. data/test/fixtures/courses/java +0 -2
  64. data/test/fixtures/courses/ruby +0 -2
  65. data/test/fixtures/customers/david +0 -6
  66. data/test/fixtures/entrants/first +0 -3
  67. data/test/fixtures/entrants/second +0 -3
  68. data/test/fixtures/entrants/third +0 -3
  69. data/test/fixtures/movies/first +0 -2
  70. data/test/fixtures/movies/second +0 -2
  71. data/test/fixtures/projects/action_controller +0 -2
  72. data/test/fixtures/projects/active_record +0 -2
  73. data/test/fixtures/topics/first +0 -10
  74. data/test/fixtures/topics/second +0 -8
  75. data/test/inflector_test.rb +0 -122
@@ -1091,7 +1091,7 @@ class << Mysql
1091
1091
  when "\0" then "\\0"
1092
1092
  when "\n" then "\\n"
1093
1093
  when "\r" then "\\r"
1094
- when "\032" then "\Z"
1094
+ when "\032" then "\\Z"
1095
1095
  else "\\"+$1
1096
1096
  end
1097
1097
  end
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.3.0' + PKG_BUILD
11
+ PKG_VERSION = '1.4.0' + PKG_BUILD
12
12
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
13
13
 
14
14
  PKG_FILES = FileList[
@@ -45,6 +45,18 @@ Rake::TestTask.new("test_sqlite") { |t|
45
45
  t.verbose = true
46
46
  }
47
47
 
48
+ Rake::TestTask.new("test_sqlserver") { |t|
49
+ t.libs << "test" << "test/connections/native_sqlserver"
50
+ t.pattern = 'test/*_test.rb'
51
+ t.verbose = true
52
+ }
53
+
54
+ Rake::TestTask.new("test_db2") { |t|
55
+ t.libs << "test" << "test/connections/native_db2"
56
+ t.pattern = 'test/*_test.rb'
57
+ t.verbose = true
58
+ }
59
+
48
60
  # Generate the RDoc documentation
49
61
 
50
62
  Rake::RDocTask.new { |rdoc|
@@ -61,14 +73,14 @@ Rake::RDocTask.new { |rdoc|
61
73
  # Publish beta gem
62
74
  desc "Publish the beta gem"
63
75
  task :pgem => [:package] do
64
- Rake::SshFilePublisher.new("davidhh@one.textdrive.com", "domains/rubyonrails.org/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
65
- `ssh davidhh@one.textdrive.com './gemupdate.sh'`
76
+ Rake::SshFilePublisher.new("davidhh@comox.textdrive.com", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
77
+ `ssh davidhh@comox.textdrive.com './gemupdate.sh'`
66
78
  end
67
79
 
68
80
  # Publish documentation
69
81
  desc "Publish the API documentation"
70
82
  task :pdoc => [:rdoc] do
71
- Rake::SshDirPublisher.new("davidhh@one.textdrive.com", "domains/rubyonrails.org/ar", "doc").upload
83
+ Rake::SshDirPublisher.new("davidhh@comox.textdrive.com", "public_html/ar", "doc").upload
72
84
  end
73
85
 
74
86
 
@@ -96,7 +108,7 @@ spec = Gem::Specification.new do |s|
96
108
 
97
109
  s.author = "David Heinemeier Hansson"
98
110
  s.email = "david@loudthinking.com"
99
- s.homepage = "http://activerecord.rubyonrails.org"
111
+ s.homepage = "http://www.rubyonrails.com"
100
112
  s.rubyforge_project = "activerecord"
101
113
  end
102
114
 
@@ -149,6 +149,10 @@ class HasManyAssociationsTest < Test::Unit::TestCase
149
149
  @signals37.clients_of_firm.each {|f| }
150
150
  end
151
151
 
152
+ def test_counting
153
+ assert_equal 2, Firm.find_first.clients.count
154
+ end
155
+
152
156
  def test_finding
153
157
  assert_equal 2, Firm.find_first.clients.length
154
158
  end
@@ -183,9 +187,31 @@ class HasManyAssociationsTest < Test::Unit::TestCase
183
187
  assert_equal 0, Firm.find_first.clients_using_zero_counter_sql.size
184
188
  end
185
189
 
190
+ def test_find_ids
191
+ firm = Firm.find_first
192
+
193
+ assert_raises(ActiveRecord::RecordNotFound) { firm.clients.find }
194
+
195
+ client = firm.clients.find(2)
196
+ assert_kind_of Client, client
197
+
198
+ client_ary = firm.clients.find([2])
199
+ assert_kind_of Array, client_ary
200
+ assert_equal client, client_ary.first
201
+
202
+ client_ary = firm.clients.find(2, 3)
203
+ assert_kind_of Array, client_ary
204
+ assert_equal 2, client_ary.size
205
+ assert_equal client, client_ary.first
206
+
207
+ assert_raises(ActiveRecord::RecordNotFound) { firm.clients.find(2, 99) }
208
+ end
209
+
186
210
  def test_find_all
187
- assert_equal 2, Firm.find_first.clients.find_all("type = 'Client'").length
188
- assert_equal 1, Firm.find_first.clients.find_all("name = 'Summit'").length
211
+ firm = Firm.find_first
212
+ assert_equal firm.clients, firm.clients.find_all
213
+ assert_equal 2, firm.clients.find_all("type = 'Client'").length
214
+ assert_equal 1, firm.clients.find_all("name = 'Summit'").length
189
215
  end
190
216
 
191
217
  def test_find_all_sanitized
@@ -193,9 +219,18 @@ class HasManyAssociationsTest < Test::Unit::TestCase
193
219
  assert_equal firm.clients.find_all("name = 'Summit'"), firm.clients.find_all(["name = '%s'", "Summit"])
194
220
  end
195
221
 
222
+ def test_find_first
223
+ firm = Firm.find_first
224
+ assert_equal firm.clients.first, firm.clients.find_first
225
+ assert_equal Client.find(2), firm.clients.find_first("type = 'Client'")
226
+ end
227
+
228
+ def test_find_first_sanitized
229
+ assert_equal Client.find(2), Firm.find_first.clients.find_first(["type = ?", "Client"])
230
+ end
231
+
196
232
  def test_find_in_collection
197
233
  assert_equal Client.find(2).name, @signals37.clients.find(2).name
198
- assert_equal Client.find(2).name, @signals37.clients.find {|c| c.name == @signals37.clients.find(2).name }.name
199
234
  assert_raises(ActiveRecord::RecordNotFound) { @signals37.clients.find(6) }
200
235
  end
201
236
 
@@ -519,7 +554,7 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
519
554
 
520
555
  def test_removing_associations_on_destroy
521
556
  Developer.find(1).destroy
522
- assert Developer.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = '1'").empty?
557
+ assert Developer.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = 1").empty?
523
558
  end
524
559
 
525
560
  def test_additional_columns_from_join_table
@@ -119,7 +119,7 @@ class BasicsTest < Test::Unit::TestCase
119
119
  def test_update
120
120
  topic = Topic.new
121
121
  topic.title = "Another New Topic"
122
- topic.written_on = "2003-12-12 23:23"
122
+ topic.written_on = "2003-12-12 23:23:00"
123
123
  topic.save
124
124
  id = topic.id
125
125
  assert_equal(id, topic.id)
@@ -162,7 +162,7 @@ class BasicsTest < Test::Unit::TestCase
162
162
  def test_destroy
163
163
  topic = Topic.new
164
164
  topic.title = "Yet Another New Topic"
165
- topic.written_on = "2003-12-12 23:23"
165
+ topic.written_on = "2003-12-12 23:23:00"
166
166
  topic.save
167
167
  id = topic.id
168
168
  topic.destroy
@@ -327,7 +327,16 @@ class BasicsTest < Test::Unit::TestCase
327
327
  assert_equal 1, topic.approved
328
328
  assert_nil topic.last_read
329
329
  end
330
-
330
+
331
+ def test_utc_as_time_zone
332
+ Topic.default_timezone = :utc
333
+ attributes = { "bonus_time" => "5:42:00AM" }
334
+ topic = Topic.find(1)
335
+ topic.attributes = attributes
336
+ assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time
337
+ Topic.default_timezone = :local
338
+ end
339
+
331
340
  def test_default_values_on_empty_strings
332
341
  topic = Topic.new
333
342
  topic.approved = nil
@@ -576,4 +585,4 @@ class BasicsTest < Test::Unit::TestCase
576
585
  assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1) }
577
586
  assert_nothing_raised { Reply.find(should_be_destroyed_reply.id) }
578
587
  end
579
- end
588
+ end
@@ -0,0 +1,43 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/binary'
3
+
4
+ class BinaryTest < Test::Unit::TestCase
5
+ def setup
6
+ @data = create_data_fixture
7
+ end
8
+
9
+ def test_load_save
10
+ # Without using prepared statements, it makes no sense to test
11
+ # BLOB data with DB2, because the length of a statement is
12
+ # limited to 32KB.
13
+ if ActiveRecord::ConnectionAdapters.const_defined? :DB2Adapter
14
+ return true if ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters::DB2Adapter)
15
+ end
16
+ bin = Binary.new
17
+ bin.data = @data
18
+
19
+ assert bin.data == @data,
20
+ "Assigned data differs from file data"
21
+
22
+ bin.save
23
+
24
+ assert bin.data == @data,
25
+ "Assigned data differs from file data after save"
26
+
27
+ db_bin = Binary.find(bin.id)
28
+
29
+ assert db_bin.data == bin.data,
30
+ "Loaded binary data differes from memory version"
31
+
32
+ assert db_bin.data == File.new(File.dirname(__FILE__)+"/fixtures/associations.png","rb").read,
33
+ "Loaded binary data differes from file version"
34
+ end
35
+
36
+ private
37
+
38
+ def create_data_fixture
39
+ Binary.connection.execute("DELETE FROM binaries")
40
+ File.new(File.dirname(__FILE__)+"/fixtures/associations.png","rb").read
41
+ end
42
+
43
+ end
@@ -0,0 +1,230 @@
1
+ require 'abstract_unit'
2
+
3
+ class CallbackDeveloper < ActiveRecord::Base
4
+ class << self
5
+ def table_name; 'developers' end
6
+
7
+ def callback_string(callback_method)
8
+ "history << [#{callback_method.to_sym.inspect}, :string]"
9
+ end
10
+
11
+ def callback_proc(callback_method)
12
+ Proc.new { |model| model.history << [callback_method, :proc] }
13
+ end
14
+
15
+ def define_callback_method(callback_method)
16
+ define_method("#{callback_method}_method") do |model|
17
+ model.history << [callback_method, :method]
18
+ end
19
+ end
20
+
21
+ def callback_object(callback_method)
22
+ klass = Class.new
23
+ klass.send(:define_method, callback_method) do |model|
24
+ model.history << [callback_method, :object]
25
+ end
26
+ klass.new
27
+ end
28
+ end
29
+
30
+ ActiveRecord::Callbacks::CALLBACKS.each do |callback_method|
31
+ callback_method_sym = callback_method.to_sym
32
+ define_callback_method(callback_method_sym)
33
+ send(callback_method, callback_method_sym)
34
+ send(callback_method, callback_string(callback_method_sym))
35
+ send(callback_method, callback_proc(callback_method_sym))
36
+ send(callback_method, callback_object(callback_method_sym))
37
+ send(callback_method) { |model| model.history << [callback_method_sym, :block] }
38
+ end
39
+
40
+ def history
41
+ @history ||= []
42
+ end
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.
46
+ def after_initialize
47
+ history << [:after_initialize, :method]
48
+ end
49
+
50
+ def after_find
51
+ history << [:after_find, :method]
52
+ end
53
+ end
54
+
55
+
56
+ class CallbacksTest < Test::Unit::TestCase
57
+ def setup
58
+ @developers = create_fixtures('developers')
59
+ end
60
+
61
+ def test_initialize
62
+ david = CallbackDeveloper.new
63
+ assert_equal [
64
+ [ :after_initialize, :method ]
65
+ ], david.history
66
+ end
67
+
68
+ def test_find
69
+ david = CallbackDeveloper.find(1)
70
+ assert_equal [
71
+ [ :after_find, :method ],
72
+ [ :after_initialize, :method ]
73
+ ], david.history
74
+ end
75
+
76
+ def test_new_valid?
77
+ david = CallbackDeveloper.new
78
+ david.valid?
79
+ assert_equal [
80
+ [ :after_initialize, :method ],
81
+ [ :before_validation, :string ],
82
+ [ :before_validation, :proc ],
83
+ [ :before_validation, :object ],
84
+ [ :before_validation, :block ],
85
+ [ :before_validation_on_create, :string ],
86
+ [ :before_validation_on_create, :proc ],
87
+ [ :before_validation_on_create, :object ],
88
+ [ :before_validation_on_create, :block ],
89
+ [ :after_validation, :string ],
90
+ [ :after_validation, :proc ],
91
+ [ :after_validation, :object ],
92
+ [ :after_validation, :block ],
93
+ [ :after_validation_on_create, :string ],
94
+ [ :after_validation_on_create, :proc ],
95
+ [ :after_validation_on_create, :object ],
96
+ [ :after_validation_on_create, :block ]
97
+ ], david.history
98
+ end
99
+
100
+ def test_existing_valid?
101
+ david = CallbackDeveloper.find(1)
102
+ david.valid?
103
+ assert_equal [
104
+ [ :after_find, :method ],
105
+ [ :after_initialize, :method ],
106
+ [ :before_validation, :string ],
107
+ [ :before_validation, :proc ],
108
+ [ :before_validation, :object ],
109
+ [ :before_validation, :block ],
110
+ [ :before_validation_on_update, :string ],
111
+ [ :before_validation_on_update, :proc ],
112
+ [ :before_validation_on_update, :object ],
113
+ [ :before_validation_on_update, :block ],
114
+ [ :after_validation, :string ],
115
+ [ :after_validation, :proc ],
116
+ [ :after_validation, :object ],
117
+ [ :after_validation, :block ],
118
+ [ :after_validation_on_update, :string ],
119
+ [ :after_validation_on_update, :proc ],
120
+ [ :after_validation_on_update, :object ],
121
+ [ :after_validation_on_update, :block ]
122
+ ], david.history
123
+ end
124
+
125
+ def test_create
126
+ david = CallbackDeveloper.create('name' => 'David', 'salary' => 1000000)
127
+ assert_equal [
128
+ [ :after_initialize, :method ],
129
+ [ :before_validation, :string ],
130
+ [ :before_validation, :proc ],
131
+ [ :before_validation, :object ],
132
+ [ :before_validation, :block ],
133
+ [ :before_validation_on_create, :string ],
134
+ [ :before_validation_on_create, :proc ],
135
+ [ :before_validation_on_create, :object ],
136
+ [ :before_validation_on_create, :block ],
137
+ [ :after_validation, :string ],
138
+ [ :after_validation, :proc ],
139
+ [ :after_validation, :object ],
140
+ [ :after_validation, :block ],
141
+ [ :after_validation_on_create, :string ],
142
+ [ :after_validation_on_create, :proc ],
143
+ [ :after_validation_on_create, :object ],
144
+ [ :after_validation_on_create, :block ],
145
+ [ :before_save, :string ],
146
+ [ :before_save, :proc ],
147
+ [ :before_save, :object ],
148
+ [ :before_save, :block ],
149
+ [ :before_create, :string ],
150
+ [ :before_create, :proc ],
151
+ [ :before_create, :object ],
152
+ [ :before_create, :block ],
153
+ [ :after_create, :string ],
154
+ [ :after_create, :proc ],
155
+ [ :after_create, :object ],
156
+ [ :after_create, :block ],
157
+ [ :after_save, :string ],
158
+ [ :after_save, :proc ],
159
+ [ :after_save, :object ],
160
+ [ :after_save, :block ]
161
+ ], david.history
162
+ end
163
+
164
+ def test_save
165
+ david = CallbackDeveloper.find(1)
166
+ david.save
167
+ assert_equal [
168
+ [ :after_find, :method ],
169
+ [ :after_initialize, :method ],
170
+ [ :before_validation, :string ],
171
+ [ :before_validation, :proc ],
172
+ [ :before_validation, :object ],
173
+ [ :before_validation, :block ],
174
+ [ :before_validation_on_update, :string ],
175
+ [ :before_validation_on_update, :proc ],
176
+ [ :before_validation_on_update, :object ],
177
+ [ :before_validation_on_update, :block ],
178
+ [ :after_validation, :string ],
179
+ [ :after_validation, :proc ],
180
+ [ :after_validation, :object ],
181
+ [ :after_validation, :block ],
182
+ [ :after_validation_on_update, :string ],
183
+ [ :after_validation_on_update, :proc ],
184
+ [ :after_validation_on_update, :object ],
185
+ [ :after_validation_on_update, :block ],
186
+ [ :before_save, :string ],
187
+ [ :before_save, :proc ],
188
+ [ :before_save, :object ],
189
+ [ :before_save, :block ],
190
+ [ :before_update, :string ],
191
+ [ :before_update, :proc ],
192
+ [ :before_update, :object ],
193
+ [ :before_update, :block ],
194
+ [ :after_update, :string ],
195
+ [ :after_update, :proc ],
196
+ [ :after_update, :object ],
197
+ [ :after_update, :block ],
198
+ [ :after_save, :string ],
199
+ [ :after_save, :proc ],
200
+ [ :after_save, :object ],
201
+ [ :after_save, :block ]
202
+ ], david.history
203
+ end
204
+
205
+ def test_destroy
206
+ david = CallbackDeveloper.find(1)
207
+ david.destroy
208
+ assert_equal [
209
+ [ :after_find, :method ],
210
+ [ :after_initialize, :method ],
211
+ [ :before_destroy, :string ],
212
+ [ :before_destroy, :proc ],
213
+ [ :before_destroy, :object ],
214
+ [ :before_destroy, :block ],
215
+ [ :after_destroy, :string ],
216
+ [ :after_destroy, :proc ],
217
+ [ :after_destroy, :object ],
218
+ [ :after_destroy, :block ]
219
+ ], david.history
220
+ end
221
+
222
+ def test_delete
223
+ david = CallbackDeveloper.find(1)
224
+ CallbackDeveloper.delete(david.id)
225
+ assert_equal [
226
+ [ :after_find, :method ],
227
+ [ :after_initialize, :method ]
228
+ ], david.history
229
+ end
230
+ end