odbc-rails 1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/AUTHORS +16 -0
  2. data/COPYING +21 -0
  3. data/ChangeLog +89 -0
  4. data/LICENSE +5 -0
  5. data/NEWS +12 -0
  6. data/README +282 -0
  7. data/lib/active_record/connection_adapters/odbc_adapter.rb +1792 -0
  8. data/lib/active_record/vendor/odbcext_db2.rb +87 -0
  9. data/lib/active_record/vendor/odbcext_informix.rb +132 -0
  10. data/lib/active_record/vendor/odbcext_informix_col.rb +45 -0
  11. data/lib/active_record/vendor/odbcext_ingres.rb +156 -0
  12. data/lib/active_record/vendor/odbcext_microsoftsqlserver.rb +185 -0
  13. data/lib/active_record/vendor/odbcext_microsoftsqlserver_col.rb +40 -0
  14. data/lib/active_record/vendor/odbcext_mysql.rb +136 -0
  15. data/lib/active_record/vendor/odbcext_oracle.rb +220 -0
  16. data/lib/active_record/vendor/odbcext_postgresql.rb +179 -0
  17. data/lib/active_record/vendor/odbcext_progress.rb +139 -0
  18. data/lib/active_record/vendor/odbcext_progress89.rb +259 -0
  19. data/lib/active_record/vendor/odbcext_sybase.rb +212 -0
  20. data/lib/active_record/vendor/odbcext_sybase_col.rb +49 -0
  21. data/lib/active_record/vendor/odbcext_virtuoso.rb +146 -0
  22. data/lib/odbc_adapter.rb +28 -0
  23. data/support/lib/active_record/connection_adapters/abstract/schema_definitions.rb +259 -0
  24. data/support/odbc_rails.diff +707 -0
  25. data/support/pack_odbc.rb +119 -0
  26. data/support/rake/rails_plugin_package_task.rb +212 -0
  27. data/support/test/base_test.rb +1349 -0
  28. data/support/test/migration_test.rb +566 -0
  29. data/test/connections/native_odbc/connection.rb +95 -0
  30. data/test/fixtures/db_definitions/db2.drop.sql +30 -0
  31. data/test/fixtures/db_definitions/db2.sql +217 -0
  32. data/test/fixtures/db_definitions/db22.drop.sql +2 -0
  33. data/test/fixtures/db_definitions/db22.sql +5 -0
  34. data/test/fixtures/db_definitions/informix.drop.sql +30 -0
  35. data/test/fixtures/db_definitions/informix.sql +205 -0
  36. data/test/fixtures/db_definitions/informix2.drop.sql +2 -0
  37. data/test/fixtures/db_definitions/informix2.sql +5 -0
  38. data/test/fixtures/db_definitions/ingres.drop.sql +62 -0
  39. data/test/fixtures/db_definitions/ingres.sql +232 -0
  40. data/test/fixtures/db_definitions/ingres2.drop.sql +2 -0
  41. data/test/fixtures/db_definitions/ingres2.sql +5 -0
  42. data/test/fixtures/db_definitions/mysql.drop.sql +30 -0
  43. data/test/fixtures/db_definitions/mysql.sql +219 -0
  44. data/test/fixtures/db_definitions/mysql2.drop.sql +2 -0
  45. data/test/fixtures/db_definitions/mysql2.sql +5 -0
  46. data/test/fixtures/db_definitions/oracle_odbc.drop.sql +64 -0
  47. data/test/fixtures/db_definitions/oracle_odbc.sql +257 -0
  48. data/test/fixtures/db_definitions/oracle_odbc2.drop.sql +2 -0
  49. data/test/fixtures/db_definitions/oracle_odbc2.sql +6 -0
  50. data/test/fixtures/db_definitions/progress.drop.sql +61 -0
  51. data/test/fixtures/db_definitions/progress.sql +234 -0
  52. data/test/fixtures/db_definitions/progress2.drop.sql +2 -0
  53. data/test/fixtures/db_definitions/progress2.sql +6 -0
  54. data/test/fixtures/db_definitions/sqlserver.drop.sql +30 -0
  55. data/test/fixtures/db_definitions/sqlserver.sql +203 -0
  56. data/test/fixtures/db_definitions/sqlserver2.drop.sql +2 -0
  57. data/test/fixtures/db_definitions/sqlserver2.sql +5 -0
  58. data/test/fixtures/db_definitions/sybase.drop.sql +31 -0
  59. data/test/fixtures/db_definitions/sybase.sql +204 -0
  60. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  61. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  62. data/test/fixtures/db_definitions/virtuoso.drop.sql +30 -0
  63. data/test/fixtures/db_definitions/virtuoso.sql +200 -0
  64. data/test/fixtures/db_definitions/virtuoso2.drop.sql +2 -0
  65. data/test/fixtures/db_definitions/virtuoso2.sql +5 -0
  66. metadata +149 -0
@@ -0,0 +1,119 @@
1
+ #
2
+ # $Id: pack_odbc.rb,v 1.2 2006/08/21 23:37:17 source Exp $
3
+ #
4
+ # OpenLink ODBC Adapter for Ruby on Rails
5
+ # Copyright (C) 2006 OpenLink Software
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject
13
+ # to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
22
+ # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
23
+ # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+ #
26
+
27
+ #
28
+ # Copies Rails ODBC adapter files from development ActiveRecord tree to a
29
+ # staging area ready for packaging.
30
+ #
31
+
32
+ require 'fileutils'
33
+ require 'find'
34
+ require 'ftools'
35
+ require 'rdoc/rdoc'
36
+
37
+ # Development ActiveRecord source tree
38
+ $AR_DEV_ROOT = "/data/radrails/carl/activerecord_svn_co"
39
+ # Staging area
40
+ $PACK_ROOT = "/tmp/rails_odbc_pack"
41
+ # Location of miscellaneous files not in $AR_DEV_ROOT
42
+ $MISC_ROOT = "/dev/rails_odbc"
43
+
44
+ miscFiles = [
45
+ "install_odbc.rb",
46
+ "pack_odbc.rb",
47
+ "readme.html"
48
+ ]
49
+
50
+ # Stems of new .sql files added to $AR_DEV_ROOT/test/fixtures/db_definitions
51
+ # !! UPDATE AS SUPPORT FOR OTHER DATABASES IS ADDED TO ODBC ADAPTER
52
+ wantedBasenamePrefixes = [
53
+ "informix", "ingres", "virtuoso", "oracle_odbc"
54
+ ]
55
+
56
+ # Files in public ActiveRecord source tree which have been modified for
57
+ # ODBC adapter
58
+ modifiedFiles = [
59
+ "./test/base_test.rb",
60
+ "./test/migration_test.rb",
61
+ "./lib/active_record/connection_adapters/abstract/schema_definitions.rb"
62
+ ]
63
+
64
+ raise Exception, "Directory doesn't exist: #{$AR_DEV_ROOT}" if !File.exist?($AR_DEV_ROOT)
65
+
66
+ # Create directory tree under $PACK_ROOT
67
+ FileUtils.mkdir_p($PACK_ROOT)
68
+ FileUtils.mkdir_p(File.join($PACK_ROOT, 'new_files/lib/active_record/connection_adapters'))
69
+ FileUtils.mkdir_p(File.join($PACK_ROOT, 'new_files/lib/active_record/vendor'))
70
+ FileUtils.mkdir_p(File.join($PACK_ROOT, 'new_files/test/fixtures/db_definitions'))
71
+ FileUtils.mkdir_p(File.join($PACK_ROOT, 'new_files/test/connections/native_odbc'))
72
+ FileUtils.mkdir_p(File.join($PACK_ROOT,
73
+ 'modified_files/lib/active_record/connection_adapters/abstract'))
74
+ FileUtils.mkdir_p(File.join($PACK_ROOT, 'modified_files/test'))
75
+
76
+ # Generate RDoc's
77
+ Dir.glob($AR_DEV_ROOT + "/**/odbc_adapter.rb") { |f|
78
+ FileUtils.cp(f, $PACK_ROOT)
79
+ Dir.chdir($PACK_ROOT)
80
+ FileUtils.rmtree('doc')
81
+ r = RDoc::RDoc.new
82
+ r.document(['-q', 'odbc_adapter.rb'])
83
+ FileUtils.rm('odbc_adapter.rb')
84
+ }
85
+
86
+ # Copy new files into $PACK_ROOT
87
+ # i.e. files not in the current ActiveRecord distribution
88
+ Dir.chdir($AR_DEV_ROOT)
89
+ Find.find(".") { |f|
90
+ if File.fnmatch("./**/odbc*.rb" , f)
91
+ FileUtils.cp(f, File.join($PACK_ROOT, "new_files", *f.split(/\//)),
92
+ :verbose => true)
93
+ end
94
+ }
95
+
96
+ f = "./test/connections/native_odbc/connection.rb"
97
+ FileUtils.cp(f, File.join($PACK_ROOT, "new_files", *f.split(/\//)),
98
+ :verbose => true)
99
+
100
+ Find.find(".") { |f|
101
+ if File.fnmatch("./**/*.sql" , f)
102
+ basename = File.basename(f, ".sql")
103
+ wantedBasenamePrefixes.each do |prefix|
104
+ if basename.match("^#{prefix}")
105
+ FileUtils.cp(f, File.join($PACK_ROOT, "new_files", *f.split(/\//)),
106
+ :verbose => true)
107
+ end
108
+ end
109
+ end
110
+ }
111
+
112
+ modifiedFiles.each do |f|
113
+ FileUtils.cp(f, File.join($PACK_ROOT, "modified_files", *f.split(/\//)),
114
+ :verbose => true)
115
+ end
116
+
117
+ miscFiles.each do |f|
118
+ FileUtils.cp(File.join($MISC_ROOT, f), $PACK_ROOT, :verbose => true)
119
+ end
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright (c) 2006 by Zak Mandhro
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software
6
+ # and associated documentation files (the "Software"), to deal in the Software without restriction,
7
+ # including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
+ # and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
9
+ # subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all copies or substantial
12
+ # portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
15
+ # LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
16
+ # EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
18
+ # USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+ #
20
+ # = Rails Plug-in Package Task
21
+ #
22
+ # RailsPluginPackageTask is a rake task designed to automate the publishing of
23
+ # Ruby on Rails plug-ins. The Rails plug-in installer _RecursiveHTTPFetcher_ makes
24
+ # certain assumptions about the web servers that does not hold through from
25
+ # server to server, for example:
26
+ #
27
+ # * Server generates an index page with links
28
+ # * All links to plug-in files are relative links
29
+ # * Folder links end with a forward slash (used to recurse)
30
+ #
31
+ # RubyForge web server is an example of where these assupmtions don't hold
32
+ # true. As a result, you can not simply copy your files to a web server
33
+ # and expect Rails HTTP plugin installer to just work.
34
+ #
35
+ # This Rake task helps fill the gap by complying to the plug-in scripts assumptions.
36
+ # Following the Rake package task conventions, it defines the "rails_plugin" task
37
+ # that recurses through your _package_files_, generates compliant index.html for
38
+ # each folder (that contains a file), and creates a directory structure that you
39
+ # can publish as a set for your plugin.
40
+ #
41
+ # = Example
42
+ #
43
+ # The following example uses the Rake::RailsPluginPackageTask to create
44
+ # the package. It then uses the Rake::SshDirPublisher to publish the plugin
45
+ # directory to RubyForge.
46
+ # #
47
+ # Rake::RailsPluginPackageTask.new(ProjectInfo[:name], ProjectInfo[:version]) do |p|
48
+ # p.package_files = PluginPackageFiles
49
+ # p.plugin_files = FileList["rails_plugin/**/*"]
50
+ # p.extra_links = {"Project page"=>ProjectInfo[:homepage],
51
+ # "Author: #{ProjectInfo[:author_name]}"=>ProjectInfo[:author_link]}
52
+ # p.verbose = true
53
+ # end
54
+ # task :rails_plugin=>:clobber
55
+ #
56
+ # desc "Publish Ruby on Rails plug-in on RubyForge"
57
+ # task :release_plugin=>:rails_plugin do |task|
58
+ # pub = Rake::SshDirPublisher.new("#{RubyForgeConfig[:user_name]}@rubyforge.org",
59
+ # "/var/www/gforge-projects/#{RubyForgeConfig[:unix_name]}",
60
+ # "pkg/rails_plugin")
61
+ # pub.upload()
62
+ # end
63
+
64
+
65
+ require 'rake'
66
+ require 'rake/tasklib'
67
+
68
+ module Rake
69
+ # RailsPluginPackageTask defines the "rails_plugin" task
70
+ # that recurses through your _package_files_, generates compliant index.html for
71
+ # each folder (that contains a file), and creates a directory structure that you
72
+ # can publish as a set for your plugin.
73
+ #
74
+ # Noteworthy attributes:
75
+ #
76
+ # [package_dir] Directory to store the package. Default 'pkg/rails_plugin'
77
+ #
78
+ # [package_dir] Files to include in the plugin.
79
+ #
80
+ # [extra_links] Links to put on every generated index page. Can be a hash, e.g.
81
+ # {"Home"=>"http://roxml.rubyforge.org"}, an array of strings or
82
+ # a single string.
83
+ #
84
+ # [plugin_files] Files to be placed in the root folder of the plug-in, e.g.
85
+ # init.rb. All files that are in the root of _package_dir_
86
+ # will also be placed in the root of the plug-in.
87
+ #
88
+ class RailsPluginPackageTask < TaskLib
89
+ # Name of plug-in or application
90
+ attr_accessor :name
91
+ # Version of plugin - distribution folder will be name_version
92
+ attr_accessor :version
93
+ # Directory used to store the package files (default is 'pkg/rails_plugin').
94
+ attr_accessor :package_dir
95
+ # Files to be stored in the package
96
+ attr_accessor :package_files
97
+ # Files to go into the root of the plug-in folder (e.g. init.rb)
98
+ attr_accessor :plugin_files
99
+ # Homepage for more information
100
+ attr_accessor :extra_links
101
+ # Verbose [true | false]
102
+ attr_accessor :verbose
103
+
104
+ # Create the "rails_plugin" task
105
+ def initialize(name=nil, version=nil)
106
+ init(name, version)
107
+ yield self if block_given?
108
+ define unless name.nil?
109
+ end
110
+
111
+ # Initialize with defaults
112
+ def init(name, version)
113
+ @name = name
114
+ @version = version
115
+ @extra_links = nil
116
+ @package_files = Rake::FileList.new
117
+ @plugin_files = Rake::FileList.new
118
+ @package_dir = 'pkg/rails_plugin'
119
+ @folders = {}
120
+ @verbose = false
121
+ end
122
+
123
+ # Define the rails_plugin task
124
+ def define
125
+ desc "Create Rails plug-in package"
126
+ task :rails_plugin do
127
+ @dest = "#@package_dir/#{@name}_#{@version}"
128
+ makedirs(@dest,:verbose=>false)
129
+ @plugin_files.each do |fn|
130
+ cp(fn, @dest,:verbose=>false)
131
+ add_file(File.basename(fn))
132
+ end
133
+
134
+ @package_files.each do |fn|
135
+ puts ". #{fn}" if verbose
136
+ f = File.join(@dest, fn)
137
+ fdir = File.dirname(f)
138
+ unless File.exist?(fdir)
139
+ mkdir_p(fdir,:verbose=>false)
140
+ add_folder("#{fdir}/")
141
+ end
142
+ if File.directory?(fn)
143
+ mkdir_p(f,:verbose=>false)
144
+ add_folder("#{fn}/")
145
+ else
146
+ cp(fn, f, :verbose=>false)
147
+ add_file(fn)
148
+ end
149
+ end
150
+
151
+ generate_index_files()
152
+ end
153
+ end
154
+
155
+ # Generate the index.html files
156
+ def generate_index_files
157
+ @folders.each do |folder, files|
158
+ puts " + Creating #{@dest}/#{folder}/index.html" if @verbose
159
+ File.open("#{@dest}/#{folder}/index.html", "w") do |index|
160
+ title = "Rails Plug-in for #@name #@version"
161
+ index.write("<html><head><title>#{title}</title></head>\n")
162
+ index.write("<body>\n")
163
+ index.write("<h2>#{title}</h2>\n")
164
+ extra_links = create_extra_links()
165
+ index.write("<p>#{extra_links}</p>\n") if extra_links
166
+ files.each { |fn|
167
+ puts(" - Adding #{fn}") if @verbose
168
+ index.write("&nbsp;&nbsp;<a href=\"#{fn}\">#{fn}</a><br/>\n")
169
+ }
170
+ index.write("<hr size=\"1\"/><p style=\"font-size: x-small\">Generated with RailsPluginPackageTask<p>")
171
+ index.write("</body>\n")
172
+ index.write("</html>\n")
173
+ end
174
+ end
175
+ end
176
+
177
+ private
178
+ # Add a file to the folders hash
179
+ def add_file(filename)
180
+ dir = File.dirname(filename).gsub("#{@dest}",".")
181
+ fn = File.basename(filename)
182
+ folder = @folders[dir] || @folders[dir]=[]
183
+ folder << fn
184
+ end
185
+
186
+ # Add a folder to the folders hash
187
+ def add_folder(folder_name)
188
+ dir = File.dirname(folder_name).gsub("#{@dest}",".").gsub("./","")
189
+ fn = File.basename(folder_name) + "/"
190
+ folder = @folders[dir] || @folders[dir]=[]
191
+ folder << fn
192
+ end
193
+
194
+ # Create the anchor tag for extra links
195
+ def create_extra_links
196
+ return nil unless @extra_links
197
+ x_links = ""
198
+ if (@extra_links.class==Hash)
199
+ @extra_links.each do |k,v|
200
+ x_links << "<a href=\"#{v}\">#{k}</a>&nbsp;"
201
+ end
202
+ elsif (@extra_links.class==Array)
203
+ @extra_links.each do |link|
204
+ x_links << "<a href=\"#{link}\">#{link}</a>&nbsp;"
205
+ end
206
+ else
207
+ x_links = "<a href=\"#{@extra_links.to_s}\">#{@extra_links.to_s}</a>"
208
+ end
209
+ return x_links
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,1349 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/topic'
3
+ require 'fixtures/reply'
4
+ require 'fixtures/company'
5
+ require 'fixtures/customer'
6
+ require 'fixtures/developer'
7
+ require 'fixtures/project'
8
+ require 'fixtures/default'
9
+ require 'fixtures/auto_id'
10
+ require 'fixtures/column_name'
11
+ require 'fixtures/subscriber'
12
+ require 'fixtures/keyboard'
13
+
14
+ class Category < ActiveRecord::Base; end
15
+ class Smarts < ActiveRecord::Base; end
16
+ class CreditCard < ActiveRecord::Base; end
17
+ class MasterCreditCard < ActiveRecord::Base; end
18
+ class Post < ActiveRecord::Base; end
19
+ class Computer < ActiveRecord::Base; end
20
+ class NonExistentTable < ActiveRecord::Base; end
21
+ class TestOracleDefault < ActiveRecord::Base; end
22
+
23
+ class LoosePerson < ActiveRecord::Base
24
+ attr_protected :credit_rating, :administrator
25
+ self.abstract_class = true
26
+ end
27
+
28
+ class LooseDescendant < LoosePerson
29
+ attr_protected :phone_number
30
+ end
31
+
32
+ class TightPerson < ActiveRecord::Base
33
+ attr_accessible :name, :address
34
+ end
35
+
36
+ class TightDescendant < TightPerson
37
+ attr_accessible :phone_number
38
+ end
39
+
40
+ class Booleantest < ActiveRecord::Base; end
41
+
42
+ class Task < ActiveRecord::Base
43
+ attr_protected :starting
44
+ end
45
+
46
+ class BasicsTest < Test::Unit::TestCase
47
+ fixtures :topics, :companies, :developers, :projects, :computers, :accounts
48
+
49
+ def test_table_exists
50
+ assert !NonExistentTable.table_exists?
51
+ assert Topic.table_exists?
52
+ end
53
+
54
+ def test_set_attributes
55
+ topic = Topic.find(1)
56
+ topic.attributes = { "title" => "Budget", "author_name" => "Jason" }
57
+ topic.save
58
+ assert_equal("Budget", topic.title)
59
+ assert_equal("Jason", topic.author_name)
60
+ assert_equal(topics(:first).author_email_address, Topic.find(1).author_email_address)
61
+ end
62
+
63
+ def test_integers_as_nil
64
+ test = AutoId.create('value' => '')
65
+ assert_nil AutoId.find(test.id).value
66
+ end
67
+
68
+ def test_set_attributes_with_block
69
+ topic = Topic.new do |t|
70
+ t.title = "Budget"
71
+ t.author_name = "Jason"
72
+ end
73
+
74
+ assert_equal("Budget", topic.title)
75
+ assert_equal("Jason", topic.author_name)
76
+ end
77
+
78
+ def test_respond_to?
79
+ topic = Topic.find(1)
80
+ assert topic.respond_to?("title")
81
+ assert topic.respond_to?("title?")
82
+ assert topic.respond_to?("title=")
83
+ assert topic.respond_to?(:title)
84
+ assert topic.respond_to?(:title?)
85
+ assert topic.respond_to?(:title=)
86
+ assert topic.respond_to?("author_name")
87
+ assert topic.respond_to?("attribute_names")
88
+ assert !topic.respond_to?("nothingness")
89
+ assert !topic.respond_to?(:nothingness)
90
+ end
91
+
92
+ def test_array_content
93
+ topic = Topic.new
94
+ topic.content = %w( one two three )
95
+ topic.save
96
+
97
+ assert_equal(%w( one two three ), Topic.find(topic.id).content)
98
+ end
99
+
100
+ def test_hash_content
101
+ topic = Topic.new
102
+ topic.content = { "one" => 1, "two" => 2 }
103
+ topic.save
104
+
105
+ assert_equal 2, Topic.find(topic.id).content["two"]
106
+
107
+ topic.content["three"] = 3
108
+ topic.save
109
+
110
+ assert_equal 3, Topic.find(topic.id).content["three"]
111
+ end
112
+
113
+ def test_update_array_content
114
+ topic = Topic.new
115
+ topic.content = %w( one two three )
116
+
117
+ topic.content.push "four"
118
+ assert_equal(%w( one two three four ), topic.content)
119
+
120
+ topic.save
121
+
122
+ topic = Topic.find(topic.id)
123
+ topic.content << "five"
124
+ assert_equal(%w( one two three four five ), topic.content)
125
+ end
126
+
127
+ def test_case_sensitive_attributes_hash
128
+ # DB2 is not case-sensitive
129
+ return true if current_adapter?(:DB2Adapter)
130
+
131
+ assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.find(:first).attributes
132
+ end
133
+
134
+ def test_create
135
+ topic = Topic.new
136
+ topic.title = "New Topic"
137
+ topic.save
138
+ topic_reloaded = Topic.find(topic.id)
139
+ assert_equal("New Topic", topic_reloaded.title)
140
+ end
141
+
142
+ def test_save!
143
+ topic = Topic.new(:title => "New Topic")
144
+ assert topic.save!
145
+ end
146
+
147
+ def test_hashes_not_mangled
148
+ new_topic = { :title => "New Topic" }
149
+ new_topic_values = { :title => "AnotherTopic" }
150
+
151
+ topic = Topic.new(new_topic)
152
+ assert_equal new_topic[:title], topic.title
153
+
154
+ topic.attributes= new_topic_values
155
+ assert_equal new_topic_values[:title], topic.title
156
+ end
157
+
158
+ def test_create_many
159
+ topics = Topic.create([ { "title" => "first" }, { "title" => "second" }])
160
+ assert_equal 2, topics.size
161
+ assert_equal "first", topics.first.title
162
+ end
163
+
164
+ def test_create_columns_not_equal_attributes
165
+ topic = Topic.new
166
+ topic.title = 'Another New Topic'
167
+ topic.send :write_attribute, 'does_not_exist', 'test'
168
+ assert_nothing_raised { topic.save }
169
+ end
170
+
171
+ def test_create_through_factory
172
+ topic = Topic.create("title" => "New Topic")
173
+ topicReloaded = Topic.find(topic.id)
174
+ assert_equal(topic, topicReloaded)
175
+ end
176
+
177
+ def test_update
178
+ topic = Topic.new
179
+ topic.title = "Another New Topic"
180
+ topic.written_on = "2003-12-12 23:23:00"
181
+ topic.save
182
+ topicReloaded = Topic.find(topic.id)
183
+ assert_equal("Another New Topic", topicReloaded.title)
184
+
185
+ topicReloaded.title = "Updated topic"
186
+ topicReloaded.save
187
+
188
+ topicReloadedAgain = Topic.find(topic.id)
189
+
190
+ assert_equal("Updated topic", topicReloadedAgain.title)
191
+ end
192
+
193
+ def test_update_columns_not_equal_attributes
194
+ topic = Topic.new
195
+ topic.title = "Still another topic"
196
+ topic.save
197
+
198
+ topicReloaded = Topic.find(topic.id)
199
+ topicReloaded.title = "A New Topic"
200
+ topicReloaded.send :write_attribute, 'does_not_exist', 'test'
201
+ assert_nothing_raised { topicReloaded.save }
202
+ end
203
+
204
+ def test_write_attribute
205
+ topic = Topic.new
206
+ topic.send(:write_attribute, :title, "Still another topic")
207
+ assert_equal "Still another topic", topic.title
208
+
209
+ topic.send(:write_attribute, "title", "Still another topic: part 2")
210
+ assert_equal "Still another topic: part 2", topic.title
211
+ end
212
+
213
+ def test_read_attribute
214
+ topic = Topic.new
215
+ topic.title = "Don't change the topic"
216
+ assert_equal "Don't change the topic", topic.send(:read_attribute, "title")
217
+ assert_equal "Don't change the topic", topic["title"]
218
+
219
+ assert_equal "Don't change the topic", topic.send(:read_attribute, :title)
220
+ assert_equal "Don't change the topic", topic[:title]
221
+ end
222
+
223
+ def test_read_attribute_when_false
224
+ topic = topics(:first)
225
+ topic.approved = false
226
+ assert !topic.approved?, "approved should be false"
227
+ topic.approved = "false"
228
+ assert !topic.approved?, "approved should be false"
229
+ end
230
+
231
+ def test_read_attribute_when_true
232
+ topic = topics(:first)
233
+ topic.approved = true
234
+ assert topic.approved?, "approved should be true"
235
+ topic.approved = "true"
236
+ assert topic.approved?, "approved should be true"
237
+ end
238
+
239
+ def test_read_write_boolean_attribute
240
+ topic = Topic.new
241
+ # puts ""
242
+ # puts "New Topic"
243
+ # puts topic.inspect
244
+ topic.approved = "false"
245
+ # puts "Expecting false"
246
+ # puts topic.inspect
247
+ assert !topic.approved?, "approved should be false"
248
+ topic.approved = "false"
249
+ # puts "Expecting false"
250
+ # puts topic.inspect
251
+ assert !topic.approved?, "approved should be false"
252
+ topic.approved = "true"
253
+ # puts "Expecting true"
254
+ # puts topic.inspect
255
+ assert topic.approved?, "approved should be true"
256
+ topic.approved = "true"
257
+ # puts "Expecting true"
258
+ # puts topic.inspect
259
+ assert topic.approved?, "approved should be true"
260
+ # puts ""
261
+ end
262
+
263
+ def test_reader_generation
264
+ Topic.find(:first).title
265
+ Firm.find(:first).name
266
+ Client.find(:first).name
267
+ if ActiveRecord::Base.generate_read_methods
268
+ assert_readers(Topic, %w(type replies_count))
269
+ assert_readers(Firm, %w(type))
270
+ assert_readers(Client, %w(type ruby_type rating?))
271
+ else
272
+ [Topic, Firm, Client].each {|klass| assert_equal klass.read_methods, {}}
273
+ end
274
+ end
275
+
276
+ def test_reader_for_invalid_column_names
277
+ # column names which aren't legal ruby ids
278
+ topic = Topic.find(:first)
279
+ topic.send(:define_read_method, "mumub-jumbo".to_sym, "mumub-jumbo", nil)
280
+ assert !Topic.read_methods.include?("mumub-jumbo")
281
+ end
282
+
283
+ def test_non_attribute_access_and_assignment
284
+ topic = Topic.new
285
+ assert !topic.respond_to?("mumbo")
286
+ assert_raises(NoMethodError) { topic.mumbo }
287
+ assert_raises(NoMethodError) { topic.mumbo = 5 }
288
+ end
289
+
290
+ def test_preserving_date_objects
291
+ # SQL Server doesn't have a separate column type just for dates, so all are returned as time
292
+ return true if current_adapter?(:SQLServerAdapter)
293
+
294
+ if current_adapter?(:SybaseAdapter)
295
+ # Sybase ctlib does not (yet?) support the date type; use datetime instead.
296
+ assert_kind_of(
297
+ Time, Topic.find(1).last_read,
298
+ "The last_read attribute should be of the Time class"
299
+ )
300
+ elsif current_adapter?(:ODBCAdapter) && [:ingres, :microsoftsqlserver, :oracle].include?(ActiveRecord::Base.connection.dbmsName)
301
+ # Above databases don't have a pure date type. (They have a datetime-like type).
302
+ assert_kind_of(
303
+ Time, Topic.find(1).last_read,
304
+ "The last_read attribute should be of the Time class"
305
+ )
306
+ else
307
+ # ODBCAdapter fails against SQL Server because topics.last_read is
308
+ # defined as a datetime column, which is returned as a Ruby Time object.
309
+ assert_kind_of(
310
+ Date, Topic.find(1).last_read,
311
+ "The last_read attribute should be of the Date class"
312
+ )
313
+ end
314
+ end
315
+
316
+ def test_preserving_time_objects
317
+ assert_kind_of(
318
+ Time, Topic.find(1).bonus_time,
319
+ "The bonus_time attribute should be of the Time class"
320
+ )
321
+
322
+ assert_kind_of(
323
+ Time, Topic.find(1).written_on,
324
+ "The written_on attribute should be of the Time class"
325
+ )
326
+ end
327
+
328
+ def test_destroy
329
+ topic = Topic.new
330
+ topic.title = "Yet Another New Topic"
331
+ topic.written_on = "2003-12-12 23:23:00"
332
+ topic.save
333
+ topic.destroy
334
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
335
+ end
336
+
337
+ def test_destroy_returns_self
338
+ topic = Topic.new("title" => "Yet Another Title")
339
+ assert topic.save
340
+ assert_equal topic, topic.destroy, "destroy did not return destroyed object"
341
+ end
342
+
343
+ def test_record_not_found_exception
344
+ assert_raises(ActiveRecord::RecordNotFound) { topicReloaded = Topic.find(99999) }
345
+ end
346
+
347
+ def test_initialize_with_attributes
348
+ topic = Topic.new({
349
+ "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23"
350
+ })
351
+
352
+ assert_equal("initialized from attributes", topic.title)
353
+ end
354
+
355
+ def test_initialize_with_invalid_attribute
356
+ begin
357
+ topic = Topic.new({ "title" => "test",
358
+ "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"})
359
+ rescue ActiveRecord::MultiparameterAssignmentErrors => ex
360
+ assert_equal(1, ex.errors.size)
361
+ assert_equal("last_read", ex.errors[0].attribute)
362
+ end
363
+ end
364
+
365
+ def test_load
366
+ topics = Topic.find(:all, :order => 'id')
367
+ assert_equal(2, topics.size)
368
+ assert_equal(topics(:first).title, topics.first.title)
369
+ end
370
+
371
+ def test_load_with_condition
372
+ topics = Topic.find(:all, :conditions => "author_name = 'Mary'")
373
+
374
+ assert_equal(1, topics.size)
375
+ assert_equal(topics(:second).title, topics.first.title)
376
+ end
377
+
378
+ def test_table_name_guesses
379
+ assert_equal "topics", Topic.table_name
380
+
381
+ assert_equal "categories", Category.table_name
382
+ assert_equal "smarts", Smarts.table_name
383
+ assert_equal "credit_cards", CreditCard.table_name
384
+ assert_equal "master_credit_cards", MasterCreditCard.table_name
385
+
386
+ ActiveRecord::Base.pluralize_table_names = false
387
+ [Category, Smarts, CreditCard, MasterCreditCard].each{|c| c.reset_table_name}
388
+ assert_equal "category", Category.table_name
389
+ assert_equal "smarts", Smarts.table_name
390
+ assert_equal "credit_card", CreditCard.table_name
391
+ assert_equal "master_credit_card", MasterCreditCard.table_name
392
+ ActiveRecord::Base.pluralize_table_names = true
393
+ [Category, Smarts, CreditCard, MasterCreditCard].each{|c| c.reset_table_name}
394
+
395
+ ActiveRecord::Base.table_name_prefix = "test_"
396
+ Category.reset_table_name
397
+ assert_equal "test_categories", Category.table_name
398
+ ActiveRecord::Base.table_name_suffix = "_test"
399
+ Category.reset_table_name
400
+ assert_equal "test_categories_test", Category.table_name
401
+ ActiveRecord::Base.table_name_prefix = ""
402
+ Category.reset_table_name
403
+ assert_equal "categories_test", Category.table_name
404
+ ActiveRecord::Base.table_name_suffix = ""
405
+ Category.reset_table_name
406
+ assert_equal "categories", Category.table_name
407
+
408
+ ActiveRecord::Base.pluralize_table_names = false
409
+ ActiveRecord::Base.table_name_prefix = "test_"
410
+ Category.reset_table_name
411
+ assert_equal "test_category", Category.table_name
412
+ ActiveRecord::Base.table_name_suffix = "_test"
413
+ Category.reset_table_name
414
+ assert_equal "test_category_test", Category.table_name
415
+ ActiveRecord::Base.table_name_prefix = ""
416
+ Category.reset_table_name
417
+ assert_equal "category_test", Category.table_name
418
+ ActiveRecord::Base.table_name_suffix = ""
419
+ Category.reset_table_name
420
+ assert_equal "category", Category.table_name
421
+ ActiveRecord::Base.pluralize_table_names = true
422
+ [Category, Smarts, CreditCard, MasterCreditCard].each{|c| c.reset_table_name}
423
+ end
424
+
425
+ def test_destroy_all
426
+ assert_equal 2, Topic.count
427
+
428
+ Topic.destroy_all "author_name = 'Mary'"
429
+ assert_equal 1, Topic.count
430
+ end
431
+
432
+ def test_destroy_many
433
+ assert_equal 3, Client.count
434
+ Client.destroy([2, 3])
435
+ assert_equal 1, Client.count
436
+ end
437
+
438
+ def test_delete_many
439
+ Topic.delete([1, 2])
440
+ assert_equal 0, Topic.count
441
+ end
442
+
443
+ def test_boolean_attributes
444
+ assert ! Topic.find(1).approved?
445
+ assert Topic.find(2).approved?
446
+ end
447
+
448
+ def test_increment_counter
449
+ Topic.increment_counter("replies_count", 1)
450
+ assert_equal 1, Topic.find(1).replies_count
451
+
452
+ Topic.increment_counter("replies_count", 1)
453
+ assert_equal 2, Topic.find(1).replies_count
454
+ end
455
+
456
+ def test_decrement_counter
457
+ Topic.decrement_counter("replies_count", 2)
458
+ assert_equal 1, Topic.find(2).replies_count
459
+
460
+ Topic.decrement_counter("replies_count", 2)
461
+ assert_equal 0, Topic.find(1).replies_count
462
+ end
463
+
464
+ def test_update_all
465
+ # The ADO library doesn't support the number of affected rows
466
+ return true if current_adapter?(:SQLServerAdapter)
467
+
468
+ assert_equal 2, Topic.update_all("content = 'bulk updated!'")
469
+ assert_equal "bulk updated!", Topic.find(1).content
470
+ assert_equal "bulk updated!", Topic.find(2).content
471
+ assert_equal 2, Topic.update_all(['content = ?', 'bulk updated again!'])
472
+ assert_equal "bulk updated again!", Topic.find(1).content
473
+ assert_equal "bulk updated again!", Topic.find(2).content
474
+ end
475
+
476
+ def test_update_many
477
+ topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
478
+ updated = Topic.update(topic_data.keys, topic_data.values)
479
+
480
+ assert_equal 2, updated.size
481
+ assert_equal "1 updated", Topic.find(1).content
482
+ assert_equal "2 updated", Topic.find(2).content
483
+ end
484
+
485
+ def test_delete_all
486
+ # The ADO library doesn't support the number of affected rows
487
+ return true if current_adapter?(:SQLServerAdapter)
488
+
489
+ assert_equal 2, Topic.delete_all
490
+ end
491
+
492
+ def test_update_by_condition
493
+ Topic.update_all "content = 'bulk updated!'", ["approved = ?", true]
494
+ assert_equal "Have a nice day", Topic.find(1).content
495
+ assert_equal "bulk updated!", Topic.find(2).content
496
+ end
497
+
498
+ def test_attribute_present
499
+ t = Topic.new
500
+ t.title = "hello there!"
501
+ t.written_on = Time.now
502
+ assert t.attribute_present?("title")
503
+ assert t.attribute_present?("written_on")
504
+ assert !t.attribute_present?("content")
505
+ end
506
+
507
+ def test_attribute_keys_on_new_instance
508
+ t = Topic.new
509
+ assert_equal nil, t.title, "The topics table has a title column, so it should be nil"
510
+ assert_raise(NoMethodError) { t.title2 }
511
+ end
512
+
513
+ def test_class_name
514
+ assert_equal "Firm", ActiveRecord::Base.class_name("firms")
515
+ assert_equal "Category", ActiveRecord::Base.class_name("categories")
516
+ assert_equal "AccountHolder", ActiveRecord::Base.class_name("account_holder")
517
+
518
+ ActiveRecord::Base.pluralize_table_names = false
519
+ assert_equal "Firms", ActiveRecord::Base.class_name( "firms" )
520
+ ActiveRecord::Base.pluralize_table_names = true
521
+
522
+ ActiveRecord::Base.table_name_prefix = "test_"
523
+ assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms" )
524
+ ActiveRecord::Base.table_name_suffix = "_tests"
525
+ assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms_tests" )
526
+ ActiveRecord::Base.table_name_prefix = ""
527
+ assert_equal "Firm", ActiveRecord::Base.class_name( "firms_tests" )
528
+ ActiveRecord::Base.table_name_suffix = ""
529
+ assert_equal "Firm", ActiveRecord::Base.class_name( "firms" )
530
+ end
531
+
532
+ def test_null_fields
533
+ assert_nil Topic.find(1).parent_id
534
+ assert_nil Topic.create("title" => "Hey you").parent_id
535
+ end
536
+
537
+ def test_default_values
538
+ topic = Topic.new
539
+ assert topic.approved?
540
+ assert_nil topic.written_on
541
+ assert_nil topic.bonus_time
542
+ assert_nil topic.last_read
543
+
544
+ topic.save
545
+
546
+ topic = Topic.find(topic.id)
547
+ assert topic.approved?
548
+ assert_nil topic.last_read
549
+
550
+ # Oracle has some funky default handling, so it requires a bit of
551
+ # extra testing. See ticket #2788.
552
+ if current_adapter?(:OracleAdapter)
553
+ test = TestOracleDefault.new
554
+ assert_equal "X", test.test_char
555
+ assert_equal "hello", test.test_string
556
+ assert_equal 3, test.test_int
557
+ end
558
+ end
559
+
560
+ def test_utc_as_time_zone
561
+ # Oracle and SQLServer do not have a TIME datatype.
562
+ return true if current_adapter?(:SQLServerAdapter) || current_adapter?(:OracleAdapter)
563
+
564
+ Topic.default_timezone = :utc
565
+ attributes = { "bonus_time" => "5:42:00AM" }
566
+ topic = Topic.find(1)
567
+ topic.attributes = attributes
568
+ assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time
569
+ Topic.default_timezone = :local
570
+ end
571
+
572
+ def test_default_values_on_empty_strings
573
+ topic = Topic.new
574
+ #Sybase does not allow nulls in boolean columns
575
+ if current_adapter?(:ODBCAdapter) && ActiveRecord::Base.connection.dbmsName == :sybase
576
+ topic.approved = false
577
+ else
578
+ topic.approved = nil
579
+ end
580
+ topic.last_read = nil
581
+
582
+ topic.save
583
+
584
+ topic = Topic.find(topic.id)
585
+ assert_nil topic.last_read
586
+
587
+ # Sybase adapter does not allow nulls in boolean columns
588
+ if current_adapter?(:SybaseAdapter) ||
589
+ current_adapter?(:ODBCAdapter) && ActiveRecord::Base.connection.dbmsName == :sybase
590
+ assert topic.approved == false
591
+ else
592
+ assert_nil topic.approved
593
+ end
594
+ end
595
+
596
+ def test_equality
597
+ assert_equal Topic.find(1), Topic.find(2).topic
598
+ end
599
+
600
+ def test_equality_of_new_records
601
+ assert_not_equal Topic.new, Topic.new
602
+ end
603
+
604
+ def test_hashing
605
+ assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
606
+ end
607
+
608
+ def test_destroy_new_record
609
+ client = Client.new
610
+ client.destroy
611
+ assert client.frozen?
612
+ end
613
+
614
+ def test_destroy_record_with_associations
615
+ client = Client.find(3)
616
+ client.destroy
617
+ assert client.frozen?
618
+ assert_kind_of Firm, client.firm
619
+ assert_raises(TypeError) { client.name = "something else" }
620
+ end
621
+
622
+ def test_update_attribute
623
+ assert !Topic.find(1).approved?
624
+ Topic.find(1).update_attribute("approved", true)
625
+ assert Topic.find(1).approved?
626
+
627
+ Topic.find(1).update_attribute(:approved, false)
628
+ assert !Topic.find(1).approved?
629
+ end
630
+
631
+ def test_mass_assignment_protection
632
+ firm = Firm.new
633
+ firm.attributes = { "name" => "Next Angle", "rating" => 5 }
634
+ assert_equal 1, firm.rating
635
+ end
636
+
637
+ def test_customized_primary_key_remains_protected
638
+ subscriber = Subscriber.new(:nick => 'webster123', :name => 'nice try')
639
+ assert_nil subscriber.id
640
+
641
+ keyboard = Keyboard.new(:key_number => 9, :name => 'nice try')
642
+ assert_nil keyboard.id
643
+ end
644
+
645
+ def test_customized_primary_key_remains_protected_when_refered_to_as_id
646
+ subscriber = Subscriber.new(:id => 'webster123', :name => 'nice try')
647
+ assert_nil subscriber.id
648
+
649
+ keyboard = Keyboard.new(:id => 9, :name => 'nice try')
650
+ assert_nil keyboard.id
651
+ end
652
+
653
+ def test_mass_assignment_protection_on_defaults
654
+ firm = Firm.new
655
+ firm.attributes = { "id" => 5, "type" => "Client" }
656
+ assert_nil firm.id
657
+ assert_equal "Firm", firm[:type]
658
+ end
659
+
660
+ def test_mass_assignment_accessible
661
+ reply = Reply.new("title" => "hello", "content" => "world", "approved" => true)
662
+ reply.save
663
+
664
+ assert reply.approved?
665
+
666
+ reply.approved = false
667
+ reply.save
668
+
669
+ assert !reply.approved?
670
+ end
671
+
672
+ def test_mass_assignment_protection_inheritance
673
+ assert_nil LoosePerson.accessible_attributes
674
+ assert_equal [ :credit_rating, :administrator ], LoosePerson.protected_attributes
675
+
676
+ assert_nil LooseDescendant.accessible_attributes
677
+ assert_equal [ :credit_rating, :administrator, :phone_number ], LooseDescendant.protected_attributes
678
+
679
+ assert_nil TightPerson.protected_attributes
680
+ assert_equal [ :name, :address ], TightPerson.accessible_attributes
681
+
682
+ assert_nil TightDescendant.protected_attributes
683
+ assert_equal [ :name, :address, :phone_number ], TightDescendant.accessible_attributes
684
+ end
685
+
686
+ def test_multiparameter_attributes_on_date
687
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
688
+ topic = Topic.find(1)
689
+ topic.attributes = attributes
690
+ # note that extra #to_date call allows test to pass for Oracle, which
691
+ # treats dates/times the same
692
+ assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date
693
+ end
694
+
695
+ def test_multiparameter_attributes_on_date_with_empty_date
696
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
697
+ topic = Topic.find(1)
698
+ topic.attributes = attributes
699
+ # note that extra #to_date call allows test to pass for Oracle, which
700
+ # treats dates/times the same
701
+ assert_date_from_db Date.new(2004, 6, 1), topic.last_read.to_date
702
+ end
703
+
704
+ def test_multiparameter_attributes_on_date_with_all_empty
705
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" }
706
+ topic = Topic.find(1)
707
+ topic.attributes = attributes
708
+ assert_nil topic.last_read
709
+ end
710
+
711
+ def test_multiparameter_attributes_on_time
712
+ attributes = {
713
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
714
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
715
+ }
716
+ topic = Topic.find(1)
717
+ topic.attributes = attributes
718
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
719
+ end
720
+
721
+ def test_multiparameter_attributes_on_time_with_empty_seconds
722
+ attributes = {
723
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
724
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => ""
725
+ }
726
+ topic = Topic.find(1)
727
+ topic.attributes = attributes
728
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
729
+ end
730
+
731
+ def test_multiparameter_mass_assignment_protector
732
+ task = Task.new
733
+ time = Time.mktime(2000, 1, 1, 1)
734
+ task.starting = time
735
+ attributes = { "starting(1i)" => "2004", "starting(2i)" => "6", "starting(3i)" => "24" }
736
+ task.attributes = attributes
737
+ assert_equal time, task.starting
738
+ end
739
+
740
+ def test_multiparameter_assignment_of_aggregation
741
+ customer = Customer.new
742
+ address = Address.new("The Street", "The City", "The Country")
743
+ attributes = { "address(1)" => address.street, "address(2)" => address.city, "address(3)" => address.country }
744
+ customer.attributes = attributes
745
+ assert_equal address, customer.address
746
+ end
747
+
748
+ def test_attributes_on_dummy_time
749
+ # Oracle and SQL Server do not have a TIME datatype.
750
+ return true if current_adapter?(:SQLServerAdapter) ||
751
+ current_adapter?(:OracleAdapter)
752
+ if current_adapter?(:ODBCAdapter)
753
+ # Check for databases which don't have a true TIME datatype
754
+ return true if [:ingres, :oracle].include?(ActiveRecord::Base.connection.dbmsName)
755
+ end
756
+
757
+ attributes = {
758
+ "bonus_time" => "5:42:00AM"
759
+ }
760
+ topic = Topic.find(1)
761
+ topic.attributes = attributes
762
+ assert_equal Time.local(2000, 1, 1, 5, 42, 0), topic.bonus_time
763
+ end
764
+
765
+ def test_boolean
766
+ b_false = Booleantest.create({ "value" => false })
767
+ false_id = b_false.id
768
+ b_true = Booleantest.create({ "value" => true })
769
+ true_id = b_true.id
770
+
771
+ b_false = Booleantest.find(false_id)
772
+ assert !b_false.value?
773
+ b_true = Booleantest.find(true_id)
774
+ assert b_true.value?
775
+ end
776
+
777
+ def test_boolean_cast_from_string
778
+ b_false = Booleantest.create({ "value" => "0" })
779
+ false_id = b_false.id
780
+ b_true = Booleantest.create({ "value" => "1" })
781
+ true_id = b_true.id
782
+
783
+ b_false = Booleantest.find(false_id)
784
+ assert !b_false.value?
785
+ b_true = Booleantest.find(true_id)
786
+ assert b_true.value?
787
+ end
788
+
789
+ def test_clone
790
+ topic = Topic.find(1)
791
+ cloned_topic = nil
792
+ assert_nothing_raised { cloned_topic = topic.clone }
793
+ assert_equal topic.title, cloned_topic.title
794
+ assert cloned_topic.new_record?
795
+
796
+ # test if the attributes have been cloned
797
+ topic.title = "a"
798
+ cloned_topic.title = "b"
799
+ assert_equal "a", topic.title
800
+ assert_equal "b", cloned_topic.title
801
+
802
+ # test if the attribute values have been cloned
803
+ topic.title = {"a" => "b"}
804
+ cloned_topic = topic.clone
805
+ cloned_topic.title["a"] = "c"
806
+ assert_equal "b", topic.title["a"]
807
+
808
+ cloned_topic.save
809
+ assert !cloned_topic.new_record?
810
+ assert cloned_topic.id != topic.id
811
+ end
812
+
813
+ def test_clone_with_aggregate_of_same_name_as_attribute
814
+ dev = DeveloperWithAggregate.find(1)
815
+ assert_kind_of DeveloperSalary, dev.salary
816
+
817
+ clone = nil
818
+ assert_nothing_raised { clone = dev.clone }
819
+ assert_kind_of DeveloperSalary, clone.salary
820
+ assert_equal dev.salary.amount, clone.salary.amount
821
+ assert clone.new_record?
822
+
823
+ # test if the attributes have been cloned
824
+ original_amount = clone.salary.amount
825
+ dev.salary.amount = 1
826
+ assert_equal original_amount, clone.salary.amount
827
+
828
+ assert clone.save
829
+ assert !clone.new_record?
830
+ assert clone.id != dev.id
831
+ end
832
+
833
+ def test_clone_preserves_subtype
834
+ clone = nil
835
+ assert_nothing_raised { clone = Company.find(3).clone }
836
+ assert_kind_of Client, clone
837
+ end
838
+
839
+ def test_bignum
840
+ company = Company.find(1)
841
+ company.rating = 2147483647
842
+ company.save
843
+ company = Company.find(1)
844
+ assert_equal 2147483647, company.rating
845
+ end
846
+
847
+ # TODO: extend defaults tests to other databases!
848
+ if current_adapter?(:PostgreSQLAdapter)
849
+ def test_default
850
+ default = Default.new
851
+
852
+ # fixed dates / times
853
+ assert_equal Date.new(2004, 1, 1), default.fixed_date
854
+ assert_equal Time.local(2004, 1,1,0,0,0,0), default.fixed_time
855
+
856
+ # char types
857
+ assert_equal 'Y', default.char1
858
+ assert_equal 'a varchar field', default.char2
859
+ assert_equal 'a text field', default.char3
860
+ end
861
+
862
+ class Geometric < ActiveRecord::Base; end
863
+ def test_geometric_content
864
+
865
+ # accepted format notes:
866
+ # ()'s aren't required
867
+ # values can be a mix of float or integer
868
+
869
+ g = Geometric.new(
870
+ :a_point => '(5.0, 6.1)',
871
+ #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
872
+ :a_line_segment => '(2.0, 3), (5.5, 7.0)',
873
+ :a_box => '2.0, 3, 5.5, 7.0',
874
+ :a_path => '[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]', # [ ] is an open path
875
+ :a_polygon => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))',
876
+ :a_circle => '<(5.3, 10.4), 2>'
877
+ )
878
+
879
+ assert g.save
880
+
881
+ # Reload and check that we have all the geometric attributes.
882
+ h = Geometric.find(g.id)
883
+
884
+ assert_equal '(5,6.1)', h.a_point
885
+ assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
886
+ assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
887
+ assert_equal '[(2,3),(5.5,7),(8.5,11)]', h.a_path
888
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
889
+ assert_equal '<(5.3,10.4),2>', h.a_circle
890
+
891
+ # use a geometric function to test for an open path
892
+ objs = Geometric.find_by_sql ["select isopen(a_path) from geometrics where id = ?", g.id]
893
+ assert_equal objs[0].isopen, 't'
894
+
895
+ # test alternate formats when defining the geometric types
896
+
897
+ g = Geometric.new(
898
+ :a_point => '5.0, 6.1',
899
+ #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
900
+ :a_line_segment => '((2.0, 3), (5.5, 7.0))',
901
+ :a_box => '(2.0, 3), (5.5, 7.0)',
902
+ :a_path => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))', # ( ) is a closed path
903
+ :a_polygon => '2.0, 3, 5.5, 7.0, 8.5, 11.0',
904
+ :a_circle => '((5.3, 10.4), 2)'
905
+ )
906
+
907
+ assert g.save
908
+
909
+ # Reload and check that we have all the geometric attributes.
910
+ h = Geometric.find(g.id)
911
+
912
+ assert_equal '(5,6.1)', h.a_point
913
+ assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
914
+ assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
915
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path
916
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
917
+ assert_equal '<(5.3,10.4),2>', h.a_circle
918
+
919
+ # use a geometric function to test for an closed path
920
+ objs = Geometric.find_by_sql ["select isclosed(a_path) from geometrics where id = ?", g.id]
921
+ assert_equal objs[0].isclosed, 't'
922
+ end
923
+ end
924
+
925
+ def test_auto_id
926
+ auto = AutoId.new
927
+ auto.save
928
+ assert (auto.id > 0)
929
+ end
930
+
931
+ def quote_column_name(name)
932
+ "<#{name}>"
933
+ end
934
+
935
+ def test_quote_keys
936
+ ar = AutoId.new
937
+ source = {"foo" => "bar", "baz" => "quux"}
938
+ actual = ar.send(:quote_columns, self, source)
939
+ inverted = actual.invert
940
+ assert_equal("<foo>", inverted["bar"])
941
+ assert_equal("<baz>", inverted["quux"])
942
+ end
943
+
944
+ def test_sql_injection_via_find
945
+ assert_raises(ActiveRecord::RecordNotFound) do
946
+ Topic.find("123456 OR id > 0")
947
+ end
948
+
949
+ assert_raises(ActiveRecord::RecordNotFound) do
950
+ Topic.find(";;; this should raise an RecordNotFound error")
951
+ end
952
+ end
953
+
954
+ def test_column_name_properly_quoted
955
+ col_record = ColumnName.new
956
+ col_record.references = 40
957
+ assert col_record.save
958
+ col_record.references = 41
959
+ assert col_record.save
960
+ assert_not_nil c2 = ColumnName.find(col_record.id)
961
+ assert_equal(41, c2.references)
962
+ end
963
+
964
+ MyObject = Struct.new :attribute1, :attribute2
965
+
966
+ def test_serialized_attribute
967
+ myobj = MyObject.new('value1', 'value2')
968
+ topic = Topic.create("content" => myobj)
969
+ Topic.serialize("content", MyObject)
970
+ assert_equal(myobj, topic.content)
971
+ end
972
+
973
+ def test_serialized_attribute_with_class_constraint
974
+ myobj = MyObject.new('value1', 'value2')
975
+ topic = Topic.create("content" => myobj)
976
+ Topic.serialize(:content, Hash)
977
+
978
+ assert_raise(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).content }
979
+
980
+ settings = { "color" => "blue" }
981
+ Topic.find(topic.id).update_attribute("content", settings)
982
+ assert_equal(settings, Topic.find(topic.id).content)
983
+ Topic.serialize(:content)
984
+ end
985
+
986
+ def test_quote
987
+ if current_adapter?(:ODBCAdapter) && [:informix, :sybase].include?(ActiveRecord::Base.connection.dbmsName)
988
+ #Informix and Sybase only allow printable characters in VARCHAR columns.
989
+ author_name = "\\ \041 ' \n \\n \""
990
+ else
991
+ author_name = "\\ \001 ' \n \\n \""
992
+ end
993
+ topic = Topic.create('author_name' => author_name)
994
+ assert_equal author_name, Topic.find(topic.id).author_name
995
+ end
996
+
997
+ def test_class_level_destroy
998
+ should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
999
+ Topic.find(1).replies << should_be_destroyed_reply
1000
+
1001
+ Topic.destroy(1)
1002
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
1003
+ assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) }
1004
+ end
1005
+
1006
+ def test_class_level_delete
1007
+ should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
1008
+ Topic.find(1).replies << should_be_destroyed_reply
1009
+
1010
+ Topic.delete(1)
1011
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
1012
+ assert_nothing_raised { Reply.find(should_be_destroyed_reply.id) }
1013
+ end
1014
+
1015
+ def test_increment_attribute
1016
+ assert_equal 0, topics(:first).replies_count
1017
+ topics(:first).increment! :replies_count
1018
+ assert_equal 1, topics(:first, :reload).replies_count
1019
+
1020
+ topics(:first).increment(:replies_count).increment!(:replies_count)
1021
+ assert_equal 3, topics(:first, :reload).replies_count
1022
+ end
1023
+
1024
+ def test_increment_nil_attribute
1025
+ assert_nil topics(:first).parent_id
1026
+ topics(:first).increment! :parent_id
1027
+ assert_equal 1, topics(:first).parent_id
1028
+ end
1029
+
1030
+ def test_decrement_attribute
1031
+ topics(:first).increment(:replies_count).increment!(:replies_count)
1032
+ assert_equal 2, topics(:first).replies_count
1033
+
1034
+ topics(:first).decrement!(:replies_count)
1035
+ assert_equal 1, topics(:first, :reload).replies_count
1036
+
1037
+ topics(:first).decrement(:replies_count).decrement!(:replies_count)
1038
+ assert_equal -1, topics(:first, :reload).replies_count
1039
+ end
1040
+
1041
+ def test_toggle_attribute
1042
+ assert !topics(:first).approved?
1043
+ topics(:first).toggle!(:approved)
1044
+ assert topics(:first).approved?
1045
+ topic = topics(:first)
1046
+ topic.toggle(:approved)
1047
+ assert !topic.approved?
1048
+ topic.reload
1049
+ assert topic.approved?
1050
+ end
1051
+
1052
+ def test_reload
1053
+ t1 = Topic.find(1)
1054
+ t2 = Topic.find(1)
1055
+ t1.title = "something else"
1056
+ t1.save
1057
+ t2.reload
1058
+ assert_equal t1.title, t2.title
1059
+ end
1060
+
1061
+ def test_define_attr_method_with_value
1062
+ k = Class.new( ActiveRecord::Base )
1063
+ k.send(:define_attr_method, :table_name, "foo")
1064
+ assert_equal "foo", k.table_name
1065
+ end
1066
+
1067
+ def test_define_attr_method_with_block
1068
+ k = Class.new( ActiveRecord::Base )
1069
+ k.send(:define_attr_method, :primary_key) { "sys_" + original_primary_key }
1070
+ assert_equal "sys_id", k.primary_key
1071
+ end
1072
+
1073
+ def test_set_table_name_with_value
1074
+ k = Class.new( ActiveRecord::Base )
1075
+ k.table_name = "foo"
1076
+ assert_equal "foo", k.table_name
1077
+ k.set_table_name "bar"
1078
+ assert_equal "bar", k.table_name
1079
+ end
1080
+
1081
+ def test_set_table_name_with_block
1082
+ k = Class.new( ActiveRecord::Base )
1083
+ k.set_table_name { "ks" }
1084
+ assert_equal "ks", k.table_name
1085
+ end
1086
+
1087
+ def test_set_primary_key_with_value
1088
+ k = Class.new( ActiveRecord::Base )
1089
+ k.primary_key = "foo"
1090
+ assert_equal "foo", k.primary_key
1091
+ k.set_primary_key "bar"
1092
+ assert_equal "bar", k.primary_key
1093
+ end
1094
+
1095
+ def test_set_primary_key_with_block
1096
+ k = Class.new( ActiveRecord::Base )
1097
+ k.set_primary_key { "sys_" + original_primary_key }
1098
+ assert_equal "sys_id", k.primary_key
1099
+ end
1100
+
1101
+ def test_set_inheritance_column_with_value
1102
+ k = Class.new( ActiveRecord::Base )
1103
+ k.inheritance_column = "foo"
1104
+ assert_equal "foo", k.inheritance_column
1105
+ k.set_inheritance_column "bar"
1106
+ assert_equal "bar", k.inheritance_column
1107
+ end
1108
+
1109
+ def test_set_inheritance_column_with_block
1110
+ k = Class.new( ActiveRecord::Base )
1111
+ k.set_inheritance_column { original_inheritance_column + "_id" }
1112
+ assert_equal "type_id", k.inheritance_column
1113
+ end
1114
+
1115
+ def test_count_with_join
1116
+ res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
1117
+ res2 = nil
1118
+ assert_nothing_raised do
1119
+ res2 = Post.count("posts.#{QUOTED_TYPE} = 'Post'",
1120
+ "LEFT JOIN comments ON posts.id=comments.post_id")
1121
+ end
1122
+ assert_equal res, res2
1123
+
1124
+ res3 = nil
1125
+ assert_nothing_raised do
1126
+ res3 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'",
1127
+ :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1128
+ end
1129
+ assert_equal res, res3
1130
+
1131
+ res4 = Post.count_by_sql "SELECT COUNT(p.id) FROM posts p, comments c WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=c.post_id"
1132
+ res5 = nil
1133
+ assert_nothing_raised do
1134
+ res5 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=c.post_id",
1135
+ :joins => "p, comments c",
1136
+ :select => "p.id")
1137
+ end
1138
+
1139
+ assert_equal res4, res5
1140
+
1141
+ res6 = Post.count_by_sql "SELECT COUNT(DISTINCT p.id) FROM posts p, comments c WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=c.post_id"
1142
+ res7 = nil
1143
+ assert_nothing_raised do
1144
+ res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=c.post_id",
1145
+ :joins => "p, comments c",
1146
+ :select => "p.id",
1147
+ :distinct => true)
1148
+ end
1149
+ assert_equal res6, res7
1150
+ end
1151
+
1152
+ def test_clear_association_cache_stored
1153
+ firm = Firm.find(1)
1154
+ assert_kind_of Firm, firm
1155
+
1156
+ firm.clear_association_cache
1157
+ assert_equal Firm.find(1).clients.collect{ |x| x.name }.sort, firm.clients.collect{ |x| x.name }.sort
1158
+ end
1159
+
1160
+ def test_clear_association_cache_new_record
1161
+ firm = Firm.new
1162
+ client_stored = Client.find(3)
1163
+ client_new = Client.new
1164
+ client_new.name = "The Joneses"
1165
+ clients = [ client_stored, client_new ]
1166
+
1167
+ firm.clients << clients
1168
+
1169
+ firm.clear_association_cache
1170
+
1171
+ assert_equal firm.clients.collect{ |x| x.name }.sort, clients.collect{ |x| x.name }.sort
1172
+ end
1173
+
1174
+ def test_interpolate_sql
1175
+ assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo@bar') }
1176
+ assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar) baz') }
1177
+ assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar} baz') }
1178
+ end
1179
+
1180
+ def test_scoped_find_conditions
1181
+ scoped_developers = Developer.with_scope(:find => { :conditions => 'salary > 90000' }) do
1182
+ Developer.find(:all, :conditions => 'id < 5')
1183
+ end
1184
+ assert !scoped_developers.include?(developers(:david)) # David's salary is less than 90,000
1185
+ assert_equal 3, scoped_developers.size
1186
+ end
1187
+
1188
+ def test_scoped_find_limit_offset
1189
+ scoped_developers = Developer.with_scope(:find => { :limit => 3, :offset => 2 }) do
1190
+ Developer.find(:all, :order => 'id')
1191
+ end
1192
+ assert !scoped_developers.include?(developers(:david))
1193
+ assert !scoped_developers.include?(developers(:jamis))
1194
+ assert_equal 3, scoped_developers.size
1195
+
1196
+ # Test without scoped find conditions to ensure we get the whole thing
1197
+ developers = Developer.find(:all, :order => 'id')
1198
+ assert_equal Developer.count, developers.size
1199
+ end
1200
+
1201
+ def test_base_class
1202
+ assert LoosePerson.abstract_class?
1203
+ assert !LooseDescendant.abstract_class?
1204
+ assert_equal LoosePerson, LoosePerson.base_class
1205
+ assert_equal LooseDescendant, LooseDescendant.base_class
1206
+ assert_equal TightPerson, TightPerson.base_class
1207
+ assert_equal TightPerson, TightDescendant.base_class
1208
+ end
1209
+
1210
+ def test_assert_queries
1211
+ query = lambda { ActiveRecord::Base.connection.execute 'select count(*) from developers' }
1212
+ assert_queries(2) { 2.times { query.call } }
1213
+ assert_queries 1, &query
1214
+ assert_no_queries { assert true }
1215
+ end
1216
+
1217
+ def test_to_xml
1218
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true)
1219
+ bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema
1220
+ written_on_in_current_timezone = topics(:first).written_on.xmlschema
1221
+ last_read_in_current_timezone = topics(:first).last_read.xmlschema
1222
+ assert_equal "<topic>", xml.first(7)
1223
+ assert xml.include?(%(<title>The First Topic</title>))
1224
+ assert xml.include?(%(<author-name>David</author-name>))
1225
+ assert xml.include?(%(<id type="integer">1</id>))
1226
+ assert xml.include?(%(<replies-count type="integer">0</replies-count>))
1227
+ assert xml.include?(%(<written-on type="datetime">#{written_on_in_current_timezone}</written-on>))
1228
+ assert xml.include?(%(<content>Have a nice day</content>))
1229
+ assert xml.include?(%(<author-email-address>david@loudthinking.com</author-email-address>))
1230
+ assert xml.include?(%(<parent-id></parent-id>))
1231
+ # Following databases don't have a true date type, only a composite datetime type
1232
+ if current_adapter?(:SybaseAdapter) or current_adapter?(:SQLServerAdapter) or
1233
+ current_adapter?(:ODBCAdapter) &&
1234
+ [:ingres,:oracle,:microsoftsqlserver].include?(ActiveRecord::Base.connection.dbmsName)
1235
+ assert xml.include?(%(<last-read type="datetime">#{last_read_in_current_timezone}</last-read>))
1236
+ else
1237
+ assert xml.include?(%(<last-read type="date">2004-04-15</last-read>))
1238
+ end
1239
+ # Following databases don't have a true boolean type
1240
+ unless current_adapter?(:OracleAdapter) || current_adapter?(:DB2Adapter)
1241
+ if current_adapter?(:ODBCAdapter) &&
1242
+ [:ingres,:virtuoso,:oracle,:mysql,:db2,:progress].include?(ActiveRecord::Base.connection.dbmsName)
1243
+ assert xml.include?(%(<approved type="integer">0</approved>)), "Approved should be an integer"
1244
+ else
1245
+ assert xml.include?(%(<approved type="boolean">false</approved>)), "Approved should be a boolean"
1246
+ end
1247
+ end
1248
+ # Oracle and DB2 don't have a true time-only field
1249
+ unless current_adapter?(:OracleAdapter) || current_adapter?(:DB2Adapter)
1250
+ assert xml.include?(%(<bonus-time type="datetime">#{bonus_time_in_current_timezone}</bonus-time>))
1251
+ end
1252
+ end
1253
+
1254
+ def test_to_xml_skipping_attributes
1255
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => :title)
1256
+ assert_equal "<topic>", xml.first(7)
1257
+ assert !xml.include?(%(<title>The First Topic</title>))
1258
+ assert xml.include?(%(<author-name>David</author-name>))
1259
+
1260
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [ :title, :author_name ])
1261
+ assert !xml.include?(%(<title>The First Topic</title>))
1262
+ assert !xml.include?(%(<author-name>David</author-name>))
1263
+ end
1264
+
1265
+ def test_to_xml_including_has_many_association
1266
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies)
1267
+ assert_equal "<topic>", xml.first(7)
1268
+ assert xml.include?(%(<replies><reply>))
1269
+ assert xml.include?(%(<title>The Second Topic's of the day</title>))
1270
+ end
1271
+
1272
+ def test_to_xml_including_belongs_to_association
1273
+ xml = companies(:first_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
1274
+ assert !xml.include?("<firm>")
1275
+
1276
+ xml = companies(:second_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
1277
+ assert xml.include?("<firm>")
1278
+ end
1279
+
1280
+ def test_to_xml_including_multiple_associations
1281
+ xml = companies(:first_firm).to_xml(:indent => 0, :skip_instruct => true, :include => [ :clients, :account ])
1282
+ assert_equal "<firm>", xml.first(6)
1283
+ assert xml.include?(%(<account>))
1284
+ assert xml.include?(%(<clients><client>))
1285
+ end
1286
+
1287
+ def test_to_xml_including_multiple_associations_with_options
1288
+ xml = companies(:first_firm).to_xml(
1289
+ :indent => 0, :skip_instruct => true,
1290
+ :include => { :clients => { :only => :name } }
1291
+ )
1292
+
1293
+ assert_equal "<firm>", xml.first(6)
1294
+ assert xml.include?(%(<client><name>Summit</name></client>))
1295
+ assert xml.include?(%(<clients><client>))
1296
+ end
1297
+
1298
+ def test_except_attributes
1299
+ assert_equal(
1300
+ %w( author_name type id approved replies_count bonus_time written_on content author_email_address parent_id last_read),
1301
+ topics(:first).attributes(:except => :title).keys
1302
+ )
1303
+
1304
+ assert_equal(
1305
+ %w( replies_count bonus_time written_on content author_email_address parent_id last_read),
1306
+ topics(:first).attributes(:except => [ :title, :id, :type, :approved, :author_name ]).keys
1307
+ )
1308
+ end
1309
+
1310
+ def test_include_attributes
1311
+ assert_equal(%w( title ), topics(:first).attributes(:only => :title).keys)
1312
+ assert_equal(%w( title author_name type id approved ), topics(:first).attributes(:only => [ :title, :id, :type, :approved, :author_name ]).keys)
1313
+ end
1314
+
1315
+ def test_type_name_with_module_should_handle_beginning
1316
+ assert_equal 'ActiveRecord::Person', ActiveRecord::Base.send(:type_name_with_module, 'Person')
1317
+ assert_equal '::Person', ActiveRecord::Base.send(:type_name_with_module, '::Person')
1318
+ end
1319
+
1320
+ # FIXME: this test ought to run, but it needs to run sandboxed so that it
1321
+ # doesn't b0rk the current test environment by undefing everything.
1322
+ #
1323
+ #def test_dev_mode_memory_leak
1324
+ # counts = []
1325
+ # 2.times do
1326
+ # require_dependency 'fixtures/company'
1327
+ # Firm.find(:first)
1328
+ # Dependencies.clear
1329
+ # ActiveRecord::Base.reset_subclasses
1330
+ # Dependencies.remove_subclasses_for(ActiveRecord::Base)
1331
+ #
1332
+ # GC.start
1333
+ #
1334
+ # count = 0
1335
+ # ObjectSpace.each_object(Proc) { count += 1 }
1336
+ # counts << count
1337
+ # end
1338
+ # assert counts.last <= counts.first,
1339
+ # "expected last count (#{counts.last}) to be <= first count (#{counts.first})"
1340
+ #end
1341
+
1342
+ private
1343
+ def assert_readers(model, exceptions)
1344
+ expected_readers = Set.new(model.column_names - (model.serialized_attributes.keys + ['id']))
1345
+ expected_readers += expected_readers.map { |col| "#{col}?" }
1346
+ expected_readers -= exceptions
1347
+ assert_equal expected_readers, model.read_methods
1348
+ end
1349
+ end