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
@@ -0,0 +1,24 @@
1
+ print "Using native DB2\n"
2
+ require 'fixtures/course'
3
+ require 'logger'
4
+
5
+ ActiveRecord::Base.logger = Logger.new("debug.log")
6
+
7
+ db1 = 'arunit'
8
+ db2 = 'arunit2'
9
+
10
+ ActiveRecord::Base.establish_connection(
11
+ :adapter => "db2",
12
+ :host => "localhost",
13
+ :username => "arunit",
14
+ :password => "arunit",
15
+ :database => db1
16
+ )
17
+
18
+ Course.establish_connection(
19
+ :adapter => "db2",
20
+ :host => "localhost",
21
+ :username => "arunit2",
22
+ :password => "arunit2",
23
+ :database => db2
24
+ )
@@ -6,10 +6,16 @@ ActiveRecord::Base.logger = Logger.new("debug.log")
6
6
 
7
7
  ActiveRecord::Base.establish_connection(
8
8
  :adapter => "sqlserver",
9
- :dsn => "DBI:ADO:Provider=SQLOLEDB;Data Source=(local);Initial Catalog=test;User Id=sa;Password=password;"
9
+ :host => "localhost",
10
+ :username => "sa",
11
+ :password => "",
12
+ :database => db1
10
13
  )
11
14
 
12
15
  Course.establish_connection(
13
- :adapter => "sqlserver",
14
- :dsn => "DBI:ADO:Provider=SQLOLEDB;Data Source=(local);Initial Catalog=test2;User Id=sa;Password=password;"
16
+ :adapter => "sqlserver",
17
+ :host => "localhost",
18
+ :username => "sa",
19
+ :password => "",
20
+ :database => db2
15
21
  )
@@ -286,9 +286,11 @@ class DeprecatedAssociationsTest < Test::Unit::TestCase
286
286
  natural = Client.create("name" => "Natural Company")
287
287
  apple.clients << natural
288
288
  assert_equal apple.id, natural.firm_id
289
- assert_equal Client.find(natural.id), Firm.find(apple.id).clients.find { |c| c.id == natural.id }
289
+ assert_equal Client.find(natural.id), Firm.find(apple.id).clients.find(natural.id)
290
290
  apple.clients.delete natural
291
- assert_nil Firm.find(apple.id).clients.find { |c| c.id == natural.id }
291
+ assert_raises(ActiveRecord::RecordNotFound) {
292
+ Firm.find(apple.id).clients.find(natural.id)
293
+ }
292
294
  end
293
295
 
294
296
  def test_natural_adding_of_has_and_belongs_to_many
@@ -299,17 +301,21 @@ class DeprecatedAssociationsTest < Test::Unit::TestCase
299
301
  rails.developers << john
300
302
  rails.developers << mike
301
303
 
302
- assert_equal Developer.find(john.id), Project.find(rails.id).developers.find { |d| d.id == john.id }
303
- assert_equal Developer.find(mike.id), Project.find(rails.id).developers.find { |d| d.id == mike.id }
304
- assert_equal Project.find(rails.id), Developer.find(mike.id).projects.find { |p| p.id == rails.id }
305
- assert_equal Project.find(rails.id), Developer.find(john.id).projects.find { |p| p.id == rails.id }
304
+ assert_equal Developer.find(john.id), Project.find(rails.id).developers.find(john.id)
305
+ assert_equal Developer.find(mike.id), Project.find(rails.id).developers.find(mike.id)
306
+ assert_equal Project.find(rails.id), Developer.find(mike.id).projects.find(rails.id)
307
+ assert_equal Project.find(rails.id), Developer.find(john.id).projects.find(rails.id)
306
308
  ap.developers << john
307
- assert_equal Developer.find(john.id), Project.find(ap.id).developers.find { |d| d.id == john.id }
308
- assert_equal Project.find(ap.id), Developer.find(john.id).projects.find { |p| p.id == ap.id }
309
+ assert_equal Developer.find(john.id), Project.find(ap.id).developers.find(john.id)
310
+ assert_equal Project.find(ap.id), Developer.find(john.id).projects.find(ap.id)
309
311
 
310
312
  ap.developers.delete john
311
- assert_nil Project.find(ap.id).developers.find { |d| d.id == john.id }
312
- assert_nil Developer.find(john.id).projects.find { |p| p.id == ap.id }
313
+ assert_raises(ActiveRecord::RecordNotFound) {
314
+ Project.find(ap.id).developers.find(john.id)
315
+ }
316
+ assert_raises(ActiveRecord::RecordNotFound) {
317
+ Developer.find(john.id).projects.find(ap.id)
318
+ }
313
319
  end
314
320
 
315
321
  def test_storing_in_pstore
@@ -4,14 +4,10 @@ require 'fixtures/topic'
4
4
  require 'fixtures/entrant'
5
5
 
6
6
  class FinderTest < Test::Unit::TestCase
7
- def setup
8
- @company_fixtures = create_fixtures("companies")
9
- @topic_fixtures = create_fixtures("topics")
10
- @entrant_fixtures = create_fixtures("entrants")
11
- end
7
+ fixtures :companies, :topics, :entrants
12
8
 
13
9
  def test_find
14
- assert_equal(@topic_fixtures["first"]["title"], Topic.find(1).title)
10
+ assert_equal(@topics["first"]["title"], Topic.find(1).title)
15
11
  end
16
12
 
17
13
  def test_find_by_array_of_one_id
@@ -21,7 +17,7 @@ class FinderTest < Test::Unit::TestCase
21
17
 
22
18
  def test_find_by_ids
23
19
  assert_equal(2, Topic.find(1, 2).length)
24
- assert_equal(@topic_fixtures["second"]["title"], Topic.find([ 2 ]).first.title)
20
+ assert_equal(@topics["second"]["title"], Topic.find([ 2 ]).first.title)
25
21
  end
26
22
 
27
23
  def test_find_by_ids_missing_one
@@ -34,33 +30,33 @@ class FinderTest < Test::Unit::TestCase
34
30
  entrants = Entrant.find_all nil, "id ASC", 2
35
31
 
36
32
  assert_equal(2, entrants.size)
37
- assert_equal(@entrant_fixtures["first"]["name"], entrants.first.name)
33
+ assert_equal(@entrants["first"]["name"], entrants.first.name)
38
34
  end
39
35
 
40
36
  def test_find_all_with_prepared_limit_and_offset
41
37
  entrants = Entrant.find_all nil, "id ASC", ["? OFFSET ?", 2, 1]
42
38
 
43
39
  assert_equal(2, entrants.size)
44
- assert_equal(@entrant_fixtures["second"]["name"], entrants.first.name)
40
+ assert_equal(@entrants["second"]["name"], entrants.first.name)
45
41
  end
46
42
 
47
43
  def test_find_with_entire_select_statement
48
44
  topics = Topic.find_by_sql "SELECT * FROM topics WHERE author_name = 'Mary'"
49
45
 
50
46
  assert_equal(1, topics.size)
51
- assert_equal(@topic_fixtures["second"]["title"], topics.first.title)
47
+ assert_equal(@topics["second"]["title"], topics.first.title)
52
48
  end
53
49
 
54
50
  def test_find_with_prepared_select_statement
55
51
  topics = Topic.find_by_sql ["SELECT * FROM topics WHERE author_name = ?", "Mary"]
56
52
 
57
53
  assert_equal(1, topics.size)
58
- assert_equal(@topic_fixtures["second"]["title"], topics.first.title)
54
+ assert_equal(@topics["second"]["title"], topics.first.title)
59
55
  end
60
56
 
61
57
  def test_find_first
62
58
  first = Topic.find_first "title = 'The First Topic'"
63
- assert_equal(@topic_fixtures["first"]["title"], first.title)
59
+ assert_equal(@topics["first"]["title"], first.title)
64
60
  end
65
61
 
66
62
  def test_find_first_failing
@@ -77,6 +73,11 @@ class FinderTest < Test::Unit::TestCase
77
73
  end
78
74
 
79
75
  def test_find_on_conditions
76
+ assert Topic.find(1, :conditions => "approved = 0")
77
+ assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => "approved = 1") }
78
+ end
79
+
80
+ def test_deprecated_find_on_conditions
80
81
  assert Topic.find_on_conditions(1, "approved = 0")
81
82
  assert_raises(ActiveRecord::RecordNotFound) { Topic.find_on_conditions(1, "approved = 1") }
82
83
  end
@@ -111,7 +112,19 @@ class FinderTest < Test::Unit::TestCase
111
112
  assert Company.find_first(["name = :name", {:name => "37signals' go'es agains"}])
112
113
  end
113
114
 
115
+ def test_bind_arity
116
+ assert_nothing_raised { bind '' }
117
+ assert_raises(ActiveRecord::PreparedStatementInvalid) { bind '', 1 }
118
+
119
+ assert_raises(ActiveRecord::PreparedStatementInvalid) { bind '?' }
120
+ assert_nothing_raised { bind '?', 1 }
121
+ assert_raises(ActiveRecord::PreparedStatementInvalid) { bind '?', 1, 1 }
122
+ end
123
+
114
124
  def test_named_bind_variables
125
+ assert_equal '1', bind(':a', :a => 1) # ' ruby-mode
126
+ assert_equal '1 1', bind(':a :a', :a => 1) # ' ruby-mode
127
+
115
128
  assert_kind_of Firm, Company.find_first(["name = :name", { :name => "37signals" }])
116
129
  assert_nil Company.find_first(["name = :name", { :name => "37signals!" }])
117
130
  assert_nil Company.find_first(["name = :name", { :name => "37signals!' OR 1=1" }])
@@ -124,7 +137,15 @@ class FinderTest < Test::Unit::TestCase
124
137
  }
125
138
  end
126
139
 
127
-
140
+ def test_named_bind_arity
141
+ assert_nothing_raised { bind '', {} }
142
+ assert_raises(ActiveRecord::PreparedStatementInvalid) { bind '', :a => 1 }
143
+ assert_raises(ActiveRecord::PreparedStatementInvalid) { bind ':a', {} } # ' ruby-mode
144
+ assert_nothing_raised { bind ':a', :a => 1 } # ' ruby-mode
145
+ assert_raises(ActiveRecord::PreparedStatementInvalid) { bind ':a', :a => 1, :b => 2 } # ' ruby-mode
146
+ assert_nothing_raised { bind ':a :a', :a => 1 } # ' ruby-mode
147
+ assert_raises(ActiveRecord::PreparedStatementInvalid) { bind ':a :a', :a => 1, :b => 2 } # ' ruby-mode
148
+ end
128
149
 
129
150
  def test_string_sanitation
130
151
  assert_not_equal "'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1")
@@ -142,4 +163,35 @@ class FinderTest < Test::Unit::TestCase
142
163
  assert_equal(1, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 2]))
143
164
  assert_equal(2, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 1]))
144
165
  end
166
+
167
+ def test_find_by_one_attribute
168
+ assert_equal @topics["first"].find, Topic.find_by_title("The First Topic")
169
+ assert_nil Topic.find_by_title("The First Topic!")
170
+ end
171
+
172
+ def test_find_by_one_missing_attribute
173
+ assert_raises(NoMethodError) { Topic.find_by_undertitle("The First Topic!") }
174
+ end
175
+
176
+ def test_find_by_two_attributes
177
+ assert_equal @topics["first"].find, Topic.find_by_title_and_author_name("The First Topic", "David")
178
+ assert_nil Topic.find_by_title_and_author_name("The First Topic", "Mary")
179
+ end
180
+
181
+ def test_find_all_by_one_attribute
182
+ topics = Topic.find_all_by_content("Have a nice day")
183
+ assert_equal 2, topics.size
184
+ assert topics.include?(@topics["first"].find)
185
+
186
+ assert_equal [], Topic.find_all_by_title("The First Topic!!")
187
+ end
188
+
189
+ protected
190
+ def bind(statement, *vars)
191
+ if vars.first.is_a?(Hash)
192
+ ActiveRecord::Base.send(:replace_named_bind_variables, statement, vars.first)
193
+ else
194
+ ActiveRecord::Base.send(:replace_bind_variables, statement, vars)
195
+ end
196
+ end
145
197
  end
@@ -0,0 +1,2 @@
1
+ class Binary < ActiveRecord::Base
2
+ end
@@ -0,0 +1,21 @@
1
+ first_client:
2
+ id: 2
3
+ type: Client
4
+ firm_id: 1
5
+ client_of: 2
6
+ name: Summit
7
+ ruby_type: Client
8
+
9
+ first_firm:
10
+ id: 1
11
+ type: Firm
12
+ name: 37signals
13
+ ruby_type: Firm
14
+
15
+ second_client:
16
+ id: 3
17
+ type: Client
18
+ firm_id: 1
19
+ client_of: 1
20
+ name: Microsoft
21
+ ruby_type: Client
@@ -0,0 +1,7 @@
1
+ java:
2
+ id: 2
3
+ name: Java Development
4
+
5
+ ruby:
6
+ id: 1
7
+ name: Ruby Development
@@ -0,0 +1,7 @@
1
+ david:
2
+ id: 1
3
+ name: David
4
+ balance: 50
5
+ address_street: Funny Street
6
+ address_city: Scary Town
7
+ address_country: Loony Land
@@ -0,0 +1,124 @@
1
+ CREATE TABLE accounts (
2
+ id int generated by default as identity (start with +10000),
3
+ firm_id int default NULL,
4
+ credit_limit int default NULL,
5
+ PRIMARY KEY (id)
6
+ );
7
+
8
+ CREATE TABLE companies (
9
+ id int generated by default as identity (start with +10000),
10
+ type varchar(50) default NULL,
11
+ ruby_type varchar(50) default NULL,
12
+ firm_id int default NULL,
13
+ name varchar(50) default NULL,
14
+ client_of int default NULL,
15
+ rating int default 1,
16
+ PRIMARY KEY (id)
17
+ );
18
+
19
+ CREATE TABLE topics (
20
+ id int generated by default as identity (start with +10000),
21
+ title varchar(255) default NULL,
22
+ author_name varchar(255) default NULL,
23
+ author_email_address varchar(255) default NULL,
24
+ written_on timestamp default NULL,
25
+ bonus_time time default NULL,
26
+ last_read date default NULL,
27
+ content varchar(3000),
28
+ approved smallint default 1,
29
+ replies_count int default 0,
30
+ parent_id int default NULL,
31
+ type varchar(50) default NULL,
32
+ PRIMARY KEY (id)
33
+ );
34
+
35
+ CREATE TABLE developers (
36
+ id int generated by default as identity (start with +10000),
37
+ name varchar(100) default NULL,
38
+ salary int default 70000,
39
+ PRIMARY KEY (id)
40
+ );
41
+
42
+ CREATE TABLE projects (
43
+ id int generated by default as identity (start with +10000),
44
+ name varchar(100) default NULL,
45
+ PRIMARY KEY (id)
46
+ );
47
+
48
+ CREATE TABLE developers_projects (
49
+ developer_id int NOT NULL,
50
+ project_id int NOT NULL,
51
+ joined_on date default NULL
52
+ );
53
+
54
+ CREATE TABLE customers (
55
+ id int generated by default as identity (start with +10000),
56
+ name varchar(100) default NULL,
57
+ balance int default 0,
58
+ address_street varchar(100) default NULL,
59
+ address_city varchar(100) default NULL,
60
+ address_country varchar(100) default NULL,
61
+ PRIMARY KEY (id)
62
+ );
63
+
64
+ CREATE TABLE movies (
65
+ movieid int generated by default as identity (start with +10000),
66
+ name varchar(100) default NULL,
67
+ PRIMARY KEY (movieid)
68
+ );
69
+
70
+ CREATE TABLE subscribers (
71
+ nick varchar(100) NOT NULL,
72
+ name varchar(100) default NULL,
73
+ PRIMARY KEY (nick)
74
+ );
75
+
76
+ CREATE TABLE booleantests (
77
+ id int generated by default as identity (start with +10000),
78
+ value int default NULL,
79
+ PRIMARY KEY (id)
80
+ );
81
+
82
+ CREATE TABLE auto_id_tests (
83
+ auto_id int generated by default as identity (start with +10000),
84
+ value int default NULL,
85
+ PRIMARY KEY (auto_id)
86
+ );
87
+
88
+ CREATE TABLE entrants (
89
+ id int NOT NULL PRIMARY KEY,
90
+ name varchar(255) NOT NULL,
91
+ course_id int NOT NULL
92
+ );
93
+
94
+ CREATE TABLE colnametests (
95
+ id int generated by default as identity (start with +10000),
96
+ references int NOT NULL,
97
+ PRIMARY KEY (id)
98
+ );
99
+
100
+ CREATE TABLE mixins (
101
+ id int generated by default as identity (start with +10000),
102
+ parent_id int default NULL,
103
+ pos int default NULL,
104
+ created_at timestamp default NULL,
105
+ updated_at timestamp default NULL,
106
+ lft int default NULL,
107
+ rgt int default NULL,
108
+ root_id int default NULL,
109
+ type varchar(40) default NULL,
110
+ PRIMARY KEY (id)
111
+ );
112
+
113
+ CREATE TABLE people (
114
+ id int generated by default as identity (start with +10000),
115
+ first_name varchar(40) NOT NULL,
116
+ lock_version int default 0,
117
+ PRIMARY KEY (id)
118
+ );
119
+
120
+ CREATE TABLE binaries (
121
+ id int generated by default as identity (start with +10000),
122
+ data blob(50000),
123
+ PRIMARY KEY (id)
124
+ );
@@ -0,0 +1,4 @@
1
+ CREATE TABLE courses (
2
+ id int NOT NULL PRIMARY KEY,
3
+ name varchar(255) NOT NULL
4
+ );
@@ -110,3 +110,15 @@ CREATE TABLE `mixins` (
110
110
  `type` varchar(40) default NULL,
111
111
  PRIMARY KEY (`id`)
112
112
  );
113
+
114
+ CREATE TABLE `people` (
115
+ `id` INTEGER NOT NULL PRIMARY KEY,
116
+ `first_name` VARCHAR(40) NOT NULL,
117
+ `lock_version` INTEGER NOT NULL DEFAULT 0
118
+ );
119
+
120
+ CREATE TABLE `binaries` (
121
+ `id` int(11) NOT NULL auto_increment,
122
+ `data` mediumblob,
123
+ PRIMARY KEY (`id`)
124
+ );
@@ -127,3 +127,16 @@ CREATE TABLE mixins (
127
127
  updated_at timestamp,
128
128
  PRIMARY KEY (id)
129
129
  );
130
+
131
+ CREATE TABLE people (
132
+ id serial,
133
+ first_name text,
134
+ lock_version integer default 0,
135
+ PRIMARY KEY (id)
136
+ );
137
+
138
+ CREATE TABLE binaries (
139
+ id serial ,
140
+ data bytea,
141
+ PRIMARY KEY (id)
142
+ );