activerecord-odbc-adapter 2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. data/AUTHORS +16 -0
  2. data/COPYING +21 -0
  3. data/ChangeLog +139 -0
  4. data/LICENSE +5 -0
  5. data/NEWS +25 -0
  6. data/README +229 -0
  7. data/lib/active_record/connection_adapters/odbc_adapter.rb +1950 -0
  8. data/lib/active_record/vendor/odbcext_db2.rb +87 -0
  9. data/lib/active_record/vendor/odbcext_informix.rb +144 -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 +216 -0
  13. data/lib/active_record/vendor/odbcext_microsoftsqlserver_col.rb +40 -0
  14. data/lib/active_record/vendor/odbcext_mysql.rb +174 -0
  15. data/lib/active_record/vendor/odbcext_oracle.rb +219 -0
  16. data/lib/active_record/vendor/odbcext_postgresql.rb +158 -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_sqlanywhere.rb +115 -0
  20. data/lib/active_record/vendor/odbcext_sqlanywhere_col.rb +49 -0
  21. data/lib/active_record/vendor/odbcext_sybase.rb +213 -0
  22. data/lib/active_record/vendor/odbcext_sybase_col.rb +49 -0
  23. data/lib/active_record/vendor/odbcext_virtuoso.rb +158 -0
  24. data/lib/odbc_adapter.rb +28 -0
  25. data/support/lib/active_record/connection_adapters/abstract/schema_definitions.rb +259 -0
  26. data/support/odbc_rails.diff +367 -0
  27. data/support/pack_odbc.rb +119 -0
  28. data/support/rake/rails_plugin_package_task.rb +212 -0
  29. data/support/rake_fixes/README +6 -0
  30. data/support/rake_fixes/databases.dif +13 -0
  31. data/support/test/base_test.rb +1765 -0
  32. data/support/test/migration_test.rb +1007 -0
  33. data/test/connections/native_odbc/connection.rb +137 -0
  34. data/test/fixtures/db_definitions/db2.drop.sql +33 -0
  35. data/test/fixtures/db_definitions/db2.sql +237 -0
  36. data/test/fixtures/db_definitions/db22.drop.sql +2 -0
  37. data/test/fixtures/db_definitions/db22.sql +5 -0
  38. data/test/fixtures/db_definitions/informix.drop.sql +33 -0
  39. data/test/fixtures/db_definitions/informix.sql +223 -0
  40. data/test/fixtures/db_definitions/informix2.drop.sql +2 -0
  41. data/test/fixtures/db_definitions/informix2.sql +5 -0
  42. data/test/fixtures/db_definitions/ingres.drop.sql +68 -0
  43. data/test/fixtures/db_definitions/ingres.sql +252 -0
  44. data/test/fixtures/db_definitions/ingres2.drop.sql +2 -0
  45. data/test/fixtures/db_definitions/ingres2.sql +5 -0
  46. data/test/fixtures/db_definitions/mysql.drop.sql +33 -0
  47. data/test/fixtures/db_definitions/mysql.sql +238 -0
  48. data/test/fixtures/db_definitions/mysql2.drop.sql +2 -0
  49. data/test/fixtures/db_definitions/mysql2.sql +5 -0
  50. data/test/fixtures/db_definitions/oracle_odbc.drop.sql +72 -0
  51. data/test/fixtures/db_definitions/oracle_odbc.sql +296 -0
  52. data/test/fixtures/db_definitions/oracle_odbc2.drop.sql +2 -0
  53. data/test/fixtures/db_definitions/oracle_odbc2.sql +6 -0
  54. data/test/fixtures/db_definitions/postgresql.drop.sql +38 -0
  55. data/test/fixtures/db_definitions/postgresql.sql +267 -0
  56. data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
  57. data/test/fixtures/db_definitions/postgresql2.sql +5 -0
  58. data/test/fixtures/db_definitions/progress.drop.sql +67 -0
  59. data/test/fixtures/db_definitions/progress.sql +255 -0
  60. data/test/fixtures/db_definitions/progress2.drop.sql +2 -0
  61. data/test/fixtures/db_definitions/progress2.sql +6 -0
  62. data/test/fixtures/db_definitions/sqlserver.drop.sql +35 -0
  63. data/test/fixtures/db_definitions/sqlserver.sql +247 -0
  64. data/test/fixtures/db_definitions/sqlserver2.drop.sql +2 -0
  65. data/test/fixtures/db_definitions/sqlserver2.sql +5 -0
  66. data/test/fixtures/db_definitions/sybase.drop.sql +35 -0
  67. data/test/fixtures/db_definitions/sybase.sql +222 -0
  68. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  69. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  70. data/test/fixtures/db_definitions/virtuoso.drop.sql +33 -0
  71. data/test/fixtures/db_definitions/virtuoso.sql +218 -0
  72. data/test/fixtures/db_definitions/virtuoso2.drop.sql +2 -0
  73. data/test/fixtures/db_definitions/virtuoso2.sql +5 -0
  74. metadata +166 -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,6 @@
1
+ databases.dif contains changes required to databases.rake in order for
2
+ the test:units and test:functional tasks to recognize the odbc-rails
3
+ adapter.
4
+
5
+ databases.rake can be found somewhere like:
6
+ ruby/lib/ruby/gems/1.8/gems/rails-1.2.1/lib/tasks
@@ -0,0 +1,13 @@
1
+ --- databases.rake.orig 2007-01-22 12:09:02.230758200 +0000
2
+ +++ databases.rake 2007-02-01 17:08:12.350537700 +0000
3
+ @@ -142,6 +142,10 @@
4
+ when "firebird"
5
+ ActiveRecord::Base.establish_connection(:test)
6
+ ActiveRecord::Base.connection.recreate_database!
7
+ + when "odbc"
8
+ + ActiveRecord::Base.establish_connection(:test)
9
+ + test_db = ActiveRecord::Base.connection.current_database
10
+ + ActiveRecord::Base.connection.recreate_database(test_db, false) unless test_db.empty?
11
+ else
12
+ raise "Task not supported by '#{abcs["test"]["adapter"]}'"
13
+ end
@@ -0,0 +1,1765 @@
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
+ require 'fixtures/post'
14
+ require 'fixtures/minimalistic'
15
+ require 'rexml/document'
16
+
17
+ class Category < ActiveRecord::Base; end
18
+ class Smarts < ActiveRecord::Base; end
19
+ class CreditCard < ActiveRecord::Base
20
+ class PinNumber < ActiveRecord::Base
21
+ class CvvCode < ActiveRecord::Base; end
22
+ class SubCvvCode < CvvCode; end
23
+ end
24
+ class SubPinNumber < PinNumber; end
25
+ class Brand < Category; end
26
+ end
27
+ class MasterCreditCard < ActiveRecord::Base; end
28
+ class Post < ActiveRecord::Base; end
29
+ class Computer < ActiveRecord::Base; end
30
+ class NonExistentTable < ActiveRecord::Base; end
31
+ class TestOracleDefault < ActiveRecord::Base; end
32
+
33
+ class LoosePerson < ActiveRecord::Base
34
+ self.table_name = 'people'
35
+ self.abstract_class = true
36
+ attr_protected :credit_rating, :administrator
37
+ end
38
+
39
+ class LooseDescendant < LoosePerson
40
+ attr_protected :phone_number
41
+ end
42
+
43
+ class LooseDescendantSecond< LoosePerson
44
+ attr_protected :phone_number
45
+ attr_protected :name
46
+ end
47
+
48
+ class TightPerson < ActiveRecord::Base
49
+ self.table_name = 'people'
50
+ attr_accessible :name, :address
51
+ end
52
+
53
+ class TightDescendant < TightPerson
54
+ attr_accessible :phone_number
55
+ end
56
+
57
+ class ReadonlyTitlePost < Post
58
+ attr_readonly :title
59
+ end
60
+
61
+ class Booleantest < ActiveRecord::Base; end
62
+
63
+ class Task < ActiveRecord::Base
64
+ attr_protected :starting
65
+ end
66
+
67
+ class TopicWithProtectedContentAndAccessibleAuthorName < ActiveRecord::Base
68
+ self.table_name = 'topics'
69
+ attr_accessible :author_name
70
+ attr_protected :content
71
+ end
72
+
73
+ class BasicsTest < Test::Unit::TestCase
74
+ fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics
75
+
76
+ def test_table_exists
77
+ assert !NonExistentTable.table_exists?
78
+ assert Topic.table_exists?
79
+ end
80
+
81
+ def test_set_attributes
82
+ topic = Topic.find(1)
83
+ topic.attributes = { "title" => "Budget", "author_name" => "Jason" }
84
+ topic.save
85
+ assert_equal("Budget", topic.title)
86
+ assert_equal("Jason", topic.author_name)
87
+ assert_equal(topics(:first).author_email_address, Topic.find(1).author_email_address)
88
+ end
89
+
90
+ def test_integers_as_nil
91
+ test = AutoId.create('value' => '')
92
+ assert_nil AutoId.find(test.id).value
93
+ end
94
+
95
+ def test_set_attributes_with_block
96
+ topic = Topic.new do |t|
97
+ t.title = "Budget"
98
+ t.author_name = "Jason"
99
+ end
100
+
101
+ assert_equal("Budget", topic.title)
102
+ assert_equal("Jason", topic.author_name)
103
+ end
104
+
105
+ def test_respond_to?
106
+ topic = Topic.find(1)
107
+ assert topic.respond_to?("title")
108
+ assert topic.respond_to?("title?")
109
+ assert topic.respond_to?("title=")
110
+ assert topic.respond_to?(:title)
111
+ assert topic.respond_to?(:title?)
112
+ assert topic.respond_to?(:title=)
113
+ assert topic.respond_to?("author_name")
114
+ assert topic.respond_to?("attribute_names")
115
+ assert !topic.respond_to?("nothingness")
116
+ assert !topic.respond_to?(:nothingness)
117
+ end
118
+
119
+ def test_array_content
120
+ topic = Topic.new
121
+ topic.content = %w( one two three )
122
+ topic.save
123
+
124
+ assert_equal(%w( one two three ), Topic.find(topic.id).content)
125
+ end
126
+
127
+ def test_hash_content
128
+ topic = Topic.new
129
+ topic.content = { "one" => 1, "two" => 2 }
130
+ topic.save
131
+
132
+ assert_equal 2, Topic.find(topic.id).content["two"]
133
+
134
+ topic.content["three"] = 3
135
+ topic.save
136
+
137
+ assert_equal 3, Topic.find(topic.id).content["three"]
138
+ end
139
+
140
+ def test_update_array_content
141
+ topic = Topic.new
142
+ topic.content = %w( one two three )
143
+
144
+ topic.content.push "four"
145
+ assert_equal(%w( one two three four ), topic.content)
146
+
147
+ topic.save
148
+
149
+ topic = Topic.find(topic.id)
150
+ topic.content << "five"
151
+ assert_equal(%w( one two three four five ), topic.content)
152
+ end
153
+
154
+ def test_case_sensitive_attributes_hash
155
+ # DB2 is not case-sensitive
156
+ return true if current_adapter?(:DB2Adapter)
157
+
158
+ assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.find(:first).attributes
159
+ end
160
+
161
+ def test_create
162
+ topic = Topic.new
163
+ topic.title = "New Topic"
164
+ topic.save
165
+ topic_reloaded = Topic.find(topic.id)
166
+ assert_equal("New Topic", topic_reloaded.title)
167
+ end
168
+
169
+ def test_save!
170
+ topic = Topic.new(:title => "New Topic")
171
+ assert topic.save!
172
+
173
+ reply = Reply.new
174
+ assert_raise(ActiveRecord::RecordInvalid) { reply.save! }
175
+ end
176
+
177
+ def test_save_null_string_attributes
178
+ topic = Topic.find(1)
179
+ topic.attributes = { "title" => "null", "author_name" => "null" }
180
+ topic.save!
181
+ topic.reload
182
+ assert_equal("null", topic.title)
183
+ assert_equal("null", topic.author_name)
184
+ end
185
+
186
+ def test_save_nil_string_attributes
187
+ topic = Topic.find(1)
188
+ topic.title = nil
189
+ topic.save!
190
+ topic.reload
191
+ assert_nil topic.title
192
+ end
193
+
194
+ def test_save_for_record_with_only_primary_key
195
+ minimalistic = Minimalistic.new
196
+ assert_nothing_raised { minimalistic.save }
197
+ end
198
+
199
+ def test_save_for_record_with_only_primary_key_that_is_provided
200
+ assert_nothing_raised { Minimalistic.create!(:id => 2) }
201
+ end
202
+
203
+ def test_hashes_not_mangled
204
+ new_topic = { :title => "New Topic" }
205
+ new_topic_values = { :title => "AnotherTopic" }
206
+
207
+ topic = Topic.new(new_topic)
208
+ assert_equal new_topic[:title], topic.title
209
+
210
+ topic.attributes= new_topic_values
211
+ assert_equal new_topic_values[:title], topic.title
212
+ end
213
+
214
+ def test_create_many
215
+ topics = Topic.create([ { "title" => "first" }, { "title" => "second" }])
216
+ assert_equal 2, topics.size
217
+ assert_equal "first", topics.first.title
218
+ end
219
+
220
+ def test_create_columns_not_equal_attributes
221
+ topic = Topic.new
222
+ topic.title = 'Another New Topic'
223
+ topic.send :write_attribute, 'does_not_exist', 'test'
224
+ assert_nothing_raised { topic.save }
225
+ end
226
+
227
+ def test_create_through_factory
228
+ topic = Topic.create("title" => "New Topic")
229
+ topicReloaded = Topic.find(topic.id)
230
+ assert_equal(topic, topicReloaded)
231
+ end
232
+
233
+ def test_update
234
+ topic = Topic.new
235
+ topic.title = "Another New Topic"
236
+ topic.written_on = "2003-12-12 23:23:00"
237
+ topic.save
238
+ topicReloaded = Topic.find(topic.id)
239
+ assert_equal("Another New Topic", topicReloaded.title)
240
+
241
+ topicReloaded.title = "Updated topic"
242
+ topicReloaded.save
243
+
244
+ topicReloadedAgain = Topic.find(topic.id)
245
+
246
+ assert_equal("Updated topic", topicReloadedAgain.title)
247
+ end
248
+
249
+ def test_update_columns_not_equal_attributes
250
+ topic = Topic.new
251
+ topic.title = "Still another topic"
252
+ topic.save
253
+
254
+ topicReloaded = Topic.find(topic.id)
255
+ topicReloaded.title = "A New Topic"
256
+ topicReloaded.send :write_attribute, 'does_not_exist', 'test'
257
+ assert_nothing_raised { topicReloaded.save }
258
+ end
259
+
260
+ def test_update_for_record_with_only_primary_key
261
+ minimalistic = minimalistics(:first)
262
+ assert_nothing_raised { minimalistic.save }
263
+ end
264
+
265
+ def test_write_attribute
266
+ topic = Topic.new
267
+ topic.send(:write_attribute, :title, "Still another topic")
268
+ assert_equal "Still another topic", topic.title
269
+
270
+ topic.send(:write_attribute, "title", "Still another topic: part 2")
271
+ assert_equal "Still another topic: part 2", topic.title
272
+ end
273
+
274
+ def test_read_attribute
275
+ topic = Topic.new
276
+ topic.title = "Don't change the topic"
277
+ assert_equal "Don't change the topic", topic.send(:read_attribute, "title")
278
+ assert_equal "Don't change the topic", topic["title"]
279
+
280
+ assert_equal "Don't change the topic", topic.send(:read_attribute, :title)
281
+ assert_equal "Don't change the topic", topic[:title]
282
+ end
283
+
284
+ def test_read_attribute_when_false
285
+ topic = topics(:first)
286
+ topic.approved = false
287
+ assert !topic.approved?, "approved should be false"
288
+ topic.approved = "false"
289
+ assert !topic.approved?, "approved should be false"
290
+ end
291
+
292
+ def test_read_attribute_when_true
293
+ topic = topics(:first)
294
+ topic.approved = true
295
+ assert topic.approved?, "approved should be true"
296
+ topic.approved = "true"
297
+ assert topic.approved?, "approved should be true"
298
+ end
299
+
300
+ def test_read_write_boolean_attribute
301
+ topic = Topic.new
302
+ # puts ""
303
+ # puts "New Topic"
304
+ # puts topic.inspect
305
+ topic.approved = "false"
306
+ # puts "Expecting false"
307
+ # puts topic.inspect
308
+ assert !topic.approved?, "approved should be false"
309
+ topic.approved = "false"
310
+ # puts "Expecting false"
311
+ # puts topic.inspect
312
+ assert !topic.approved?, "approved should be false"
313
+ topic.approved = "true"
314
+ # puts "Expecting true"
315
+ # puts topic.inspect
316
+ assert topic.approved?, "approved should be true"
317
+ topic.approved = "true"
318
+ # puts "Expecting true"
319
+ # puts topic.inspect
320
+ assert topic.approved?, "approved should be true"
321
+ # puts ""
322
+ end
323
+
324
+ def test_query_attribute_string
325
+ [nil, "", " "].each do |value|
326
+ assert_equal false, Topic.new(:author_name => value).author_name?
327
+ end
328
+
329
+ assert_equal true, Topic.new(:author_name => "Name").author_name?
330
+ end
331
+
332
+ def test_query_attribute_number
333
+ [nil, 0, "0"].each do |value|
334
+ assert_equal false, Developer.new(:salary => value).salary?
335
+ end
336
+
337
+ assert_equal true, Developer.new(:salary => 1).salary?
338
+ assert_equal true, Developer.new(:salary => "1").salary?
339
+ end
340
+
341
+ def test_query_attribute_boolean
342
+ [nil, "", false, "false", "f", 0].each do |value|
343
+ assert_equal false, Topic.new(:approved => value).approved?
344
+ end
345
+
346
+ [true, "true", "1", 1].each do |value|
347
+ assert_equal true, Topic.new(:approved => value).approved?
348
+ end
349
+ end
350
+
351
+ def test_query_attribute_with_custom_fields
352
+ object = Company.find_by_sql(<<-SQL).first
353
+ SELECT c1.*, c2.ruby_type as string_value, c2.rating as int_value
354
+ FROM companies c1, companies c2
355
+ WHERE c1.firm_id = c2.id
356
+ AND c1.id = 2
357
+ SQL
358
+
359
+ assert_equal "Firm", object.string_value
360
+ assert object.string_value?
361
+
362
+ object.string_value = " "
363
+ assert !object.string_value?
364
+
365
+ assert_equal 1, object.int_value.to_i
366
+ assert object.int_value?
367
+
368
+ object.int_value = "0"
369
+ assert !object.int_value?
370
+ end
371
+
372
+
373
+ def test_reader_for_invalid_column_names
374
+ Topic.send(:define_read_method, "mumub-jumbo".to_sym, "mumub-jumbo", nil)
375
+ assert !Topic.generated_methods.include?("mumub-jumbo")
376
+ end
377
+
378
+ def test_non_attribute_access_and_assignment
379
+ topic = Topic.new
380
+ assert !topic.respond_to?("mumbo")
381
+ assert_raises(NoMethodError) { topic.mumbo }
382
+ assert_raises(NoMethodError) { topic.mumbo = 5 }
383
+ end
384
+
385
+ def test_preserving_date_objects
386
+ # SQL Server doesn't have a separate column type just for dates, so all are returned as time
387
+ return true if current_adapter?(:SQLServerAdapter)
388
+
389
+ if current_adapter?(:SybaseAdapter, :OracleAdapter)
390
+ # Sybase ctlib does not (yet?) support the date type; use datetime instead.
391
+ # Oracle treats all dates/times as Time.
392
+ elsif current_adapter?(:ODBCAdapter) && [:ingres, :microsoftsqlserver, :oracle].include?(ActiveRecord::Base.connection.dbmsName)
393
+ # Above databases don't have a pure date type. (They have a datetime-like type).
394
+ assert_kind_of(
395
+ Time, Topic.find(1).last_read,
396
+ "The last_read attribute should be of the Time class"
397
+ )
398
+ else
399
+ assert_kind_of(
400
+ Date, Topic.find(1).last_read,
401
+ "The last_read attribute should be of the Date class"
402
+ )
403
+ end
404
+ end
405
+
406
+ def test_preserving_time_objects
407
+ assert_kind_of(
408
+ Time, Topic.find(1).bonus_time,
409
+ "The bonus_time attribute should be of the Time class"
410
+ )
411
+
412
+ assert_kind_of(
413
+ Time, Topic.find(1).written_on,
414
+ "The written_on attribute should be of the Time class"
415
+ )
416
+
417
+ # For adapters which support microsecond resolution.
418
+ if current_adapter?(:PostgreSQLAdapter)
419
+ assert_equal 11, Topic.find(1).written_on.sec
420
+ assert_equal 223300, Topic.find(1).written_on.usec
421
+ assert_equal 9900, Topic.find(2).written_on.usec
422
+ end
423
+ end
424
+
425
+ def test_custom_mutator
426
+ topic = Topic.find(1)
427
+ # This mutator is protected in the class definition
428
+ topic.send(:approved=, true)
429
+ assert topic.instance_variable_get("@custom_approved")
430
+ end
431
+
432
+ def test_destroy
433
+ topic = Topic.find(1)
434
+ assert_equal topic, topic.destroy, 'topic.destroy did not return self'
435
+ assert topic.frozen?, 'topic not frozen after destroy'
436
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
437
+ end
438
+
439
+ def test_record_not_found_exception
440
+ assert_raises(ActiveRecord::RecordNotFound) { topicReloaded = Topic.find(99999) }
441
+ end
442
+
443
+ def test_initialize_with_attributes
444
+ topic = Topic.new({
445
+ "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23"
446
+ })
447
+
448
+ assert_equal("initialized from attributes", topic.title)
449
+ end
450
+
451
+ def test_initialize_with_invalid_attribute
452
+ begin
453
+ topic = Topic.new({ "title" => "test",
454
+ "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"})
455
+ rescue ActiveRecord::MultiparameterAssignmentErrors => ex
456
+ assert_equal(1, ex.errors.size)
457
+ assert_equal("last_read", ex.errors[0].attribute)
458
+ end
459
+ end
460
+
461
+ def test_load
462
+ topics = Topic.find(:all, :order => 'id')
463
+ assert_equal(2, topics.size)
464
+ assert_equal(topics(:first).title, topics.first.title)
465
+ end
466
+
467
+ def test_load_with_condition
468
+ topics = Topic.find(:all, :conditions => "author_name = 'Mary'")
469
+
470
+ assert_equal(1, topics.size)
471
+ assert_equal(topics(:second).title, topics.first.title)
472
+ end
473
+
474
+ def test_table_name_guesses
475
+ classes = [Category, Smarts, CreditCard, CreditCard::PinNumber, CreditCard::PinNumber::CvvCode, CreditCard::SubPinNumber, CreditCard::Brand, MasterCreditCard]
476
+
477
+ assert_equal "topics", Topic.table_name
478
+
479
+ assert_equal "categories", Category.table_name
480
+ assert_equal "smarts", Smarts.table_name
481
+ assert_equal "credit_cards", CreditCard.table_name
482
+ assert_equal "credit_card_pin_numbers", CreditCard::PinNumber.table_name
483
+ assert_equal "credit_card_pin_number_cvv_codes", CreditCard::PinNumber::CvvCode.table_name
484
+ assert_equal "credit_card_pin_numbers", CreditCard::SubPinNumber.table_name
485
+ assert_equal "categories", CreditCard::Brand.table_name
486
+ assert_equal "master_credit_cards", MasterCreditCard.table_name
487
+
488
+ ActiveRecord::Base.pluralize_table_names = false
489
+ classes.each(&:reset_table_name)
490
+
491
+ assert_equal "category", Category.table_name
492
+ assert_equal "smarts", Smarts.table_name
493
+ assert_equal "credit_card", CreditCard.table_name
494
+ assert_equal "credit_card_pin_number", CreditCard::PinNumber.table_name
495
+ assert_equal "credit_card_pin_number_cvv_code", CreditCard::PinNumber::CvvCode.table_name
496
+ assert_equal "credit_card_pin_number", CreditCard::SubPinNumber.table_name
497
+ assert_equal "category", CreditCard::Brand.table_name
498
+ assert_equal "master_credit_card", MasterCreditCard.table_name
499
+
500
+ ActiveRecord::Base.pluralize_table_names = true
501
+ classes.each(&:reset_table_name)
502
+
503
+ ActiveRecord::Base.table_name_prefix = "test_"
504
+ Category.reset_table_name
505
+ assert_equal "test_categories", Category.table_name
506
+ ActiveRecord::Base.table_name_suffix = "_test"
507
+ Category.reset_table_name
508
+ assert_equal "test_categories_test", Category.table_name
509
+ ActiveRecord::Base.table_name_prefix = ""
510
+ Category.reset_table_name
511
+ assert_equal "categories_test", Category.table_name
512
+ ActiveRecord::Base.table_name_suffix = ""
513
+ Category.reset_table_name
514
+ assert_equal "categories", Category.table_name
515
+
516
+ ActiveRecord::Base.pluralize_table_names = false
517
+ ActiveRecord::Base.table_name_prefix = "test_"
518
+ Category.reset_table_name
519
+ assert_equal "test_category", Category.table_name
520
+ ActiveRecord::Base.table_name_suffix = "_test"
521
+ Category.reset_table_name
522
+ assert_equal "test_category_test", Category.table_name
523
+ ActiveRecord::Base.table_name_prefix = ""
524
+ Category.reset_table_name
525
+ assert_equal "category_test", Category.table_name
526
+ ActiveRecord::Base.table_name_suffix = ""
527
+ Category.reset_table_name
528
+ assert_equal "category", Category.table_name
529
+
530
+ ActiveRecord::Base.pluralize_table_names = true
531
+ classes.each(&:reset_table_name)
532
+ end
533
+
534
+ def test_destroy_all
535
+ assert_equal 2, Topic.count
536
+
537
+ Topic.destroy_all "author_name = 'Mary'"
538
+ assert_equal 1, Topic.count
539
+ end
540
+
541
+ def test_destroy_many
542
+ assert_equal 3, Client.count
543
+ Client.destroy([2, 3])
544
+ assert_equal 1, Client.count
545
+ end
546
+
547
+ def test_delete_many
548
+ Topic.delete([1, 2])
549
+ assert_equal 0, Topic.count
550
+ end
551
+
552
+ def test_boolean_attributes
553
+ assert ! Topic.find(1).approved?
554
+ assert Topic.find(2).approved?
555
+ end
556
+
557
+ def test_increment_counter
558
+ Topic.increment_counter("replies_count", 1)
559
+ assert_equal 2, Topic.find(1).replies_count
560
+
561
+ Topic.increment_counter("replies_count", 1)
562
+ assert_equal 3, Topic.find(1).replies_count
563
+ end
564
+
565
+ def test_decrement_counter
566
+ Topic.decrement_counter("replies_count", 2)
567
+ assert_equal -1, Topic.find(2).replies_count
568
+
569
+ Topic.decrement_counter("replies_count", 2)
570
+ assert_equal -2, Topic.find(2).replies_count
571
+ end
572
+
573
+ def test_update_all
574
+ assert_equal 2, Topic.update_all("content = 'bulk updated!'")
575
+ assert_equal "bulk updated!", Topic.find(1).content
576
+ assert_equal "bulk updated!", Topic.find(2).content
577
+
578
+ assert_equal 2, Topic.update_all(['content = ?', 'bulk updated again!'])
579
+ assert_equal "bulk updated again!", Topic.find(1).content
580
+ assert_equal "bulk updated again!", Topic.find(2).content
581
+
582
+ assert_equal 2, Topic.update_all(['content = ?', nil])
583
+ assert_nil Topic.find(1).content
584
+ end
585
+
586
+ def test_update_all_with_hash
587
+ assert_not_nil Topic.find(1).last_read
588
+ assert_equal 2, Topic.update_all(:content => 'bulk updated with hash!', :last_read => nil)
589
+ assert_equal "bulk updated with hash!", Topic.find(1).content
590
+ assert_equal "bulk updated with hash!", Topic.find(2).content
591
+ assert_nil Topic.find(1).last_read
592
+ assert_nil Topic.find(2).last_read
593
+ end
594
+
595
+ if current_adapter?(:MysqlAdapter)
596
+ def test_update_all_with_order_and_limit
597
+ assert_equal 1, Topic.update_all("content = 'bulk updated!'", nil, :limit => 1, :order => 'id DESC')
598
+ end
599
+ end
600
+
601
+ def test_update_many
602
+ topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
603
+ updated = Topic.update(topic_data.keys, topic_data.values)
604
+
605
+ assert_equal 2, updated.size
606
+ assert_equal "1 updated", Topic.find(1).content
607
+ assert_equal "2 updated", Topic.find(2).content
608
+ end
609
+
610
+ def test_delete_all
611
+ assert_equal 2, Topic.delete_all
612
+ end
613
+
614
+ def test_update_by_condition
615
+ Topic.update_all "content = 'bulk updated!'", ["approved = ?", true]
616
+ assert_equal "Have a nice day", Topic.find(1).content
617
+ assert_equal "bulk updated!", Topic.find(2).content
618
+ end
619
+
620
+ def test_attribute_present
621
+ t = Topic.new
622
+ t.title = "hello there!"
623
+ t.written_on = Time.now
624
+ assert t.attribute_present?("title")
625
+ assert t.attribute_present?("written_on")
626
+ assert !t.attribute_present?("content")
627
+ end
628
+
629
+ def test_attribute_keys_on_new_instance
630
+ t = Topic.new
631
+ assert_equal nil, t.title, "The topics table has a title column, so it should be nil"
632
+ assert_raise(NoMethodError) { t.title2 }
633
+ end
634
+
635
+ def test_class_name
636
+ assert_equal "Firm", ActiveRecord::Base.class_name("firms")
637
+ assert_equal "Category", ActiveRecord::Base.class_name("categories")
638
+ assert_equal "AccountHolder", ActiveRecord::Base.class_name("account_holder")
639
+
640
+ ActiveRecord::Base.pluralize_table_names = false
641
+ assert_equal "Firms", ActiveRecord::Base.class_name( "firms" )
642
+ ActiveRecord::Base.pluralize_table_names = true
643
+
644
+ ActiveRecord::Base.table_name_prefix = "test_"
645
+ assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms" )
646
+ ActiveRecord::Base.table_name_suffix = "_tests"
647
+ assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms_tests" )
648
+ ActiveRecord::Base.table_name_prefix = ""
649
+ assert_equal "Firm", ActiveRecord::Base.class_name( "firms_tests" )
650
+ ActiveRecord::Base.table_name_suffix = ""
651
+ assert_equal "Firm", ActiveRecord::Base.class_name( "firms" )
652
+ end
653
+
654
+ def test_null_fields
655
+ assert_nil Topic.find(1).parent_id
656
+ assert_nil Topic.create("title" => "Hey you").parent_id
657
+ end
658
+
659
+ def test_default_values
660
+ topic = Topic.new
661
+ assert topic.approved?
662
+ assert_nil topic.written_on
663
+ assert_nil topic.bonus_time
664
+ assert_nil topic.last_read
665
+
666
+ topic.save
667
+
668
+ topic = Topic.find(topic.id)
669
+ assert topic.approved?
670
+ assert_nil topic.last_read
671
+
672
+ # Oracle has some funky default handling, so it requires a bit of
673
+ # extra testing. See ticket #2788.
674
+ if current_adapter?(:OracleAdapter)
675
+ test = TestOracleDefault.new
676
+ assert_equal "X", test.test_char
677
+ assert_equal "hello", test.test_string
678
+ assert_equal 3, test.test_int
679
+ end
680
+ end
681
+
682
+ # Oracle, SQLServer, and Sybase do not have a TIME datatype.
683
+ unless current_adapter?(:SQLServerAdapter, :OracleAdapter, :SybaseAdapter)
684
+ def test_utc_as_time_zone
685
+ Topic.default_timezone = :utc
686
+ attributes = { "bonus_time" => "5:42:00AM" }
687
+ topic = Topic.find(1)
688
+ topic.attributes = attributes
689
+ assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time
690
+ Topic.default_timezone = :local
691
+ end
692
+
693
+ def test_utc_as_time_zone_and_new
694
+ Topic.default_timezone = :utc
695
+ attributes = { "bonus_time(1i)"=>"2000",
696
+ "bonus_time(2i)"=>"1",
697
+ "bonus_time(3i)"=>"1",
698
+ "bonus_time(4i)"=>"10",
699
+ "bonus_time(5i)"=>"35",
700
+ "bonus_time(6i)"=>"50" }
701
+ topic = Topic.new(attributes)
702
+ assert_equal Time.utc(2000, 1, 1, 10, 35, 50), topic.bonus_time
703
+ Topic.default_timezone = :local
704
+ end
705
+ end
706
+
707
+ def test_default_values_on_empty_strings
708
+ topic = Topic.new
709
+ #Sybase does not allow nulls in boolean columns
710
+ if current_adapter?(:ODBCAdapter) && ActiveRecord::Base.connection.dbmsName == :sybase
711
+ topic.approved = false
712
+ else
713
+ topic.approved = nil
714
+ end
715
+ topic.last_read = nil
716
+
717
+ topic.save
718
+
719
+ topic = Topic.find(topic.id)
720
+ assert_nil topic.last_read
721
+
722
+ # Sybase adapter does not allow nulls in boolean columns
723
+ if current_adapter?(:SybaseAdapter) ||
724
+ current_adapter?(:ODBCAdapter) && ActiveRecord::Base.connection.dbmsName == :sybase
725
+ assert topic.approved == false
726
+ else
727
+ assert_nil topic.approved
728
+ end
729
+ end
730
+
731
+ def test_equality
732
+ assert_equal Topic.find(1), Topic.find(2).topic
733
+ end
734
+
735
+ def test_equality_of_new_records
736
+ assert_not_equal Topic.new, Topic.new
737
+ end
738
+
739
+ def test_hashing
740
+ assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
741
+ end
742
+
743
+ def test_destroy_new_record
744
+ client = Client.new
745
+ client.destroy
746
+ assert client.frozen?
747
+ end
748
+
749
+ def test_destroy_record_with_associations
750
+ client = Client.find(3)
751
+ client.destroy
752
+ assert client.frozen?
753
+ assert_kind_of Firm, client.firm
754
+ assert_raises(TypeError) { client.name = "something else" }
755
+ end
756
+
757
+ def test_update_attribute
758
+ assert !Topic.find(1).approved?
759
+ Topic.find(1).update_attribute("approved", true)
760
+ assert Topic.find(1).approved?
761
+
762
+ Topic.find(1).update_attribute(:approved, false)
763
+ assert !Topic.find(1).approved?
764
+ end
765
+
766
+ def test_update_attributes
767
+ topic = Topic.find(1)
768
+ assert !topic.approved?
769
+ assert_equal "The First Topic", topic.title
770
+
771
+ topic.update_attributes("approved" => true, "title" => "The First Topic Updated")
772
+ topic.reload
773
+ assert topic.approved?
774
+ assert_equal "The First Topic Updated", topic.title
775
+
776
+ topic.update_attributes(:approved => false, :title => "The First Topic")
777
+ topic.reload
778
+ assert !topic.approved?
779
+ assert_equal "The First Topic", topic.title
780
+ end
781
+
782
+ def test_update_attributes!
783
+ reply = Reply.find(2)
784
+ assert_equal "The Second Topic's of the day", reply.title
785
+ assert_equal "Have a nice day", reply.content
786
+
787
+ reply.update_attributes!("title" => "The Second Topic's of the day updated", "content" => "Have a nice evening")
788
+ reply.reload
789
+ assert_equal "The Second Topic's of the day updated", reply.title
790
+ assert_equal "Have a nice evening", reply.content
791
+
792
+ reply.update_attributes!(:title => "The Second Topic's of the day", :content => "Have a nice day")
793
+ reply.reload
794
+ assert_equal "The Second Topic's of the day", reply.title
795
+ assert_equal "Have a nice day", reply.content
796
+
797
+ assert_raise(ActiveRecord::RecordInvalid) { reply.update_attributes!(:title => nil, :content => "Have a nice evening") }
798
+ end
799
+
800
+ def test_mass_assignment_should_raise_exception_if_accessible_and_protected_attribute_writers_are_both_used
801
+ topic = TopicWithProtectedContentAndAccessibleAuthorName.new
802
+ assert_raises(RuntimeError) { topic.attributes = { "author_name" => "me" } }
803
+ assert_raises(RuntimeError) { topic.attributes = { "content" => "stuff" } }
804
+ end
805
+
806
+ def test_mass_assignment_protection
807
+ firm = Firm.new
808
+ firm.attributes = { "name" => "Next Angle", "rating" => 5 }
809
+ assert_equal 1, firm.rating
810
+ end
811
+
812
+ def test_mass_assignment_protection_against_class_attribute_writers
813
+ [:logger, :configurations, :primary_key_prefix_type, :table_name_prefix, :table_name_suffix, :pluralize_table_names, :colorize_logging,
814
+ :default_timezone, :allow_concurrency, :schema_format, :verification_timeout, :lock_optimistically, :record_timestamps].each do |method|
815
+ assert Task.respond_to?(method)
816
+ assert Task.respond_to?("#{method}=")
817
+ assert Task.new.respond_to?(method)
818
+ assert !Task.new.respond_to?("#{method}=")
819
+ end
820
+ end
821
+
822
+ def test_customized_primary_key_remains_protected
823
+ subscriber = Subscriber.new(:nick => 'webster123', :name => 'nice try')
824
+ assert_nil subscriber.id
825
+
826
+ keyboard = Keyboard.new(:key_number => 9, :name => 'nice try')
827
+ assert_nil keyboard.id
828
+ end
829
+
830
+ def test_customized_primary_key_remains_protected_when_referred_to_as_id
831
+ subscriber = Subscriber.new(:id => 'webster123', :name => 'nice try')
832
+ assert_nil subscriber.id
833
+
834
+ keyboard = Keyboard.new(:id => 9, :name => 'nice try')
835
+ assert_nil keyboard.id
836
+ end
837
+
838
+ def test_mass_assignment_protection_on_defaults
839
+ firm = Firm.new
840
+ firm.attributes = { "id" => 5, "type" => "Client" }
841
+ assert_nil firm.id
842
+ assert_equal "Firm", firm[:type]
843
+ end
844
+
845
+ def test_mass_assignment_accessible
846
+ reply = Reply.new("title" => "hello", "content" => "world", "approved" => true)
847
+ reply.save
848
+
849
+ assert reply.approved?
850
+
851
+ reply.approved = false
852
+ reply.save
853
+
854
+ assert !reply.approved?
855
+ end
856
+
857
+ def test_mass_assignment_protection_inheritance
858
+ assert_nil LoosePerson.accessible_attributes
859
+ assert_equal Set.new([ 'credit_rating', 'administrator' ]), LoosePerson.protected_attributes
860
+
861
+ assert_nil LooseDescendant.accessible_attributes
862
+ assert_equal Set.new([ 'credit_rating', 'administrator', 'phone_number' ]), LooseDescendant.protected_attributes
863
+
864
+ assert_nil LooseDescendantSecond.accessible_attributes
865
+ assert_equal Set.new([ 'credit_rating', 'administrator', 'phone_number', 'name' ]), LooseDescendantSecond.protected_attributes, 'Running attr_protected twice in one class should merge the protections'
866
+
867
+ assert_nil TightPerson.protected_attributes
868
+ assert_equal Set.new([ 'name', 'address' ]), TightPerson.accessible_attributes
869
+
870
+ assert_nil TightDescendant.protected_attributes
871
+ assert_equal Set.new([ 'name', 'address', 'phone_number' ]), TightDescendant.accessible_attributes
872
+ end
873
+
874
+ def test_readonly_attributes
875
+ assert_equal Set.new([ 'title' ]), ReadonlyTitlePost.readonly_attributes
876
+
877
+ post = ReadonlyTitlePost.create(:title => "cannot change this", :body => "changeable")
878
+ post.reload
879
+ assert_equal "cannot change this", post.title
880
+
881
+ post.update_attributes(:title => "try to change", :body => "changed")
882
+ post.reload
883
+ assert_equal "cannot change this", post.title
884
+ assert_equal "changed", post.body
885
+ end
886
+
887
+ def test_multiparameter_attributes_on_date
888
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
889
+ topic = Topic.find(1)
890
+ topic.attributes = attributes
891
+ # note that extra #to_date call allows test to pass for Oracle, which
892
+ # treats dates/times the same
893
+ assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date
894
+ end
895
+
896
+ def test_multiparameter_attributes_on_date_with_empty_date
897
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
898
+ topic = Topic.find(1)
899
+ topic.attributes = attributes
900
+ # note that extra #to_date call allows test to pass for Oracle, which
901
+ # treats dates/times the same
902
+ assert_date_from_db Date.new(2004, 6, 1), topic.last_read.to_date
903
+ end
904
+
905
+ def test_multiparameter_attributes_on_date_with_all_empty
906
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" }
907
+ topic = Topic.find(1)
908
+ topic.attributes = attributes
909
+ assert_nil topic.last_read
910
+ end
911
+
912
+ def test_multiparameter_attributes_on_time
913
+ attributes = {
914
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
915
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
916
+ }
917
+ topic = Topic.find(1)
918
+ topic.attributes = attributes
919
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
920
+ end
921
+
922
+ def test_multiparameter_attributes_on_time_with_empty_seconds
923
+ attributes = {
924
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
925
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => ""
926
+ }
927
+ topic = Topic.find(1)
928
+ topic.attributes = attributes
929
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
930
+ end
931
+
932
+ def test_multiparameter_mass_assignment_protector
933
+ task = Task.new
934
+ time = Time.mktime(2000, 1, 1, 1)
935
+ task.starting = time
936
+ attributes = { "starting(1i)" => "2004", "starting(2i)" => "6", "starting(3i)" => "24" }
937
+ task.attributes = attributes
938
+ assert_equal time, task.starting
939
+ end
940
+
941
+ def test_multiparameter_assignment_of_aggregation
942
+ customer = Customer.new
943
+ address = Address.new("The Street", "The City", "The Country")
944
+ attributes = { "address(1)" => address.street, "address(2)" => address.city, "address(3)" => address.country }
945
+ customer.attributes = attributes
946
+ assert_equal address, customer.address
947
+ end
948
+
949
+ def test_attributes_on_dummy_time
950
+ # Oracle, SQL Server, and Sybase do not have a TIME datatype.
951
+ return true if current_adapter?(:SQLServerAdapter, :OracleAdapter, :SybaseAdapter)
952
+ if current_adapter?(:ODBCAdapter)
953
+ # Check for databases which don't have a true TIME datatype
954
+ return true if [:ingres, :oracle].include?(ActiveRecord::Base.connection.dbmsName)
955
+ end
956
+
957
+ attributes = {
958
+ "bonus_time" => "5:42:00AM"
959
+ }
960
+ topic = Topic.find(1)
961
+ topic.attributes = attributes
962
+ assert_equal Time.local(2000, 1, 1, 5, 42, 0), topic.bonus_time
963
+ end
964
+
965
+ def test_boolean
966
+ b_false = Booleantest.create({ "value" => false })
967
+ false_id = b_false.id
968
+ b_true = Booleantest.create({ "value" => true })
969
+ true_id = b_true.id
970
+
971
+ b_false = Booleantest.find(false_id)
972
+ assert !b_false.value?
973
+ b_true = Booleantest.find(true_id)
974
+ assert b_true.value?
975
+ end
976
+
977
+ def test_boolean_cast_from_string
978
+ b_false = Booleantest.create({ "value" => "0" })
979
+ false_id = b_false.id
980
+ b_true = Booleantest.create({ "value" => "1" })
981
+ true_id = b_true.id
982
+
983
+ b_false = Booleantest.find(false_id)
984
+ assert !b_false.value?
985
+ b_true = Booleantest.find(true_id)
986
+ assert b_true.value?
987
+ end
988
+
989
+ def test_clone
990
+ topic = Topic.find(1)
991
+ cloned_topic = nil
992
+ assert_nothing_raised { cloned_topic = topic.clone }
993
+ assert_equal topic.title, cloned_topic.title
994
+ assert cloned_topic.new_record?
995
+
996
+ # test if the attributes have been cloned
997
+ topic.title = "a"
998
+ cloned_topic.title = "b"
999
+ assert_equal "a", topic.title
1000
+ assert_equal "b", cloned_topic.title
1001
+
1002
+ # test if the attribute values have been cloned
1003
+ topic.title = {"a" => "b"}
1004
+ cloned_topic = topic.clone
1005
+ cloned_topic.title["a"] = "c"
1006
+ assert_equal "b", topic.title["a"]
1007
+
1008
+ #test if attributes set as part of after_initialize are cloned correctly
1009
+ assert_equal topic.author_email_address, cloned_topic.author_email_address
1010
+
1011
+ # test if saved clone object differs from original
1012
+ cloned_topic.save
1013
+ assert !cloned_topic.new_record?
1014
+ assert cloned_topic.id != topic.id
1015
+ end
1016
+
1017
+ def test_clone_with_aggregate_of_same_name_as_attribute
1018
+ dev = DeveloperWithAggregate.find(1)
1019
+ assert_kind_of DeveloperSalary, dev.salary
1020
+
1021
+ clone = nil
1022
+ assert_nothing_raised { clone = dev.clone }
1023
+ assert_kind_of DeveloperSalary, clone.salary
1024
+ assert_equal dev.salary.amount, clone.salary.amount
1025
+ assert clone.new_record?
1026
+
1027
+ # test if the attributes have been cloned
1028
+ original_amount = clone.salary.amount
1029
+ dev.salary.amount = 1
1030
+ assert_equal original_amount, clone.salary.amount
1031
+
1032
+ assert clone.save
1033
+ assert !clone.new_record?
1034
+ assert clone.id != dev.id
1035
+ end
1036
+
1037
+ def test_clone_preserves_subtype
1038
+ clone = nil
1039
+ assert_nothing_raised { clone = Company.find(3).clone }
1040
+ assert_kind_of Client, clone
1041
+ end
1042
+
1043
+ def test_bignum
1044
+ company = Company.find(1)
1045
+ company.rating = 2147483647
1046
+ company.save
1047
+ company = Company.find(1)
1048
+ assert_equal 2147483647, company.rating
1049
+ end
1050
+
1051
+ # TODO: extend defaults tests to other databases!
1052
+ if current_adapter?(:PostgreSQLAdapter)
1053
+ def test_default
1054
+ default = Default.new
1055
+
1056
+ # fixed dates / times
1057
+ assert_equal Date.new(2004, 1, 1), default.fixed_date
1058
+ assert_equal Time.local(2004, 1,1,0,0,0,0), default.fixed_time
1059
+
1060
+ # char types
1061
+ assert_equal 'Y', default.char1
1062
+ assert_equal 'a varchar field', default.char2
1063
+ assert_equal 'a text field', default.char3
1064
+ end
1065
+
1066
+ class Geometric < ActiveRecord::Base; end
1067
+ def test_geometric_content
1068
+
1069
+ # accepted format notes:
1070
+ # ()'s aren't required
1071
+ # values can be a mix of float or integer
1072
+
1073
+ g = Geometric.new(
1074
+ :a_point => '(5.0, 6.1)',
1075
+ #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
1076
+ :a_line_segment => '(2.0, 3), (5.5, 7.0)',
1077
+ :a_box => '2.0, 3, 5.5, 7.0',
1078
+ :a_path => '[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]', # [ ] is an open path
1079
+ :a_polygon => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))',
1080
+ :a_circle => '<(5.3, 10.4), 2>'
1081
+ )
1082
+
1083
+ assert g.save
1084
+
1085
+ # Reload and check that we have all the geometric attributes.
1086
+ h = Geometric.find(g.id)
1087
+
1088
+ assert_equal '(5,6.1)', h.a_point
1089
+ assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
1090
+ assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
1091
+ assert_equal '[(2,3),(5.5,7),(8.5,11)]', h.a_path
1092
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
1093
+ assert_equal '<(5.3,10.4),2>', h.a_circle
1094
+
1095
+ # use a geometric function to test for an open path
1096
+ objs = Geometric.find_by_sql ["select isopen(a_path) from geometrics where id = ?", g.id]
1097
+ assert_equal objs[0].isopen, 't'
1098
+
1099
+ # test alternate formats when defining the geometric types
1100
+
1101
+ g = Geometric.new(
1102
+ :a_point => '5.0, 6.1',
1103
+ #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
1104
+ :a_line_segment => '((2.0, 3), (5.5, 7.0))',
1105
+ :a_box => '(2.0, 3), (5.5, 7.0)',
1106
+ :a_path => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))', # ( ) is a closed path
1107
+ :a_polygon => '2.0, 3, 5.5, 7.0, 8.5, 11.0',
1108
+ :a_circle => '((5.3, 10.4), 2)'
1109
+ )
1110
+
1111
+ assert g.save
1112
+
1113
+ # Reload and check that we have all the geometric attributes.
1114
+ h = Geometric.find(g.id)
1115
+
1116
+ assert_equal '(5,6.1)', h.a_point
1117
+ assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
1118
+ assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
1119
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path
1120
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
1121
+ assert_equal '<(5.3,10.4),2>', h.a_circle
1122
+
1123
+ # use a geometric function to test for an closed path
1124
+ objs = Geometric.find_by_sql ["select isclosed(a_path) from geometrics where id = ?", g.id]
1125
+ assert_equal objs[0].isclosed, 't'
1126
+ end
1127
+ end
1128
+
1129
+ class NumericData < ActiveRecord::Base
1130
+ self.table_name = 'numeric_data'
1131
+ end
1132
+
1133
+ def test_numeric_fields
1134
+ m = NumericData.new(
1135
+ :bank_balance => 1586.43,
1136
+ :big_bank_balance => BigDecimal("1000234000567.95"),
1137
+ :world_population => 6000000000,
1138
+ :my_house_population => 3
1139
+ )
1140
+ assert m.save
1141
+
1142
+ m1 = NumericData.find(m.id)
1143
+ assert_not_nil m1
1144
+
1145
+ # As with migration_test.rb, we should make world_population >= 2**62
1146
+ # to cover 64-bit platforms and test it is a Bignum, but the main thing
1147
+ # is that it's an Integer.
1148
+ assert_kind_of Integer, m1.world_population
1149
+ assert_equal 6000000000, m1.world_population
1150
+
1151
+ assert_kind_of Fixnum, m1.my_house_population
1152
+ assert_equal 3, m1.my_house_population
1153
+
1154
+ assert_kind_of BigDecimal, m1.bank_balance
1155
+ assert_equal BigDecimal("1586.43"), m1.bank_balance
1156
+
1157
+ assert_kind_of BigDecimal, m1.big_bank_balance
1158
+ assert_equal BigDecimal("1000234000567.95"), m1.big_bank_balance
1159
+ end
1160
+
1161
+ def test_auto_id
1162
+ auto = AutoId.new
1163
+ auto.save
1164
+ assert (auto.id > 0)
1165
+ end
1166
+
1167
+ def quote_column_name(name)
1168
+ "<#{name}>"
1169
+ end
1170
+
1171
+ def test_quote_keys
1172
+ ar = AutoId.new
1173
+ source = {"foo" => "bar", "baz" => "quux"}
1174
+ actual = ar.send(:quote_columns, self, source)
1175
+ inverted = actual.invert
1176
+ assert_equal("<foo>", inverted["bar"])
1177
+ assert_equal("<baz>", inverted["quux"])
1178
+ end
1179
+
1180
+ def test_sql_injection_via_find
1181
+ assert_raises(ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid) do
1182
+ Topic.find("123456 OR id > 0")
1183
+ end
1184
+ end
1185
+
1186
+ def test_column_name_properly_quoted
1187
+ col_record = ColumnName.new
1188
+ col_record.references = 40
1189
+ assert col_record.save
1190
+ col_record.references = 41
1191
+ assert col_record.save
1192
+ assert_not_nil c2 = ColumnName.find(col_record.id)
1193
+ assert_equal(41, c2.references)
1194
+ end
1195
+
1196
+ def test_quoting_arrays
1197
+ replies = Reply.find(:all, :conditions => [ "id IN (?)", topics(:first).replies.collect(&:id) ])
1198
+ assert_equal topics(:first).replies.size, replies.size
1199
+
1200
+ # DB2 doesn't support "WHERE (id IN (NULL))" clause
1201
+ unless current_adapter?(:ODBCAdapter) && [:db2].include?(ActiveRecord::Base.connection.dbmsName)
1202
+ replies = Reply.find(:all, :conditions => [ "id IN (?)", [] ])
1203
+ assert_equal 0, replies.size
1204
+ end
1205
+ end
1206
+
1207
+ MyObject = Struct.new :attribute1, :attribute2
1208
+
1209
+ def test_serialized_attribute
1210
+ myobj = MyObject.new('value1', 'value2')
1211
+ topic = Topic.create("content" => myobj)
1212
+ Topic.serialize("content", MyObject)
1213
+ assert_equal(myobj, topic.content)
1214
+ end
1215
+
1216
+ def test_nil_serialized_attribute_with_class_constraint
1217
+ myobj = MyObject.new('value1', 'value2')
1218
+ topic = Topic.new
1219
+ assert_nil topic.content
1220
+ end
1221
+
1222
+ def test_should_raise_exception_on_serialized_attribute_with_type_mismatch
1223
+ myobj = MyObject.new('value1', 'value2')
1224
+ topic = Topic.new(:content => myobj)
1225
+ assert topic.save
1226
+ Topic.serialize(:content, Hash)
1227
+ assert_raise(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).content }
1228
+ ensure
1229
+ Topic.serialize(:content)
1230
+ end
1231
+
1232
+ def test_serialized_attribute_with_class_constraint
1233
+ settings = { "color" => "blue" }
1234
+ Topic.serialize(:content, Hash)
1235
+ topic = Topic.new(:content => settings)
1236
+ assert topic.save
1237
+ assert_equal(settings, Topic.find(topic.id).content)
1238
+ ensure
1239
+ Topic.serialize(:content)
1240
+ end
1241
+
1242
+ def test_quote
1243
+ if current_adapter?(:ODBCAdapter) && [:informix, :sybase].include?(ActiveRecord::Base.connection.dbmsName)
1244
+ #Some databases only allow printable characters in VARCHAR columns.
1245
+ author_name = "\\ \041 ' \n \\n \""
1246
+ else
1247
+ author_name = "\\ \001 ' \n \\n \""
1248
+ end
1249
+ topic = Topic.create('author_name' => author_name)
1250
+ assert_equal author_name, Topic.find(topic.id).author_name
1251
+ end
1252
+
1253
+ def test_quote_chars
1254
+ str = 'The Narrator'
1255
+ topic = Topic.create(:author_name => str)
1256
+ assert_equal str, topic.author_name
1257
+
1258
+ assert_kind_of ActiveSupport::Multibyte::Chars, str.chars
1259
+ topic = Topic.find_by_author_name(str.chars)
1260
+
1261
+ assert_kind_of Topic, topic
1262
+ assert_equal str, topic.author_name, "The right topic should have been found by name even with name passed as Chars"
1263
+ end
1264
+
1265
+ def test_class_level_destroy
1266
+ should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
1267
+ Topic.find(1).replies << should_be_destroyed_reply
1268
+
1269
+ Topic.destroy(1)
1270
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
1271
+ assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) }
1272
+ end
1273
+
1274
+ def test_class_level_delete
1275
+ should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
1276
+ Topic.find(1).replies << should_be_destroyed_reply
1277
+
1278
+ Topic.delete(1)
1279
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
1280
+ assert_nothing_raised { Reply.find(should_be_destroyed_reply.id) }
1281
+ end
1282
+
1283
+ def test_increment_attribute
1284
+ assert_equal 50, accounts(:signals37).credit_limit
1285
+ accounts(:signals37).increment! :credit_limit
1286
+ assert_equal 51, accounts(:signals37, :reload).credit_limit
1287
+
1288
+ accounts(:signals37).increment(:credit_limit).increment!(:credit_limit)
1289
+ assert_equal 53, accounts(:signals37, :reload).credit_limit
1290
+ end
1291
+
1292
+ def test_increment_nil_attribute
1293
+ assert_nil topics(:first).parent_id
1294
+ topics(:first).increment! :parent_id
1295
+ assert_equal 1, topics(:first).parent_id
1296
+ end
1297
+
1298
+ def test_decrement_attribute
1299
+ assert_equal 50, accounts(:signals37).credit_limit
1300
+
1301
+ accounts(:signals37).decrement!(:credit_limit)
1302
+ assert_equal 49, accounts(:signals37, :reload).credit_limit
1303
+
1304
+ accounts(:signals37).decrement(:credit_limit).decrement!(:credit_limit)
1305
+ assert_equal 47, accounts(:signals37, :reload).credit_limit
1306
+ end
1307
+
1308
+ def test_toggle_attribute
1309
+ assert !topics(:first).approved?
1310
+ topics(:first).toggle!(:approved)
1311
+ assert topics(:first).approved?
1312
+ topic = topics(:first)
1313
+ topic.toggle(:approved)
1314
+ assert !topic.approved?
1315
+ topic.reload
1316
+ assert topic.approved?
1317
+ end
1318
+
1319
+ def test_reload
1320
+ t1 = Topic.find(1)
1321
+ t2 = Topic.find(1)
1322
+ t1.title = "something else"
1323
+ t1.save
1324
+ t2.reload
1325
+ assert_equal t1.title, t2.title
1326
+ end
1327
+
1328
+ def test_define_attr_method_with_value
1329
+ k = Class.new( ActiveRecord::Base )
1330
+ k.send(:define_attr_method, :table_name, "foo")
1331
+ assert_equal "foo", k.table_name
1332
+ end
1333
+
1334
+ def test_define_attr_method_with_block
1335
+ k = Class.new( ActiveRecord::Base )
1336
+ k.send(:define_attr_method, :primary_key) { "sys_" + original_primary_key }
1337
+ assert_equal "sys_id", k.primary_key
1338
+ end
1339
+
1340
+ def test_set_table_name_with_value
1341
+ k = Class.new( ActiveRecord::Base )
1342
+ k.table_name = "foo"
1343
+ assert_equal "foo", k.table_name
1344
+ k.set_table_name "bar"
1345
+ assert_equal "bar", k.table_name
1346
+ end
1347
+
1348
+ def test_set_table_name_with_block
1349
+ k = Class.new( ActiveRecord::Base )
1350
+ k.set_table_name { "ks" }
1351
+ assert_equal "ks", k.table_name
1352
+ end
1353
+
1354
+ def test_set_primary_key_with_value
1355
+ k = Class.new( ActiveRecord::Base )
1356
+ k.primary_key = "foo"
1357
+ assert_equal "foo", k.primary_key
1358
+ k.set_primary_key "bar"
1359
+ assert_equal "bar", k.primary_key
1360
+ end
1361
+
1362
+ def test_set_primary_key_with_block
1363
+ k = Class.new( ActiveRecord::Base )
1364
+ k.set_primary_key { "sys_" + original_primary_key }
1365
+ assert_equal "sys_id", k.primary_key
1366
+ end
1367
+
1368
+ def test_set_inheritance_column_with_value
1369
+ k = Class.new( ActiveRecord::Base )
1370
+ k.inheritance_column = "foo"
1371
+ assert_equal "foo", k.inheritance_column
1372
+ k.set_inheritance_column "bar"
1373
+ assert_equal "bar", k.inheritance_column
1374
+ end
1375
+
1376
+ def test_set_inheritance_column_with_block
1377
+ k = Class.new( ActiveRecord::Base )
1378
+ k.set_inheritance_column { original_inheritance_column + "_id" }
1379
+ assert_equal "type_id", k.inheritance_column
1380
+ end
1381
+
1382
+ def test_count_with_join
1383
+ res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
1384
+
1385
+ res2 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'", :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1386
+ assert_equal res, res2
1387
+
1388
+ res3 = nil
1389
+ assert_nothing_raised do
1390
+ res3 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'",
1391
+ :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1392
+ end
1393
+ assert_equal res, res3
1394
+
1395
+ res4 = Post.count_by_sql "SELECT COUNT(p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
1396
+ res5 = nil
1397
+ assert_nothing_raised do
1398
+ res5 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
1399
+ :joins => "p, comments co",
1400
+ :select => "p.id")
1401
+ end
1402
+
1403
+ assert_equal res4, res5
1404
+
1405
+ unless current_adapter?(:SQLite2Adapter, :DeprecatedSQLiteAdapter)
1406
+ res6 = Post.count_by_sql "SELECT COUNT(DISTINCT p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
1407
+ res7 = nil
1408
+ assert_nothing_raised do
1409
+ res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
1410
+ :joins => "p, comments co",
1411
+ :select => "p.id",
1412
+ :distinct => true)
1413
+ end
1414
+ assert_equal res6, res7
1415
+ end
1416
+ end
1417
+
1418
+ def test_clear_association_cache_stored
1419
+ firm = Firm.find(1)
1420
+ assert_kind_of Firm, firm
1421
+
1422
+ firm.clear_association_cache
1423
+ assert_equal Firm.find(1).clients.collect{ |x| x.name }.sort, firm.clients.collect{ |x| x.name }.sort
1424
+ end
1425
+
1426
+ def test_clear_association_cache_new_record
1427
+ firm = Firm.new
1428
+ client_stored = Client.find(3)
1429
+ client_new = Client.new
1430
+ client_new.name = "The Joneses"
1431
+ clients = [ client_stored, client_new ]
1432
+
1433
+ firm.clients << clients
1434
+ assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
1435
+
1436
+ firm.clear_association_cache
1437
+ assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
1438
+ end
1439
+
1440
+ def test_interpolate_sql
1441
+ assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo@bar') }
1442
+ assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar) baz') }
1443
+ assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar} baz') }
1444
+ end
1445
+
1446
+ def test_scoped_find_conditions
1447
+ scoped_developers = Developer.with_scope(:find => { :conditions => 'salary > 90000' }) do
1448
+ Developer.find(:all, :conditions => 'id < 5')
1449
+ end
1450
+ assert !scoped_developers.include?(developers(:david)) # David's salary is less than 90,000
1451
+ assert_equal 3, scoped_developers.size
1452
+ end
1453
+
1454
+ def test_scoped_find_limit_offset
1455
+ scoped_developers = Developer.with_scope(:find => { :limit => 3, :offset => 2 }) do
1456
+ Developer.find(:all, :order => 'id')
1457
+ end
1458
+ assert !scoped_developers.include?(developers(:david))
1459
+ assert !scoped_developers.include?(developers(:jamis))
1460
+ assert_equal 3, scoped_developers.size
1461
+
1462
+ # Test without scoped find conditions to ensure we get the whole thing
1463
+ developers = Developer.find(:all, :order => 'id')
1464
+ assert_equal Developer.count, developers.size
1465
+ end
1466
+
1467
+ def test_scoped_find_order
1468
+ # Test order in scope
1469
+ scoped_developers = Developer.with_scope(:find => { :limit => 1, :order => 'salary DESC' }) do
1470
+ Developer.find(:all)
1471
+ end
1472
+ assert_equal 'Jamis', scoped_developers.first.name
1473
+ assert scoped_developers.include?(developers(:jamis))
1474
+ # Test scope without order and order in find
1475
+ scoped_developers = Developer.with_scope(:find => { :limit => 1 }) do
1476
+ Developer.find(:all, :order => 'salary DESC')
1477
+ end
1478
+ # Test scope order + find order, find has priority
1479
+ scoped_developers = Developer.with_scope(:find => { :limit => 3, :order => 'id DESC' }) do
1480
+ Developer.find(:all, :order => 'salary ASC')
1481
+ end
1482
+ assert scoped_developers.include?(developers(:poor_jamis))
1483
+ assert scoped_developers.include?(developers(:david))
1484
+ assert scoped_developers.include?(developers(:dev_10))
1485
+ # Test without scoped find conditions to ensure we get the right thing
1486
+ developers = Developer.find(:all, :order => 'id', :limit => 1)
1487
+ assert scoped_developers.include?(developers(:david))
1488
+ end
1489
+
1490
+ def test_scoped_find_limit_offset_including_has_many_association
1491
+ topics = Topic.with_scope(:find => {:limit => 1, :offset => 1, :include => :replies}) do
1492
+ Topic.find(:all, :order => "topics.id")
1493
+ end
1494
+ assert_equal 1, topics.size
1495
+ assert_equal 2, topics.first.id
1496
+ end
1497
+
1498
+ def test_scoped_find_order_including_has_many_association
1499
+ developers = Developer.with_scope(:find => { :order => 'developers.salary DESC', :include => :projects }) do
1500
+ Developer.find(:all)
1501
+ end
1502
+ assert developers.size >= 2
1503
+ for i in 1...developers.size
1504
+ assert developers[i-1].salary >= developers[i].salary
1505
+ end
1506
+ end
1507
+
1508
+ def test_abstract_class
1509
+ assert !ActiveRecord::Base.abstract_class?
1510
+ assert LoosePerson.abstract_class?
1511
+ assert !LooseDescendant.abstract_class?
1512
+ end
1513
+
1514
+ def test_base_class
1515
+ assert_equal LoosePerson, LoosePerson.base_class
1516
+ assert_equal LooseDescendant, LooseDescendant.base_class
1517
+ assert_equal TightPerson, TightPerson.base_class
1518
+ assert_equal TightPerson, TightDescendant.base_class
1519
+
1520
+ assert_equal Post, Post.base_class
1521
+ assert_equal Post, SpecialPost.base_class
1522
+ assert_equal Post, StiPost.base_class
1523
+ assert_equal SubStiPost, SubStiPost.base_class
1524
+ end
1525
+
1526
+ def test_descends_from_active_record
1527
+ # Tries to call Object.abstract_class?
1528
+ assert_raise(NoMethodError) do
1529
+ ActiveRecord::Base.descends_from_active_record?
1530
+ end
1531
+
1532
+ # Abstract subclass of AR::Base.
1533
+ assert LoosePerson.descends_from_active_record?
1534
+
1535
+ # Concrete subclass of an abstract class.
1536
+ assert LooseDescendant.descends_from_active_record?
1537
+
1538
+ # Concrete subclass of AR::Base.
1539
+ assert TightPerson.descends_from_active_record?
1540
+
1541
+ # Concrete subclass of a concrete class but has no type column.
1542
+ assert TightDescendant.descends_from_active_record?
1543
+
1544
+ # Concrete subclass of AR::Base.
1545
+ assert Post.descends_from_active_record?
1546
+
1547
+ # Abstract subclass of a concrete class which has a type column.
1548
+ # This is pathological, as you'll never have Sub < Abstract < Concrete.
1549
+ assert !StiPost.descends_from_active_record?
1550
+
1551
+ # Concrete subclasses an abstract class which has a type column.
1552
+ assert !SubStiPost.descends_from_active_record?
1553
+ end
1554
+
1555
+ def test_find_on_abstract_base_class_doesnt_use_type_condition
1556
+ old_class = LooseDescendant
1557
+ Object.send :remove_const, :LooseDescendant
1558
+
1559
+ descendant = old_class.create!
1560
+ assert_not_nil LoosePerson.find(descendant.id), "Should have found instance of LooseDescendant when finding abstract LoosePerson: #{descendant.inspect}"
1561
+ ensure
1562
+ unless Object.const_defined?(:LooseDescendant)
1563
+ Object.const_set :LooseDescendant, old_class
1564
+ end
1565
+ end
1566
+
1567
+ def test_assert_queries
1568
+ query = lambda { ActiveRecord::Base.connection.execute 'select count(*) from developers' }
1569
+ assert_queries(2) { 2.times { query.call } }
1570
+ assert_queries 1, &query
1571
+ assert_no_queries { assert true }
1572
+ end
1573
+
1574
+ def test_to_xml
1575
+ xml = REXML::Document.new(topics(:first).to_xml(:indent => 0))
1576
+ bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema
1577
+ written_on_in_current_timezone = topics(:first).written_on.xmlschema
1578
+ last_read_in_current_timezone = topics(:first).last_read.xmlschema
1579
+
1580
+ assert_equal "topic", xml.root.name
1581
+ assert_equal "The First Topic" , xml.elements["//title"].text
1582
+ assert_equal "David" , xml.elements["//author-name"].text
1583
+
1584
+ assert_equal "1", xml.elements["//id"].text
1585
+ assert_equal "integer" , xml.elements["//id"].attributes['type']
1586
+
1587
+ assert_equal "1", xml.elements["//replies-count"].text
1588
+ assert_equal "integer" , xml.elements["//replies-count"].attributes['type']
1589
+
1590
+ assert_equal written_on_in_current_timezone, xml.elements["//written-on"].text
1591
+ assert_equal "datetime" , xml.elements["//written-on"].attributes['type']
1592
+
1593
+ assert_equal "--- Have a nice day\n" , xml.elements["//content"].text
1594
+ assert_equal "yaml" , xml.elements["//content"].attributes['type']
1595
+
1596
+ assert_equal "david@loudthinking.com", xml.elements["//author-email-address"].text
1597
+
1598
+ assert_equal nil, xml.elements["//parent-id"].text
1599
+ assert_equal "integer", xml.elements["//parent-id"].attributes['type']
1600
+ assert_equal "true", xml.elements["//parent-id"].attributes['nil']
1601
+
1602
+ if current_adapter?(:SybaseAdapter, :SQLServerAdapter, :OracleAdapter)
1603
+ assert_equal last_read_in_current_timezone, xml.elements["//last-read"].text
1604
+ assert_equal "datetime" , xml.elements["//last-read"].attributes['type']
1605
+ else
1606
+ assert_equal "2004-04-15", xml.elements["//last-read"].text
1607
+ assert_equal "date" , xml.elements["//last-read"].attributes['type']
1608
+ end
1609
+
1610
+ # Oracle and DB2 don't have true boolean or time-only fields
1611
+ unless current_adapter?(:OracleAdapter, :DB2Adapter)
1612
+ assert_equal "false", xml.elements["//approved"].text
1613
+ assert_equal "boolean" , xml.elements["//approved"].attributes['type']
1614
+
1615
+ assert_equal bonus_time_in_current_timezone, xml.elements["//bonus-time"].text
1616
+ assert_equal "datetime" , xml.elements["//bonus-time"].attributes['type']
1617
+ end
1618
+ end
1619
+
1620
+ def test_to_xml_skipping_attributes
1621
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :replies_count])
1622
+ assert_equal "<topic>", xml.first(7)
1623
+ assert !xml.include?(%(<title>The First Topic</title>))
1624
+ assert xml.include?(%(<author-name>David</author-name>))
1625
+
1626
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :author_name, :replies_count])
1627
+ assert !xml.include?(%(<title>The First Topic</title>))
1628
+ assert !xml.include?(%(<author-name>David</author-name>))
1629
+ end
1630
+
1631
+ def test_to_xml_including_has_many_association
1632
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count)
1633
+ assert_equal "<topic>", xml.first(7)
1634
+ assert xml.include?(%(<replies type="array"><reply>))
1635
+ assert xml.include?(%(<title>The Second Topic's of the day</title>))
1636
+ end
1637
+
1638
+ def test_array_to_xml_including_has_many_association
1639
+ xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :include => :replies)
1640
+ assert xml.include?(%(<replies type="array"><reply>))
1641
+ end
1642
+
1643
+ def test_array_to_xml_including_methods
1644
+ xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :methods => [ :topic_id ])
1645
+ assert xml.include?(%(<topic-id type="integer">#{topics(:first).topic_id}</topic-id>)), xml
1646
+ assert xml.include?(%(<topic-id type="integer">#{topics(:second).topic_id}</topic-id>)), xml
1647
+ end
1648
+
1649
+ def test_array_to_xml_including_has_one_association
1650
+ xml = [ companies(:first_firm), companies(:rails_core) ].to_xml(:indent => 0, :skip_instruct => true, :include => :account)
1651
+ assert xml.include?(companies(:first_firm).account.to_xml(:indent => 0, :skip_instruct => true))
1652
+ assert xml.include?(companies(:rails_core).account.to_xml(:indent => 0, :skip_instruct => true))
1653
+ end
1654
+
1655
+ def test_array_to_xml_including_belongs_to_association
1656
+ xml = [ companies(:first_client), companies(:second_client), companies(:another_client) ].to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
1657
+ assert xml.include?(companies(:first_client).to_xml(:indent => 0, :skip_instruct => true))
1658
+ assert xml.include?(companies(:second_client).firm.to_xml(:indent => 0, :skip_instruct => true))
1659
+ assert xml.include?(companies(:another_client).firm.to_xml(:indent => 0, :skip_instruct => true))
1660
+ end
1661
+
1662
+ def test_to_xml_including_belongs_to_association
1663
+ xml = companies(:first_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
1664
+ assert !xml.include?("<firm>")
1665
+
1666
+ xml = companies(:second_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
1667
+ assert xml.include?("<firm>")
1668
+ end
1669
+
1670
+ def test_to_xml_including_multiple_associations
1671
+ xml = companies(:first_firm).to_xml(:indent => 0, :skip_instruct => true, :include => [ :clients, :account ])
1672
+ assert_equal "<firm>", xml.first(6)
1673
+ assert xml.include?(%(<account>))
1674
+ assert xml.include?(%(<clients type="array"><client>))
1675
+ end
1676
+
1677
+ def test_to_xml_including_multiple_associations_with_options
1678
+ xml = companies(:first_firm).to_xml(
1679
+ :indent => 0, :skip_instruct => true,
1680
+ :include => { :clients => { :only => :name } }
1681
+ )
1682
+
1683
+ assert_equal "<firm>", xml.first(6)
1684
+ assert xml.include?(%(<client><name>Summit</name></client>))
1685
+ assert xml.include?(%(<clients type="array"><client>))
1686
+ end
1687
+
1688
+ def test_to_xml_including_methods
1689
+ xml = Company.new.to_xml(:methods => :arbitrary_method, :skip_instruct => true)
1690
+ assert_equal "<company>", xml.first(9)
1691
+ assert xml.include?(%(<arbitrary-method>I am Jack's profound disappointment</arbitrary-method>))
1692
+ end
1693
+
1694
+ def test_to_xml_with_block
1695
+ value = "Rockin' the block"
1696
+ xml = Company.new.to_xml(:skip_instruct => true) do |xml|
1697
+ xml.tag! "arbitrary-element", value
1698
+ end
1699
+ assert_equal "<company>", xml.first(9)
1700
+ assert xml.include?(%(<arbitrary-element>#{value}</arbitrary-element>))
1701
+ end
1702
+
1703
+ def test_except_attributes
1704
+ assert_equal(
1705
+ %w( author_name type id approved replies_count bonus_time written_on content author_email_address parent_id last_read),
1706
+ topics(:first).attributes(:except => :title).keys
1707
+ )
1708
+
1709
+ assert_equal(
1710
+ %w( replies_count bonus_time written_on content author_email_address parent_id last_read),
1711
+ topics(:first).attributes(:except => [ :title, :id, :type, :approved, :author_name ]).keys
1712
+ )
1713
+ end
1714
+
1715
+ def test_include_attributes
1716
+ assert_equal(%w( title ), topics(:first).attributes(:only => :title).keys)
1717
+ assert_equal(%w( title author_name type id approved ), topics(:first).attributes(:only => [ :title, :id, :type, :approved, :author_name ]).keys)
1718
+ end
1719
+
1720
+ def test_type_name_with_module_should_handle_beginning
1721
+ assert_equal 'ActiveRecord::Person', ActiveRecord::Base.send(:type_name_with_module, 'Person')
1722
+ assert_equal '::Person', ActiveRecord::Base.send(:type_name_with_module, '::Person')
1723
+ end
1724
+
1725
+ def test_to_param_should_return_string
1726
+ assert_kind_of String, Client.find(:first).to_param
1727
+ end
1728
+
1729
+ def test_inspect_class
1730
+ assert_equal 'ActiveRecord::Base', ActiveRecord::Base.inspect
1731
+ assert_equal 'LoosePerson(abstract)', LoosePerson.inspect
1732
+ assert_match(/^Topic\(id: integer, title: string/, Topic.inspect)
1733
+ end
1734
+
1735
+ def test_inspect_instance
1736
+ topic = topics(:first)
1737
+ assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david@loudthinking.com", written_on: "#{topic.written_on.to_s(:db)}", bonus_time: "#{topic.bonus_time.to_s(:db)}", last_read: "#{topic.last_read.to_s(:db)}", content: "Have a nice day", approved: false, replies_count: 1, parent_id: nil, type: nil>), topic.inspect
1738
+ end
1739
+
1740
+ def test_inspect_new_instance
1741
+ assert_match /Topic id: nil/, Topic.new.inspect
1742
+ end
1743
+
1744
+ def test_inspect_limited_select_instance
1745
+ assert_equal %(#<Topic id: 1>), Topic.find(:first, :select => 'id', :conditions => 'id = 1').inspect
1746
+ assert_equal %(#<Topic id: 1, title: "The First Topic">), Topic.find(:first, :select => 'id, title', :conditions => 'id = 1').inspect
1747
+ end
1748
+
1749
+ def test_inspect_class_without_table
1750
+ assert_equal "NonExistentTable(Table doesn't exist)", NonExistentTable.inspect
1751
+ end
1752
+
1753
+ def test_attribute_for_inspect
1754
+ t = topics(:first)
1755
+ t.title = "The First Topic Now Has A Title With\nNewlines And More Than 50 Characters"
1756
+
1757
+ assert_equal %("#{t.written_on.to_s(:db)}"), t.attribute_for_inspect(:written_on)
1758
+ assert_equal '"The First Topic Now Has A Title With\nNewlines And M..."', t.attribute_for_inspect(:title)
1759
+ end
1760
+
1761
+ def test_becomes
1762
+ assert_kind_of Reply, topics(:first).becomes(Reply)
1763
+ assert_equal "The First Topic", topics(:first).becomes(Reply).title
1764
+ end
1765
+ end