odbc-rails 1.2
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.
- data/AUTHORS +16 -0
- data/COPYING +21 -0
- data/ChangeLog +89 -0
- data/LICENSE +5 -0
- data/NEWS +12 -0
- data/README +282 -0
- data/lib/active_record/connection_adapters/odbc_adapter.rb +1792 -0
- data/lib/active_record/vendor/odbcext_db2.rb +87 -0
- data/lib/active_record/vendor/odbcext_informix.rb +132 -0
- data/lib/active_record/vendor/odbcext_informix_col.rb +45 -0
- data/lib/active_record/vendor/odbcext_ingres.rb +156 -0
- data/lib/active_record/vendor/odbcext_microsoftsqlserver.rb +185 -0
- data/lib/active_record/vendor/odbcext_microsoftsqlserver_col.rb +40 -0
- data/lib/active_record/vendor/odbcext_mysql.rb +136 -0
- data/lib/active_record/vendor/odbcext_oracle.rb +220 -0
- data/lib/active_record/vendor/odbcext_postgresql.rb +179 -0
- data/lib/active_record/vendor/odbcext_progress.rb +139 -0
- data/lib/active_record/vendor/odbcext_progress89.rb +259 -0
- data/lib/active_record/vendor/odbcext_sybase.rb +212 -0
- data/lib/active_record/vendor/odbcext_sybase_col.rb +49 -0
- data/lib/active_record/vendor/odbcext_virtuoso.rb +146 -0
- data/lib/odbc_adapter.rb +28 -0
- data/support/lib/active_record/connection_adapters/abstract/schema_definitions.rb +259 -0
- data/support/odbc_rails.diff +707 -0
- data/support/pack_odbc.rb +119 -0
- data/support/rake/rails_plugin_package_task.rb +212 -0
- data/support/test/base_test.rb +1349 -0
- data/support/test/migration_test.rb +566 -0
- data/test/connections/native_odbc/connection.rb +95 -0
- data/test/fixtures/db_definitions/db2.drop.sql +30 -0
- data/test/fixtures/db_definitions/db2.sql +217 -0
- data/test/fixtures/db_definitions/db22.drop.sql +2 -0
- data/test/fixtures/db_definitions/db22.sql +5 -0
- data/test/fixtures/db_definitions/informix.drop.sql +30 -0
- data/test/fixtures/db_definitions/informix.sql +205 -0
- data/test/fixtures/db_definitions/informix2.drop.sql +2 -0
- data/test/fixtures/db_definitions/informix2.sql +5 -0
- data/test/fixtures/db_definitions/ingres.drop.sql +62 -0
- data/test/fixtures/db_definitions/ingres.sql +232 -0
- data/test/fixtures/db_definitions/ingres2.drop.sql +2 -0
- data/test/fixtures/db_definitions/ingres2.sql +5 -0
- data/test/fixtures/db_definitions/mysql.drop.sql +30 -0
- data/test/fixtures/db_definitions/mysql.sql +219 -0
- data/test/fixtures/db_definitions/mysql2.drop.sql +2 -0
- data/test/fixtures/db_definitions/mysql2.sql +5 -0
- data/test/fixtures/db_definitions/oracle_odbc.drop.sql +64 -0
- data/test/fixtures/db_definitions/oracle_odbc.sql +257 -0
- data/test/fixtures/db_definitions/oracle_odbc2.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle_odbc2.sql +6 -0
- data/test/fixtures/db_definitions/progress.drop.sql +61 -0
- data/test/fixtures/db_definitions/progress.sql +234 -0
- data/test/fixtures/db_definitions/progress2.drop.sql +2 -0
- data/test/fixtures/db_definitions/progress2.sql +6 -0
- data/test/fixtures/db_definitions/sqlserver.drop.sql +30 -0
- data/test/fixtures/db_definitions/sqlserver.sql +203 -0
- data/test/fixtures/db_definitions/sqlserver2.drop.sql +2 -0
- data/test/fixtures/db_definitions/sqlserver2.sql +5 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +31 -0
- data/test/fixtures/db_definitions/sybase.sql +204 -0
- data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
- data/test/fixtures/db_definitions/sybase2.sql +5 -0
- data/test/fixtures/db_definitions/virtuoso.drop.sql +30 -0
- data/test/fixtures/db_definitions/virtuoso.sql +200 -0
- data/test/fixtures/db_definitions/virtuoso2.drop.sql +2 -0
- data/test/fixtures/db_definitions/virtuoso2.sql +5 -0
- 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(" <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> "
|
201
|
+
end
|
202
|
+
elsif (@extra_links.class==Array)
|
203
|
+
@extra_links.each do |link|
|
204
|
+
x_links << "<a href=\"#{link}\">#{link}</a> "
|
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
|