mirror of
https://github.com/github/rails.git
synced 2026-01-13 08:38:05 -05:00
Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93bd3b00e1 | ||
|
|
57e992dc0f | ||
|
|
a9f4205b21 | ||
|
|
8a19d148b7 | ||
|
|
bd9b27df8b | ||
|
|
c6120acc00 | ||
|
|
60d68aab53 | ||
|
|
67cbb0f04c | ||
|
|
0bb6521649 | ||
|
|
966b027c71 | ||
|
|
2c5d342809 | ||
|
|
d45ca84858 | ||
|
|
142304b79d | ||
|
|
2deefbade0 | ||
|
|
66cd1330a5 | ||
|
|
cf656ec1f7 | ||
|
|
1c86c6f4d9 | ||
|
|
14c2334c84 | ||
|
|
50c5b2817f | ||
|
|
786726692f | ||
|
|
b762e0141c | ||
|
|
6865b66621 | ||
|
|
1dc4783707 | ||
|
|
98811479f0 | ||
|
|
2b4dd8c6e7 | ||
|
|
3fcec37473 | ||
|
|
15839d56d0 | ||
|
|
9c05146f4b | ||
|
|
a766ae6d84 | ||
|
|
cc4deb1610 | ||
|
|
b87294c671 | ||
|
|
7d6e3d0857 | ||
|
|
4906b32450 | ||
|
|
541da098f0 | ||
|
|
7bf86f2c57 | ||
|
|
81f3a923d7 | ||
|
|
4a9847f987 | ||
|
|
743fb60a8a | ||
|
|
6329724293 | ||
|
|
78e95faec5 | ||
|
|
371fa97ccc | ||
|
|
1aac21ca24 | ||
|
|
54977e0364 | ||
|
|
3f2541b4ff | ||
|
|
bed4fec60e | ||
|
|
fe09fa278e | ||
|
|
d4039f5246 | ||
|
|
b8ea04f8e8 | ||
|
|
beddd9680e | ||
|
|
dcaa871695 | ||
|
|
9796a587b8 | ||
|
|
de5980289c | ||
|
|
0023cb1b8c | ||
|
|
37664eb90c | ||
|
|
d212f03093 | ||
|
|
afcd3ebcbb | ||
|
|
b35d05f594 | ||
|
|
1ec7fb609d | ||
|
|
113c815c6a | ||
|
|
9decb1a46f | ||
|
|
49de64e0a4 | ||
|
|
58c7f2a744 | ||
|
|
e24096d616 | ||
|
|
902cb21124 | ||
|
|
e01febbe68 | ||
|
|
364f6d645a | ||
|
|
971b40f59f | ||
|
|
af0ad4d50b | ||
|
|
f0b5630dce | ||
|
|
532d4e8782 | ||
|
|
b970d2055a | ||
|
|
9403f471b4 | ||
|
|
17836f4819 | ||
|
|
e823ef8336 | ||
|
|
ce6545e574 | ||
|
|
e8086859c5 | ||
|
|
e0ef631055 |
@@ -1,3 +1,8 @@
|
||||
*1.1.4* (December 7th, 2005)
|
||||
|
||||
* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
|
||||
|
||||
|
||||
*1.1.3* (November 7th, 2005)
|
||||
|
||||
* Allow Mailers to have custom initialize methods that set default instance variables for all mail actions #2563 [mrj@bigpond.net.au]
|
||||
|
||||
@@ -9,7 +9,7 @@ require File.join(File.dirname(__FILE__), 'lib', 'action_mailer', 'version')
|
||||
|
||||
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
||||
PKG_NAME = 'actionmailer'
|
||||
PKG_VERSION = ActionMailer::Version::STRING + PKG_BUILD
|
||||
PKG_VERSION = ActionMailer::VERSION::STRING + PKG_BUILD
|
||||
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
||||
|
||||
RELEASE_NAME = "REL #{PKG_VERSION}"
|
||||
@@ -53,7 +53,7 @@ spec = Gem::Specification.new do |s|
|
||||
s.rubyforge_project = "actionmailer"
|
||||
s.homepage = "http://www.rubyonrails.org"
|
||||
|
||||
s.add_dependency('actionpack', '= 1.11.0' + PKG_BUILD)
|
||||
s.add_dependency('actionpack', '= 1.11.1' + PKG_BUILD)
|
||||
|
||||
s.has_rdoc = true
|
||||
s.requirements << 'none'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
module ActionMailer
|
||||
module Version #:nodoc:
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 1
|
||||
MINOR = 1
|
||||
TINY = 3
|
||||
TINY = 4
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
||||
@@ -1,4 +1,34 @@
|
||||
*SVN*
|
||||
*1.11.1* (December 7th, 2005)
|
||||
|
||||
* More robust relative url root discovery for SCGI compatibility. This solves the 'SCGI routes problem' -- you no longer need to prefix all your routes with the name of the SCGI mountpoint. #3070 [Dave Ringoen]
|
||||
|
||||
* Fix docs for text_area_tag. #3083. [Christopher Cotton]
|
||||
|
||||
* Make ActionController's render honor the :locals option when rendering a :file. #1665. [Emanuel Borsboom, Marcel Molina Jr.]
|
||||
|
||||
* Don't put flash in session if sessions are disabled. [Jeremy Kemper]
|
||||
|
||||
* Strip out trailing &_= for raw post bodies. Closes #2868. [Sam Stephenson]
|
||||
|
||||
* Correct docs for automatic layout assignment. #2610. [Charles M. Gerungan]
|
||||
|
||||
* Always create new AR sessions rather than trying too hard to avoid database traffic. #2731 [Jeremy Kemper]
|
||||
|
||||
* Update to Prototype 1.4.0_rc4. Closes #2943 (old Array.prototype.reverse behavior can be obtained by passing false as an argument). [Sam Stephenson]
|
||||
|
||||
* Update to Prototype 1.4.0_rc3. Closes #1893, #2505, #2550, #2748, #2783. [Sam Stephenson]
|
||||
|
||||
* Updated docs for in_place_editor, fixes a couple bugs and offers extended support for external controls [Justin Palmer]
|
||||
|
||||
* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
|
||||
|
||||
* Remove the unused, slow response_dump and session_dump variables from error pages. #1222 [lmarlow@yahoo.com]
|
||||
|
||||
* Update to latest script.aculo.us version (as of [3031])
|
||||
|
||||
* Update documentation for render :file. #2858 [Tom Werner]
|
||||
|
||||
* Only include builtin filters whose filenames match /^[a-z][a-z_]*_helper.rb$/ to avoid including operating system metadata such as ._foo_helper.rb. #2855 [court3nay@gmail.com]
|
||||
|
||||
* options_for_select allows any objects which respond_to? :first and :last rather than restricting to Array and Range. #2824 [Jacob Robbins <jrobbins@cmj.com>, Jeremy Kemper]
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ require File.join(File.dirname(__FILE__), 'lib', 'action_pack', 'version')
|
||||
|
||||
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
||||
PKG_NAME = 'actionpack'
|
||||
PKG_VERSION = ActionPack::Version::STRING + PKG_BUILD
|
||||
PKG_VERSION = ActionPack::VERSION::STRING + PKG_BUILD
|
||||
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
||||
|
||||
RELEASE_NAME = "REL #{PKG_VERSION}"
|
||||
@@ -62,7 +62,7 @@ spec = Gem::Specification.new do |s|
|
||||
s.has_rdoc = true
|
||||
s.requirements << 'none'
|
||||
|
||||
s.add_dependency('activesupport', '= 1.2.3' + PKG_BUILD)
|
||||
s.add_dependency('activesupport', '= 1.2.4' + PKG_BUILD)
|
||||
|
||||
s.require_path = 'lib'
|
||||
s.autorequire = 'action_controller'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
require 'test/unit'
|
||||
require 'test/unit/assertions'
|
||||
require 'rexml/document'
|
||||
require "#{File.dirname(__FILE__)}/vendor/html-scanner/html/document"
|
||||
require File.dirname(__FILE__) + "/vendor/html-scanner/html/document"
|
||||
|
||||
module Test #:nodoc:
|
||||
module Unit #:nodoc:
|
||||
|
||||
@@ -459,9 +459,12 @@ module ActionController #:nodoc:
|
||||
self.class.controller_name
|
||||
end
|
||||
|
||||
def session_enabled?
|
||||
request.session_options[:disabled] != false
|
||||
end
|
||||
|
||||
protected
|
||||
# Renders the content that'll be returned to the browser as the response body. This can just be as regular text, but is
|
||||
# more often the compilation of a template.
|
||||
# Renders the content that will be returned to the browser as the response body.
|
||||
#
|
||||
# === Rendering an action
|
||||
#
|
||||
@@ -521,14 +524,19 @@ module ActionController #:nodoc:
|
||||
#
|
||||
# === Rendering a file
|
||||
#
|
||||
# File rendering works just like action rendering except that it takes an absolute path.
|
||||
# The current layout is not applied automatically.
|
||||
# File rendering works just like action rendering except that it takes a filesystem path. By default, the path
|
||||
# is assumed to be absolute, and the current layout is not applied.
|
||||
#
|
||||
# # Renders the template located in /path/to/some/template.r(html|xml)
|
||||
# render :file => "/path/to/some/template"
|
||||
# # Renders the template located at the absolute filesystem path
|
||||
# render :file => "/path/to/some/template.rhtml"
|
||||
# render :file => "c:/path/to/some/template.rhtml"
|
||||
#
|
||||
# # Renders the same template within the current layout, but with a 404 status code
|
||||
# render :file => "/path/to/some/template", :layout => true, :status => 404
|
||||
# # Renders a template within the current layout, and with a 404 status code
|
||||
# render :file => "/path/to/some/template.rhtml", :layout => true, :status => 404
|
||||
# render :file => "c:/path/to/some/template.rhtml", :layout => true, :status => 404
|
||||
#
|
||||
# # Renders a template relative to the template root and chooses the proper file extension
|
||||
# render :file => "some/template", :use_full_path => true
|
||||
#
|
||||
# _Deprecation_ _notice_: This used to have the signature <tt>render_file(path, status = 200)</tt>
|
||||
#
|
||||
@@ -592,7 +600,7 @@ module ActionController #:nodoc:
|
||||
|
||||
else
|
||||
if file = options[:file]
|
||||
render_file(file, options[:status], options[:use_full_path])
|
||||
render_file(file, options[:status], options[:use_full_path], options[:locals] || {})
|
||||
|
||||
elsif template = options[:template]
|
||||
render_file(template, options[:status], true)
|
||||
@@ -640,11 +648,11 @@ module ActionController #:nodoc:
|
||||
end
|
||||
end
|
||||
|
||||
def render_file(template_path, status = nil, use_full_path = false)
|
||||
def render_file(template_path, status = nil, use_full_path = false, locals = {})
|
||||
add_variables_to_assigns
|
||||
assert_existance_of_template_file(template_path) if use_full_path
|
||||
logger.info("Rendering #{template_path}" + (status ? " (#{status})" : '')) if logger
|
||||
render_text(@template.render_file(template_path, use_full_path), status)
|
||||
render_text(@template.render_file(template_path, use_full_path, locals), status)
|
||||
end
|
||||
|
||||
def render_template(template, status = nil, type = :rhtml, local_assigns = {})
|
||||
|
||||
@@ -236,6 +236,7 @@ module ActionController #:nodoc:
|
||||
# * DRbStore: Keeps the fragments in the memory of a separate, shared DRb process. This works for all environments and only keeps one cache
|
||||
# around for all processes, but requires that you run and manage a separate DRb process.
|
||||
# * MemCacheStore: Works like DRbStore, but uses Danga's MemCache instead.
|
||||
# Requires the ruby-memcache library: gem install ruby-memcache.
|
||||
#
|
||||
# Configuration examples (MemoryStore is the default):
|
||||
#
|
||||
|
||||
@@ -54,6 +54,7 @@ class CGI #:nodoc:
|
||||
content = stdinput.read(Integer(env_table['CONTENT_LENGTH'])) || ''
|
||||
# fix for Safari Ajax postings that always append \000
|
||||
content.chop! if content[-1] == 0
|
||||
content.gsub! /&_=$/, ''
|
||||
env_table['RAW_POST_DATA'] = content.freeze
|
||||
end
|
||||
|
||||
|
||||
@@ -132,22 +132,31 @@ module ActionController #:nodoc:
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
protected
|
||||
# Access the contents of the flash. Use <tt>flash["notice"]</tt> to read a notice you put there or
|
||||
# <tt>flash["notice"] = "hello"</tt> to put a new one.
|
||||
# Note that if sessions are disabled only flash.now will work.
|
||||
def flash #:doc:
|
||||
@session['flash'] ||= FlashHash.new
|
||||
# @session = Hash.new if sessions are disabled
|
||||
if @session.is_a?(Hash)
|
||||
@__flash ||= FlashHash.new
|
||||
|
||||
# otherwise, @session is a CGI::Session or a TestSession
|
||||
else
|
||||
@session['flash'] ||= FlashHash.new
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# deprecated. use <tt>flash.keep</tt> instead
|
||||
def keep_flash #:doc:
|
||||
warn 'keep_flash is deprecated; use flash.keep instead.'
|
||||
flash.keep
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
|
||||
private
|
||||
|
||||
# marks flash entries as used and expose the flash to the view
|
||||
def fire_flash
|
||||
|
||||
@@ -66,7 +66,9 @@ module ActionController #:nodoc:
|
||||
# <tt>app/views/layouts/weblog.rhtml</tt> or <tt>app/views/layouts/weblog.rxml</tt> exists then it will be automatically set as
|
||||
# the layout for your WeblogController. You can create a layout with the name <tt>application.rhtml</tt> or <tt>application.rxml</tt>
|
||||
# and this will be set as the default controller if there is no layout with the same name as the current controller and there is
|
||||
# no layout explicitly assigned with the +layout+ method. Setting a layout explicitly will always override the automatic behaviour.
|
||||
# no layout explicitly assigned with the +layout+ method. Setting a layout explicitly will always override the automatic behaviour
|
||||
# for the controller where the layout is set. Explicitly setting the layout in a parent class, though, will not override the
|
||||
# child class's layout assignement if the child class has a layout with the same name.
|
||||
#
|
||||
# == Inheritance for layouts
|
||||
#
|
||||
|
||||
@@ -14,7 +14,7 @@ module ActionController
|
||||
# end
|
||||
#
|
||||
# # View
|
||||
# <%= in_place_editor_field :post, title %>
|
||||
# <%= in_place_editor_field :post, 'title' %>
|
||||
#
|
||||
# For help on defining an in place editor in the browser,
|
||||
# see ActionView::Helpers::JavaScriptHelper.
|
||||
|
||||
@@ -2,7 +2,7 @@ module ActionController
|
||||
# These methods are available in both the production and test Request objects.
|
||||
class AbstractRequest
|
||||
cattr_accessor :relative_url_root
|
||||
|
||||
|
||||
# Returns both GET and POST parameters in a single hash.
|
||||
def parameters
|
||||
@parameters ||= request_parameters.merge(query_parameters).merge(path_parameters).with_indifferent_access
|
||||
@@ -110,7 +110,7 @@ module ActionController
|
||||
# a different <tt>tld_length</tt>, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk".
|
||||
def domain(tld_length = 1)
|
||||
return nil if !/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/.match(host).nil? or host.nil?
|
||||
|
||||
|
||||
host.split('.').last(1 + tld_length).join('.')
|
||||
end
|
||||
|
||||
@@ -123,13 +123,13 @@ module ActionController
|
||||
parts[0..-(tld_length+2)]
|
||||
end
|
||||
|
||||
# Receive the raw post data.
|
||||
# This is useful for services such as REST, XMLRPC and SOAP
|
||||
# which communicate over HTTP POST but don't use the traditional parameter format.
|
||||
# Receive the raw post data.
|
||||
# This is useful for services such as REST, XMLRPC and SOAP
|
||||
# which communicate over HTTP POST but don't use the traditional parameter format.
|
||||
def raw_post
|
||||
env['RAW_POST_DATA']
|
||||
end
|
||||
|
||||
|
||||
# Returns the request URI correctly, taking into account the idiosyncracies
|
||||
# of the various servers.
|
||||
def request_uri
|
||||
@@ -153,9 +153,9 @@ module ActionController
|
||||
|
||||
# Is this an SSL request?
|
||||
def ssl?
|
||||
env['HTTPS'] == 'on'
|
||||
env['HTTPS'] == 'on'
|
||||
end
|
||||
|
||||
|
||||
# Returns the interpreted path to requested resource after all the installation directory of this application was taken into account
|
||||
def path
|
||||
path = (uri = request_uri) ? uri.split('?').first : ''
|
||||
@@ -165,18 +165,19 @@ module ActionController
|
||||
path[0, root.length] = '' if root
|
||||
path || ''
|
||||
end
|
||||
|
||||
|
||||
# Returns the path minus the web server relative installation directory.
|
||||
# This method returns nil unless the web server is apache.
|
||||
def relative_url_root
|
||||
@@relative_url_root ||= server_software == 'apache' ? File.dirname(env["SCRIPT_NAME"].to_s).gsub(/(^\.$|^\/$)/, '') : ''
|
||||
@@relative_url_root ||= server_software == 'apache' ? env["SCRIPT_NAME"].to_s.sub(/\/dispatch\.(fcgi|rb|cgi)$/, '') : ''
|
||||
|
||||
end
|
||||
|
||||
# Returns the port number of this request as an integer.
|
||||
def port
|
||||
@port_as_int ||= env['SERVER_PORT'].to_i
|
||||
end
|
||||
|
||||
|
||||
# Returns the standard port number for this request's protocol
|
||||
def standard_port
|
||||
case protocol
|
||||
@@ -196,12 +197,12 @@ module ActionController
|
||||
def host_with_port
|
||||
host + port_string
|
||||
end
|
||||
|
||||
|
||||
def path_parameters=(parameters)
|
||||
@path_parameters = parameters
|
||||
@symbolized_path_parameters = @parameters = nil
|
||||
end
|
||||
|
||||
|
||||
def symbolized_path_parameters
|
||||
@symbolized_path_parameters ||= path_parameters.symbolize_keys
|
||||
end
|
||||
@@ -224,10 +225,13 @@ module ActionController
|
||||
def request_parameters #:nodoc:
|
||||
end
|
||||
|
||||
def env #:nodoc:
|
||||
# Returns the hash of environment variables for this request,
|
||||
# such as { 'RAILS_ENV' => 'production' }.
|
||||
def env
|
||||
end
|
||||
|
||||
def host #:nodoc:
|
||||
# Returns the host for this request, such as example.com.
|
||||
def host
|
||||
end
|
||||
|
||||
def cookies #:nodoc:
|
||||
@@ -237,6 +241,6 @@ module ActionController
|
||||
end
|
||||
|
||||
def reset_session #:nodoc:
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -80,8 +80,8 @@ class CGI
|
||||
find_by_session_id(session_id)
|
||||
end
|
||||
|
||||
def marshal(data) Base64.encode64(Marshal.dump(data)) end
|
||||
def unmarshal(data) Marshal.load(Base64.decode64(data)) end
|
||||
def marshal(data) Base64.encode64(Marshal.dump(data)) if data end
|
||||
def unmarshal(data) Marshal.load(Base64.decode64(data)) if data end
|
||||
|
||||
def create_table!
|
||||
connection.execute <<-end_sql
|
||||
@@ -119,18 +119,12 @@ class CGI
|
||||
|
||||
# Lazy-unmarshal session state.
|
||||
def data
|
||||
unless @data
|
||||
case d = read_attribute(@@data_column_name)
|
||||
when String
|
||||
@data = self.class.unmarshal(d)
|
||||
else
|
||||
@data = d || {}
|
||||
end
|
||||
end
|
||||
@data
|
||||
@data ||= self.class.unmarshal(read_attribute(@@data_column_name)) || {}
|
||||
end
|
||||
|
||||
private
|
||||
attr_writer :data
|
||||
|
||||
def marshal_data!
|
||||
write_attribute(@@data_column_name, self.class.marshal(self.data))
|
||||
end
|
||||
@@ -193,8 +187,8 @@ class CGI
|
||||
end
|
||||
end
|
||||
|
||||
def marshal(data) Base64.encode64(Marshal.dump(data)) end
|
||||
def unmarshal(data) Marshal.load(Base64.decode64(data)) end
|
||||
def marshal(data) Base64.encode64(Marshal.dump(data)) if data end
|
||||
def unmarshal(data) Marshal.load(Base64.decode64(data)) if data end
|
||||
|
||||
def create_table!
|
||||
@@connection.execute <<-end_sql
|
||||
@@ -230,7 +224,7 @@ class CGI
|
||||
def data
|
||||
unless @data
|
||||
if @marshaled_data
|
||||
@data, @marshaled_data = self.class.unmarshal(@marshaled_data), nil
|
||||
@data, @marshaled_data = self.class.unmarshal(@marshaled_data) || {}, nil
|
||||
else
|
||||
@data = {}
|
||||
end
|
||||
@@ -284,6 +278,7 @@ class CGI
|
||||
raise CGI::Session::NoSession, 'uninitialized session'
|
||||
end
|
||||
@session = @@session_class.new(:session_id => session_id, :data => {})
|
||||
@session.save
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -31,8 +31,6 @@
|
||||
request_parameters_without_action.delete("controller")
|
||||
|
||||
request_dump = request_parameters_without_action.inspect.gsub(/,/, ",\n")
|
||||
session_dump = @request.session.instance_variable_get("@data").inspect.gsub(/,/, ",\n")
|
||||
response_dump = @response.inspect.gsub(/,/, ",\n")
|
||||
%>
|
||||
|
||||
<h2 style="margin-top: 30px">Request</h2>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
module ActionPack
|
||||
module Version #:nodoc:
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 1
|
||||
MINOR = 11
|
||||
TINY = 0
|
||||
TINY = 1
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
||||
@@ -157,13 +157,12 @@ module ActionView #:nodoc:
|
||||
|
||||
class ObjectWrapper < Struct.new(:value) #:nodoc:
|
||||
end
|
||||
|
||||
|
||||
def self.load_helpers(helper_dir)#:nodoc:
|
||||
Dir.foreach(helper_dir) do |helper_file|
|
||||
next unless helper_file =~ /_helper.rb$/
|
||||
require helper_dir + helper_file
|
||||
helper_module_name = helper_file.capitalize.gsub(/_([a-z])/) { |m| $1.capitalize }[0..-4]
|
||||
|
||||
next unless helper_file =~ /^([a-z][a-z_]*_helper).rb$/
|
||||
require File.join(helper_dir, $1)
|
||||
helper_module_name = $1.camelize
|
||||
class_eval("include ActionView::Helpers::#{helper_module_name}") if Helpers.const_defined?(helper_module_name)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,7 +35,7 @@ module ActionView
|
||||
compute_public_path(source, 'javascripts', 'js')
|
||||
end
|
||||
|
||||
JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls']
|
||||
JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls'] unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES)
|
||||
@@javascript_default_sources = JAVASCRIPT_DEFAULT_SOURCES.dup
|
||||
|
||||
# Returns a script include tag per source given as argument. Examples:
|
||||
@@ -60,7 +60,7 @@ module ActionView
|
||||
def javascript_include_tag(*sources)
|
||||
options = sources.last.is_a?(Hash) ? sources.pop.stringify_keys : { }
|
||||
if sources.first == :defaults
|
||||
sources = @@javascript_default_sources
|
||||
sources = @@javascript_default_sources.dup
|
||||
if defined?(RAILS_ROOT) and File.exists?("#{RAILS_ROOT}/public/javascripts/application.js")
|
||||
sources << 'application'
|
||||
end
|
||||
|
||||
@@ -95,7 +95,7 @@ module ActionView
|
||||
# Options:
|
||||
# * <tt>:size</tt> - A string specifying the dimensions of the textarea.
|
||||
# # Outputs <textarea name="body" id="body" cols="25" rows="10"></textarea>
|
||||
# <%= text_area_tag "body", nil, :size => 25x10 %>
|
||||
# <%= text_area_tag "body", nil, :size => "25x10" %>
|
||||
def text_area_tag(name, content = nil, options = {})
|
||||
options = options.stringify_keys
|
||||
if options["size"]
|
||||
|
||||
@@ -28,21 +28,23 @@ module ActionView
|
||||
# be sent after the user presses "ok".
|
||||
#
|
||||
# Addtional +options+ are:
|
||||
# <tt>:rows</tt>:: Number of rows (more than 1 will use a TEXTAREA)
|
||||
# <tt>:cancel_text</tt>:: The text on the cancel link. (default: "cancel")
|
||||
# <tt>:ok_text</tt>:: The text on the save link. (default: "ok")
|
||||
# <tt>:options</tt>:: Pass through options to the AJAX call (see prototype's Ajax.Updater)
|
||||
# <tt>:with</tt>:: JavaScript snippet that should return what is to be sent
|
||||
# in the AJAX call, +form+ is an implicit parameter
|
||||
# <tt>:rows</tt>:: Number of rows (more than 1 will use a TEXTAREA)
|
||||
# <tt>:cancel_text</tt>:: The text on the cancel link. (default: "cancel")
|
||||
# <tt>:save_text</tt>:: The text on the save link. (default: "ok")
|
||||
# <tt>:external_control</tt>:: The id of an external control used to enter edit mode.
|
||||
# <tt>:options</tt>:: Pass through options to the AJAX call (see prototype's Ajax.Updater)
|
||||
# <tt>:with</tt>:: JavaScript snippet that should return what is to be sent
|
||||
# in the AJAX call, +form+ is an implicit parameter
|
||||
def in_place_editor(field_id, options = {})
|
||||
function = "new Ajax.InPlaceEditor("
|
||||
function << "'#{field_id}', "
|
||||
function << "'#{url_for(options[:url])}'"
|
||||
|
||||
js_options = {}
|
||||
js_options['cancelText'] = options[:cancel_text] if options[:cancel_text]
|
||||
js_options['okText'] = options[:save_text] if options[:save_text]
|
||||
js_options['cancelText'] = %('#{options[:cancel_text]}') if options[:cancel_text]
|
||||
js_options['okText'] = %('#{options[:save_text]}') if options[:save_text]
|
||||
js_options['rows'] = options[:rows] if options[:rows]
|
||||
js_options['externalControl'] = options[:external_control] if options[:external_control]
|
||||
js_options['ajaxOptions'] = options[:options] if options[:options]
|
||||
js_options['callback'] = "function(form) { return #{options[:with]} }" if options[:with]
|
||||
function << (', ' + options_for_javascript(js_options)) unless js_options.empty?
|
||||
@@ -59,7 +61,7 @@ module ActionView
|
||||
tag = ::ActionView::Helpers::InstanceTag.new(object, method, self)
|
||||
tag_options = {:tag => "span", :id => "#{object}_#{method}_#{tag.object.id}_in_place_editor", :class => "in_place_editor_field"}.merge!(tag_options)
|
||||
in_place_editor_options[:url] = in_place_editor_options[:url] || url_for({ :action => "set_#{object}_#{method}", :id => tag.object.id })
|
||||
tag.to_content_tag(tag_options[:tag], tag_options) +
|
||||
tag.to_content_tag(tag_options.delete(:tag), tag_options) +
|
||||
in_place_editor(tag_options[:id], in_place_editor_options)
|
||||
end
|
||||
|
||||
|
||||
@@ -80,7 +80,10 @@ Autocompleter.Base.prototype = {
|
||||
|
||||
show: function() {
|
||||
if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
|
||||
if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0) && (Element.getStyle(this.update, 'position')=='absolute')) {
|
||||
if(!this.iefix &&
|
||||
(navigator.appVersion.indexOf('MSIE')>0) &&
|
||||
(navigator.userAgent.indexOf('Opera')<0) &&
|
||||
(Element.getStyle(this.update, 'position')=='absolute')) {
|
||||
new Insertion.After(this.update,
|
||||
'<iframe id="' + this.update.id + '_iefix" '+
|
||||
'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
|
||||
@@ -718,4 +721,30 @@ Ajax.InPlaceEditor.prototype = {
|
||||
Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Delayed observer, like Form.Element.Observer,
|
||||
// but waits for delay after last key input
|
||||
// Ideal for live-search fields
|
||||
|
||||
Form.Element.DelayedObserver = Class.create();
|
||||
Form.Element.DelayedObserver.prototype = {
|
||||
initialize: function(element, delay, callback) {
|
||||
this.delay = delay || 0.5;
|
||||
this.element = $(element);
|
||||
this.callback = callback;
|
||||
this.timer = null;
|
||||
this.lastValue = $F(this.element);
|
||||
Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
|
||||
},
|
||||
delayedListener: function(event) {
|
||||
if(this.lastValue == $F(this.element)) return;
|
||||
if(this.timer) clearTimeout(this.timer);
|
||||
this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
|
||||
this.lastValue = $F(this.element);
|
||||
},
|
||||
onTimerEvent: function() {
|
||||
this.timer = null;
|
||||
this.callback(this.element, $F(this.element));
|
||||
}
|
||||
};
|
||||
@@ -1,7 +1,5 @@
|
||||
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||
//
|
||||
// Element.Class part Copyright (c) 2005 by Rick Olson
|
||||
//
|
||||
// See scriptaculous.js for full license.
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
@@ -31,6 +29,8 @@ var Droppables = {
|
||||
options._containers.push($(containment));
|
||||
}
|
||||
}
|
||||
|
||||
if(options.accept) options.accept = [options.accept].flatten();
|
||||
|
||||
Element.makePositioned(element); // fix IE
|
||||
options.element = element;
|
||||
@@ -49,20 +49,21 @@ var Droppables = {
|
||||
((!drop._containers) ||
|
||||
this.isContained(element, drop)) &&
|
||||
((!drop.accept) ||
|
||||
(Element.Class.has_any(element, drop.accept))) &&
|
||||
(Element.classNames(element).detect(
|
||||
function(v) { return drop.accept.include(v) } ) )) &&
|
||||
Position.within(drop.element, pX, pY) );
|
||||
},
|
||||
|
||||
deactivate: function(drop) {
|
||||
if(drop.hoverclass)
|
||||
Element.Class.remove(drop.element, drop.hoverclass);
|
||||
Element.removeClassName(drop.element, drop.hoverclass);
|
||||
this.last_active = null;
|
||||
},
|
||||
|
||||
activate: function(drop) {
|
||||
if(this.last_active) this.deactivate(this.last_active);
|
||||
if(drop.hoverclass)
|
||||
Element.Class.add(drop.element, drop.hoverclass);
|
||||
Element.addClassName(drop.element, drop.hoverclass);
|
||||
this.last_active = drop;
|
||||
},
|
||||
|
||||
@@ -105,13 +106,25 @@ var Droppables = {
|
||||
var Draggables = {
|
||||
observers: [],
|
||||
addObserver: function(observer) {
|
||||
this.observers.push(observer);
|
||||
this.observers.push(observer);
|
||||
this._cacheObserverCallbacks();
|
||||
},
|
||||
removeObserver: function(element) { // element instead of obsever fixes mem leaks
|
||||
removeObserver: function(element) { // element instead of observer fixes mem leaks
|
||||
this.observers = this.observers.reject( function(o) { return o.element==element });
|
||||
this._cacheObserverCallbacks();
|
||||
},
|
||||
notify: function(eventName, draggable) { // 'onStart', 'onEnd'
|
||||
this.observers.invoke(eventName, draggable);
|
||||
notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
|
||||
if(this[eventName+'Count'] > 0)
|
||||
this.observers.each( function(o) {
|
||||
if(o[eventName]) o[eventName](eventName, draggable, event);
|
||||
});
|
||||
},
|
||||
_cacheObserverCallbacks: function() {
|
||||
['onStart','onEnd','onDrag'].each( function(eventName) {
|
||||
Draggables[eventName+'Count'] = Draggables.observers.select(
|
||||
function(o) { return o[eventName]; }
|
||||
).length;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +151,7 @@ Draggable.prototype = {
|
||||
|
||||
this.element = $(element);
|
||||
if(options.handle && (typeof options.handle == 'string'))
|
||||
this.handle = Element.Class.childrenWith(this.element, options.handle)[0];
|
||||
this.handle = Element.childrenWithClassName(this.element, options.handle)[0];
|
||||
|
||||
if(!this.handle) this.handle = $(options.handle);
|
||||
if(!this.handle) this.handle = this.element;
|
||||
@@ -219,7 +232,7 @@ Draggable.prototype = {
|
||||
}
|
||||
|
||||
if(success) Droppables.fire(event, this.element);
|
||||
Draggables.notify('onEnd', this);
|
||||
Draggables.notify('onEnd', this, event);
|
||||
|
||||
var revert = this.options.revert;
|
||||
if(revert && typeof revert == 'function') revert = revert(this.element);
|
||||
@@ -290,11 +303,12 @@ Draggable.prototype = {
|
||||
this.element.parentNode.insertBefore(this._clone, this.element);
|
||||
}
|
||||
|
||||
Draggables.notify('onStart', this);
|
||||
Draggables.notify('onStart', this, event);
|
||||
if(this.options.starteffect) this.options.starteffect(this.element);
|
||||
}
|
||||
|
||||
Droppables.show(event, this.element);
|
||||
Draggables.notify('onDrag', this, event);
|
||||
this.draw(event);
|
||||
if(this.options.change) this.options.change(this);
|
||||
|
||||
@@ -413,7 +427,7 @@ var Sortable = {
|
||||
(this.findElements(element, options) || []).each( function(e) {
|
||||
// handles are per-draggable
|
||||
var handle = options.handle ?
|
||||
Element.Class.childrenWith(e, options.handle)[0] : e;
|
||||
Element.childrenWithClassName(e, options.handle)[0] : e;
|
||||
options.draggables.push(
|
||||
new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
|
||||
Droppables.add(e, options_for_droppable);
|
||||
@@ -434,7 +448,7 @@ var Sortable = {
|
||||
var elements = [];
|
||||
$A(element.childNodes).each( function(e) {
|
||||
if(e.tagName && e.tagName==options.tag.toUpperCase() &&
|
||||
(!options.only || (Element.Class.has(e, options.only))))
|
||||
(!options.only || (Element.hasClassName(e, options.only))))
|
||||
elements.push(e);
|
||||
if(options.tree) {
|
||||
var grandchildren = this.findElements(e, options);
|
||||
@@ -491,14 +505,20 @@ var Sortable = {
|
||||
if(!Sortable._marker) {
|
||||
Sortable._marker = $('dropmarker') || document.createElement('DIV');
|
||||
Element.hide(Sortable._marker);
|
||||
Element.Class.add(Sortable._marker, 'dropmarker');
|
||||
Element.addClassName(Sortable._marker, 'dropmarker');
|
||||
Sortable._marker.style.position = 'absolute';
|
||||
document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
|
||||
}
|
||||
var offsets = Position.cumulativeOffset(dropon);
|
||||
Sortable._marker.style.top = offsets[1] + 'px';
|
||||
if(position=='after') Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
|
||||
Sortable._marker.style.left = offsets[0] + 'px';
|
||||
Sortable._marker.style.top = offsets[1] + 'px';
|
||||
|
||||
if(position=='after')
|
||||
if(sortable.overlap == 'horizontal')
|
||||
Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
|
||||
else
|
||||
Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
|
||||
|
||||
Element.show(Sortable._marker);
|
||||
},
|
||||
|
||||
|
||||
@@ -86,97 +86,9 @@ Element.setInlineOpacity = function(element, value){
|
||||
els.opacity = value;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
Element.Class = {
|
||||
// Element.toggleClass(element, className) toggles the class being on/off
|
||||
// Element.toggleClass(element, className1, className2) toggles between both classes,
|
||||
// defaulting to className1 if neither exist
|
||||
toggle: function(element, className) {
|
||||
if(Element.Class.has(element, className)) {
|
||||
Element.Class.remove(element, className);
|
||||
if(arguments.length == 3) Element.Class.add(element, arguments[2]);
|
||||
} else {
|
||||
Element.Class.add(element, className);
|
||||
if(arguments.length == 3) Element.Class.remove(element, arguments[2]);
|
||||
}
|
||||
},
|
||||
|
||||
// gets space-delimited classnames of an element as an array
|
||||
get: function(element) {
|
||||
return $(element).className.split(' ');
|
||||
},
|
||||
|
||||
// functions adapted from original functions by Gavin Kistner
|
||||
remove: function(element) {
|
||||
element = $(element);
|
||||
var removeClasses = arguments;
|
||||
$R(1,arguments.length-1).each( function(index) {
|
||||
element.className =
|
||||
element.className.split(' ').reject(
|
||||
function(klass) { return (klass == removeClasses[index]) } ).join(' ');
|
||||
});
|
||||
},
|
||||
|
||||
add: function(element) {
|
||||
element = $(element);
|
||||
for(var i = 1; i < arguments.length; i++) {
|
||||
Element.Class.remove(element, arguments[i]);
|
||||
element.className += (element.className.length > 0 ? ' ' : '') + arguments[i];
|
||||
}
|
||||
},
|
||||
|
||||
// returns true if all given classes exist in said element
|
||||
has: function(element) {
|
||||
element = $(element);
|
||||
if(!element || !element.className) return false;
|
||||
var regEx;
|
||||
for(var i = 1; i < arguments.length; i++) {
|
||||
if((typeof arguments[i] == 'object') &&
|
||||
(arguments[i].constructor == Array)) {
|
||||
for(var j = 0; j < arguments[i].length; j++) {
|
||||
regEx = new RegExp("(^|\\s)" + arguments[i][j] + "(\\s|$)");
|
||||
if(!regEx.test(element.className)) return false;
|
||||
}
|
||||
} else {
|
||||
regEx = new RegExp("(^|\\s)" + arguments[i] + "(\\s|$)");
|
||||
if(!regEx.test(element.className)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
// expects arrays of strings and/or strings as optional paramters
|
||||
// Element.Class.has_any(element, ['classA','classB','classC'], 'classD')
|
||||
has_any: function(element) {
|
||||
element = $(element);
|
||||
if(!element || !element.className) return false;
|
||||
var regEx;
|
||||
for(var i = 1; i < arguments.length; i++) {
|
||||
if((typeof arguments[i] == 'object') &&
|
||||
(arguments[i].constructor == Array)) {
|
||||
for(var j = 0; j < arguments[i].length; j++) {
|
||||
regEx = new RegExp("(^|\\s)" + arguments[i][j] + "(\\s|$)");
|
||||
if(regEx.test(element.className)) return true;
|
||||
}
|
||||
} else {
|
||||
regEx = new RegExp("(^|\\s)" + arguments[i] + "(\\s|$)");
|
||||
if(regEx.test(element.className)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
childrenWith: function(element, className) {
|
||||
var children = $(element).getElementsByTagName('*');
|
||||
var elements = new Array();
|
||||
|
||||
for (var i = 0; i < children.length; i++)
|
||||
if (Element.Class.has(children[i], className))
|
||||
elements.push(children[i]);
|
||||
|
||||
return elements;
|
||||
}
|
||||
Element.childrenWithClassName = function(element, className) {
|
||||
return $A($(element).getElementsByTagName('*')).select(
|
||||
function(c) { return Element.hasClassName(c, className) });
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Prototype JavaScript framework, version 1.4.0_rc2
|
||||
/* Prototype JavaScript framework, version 1.4.0_rc4
|
||||
* (c) 2005 Sam Stephenson <sam@conio.net>
|
||||
*
|
||||
* THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff
|
||||
@@ -11,7 +11,8 @@
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
var Prototype = {
|
||||
Version: '1.4.0_rc2',
|
||||
Version: '1.4.0_rc4',
|
||||
ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
|
||||
|
||||
emptyFunction: function() {},
|
||||
K: function(x) {return x}
|
||||
@@ -143,6 +144,22 @@ Object.extend(String.prototype, {
|
||||
return this.replace(/<\/?[^>]+>/gi, '');
|
||||
},
|
||||
|
||||
stripScripts: function() {
|
||||
return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
|
||||
},
|
||||
|
||||
extractScripts: function() {
|
||||
var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
|
||||
var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
|
||||
return (this.match(matchAll) || []).map(function(scriptTag) {
|
||||
return (scriptTag.match(matchOne) || ['', ''])[1];
|
||||
});
|
||||
},
|
||||
|
||||
evalScripts: function() {
|
||||
return this.extractScripts().map(eval);
|
||||
},
|
||||
|
||||
escapeHTML: function() {
|
||||
var div = document.createElement('div');
|
||||
var text = document.createTextNode(this);
|
||||
@@ -214,8 +231,8 @@ var Enumerable = {
|
||||
all: function(iterator) {
|
||||
var result = true;
|
||||
this.each(function(value, index) {
|
||||
if (!(result &= (iterator || Prototype.K)(value, index)))
|
||||
throw $break;
|
||||
result = result && !!(iterator || Prototype.K)(value, index);
|
||||
if (!result) throw $break;
|
||||
});
|
||||
return result;
|
||||
},
|
||||
@@ -223,7 +240,7 @@ var Enumerable = {
|
||||
any: function(iterator) {
|
||||
var result = true;
|
||||
this.each(function(value, index) {
|
||||
if (result &= (iterator || Prototype.K)(value, index))
|
||||
if (result = !!(iterator || Prototype.K)(value, index))
|
||||
throw $break;
|
||||
});
|
||||
return result;
|
||||
@@ -388,12 +405,19 @@ var $A = Array.from = function(iterable) {
|
||||
|
||||
Object.extend(Array.prototype, Enumerable);
|
||||
|
||||
Array.prototype._reverse = Array.prototype.reverse;
|
||||
|
||||
Object.extend(Array.prototype, {
|
||||
_each: function(iterator) {
|
||||
for (var i = 0; i < this.length; i++)
|
||||
iterator(this[i]);
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this.length = 0;
|
||||
return this;
|
||||
},
|
||||
|
||||
first: function() {
|
||||
return this[0];
|
||||
},
|
||||
@@ -425,14 +449,11 @@ Object.extend(Array.prototype, {
|
||||
indexOf: function(object) {
|
||||
for (var i = 0; i < this.length; i++)
|
||||
if (this[i] == object) return i;
|
||||
return false;
|
||||
return -1;
|
||||
},
|
||||
|
||||
reverse: function() {
|
||||
var result = [];
|
||||
for (var i = this.length; i > 0; i--)
|
||||
result.push(this[i-1]);
|
||||
return result;
|
||||
reverse: function(inline) {
|
||||
return (inline !== false ? this : this.toArray())._reverse();
|
||||
},
|
||||
|
||||
inspect: function() {
|
||||
@@ -486,9 +507,9 @@ function $H(object) {
|
||||
Object.extend(hash, Hash);
|
||||
return hash;
|
||||
}
|
||||
var Range = Class.create();
|
||||
Object.extend(Range.prototype, Enumerable);
|
||||
Object.extend(Range.prototype, {
|
||||
ObjectRange = Class.create();
|
||||
Object.extend(ObjectRange.prototype, Enumerable);
|
||||
Object.extend(ObjectRange.prototype, {
|
||||
initialize: function(start, end, exclusive) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
@@ -513,7 +534,7 @@ Object.extend(Range.prototype, {
|
||||
});
|
||||
|
||||
var $R = function(start, end, exclusive) {
|
||||
return new Range(start, end, exclusive);
|
||||
return new ObjectRange(start, end, exclusive);
|
||||
}
|
||||
|
||||
var Ajax = {
|
||||
@@ -549,8 +570,7 @@ Ajax.Responders = {
|
||||
if (responder[callback] && typeof responder[callback] == 'function') {
|
||||
try {
|
||||
responder[callback].apply(responder, [request, transport, json]);
|
||||
} catch (e) {
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -626,8 +646,7 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
|
||||
this.transport.send(this.options.method == 'post' ? body : null);
|
||||
|
||||
} catch (e) {
|
||||
(this.options.onException || Prototype.emptyFunction)(this, e);
|
||||
Ajax.Responders.dispatch('onException', this, e);
|
||||
this.dispatchException(e);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -661,12 +680,23 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
|
||||
this.respondToReadyState(this.transport.readyState);
|
||||
},
|
||||
|
||||
header: function(name) {
|
||||
try {
|
||||
return this.transport.getResponseHeader(name);
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
evalJSON: function() {
|
||||
try {
|
||||
var json = this.transport.getResponseHeader('X-JSON'), object;
|
||||
object = eval(json);
|
||||
return object;
|
||||
return eval(this.header('X-JSON'));
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
evalResponse: function() {
|
||||
try {
|
||||
return eval(this.transport.responseText);
|
||||
} catch (e) {
|
||||
this.dispatchException(e);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -674,22 +704,38 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
|
||||
var event = Ajax.Request.Events[readyState];
|
||||
var transport = this.transport, json = this.evalJSON();
|
||||
|
||||
if (event == 'Complete')
|
||||
(this.options['on' + this.transport.status]
|
||||
|| this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
|
||||
|| Prototype.emptyFunction)(transport, json);
|
||||
if (event == 'Complete') {
|
||||
try {
|
||||
(this.options['on' + this.transport.status]
|
||||
|| this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
|
||||
|| Prototype.emptyFunction)(transport, json);
|
||||
} catch (e) {
|
||||
this.dispatchException(e);
|
||||
}
|
||||
|
||||
(this.options['on' + event] || Prototype.emptyFunction)(transport, json);
|
||||
Ajax.Responders.dispatch('on' + event, this, transport, json);
|
||||
if ((this.header('Content-type') || '').match(/^text\/javascript/i))
|
||||
this.evalResponse();
|
||||
}
|
||||
|
||||
try {
|
||||
(this.options['on' + event] || Prototype.emptyFunction)(transport, json);
|
||||
Ajax.Responders.dispatch('on' + event, this, transport, json);
|
||||
} catch (e) {
|
||||
this.dispatchException(e);
|
||||
}
|
||||
|
||||
/* Avoid memory leak in MSIE: clean up the oncomplete event handler */
|
||||
if (event == 'Complete')
|
||||
this.transport.onreadystatechange = Prototype.emptyFunction;
|
||||
},
|
||||
|
||||
dispatchException: function(exception) {
|
||||
(this.options.onException || Prototype.emptyFunction)(this, exception);
|
||||
Ajax.Responders.dispatch('onException', this, exception);
|
||||
}
|
||||
});
|
||||
|
||||
Ajax.Updater = Class.create();
|
||||
Ajax.Updater.ScriptFragment = '(?:<script.*?>)((\n|.)*?)(?:<\/script>)';
|
||||
|
||||
Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
|
||||
initialize: function(container, url, options) {
|
||||
@@ -714,16 +760,16 @@ Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
|
||||
updateContent: function() {
|
||||
var receiver = this.responseIsSuccess() ?
|
||||
this.containers.success : this.containers.failure;
|
||||
var response = this.transport.responseText;
|
||||
|
||||
var match = new RegExp(Ajax.Updater.ScriptFragment, 'img');
|
||||
var response = this.transport.responseText.replace(match, '');
|
||||
var scripts = this.transport.responseText.match(match);
|
||||
if (!this.options.evalScripts)
|
||||
response = response.stripScripts();
|
||||
|
||||
if (receiver) {
|
||||
if (this.options.insertion) {
|
||||
new this.options.insertion(receiver, response);
|
||||
} else {
|
||||
receiver.innerHTML = response;
|
||||
Element.update(receiver, response);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -731,14 +777,6 @@ Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
|
||||
if (this.onComplete)
|
||||
setTimeout(this.onComplete.bind(this), 10);
|
||||
}
|
||||
|
||||
if (this.options.evalScripts && scripts) {
|
||||
match = new RegExp(Ajax.Updater.ScriptFragment, 'im');
|
||||
setTimeout((function() {
|
||||
for (var i = 0; i < scripts.length; i++)
|
||||
eval(scripts[i].match(match)[1]);
|
||||
}).bind(this), 10);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -830,6 +868,11 @@ Object.extend(Element, {
|
||||
element.parentNode.removeChild(element);
|
||||
},
|
||||
|
||||
update: function(element, html) {
|
||||
$(element).innerHTML = html.stripScripts();
|
||||
setTimeout(function() {html.evalScripts()}, 10);
|
||||
},
|
||||
|
||||
getHeight: function(element) {
|
||||
element = $(element);
|
||||
return element.offsetHeight;
|
||||
@@ -893,6 +936,12 @@ Object.extend(Element, {
|
||||
return value == 'auto' ? null : value;
|
||||
},
|
||||
|
||||
setStyle: function(element, style) {
|
||||
element = $(element);
|
||||
for (name in style)
|
||||
element.style[name.camelize()] = style[name];
|
||||
},
|
||||
|
||||
getDimensions: function(element) {
|
||||
element = $(element);
|
||||
if (Element.getStyle(element, 'display') != 'none')
|
||||
@@ -969,7 +1018,7 @@ Abstract.Insertion = function(adjacency) {
|
||||
Abstract.Insertion.prototype = {
|
||||
initialize: function(element, content) {
|
||||
this.element = $(element);
|
||||
this.content = content;
|
||||
this.content = content.stripScripts();
|
||||
|
||||
if (this.adjacency && this.element.insertAdjacentHTML) {
|
||||
try {
|
||||
@@ -986,6 +1035,8 @@ Abstract.Insertion.prototype = {
|
||||
if (this.initializeRange) this.initializeRange();
|
||||
this.insertContent([this.range.createContextualFragment(this.content)]);
|
||||
}
|
||||
|
||||
setTimeout(function() {content.evalScripts()}, 10);
|
||||
},
|
||||
|
||||
contentFromAnonymousTable: function() {
|
||||
@@ -1018,7 +1069,7 @@ Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
|
||||
},
|
||||
|
||||
insertContent: function(fragments) {
|
||||
fragments.reverse().each((function(fragment) {
|
||||
fragments.reverse(false).each((function(fragment) {
|
||||
this.element.insertBefore(fragment, this.element.firstChild);
|
||||
}).bind(this));
|
||||
}
|
||||
@@ -1079,7 +1130,7 @@ Element.ClassNames.prototype = {
|
||||
if (!this.include(classNameToRemove)) return;
|
||||
this.set(this.select(function(className) {
|
||||
return className != classNameToRemove;
|
||||
}));
|
||||
}).join(' '));
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
@@ -1109,8 +1160,10 @@ var Field = {
|
||||
},
|
||||
|
||||
activate: function(element) {
|
||||
$(element).focus();
|
||||
$(element).select();
|
||||
element = $(element);
|
||||
element.focus();
|
||||
if (element.select)
|
||||
element.select();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1178,16 +1231,15 @@ var Form = {
|
||||
}
|
||||
},
|
||||
|
||||
findFirstElement: function(form) {
|
||||
return Form.getElements(form).find(function(element) {
|
||||
return element.type != 'hidden' && !element.disabled &&
|
||||
['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
|
||||
});
|
||||
},
|
||||
|
||||
focusFirstElement: function(form) {
|
||||
form = $(form);
|
||||
var elements = Form.getElements(form);
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
var element = elements[i];
|
||||
if (element.type != 'hidden' && !element.disabled) {
|
||||
Field.activate(element);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Field.activate(Form.findFirstElement(form));
|
||||
},
|
||||
|
||||
reset: function(form) {
|
||||
@@ -1349,24 +1401,14 @@ Abstract.EventObserver.prototype = {
|
||||
switch (element.type.toLowerCase()) {
|
||||
case 'checkbox':
|
||||
case 'radio':
|
||||
element.target = this;
|
||||
element.prev_onclick = element.onclick || Prototype.emptyFunction;
|
||||
element.onclick = function() {
|
||||
this.prev_onclick();
|
||||
this.target.onElementEvent();
|
||||
}
|
||||
Event.observe(element, 'click', this.onElementEvent.bind(this));
|
||||
break;
|
||||
case 'password':
|
||||
case 'text':
|
||||
case 'textarea':
|
||||
case 'select-one':
|
||||
case 'select-multiple':
|
||||
element.target = this;
|
||||
element.prev_onchange = element.onchange || Prototype.emptyFunction;
|
||||
element.onchange = function() {
|
||||
this.prev_onchange();
|
||||
this.target.onElementEvent();
|
||||
}
|
||||
Event.observe(element, 'change', this.onElementEvent.bind(this));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ end
|
||||
|
||||
# Set up company fixtures.
|
||||
$LOAD_PATH << "#{path_to_ar}/test"
|
||||
QUOTED_TYPE = ActiveRecord::Base.connection.quote_column_name('type') unless Object.const_defined?(:QUOTED_TYPE)
|
||||
require 'fixtures/company'
|
||||
File.read("#{path_to_ar}/test/fixtures/db_definitions/sqlite.sql").split(';').each do |sql|
|
||||
ActiveRecord::Base.connection.execute(sql) unless sql.blank?
|
||||
|
||||
@@ -15,12 +15,12 @@ require 'action_controller/session/active_record_store'
|
||||
|
||||
#ActiveRecord::Base.logger = Logger.new($stdout)
|
||||
begin
|
||||
CGI::Session::ActiveRecordStore::Session.establish_connection(:adapter => 'sqlite3', :dbfile => ':memory:')
|
||||
CGI::Session::ActiveRecordStore::Session.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
|
||||
CGI::Session::ActiveRecordStore::Session.connection
|
||||
rescue Object
|
||||
$stderr.puts 'SQLite 3 unavailable; falling back to SQLite 2.'
|
||||
begin
|
||||
CGI::Session::ActiveRecordStore::Session.establish_connection(:adapter => 'sqlite', :dbfile => ':memory:')
|
||||
CGI::Session::ActiveRecordStore::Session.establish_connection(:adapter => 'sqlite', :database => ':memory:')
|
||||
CGI::Session::ActiveRecordStore::Session.connection
|
||||
rescue Object
|
||||
$stderr.puts 'SQLite 2 unavailable; skipping ActiveRecordStore test suite.'
|
||||
@@ -68,10 +68,16 @@ class ActiveRecordStoreTest < Test::Unit::TestCase
|
||||
ENV['REQUEST_METHOD'] = 'GET'
|
||||
CGI::Session::ActiveRecordStore.session_class = session_class
|
||||
|
||||
@new_session = CGI::Session.new(CGI.new, 'database_manager' => CGI::Session::ActiveRecordStore, 'new_session' => true)
|
||||
@cgi = CGI.new
|
||||
@new_session = CGI::Session.new(@cgi, 'database_manager' => CGI::Session::ActiveRecordStore, 'new_session' => true)
|
||||
@new_session['foo'] = 'bar'
|
||||
end
|
||||
|
||||
def test_another_instance
|
||||
@another = CGI::Session.new(@cgi, 'session_id' => @new_session.session_id, 'database_manager' => CGI::Session::ActiveRecordStore)
|
||||
assert_equal @new_session.session_id, @another.session_id
|
||||
end
|
||||
|
||||
def test_model_attribute
|
||||
assert_kind_of CGI::Session::ActiveRecordStore::Session, @new_session.model
|
||||
assert_equal({ 'foo' => 'bar' }, @new_session.model.data)
|
||||
|
||||
@@ -31,7 +31,7 @@ class FlashTest < Test::Unit::TestCase
|
||||
def use_flash_and_keep_it
|
||||
@flash_copy = {}.update flash
|
||||
@flashy = flash["that"]
|
||||
keep_flash
|
||||
silence_warnings { keep_flash }
|
||||
render :inline => "hello"
|
||||
end
|
||||
|
||||
|
||||
@@ -43,6 +43,22 @@ class NewRenderTestController < ActionController::Base
|
||||
def render_custom_code
|
||||
render :text => "hello world", :status => "404 Moved"
|
||||
end
|
||||
|
||||
def render_file_with_instance_variables
|
||||
@secret = 'in the sauce'
|
||||
path = File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_ivar.rhtml')
|
||||
render :file => path
|
||||
end
|
||||
|
||||
def render_file_with_locals
|
||||
path = File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_locals.rhtml')
|
||||
render :file => path, :locals => {:secret => 'in the sauce'}
|
||||
end
|
||||
|
||||
def render_file_not_using_full_path
|
||||
@secret = 'in the sauce'
|
||||
render :file => 'test/render_file_with_ivar', :use_full_path => true
|
||||
end
|
||||
|
||||
def render_xml_hello
|
||||
@name = "David"
|
||||
@@ -242,6 +258,21 @@ class NewRenderTest < Test::Unit::TestCase
|
||||
assert_response :missing
|
||||
end
|
||||
|
||||
def test_render_file_with_instance_variables
|
||||
get :render_file_with_instance_variables
|
||||
assert_equal "The secret is in the sauce\n", @response.body
|
||||
end
|
||||
|
||||
def test_render_file_not_using_full_path
|
||||
get :render_file_not_using_full_path
|
||||
assert_equal "The secret is in the sauce\n", @response.body
|
||||
end
|
||||
|
||||
def test_render_file_with_locals
|
||||
get :render_file_with_locals
|
||||
assert_equal "The secret is in the sauce\n", @response.body
|
||||
end
|
||||
|
||||
def test_attempt_to_access_object_method
|
||||
assert_raises(ActionController::UnknownAction, "No action responded to [clone]") { get :clone }
|
||||
end
|
||||
|
||||
@@ -100,6 +100,11 @@ class RequestTest < Test::Unit::TestCase
|
||||
@request.relative_url_root = nil
|
||||
@request.env['SCRIPT_NAME'] = "/collaboration/hieraki/dispatch.cgi"
|
||||
assert_equal "/collaboration/hieraki", @request.relative_url_root
|
||||
|
||||
# apache/scgi case
|
||||
@request.relative_url_root = nil
|
||||
@request.env['SCRIPT_NAME'] = "/collaboration/hieraki"
|
||||
assert_equal "/collaboration/hieraki", @request.relative_url_root
|
||||
end
|
||||
|
||||
def test_request_uri
|
||||
|
||||
1
actionpack/test/fixtures/test/render_file_with_ivar.rhtml
vendored
Normal file
1
actionpack/test/fixtures/test/render_file_with_ivar.rhtml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
The secret is <%= @secret %>
|
||||
1
actionpack/test/fixtures/test/render_file_with_locals.rhtml
vendored
Normal file
1
actionpack/test/fixtures/test/render_file_with_locals.rhtml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
The secret is <%= secret %>
|
||||
@@ -1,7 +1,12 @@
|
||||
*SVN*
|
||||
*0.9.4* (December 7th, 2005)
|
||||
|
||||
* Update from LGPL to MIT license as per Minero Aoki's permission. [Marcel Molina Jr.]
|
||||
|
||||
* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
|
||||
|
||||
* Fix that XML-RPC date/time values did not have well-defined behaviour (#2516, #2534). This fix has one caveat, in that we can't support pre-1970 dates from XML-RPC clients.
|
||||
|
||||
|
||||
*0.9.3* (November 7th, 2005)
|
||||
|
||||
* Upgraded to Action Pack 1.11.0 and Active Record 1.13.0
|
||||
|
||||
@@ -10,7 +10,7 @@ require File.join(File.dirname(__FILE__), 'lib', 'action_web_service', 'version'
|
||||
|
||||
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
||||
PKG_NAME = 'actionwebservice'
|
||||
PKG_VERSION = ActionWebService::Version::STRING + PKG_BUILD
|
||||
PKG_VERSION = ActionWebService::VERSION::STRING + PKG_BUILD
|
||||
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
||||
PKG_DESTINATION = ENV["RAILS_PKG_DESTINATION"] || "../#{PKG_NAME}"
|
||||
|
||||
@@ -63,8 +63,8 @@ spec = Gem::Specification.new do |s|
|
||||
s.rubyforge_project = "aws"
|
||||
s.homepage = "http://www.rubyonrails.org"
|
||||
|
||||
s.add_dependency('actionpack', '= 1.11.0' + PKG_BUILD)
|
||||
s.add_dependency('activerecord', '= 1.13.0' + PKG_BUILD)
|
||||
s.add_dependency('actionpack', '= 1.11.1' + PKG_BUILD)
|
||||
s.add_dependency('activerecord', '= 1.13.1' + PKG_BUILD)
|
||||
|
||||
s.has_rdoc = true
|
||||
s.requirements << 'none'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
module ActionWebService
|
||||
module Version
|
||||
module VERSION
|
||||
MAJOR = 0
|
||||
MINOR = 9
|
||||
TINY = 3
|
||||
TINY = 4
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
||||
@@ -3,9 +3,28 @@
|
||||
#
|
||||
# Copyright (c) 2000-2004 Minero Aoki
|
||||
#
|
||||
# This program is free software.
|
||||
# You can distribute/modify this program under the terms of
|
||||
# the GNU LGPL, Lesser General Public License version 2.1.
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
|
||||
#
|
||||
|
||||
unless Enumerable.method_defined?(:map) # Ruby 1.4.6
|
||||
|
||||
@@ -1,4 +1,70 @@
|
||||
*SVN*
|
||||
*1.13.1* (December 7th, 2005)
|
||||
|
||||
* MySQL: more robust test for nullified result hashes. #3124 [Stefan Kaes]
|
||||
|
||||
* SQLite: find database file when RAILS_ROOT is a symlink. #3116 [anna@wota.jp]
|
||||
|
||||
* Reloading an instance refreshes its aggregations as well as its associations. #3024 [François Beausolei]
|
||||
|
||||
* Fixed that using :include together with :conditions array in Base.find would cause NoMethodError #2887 [Paul Hammmond]
|
||||
|
||||
* PostgreSQL: more robust sequence name discovery. #3087 [Rick Olson]
|
||||
|
||||
* Oracle: use syntax compatible with Oracle 8. #3131 [Michael Schoen]
|
||||
|
||||
* MySQL: work around ruby-mysql/mysql-ruby inconsistency with mysql.stat. Eliminate usage of mysql.ping because it doesn't guarantee reconnect. Explicitly close and reopen the connection instead. [Jeremy Kemper]
|
||||
|
||||
* When AbstractAdapter#log rescues an exception, attempt to detect and reconnect to an inactive database connection. Connection adapter must respond to the active? and reconnect! instance methods. Initial support for PostgreSQL, MySQL, and SQLite. Make certain that all statements which may need reconnection are performed within a logged block: for example, this means no avoiding log(sql, name) { } if @logger.nil? [Jeremy Kemper]
|
||||
|
||||
* Firebird: active? and reconnect! methods for handling stale connections. #428 [Ken Kunz <kennethkunz@gmail.com>]
|
||||
|
||||
* Firebird: updated for FireRuby 0.4.0. #3009 [Ken Kunz <kennethkunz@gmail.com>]
|
||||
|
||||
* Introducing the Firebird adapter. Quote columns and use attribute_condition more consistently. Setup guide: http://wiki.rubyonrails.com/rails/pages/Firebird+Adapter #1874 [Ken Kunz <kennethkunz@gmail.com>]
|
||||
|
||||
* MySQL and PostgreSQL: active? compatibility with the pure-Ruby driver. #428 [Jeremy Kemper]
|
||||
|
||||
* Oracle: active? check pings the database rather than testing the last command status. #428 [Michael Schoen]
|
||||
|
||||
* SQLServer: resolve column aliasing/quoting collision when using limit or offset in an eager find. #2974 [kajism@yahoo.com]
|
||||
|
||||
* Reloading a model doesn't lose track of its connection. #2996 [junk@miriamtech.com, Jeremy Kemper]
|
||||
|
||||
* Fixed bug where using update_attribute after pushing a record to a habtm association of the object caused duplicate rows in the join table. #2888 [colman@rominato.com, Florian Weber, Michael Schoen]
|
||||
|
||||
* MySQL: introduce :encoding option to specify the character set for client, connection, and results. Only available for MySQL 4.1 and later with the mysql-ruby driver. Do SHOW CHARACTER SET in mysql client to see available encodings. #2975 [Shugo Maeda]
|
||||
|
||||
* Add tasks to create, drop and rebuild the MySQL and PostgreSQL test databases. [Marcel Molina Jr.]
|
||||
|
||||
* Correct boolean handling in generated reader methods. #2945 [don.park@gmail.com, Stefan Kaes]
|
||||
|
||||
* Don't generate read methods for columns whose names are not valid ruby method names. #2946 [Stefan Kaes]
|
||||
|
||||
* Document :force option to create_table. #2921 [Blair Zajac <blair@orcaware.com>]
|
||||
|
||||
* Don't add the same conditions twice in has_one finder sql. #2916 [Jeremy Evans]
|
||||
|
||||
* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
|
||||
|
||||
* SQLServer: insert uses given primary key value if not nil rather than SELECT @@IDENTITY. #2866 [kajism@yahoo.com, Tom Ward <tom@popdog.net>]
|
||||
|
||||
* Correct documentation for Base.delete_all. #1568 [Newhydra]
|
||||
|
||||
* Oracle: test case for column default parsing. #2788 [Michael Schoen <schoenm@earthlink.net>]
|
||||
|
||||
* Update documentation for Migrations. #2861 [Tom Werner <tom@cube6media.com>]
|
||||
|
||||
* Oracle: Much faster column reflection. #2848 [Michael Schoen <schoenm@earthlink.net>]
|
||||
|
||||
* Base.reset_sequence_name analogous to reset_table_name (mostly useful for testing). Base.define_attr_method allows nil values. [Jeremy Kemper]
|
||||
|
||||
* PostgreSQL: smarter sequence name defaults, stricter last_insert_id, warn on pk without sequence. [Jeremy Kemper]
|
||||
|
||||
* PostgreSQL: correctly discover custom primary key sequences. #2594 [Blair Zajac <blair@orcaware.com>, meadow.nnick@gmail.com, Jeremy Kemper]
|
||||
|
||||
* SQLServer: don't report limits for unsupported field types. #2835 [Ryan Tomayko]
|
||||
|
||||
* Include the Enumerable module in ActiveRecord::Errors. [Rick Bradley <rick@rickbradley.com>]
|
||||
|
||||
* Add :group option, correspond to GROUP BY, to the find method and to the has_many association. #2818 [rubyonrails@atyp.de]
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ require File.join(File.dirname(__FILE__), 'lib', 'active_record', 'version')
|
||||
|
||||
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
||||
PKG_NAME = 'activerecord'
|
||||
PKG_VERSION = ActiveRecord::Version::STRING + PKG_BUILD
|
||||
PKG_VERSION = ActiveRecord::VERSION::STRING + PKG_BUILD
|
||||
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
||||
|
||||
RELEASE_NAME = "REL #{PKG_VERSION}"
|
||||
@@ -27,7 +27,7 @@ task :default => [ :test_mysql, :test_sqlite, :test_postgresql ]
|
||||
|
||||
# Run the unit tests
|
||||
|
||||
for adapter in %w( mysql postgresql sqlite sqlite3 sqlserver sqlserver_odbc db2 oci )
|
||||
for adapter in %w( mysql postgresql sqlite sqlite3 firebird sqlserver sqlserver_odbc db2 oci )
|
||||
Rake::TestTask.new("test_#{adapter}") { |t|
|
||||
t.libs << "test" << "test/connections/native_#{adapter}"
|
||||
t.pattern = "test/*_test{,_#{adapter}}.rb"
|
||||
@@ -35,6 +35,41 @@ for adapter in %w( mysql postgresql sqlite sqlite3 sqlserver sqlserver_odbc db2
|
||||
}
|
||||
end
|
||||
|
||||
SCHEMA_PATH = File.join(File.dirname(__FILE__), *%w(test fixtures db_definitions))
|
||||
|
||||
desc 'Build the MySQL test databases'
|
||||
task :build_mysql_databases do
|
||||
%x( mysqladmin create activerecord_unittest )
|
||||
%x( mysqladmin create activerecord_unittest2 )
|
||||
%x( mysql activerecord_unittest < #{File.join(SCHEMA_PATH, 'mysql.sql')} )
|
||||
%x( mysql activerecord_unittest < #{File.join(SCHEMA_PATH, 'mysql2.sql')} )
|
||||
end
|
||||
|
||||
desc 'Drop the MySQL test databases'
|
||||
task :drop_mysql_databases do
|
||||
%x( mysqladmin -f drop activerecord_unittest )
|
||||
%x( mysqladmin -f drop activerecord_unittest2 )
|
||||
end
|
||||
|
||||
desc 'Rebuild the MySQL test databases'
|
||||
task :rebuild_mysql_databases => [:drop_mysql_databases, :build_mysql_databases]
|
||||
|
||||
desc 'Build the PostgreSQL test databases'
|
||||
task :build_postgresql_databases do
|
||||
%x( createdb activerecord_unittest )
|
||||
%x( createdb activerecord_unittest2 )
|
||||
%x( psql activerecord_unittest -f #{File.join(SCHEMA_PATH, 'postgresql.sql')} )
|
||||
%x( psql activerecord_unittest -f #{File.join(SCHEMA_PATH, 'postgresql2.sql')} )
|
||||
end
|
||||
|
||||
desc 'Drop the PostgreSQL test databases'
|
||||
task :drop_postgresql_databases do
|
||||
%x( dropdb activerecord_unittest )
|
||||
%x( dropdb activerecord_unittest2 )
|
||||
end
|
||||
|
||||
desc 'Rebuild the PostgreSQL test databases'
|
||||
task :rebuild_postgresql_databases => [:drop_postgresql_databases, :build_postgresql_databases]
|
||||
|
||||
# Generate the RDoc documentation
|
||||
|
||||
@@ -71,7 +106,7 @@ spec = Gem::Specification.new do |s|
|
||||
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
|
||||
end
|
||||
|
||||
s.add_dependency('activesupport', '= 1.2.3' + PKG_BUILD)
|
||||
s.add_dependency('activesupport', '= 1.2.4' + PKG_BUILD)
|
||||
|
||||
s.files.delete "test/fixtures/fixture_database.sqlite"
|
||||
s.files.delete "test/fixtures/fixture_database_2.sqlite"
|
||||
|
||||
@@ -66,11 +66,11 @@ ActiveRecord::Base.class_eval do
|
||||
end
|
||||
|
||||
unless defined?(RAILS_CONNECTION_ADAPTERS)
|
||||
RAILS_CONNECTION_ADAPTERS = %w(mysql postgresql sqlite sqlserver db2 oci)
|
||||
RAILS_CONNECTION_ADAPTERS = %w(mysql postgresql sqlite firebird sqlserver db2 oci)
|
||||
end
|
||||
|
||||
RAILS_CONNECTION_ADAPTERS.each do |adapter|
|
||||
require "active_record/connection_adapters/#{adapter}_adapter"
|
||||
require "active_record/connection_adapters/" + adapter + "_adapter"
|
||||
end
|
||||
|
||||
require 'active_record/query_cache'
|
||||
|
||||
@@ -5,6 +5,12 @@ module ActiveRecord
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
def clear_aggregation_cache #:nodoc:
|
||||
self.class.reflect_on_all_aggregations.to_a.each do |assoc|
|
||||
instance_variable_set "@#{assoc.name}", nil
|
||||
end unless self.new_record?
|
||||
end
|
||||
|
||||
# Active Record implements aggregation through a macro-like class method called +composed_of+ for representing attributes
|
||||
# as value objects. It expresses relationships like "Account [is] composed of Money [among other things]" or "Person [is]
|
||||
# composed of [an] address". Each call to the macro adds a description of how the value objects are created from the
|
||||
|
||||
@@ -809,6 +809,9 @@ module ActiveRecord
|
||||
records_to_save.each { |record| association.send(:insert_record, record) }
|
||||
association.send(:construct_sql) # reconstruct the SQL queries now that we know the owner's id
|
||||
end
|
||||
|
||||
@new_record_before_save = false
|
||||
true
|
||||
end_eval
|
||||
|
||||
# Doesn't use after_save as that would save associations added in after_create/after_update twice
|
||||
@@ -977,9 +980,10 @@ module ActiveRecord
|
||||
end
|
||||
|
||||
def include_eager_conditions?(options)
|
||||
return false unless options[:conditions]
|
||||
|
||||
options[:conditions].scan(/ ([^.]+)\.[^.]+ /).flatten.any? do |condition_table_name|
|
||||
conditions = options[:conditions]
|
||||
return false unless conditions
|
||||
conditions = conditions.first if conditions.is_a?(Array)
|
||||
conditions.scan(/(\w+)\.\w+/).flatten.any? do |condition_table_name|
|
||||
condition_table_name != table_name
|
||||
end
|
||||
end
|
||||
@@ -999,7 +1003,7 @@ module ActiveRecord
|
||||
end
|
||||
|
||||
def column_aliases(schema_abbreviations)
|
||||
schema_abbreviations.collect { |cn, tc| "#{tc.join(".")} AS #{cn}" }.join(", ")
|
||||
schema_abbreviations.collect { |cn, tc| "#{tc[0]}.#{connection.quote_column_name tc[1]} AS #{cn}" }.join(", ")
|
||||
end
|
||||
|
||||
def association_join(reflection)
|
||||
|
||||
@@ -65,7 +65,7 @@ module ActiveRecord
|
||||
end
|
||||
|
||||
def construct_sql
|
||||
@finder_sql = "#{@association_class.table_name}.#{@association_class_primary_key_name} = #{@owner.quoted_id}#{@options[:conditions] ? " AND " + @options[:conditions] : ""}"
|
||||
@finder_sql = "#{@association_class.table_name}.#{@association_class_primary_key_name} = #{@owner.quoted_id}"
|
||||
@finder_sql << " AND (#{sanitize_sql(@options[:conditions])})" if @options[:conditions]
|
||||
@finder_sql
|
||||
end
|
||||
|
||||
@@ -243,24 +243,6 @@ module ActiveRecord #:nodoc:
|
||||
# on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+.
|
||||
cattr_accessor :logger
|
||||
|
||||
# Returns the connection currently associated with the class. This can
|
||||
# also be used to "borrow" the connection to do database work unrelated
|
||||
# to any of the specific Active Records.
|
||||
def self.connection
|
||||
if allow_concurrency
|
||||
retrieve_connection
|
||||
else
|
||||
@connection ||= retrieve_connection
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the connection currently associated with the class. This can
|
||||
# also be used to "borrow" the connection to do database work that isn't
|
||||
# easily done without going straight to SQL.
|
||||
def connection
|
||||
self.class.connection
|
||||
end
|
||||
|
||||
def self.inherited(child) #:nodoc:
|
||||
@@subclasses[self] ||= []
|
||||
@@subclasses[self] << child
|
||||
@@ -513,7 +495,7 @@ module ActiveRecord #:nodoc:
|
||||
|
||||
# Deletes all the records that match the +condition+ without instantiating the objects first (and hence not
|
||||
# calling the destroy method). Example:
|
||||
# Post.destroy_all "person_id = 5 AND (category = 'Something' OR category = 'Else')"
|
||||
# Post.delete_all "person_id = 5 AND (category = 'Something' OR category = 'Else')"
|
||||
def delete_all(conditions = nil)
|
||||
sql = "DELETE FROM #{table_name} "
|
||||
add_conditions!(sql, conditions)
|
||||
@@ -646,9 +628,16 @@ module ActiveRecord #:nodoc:
|
||||
"type"
|
||||
end
|
||||
|
||||
# Default sequence_name. Use set_sequence_name to override.
|
||||
# Lazy-set the sequence name to the connection's default. This method
|
||||
# is only ever called once since set_sequence_name overrides it.
|
||||
def sequence_name
|
||||
connection.default_sequence_name(table_name, primary_key)
|
||||
reset_sequence_name
|
||||
end
|
||||
|
||||
def reset_sequence_name
|
||||
default = connection.default_sequence_name(table_name, primary_key)
|
||||
set_sequence_name(default)
|
||||
default
|
||||
end
|
||||
|
||||
# Sets the table name to use to the given value, or (if the value
|
||||
@@ -699,9 +688,11 @@ module ActiveRecord #:nodoc:
|
||||
# given block. This is required for Oracle and is useful for any
|
||||
# database which relies on sequences for primary key generation.
|
||||
#
|
||||
# Setting the sequence name when using other dbs will have no effect.
|
||||
# If a sequence name is not explicitly set when using Oracle, it will
|
||||
# default to the commonly used pattern of: #{table_name}_seq
|
||||
# If a sequence name is not explicitly set when using Oracle or Firebird,
|
||||
# it will default to the commonly used pattern of: #{table_name}_seq
|
||||
#
|
||||
# If a sequence name is not explicitly set when using PostgreSQL, it
|
||||
# will discover the sequence corresponding to your primary key for you.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
@@ -955,8 +946,9 @@ module ActiveRecord #:nodoc:
|
||||
end
|
||||
|
||||
def type_condition
|
||||
type_condition = subclasses.inject("#{table_name}.#{inheritance_column} = '#{name.demodulize}' ") do |condition, subclass|
|
||||
condition << "OR #{table_name}.#{inheritance_column} = '#{subclass.name.demodulize}' "
|
||||
quoted_inheritance_column = connection.quote_column_name(inheritance_column)
|
||||
type_condition = subclasses.inject("#{table_name}.#{quoted_inheritance_column} = '#{name.demodulize}' ") do |condition, subclass|
|
||||
condition << "OR #{table_name}.#{quoted_inheritance_column} = '#{subclass.name.demodulize}' "
|
||||
end
|
||||
|
||||
" (#{type_condition}) "
|
||||
@@ -1010,7 +1002,7 @@ module ActiveRecord #:nodoc:
|
||||
|
||||
def construct_conditions_from_arguments(attribute_names, arguments)
|
||||
conditions = []
|
||||
attribute_names.each_with_index { |name, idx| conditions << "#{table_name}.#{name} #{attribute_condition(arguments[idx])} " }
|
||||
attribute_names.each_with_index { |name, idx| conditions << "#{table_name}.#{connection.quote_column_name(name)} #{attribute_condition(arguments[idx])} " }
|
||||
[ conditions.join(" AND "), *arguments[0...attribute_names.length] ]
|
||||
end
|
||||
|
||||
@@ -1053,12 +1045,12 @@ module ActiveRecord #:nodoc:
|
||||
def define_attr_method(name, value=nil, &block)
|
||||
sing = class << self; self; end
|
||||
sing.send :alias_method, "original_#{name}", name
|
||||
if value
|
||||
if block_given?
|
||||
sing.send :define_method, name, &block
|
||||
else
|
||||
# use eval instead of a block to work around a memory leak in dev
|
||||
# mode in fcgi
|
||||
sing.class_eval "def #{name}; #{value.to_s.inspect}; end"
|
||||
else
|
||||
sing.send :define_method, name, &block
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1312,6 +1304,7 @@ module ActiveRecord #:nodoc:
|
||||
|
||||
# Reloads the attributes of this object from the database.
|
||||
def reload
|
||||
clear_aggregation_cache
|
||||
clear_association_cache
|
||||
@attributes.update(self.class.find(self.id).instance_variable_get('@attributes'))
|
||||
self
|
||||
@@ -1450,6 +1443,10 @@ module ActiveRecord #:nodoc:
|
||||
|
||||
# Creates a new record with values matching those of the instance attributes.
|
||||
def create
|
||||
if self.id.nil? and connection.prefetch_primary_key?(self.class.table_name)
|
||||
self.id = connection.next_sequence_value(self.class.sequence_name)
|
||||
end
|
||||
|
||||
self.id = connection.insert(
|
||||
"INSERT INTO #{self.class.table_name} " +
|
||||
"(#{quoted_column_names.join(', ')}) " +
|
||||
@@ -1549,7 +1546,16 @@ module ActiveRecord #:nodoc:
|
||||
self.class.read_methods << attr_name
|
||||
end
|
||||
|
||||
self.class.class_eval("def #{symbol}; #{access_code}; end")
|
||||
begin
|
||||
self.class.class_eval("def #{symbol}; #{access_code}; end")
|
||||
rescue SyntaxError => err
|
||||
self.class.read_methods.delete(attr_name)
|
||||
if logger
|
||||
logger.warn "Exception occured during reader method compilation."
|
||||
logger.warn "Maybe #{attr_name} is not a valid Ruby identifier?"
|
||||
logger.warn "#{err.message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Returns true if the attribute is of a text column and marked for serialization.
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
module ActiveRecord
|
||||
# The root class of all active record objects.
|
||||
class Base
|
||||
class ConnectionSpecification #:nodoc:
|
||||
attr_reader :config, :adapter_method
|
||||
@@ -11,6 +10,28 @@ module ActiveRecord
|
||||
# The class -> [adapter_method, config] map
|
||||
@@defined_connections = {}
|
||||
|
||||
# The class -> thread id -> adapter cache.
|
||||
@@connection_cache = Hash.new { |h, k| h[k] = Hash.new }
|
||||
|
||||
# Returns the connection currently associated with the class. This can
|
||||
# also be used to "borrow" the connection to do database work unrelated
|
||||
# to any of the specific Active Records.
|
||||
def self.connection
|
||||
@@connection_cache[Thread.current.object_id][name] ||= retrieve_connection
|
||||
end
|
||||
|
||||
# Clears the cache which maps classes to connections.
|
||||
def self.clear_connection_cache!
|
||||
@@connection_cache.clear
|
||||
end
|
||||
|
||||
# Returns the connection currently associated with the class. This can
|
||||
# also be used to "borrow" the connection to do database work that isn't
|
||||
# easily done without going straight to SQL.
|
||||
def connection
|
||||
self.class.connection
|
||||
end
|
||||
|
||||
# Establishes the connection to the database. Accepts a hash as input where
|
||||
# the :adapter key must be specified with the name of a database adapter (in lower-case)
|
||||
# example for regular databases (MySQL, Postgresql, etc):
|
||||
@@ -44,7 +65,7 @@ module ActiveRecord
|
||||
raise AdapterNotSpecified unless defined? RAILS_ENV
|
||||
establish_connection(RAILS_ENV)
|
||||
when ConnectionSpecification
|
||||
@@defined_connections[self] = spec
|
||||
@@defined_connections[name] = spec
|
||||
when Symbol, String
|
||||
if configuration = configurations[spec.to_s]
|
||||
establish_connection(configuration)
|
||||
@@ -77,9 +98,11 @@ module ActiveRecord
|
||||
klass = self
|
||||
ar_super = ActiveRecord::Base.superclass
|
||||
until klass == ar_super
|
||||
if conn = active_connections[klass]
|
||||
if conn = active_connections[klass.name]
|
||||
# Reconnect if the connection is inactive.
|
||||
conn.reconnect! unless conn.active?
|
||||
return conn
|
||||
elsif conn = @@defined_connections[klass]
|
||||
elsif conn = @@defined_connections[klass.name]
|
||||
klass.connection = conn
|
||||
return self.connection
|
||||
end
|
||||
@@ -92,7 +115,7 @@ module ActiveRecord
|
||||
def self.connected?
|
||||
klass = self
|
||||
until klass == ActiveRecord::Base.superclass
|
||||
if active_connections[klass]
|
||||
if active_connections[klass.name]
|
||||
return true
|
||||
else
|
||||
klass = klass.superclass
|
||||
@@ -106,9 +129,10 @@ module ActiveRecord
|
||||
# can be used as argument for establish_connection, for easy
|
||||
# re-establishing of the connection.
|
||||
def self.remove_connection(klass=self)
|
||||
conn = @@defined_connections[klass]
|
||||
@@defined_connections.delete(klass)
|
||||
active_connections[klass] = nil
|
||||
conn = @@defined_connections[klass.name]
|
||||
@@defined_connections.delete(klass.name)
|
||||
@@connection_cache[Thread.current.object_id].delete(klass.name)
|
||||
active_connections.delete(klass.name)
|
||||
@connection = nil
|
||||
conn.config if conn
|
||||
end
|
||||
@@ -116,7 +140,7 @@ module ActiveRecord
|
||||
# Set the connection for the class.
|
||||
def self.connection=(spec)
|
||||
if spec.kind_of?(ActiveRecord::ConnectionAdapters::AbstractAdapter)
|
||||
active_connections[self] = spec
|
||||
active_connections[name] = spec
|
||||
elsif spec.kind_of?(ConnectionSpecification)
|
||||
self.connection = self.send(spec.adapter_method, spec.config)
|
||||
elsif spec.nil?
|
||||
|
||||
@@ -59,7 +59,7 @@ module ActiveRecord
|
||||
when :time then self.class.string_to_dummy_time(value)
|
||||
when :date then self.class.string_to_date(value)
|
||||
when :binary then self.class.binary_to_string(value)
|
||||
when :boolean then value == true or (value =~ /^t(rue)?$/i) == 0 or value.to_s == '1'
|
||||
when :boolean then self.class.value_to_boolean(value)
|
||||
else value
|
||||
end
|
||||
end
|
||||
@@ -75,7 +75,7 @@ module ActiveRecord
|
||||
when :time then "#{self.class.name}.string_to_dummy_time(#{var_name})"
|
||||
when :date then "#{self.class.name}.string_to_date(#{var_name})"
|
||||
when :binary then "#{self.class.name}.binary_to_string(#{var_name})"
|
||||
when :boolean then "(#{var_name} == true or (#{var_name} =~ /^t(?:true)?$/i) == 0 or #{var_name}.to_s == '1')"
|
||||
when :boolean then "#{self.class.name}.value_to_boolean(#{var_name})"
|
||||
else nil
|
||||
end
|
||||
end
|
||||
@@ -120,6 +120,15 @@ module ActiveRecord
|
||||
Time.send(Base.default_timezone, *time_array) rescue nil
|
||||
end
|
||||
|
||||
# convert something to a boolean
|
||||
def self.value_to_boolean(value)
|
||||
return value if value==true || value==false
|
||||
case value.to_s.downcase
|
||||
when "true", "t", "1" then true
|
||||
else false
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def extract_limit(sql_type)
|
||||
$1.to_i if sql_type =~ /\((.*)\)/
|
||||
|
||||
@@ -43,6 +43,9 @@ module ActiveRecord
|
||||
# Any extra options you want appended to the table definition.
|
||||
# [<tt>:temporary</tt>]
|
||||
# Make a temporary table.
|
||||
# [<tt>:force</tt>]
|
||||
# Set to true or false to drop the table before creating it.
|
||||
# Defaults to false.
|
||||
#
|
||||
# ===== Examples
|
||||
# ====== Add a backend specific option to the generated SQL (MySQL)
|
||||
|
||||
@@ -32,40 +32,60 @@ module ActiveRecord
|
||||
def adapter_name
|
||||
'Abstract'
|
||||
end
|
||||
|
||||
|
||||
# Does this adapter support migrations? Backend specific, as the
|
||||
# abstract adapter always returns +false+.
|
||||
def supports_migrations?
|
||||
false
|
||||
end
|
||||
|
||||
def reset_runtime #:nodoc:
|
||||
rt = @runtime
|
||||
@runtime = 0
|
||||
return rt
|
||||
# Should primary key values be selected from their corresponding
|
||||
# sequence before the insert statement? If true, next_sequence_value
|
||||
# is called before each insert to set the record's primary key.
|
||||
# This is false for all adapters but Firebird.
|
||||
def prefetch_primary_key?(table_name = nil)
|
||||
false
|
||||
end
|
||||
|
||||
protected
|
||||
def reset_runtime #:nodoc:
|
||||
rt, @runtime = @runtime, 0
|
||||
rt
|
||||
end
|
||||
|
||||
|
||||
# CONNECTION MANAGEMENT ====================================
|
||||
|
||||
# Is this connection active and ready to perform queries?
|
||||
def active?
|
||||
true
|
||||
end
|
||||
|
||||
# Close this connection and open a new one in its place.
|
||||
def reconnect!
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
def log(sql, name)
|
||||
begin
|
||||
if block_given?
|
||||
if @logger and @logger.level <= Logger::INFO
|
||||
result = nil
|
||||
seconds = Benchmark.realtime { result = yield }
|
||||
@runtime += seconds
|
||||
log_info(sql, name, seconds)
|
||||
result
|
||||
else
|
||||
yield
|
||||
end
|
||||
if block_given?
|
||||
if @logger and @logger.level <= Logger::INFO
|
||||
result = nil
|
||||
seconds = Benchmark.realtime { result = yield }
|
||||
@runtime += seconds
|
||||
log_info(sql, name, seconds)
|
||||
result
|
||||
else
|
||||
log_info(sql, name, 0)
|
||||
nil
|
||||
yield
|
||||
end
|
||||
rescue Exception => e
|
||||
log_info("#{e.message}: #{sql}", name, 0)
|
||||
raise ActiveRecord::StatementInvalid, "#{e.message}: #{sql}"
|
||||
else
|
||||
log_info(sql, name, 0)
|
||||
nil
|
||||
end
|
||||
rescue Exception => e
|
||||
# Log message and raise exception.
|
||||
message = "#{e.class.name}: #{e.message}: #{sql}"
|
||||
log_info(message, name, 0)
|
||||
raise ActiveRecord::StatementInvalid, message
|
||||
end
|
||||
|
||||
def log_info(sql, name, runtime)
|
||||
|
||||
@@ -0,0 +1,414 @@
|
||||
# Author: Ken Kunz <kennethkunz@gmail.com>
|
||||
|
||||
require 'active_record/connection_adapters/abstract_adapter'
|
||||
|
||||
module FireRuby # :nodoc: all
|
||||
class Database
|
||||
def self.new_from_params(database, host, port, service)
|
||||
db_string = ""
|
||||
if host
|
||||
db_string << host
|
||||
db_string << "/#{service || port}" if service || port
|
||||
db_string << ":"
|
||||
end
|
||||
db_string << database
|
||||
new(db_string)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ActiveRecord
|
||||
class << Base
|
||||
def firebird_connection(config) # :nodoc:
|
||||
require_library_or_gem 'fireruby'
|
||||
unless defined? FireRuby::SQLType
|
||||
raise AdapterNotFound,
|
||||
'The Firebird adapter requires FireRuby version 0.4.0 or greater; you appear ' <<
|
||||
'to be running an older version -- please update FireRuby (gem install fireruby).'
|
||||
end
|
||||
config = config.symbolize_keys
|
||||
unless config.has_key?(:database)
|
||||
raise ArgumentError, "No database specified. Missing argument: database."
|
||||
end
|
||||
options = config[:charset] ? { CHARACTER_SET => config[:charset] } : {}
|
||||
connection_params = [config[:username], config[:password], options]
|
||||
db = FireRuby::Database.new_from_params(*config.values_at(:database, :host, :port, :service))
|
||||
connection = db.connect(*connection_params)
|
||||
ConnectionAdapters::FirebirdAdapter.new(connection, logger, connection_params)
|
||||
end
|
||||
end
|
||||
|
||||
module ConnectionAdapters
|
||||
class FirebirdColumn < Column # :nodoc:
|
||||
VARCHAR_MAX_LENGTH = 32_765
|
||||
BLOB_MAX_LENGTH = 32_767
|
||||
|
||||
def initialize(name, domain, type, sub_type, length, precision, scale, default_source, null_flag)
|
||||
@firebird_type = FireRuby::SQLType.to_base_type(type, sub_type).to_s
|
||||
super(name.downcase, nil, @firebird_type, !null_flag)
|
||||
@default = parse_default(default_source) if default_source
|
||||
@limit = type == 'BLOB' ? BLOB_MAX_LENGTH : length
|
||||
@domain, @sub_type, @precision, @scale = domain, sub_type, precision, scale
|
||||
end
|
||||
|
||||
def type
|
||||
if @domain =~ /BOOLEAN/
|
||||
:boolean
|
||||
elsif @type == :binary and @sub_type == 1
|
||||
:text
|
||||
else
|
||||
@type
|
||||
end
|
||||
end
|
||||
|
||||
# Submits a _CAST_ query to the database, casting the default value to the specified SQL type.
|
||||
# This enables Firebird to provide an actual value when context variables are used as column
|
||||
# defaults (such as CURRENT_TIMESTAMP).
|
||||
def default
|
||||
if @default
|
||||
sql = "SELECT CAST(#{@default} AS #{column_def}) FROM RDB$DATABASE"
|
||||
connection = ActiveRecord::Base.active_connections.values.detect { |conn| conn && conn.adapter_name == 'Firebird' }
|
||||
if connection
|
||||
type_cast connection.execute(sql).to_a.first['CAST']
|
||||
else
|
||||
raise ConnectionNotEstablished, "No Firebird connections established."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def type_cast(value)
|
||||
if type == :boolean
|
||||
value == true or value == ActiveRecord::ConnectionAdapters::FirebirdAdapter.boolean_domain[:true]
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def parse_default(default_source)
|
||||
default_source =~ /^\s*DEFAULT\s+(.*)\s*$/i
|
||||
return $1 unless $1.upcase == "NULL"
|
||||
end
|
||||
|
||||
def column_def
|
||||
case @firebird_type
|
||||
when 'BLOB' then "VARCHAR(#{VARCHAR_MAX_LENGTH})"
|
||||
when 'CHAR', 'VARCHAR' then "#{@firebird_type}(#{@limit})"
|
||||
when 'NUMERIC', 'DECIMAL' then "#{@firebird_type}(#{@precision},#{@scale.abs})"
|
||||
when 'DOUBLE' then "DOUBLE PRECISION"
|
||||
else @firebird_type
|
||||
end
|
||||
end
|
||||
|
||||
def simplified_type(field_type)
|
||||
if field_type == 'TIMESTAMP'
|
||||
:datetime
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# The Firebird adapter relies on the FireRuby[http://rubyforge.org/projects/fireruby/]
|
||||
# extension, version 0.4.0 or later (available as a gem or from
|
||||
# RubyForge[http://rubyforge.org/projects/fireruby/]). FireRuby works with
|
||||
# Firebird 1.5.x on Linux, OS X and Win32 platforms.
|
||||
#
|
||||
# == Usage Notes
|
||||
#
|
||||
# === Sequence (Generator) Names
|
||||
# The Firebird adapter supports the same approach adopted for the Oracle
|
||||
# adapter. See ActiveRecord::Base#set_sequence_name for more details.
|
||||
#
|
||||
# Note that in general there is no need to create a <tt>BEFORE INSERT</tt>
|
||||
# trigger corresponding to a Firebird sequence generator when using
|
||||
# ActiveRecord. In other words, you don't have to try to make Firebird
|
||||
# simulate an <tt>AUTO_INCREMENT</tt> or +IDENTITY+ column. When saving a
|
||||
# new record, ActiveRecord pre-fetches the next sequence value for the table
|
||||
# and explicitly includes it in the +INSERT+ statement. (Pre-fetching the
|
||||
# next primary key value is the only reliable method for the Firebird
|
||||
# adapter to report back the +id+ after a successful insert.)
|
||||
#
|
||||
# === BOOLEAN Domain
|
||||
# Firebird 1.5 does not provide a native +BOOLEAN+ type. But you can easily
|
||||
# define a +BOOLEAN+ _domain_ for this purpose, e.g.:
|
||||
#
|
||||
# CREATE DOMAIN D_BOOLEAN AS SMALLINT CHECK (VALUE IN (0, 1));
|
||||
#
|
||||
# When the Firebird adapter encounters a column that is based on a domain
|
||||
# that includes "BOOLEAN" in the domain name, it will attempt to treat
|
||||
# the column as a +BOOLEAN+.
|
||||
#
|
||||
# By default, the Firebird adapter will assume that the BOOLEAN domain is
|
||||
# defined as above. This can be modified if needed. For example, if you
|
||||
# have a legacy schema with the following +BOOLEAN+ domain defined:
|
||||
#
|
||||
# CREATE DOMAIN BOOLEAN AS CHAR(1) CHECK (VALUE IN ('T', 'F'));
|
||||
#
|
||||
# ...you can add the following line to your <tt>environment.rb</tt> file:
|
||||
#
|
||||
# ActiveRecord::ConnectionAdapters::FirebirdAdapter.boolean_domain = { :true => 'T', :false => 'F' }
|
||||
#
|
||||
# === BLOB Elements
|
||||
# The Firebird adapter currently provides only limited support for +BLOB+
|
||||
# columns. You cannot currently retrieve or insert a +BLOB+ as an IO stream.
|
||||
# When selecting a +BLOB+, the entire element is converted into a String.
|
||||
# When inserting or updating a +BLOB+, the entire value is included in-line
|
||||
# in the SQL statement, limiting you to values <= 32KB in size.
|
||||
#
|
||||
# === Column Name Case Semantics
|
||||
# Firebird and ActiveRecord have somewhat conflicting case semantics for
|
||||
# column names.
|
||||
#
|
||||
# [*Firebird*]
|
||||
# The standard practice is to use unquoted column names, which can be
|
||||
# thought of as case-insensitive. (In fact, Firebird converts them to
|
||||
# uppercase.) Quoted column names (not typically used) are case-sensitive.
|
||||
# [*ActiveRecord*]
|
||||
# Attribute accessors corresponding to column names are case-sensitive.
|
||||
# The defaults for primary key and inheritance columns are lowercase, and
|
||||
# in general, people use lowercase attribute names.
|
||||
#
|
||||
# In order to map between the differing semantics in a way that conforms
|
||||
# to common usage for both Firebird and ActiveRecord, uppercase column names
|
||||
# in Firebird are converted to lowercase attribute names in ActiveRecord,
|
||||
# and vice-versa. Mixed-case column names retain their case in both
|
||||
# directions. Lowercase (quoted) Firebird column names are not supported.
|
||||
# This is similar to the solutions adopted by other adapters.
|
||||
#
|
||||
# In general, the best approach is to use unqouted (case-insensitive) column
|
||||
# names in your Firebird DDL (or if you must quote, use uppercase column
|
||||
# names). These will correspond to lowercase attributes in ActiveRecord.
|
||||
#
|
||||
# For example, a Firebird table based on the following DDL:
|
||||
#
|
||||
# CREATE TABLE products (
|
||||
# id BIGINT NOT NULL PRIMARY KEY,
|
||||
# "TYPE" VARCHAR(50),
|
||||
# name VARCHAR(255) );
|
||||
#
|
||||
# ...will correspond to an ActiveRecord model class called +Product+ with
|
||||
# the following attributes: +id+, +type+, +name+.
|
||||
#
|
||||
# ==== Quoting <tt>"TYPE"</tt> and other Firebird reserved words:
|
||||
# In ActiveRecord, the default inheritance column name is +type+. The word
|
||||
# _type_ is a Firebird reserved word, so it must be quoted in any Firebird
|
||||
# SQL statements. Because of the case mapping described above, you should
|
||||
# always reference this column using quoted-uppercase syntax
|
||||
# (<tt>"TYPE"</tt>) within Firebird DDL or other SQL statements (as in the
|
||||
# example above). This holds true for any other Firebird reserved words used
|
||||
# as column names as well.
|
||||
#
|
||||
# === Migrations
|
||||
# The Firebird adapter does not currently support Migrations. I hope to
|
||||
# add this feature in the near future.
|
||||
#
|
||||
# == Connection Options
|
||||
# The following options are supported by the Firebird adapter. None of the
|
||||
# options have default values.
|
||||
#
|
||||
# <tt>:database</tt>::
|
||||
# <i>Required option.</i> Specifies one of: (i) a Firebird database alias;
|
||||
# (ii) the full path of a database file; _or_ (iii) a full Firebird
|
||||
# connection string. <i>Do not specify <tt>:host</tt>, <tt>:service</tt>
|
||||
# or <tt>:port</tt> as separate options when using a full connection
|
||||
# string.</i>
|
||||
# <tt>:host</tt>::
|
||||
# Set to <tt>"remote.host.name"</tt> for remote database connections.
|
||||
# May be omitted for local connections if a full database path is
|
||||
# specified for <tt>:database</tt>. Some platforms require a value of
|
||||
# <tt>"localhost"</tt> for local connections when using a Firebird
|
||||
# database _alias_.
|
||||
# <tt>:service</tt>::
|
||||
# Specifies a service name for the connection. Only used if <tt>:host</tt>
|
||||
# is provided. Required when connecting to a non-standard service.
|
||||
# <tt>:port</tt>::
|
||||
# Specifies the connection port. Only used if <tt>:host</tt> is provided
|
||||
# and <tt>:service</tt> is not. Required when connecting to a non-standard
|
||||
# port and <tt>:service</tt> is not defined.
|
||||
# <tt>:username</tt>::
|
||||
# Specifies the database user. May be omitted or set to +nil+ (together
|
||||
# with <tt>:password</tt>) to use the underlying operating system user
|
||||
# credentials on supported platforms.
|
||||
# <tt>:password</tt>::
|
||||
# Specifies the database password. Must be provided if <tt>:username</tt>
|
||||
# is explicitly specified; should be omitted if OS user credentials are
|
||||
# are being used.
|
||||
# <tt>:charset</tt>::
|
||||
# Specifies the character set to be used by the connection. Refer to
|
||||
# Firebird documentation for valid options.
|
||||
class FirebirdAdapter < AbstractAdapter
|
||||
@@boolean_domain = { :true => 1, :false => 0 }
|
||||
cattr_accessor :boolean_domain
|
||||
|
||||
def initialize(connection, logger, connection_params=nil)
|
||||
super(connection, logger)
|
||||
@connection_params = connection_params
|
||||
end
|
||||
|
||||
def adapter_name # :nodoc:
|
||||
'Firebird'
|
||||
end
|
||||
|
||||
# Returns true for Firebird adapter (since Firebird requires primary key
|
||||
# values to be pre-fetched before insert). See also #next_sequence_value.
|
||||
def prefetch_primary_key?(table_name = nil)
|
||||
true
|
||||
end
|
||||
|
||||
def default_sequence_name(table_name, primary_key) # :nodoc:
|
||||
"#{table_name}_seq"
|
||||
end
|
||||
|
||||
|
||||
# QUOTING ==================================================
|
||||
|
||||
def quote(value, column = nil) # :nodoc:
|
||||
if [Time, DateTime].include?(value.class)
|
||||
"CAST('#{value.strftime("%Y-%m-%d %H:%M:%S")}' AS TIMESTAMP)"
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def quote_string(string) # :nodoc:
|
||||
string.gsub(/'/, "''")
|
||||
end
|
||||
|
||||
def quote_column_name(column_name) # :nodoc:
|
||||
%Q("#{ar_to_fb_case(column_name)}")
|
||||
end
|
||||
|
||||
def quoted_true # :nodoc:
|
||||
quote(boolean_domain[:true])
|
||||
end
|
||||
|
||||
def quoted_false # :nodoc:
|
||||
quote(boolean_domain[:false])
|
||||
end
|
||||
|
||||
|
||||
# CONNECTION MANAGEMENT ====================================
|
||||
|
||||
def active?
|
||||
not @connection.closed?
|
||||
end
|
||||
|
||||
def reconnect!
|
||||
@connection.close
|
||||
@connection = @connection.database.connect(*@connection_params)
|
||||
end
|
||||
|
||||
|
||||
# DATABASE STATEMENTS ======================================
|
||||
|
||||
def select_all(sql, name = nil) # :nodoc:
|
||||
select(sql, name)
|
||||
end
|
||||
|
||||
def select_one(sql, name = nil) # :nodoc:
|
||||
result = select(sql, name)
|
||||
result.nil? ? nil : result.first
|
||||
end
|
||||
|
||||
def execute(sql, name = nil, &block) # :nodoc:
|
||||
log(sql, name) do
|
||||
if @transaction
|
||||
@connection.execute(sql, @transaction, &block)
|
||||
else
|
||||
@connection.execute_immediate(sql, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) # :nodoc:
|
||||
execute(sql, name)
|
||||
id_value
|
||||
end
|
||||
|
||||
alias_method :update, :execute
|
||||
alias_method :delete, :execute
|
||||
|
||||
def begin_db_transaction() # :nodoc:
|
||||
@transaction = @connection.start_transaction
|
||||
end
|
||||
|
||||
def commit_db_transaction() # :nodoc:
|
||||
@transaction.commit
|
||||
ensure
|
||||
@transaction = nil
|
||||
end
|
||||
|
||||
def rollback_db_transaction() # :nodoc:
|
||||
@transaction.rollback
|
||||
ensure
|
||||
@transaction = nil
|
||||
end
|
||||
|
||||
def add_limit_offset!(sql, options) # :nodoc:
|
||||
if options[:limit]
|
||||
limit_string = "FIRST #{options[:limit]}"
|
||||
limit_string << " SKIP #{options[:offset]}" if options[:offset]
|
||||
sql.sub!(/\A(\s*SELECT\s)/i, '\&' + limit_string + ' ')
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the next sequence value from a sequence generator. Not generally
|
||||
# called directly; used by ActiveRecord to get the next primary key value
|
||||
# when inserting a new database record (see #prefetch_primary_key?).
|
||||
def next_sequence_value(sequence_name)
|
||||
FireRuby::Generator.new(sequence_name, @connection).next(1)
|
||||
end
|
||||
|
||||
|
||||
# SCHEMA STATEMENTS ========================================
|
||||
|
||||
def columns(table_name, name = nil) # :nodoc:
|
||||
sql = <<-END_SQL
|
||||
SELECT r.rdb$field_name, r.rdb$field_source, f.rdb$field_type, f.rdb$field_sub_type,
|
||||
f.rdb$field_length, f.rdb$field_precision, f.rdb$field_scale,
|
||||
COALESCE(r.rdb$default_source, f.rdb$default_source) rdb$default_source,
|
||||
COALESCE(r.rdb$null_flag, f.rdb$null_flag) rdb$null_flag
|
||||
FROM rdb$relation_fields r
|
||||
JOIN rdb$fields f ON r.rdb$field_source = f.rdb$field_name
|
||||
WHERE r.rdb$relation_name = '#{table_name.to_s.upcase}'
|
||||
ORDER BY r.rdb$field_position
|
||||
END_SQL
|
||||
execute(sql, name).collect do |field|
|
||||
field_values = field.values.collect do |value|
|
||||
case value
|
||||
when String then value.rstrip
|
||||
when FireRuby::Blob then value.to_s
|
||||
else value
|
||||
end
|
||||
end
|
||||
FirebirdColumn.new(*field_values)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def select(sql, name = nil)
|
||||
execute(sql, name).collect do |row|
|
||||
hashed_row = {}
|
||||
row.each do |column, value|
|
||||
value = value.to_s if FireRuby::Blob === value
|
||||
hashed_row[fb_to_ar_case(column)] = value
|
||||
end
|
||||
hashed_row
|
||||
end
|
||||
end
|
||||
|
||||
# Maps uppercase Firebird column names to lowercase for ActiveRecord;
|
||||
# mixed-case columns retain their original case.
|
||||
def fb_to_ar_case(column_name)
|
||||
column_name =~ /[[:lower:]]/ ? column_name : column_name.downcase
|
||||
end
|
||||
|
||||
# Maps lowercase ActiveRecord column names to uppercase for Fierbird;
|
||||
# mixed-case columns retain their original case.
|
||||
def ar_to_fb_case(column_name)
|
||||
column_name =~ /[[:upper:]]/ ? column_name : column_name.upcase
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -5,23 +5,19 @@ module ActiveRecord
|
||||
# Establishes a connection to the database that's used by all Active Record objects.
|
||||
def self.mysql_connection(config) # :nodoc:
|
||||
# Only include the MySQL driver if one hasn't already been loaded
|
||||
unless self.class.const_defined?(:Mysql)
|
||||
unless defined? Mysql
|
||||
begin
|
||||
require_library_or_gem 'mysql'
|
||||
# The C version of mysql returns null fields in each_hash if Mysql::VERSION is defined
|
||||
ConnectionAdapters::MysqlAdapter.null_values_in_each_hash = Mysql.const_defined?(:VERSION)
|
||||
rescue LoadError => cannot_require_mysql
|
||||
# Only use the supplied backup Ruby/MySQL driver if no driver is already in place
|
||||
begin
|
||||
require 'active_record/vendor/mysql'
|
||||
# The ruby version of mysql returns null fields in each_hash
|
||||
ConnectionAdapters::MysqlAdapter.null_values_in_each_hash = true
|
||||
rescue LoadError
|
||||
raise cannot_require_mysql
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
config = config.symbolize_keys
|
||||
host = config[:host]
|
||||
@@ -38,7 +34,17 @@ module ActiveRecord
|
||||
|
||||
mysql = Mysql.init
|
||||
mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslkey]
|
||||
ConnectionAdapters::MysqlAdapter.new(mysql.real_connect(host, username, password, database, port, socket), logger, [host, username, password, database, port, socket])
|
||||
if config[:encoding]
|
||||
begin
|
||||
mysql.options(Mysql::SET_CHARSET_NAME, config[:encoding])
|
||||
rescue
|
||||
raise ActiveRecord::ConnectionFailed, 'The :encoding option is only available for MySQL 4.1 and later with the mysql-ruby driver. Again, this does not work with the ruby-mysql driver or MySQL < 4.1.'
|
||||
end
|
||||
end
|
||||
|
||||
conn = mysql.real_connect(host, username, password, database, port, socket)
|
||||
conn.query("SET NAMES '#{config[:encoding]}'") if config[:encoding]
|
||||
ConnectionAdapters::MysqlAdapter.new(conn, logger, [host, username, password, database, port, socket], mysql)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -77,9 +83,6 @@ module ActiveRecord
|
||||
@@emulate_booleans = true
|
||||
cattr_accessor :emulate_booleans
|
||||
|
||||
cattr_accessor :null_values_in_each_hash
|
||||
@@null_values_in_each_hash = false
|
||||
|
||||
LOST_CONNECTION_ERROR_MESSAGES = [
|
||||
"Server shutdown in progress",
|
||||
"Broken pipe",
|
||||
@@ -87,9 +90,11 @@ module ActiveRecord
|
||||
"MySQL server has gone away"
|
||||
]
|
||||
|
||||
def initialize(connection, logger, connection_options=nil)
|
||||
def initialize(connection, logger, connection_options=nil, mysql=Mysql)
|
||||
super(connection, logger)
|
||||
@connection_options = connection_options
|
||||
@null_values_in_each_hash = Mysql.const_defined?(:VERSION)
|
||||
@mysql = mysql
|
||||
end
|
||||
|
||||
def adapter_name #:nodoc:
|
||||
@@ -119,12 +124,21 @@ module ActiveRecord
|
||||
|
||||
# QUOTING ==================================================
|
||||
|
||||
def quote(value, column = nil)
|
||||
if value.kind_of?(String) && column && column.type == :binary
|
||||
s = column.class.string_to_binary(value).unpack("H*")[0]
|
||||
"x'#{s}'"
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def quote_column_name(name) #:nodoc:
|
||||
"`#{name}`"
|
||||
end
|
||||
|
||||
def quote_string(string) #:nodoc:
|
||||
Mysql::quote(string)
|
||||
@mysql.quote(string)
|
||||
end
|
||||
|
||||
def quoted_true
|
||||
@@ -136,6 +150,31 @@ module ActiveRecord
|
||||
end
|
||||
|
||||
|
||||
# CONNECTION MANAGEMENT ====================================
|
||||
|
||||
def active?
|
||||
if @connection.respond_to?(:stat)
|
||||
@connection.stat
|
||||
else
|
||||
@connection.query 'select 1'
|
||||
end
|
||||
|
||||
# mysql-ruby doesn't raise an exception when stat fails.
|
||||
if @connection.respond_to?(:errno)
|
||||
@connection.errno.zero?
|
||||
else
|
||||
true
|
||||
end
|
||||
rescue Mysql::Error
|
||||
false
|
||||
end
|
||||
|
||||
def reconnect!
|
||||
@connection.close rescue nil
|
||||
connect
|
||||
end
|
||||
|
||||
|
||||
# DATABASE STATEMENTS ======================================
|
||||
|
||||
def select_all(sql, name = nil) #:nodoc:
|
||||
@@ -148,22 +187,10 @@ module ActiveRecord
|
||||
end
|
||||
|
||||
def execute(sql, name = nil, retries = 2) #:nodoc:
|
||||
unless @logger
|
||||
@connection.query(sql)
|
||||
else
|
||||
log(sql, name) { @connection.query(sql) }
|
||||
end
|
||||
log(sql, name) { @connection.query(sql) }
|
||||
rescue ActiveRecord::StatementInvalid => exception
|
||||
if LOST_CONNECTION_ERROR_MESSAGES.any? { |msg| exception.message.split(":").first =~ /^#{msg}/ }
|
||||
@connection.real_connect(*@connection_options)
|
||||
unless @logger
|
||||
@connection.query(sql)
|
||||
else
|
||||
@logger.info "Retrying invalid statement with reopened connection"
|
||||
log(sql, name) { @connection.query(sql) }
|
||||
end
|
||||
elsif exception.message.split(":").first =~ /Packets out of order/
|
||||
raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem update mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information."
|
||||
if exception.message.split(":").first =~ /Packets out of order/
|
||||
raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
|
||||
else
|
||||
raise
|
||||
end
|
||||
@@ -291,11 +318,24 @@ module ActiveRecord
|
||||
|
||||
|
||||
private
|
||||
def connect
|
||||
encoding = @config[:encoding]
|
||||
if encoding
|
||||
begin
|
||||
@connection.options(Mysql::SET_CHARSET_NAME, encoding)
|
||||
rescue
|
||||
raise ActiveRecord::ConnectionFailed, 'The :encoding option is only available for MySQL 4.1 and later with the mysql-ruby driver. Again, this does not work with the ruby-mysql driver or MySQL < 4.1.'
|
||||
end
|
||||
end
|
||||
@connection.real_connect(*@connection_options)
|
||||
execute("SET NAMES '#{encoding}'") if encoding
|
||||
end
|
||||
|
||||
def select(sql, name = nil)
|
||||
@connection.query_with_result = true
|
||||
result = execute(sql, name)
|
||||
rows = []
|
||||
if @@null_values_in_each_hash
|
||||
if @null_values_in_each_hash
|
||||
result.each_hash { |row| rows << row }
|
||||
else
|
||||
all_fields = result.fetch_fields.inject({}) { |fields, f| fields[f.name] = nil; fields }
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
# portions Copyright 2005 Graham Jenkins
|
||||
|
||||
require 'active_record/connection_adapters/abstract_adapter'
|
||||
require 'delegate'
|
||||
|
||||
begin
|
||||
require_library_or_gem 'oci8' unless self.class.const_defined? :OCI8
|
||||
@@ -30,11 +31,8 @@ begin
|
||||
module ActiveRecord
|
||||
class Base
|
||||
def self.oci_connection(config) #:nodoc:
|
||||
conn = OCI8.new config[:username], config[:password], config[:host]
|
||||
conn.exec %q{alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'}
|
||||
conn.exec %q{alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS'}
|
||||
conn.autocommit = true
|
||||
ConnectionAdapters::OCIAdapter.new conn, logger
|
||||
# Use OCI8AutoRecover instead of normal OCI8 driver.
|
||||
ConnectionAdapters::OCIAdapter.new OCI8AutoRecover.new(config), logger
|
||||
end
|
||||
|
||||
# Enable the id column to be bound into the sql later, by the adapter's insert method.
|
||||
@@ -118,7 +116,7 @@ begin
|
||||
return value if value.is_a? Time
|
||||
time_array = ParseDate.parsedate value
|
||||
time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
|
||||
Time.send Base.default_timezone, *time_array
|
||||
Time.send(Base.default_timezone, *time_array) rescue nil
|
||||
end
|
||||
|
||||
def guess_date_or_time(value)
|
||||
@@ -213,6 +211,27 @@ begin
|
||||
end
|
||||
|
||||
|
||||
# CONNECTION MANAGEMENT ====================================#
|
||||
|
||||
# Returns true if the connection is active.
|
||||
def active?
|
||||
# Pings the connection to check if it's still good. Note that an
|
||||
# #active? method is also available, but that simply returns the
|
||||
# last known state, which isn't good enough if the connection has
|
||||
# gone stale since the last use.
|
||||
@connection.ping
|
||||
rescue OCIError
|
||||
false
|
||||
end
|
||||
|
||||
# Reconnects to the database.
|
||||
def reconnect!
|
||||
@connection.reset!
|
||||
rescue OCIError => e
|
||||
@logger.warn "#{adapter_name} automatic reconnection failed: #{e.message}"
|
||||
end
|
||||
|
||||
|
||||
# DATABASE STATEMENTS ======================================
|
||||
#
|
||||
# see: abstract/database_statements.rb
|
||||
@@ -316,23 +335,28 @@ begin
|
||||
table_name = table_name.to_s.upcase
|
||||
owner = table_name.include?('.') ? "'#{table_name.split('.').first}'" : "user"
|
||||
table = "'#{table_name.split('.').last}'"
|
||||
scope = (owner == "user" ? "user" : "all")
|
||||
|
||||
select_all(%Q{
|
||||
select column_name, data_type, data_default, nullable,
|
||||
case when data_type = 'NUMBER' then data_precision
|
||||
when data_type = 'VARCHAR2' then data_length
|
||||
else null end as length,
|
||||
case when data_type = 'NUMBER' then data_scale
|
||||
else null end as scale
|
||||
from all_catalog cat, all_synonyms syn, all_tab_columns col
|
||||
where cat.owner = #{owner}
|
||||
and cat.table_name = #{table}
|
||||
and syn.owner (+)= cat.owner
|
||||
and syn.synonym_name (+)= cat.table_name
|
||||
and col.owner = nvl(syn.table_owner, cat.owner)
|
||||
and col.table_name = nvl(syn.table_name, cat.table_name)
|
||||
}).map do |row|
|
||||
row['data_default'].gsub!(/^'(.*)'\s*$/, '\1') if row['data_default']
|
||||
table_cols = %Q{
|
||||
select column_name, data_type, data_default, nullable,
|
||||
decode(data_type, 'NUMBER', data_precision,
|
||||
'VARCHAR2', data_length,
|
||||
null) as length,
|
||||
decode(data_type, 'NUMBER', data_scale, null) as scale
|
||||
from #{scope}_catalog cat, #{scope}_synonyms syn, all_tab_columns col
|
||||
where cat.table_name = #{table}
|
||||
and syn.synonym_name (+)= cat.table_name
|
||||
and col.table_name = nvl(syn.table_name, cat.table_name)
|
||||
and col.owner = nvl(syn.table_owner, #{(scope == "all" ? "cat.owner" : "user")}) }
|
||||
|
||||
if scope == "all"
|
||||
table_cols << %Q{
|
||||
and cat.owner = #{owner}
|
||||
and syn.owner (+)= cat.owner }
|
||||
end
|
||||
|
||||
select_all(table_cols, name).map do |row|
|
||||
row['data_default'].sub!(/^'(.*)'\s*$/, '\1') if row['data_default']
|
||||
OCIColumn.new(
|
||||
oci_downcase(row['column_name']),
|
||||
row['data_default'],
|
||||
@@ -479,10 +503,99 @@ begin
|
||||
when 187 : @stmt.defineByPos(i, OraDate) # Read TIMESTAMP values
|
||||
else define_a_column_pre_ar i
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# The OCIConnectionFactory factors out the code necessary to connect and
|
||||
# configure an OCI connection.
|
||||
class OCIConnectionFactory #:nodoc:
|
||||
def new_connection(username, password, host)
|
||||
conn = OCI8.new username, password, host
|
||||
conn.exec %q{alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'}
|
||||
conn.exec %q{alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS'} rescue nil
|
||||
conn.autocommit = true
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# The OCI8AutoRecover class enhances the OCI8 driver with auto-recover and
|
||||
# reset functionality. If a call to #exec fails, and autocommit is turned on
|
||||
# (ie., we're not in the middle of a longer transaction), it will
|
||||
# automatically reconnect and try again. If autocommit is turned off,
|
||||
# this would be dangerous (as the earlier part of the implied transaction
|
||||
# may have failed silently if the connection died) -- so instead the
|
||||
# connection is marked as dead, to be reconnected on it's next use.
|
||||
class OCI8AutoRecover < DelegateClass(OCI8) #:nodoc:
|
||||
attr_accessor :active
|
||||
alias :active? :active
|
||||
|
||||
cattr_accessor :auto_retry
|
||||
class << self
|
||||
alias :auto_retry? :auto_retry
|
||||
end
|
||||
@@auto_retry = false
|
||||
|
||||
def initialize(config, factory = OCIConnectionFactory.new)
|
||||
@active = true
|
||||
@username, @password, @host = config[:username], config[:password], config[:host]
|
||||
@factory = factory
|
||||
@connection = @factory.new_connection @username, @password, @host
|
||||
super @connection
|
||||
end
|
||||
|
||||
# Checks connection, returns true if active. Note that ping actively
|
||||
# checks the connection, while #active? simply returns the last
|
||||
# known state.
|
||||
def ping
|
||||
@connection.commit
|
||||
@active = true
|
||||
rescue
|
||||
@active = false
|
||||
raise
|
||||
end
|
||||
|
||||
# Resets connection, by logging off and creating a new connection.
|
||||
def reset!
|
||||
logoff rescue nil
|
||||
begin
|
||||
@connection = @factory.new_connection @username, @password, @host
|
||||
__setobj__ @connection
|
||||
@active = true
|
||||
rescue
|
||||
@active = false
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
# ORA-00028: your session has been killed
|
||||
# ORA-01012: not logged on
|
||||
# ORA-03113: end-of-file on communication channel
|
||||
# ORA-03114: not connected to ORACLE
|
||||
LOST_CONNECTION_ERROR_CODES = [ 28, 1012, 3113, 3114 ]
|
||||
|
||||
# Adds auto-recovery functionality.
|
||||
#
|
||||
# See: http://www.jiubao.org/ruby-oci8/api.en.html#label-11
|
||||
def exec(sql, *bindvars)
|
||||
should_retry = self.class.auto_retry? && autocommit?
|
||||
|
||||
begin
|
||||
@connection.exec(sql, *bindvars)
|
||||
rescue OCIError => e
|
||||
raise unless LOST_CONNECTION_ERROR_CODES.include?(e.code)
|
||||
@active = false
|
||||
raise unless should_retry
|
||||
should_retry = false
|
||||
reset! rescue nil
|
||||
retry
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
rescue LoadError
|
||||
# OCI8 driver is unavailable.
|
||||
end
|
||||
|
||||
@@ -12,7 +12,6 @@ module ActiveRecord
|
||||
username = config[:username].to_s
|
||||
password = config[:password].to_s
|
||||
|
||||
encoding = config[:encoding]
|
||||
min_messages = config[:min_messages]
|
||||
|
||||
if config.has_key?(:database)
|
||||
@@ -22,12 +21,10 @@ module ActiveRecord
|
||||
end
|
||||
|
||||
pga = ConnectionAdapters::PostgreSQLAdapter.new(
|
||||
PGconn.connect(host, port, "", "", database, username, password), logger
|
||||
PGconn.connect(host, port, "", "", database, username, password), logger, config
|
||||
)
|
||||
|
||||
pga.schema_search_path = config[:schema_search_path] || config[:schema_order]
|
||||
pga.execute("SET client_encoding TO '#{encoding}'") if encoding
|
||||
pga.execute("SET client_min_messages TO '#{min_messages}'") if min_messages
|
||||
|
||||
pga
|
||||
end
|
||||
@@ -52,6 +49,33 @@ module ActiveRecord
|
||||
'PostgreSQL'
|
||||
end
|
||||
|
||||
def initialize(connection, logger, config = {})
|
||||
super(connection, logger)
|
||||
@config = config
|
||||
configure_connection
|
||||
end
|
||||
|
||||
# Is this connection alive and ready for queries?
|
||||
def active?
|
||||
if @connection.respond_to?(:status)
|
||||
@connection.status == PGconn::CONNECTION_OK
|
||||
else
|
||||
@connection.query 'SELECT 1'
|
||||
true
|
||||
end
|
||||
rescue PGError
|
||||
false
|
||||
end
|
||||
|
||||
# Close then reopen the connection.
|
||||
def reconnect!
|
||||
# TODO: postgres-pr doesn't have PGconn#reset.
|
||||
if @connection.respond_to?(:reset)
|
||||
@connection.reset
|
||||
configure_connection
|
||||
end
|
||||
end
|
||||
|
||||
def native_database_types
|
||||
{
|
||||
:primary_key => "serial primary key",
|
||||
@@ -102,7 +126,7 @@ module ActiveRecord
|
||||
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
|
||||
execute(sql, name)
|
||||
table = sql.split(" ", 4)[2]
|
||||
id_value || last_insert_id(table, sequence_name)
|
||||
id_value || last_insert_id(table, sequence_name || default_sequence_name(table, pk))
|
||||
end
|
||||
|
||||
def query(sql, name = nil) #:nodoc:
|
||||
@@ -199,24 +223,35 @@ module ActiveRecord
|
||||
@schema_search_path ||= query('SHOW search_path')[0][0]
|
||||
end
|
||||
|
||||
def default_sequence_name(table_name, pk = 'id')
|
||||
"#{table_name}_#{pk}_seq"
|
||||
def default_sequence_name(table_name, pk = nil)
|
||||
default_pk, default_seq = pk_and_sequence_for(table_name)
|
||||
default_seq || "#{table_name}_#{pk || default_pk || 'id'}_seq"
|
||||
end
|
||||
|
||||
# Set the sequence to the max value of the table's pk.
|
||||
def reset_pk_sequence!(table)
|
||||
pk, sequence = pk_and_sequence_for(table)
|
||||
if pk and sequence
|
||||
select_value <<-end_sql, 'Reset sequence'
|
||||
SELECT setval('#{sequence}', (SELECT COALESCE(MAX(#{pk})+(SELECT increment_by FROM #{sequence}), (SELECT min_value FROM #{sequence})) FROM #{table}), false)
|
||||
end_sql
|
||||
# Resets sequence to the max value of the table's pk if present.
|
||||
def reset_pk_sequence!(table, pk = nil, sequence = nil)
|
||||
unless pk and sequence
|
||||
default_pk, default_sequence = pk_and_sequence_for(table)
|
||||
pk ||= default_pk
|
||||
sequence ||= default_sequence
|
||||
end
|
||||
if pk
|
||||
if sequence
|
||||
select_value <<-end_sql, 'Reset sequence'
|
||||
SELECT setval('#{sequence}', (SELECT COALESCE(MAX(#{pk})+(SELECT increment_by FROM #{sequence}), (SELECT min_value FROM #{sequence})) FROM #{table}), false)
|
||||
end_sql
|
||||
else
|
||||
@logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Find a table's primary key and sequence.
|
||||
def pk_and_sequence_for(table)
|
||||
execute(<<-end_sql, 'Find pk sequence')[0]
|
||||
SELECT attr.attname, (name.nspname || '.' || seq.relname)
|
||||
# First try looking for a sequence with a dependency on the
|
||||
# given table's primary key.
|
||||
result = execute(<<-end_sql, 'PK and serial sequence')[0]
|
||||
SELECT attr.attname, name.nspname, seq.relname
|
||||
FROM pg_class seq,
|
||||
pg_attribute attr,
|
||||
pg_depend dep,
|
||||
@@ -232,11 +267,30 @@ module ActiveRecord
|
||||
AND cons.contype = 'p'
|
||||
AND dep.refobjid = '#{table}'::regclass
|
||||
end_sql
|
||||
|
||||
if result.nil? or result.empty?
|
||||
# If that fails, try parsing the primary key's default value.
|
||||
# Support the 7.x and 8.0 nextval('foo'::text) as well as
|
||||
# the 8.1+ nextval('foo'::regclass).
|
||||
# TODO: assumes sequence is in same schema as table.
|
||||
result = execute(<<-end_sql, 'PK and custom sequence')[0]
|
||||
SELECT attr.attname, name.nspname, split_part(def.adsrc, '\\\'', 2)
|
||||
FROM pg_class t
|
||||
JOIN pg_namespace name ON (t.relnamespace = name.oid)
|
||||
JOIN pg_attribute attr ON (t.oid = attrelid)
|
||||
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
||||
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
||||
WHERE t.oid = '#{table}'::regclass
|
||||
AND cons.contype = 'p'
|
||||
AND def.adsrc ~* 'nextval'
|
||||
end_sql
|
||||
end
|
||||
# check for existence of . in sequence name as in public.foo_sequence. if it does not exist, join the current namespace
|
||||
result.last['.'] ? [result.first, result.last] : [result.first, "#{result[1]}.#{result[2]}"]
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
||||
|
||||
def rename_table(name, new_name)
|
||||
execute "ALTER TABLE #{name} RENAME TO #{new_name}"
|
||||
end
|
||||
@@ -276,14 +330,21 @@ module ActiveRecord
|
||||
execute "DROP INDEX #{index_name}"
|
||||
end
|
||||
|
||||
|
||||
|
||||
private
|
||||
BYTEA_COLUMN_TYPE_OID = 17
|
||||
|
||||
def last_insert_id(table, sequence_name)
|
||||
if sequence_name
|
||||
@connection.exec("SELECT currval('#{sequence_name}')")[0][0].to_i
|
||||
def configure_connection
|
||||
if @config[:encoding]
|
||||
execute("SET client_encoding TO '#{@config[:encoding]}'")
|
||||
end
|
||||
if @config[:min_messages]
|
||||
execute("SET client_min_messages TO '#{@config[:min_messages]}'")
|
||||
end
|
||||
end
|
||||
|
||||
def last_insert_id(table, sequence_name)
|
||||
Integer(select_value("SELECT currval('#{sequence_name}')"))
|
||||
end
|
||||
|
||||
def select(sql, name = nil)
|
||||
|
||||
@@ -55,7 +55,7 @@ module ActiveRecord
|
||||
# the database path is not the special path that tells
|
||||
# Sqlite build a database only in memory.
|
||||
if Object.const_defined?(:RAILS_ROOT) && ':memory:' != config[:database]
|
||||
config[:database] = File.expand_path(config[:database], RAILS_ROOT)
|
||||
config[:database] = File.join(RAILS_ROOT, config[:database])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -123,7 +123,7 @@ module ActiveRecord
|
||||
end
|
||||
|
||||
def quote_column_name(name) #:nodoc:
|
||||
"'#{name}'"
|
||||
%Q("#{name}")
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -30,16 +30,17 @@ module ActiveRecord
|
||||
if mode == "ODBC"
|
||||
raise ArgumentError, "Missing DSN. Argument ':dsn' must be set in order for this adapter to work." unless config.has_key?(:dsn)
|
||||
dsn = config[:dsn]
|
||||
conn = DBI.connect("DBI:ODBC:#{dsn}", username, password)
|
||||
driver_url = "DBI:ODBC:#{dsn}"
|
||||
else
|
||||
raise ArgumentError, "Missing Database. Argument ':database' must be set in order for this adapter to work." unless config.has_key?(:database)
|
||||
database = config[:database]
|
||||
host = config[:host] ? config[:host].to_s : 'localhost'
|
||||
conn = DBI.connect("DBI:ADO:Provider=SQLOLEDB;Data Source=#{host};Initial Catalog=#{database};User Id=#{username};Password=#{password};")
|
||||
driver_url = "DBI:ADO:Provider=SQLOLEDB;Data Source=#{host};Initial Catalog=#{database};User Id=#{username};Password=#{password};"
|
||||
end
|
||||
conn = DBI.connect(driver_url, username, password)
|
||||
|
||||
conn["AutoCommit"] = true
|
||||
ConnectionAdapters::SQLServerAdapter.new(conn, logger)
|
||||
ConnectionAdapters::SQLServerAdapter.new(conn, logger, [driver_url, username, password])
|
||||
end
|
||||
end # class Base
|
||||
|
||||
@@ -52,6 +53,8 @@ module ActiveRecord
|
||||
@identity = is_identity
|
||||
@is_special = sql_type =~ /text|ntext|image/i ? true : false
|
||||
@scale = scale_value
|
||||
# SQL Server only supports limits on *char and float types
|
||||
@limit = nil unless @type == :float or @type == :string
|
||||
end
|
||||
|
||||
def simplified_type(field_type)
|
||||
@@ -170,6 +173,12 @@ module ActiveRecord
|
||||
# unixODBC 2.2.11, Ruby ODBC 0.996, Ruby DBI 0.0.23 and Ruby 1.8.2.
|
||||
# [Linux strongmad 2.6.11-1.1369_FC4 #1 Thu Jun 2 22:55:56 EDT 2005 i686 i686 i386 GNU/Linux]
|
||||
class SQLServerAdapter < AbstractAdapter
|
||||
|
||||
def initialize(connection, logger, connection_options=nil)
|
||||
super(connection, logger)
|
||||
@connection_options = connection_options
|
||||
end
|
||||
|
||||
def native_database_types
|
||||
{
|
||||
:primary_key => "int NOT NULL IDENTITY(1, 1) PRIMARY KEY",
|
||||
@@ -194,6 +203,25 @@ module ActiveRecord
|
||||
true
|
||||
end
|
||||
|
||||
# CONNECTION MANAGEMENT ====================================#
|
||||
|
||||
# Returns true if the connection is active.
|
||||
def active?
|
||||
@connection.execute("SELECT 1") { }
|
||||
true
|
||||
rescue DBI::DatabaseError, DBI::InterfaceError
|
||||
false
|
||||
end
|
||||
|
||||
# Reconnects to the database, returns false if no connection could be made.
|
||||
def reconnect!
|
||||
@connection.disconnect rescue nil
|
||||
@connection = DBI.connect(*@connection_options)
|
||||
rescue DBI::DatabaseError => e
|
||||
@logger.warn "#{adapter_name} reconnection failed: #{e.message}" if @logger
|
||||
false
|
||||
end
|
||||
|
||||
def select_all(sql, name = nil)
|
||||
select(sql, name)
|
||||
end
|
||||
@@ -234,7 +262,7 @@ module ActiveRecord
|
||||
end
|
||||
log(sql, name) do
|
||||
@connection.execute(sql)
|
||||
select_one("SELECT @@IDENTITY AS Ident")["Ident"]
|
||||
id_value || select_one("SELECT @@IDENTITY AS Ident")["Ident"]
|
||||
end
|
||||
ensure
|
||||
if ii_enabled
|
||||
@@ -328,7 +356,12 @@ module ActiveRecord
|
||||
if options[:order]
|
||||
options[:order] = options[:order].split(',').map do |field|
|
||||
parts = field.split(" ")
|
||||
if sql =~ /#{parts[0]} AS (t\d_r\d\d?)/
|
||||
tc = parts[0]
|
||||
if sql =~ /\.\[/ and tc =~ /\./ # if column quoting used in query
|
||||
tc.gsub!(/\./, '\\.\\[')
|
||||
tc << '\\]'
|
||||
end
|
||||
if sql =~ /#{tc} AS (t\d_r\d\d?)/
|
||||
parts[0] = $1
|
||||
end
|
||||
parts.join(' ')
|
||||
|
||||
@@ -508,6 +508,7 @@ module Test #:nodoc:
|
||||
ActiveRecord::Base.connection.rollback_db_transaction
|
||||
ActiveRecord::Base.unlock_mutex
|
||||
end
|
||||
ActiveRecord::Base.clear_connection_cache!
|
||||
end
|
||||
|
||||
alias_method :teardown, :teardown_with_fixtures
|
||||
|
||||
@@ -20,7 +20,7 @@ module ActiveRecord
|
||||
# def self.up
|
||||
# add_column :accounts, :ssl_enabled, :boolean, :default => 1
|
||||
# end
|
||||
#
|
||||
#
|
||||
# def self.down
|
||||
# remove_column :accounts, :ssl_enabled
|
||||
# end
|
||||
@@ -28,7 +28,7 @@ module ActiveRecord
|
||||
#
|
||||
# This migration will add a boolean flag to the accounts table and remove it again, if you're backing out of the migration.
|
||||
# It shows how all migrations have two class methods +up+ and +down+ that describes the transformations required to implement
|
||||
# or remove the migration. These methods can consist of both the migration specific methods, like add_column and remove_column,
|
||||
# or remove the migration. These methods can consist of both the migration specific methods, like add_column and remove_column,
|
||||
# but may also contain regular Ruby code for generating data needed for the transformations.
|
||||
#
|
||||
# Example of a more complex migration that also needs to initialize data:
|
||||
@@ -42,10 +42,10 @@ module ActiveRecord
|
||||
# t.column :type, :string
|
||||
# t.column :position, :integer
|
||||
# end
|
||||
#
|
||||
#
|
||||
# SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1
|
||||
# end
|
||||
#
|
||||
#
|
||||
# def self.down
|
||||
# drop_table :system_settings
|
||||
# end
|
||||
@@ -79,13 +79,29 @@ module ActiveRecord
|
||||
#
|
||||
# == Running migrations from within Rails
|
||||
#
|
||||
# The Rails package has support for migrations with the <tt>script/generate migration my_new_migration</tt> command and
|
||||
# with the <tt>rake migrate</tt> command that'll run all the pending migrations. It'll even create the needed schema_info
|
||||
# table automatically if it's missing.
|
||||
# The Rails package has several tools to help create and apply migrations.
|
||||
#
|
||||
# To generate a new migration, use <tt>script/generate migration MyNewMigration</tt>
|
||||
# where MyNewMigration is the name of your migration. The generator will
|
||||
# create a file <tt>nnn_my_new_migration.rb</tt> in the <tt>db/migrate/</tt>
|
||||
# directory, where <tt>nnn</tt> is the next largest migration number.
|
||||
# You may then edit the <tt>self.up</tt> and <tt>self.down</tt> methods of
|
||||
# n MyNewMigration.
|
||||
#
|
||||
# To run migrations against the currently configured database, use
|
||||
# <tt>rake migrate</tt>. This will update the database by running all of the
|
||||
# pending migrations, creating the <tt>schema_info</tt> table if missing.
|
||||
#
|
||||
# To roll the database back to a previous migration version, use
|
||||
# <tt>rake migrate version=X</tt> where <tt>X</tt> is the version to which
|
||||
# you wish to downgrade. If any of the migrations throw an
|
||||
# <tt>IrreversibleMigration</tt> exception, that step will fail and you'll
|
||||
# have some manual work to do.
|
||||
#
|
||||
# == Database support
|
||||
#
|
||||
# Migrations are currently only supported in MySQL and PostgreSQL.
|
||||
# Migrations are currently supported in MySQL, PostgreSQL, SQLite,
|
||||
# SQL Server, and Oracle (all supported databases except DB2).
|
||||
#
|
||||
# == More examples
|
||||
#
|
||||
@@ -95,7 +111,7 @@ module ActiveRecord
|
||||
# def self.up
|
||||
# Tag.find(:all).each { |tag| tag.destroy if tag.pages.empty? }
|
||||
# end
|
||||
#
|
||||
#
|
||||
# def self.down
|
||||
# # not much we can do to restore deleted data
|
||||
# raise IrreversibleMigration
|
||||
@@ -128,20 +144,21 @@ module ActiveRecord
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# == Using the class after changing table
|
||||
# == Using a model after changing its table
|
||||
#
|
||||
# Sometimes you'll want to add a column in a migration and populate it immediately after. In that case, you'll need
|
||||
# to make a call to Base#reset_column_information in order to ensure that the class has the latest column data from
|
||||
# to make a call to Base#reset_column_information in order to ensure that the model has the latest column data from
|
||||
# after the new column was added. Example:
|
||||
#
|
||||
# class MakeJoinUnique < ActiveRecord::Migration
|
||||
# class AddPeopleSalary < ActiveRecord::Migration
|
||||
# def self.up
|
||||
# add_column :people, :salary, :integer
|
||||
# Person.reset_column_information
|
||||
# Person.find(:all).each do |p|
|
||||
# p.salary = SalaryCalculator.compute(p)
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
class Migration
|
||||
class << self
|
||||
def up() end
|
||||
|
||||
@@ -17,6 +17,8 @@ module ActiveRecord
|
||||
# Active Record validation is reported to and from this object, which is used by Base#save to
|
||||
# determine whether the object in a valid state to be saved. See usage example in Validations.
|
||||
class Errors
|
||||
include Enumerable
|
||||
|
||||
def initialize(base) # :nodoc:
|
||||
@base, @errors = base, {}
|
||||
end
|
||||
@@ -485,13 +487,20 @@ module ActiveRecord
|
||||
configuration = { :message => ActiveRecord::Errors.default_error_messages[:taken] }
|
||||
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
||||
|
||||
if scope = configuration[:scope]
|
||||
validates_each(attr_names,configuration) do |record, attr_name, value|
|
||||
record.errors.add(attr_name, configuration[:message]) if record.class.find(:first, :conditions => (record.new_record? ? ["#{attr_name} = ? AND #{scope} = ?", record.send(attr_name), record.send(scope)] : ["#{attr_name} = ? AND #{record.class.primary_key} <> ? AND #{scope} = ?", record.send(attr_name), record.send(:id), record.send(scope)]))
|
||||
validates_each(attr_names,configuration) do |record, attr_name, value|
|
||||
condition_sql = "#{attr_name} #{attribute_condition(value)}"
|
||||
condition_params = [value]
|
||||
if scope = configuration[:scope]
|
||||
scope_value = record.send(scope)
|
||||
condition_sql << " AND #{scope} #{attribute_condition(scope_value)}"
|
||||
condition_params << scope_value
|
||||
end
|
||||
else
|
||||
validates_each(attr_names,configuration) do |record, attr_name, value|
|
||||
record.errors.add(attr_name, configuration[:message]) if record.class.find(:first, :conditions => (record.new_record? ? ["#{attr_name} = ?", record.send(attr_name)] : ["#{attr_name} = ? AND #{record.class.primary_key} <> ?", record.send(attr_name), record.send(:id) ] ))
|
||||
unless record.new_record?
|
||||
condition_sql << " AND #{record.class.primary_key} <> ?"
|
||||
condition_params << record.send(:id)
|
||||
end
|
||||
if record.class.find(:first, :conditions => [condition_sql, *condition_params])
|
||||
record.errors.add(attr_name, configuration[:message])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -658,6 +667,8 @@ module ActiveRecord
|
||||
if attributes.is_a?(Array)
|
||||
attributes.collect { |attr| create!(attr) }
|
||||
else
|
||||
attributes.reverse_merge!(scope(:create)) if scoped?(:create)
|
||||
|
||||
object = new(attributes)
|
||||
object.save!
|
||||
object
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
module ActiveRecord
|
||||
module Version #:nodoc:
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 1
|
||||
MINOR = 13
|
||||
TINY = 0
|
||||
TINY = 1
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
||||
@@ -8,6 +8,8 @@ require 'active_support/binding_of_caller'
|
||||
require 'active_support/breakpoint'
|
||||
require 'connection'
|
||||
|
||||
QUOTED_TYPE = ActiveRecord::Base.connection.quote_column_name('type') unless Object.const_defined?(:QUOTED_TYPE)
|
||||
|
||||
class Test::Unit::TestCase #:nodoc:
|
||||
self.fixture_path = File.dirname(__FILE__) + "/fixtures/"
|
||||
self.use_instantiated_fixtures = false
|
||||
@@ -22,3 +24,6 @@ def current_adapter?(type)
|
||||
ActiveRecord::ConnectionAdapters.const_defined?(type) &&
|
||||
ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters.const_get(type))
|
||||
end
|
||||
|
||||
#ActiveRecord::Base.logger = Logger.new(STDOUT)
|
||||
#ActiveRecord::Base.colorize_logging = false
|
||||
|
||||
@@ -44,4 +44,23 @@ class AggregationsTest < Test::Unit::TestCase
|
||||
assert_equal "39", customers(:david).gps_location.latitude
|
||||
assert_equal "-110", customers(:david).gps_location.longitude
|
||||
end
|
||||
|
||||
def test_reloaded_instance_refreshes_aggregations
|
||||
assert_equal "35.544623640962634", customers(:david).gps_location.latitude
|
||||
assert_equal "-105.9309951055148", customers(:david).gps_location.longitude
|
||||
|
||||
Customer.update_all("gps_location = '24x113'")
|
||||
customers(:david).reload
|
||||
assert_equal '24x113', customers(:david)['gps_location']
|
||||
|
||||
assert_equal GpsLocation.new('24x113'), customers(:david).gps_location
|
||||
end
|
||||
|
||||
def test_gps_equality
|
||||
assert GpsLocation.new('39x110') == GpsLocation.new('39x110')
|
||||
end
|
||||
|
||||
def test_gps_inequality
|
||||
assert GpsLocation.new('39x110') != GpsLocation.new('39x111')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -21,7 +21,7 @@ class EagerAssociationTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_loading_conditions_with_or
|
||||
posts = authors(:david).posts.find(:all, :include => :comments, :conditions => "comments.body like 'Normal%' OR comments.type = 'SpecialComment'")
|
||||
posts = authors(:david).posts.find(:all, :include => :comments, :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE} = 'SpecialComment'")
|
||||
assert_nil posts.detect { |p| p.author_id != authors(:david).id },
|
||||
"expected to find only david's posts"
|
||||
end
|
||||
@@ -83,6 +83,12 @@ class EagerAssociationTest < Test::Unit::TestCase
|
||||
assert_equal [6,7,8], comments.collect { |c| c.id }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions_array
|
||||
comments = Comment.find(:all, :include => :post, :conditions => ['post_id = ?',4], :limit => 3, :offset => 1, :order => 'comments.id')
|
||||
assert_equal 3, comments.length
|
||||
assert_equal [6,7,8], comments.collect { |c| c.id }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_limit_and_multiple_associations
|
||||
posts = Post.find(:all, :include => [:author, :very_special_comment], :limit => 1)
|
||||
assert_equal 1, posts.length
|
||||
@@ -101,6 +107,24 @@ class EagerAssociationTest < Test::Unit::TestCase
|
||||
assert_equal 3, posts.inject(0) { |sum, post| sum += post.comments.size }
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit_and_conditions
|
||||
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.body = 'hello'", :order => "posts.id")
|
||||
assert_equal 2, posts.size
|
||||
assert_equal [4,5], posts.collect { |p| p.id }
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit_and_conditions_array
|
||||
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "posts.body = ?", 'hello' ], :order => "posts.id")
|
||||
assert_equal 2, posts.size
|
||||
assert_equal [4,5], posts.collect { |p| p.id }
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit_and_conditions_array_on_the_eagers
|
||||
assert_raises(ArgumentError) do
|
||||
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit_with_no_results
|
||||
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.title = 'magic forest'")
|
||||
assert_equal 0, posts.size
|
||||
@@ -120,7 +144,7 @@ class EagerAssociationTest < Test::Unit::TestCase
|
||||
assert_raises(ArgumentError) do
|
||||
posts = authors(:david).posts.find(:all,
|
||||
:include => :comments,
|
||||
:conditions => "comments.body like 'Normal%' OR comments.type = 'SpecialComment'",
|
||||
:conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
|
||||
:limit => 2
|
||||
)
|
||||
end
|
||||
|
||||
@@ -351,7 +351,7 @@ class HasManyAssociationsTest < Test::Unit::TestCase
|
||||
def test_find_all
|
||||
firm = Firm.find_first
|
||||
assert_equal firm.clients, firm.clients.find_all
|
||||
assert_equal 2, firm.clients.find(:all, :conditions => "type = 'Client'").length
|
||||
assert_equal 2, firm.clients.find(:all, :conditions => "#{QUOTED_TYPE} = 'Client'").length
|
||||
assert_equal 1, firm.clients.find(:all, :conditions => "name = 'Summit'").length
|
||||
end
|
||||
|
||||
@@ -367,16 +367,16 @@ class HasManyAssociationsTest < Test::Unit::TestCase
|
||||
firm = Firm.find_first
|
||||
client2 = Client.find(2)
|
||||
assert_equal firm.clients.first, firm.clients.find_first
|
||||
assert_equal client2, firm.clients.find_first("type = 'Client'")
|
||||
assert_equal client2, firm.clients.find(:first, :conditions => "type = 'Client'")
|
||||
assert_equal client2, firm.clients.find_first("#{QUOTED_TYPE} = 'Client'")
|
||||
assert_equal client2, firm.clients.find(:first, :conditions => "#{QUOTED_TYPE} = 'Client'")
|
||||
end
|
||||
|
||||
def test_find_first_sanitized
|
||||
firm = Firm.find_first
|
||||
client2 = Client.find(2)
|
||||
assert_equal client2, firm.clients.find_first(["type = ?", "Client"])
|
||||
assert_equal client2, firm.clients.find(:first, :conditions => ['type = ?', 'Client'])
|
||||
assert_equal client2, firm.clients.find(:first, :conditions => ['type = :type', { :type => 'Client' }])
|
||||
assert_equal client2, firm.clients.find_first(["#{QUOTED_TYPE} = ?", "Client"])
|
||||
assert_equal client2, firm.clients.find(:first, :conditions => ["#{QUOTED_TYPE} = ?", 'Client'])
|
||||
assert_equal client2, firm.clients.find(:first, :conditions => ["#{QUOTED_TYPE} = :type", { :type => 'Client' }])
|
||||
end
|
||||
|
||||
def test_find_in_collection
|
||||
@@ -1381,5 +1381,14 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
|
||||
assert developer.special_projects.include?(special_project)
|
||||
assert !developer.special_projects.include?(other_project)
|
||||
end
|
||||
|
||||
|
||||
def test_update_attributes_after_push_without_duplicate_join_table_rows
|
||||
developer = Developer.new("name" => "Kano")
|
||||
project = SpecialProject.create("name" => "Special Project")
|
||||
assert developer.save
|
||||
developer.projects << project
|
||||
developer.update_attribute("name", "Bruza")
|
||||
assert_equal 1, developer.connection.select_one("SELECT count(*) FROM developers_projects WHERE
|
||||
project_id = #{project.id} AND developer_id = #{developer.id}")["count(*)"].to_i
|
||||
end
|
||||
end
|
||||
|
||||
@@ -17,6 +17,7 @@ class MasterCreditCard < ActiveRecord::Base; end
|
||||
class Post < ActiveRecord::Base; end
|
||||
class Computer < ActiveRecord::Base; end
|
||||
class NonExistentTable < ActiveRecord::Base; end
|
||||
class TestOCIDefault < ActiveRecord::Base; end
|
||||
|
||||
class LoosePerson < ActiveRecord::Base
|
||||
attr_protected :credit_rating, :administrator
|
||||
@@ -205,6 +206,40 @@ class BasicsTest < Test::Unit::TestCase
|
||||
topic = topics(:first)
|
||||
topic.approved = false
|
||||
assert !topic.approved?, "approved should be false"
|
||||
topic.approved = "false"
|
||||
assert !topic.approved?, "approved should be false"
|
||||
end
|
||||
|
||||
def test_read_attribute_when_true
|
||||
topic = topics(:first)
|
||||
topic.approved = true
|
||||
assert topic.approved?, "approved should be true"
|
||||
topic.approved = "true"
|
||||
assert topic.approved?, "approved should be true"
|
||||
end
|
||||
|
||||
def test_read_write_boolean_attribute
|
||||
topic = Topic.new
|
||||
# puts ""
|
||||
# puts "New Topic"
|
||||
# puts topic.inspect
|
||||
topic.approved = "false"
|
||||
# puts "Expecting false"
|
||||
# puts topic.inspect
|
||||
assert !topic.approved?, "approved should be false"
|
||||
topic.approved = "false"
|
||||
# puts "Expecting false"
|
||||
# puts topic.inspect
|
||||
assert !topic.approved?, "approved should be false"
|
||||
topic.approved = "true"
|
||||
# puts "Expecting true"
|
||||
# puts topic.inspect
|
||||
assert topic.approved?, "approved should be true"
|
||||
topic.approved = "true"
|
||||
# puts "Expecting true"
|
||||
# puts topic.inspect
|
||||
assert topic.approved?, "approved should be true"
|
||||
# puts ""
|
||||
end
|
||||
|
||||
def test_reader_generation
|
||||
@@ -220,6 +255,13 @@ class BasicsTest < Test::Unit::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
def test_reader_for_invalid_column_names
|
||||
# column names which aren't legal ruby ids
|
||||
topic = Topic.find(:first)
|
||||
topic.send(:define_read_method, "mumub-jumbo".to_sym, "mumub-jumbo", nil)
|
||||
assert !Topic.read_methods.include?("mumub-jumbo")
|
||||
end
|
||||
|
||||
def test_non_attribute_access_and_assignment
|
||||
topic = Topic.new
|
||||
assert !topic.respond_to?("mumbo")
|
||||
@@ -470,6 +512,15 @@ class BasicsTest < Test::Unit::TestCase
|
||||
topic = Topic.find(topic.id)
|
||||
assert topic.approved?
|
||||
assert_nil topic.last_read
|
||||
|
||||
# Oracle has some funky default handling, so it requires a bit of
|
||||
# extra testing. See ticket #2788.
|
||||
if current_adapter?(:OCIAdapter)
|
||||
test = TestOCIDefault.new
|
||||
assert_equal "X", test.test_char
|
||||
assert_equal "hello", test.test_string
|
||||
assert_equal 3, test.test_int
|
||||
end
|
||||
end
|
||||
|
||||
def test_utc_as_time_zone
|
||||
@@ -495,7 +546,7 @@ class BasicsTest < Test::Unit::TestCase
|
||||
assert_nil topic.last_read
|
||||
assert_nil topic.approved
|
||||
end
|
||||
|
||||
|
||||
def test_equality
|
||||
assert_equal Topic.find(1), Topic.find(2).parent
|
||||
end
|
||||
@@ -839,10 +890,10 @@ class BasicsTest < Test::Unit::TestCase
|
||||
def test_column_name_properly_quoted
|
||||
col_record = ColumnName.new
|
||||
col_record.references = 40
|
||||
col_record.save
|
||||
assert col_record.save
|
||||
col_record.references = 41
|
||||
col_record.save
|
||||
c2 = ColumnName.find(col_record.id)
|
||||
assert col_record.save
|
||||
assert_not_nil c2 = ColumnName.find(col_record.id)
|
||||
assert_equal(41, c2.references)
|
||||
end
|
||||
|
||||
@@ -993,10 +1044,10 @@ class BasicsTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_count_with_join
|
||||
res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.type = 'Post'"
|
||||
res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
|
||||
res2 = res + 1
|
||||
assert_nothing_raised do
|
||||
res2 = Post.count("posts.type = 'Post'",
|
||||
res2 = Post.count("posts.#{QUOTED_TYPE} = 'Post'",
|
||||
"LEFT JOIN comments ON posts.id=comments.post_id")
|
||||
end
|
||||
assert_equal res, res2
|
||||
|
||||
@@ -18,9 +18,9 @@ class BinaryTest < Test::Unit::TestCase
|
||||
# limited to 8KB.
|
||||
#
|
||||
# Without using prepared statements, it makes no sense to test
|
||||
# BLOB data with DB2, because the length of a statement is
|
||||
# limited to 32KB.
|
||||
unless %w(SQLServer DB2 OCI).include? ActiveRecord::Base.connection.adapter_name
|
||||
# BLOB data with DB2 or Firebird, because the length of a statement
|
||||
# is limited to 32KB.
|
||||
unless %w(SQLServer DB2 OCI Firebird).include? ActiveRecord::Base.connection.adapter_name
|
||||
def test_load_save
|
||||
bin = Binary.new
|
||||
bin.data = @data
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
print "Using native DB2\n"
|
||||
require 'fixtures/course'
|
||||
require_dependency 'fixtures/course'
|
||||
require 'logger'
|
||||
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
|
||||
24
activerecord/test/connections/native_firebird/connection.rb
Normal file
24
activerecord/test/connections/native_firebird/connection.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
print "Using native Firebird\n"
|
||||
require_dependency 'fixtures/course'
|
||||
require 'logger'
|
||||
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
|
||||
db1 = 'activerecord_unittest'
|
||||
db2 = 'activerecord_unittest2'
|
||||
|
||||
ActiveRecord::Base.establish_connection(
|
||||
:adapter => "firebird",
|
||||
:host => "localhost",
|
||||
:username => "rails",
|
||||
:password => "rails",
|
||||
:database => db1
|
||||
)
|
||||
|
||||
Course.establish_connection(
|
||||
:adapter => "firebird",
|
||||
:host => "localhost",
|
||||
:username => "rails",
|
||||
:password => "rails",
|
||||
:database => db2
|
||||
)
|
||||
@@ -1,5 +1,5 @@
|
||||
print "Using native MySQL\n"
|
||||
require 'fixtures/course'
|
||||
require_dependency 'fixtures/course'
|
||||
require 'logger'
|
||||
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
print "Using OCI Oracle\n"
|
||||
require 'fixtures/course'
|
||||
require_dependency 'fixtures/course'
|
||||
require 'logger'
|
||||
|
||||
ActiveRecord::Base.logger = Logger.new STDOUT
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
print "Using native PostgreSQL\n"
|
||||
require 'fixtures/course'
|
||||
require_dependency 'fixtures/course'
|
||||
require 'logger'
|
||||
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
@@ -9,16 +9,16 @@ db2 = 'activerecord_unittest2'
|
||||
|
||||
ActiveRecord::Base.establish_connection(
|
||||
:adapter => "postgresql",
|
||||
:host => nil,
|
||||
:username => "postgres",
|
||||
:password => "postgres",
|
||||
:database => db1
|
||||
:database => db1,
|
||||
:min_messages => "warning"
|
||||
)
|
||||
|
||||
Course.establish_connection(
|
||||
:adapter => "postgresql",
|
||||
:host => nil,
|
||||
:username => "postgres",
|
||||
:password => "postgres",
|
||||
:database => db2
|
||||
)
|
||||
:database => db2,
|
||||
:min_messages => "warning"
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
print "Using native SQlite\n"
|
||||
require 'fixtures/course'
|
||||
require_dependency 'fixtures/course'
|
||||
require 'logger'
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
print "Using native SQLite3\n"
|
||||
require 'fixtures/course'
|
||||
require_dependency 'fixtures/course'
|
||||
require 'logger'
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
print "Using native SQLite3\n"
|
||||
require 'fixtures/course'
|
||||
require_dependency 'fixtures/course'
|
||||
require 'logger'
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
print "Using native SQLServer\n"
|
||||
require 'fixtures/course'
|
||||
require_dependency 'fixtures/course'
|
||||
require 'logger'
|
||||
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
print "Using native SQLServer via ODBC\n"
|
||||
require 'fixtures/course'
|
||||
require_dependency 'fixtures/course'
|
||||
require 'logger'
|
||||
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
|
||||
16
activerecord/test/default_test_firebird.rb
Normal file
16
activerecord/test/default_test_firebird.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
require 'abstract_unit'
|
||||
require 'fixtures/default'
|
||||
|
||||
class DefaultTest < Test::Unit::TestCase
|
||||
def test_default_timestamp
|
||||
default = Default.new
|
||||
assert_instance_of(Time, default.default_timestamp)
|
||||
assert_equal(:datetime, default.column_for_attribute(:default_timestamp).type)
|
||||
|
||||
# Variance should be small; increase if required -- e.g., if test db is on
|
||||
# remote host and clocks aren't synchronized.
|
||||
t1 = Time.new
|
||||
accepted_variance = 1.0
|
||||
assert_in_delta(t1.to_f, default.default_timestamp.to_f, accepted_variance)
|
||||
end
|
||||
end
|
||||
@@ -312,7 +312,7 @@ class DeprecatedAssociationsTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_has_many_find_all
|
||||
assert_equal 2, Firm.find_first.find_all_in_clients("type = 'Client'").length
|
||||
assert_equal 2, Firm.find_first.find_all_in_clients("#{QUOTED_TYPE} = 'Client'").length
|
||||
assert_equal 1, Firm.find_first.find_all_in_clients("name = 'Summit'").length
|
||||
end
|
||||
|
||||
|
||||
@@ -350,7 +350,7 @@ class FinderTest < Test::Unit::TestCase
|
||||
def test_find_by_id_with_conditions_with_or
|
||||
assert_nothing_raised do
|
||||
Post.find([1,2,3],
|
||||
:conditions => "posts.id <= 3 OR posts.type = 'Post'")
|
||||
:conditions => "posts.id <= 3 OR posts.#{QUOTED_TYPE} = 'Post'")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
2
activerecord/test/fixtures/comment.rb
vendored
2
activerecord/test/fixtures/comment.rb
vendored
@@ -6,7 +6,7 @@ class Comment < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def self.search_by_type(q)
|
||||
self.find(:all, :conditions => ['type = ?', q])
|
||||
self.find(:all, :conditions => ["#{QUOTED_TYPE} = ?", q])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
4
activerecord/test/fixtures/company.rb
vendored
4
activerecord/test/fixtures/company.rb
vendored
@@ -7,7 +7,9 @@ end
|
||||
|
||||
|
||||
class Firm < Company
|
||||
has_many :clients, :order => "id", :dependent => true, :counter_sql => "SELECT COUNT(*) FROM companies WHERE firm_id = 1 AND (type = 'Client' OR type = 'SpecialClient' OR type = 'VerySpecialClient' )"
|
||||
has_many :clients, :order => "id", :dependent => true, :counter_sql =>
|
||||
"SELECT COUNT(*) FROM companies WHERE firm_id = 1 " +
|
||||
"AND (#{QUOTED_TYPE} = 'Client' OR #{QUOTED_TYPE} = 'SpecialClient' OR #{QUOTED_TYPE} = 'VerySpecialClient' )"
|
||||
has_many :clients_sorted_desc, :class_name => "Client", :order => "id DESC"
|
||||
has_many :clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id"
|
||||
has_many :dependent_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => true
|
||||
|
||||
4
activerecord/test/fixtures/customer.rb
vendored
4
activerecord/test/fixtures/customer.rb
vendored
@@ -44,4 +44,8 @@ class GpsLocation
|
||||
def longitude
|
||||
gps_location.split("x").last
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
self.latitude == other.latitude && self.longitude == other.longitude
|
||||
end
|
||||
end
|
||||
54
activerecord/test/fixtures/db_definitions/firebird.drop.sql
vendored
Normal file
54
activerecord/test/fixtures/db_definitions/firebird.drop.sql
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
DROP TABLE accounts;
|
||||
DROP TABLE companies;
|
||||
DROP TABLE topics;
|
||||
DROP TABLE developers;
|
||||
DROP TABLE projects;
|
||||
DROP TABLE developers_projects;
|
||||
DROP TABLE orders;
|
||||
DROP TABLE customers;
|
||||
DROP TABLE movies;
|
||||
DROP TABLE subscribers;
|
||||
DROP TABLE booleantests;
|
||||
DROP TABLE auto_id_tests;
|
||||
DROP TABLE entrants;
|
||||
DROP TABLE colnametests;
|
||||
DROP TABLE mixins;
|
||||
DROP TABLE people;
|
||||
DROP TABLE binaries;
|
||||
DROP TABLE computers;
|
||||
DROP TABLE posts;
|
||||
DROP TABLE comments;
|
||||
DROP TABLE authors;
|
||||
DROP TABLE tasks;
|
||||
DROP TABLE categories;
|
||||
DROP TABLE categories_posts;
|
||||
DROP TABLE fk_test_has_fk;
|
||||
DROP TABLE fk_test_has_pk;
|
||||
DROP TABLE keyboards;
|
||||
DROP TABLE defaults;
|
||||
|
||||
DROP DOMAIN D_BOOLEAN;
|
||||
|
||||
DROP GENERATOR accounts_seq;
|
||||
DROP GENERATOR companies_nonstd_seq;
|
||||
DROP GENERATOR topics_seq;
|
||||
DROP GENERATOR developers_seq;
|
||||
DROP GENERATOR projects_seq;
|
||||
DROP GENERATOR orders_seq;
|
||||
DROP GENERATOR customers_seq;
|
||||
DROP GENERATOR movies_seq;
|
||||
DROP GENERATOR booleantests_seq;
|
||||
DROP GENERATOR auto_id_tests_seq;
|
||||
DROP GENERATOR entrants_seq;
|
||||
DROP GENERATOR colnametests_seq;
|
||||
DROP GENERATOR mixins_seq;
|
||||
DROP GENERATOR people_seq;
|
||||
DROP GENERATOR binaries_seq;
|
||||
DROP GENERATOR computers_seq;
|
||||
DROP GENERATOR posts_seq;
|
||||
DROP GENERATOR comments_seq;
|
||||
DROP GENERATOR authors_seq;
|
||||
DROP GENERATOR tasks_seq;
|
||||
DROP GENERATOR categories_seq;
|
||||
DROP GENERATOR keyboards_seq;
|
||||
DROP GENERATOR defaults_seq;
|
||||
259
activerecord/test/fixtures/db_definitions/firebird.sql
vendored
Normal file
259
activerecord/test/fixtures/db_definitions/firebird.sql
vendored
Normal file
@@ -0,0 +1,259 @@
|
||||
CREATE DOMAIN D_BOOLEAN AS SMALLINT CHECK (VALUE IN (0, 1));
|
||||
|
||||
CREATE TABLE accounts (
|
||||
id BIGINT NOT NULL,
|
||||
firm_id BIGINT,
|
||||
credit_limit INTEGER,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR accounts_seq;
|
||||
SET GENERATOR accounts_seq TO 10000;
|
||||
|
||||
CREATE TABLE companies (
|
||||
id BIGINT NOT NULL,
|
||||
"TYPE" VARCHAR(50),
|
||||
ruby_type VARCHAR(50),
|
||||
firm_id BIGINT,
|
||||
name VARCHAR(50),
|
||||
client_of INTEGER,
|
||||
rating INTEGER DEFAULT 1,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR companies_nonstd_seq;
|
||||
SET GENERATOR companies_nonstd_seq TO 10000;
|
||||
|
||||
CREATE TABLE topics (
|
||||
id BIGINT NOT NULL,
|
||||
title VARCHAR(255),
|
||||
author_name VARCHAR(255),
|
||||
author_email_address VARCHAR(255),
|
||||
written_on TIMESTAMP,
|
||||
bonus_time TIME,
|
||||
last_read DATE,
|
||||
content VARCHAR(4000),
|
||||
approved D_BOOLEAN DEFAULT 1,
|
||||
replies_count INTEGER DEFAULT 0,
|
||||
parent_id BIGINT,
|
||||
"TYPE" VARCHAR(50),
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR topics_seq;
|
||||
SET GENERATOR topics_seq TO 10000;
|
||||
|
||||
CREATE TABLE developers (
|
||||
id BIGINT NOT NULL,
|
||||
name VARCHAR(100),
|
||||
salary INTEGER DEFAULT 70000,
|
||||
created_at TIMESTAMP,
|
||||
updated_at TIMESTAMP,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR developers_seq;
|
||||
SET GENERATOR developers_seq TO 10000;
|
||||
|
||||
CREATE TABLE projects (
|
||||
id BIGINT NOT NULL,
|
||||
name VARCHAR(100),
|
||||
"TYPE" VARCHAR(255),
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR projects_seq;
|
||||
SET GENERATOR projects_seq TO 10000;
|
||||
|
||||
CREATE TABLE developers_projects (
|
||||
developer_id BIGINT NOT NULL,
|
||||
project_id BIGINT NOT NULL,
|
||||
joined_on DATE,
|
||||
access_level SMALLINT DEFAULT 1
|
||||
);
|
||||
|
||||
CREATE TABLE orders (
|
||||
id BIGINT NOT NULL,
|
||||
name VARCHAR(100),
|
||||
billing_customer_id BIGINT,
|
||||
shipping_customer_id BIGINT,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR orders_seq;
|
||||
SET GENERATOR orders_seq TO 10000;
|
||||
|
||||
CREATE TABLE customers (
|
||||
id BIGINT NOT NULL,
|
||||
name VARCHAR(100),
|
||||
balance INTEGER DEFAULT 0,
|
||||
address_street VARCHAR(100),
|
||||
address_city VARCHAR(100),
|
||||
address_country VARCHAR(100),
|
||||
gps_location VARCHAR(100),
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR customers_seq;
|
||||
SET GENERATOR customers_seq TO 10000;
|
||||
|
||||
CREATE TABLE movies (
|
||||
movieid BIGINT NOT NULL,
|
||||
name varchar(100),
|
||||
PRIMARY KEY (movieid)
|
||||
);
|
||||
CREATE GENERATOR movies_seq;
|
||||
SET GENERATOR movies_seq TO 10000;
|
||||
|
||||
CREATE TABLE subscribers (
|
||||
nick VARCHAR(100) NOT NULL,
|
||||
name VARCHAR(100),
|
||||
PRIMARY KEY (nick)
|
||||
);
|
||||
|
||||
CREATE TABLE booleantests (
|
||||
id BIGINT NOT NULL,
|
||||
"VALUE" D_BOOLEAN,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR booleantests_seq;
|
||||
SET GENERATOR booleantests_seq TO 10000;
|
||||
|
||||
CREATE TABLE auto_id_tests (
|
||||
auto_id BIGINT NOT NULL,
|
||||
"VALUE" INTEGER,
|
||||
PRIMARY KEY (auto_id)
|
||||
);
|
||||
CREATE GENERATOR auto_id_tests_seq;
|
||||
SET GENERATOR auto_id_tests_seq TO 10000;
|
||||
|
||||
CREATE TABLE entrants (
|
||||
id BIGINT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
course_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR entrants_seq;
|
||||
SET GENERATOR entrants_seq TO 10000;
|
||||
|
||||
CREATE TABLE colnametests (
|
||||
id BIGINT NOT NULL,
|
||||
"REFERENCES" INTEGER NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR colnametests_seq;
|
||||
SET GENERATOR colnametests_seq TO 10000;
|
||||
|
||||
CREATE TABLE mixins (
|
||||
id BIGINT NOT NULL,
|
||||
parent_id BIGINT,
|
||||
pos INTEGER,
|
||||
created_at TIMESTAMP,
|
||||
updated_at TIMESTAMP,
|
||||
lft INTEGER,
|
||||
rgt INTEGER,
|
||||
root_id BIGINT,
|
||||
"TYPE" VARCHAR(40),
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR mixins_seq;
|
||||
SET GENERATOR mixins_seq TO 10000;
|
||||
|
||||
CREATE TABLE people (
|
||||
id BIGINT NOT NULL,
|
||||
first_name VARCHAR(40),
|
||||
lock_version INTEGER DEFAULT 0 NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR people_seq;
|
||||
SET GENERATOR people_seq TO 10000;
|
||||
|
||||
CREATE TABLE binaries (
|
||||
id BIGINT NOT NULL,
|
||||
data BLOB,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR binaries_seq;
|
||||
SET GENERATOR binaries_seq TO 10000;
|
||||
|
||||
CREATE TABLE computers (
|
||||
id BIGINT NOT NULL,
|
||||
developer INTEGER NOT NULL,
|
||||
"extendedWarranty" INTEGER NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR computers_seq;
|
||||
SET GENERATOR computers_seq TO 10000;
|
||||
|
||||
CREATE TABLE posts (
|
||||
id BIGINT NOT NULL,
|
||||
author_id BIGINT,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
"TYPE" VARCHAR(255) NOT NULL,
|
||||
body VARCHAR(3000) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR posts_seq;
|
||||
SET GENERATOR posts_seq TO 10000;
|
||||
|
||||
CREATE TABLE comments (
|
||||
id BIGINT NOT NULL,
|
||||
post_id BIGINT NOT NULL,
|
||||
"TYPE" VARCHAR(255) NOT NULL,
|
||||
body VARCHAR(3000) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR comments_seq;
|
||||
SET GENERATOR comments_seq TO 10000;
|
||||
|
||||
CREATE TABLE authors (
|
||||
id BIGINT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR authors_seq;
|
||||
SET GENERATOR authors_seq TO 10000;
|
||||
|
||||
CREATE TABLE tasks (
|
||||
id BIGINT NOT NULL,
|
||||
"STARTING" TIMESTAMP,
|
||||
ending TIMESTAMP,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR tasks_seq;
|
||||
SET GENERATOR tasks_seq TO 10000;
|
||||
|
||||
CREATE TABLE categories (
|
||||
id BIGINT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
"TYPE" VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE GENERATOR categories_seq;
|
||||
SET GENERATOR categories_seq TO 10000;
|
||||
|
||||
CREATE TABLE categories_posts (
|
||||
category_id BIGINT NOT NULL,
|
||||
post_id BIGINT NOT NULL,
|
||||
PRIMARY KEY (category_id, post_id)
|
||||
);
|
||||
|
||||
CREATE TABLE fk_test_has_pk (
|
||||
id BIGINT NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
CREATE TABLE fk_test_has_fk (
|
||||
id BIGINT NOT NULL,
|
||||
fk_id BIGINT NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
FOREIGN KEY (fk_id) REFERENCES fk_test_has_pk(id)
|
||||
);
|
||||
|
||||
CREATE TABLE keyboards (
|
||||
key_number BIGINT NOT NULL,
|
||||
name VARCHAR(50),
|
||||
PRIMARY KEY (key_number)
|
||||
);
|
||||
CREATE GENERATOR keyboards_seq;
|
||||
SET GENERATOR keyboards_seq TO 10000;
|
||||
|
||||
CREATE TABLE defaults (
|
||||
id BIGINT NOT NULL,
|
||||
default_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE GENERATOR defaults_seq;
|
||||
SET GENERATOR defaults_seq TO 10000;
|
||||
2
activerecord/test/fixtures/db_definitions/firebird2.drop.sql
vendored
Normal file
2
activerecord/test/fixtures/db_definitions/firebird2.drop.sql
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
DROP TABLE courses;
|
||||
DROP GENERATOR courses_seq;
|
||||
6
activerecord/test/fixtures/db_definitions/firebird2.sql
vendored
Normal file
6
activerecord/test/fixtures/db_definitions/firebird2.sql
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
CREATE TABLE courses (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL
|
||||
);
|
||||
CREATE GENERATOR courses_seq;
|
||||
SET GENERATOR courses_seq TO 10000;
|
||||
@@ -261,3 +261,11 @@ create table keyboards (
|
||||
);
|
||||
create sequence keyboards_seq minvalue 10000;
|
||||
|
||||
create table test_oci_defaults (
|
||||
id integer not null primary key,
|
||||
test_char char(1) default 'X' not null,
|
||||
test_string varchar2(20) default 'hello' not null,
|
||||
test_int integer default 3 not null
|
||||
);
|
||||
create sequence test_oci_defaults_seq minvalue 10000;
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
CREATE SEQUENCE public.accounts_id_seq START 100;
|
||||
|
||||
CREATE TABLE accounts (
|
||||
id serial,
|
||||
id integer DEFAULT nextval('public.accounts_id_seq'),
|
||||
firm_id integer,
|
||||
credit_limit integer,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SELECT setval('accounts_id_seq', 100);
|
||||
|
||||
CREATE SEQUENCE companies_nonstd_seq START 101;
|
||||
|
||||
|
||||
@@ -51,42 +51,44 @@ class FixturesTest < Test::Unit::TestCase
|
||||
assert_nil(secondRow["author_email_address"])
|
||||
end
|
||||
|
||||
def test_inserts_with_pre_and_suffix
|
||||
ActiveRecord::Base.connection.create_table :prefix_topics_suffix do |t|
|
||||
t.column :title, :string
|
||||
t.column :author_name, :string
|
||||
t.column :author_email_address, :string
|
||||
t.column :written_on, :datetime
|
||||
t.column :bonus_time, :time
|
||||
t.column :last_read, :date
|
||||
t.column :content, :text
|
||||
t.column :approved, :boolean, :default => true
|
||||
t.column :replies_count, :integer, :default => 0
|
||||
t.column :parent_id, :integer
|
||||
t.column :type, :string, :limit => 50
|
||||
if ActiveRecord::Base.connection.supports_migrations?
|
||||
def test_inserts_with_pre_and_suffix
|
||||
ActiveRecord::Base.connection.create_table :prefix_topics_suffix do |t|
|
||||
t.column :title, :string
|
||||
t.column :author_name, :string
|
||||
t.column :author_email_address, :string
|
||||
t.column :written_on, :datetime
|
||||
t.column :bonus_time, :time
|
||||
t.column :last_read, :date
|
||||
t.column :content, :text
|
||||
t.column :approved, :boolean, :default => true
|
||||
t.column :replies_count, :integer, :default => 0
|
||||
t.column :parent_id, :integer
|
||||
t.column :type, :string, :limit => 50
|
||||
end
|
||||
|
||||
# Store existing prefix/suffix
|
||||
old_prefix = ActiveRecord::Base.table_name_prefix
|
||||
old_suffix = ActiveRecord::Base.table_name_suffix
|
||||
|
||||
# Set a prefix/suffix we can test against
|
||||
ActiveRecord::Base.table_name_prefix = 'prefix_'
|
||||
ActiveRecord::Base.table_name_suffix = '_suffix'
|
||||
|
||||
topics = create_fixtures("topics")
|
||||
|
||||
# Restore prefix/suffix to its previous values
|
||||
ActiveRecord::Base.table_name_prefix = old_prefix
|
||||
ActiveRecord::Base.table_name_suffix = old_suffix
|
||||
|
||||
firstRow = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'David'")
|
||||
assert_equal("The First Topic", firstRow["title"])
|
||||
|
||||
secondRow = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'Mary'")
|
||||
assert_nil(secondRow["author_email_address"])
|
||||
ensure
|
||||
ActiveRecord::Base.connection.drop_table :prefix_topics_suffix rescue nil
|
||||
end
|
||||
|
||||
# Store existing prefix/suffix
|
||||
old_prefix = ActiveRecord::Base.table_name_prefix
|
||||
old_suffix = ActiveRecord::Base.table_name_suffix
|
||||
|
||||
# Set a prefix/suffix we can test against
|
||||
ActiveRecord::Base.table_name_prefix = 'prefix_'
|
||||
ActiveRecord::Base.table_name_suffix = '_suffix'
|
||||
|
||||
topics = create_fixtures("topics")
|
||||
|
||||
# Restore prefix/suffix to its previous values
|
||||
ActiveRecord::Base.table_name_prefix = old_prefix
|
||||
ActiveRecord::Base.table_name_suffix = old_suffix
|
||||
|
||||
firstRow = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'David'")
|
||||
assert_equal("The First Topic", firstRow["title"])
|
||||
|
||||
secondRow = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'Mary'")
|
||||
assert_nil(secondRow["author_email_address"])
|
||||
ensure
|
||||
ActiveRecord::Base.connection.drop_table :prefix_topics_suffix rescue nil
|
||||
end
|
||||
|
||||
def test_insert_with_datetime
|
||||
@@ -173,27 +175,45 @@ end
|
||||
if Account.connection.respond_to?(:reset_pk_sequence!)
|
||||
class FixturesResetPkSequenceTest < Test::Unit::TestCase
|
||||
fixtures :accounts
|
||||
fixtures :companies
|
||||
|
||||
def test_resets_to_min_pk
|
||||
Account.delete_all
|
||||
Account.connection.reset_pk_sequence!(Account.table_name)
|
||||
def setup
|
||||
@instances = [Account.new(:credit_limit => 50), Company.new(:name => 'RoR Consulting')]
|
||||
end
|
||||
|
||||
one = Account.new(:credit_limit => 50)
|
||||
one.save!
|
||||
assert_equal 1, one.id
|
||||
def test_resets_to_min_pk_with_specified_pk_and_sequence
|
||||
@instances.each do |instance|
|
||||
model = instance.class
|
||||
model.delete_all
|
||||
model.connection.reset_pk_sequence!(model.table_name, model.primary_key, model.sequence_name)
|
||||
|
||||
instance.save!
|
||||
assert_equal 1, instance.id, "Sequence reset for #{model.table_name} failed."
|
||||
end
|
||||
end
|
||||
|
||||
def test_resets_to_min_pk_with_default_pk_and_sequence
|
||||
@instances.each do |instance|
|
||||
model = instance.class
|
||||
model.delete_all
|
||||
model.connection.reset_pk_sequence!(model.table_name)
|
||||
|
||||
instance.save!
|
||||
assert_equal 1, instance.id, "Sequence reset for #{model.table_name} failed."
|
||||
end
|
||||
end
|
||||
|
||||
def test_create_fixtures_resets_sequences
|
||||
# create_fixtures performs reset_pk_sequence!
|
||||
max_id = create_fixtures('accounts').inject(0) do |max_id, (name, fixture)|
|
||||
fixture_id = fixture['id'].to_i
|
||||
fixture_id > max_id ? fixture_id : max_id
|
||||
end
|
||||
@instances.each do |instance|
|
||||
max_id = create_fixtures(instance.class.table_name).inject(0) do |max_id, (name, fixture)|
|
||||
fixture_id = fixture['id'].to_i
|
||||
fixture_id > max_id ? fixture_id : max_id
|
||||
end
|
||||
|
||||
# Clone the last fixture to check that it gets the next greatest id.
|
||||
another = Account.new(:credit_limit => 1200)
|
||||
another.save!
|
||||
assert_equal max_id + 1, another.id
|
||||
# Clone the last fixture to check that it gets the next greatest id.
|
||||
instance.save!
|
||||
assert_equal max_id + 1, instance.id, "Sequence reset for #{instance.class.table_name} failed."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,7 +11,7 @@ class InheritanceTest < Test::Unit::TestCase
|
||||
if current_adapter?(:SQLServerAdapter)
|
||||
Company.connection.execute "SET IDENTITY_INSERT companies ON"
|
||||
end
|
||||
Company.connection.insert "INSERT INTO companies (id, type, name) VALUES(100, 'bad_class!', 'Not happening')"
|
||||
Company.connection.insert "INSERT INTO companies (id, #{QUOTED_TYPE}, name) VALUES(100, 'bad_class!', 'Not happening')"
|
||||
|
||||
#We then need to turn it back Off before continuing.
|
||||
if current_adapter?(:SQLServerAdapter)
|
||||
|
||||
@@ -362,6 +362,9 @@ if ActiveRecord::Base.connection.supports_migrations?
|
||||
ActiveRecord::Base.table_name_suffix = ""
|
||||
Reminder.reset_table_name
|
||||
assert_equal "schema_info", ActiveRecord::Migrator.schema_info_table_name
|
||||
ensure
|
||||
ActiveRecord::Base.table_name_prefix = ""
|
||||
ActiveRecord::Base.table_name_suffix = ""
|
||||
end
|
||||
|
||||
def test_proper_table_name
|
||||
@@ -398,17 +401,20 @@ if ActiveRecord::Base.connection.supports_migrations?
|
||||
ActiveRecord::Base.table_name_prefix = 'prefix_'
|
||||
ActiveRecord::Base.table_name_suffix = '_suffix'
|
||||
Reminder.reset_table_name
|
||||
Reminder.reset_sequence_name
|
||||
WeNeedReminders.up
|
||||
assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
|
||||
assert_equal "hello world", Reminder.find(:first).content
|
||||
|
||||
WeNeedReminders.down
|
||||
assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
|
||||
ensure
|
||||
ActiveRecord::Base.table_name_prefix = ''
|
||||
ActiveRecord::Base.table_name_suffix = ''
|
||||
Reminder.reset_table_name
|
||||
Reminder.reset_sequence_name
|
||||
end
|
||||
|
||||
|
||||
def test_migrator_with_duplicates
|
||||
assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
|
||||
ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_duplicate/', nil)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
require 'abstract_unit'
|
||||
require 'fixtures/course'
|
||||
require 'fixtures/entrant'
|
||||
|
||||
# So we can test whether Course.connection survives a reload.
|
||||
require_dependency 'fixtures/course'
|
||||
|
||||
class MultipleDbTest < Test::Unit::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
@@ -45,4 +47,14 @@ class MultipleDbTest < Test::Unit::TestCase
|
||||
e3 = Entrant.find(3)
|
||||
assert_equal e3.course.id, c2.id
|
||||
end
|
||||
|
||||
def test_course_connection_should_survive_dependency_reload
|
||||
assert Course.connection
|
||||
|
||||
Dependencies.clear
|
||||
Object.send(:remove_const, :Course)
|
||||
require_dependency 'fixtures/course'
|
||||
|
||||
assert Course.connection
|
||||
end
|
||||
end
|
||||
|
||||
@@ -54,6 +54,7 @@ class PrimaryKeysTest < Test::Unit::TestCase
|
||||
assert_equal("jdoe", subscriber.id)
|
||||
subscriber.name = "John Doe"
|
||||
assert_nothing_raised { subscriber.save! }
|
||||
assert_equal("jdoe", subscriber.id)
|
||||
|
||||
subscriberReloaded = Subscriber.find("jdoe")
|
||||
assert_equal("John Doe", subscriberReloaded.name)
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
*1.2.4* (December 7th, 2005)
|
||||
|
||||
* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
|
||||
|
||||
|
||||
*1.2.3* (November 7th, 2005)
|
||||
|
||||
* Change Inflector#constantize to use eval instead of const_get. [Nicholas Seckar]
|
||||
|
||||
* Fix const_missing handler to ignore the trailing '.rb' on files when comparing paths. [Nicholas Seckar]
|
||||
|
||||
* Define kernel.rb methods in "class Object" instead of "module Kernel" to work around a Windows peculiarity [Sam Stephenson]
|
||||
|
||||
@@ -6,7 +6,7 @@ require File.join(File.dirname(__FILE__), 'lib', 'active_support', 'version')
|
||||
|
||||
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
||||
PKG_NAME = 'activesupport'
|
||||
PKG_VERSION = ActiveSupport::Version::STRING + PKG_BUILD
|
||||
PKG_VERSION = ActiveSupport::VERSION::STRING + PKG_BUILD
|
||||
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
||||
|
||||
RELEASE_NAME = "REL #{PKG_VERSION}"
|
||||
|
||||
@@ -142,9 +142,11 @@ module Inflector
|
||||
end
|
||||
|
||||
def constantize(camel_cased_word)
|
||||
camel_cased_word.split("::").inject(Object) do |final_type, part|
|
||||
final_type = final_type.const_get(part)
|
||||
end
|
||||
raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!" unless
|
||||
camel_cased_word.split("::").all? { |part| /^[A-Z]\w*$/ =~ part }
|
||||
|
||||
camel_cased_word = "::#{camel_cased_word}" unless camel_cased_word[0, 2] == '::'
|
||||
Object.module_eval(camel_cased_word, __FILE__, __LINE__)
|
||||
end
|
||||
|
||||
def ordinalize(number)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
module ActiveSupport
|
||||
module Version #:nodoc:
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 1
|
||||
MINOR = 2
|
||||
TINY = 3
|
||||
TINY = 4
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
||||
@@ -276,6 +276,11 @@ class InflectorTest < Test::Unit::TestCase
|
||||
assert_equal Ace::Base::Case, Inflector.constantize("Ace::Base::Case")
|
||||
assert_equal InflectorTest, Inflector.constantize("InflectorTest")
|
||||
assert_raises(NameError) { Inflector.constantize("UnknownClass") }
|
||||
assert_raises(NameError) { Inflector.constantize("An invalid string") }
|
||||
end
|
||||
|
||||
def test_constantize_doesnt_look_in_parent
|
||||
assert_raises(NameError) { Inflector.constantize("Ace::Base::InflectorTest") }
|
||||
end
|
||||
|
||||
def test_ordinal
|
||||
|
||||
@@ -1,4 +1,36 @@
|
||||
*SVN*
|
||||
*0.14.4 (RC5)* (December 7th, 2005)
|
||||
|
||||
* Add builtin/ to the gemspec. Closes #3047. [Nicholas Seckar, Sam Stephenson]
|
||||
|
||||
* Run initialize_logger in script/lighttpd to ensure the log file exists before tailing it. [Sam Stephenson]
|
||||
|
||||
* Make load_fixtures include csv fixtures. #3053. [me@mdaines.com]
|
||||
|
||||
* Fix freeze_gems so that the latest rails version is dumped by default. [Nicholas Seckar]
|
||||
|
||||
* Model generator: correct relative path to test_helper in unit test. [Jeremy Kemper]
|
||||
|
||||
* Make the db_schema_dump task honor the SCHEMA environment variable if present the way db_schema_import does. #2931. [Blair Zajac]
|
||||
|
||||
* Make help for the console command more explicit about how to specify the desired environment in which to run the console. #2911. [anonymous]
|
||||
|
||||
* PostgreSQL: the purge_test_database Rake task shouldn't explicitly specify the template0 template when creating a fresh test database. #2964 [dreamer3@gmail.com]
|
||||
|
||||
* Introducing the session_migration generator. Creates an add_session_table migration. Allows generator to specify migrations directory. #2958, #2960 [Rick Olson]
|
||||
|
||||
* Update to Prototype 1.4.0_rc4. Closes #2943 (old Array.prototype.reverse behavior can be obtained by passing false as an argument). [Sam Stephenson]
|
||||
|
||||
* Eliminate nil from newly generated logfiles. #2927 [Blair Zajac <blair@orcaware.com>]
|
||||
|
||||
* Update to Prototype 1.4.0_rc3. Closes #1893, #2505, #2550, #2748, #2783. [Sam Stephenson]
|
||||
|
||||
* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
|
||||
|
||||
* Update to latest script.aculo.us version (as of [3031])
|
||||
|
||||
* SQLite: the clone_structure_to_test and purge_test_database Rake tasks should always use the test environment. #2846 [Rick Olson]
|
||||
|
||||
* Make sure that legacy db tasks also reference :database for SQLite #2830 [kazuhiko@fdiary.net]
|
||||
|
||||
* Pass __FILE__ when evaluating plugins' init.rb. #2817 [james.adam@gmail.com]
|
||||
|
||||
@@ -8,8 +40,6 @@
|
||||
|
||||
* Don't detach or fork for script/server tailing [Nicholas Seckar]
|
||||
|
||||
* Added automatic browser launching on OS X when starting script/server [DHH]
|
||||
|
||||
* Changed all script/* to use #!/usr/bin/env ruby instead of hard-coded Ruby path. public/dispatcher.* still uses the hard-coded path for compatibility with web servers that don't have Ruby in path [DHH]
|
||||
|
||||
* Force RAILS_ENV to be "test" when running tests, so that ENV["RAILS_ENV"] = "production" in config/environment.rb doesn't wreck havok [DHH] #2660
|
||||
@@ -18,6 +48,7 @@
|
||||
|
||||
* Added an omnipresent RailsInfoController with a properties action that delivers an HTML rendering of Rails::Info (but only when local_request? is true). Added a new default index.html which fetches this with Ajax. [Sam Stephenson]
|
||||
|
||||
|
||||
*0.14.3 (RC4)* (November 7th, 2005)
|
||||
|
||||
* Add 'add_new_scripts' rake task for adding new rails scripts to script/* [Jamis Buck]
|
||||
|
||||
@@ -11,7 +11,7 @@ require File.join(File.dirname(__FILE__), 'lib', 'rails_version')
|
||||
|
||||
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
||||
PKG_NAME = 'rails'
|
||||
PKG_VERSION = Rails::Version::STRING + PKG_BUILD
|
||||
PKG_VERSION = Rails::VERSION::STRING + PKG_BUILD
|
||||
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
||||
PKG_DESTINATION = ENV["RAILS_PKG_DESTINATION"] || "../#{PKG_NAME}"
|
||||
|
||||
@@ -252,6 +252,7 @@ end
|
||||
PKG_FILES = FileList[
|
||||
'[a-zA-Z]*',
|
||||
'bin/**/*',
|
||||
'builtin/**/*',
|
||||
'configs/**/*',
|
||||
'doc/**/*',
|
||||
'dispatches/**/*',
|
||||
@@ -272,11 +273,11 @@ spec = Gem::Specification.new do |s|
|
||||
EOF
|
||||
|
||||
s.add_dependency('rake', '>= 0.6.2')
|
||||
s.add_dependency('activesupport', '= 1.2.3' + PKG_BUILD)
|
||||
s.add_dependency('activerecord', '= 1.13.0' + PKG_BUILD)
|
||||
s.add_dependency('actionpack', '= 1.11.0' + PKG_BUILD)
|
||||
s.add_dependency('actionmailer', '= 1.1.3' + PKG_BUILD)
|
||||
s.add_dependency('actionwebservice', '= 0.9.3' + PKG_BUILD)
|
||||
s.add_dependency('activesupport', '= 1.2.4' + PKG_BUILD)
|
||||
s.add_dependency('activerecord', '= 1.13.1' + PKG_BUILD)
|
||||
s.add_dependency('actionpack', '= 1.11.1' + PKG_BUILD)
|
||||
s.add_dependency('actionmailer', '= 1.1.4' + PKG_BUILD)
|
||||
s.add_dependency('actionwebservice', '= 0.9.4' + PKG_BUILD)
|
||||
|
||||
s.rdoc_options << '--exclude' << '.'
|
||||
s.has_rdoc = false
|
||||
|
||||
@@ -12,7 +12,7 @@ end
|
||||
Signal.trap("INT") { puts; exit }
|
||||
|
||||
require File.dirname(__FILE__) + '/../lib/rails_version'
|
||||
abort "Rails #{Rails::Version::STRING}" if %w(--version -v).include? ARGV.first
|
||||
abort "Rails #{Rails::VERSION::STRING}" if %w(--version -v).include? ARGV.first
|
||||
|
||||
require File.dirname(__FILE__) + '/../lib/rails_generator'
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# Don't change this file. Configuration is done in config/environment.rb and config/environments/*.rb
|
||||
|
||||
unless defined?(RAILS_ROOT)
|
||||
root_path = File.join(File.dirname(__FILE__), '..')
|
||||
unless RUBY_PLATFORM =~ /mswin32/
|
||||
|
||||
@@ -197,7 +197,7 @@
|
||||
window.onload = function() {
|
||||
$('search-text').value = '';
|
||||
$('search').onsubmit = function() {
|
||||
$('search-text').value = 'site:rubyonrails.com ' + $F('search-text');
|
||||
$('search-text').value = 'site:rubyonrails.org ' + $F('search-text');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -209,7 +209,7 @@
|
||||
<li>
|
||||
<form id="search" action="http://www.google.com/search" method="get">
|
||||
<input type="hidden" name="hl" value="en" />
|
||||
<input type="text" id="search-text" name="q" value="site:rubyonrails.com " />
|
||||
<input type="text" id="search-text" name="q" value="site:rubyonrails.org " />
|
||||
<input type="submit" value="Search" /> the Rails site
|
||||
</form>
|
||||
</li>
|
||||
@@ -217,23 +217,21 @@
|
||||
<li>
|
||||
<h3>Join the community</h3>
|
||||
<ul class="links">
|
||||
<li><a href="http://www.rubyonrails.com/">Ruby on Rails</a></li>
|
||||
<li><a href="http://weblog.rubyonrails.com/">Official weblog</a></li>
|
||||
<li><a href="http://www.rubyonrails.org/">Ruby on Rails</a></li>
|
||||
<li><a href="http://weblog.rubyonrails.org/">Official weblog</a></li>
|
||||
<li><a href="http://lists.rubyonrails.org/">Mailing lists</a></li>
|
||||
<li><a href="http://wiki.rubyonrails.com/rails/pages/IRC">IRC channel</a></li>
|
||||
<li><a href="http://wiki.rubyonrails.com/">Wiki</a></li>
|
||||
<li><a href="http://dev.rubyonrails.com/">Bug tracker</a></li>
|
||||
<li><a href="http://wiki.rubyonrails.org/rails/pages/IRC">IRC channel</a></li>
|
||||
<li><a href="http://wiki.rubyonrails.org/">Wiki</a></li>
|
||||
<li><a href="http://dev.rubyonrails.org/">Bug tracker</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<h3>Browse the documentation</h3>
|
||||
<ul class="links">
|
||||
<li><a href="http://manuals.rubyonrails.com/">Manuals</a></li>
|
||||
<li><a href="http://api.rubyonrails.com/">All APIs</a></li>
|
||||
<li><a href="http://ar.rubyonrails.com/">Active Record</a></li>
|
||||
<li><a href="http://ap.rubyonrails.com/">Action Pack</a></li>
|
||||
<li><a href="http://as.rubyonrails.com/">Active Support</a></li>
|
||||
<li><a href="http://api.rubyonrails.org/">Rails API</a></li>
|
||||
<li><a href="http://www.ruby-doc.org/stdlib/">Ruby standard library</a></li>
|
||||
<li><a href="http://www.ruby-doc.org/core/">Ruby core</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -256,18 +254,18 @@
|
||||
|
||||
<ol>
|
||||
<li>
|
||||
<h2>Create your databases and edit <tt>database.yml</tt></h2>
|
||||
<p>Instructions here</p>
|
||||
<h2>Create your databases and edit <tt>config/database.yml</tt></h2>
|
||||
<p>Rails needs to know your login and password.</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<h2>Use <tt>script/generate</tt> to create your models and controllers</h2>
|
||||
<p>Instructions here</p>
|
||||
<p>To see all available options, run it without parameters.</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<h2>Set up a default route and remove or rename this file</h2>
|
||||
<p>Instructions here</p>
|
||||
<p>Routes are setup in config/routes.rb.</p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
31
railties/html/javascripts/controls.js
vendored
31
railties/html/javascripts/controls.js
vendored
@@ -80,7 +80,10 @@ Autocompleter.Base.prototype = {
|
||||
|
||||
show: function() {
|
||||
if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
|
||||
if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0) && (Element.getStyle(this.update, 'position')=='absolute')) {
|
||||
if(!this.iefix &&
|
||||
(navigator.appVersion.indexOf('MSIE')>0) &&
|
||||
(navigator.userAgent.indexOf('Opera')<0) &&
|
||||
(Element.getStyle(this.update, 'position')=='absolute')) {
|
||||
new Insertion.After(this.update,
|
||||
'<iframe id="' + this.update.id + '_iefix" '+
|
||||
'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
|
||||
@@ -718,4 +721,30 @@ Ajax.InPlaceEditor.prototype = {
|
||||
Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Delayed observer, like Form.Element.Observer,
|
||||
// but waits for delay after last key input
|
||||
// Ideal for live-search fields
|
||||
|
||||
Form.Element.DelayedObserver = Class.create();
|
||||
Form.Element.DelayedObserver.prototype = {
|
||||
initialize: function(element, delay, callback) {
|
||||
this.delay = delay || 0.5;
|
||||
this.element = $(element);
|
||||
this.callback = callback;
|
||||
this.timer = null;
|
||||
this.lastValue = $F(this.element);
|
||||
Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
|
||||
},
|
||||
delayedListener: function(event) {
|
||||
if(this.lastValue == $F(this.element)) return;
|
||||
if(this.timer) clearTimeout(this.timer);
|
||||
this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
|
||||
this.lastValue = $F(this.element);
|
||||
},
|
||||
onTimerEvent: function() {
|
||||
this.timer = null;
|
||||
this.callback(this.element, $F(this.element));
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user