activerecord 1.1.0 → 1.2.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 (68) hide show
  1. data/CHANGELOG +250 -0
  2. data/README +17 -9
  3. data/dev-utils/eval_debugger.rb +1 -1
  4. data/install.rb +3 -1
  5. data/lib/active_record.rb +9 -2
  6. data/lib/active_record/acts/list.rb +178 -0
  7. data/lib/active_record/acts/tree.rb +44 -0
  8. data/lib/active_record/associations.rb +45 -8
  9. data/lib/active_record/associations/association_collection.rb +18 -9
  10. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +14 -13
  11. data/lib/active_record/associations/has_many_association.rb +21 -12
  12. data/lib/active_record/base.rb +137 -37
  13. data/lib/active_record/callbacks.rb +30 -25
  14. data/lib/active_record/connection_adapters/abstract_adapter.rb +57 -33
  15. data/lib/active_record/connection_adapters/mysql_adapter.rb +4 -0
  16. data/lib/active_record/connection_adapters/sqlite_adapter.rb +3 -2
  17. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +298 -0
  18. data/lib/active_record/fixtures.rb +241 -147
  19. data/lib/active_record/support/class_inheritable_attributes.rb +5 -2
  20. data/lib/active_record/support/inflector.rb +13 -12
  21. data/lib/active_record/support/misc.rb +6 -0
  22. data/lib/active_record/timestamp.rb +33 -0
  23. data/lib/active_record/transactions.rb +1 -1
  24. data/lib/active_record/validations.rb +294 -16
  25. data/rakefile +3 -7
  26. data/test/abstract_unit.rb +1 -4
  27. data/test/associations_test.rb +17 -4
  28. data/test/base_test.rb +37 -5
  29. data/test/connections/native_sqlserver/connection.rb +15 -0
  30. data/test/deprecated_associations_test.rb +40 -38
  31. data/test/finder_test.rb +82 -4
  32. data/test/fixtures/accounts.yml +8 -0
  33. data/test/fixtures/company.rb +6 -0
  34. data/test/fixtures/company_in_module.rb +1 -1
  35. data/test/fixtures/db_definitions/mysql.sql +13 -0
  36. data/test/fixtures/db_definitions/postgresql.sql +13 -0
  37. data/test/fixtures/db_definitions/sqlite.sql +14 -0
  38. data/test/fixtures/db_definitions/sqlserver.sql +110 -0
  39. data/test/fixtures/db_definitions/sqlserver2.sql +4 -0
  40. data/test/fixtures/developer.rb +2 -2
  41. data/test/fixtures/developers.yml +13 -0
  42. data/test/fixtures/fixture_database.sqlite +0 -0
  43. data/test/fixtures/fixture_database_2.sqlite +0 -0
  44. data/test/fixtures/mixin.rb +17 -0
  45. data/test/fixtures/mixins.yml +14 -0
  46. data/test/fixtures/naked/csv/accounts.csv +1 -0
  47. data/test/fixtures/naked/yml/accounts.yml +1 -0
  48. data/test/fixtures/naked/yml/companies.yml +1 -0
  49. data/test/fixtures/naked/yml/courses.yml +1 -0
  50. data/test/fixtures/project.rb +6 -0
  51. data/test/fixtures/reply.rb +14 -1
  52. data/test/fixtures/topic.rb +2 -2
  53. data/test/fixtures/topics/first +1 -0
  54. data/test/fixtures_test.rb +42 -12
  55. data/test/inflector_test.rb +2 -1
  56. data/test/inheritance_test.rb +22 -12
  57. data/test/mixin_test.rb +138 -0
  58. data/test/pk_test.rb +4 -2
  59. data/test/reflection_test.rb +3 -3
  60. data/test/transactions_test.rb +15 -0
  61. data/test/validations_test.rb +229 -4
  62. metadata +24 -10
  63. data/lib/active_record/associations.rb.orig +0 -555
  64. data/test/deprecated_associations_test.rb.orig +0 -334
  65. data/test/fixtures/accounts/signals37 +0 -3
  66. data/test/fixtures/accounts/unknown +0 -2
  67. data/test/fixtures/developers/david +0 -2
  68. data/test/fixtures/developers/jamis +0 -2
@@ -1,20 +1,27 @@
1
1
  require 'abstract_unit'
2
2
  require 'fixtures/company'
3
3
  require 'fixtures/topic'
4
+ require 'fixtures/entrant'
4
5
 
5
6
  class FinderTest < Test::Unit::TestCase
6
7
  def setup
7
8
  @company_fixtures = create_fixtures("companies")
8
9
  @topic_fixtures = create_fixtures("topics")
10
+ @entrant_fixtures = create_fixtures("entrants")
9
11
  end
10
12
 
11
13
  def test_find
12
14
  assert_equal(@topic_fixtures["first"]["title"], Topic.find(1).title)
13
15
  end
14
16
 
17
+ def test_find_by_array_of_one_id
18
+ assert_kind_of(Array, Topic.find([ 1 ]))
19
+ assert_equal(1, Topic.find([ 1 ]).length)
20
+ end
21
+
15
22
  def test_find_by_ids
16
23
  assert_equal(2, Topic.find(1, 2).length)
17
- assert_equal(@topic_fixtures["second"]["title"], Topic.find([ 2 ]).title)
24
+ assert_equal(@topic_fixtures["second"]["title"], Topic.find([ 2 ]).first.title)
18
25
  end
19
26
 
20
27
  def test_find_by_ids_missing_one
@@ -23,6 +30,20 @@ class FinderTest < Test::Unit::TestCase
23
30
  }
24
31
  end
25
32
 
33
+ def test_find_all_with_limit
34
+ entrants = Entrant.find_all nil, "id ASC", 2
35
+
36
+ assert_equal(2, entrants.size)
37
+ assert_equal(@entrant_fixtures["first"]["name"], entrants.first.name)
38
+ end
39
+
40
+ def test_find_all_with_prepared_limit_and_offset
41
+ entrants = Entrant.find_all nil, "id ASC", ["? OFFSET ?", 2, 1]
42
+
43
+ assert_equal(2, entrants.size)
44
+ assert_equal(@entrant_fixtures["second"]["name"], entrants.first.name)
45
+ end
46
+
26
47
  def test_find_with_entire_select_statement
27
48
  topics = Topic.find_by_sql "SELECT * FROM topics WHERE author_name = 'Mary'"
28
49
 
@@ -30,6 +51,13 @@ class FinderTest < Test::Unit::TestCase
30
51
  assert_equal(@topic_fixtures["second"]["title"], topics.first.title)
31
52
  end
32
53
 
54
+ def test_find_with_prepared_select_statement
55
+ topics = Topic.find_by_sql ["SELECT * FROM topics WHERE author_name = ?", "Mary"]
56
+
57
+ assert_equal(1, topics.size)
58
+ assert_equal(@topic_fixtures["second"]["title"], topics.first.title)
59
+ end
60
+
33
61
  def test_find_first
34
62
  first = Topic.find_first "title = 'The First Topic'"
35
63
  assert_equal(@topic_fixtures["first"]["title"], first.title)
@@ -60,8 +88,58 @@ class FinderTest < Test::Unit::TestCase
60
88
  assert_kind_of Time, Topic.find_first(["id = %d", 1]).written_on
61
89
  end
62
90
 
91
+ def test_bind_variables
92
+ assert_kind_of Firm, Company.find_first(["name = ?", "37signals"])
93
+ assert_nil Company.find_first(["name = ?", "37signals!"])
94
+ assert_nil Company.find_first(["name = ?", "37signals!' OR 1=1"])
95
+ assert_kind_of Time, Topic.find_first(["id = ?", 1]).written_on
96
+ assert_raises(ActiveRecord::PreparedStatementInvalid) {
97
+ Company.find_first(["id=? AND name = ?", 2])
98
+ }
99
+ assert_raises(ActiveRecord::PreparedStatementInvalid) {
100
+ Company.find_first(["id=?", 2, 3, 4])
101
+ }
102
+ end
103
+
104
+ def test_bind_variables_with_quotes
105
+ Company.create("name" => "37signals' go'es agains")
106
+ assert Company.find_first(["name = ?", "37signals' go'es agains"])
107
+ end
108
+
109
+ def test_named_bind_variables_with_quotes
110
+ Company.create("name" => "37signals' go'es agains")
111
+ assert Company.find_first(["name = :name", {:name => "37signals' go'es agains"}])
112
+ end
113
+
114
+ def test_named_bind_variables
115
+ assert_kind_of Firm, Company.find_first(["name = :name", { :name => "37signals" }])
116
+ assert_nil Company.find_first(["name = :name", { :name => "37signals!" }])
117
+ assert_nil Company.find_first(["name = :name", { :name => "37signals!' OR 1=1" }])
118
+ assert_kind_of Time, Topic.find_first(["id = :id", { :id => 1 }]).written_on
119
+ assert_raises(ActiveRecord::PreparedStatementInvalid) {
120
+ Company.find_first(["id=:id and name=:name", { :id=>3 }])
121
+ }
122
+ assert_raises(ActiveRecord::PreparedStatementInvalid) {
123
+ Company.find_first(["id=:id", { :id=>3, :name=>"37signals!" }])
124
+ }
125
+ end
126
+
127
+
128
+
63
129
  def test_string_sanitation
64
- assert_equal "something '' 1=1", ActiveRecord::Base.sanitize("something ' 1=1")
65
- assert_equal "something select table", ActiveRecord::Base.sanitize("something; select table")
130
+ assert_not_equal "'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1")
131
+ assert_equal "'something; select table'", ActiveRecord::Base.sanitize("something; select table")
132
+ end
133
+
134
+ def test_count
135
+ assert_equal(0, Entrant.count("id > 3"))
136
+ assert_equal(1, Entrant.count(["id > ?", 2]))
137
+ assert_equal(2, Entrant.count(["id > ?", 1]))
138
+ end
139
+
140
+ def test_count_by_sql
141
+ assert_equal(0, Entrant.count_by_sql("SELECT COUNT(*) FROM entrants WHERE id > 3"))
142
+ assert_equal(1, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 2]))
143
+ assert_equal(2, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 1]))
66
144
  end
67
- end
145
+ end
@@ -0,0 +1,8 @@
1
+ signals37:
2
+ id: 1
3
+ firm_id: 1
4
+ credit_limit: 50
5
+
6
+ unknown:
7
+ id: 2
8
+ credit_limit: 50
@@ -9,6 +9,12 @@ class Firm < Company
9
9
  has_many :clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id"
10
10
  has_many :clients_like_ms, :conditions => "name = 'Microsoft'", :class_name => "Client", :order => "id"
11
11
  has_many :clients_using_sql, :class_name => "Client", :finder_sql => 'SELECT * FROM companies WHERE client_of = #{id}'
12
+ has_many :clients_using_counter_sql, :class_name => "Client",
13
+ :finder_sql => 'SELECT * FROM companies WHERE client_of = #{id}',
14
+ :counter_sql => 'SELECT COUNT(*) FROM companies WHERE client_of = #{id}'
15
+ has_many :clients_using_zero_counter_sql, :class_name => "Client",
16
+ :finder_sql => 'SELECT * FROM companies WHERE client_of = #{id}',
17
+ :counter_sql => 'SELECT 0 FROM companies WHERE client_of = #{id}'
12
18
 
13
19
  has_one :account, :dependent => true
14
20
  end
@@ -24,7 +24,7 @@ module MyApplication
24
24
 
25
25
  protected
26
26
  def validate
27
- errors.add_on_boundry_breaking("name", 3..20)
27
+ errors.add_on_boundary_breaking("name", 3..20)
28
28
  end
29
29
  end
30
30
 
@@ -23,6 +23,7 @@ CREATE TABLE `topics` (
23
23
  `author_name` varchar(255) default NULL,
24
24
  `author_email_address` varchar(255) default NULL,
25
25
  `written_on` datetime default NULL,
26
+ `bonus_time` time default NULL,
26
27
  `last_read` date default NULL,
27
28
  `content` text,
28
29
  `approved` tinyint(1) default 1,
@@ -95,3 +96,15 @@ CREATE TABLE `colnametests` (
95
96
  `references` int(11) NOT NULL,
96
97
  PRIMARY KEY (`id`)
97
98
  );
99
+
100
+ CREATE TABLE `mixins` (
101
+ `id` int(11) NOT NULL auto_increment,
102
+ `parent_id` int(11) default NULL,
103
+ `pos` int(11) default NULL,
104
+ `lft` int(11) default NULL,
105
+ `rgt` int(11) default NULL,
106
+ `root_id` int(11) default NULL,
107
+ `created_at` datetime default NULL,
108
+ `updated_at` datetime default NULL,
109
+ PRIMARY KEY (`id`)
110
+ );
@@ -46,6 +46,7 @@ CREATE TABLE topics (
46
46
  author_name character varying(255),
47
47
  author_email_address character varying(255),
48
48
  written_on timestamp without time zone,
49
+ bonus_time time,
49
50
  last_read date,
50
51
  content text,
51
52
  replies_count integer default 0,
@@ -112,3 +113,15 @@ CREATE TABLE colnametests (
112
113
  id serial,
113
114
  "references" integer NOT NULL
114
115
  );
116
+
117
+ CREATE TABLE mixins (
118
+ id serial,
119
+ parent_id integer,
120
+ pos integer,
121
+ lft integer,
122
+ rgt integer,
123
+ root_id integer,
124
+ created_at timestamp,
125
+ updated_at timestamp,
126
+ PRIMARY KEY (id)
127
+ );
@@ -21,6 +21,7 @@ CREATE TABLE 'topics' (
21
21
  'author_name' VARCHAR(255) DEFAULT NULL,
22
22
  'author_email_address' VARCHAR(255) DEFAULT NULL,
23
23
  'written_on' DATETIME DEFAULT NULL,
24
+ 'bonus_time' TIME DEFAULT NULL,
24
25
  'last_read' DATE DEFAULT NULL,
25
26
  'content' TEXT,
26
27
  'approved' INTEGER DEFAULT 1,
@@ -84,3 +85,16 @@ CREATE TABLE 'colnametests' (
84
85
  'id' INTEGER NOT NULL PRIMARY KEY,
85
86
  'references' INTEGER NOT NULL
86
87
  );
88
+
89
+ CREATE TABLE 'mixins' (
90
+ 'id' INTEGER NOT NULL PRIMARY KEY,
91
+ 'parent_id' INTEGER DEFAULT NULL,
92
+ 'pos' INTEGER DEFAULT NULL,
93
+ 'lft' INTEGER DEFAULT NULL,
94
+ 'rgt' INTEGER DEFAULT NULL,
95
+ 'root_id' INTEGER DEFAULT NULL,
96
+ 'created_at' DATETIME DEFAULT NULL,
97
+ 'updated_at' DATETIME DEFAULT NULL
98
+ );
99
+
100
+
@@ -0,0 +1,110 @@
1
+ CREATE TABLE accounts (
2
+ id int NOT NULL IDENTITY(1, 1),
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 NOT NULL IDENTITY(1, 1),
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
+ companies_count int default 0,
16
+ rating int default 1,
17
+ PRIMARY KEY (id)
18
+ )
19
+
20
+ CREATE TABLE topics (
21
+ id int NOT NULL IDENTITY(1, 1),
22
+ title varchar(255) default NULL,
23
+ author_name varchar(255) default NULL,
24
+ author_email_address varchar(255) default NULL,
25
+ written_on datetime default NULL,
26
+ last_read datetime default NULL,
27
+ content text,
28
+ approved tinyint 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 NOT NULL IDENTITY(1, 1),
37
+ name varchar(100) default NULL,
38
+ PRIMARY KEY (id)
39
+ );
40
+
41
+ CREATE TABLE projects (
42
+ id int NOT NULL IDENTITY(1, 1),
43
+ name varchar(100) default NULL,
44
+ PRIMARY KEY (id)
45
+ );
46
+
47
+ CREATE TABLE developers_projects (
48
+ developer_id int NOT NULL,
49
+ project_id int NOT NULL
50
+ );
51
+
52
+ CREATE TABLE customers (
53
+ id int NOT NULL IDENTITY(1, 1),
54
+ name varchar(100) default NULL,
55
+ balance int default 0,
56
+ address_street varchar(100) default NULL,
57
+ address_city varchar(100) default NULL,
58
+ address_country varchar(100) default NULL,
59
+ PRIMARY KEY (id)
60
+ );
61
+
62
+ CREATE TABLE movies (
63
+ movieid int NOT NULL IDENTITY(1, 1),
64
+ name varchar(100) default NULL,
65
+ PRIMARY KEY (movieid)
66
+ );
67
+
68
+ CREATE TABLE subscribers (
69
+ nick varchar(100) NOT NULL,
70
+ name varchar(100) default NULL,
71
+ PRIMARY KEY (nick)
72
+ );
73
+
74
+ CREATE TABLE booleantests (
75
+ id int NOT NULL IDENTITY(1, 1),
76
+ value integer default NULL,
77
+ PRIMARY KEY (id)
78
+ );
79
+
80
+ CREATE TABLE auto_id_tests (
81
+ auto_id int NOT NULL IDENTITY(1, 1),
82
+ value int default NULL,
83
+ PRIMARY KEY (auto_id)
84
+ );
85
+
86
+ CREATE TABLE entrants (
87
+ id int NOT NULL PRIMARY KEY,
88
+ name varchar(255) NOT NULL,
89
+ course_id int NOT NULL
90
+ );
91
+
92
+ CREATE TABLE colnametests (
93
+ id int NOT NULL IDENTITY(1, 1),
94
+ [references] int NOT NULL,
95
+ PRIMARY KEY (id)
96
+ );
97
+
98
+ CREATE TABLE mixins (
99
+ id int NOT NULL IDENTITY(1, 1),
100
+ parent_id int default NULL,
101
+ pos int default NULL,
102
+ lft int default NULL,
103
+ rgt int default NULL,
104
+ root_id int default NULL,
105
+ created_at datetime default NULL,
106
+ updated_at datetime default NULL,
107
+ PRIMARY KEY (id)
108
+ );
109
+
110
+
@@ -0,0 +1,4 @@
1
+ CREATE TABLE courses (
2
+ id int NOT NULL PRIMARY KEY,
3
+ name varchar(255) NOT NULL
4
+ );
@@ -3,6 +3,6 @@ class Developer < ActiveRecord::Base
3
3
 
4
4
  protected
5
5
  def validate
6
- errors.add_on_boundry_breaking("name", 3..20)
6
+ errors.add_on_boundary_breaking("name", 3..20)
7
7
  end
8
- end
8
+ end
@@ -0,0 +1,13 @@
1
+ david:
2
+ id: 1
3
+ name: David
4
+
5
+ jamis:
6
+ id: 2
7
+ name: Jamis
8
+
9
+ <% for digit in 3..10 %>
10
+ dev_<%= digit %>:
11
+ id: <%= digit %>
12
+ name: fixture_<%= digit %>
13
+ <% end %>
@@ -0,0 +1,17 @@
1
+ class Mixin < ActiveRecord::Base
2
+ acts_as_tree :foreign_key => "parent_id", :order => "id"
3
+
4
+ end
5
+
6
+ class ListMixin < ActiveRecord::Base
7
+ acts_as_list :column => "pos", :scope => :parent
8
+
9
+ def self.table_name() "mixins" end
10
+ end
11
+
12
+
13
+ class ListWithStringScopeMixin < ActiveRecord::Base
14
+ acts_as_list :column => "pos", :scope => 'parent_id = #{parent_id}'
15
+
16
+ def self.table_name() "mixins" end
17
+ end
@@ -0,0 +1,14 @@
1
+ first:
2
+ id: 1
3
+ pos: 1
4
+ parent_id: 0
5
+
6
+ second:
7
+ id: 2
8
+ pos: 1
9
+ parent_id: 1
10
+
11
+ third:
12
+ id: 3
13
+ pos: 2
14
+ parent_id: 1
@@ -0,0 +1 @@
1
+ # i wonder what will happen here
@@ -0,0 +1 @@
1
+ qwerty
@@ -1,4 +1,10 @@
1
1
  class Project < ActiveRecord::Base
2
2
  has_and_belongs_to_many :developers, :uniq => true
3
3
  has_and_belongs_to_many :developers_named_david, :class_name => "Developer", :conditions => "name = 'David'", :uniq => true
4
+ end
5
+
6
+ class SpecialProject < Project
7
+ def hello_world
8
+ "hello there!"
9
+ end
4
10
  end
@@ -1,21 +1,34 @@
1
1
  class Reply < Topic
2
2
  belongs_to :topic, :foreign_key => "parent_id", :counter_cache => true
3
+ has_many :silly_replies, :dependent => true, :foreign_key => "parent_id"
4
+
5
+ validate :errors_on_empty_content
6
+ validate_on_create :title_is_wrong_create
3
7
 
4
8
  attr_accessible :title, :author_name, :author_email_address, :written_on, :content, :last_read
5
9
 
6
10
  def validate
7
11
  errors.add("title", "Empty") unless attribute_present? "title"
12
+ end
13
+
14
+ def errors_on_empty_content
8
15
  errors.add("content", "Empty") unless attribute_present? "content"
9
16
  end
10
17
 
11
18
  def validate_on_create
12
- errors.add("title", "is Wrong Create") if attribute_present?("title") && title == "Wrong Create"
13
19
  if attribute_present?("title") && attribute_present?("content") && content == "Mismatch"
14
20
  errors.add("title", "is Content Mismatch")
15
21
  end
16
22
  end
17
23
 
24
+ def title_is_wrong_create
25
+ errors.add("title", "is Wrong Create") if attribute_present?("title") && title == "Wrong Create"
26
+ end
27
+
18
28
  def validate_on_update
19
29
  errors.add("title", "is Wrong Update") if attribute_present?("title") && title == "Wrong Update"
20
30
  end
31
+ end
32
+
33
+ class SillyReply < Reply
21
34
  end