Ooops! Fixed upgrade of Rails.
This commit is contained in:
parent
5536e6e79e
commit
bba0cf6b10
177 changed files with 13221 additions and 0 deletions
30
vendor/rails/actionmailer/test/abstract_unit.rb
vendored
Normal file
30
vendor/rails/actionmailer/test/abstract_unit.rb
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
require 'test/unit'
|
||||
|
||||
$:.unshift "#{File.dirname(__FILE__)}/../lib"
|
||||
require 'action_mailer'
|
||||
|
||||
# Show backtraces for deprecated behavior for quicker cleanup.
|
||||
ActiveSupport::Deprecation.debug = true
|
||||
|
||||
$:.unshift "#{File.dirname(__FILE__)}/fixtures/helpers"
|
||||
ActionMailer::Base.template_root = "#{File.dirname(__FILE__)}/fixtures"
|
||||
|
||||
class MockSMTP
|
||||
def self.deliveries
|
||||
@@deliveries
|
||||
end
|
||||
|
||||
def initialize
|
||||
@@deliveries = []
|
||||
end
|
||||
|
||||
def sendmail(mail, from, to)
|
||||
@@deliveries << [mail, from, to]
|
||||
end
|
||||
end
|
||||
|
||||
class Net::SMTP
|
||||
def self.start(*args)
|
||||
yield MockSMTP.new
|
||||
end
|
||||
end
|
1
vendor/rails/actionmailer/test/fixtures/first_mailer/share.rhtml
vendored
Normal file
1
vendor/rails/actionmailer/test/fixtures/first_mailer/share.rhtml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
first mail
|
|
@ -0,0 +1 @@
|
|||
Have a lovely picture, from me. Enjoy!
|
14
vendor/rails/actionmailer/test/fixtures/raw_email_quoted_with_0d0a
vendored
Normal file
14
vendor/rails/actionmailer/test/fixtures/raw_email_quoted_with_0d0a
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
Mime-Version: 1.0 (Apple Message framework v730)
|
||||
Message-Id: <9169D984-4E0B-45EF-82D4-8F5E53AD7012@example.com>
|
||||
From: foo@example.com
|
||||
Subject: testing
|
||||
Date: Mon, 6 Jun 2005 22:21:22 +0200
|
||||
To: blah@example.com
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Content-Type: text/plain
|
||||
|
||||
A fax has arrived from remote ID ''.=0D=0A-----------------------=
|
||||
-------------------------------------=0D=0ATime: 3/9/2006 3:50:52=
|
||||
PM=0D=0AReceived from remote ID: =0D=0AInbound user ID XXXXXXXXXX, r=
|
||||
outing code XXXXXXXXX=0D=0AResult: (0/352;0/0) Successful Send=0D=0AP=
|
||||
age record: 1 - 1=0D=0AElapsed time: 00:58 on channel 11=0D=0A
|
14
vendor/rails/actionmailer/test/fixtures/raw_email_with_partially_quoted_subject
vendored
Normal file
14
vendor/rails/actionmailer/test/fixtures/raw_email_with_partially_quoted_subject
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
From jamis@37signals.com Mon May 2 16:07:05 2005
|
||||
Mime-Version: 1.0 (Apple Message framework v622)
|
||||
Content-Transfer-Encoding: base64
|
||||
Message-Id: <d3b8cf8e49f04480850c28713a1f473e@37signals.com>
|
||||
Content-Type: text/plain;
|
||||
charset=EUC-KR;
|
||||
format=flowed
|
||||
To: jamis@37signals.com
|
||||
From: Jamis Buck <jamis@37signals.com>
|
||||
Subject: Re: Test: =?UTF-8?B?Iua8ouWtlyI=?= mid =?UTF-8?B?Iua8ouWtlyI=?= tail
|
||||
Date: Mon, 2 May 2005 16:07:05 -0600
|
||||
|
||||
tOu6zrrQwMcguLbC+bChwfa3ziwgv+y4rrTCIMfPs6q01MC7ILnPvcC0z7TZLg0KDQrBpiDAzLin
|
||||
wLogSmFtaXPA1LTPtNku
|
1
vendor/rails/actionmailer/test/fixtures/second_mailer/share.rhtml
vendored
Normal file
1
vendor/rails/actionmailer/test/fixtures/second_mailer/share.rhtml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
second mail
|
1
vendor/rails/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.rhtml.bak
vendored
Normal file
1
vendor/rails/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.rhtml.bak
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
Ignored when searching for implicitly multipart parts.
|
3
vendor/rails/actionmailer/test/fixtures/test_mailer/signed_up_with_url.rhtml
vendored
Normal file
3
vendor/rails/actionmailer/test/fixtures/test_mailer/signed_up_with_url.rhtml
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
Hello there,
|
||||
|
||||
Mr. <%= @recipient %>. Please see our greeting at <%= @welcome_url %>
|
68
vendor/rails/actionmailer/test/url_test.rb
vendored
Normal file
68
vendor/rails/actionmailer/test/url_test.rb
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
require "#{File.dirname(__FILE__)}/abstract_unit"
|
||||
|
||||
class TestMailer < ActionMailer::Base
|
||||
def signed_up_with_url(recipient)
|
||||
@recipients = recipient
|
||||
@subject = "[Signed up] Welcome #{recipient}"
|
||||
@from = "system@loudthinking.com"
|
||||
@sent_on = Time.local(2004, 12, 12)
|
||||
|
||||
@body["recipient"] = recipient
|
||||
@body["welcome_url"] = url_for :host => "example.com", :controller => "welcome", :action => "greeting"
|
||||
end
|
||||
|
||||
class <<self
|
||||
attr_accessor :received_body
|
||||
end
|
||||
|
||||
def receive(mail)
|
||||
self.class.received_body = mail.body
|
||||
end
|
||||
end
|
||||
|
||||
class ActionMailerUrlTest < Test::Unit::TestCase
|
||||
include ActionMailer::Quoting
|
||||
|
||||
def encode( text, charset="utf-8" )
|
||||
quoted_printable( text, charset )
|
||||
end
|
||||
|
||||
def new_mail( charset="utf-8" )
|
||||
mail = TMail::Mail.new
|
||||
mail.mime_version = "1.0"
|
||||
if charset
|
||||
mail.set_content_type "text", "plain", { "charset" => charset }
|
||||
end
|
||||
mail
|
||||
end
|
||||
|
||||
def setup
|
||||
ActionMailer::Base.delivery_method = :test
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.deliveries = []
|
||||
|
||||
@recipient = 'test@localhost'
|
||||
end
|
||||
|
||||
def test_signed_up_with_url
|
||||
ActionController::Routing::Routes.draw do |map|
|
||||
map.connect ':controller/:action/:id'
|
||||
end
|
||||
|
||||
expected = new_mail
|
||||
expected.to = @recipient
|
||||
expected.subject = "[Signed up] Welcome #{@recipient}"
|
||||
expected.body = "Hello there, \n\nMr. #{@recipient}. Please see our greeting at http://example.com/welcome/greeting"
|
||||
expected.from = "system@loudthinking.com"
|
||||
expected.date = Time.local(2004, 12, 12)
|
||||
|
||||
created = nil
|
||||
assert_nothing_raised { created = TestMailer.create_signed_up_with_url(@recipient) }
|
||||
assert_not_nil created
|
||||
assert_equal expected.encoded, created.encoded
|
||||
|
||||
assert_nothing_raised { TestMailer.deliver_signed_up_with_url(@recipient) }
|
||||
assert_not_nil ActionMailer::Base.deliveries.first
|
||||
assert_equal expected.encoded, ActionMailer::Base.deliveries.first.encoded
|
||||
end
|
||||
end
|
228
vendor/rails/actionpack/lib/action_controller/assertions/deprecated_assertions.rb
vendored
Normal file
228
vendor/rails/actionpack/lib/action_controller/assertions/deprecated_assertions.rb
vendored
Normal file
|
@ -0,0 +1,228 @@
|
|||
require 'rexml/document'
|
||||
|
||||
module ActionController #:nodoc:
|
||||
module Assertions #:nodoc:
|
||||
module DeprecatedAssertions #:nodoc:
|
||||
def assert_success(message=nil) #:nodoc:
|
||||
assert_response(:success, message)
|
||||
end
|
||||
deprecate :assert_success => "use assert_response(:success)"
|
||||
|
||||
def assert_redirect(message=nil) #:nodoc:
|
||||
assert_response(:redirect, message)
|
||||
end
|
||||
deprecate :assert_redirect => "use assert_response(:redirect)"
|
||||
|
||||
def assert_rendered_file(expected=nil, message=nil) #:nodoc:
|
||||
assert_template(expected, message)
|
||||
end
|
||||
deprecate :assert_rendered_file => :assert_template
|
||||
|
||||
# ensure that the session has an object with the specified name
|
||||
def assert_session_has(key=nil, message=nil) #:nodoc:
|
||||
msg = build_message(message, "<?> is not in the session <?>", key, @response.session)
|
||||
assert_block(msg) { @response.has_session_object?(key) }
|
||||
end
|
||||
deprecate :assert_session_has => "use assert(@response.has_session_object?(key))"
|
||||
|
||||
# ensure that the session has no object with the specified name
|
||||
def assert_session_has_no(key=nil, message=nil) #:nodoc:
|
||||
msg = build_message(message, "<?> is in the session <?>", key, @response.session)
|
||||
assert_block(msg) { !@response.has_session_object?(key) }
|
||||
end
|
||||
deprecate :assert_session_has_no => "use assert(!@response.has_session_object?(key))"
|
||||
|
||||
def assert_session_equal(expected = nil, key = nil, message = nil) #:nodoc:
|
||||
msg = build_message(message, "<?> expected in session['?'] but was <?>", expected, key, @response.session[key])
|
||||
assert_block(msg) { expected == @response.session[key] }
|
||||
end
|
||||
deprecate :assert_session_equal => "use assert_equal(expected, @response[key])"
|
||||
|
||||
# -- cookie assertions ---------------------------------------------------
|
||||
|
||||
def assert_no_cookie(key = nil, message = nil) #:nodoc:
|
||||
actual = @response.cookies[key]
|
||||
msg = build_message(message, "<?> not expected in cookies['?']", actual, key)
|
||||
assert_block(msg) { actual.nil? or actual.empty? }
|
||||
end
|
||||
deprecate :assert_no_cookie => "use assert(!@response.cookies.key?(key))"
|
||||
|
||||
def assert_cookie_equal(expected = nil, key = nil, message = nil) #:nodoc:
|
||||
actual = @response.cookies[key]
|
||||
actual = actual.first if actual
|
||||
msg = build_message(message, "<?> expected in cookies['?'] but was <?>", expected, key, actual)
|
||||
assert_block(msg) { expected == actual }
|
||||
end
|
||||
deprecate :assert_cookie_equal => "use assert(@response.cookies.key?(key))"
|
||||
|
||||
# -- flash assertions ---------------------------------------------------
|
||||
|
||||
# ensure that the flash has an object with the specified name
|
||||
def assert_flash_has(key=nil, message=nil) #:nodoc:
|
||||
msg = build_message(message, "<?> is not in the flash <?>", key, @response.flash)
|
||||
assert_block(msg) { @response.has_flash_object?(key) }
|
||||
end
|
||||
deprecate :assert_flash_has => "use assert(@response.has_flash_object?(key))"
|
||||
|
||||
# ensure that the flash has no object with the specified name
|
||||
def assert_flash_has_no(key=nil, message=nil) #:nodoc:
|
||||
msg = build_message(message, "<?> is in the flash <?>", key, @response.flash)
|
||||
assert_block(msg) { !@response.has_flash_object?(key) }
|
||||
end
|
||||
deprecate :assert_flash_has_no => "use assert(!@response.has_flash_object?(key))"
|
||||
|
||||
# ensure the flash exists
|
||||
def assert_flash_exists(message=nil) #:nodoc:
|
||||
msg = build_message(message, "the flash does not exist <?>", @response.session['flash'] )
|
||||
assert_block(msg) { @response.has_flash? }
|
||||
end
|
||||
deprecate :assert_flash_exists => "use assert(@response.has_flash?)"
|
||||
|
||||
# ensure the flash does not exist
|
||||
def assert_flash_not_exists(message=nil) #:nodoc:
|
||||
msg = build_message(message, "the flash exists <?>", @response.flash)
|
||||
assert_block(msg) { !@response.has_flash? }
|
||||
end
|
||||
deprecate :assert_flash_not_exists => "use assert(!@response.has_flash?)"
|
||||
|
||||
# ensure the flash is empty but existent
|
||||
def assert_flash_empty(message=nil) #:nodoc:
|
||||
msg = build_message(message, "the flash is not empty <?>", @response.flash)
|
||||
assert_block(msg) { !@response.has_flash_with_contents? }
|
||||
end
|
||||
deprecate :assert_flash_empty => "use assert(!@response.has_flash_with_contents?)"
|
||||
|
||||
# ensure the flash is not empty
|
||||
def assert_flash_not_empty(message=nil) #:nodoc:
|
||||
msg = build_message(message, "the flash is empty")
|
||||
assert_block(msg) { @response.has_flash_with_contents? }
|
||||
end
|
||||
deprecate :assert_flash_not_empty => "use assert(@response.has_flash_with_contents?)"
|
||||
|
||||
def assert_flash_equal(expected = nil, key = nil, message = nil) #:nodoc:
|
||||
msg = build_message(message, "<?> expected in flash['?'] but was <?>", expected, key, @response.flash[key])
|
||||
assert_block(msg) { expected == @response.flash[key] }
|
||||
end
|
||||
deprecate :assert_flash_equal => "use assert_equal(expected, @response.flash[key])"
|
||||
|
||||
|
||||
# ensure our redirection url is an exact match
|
||||
def assert_redirect_url(url=nil, message=nil) #:nodoc:
|
||||
assert_redirect(message)
|
||||
msg = build_message(message, "<?> is not the redirected location <?>", url, @response.redirect_url)
|
||||
assert_block(msg) { @response.redirect_url == url }
|
||||
end
|
||||
deprecate :assert_redirect_url => "use assert_equal(url, @response.redirect_url)"
|
||||
|
||||
# ensure our redirection url matches a pattern
|
||||
def assert_redirect_url_match(pattern=nil, message=nil) #:nodoc:
|
||||
assert_redirect(message)
|
||||
msg = build_message(message, "<?> was not found in the location: <?>", pattern, @response.redirect_url)
|
||||
assert_block(msg) { @response.redirect_url_match?(pattern) }
|
||||
end
|
||||
deprecate :assert_redirect_url_match => "use assert(@response.redirect_url_match?(pattern))"
|
||||
|
||||
|
||||
# -- template assertions ------------------------------------------------
|
||||
|
||||
# ensure that a template object with the given name exists
|
||||
def assert_template_has(key=nil, message=nil) #:nodoc:
|
||||
msg = build_message(message, "<?> is not a template object", key )
|
||||
assert_block(msg) { @response.has_template_object?(key) }
|
||||
end
|
||||
deprecate :assert_template_has => "use assert(@response.has_template_object?(key))"
|
||||
|
||||
# ensure that a template object with the given name does not exist
|
||||
def assert_template_has_no(key=nil,message=nil) #:nodoc:
|
||||
msg = build_message(message, "<?> is a template object <?>", key, @response.template_objects[key])
|
||||
assert_block(msg) { !@response.has_template_object?(key) }
|
||||
end
|
||||
deprecate :assert_template_has_no => "use assert(!@response.has_template_object?(key))"
|
||||
|
||||
# ensures that the object assigned to the template on +key+ is equal to +expected+ object.
|
||||
def assert_template_equal(expected = nil, key = nil, message = nil) #:nodoc:
|
||||
msg = build_message(message, "<?> expected in assigns['?'] but was <?>", expected, key, @response.template.assigns[key.to_s])
|
||||
assert_block(msg) { expected == @response.template.assigns[key.to_s] }
|
||||
end
|
||||
alias_method :assert_assigned_equal, :assert_template_equal
|
||||
deprecate :assert_assigned_equal => "use assert_equal(expected, @response.template.assigns[key.to_s])"
|
||||
deprecate :assert_template_equal => "use assert_equal(expected, @response.template.assigns[key.to_s])"
|
||||
|
||||
# Asserts that the template returns the +expected+ string or array based on the XPath +expression+.
|
||||
# This will only work if the template rendered a valid XML document.
|
||||
def assert_template_xpath_match(expression=nil, expected=nil, message=nil) #:nodoc:
|
||||
xml, matches = REXML::Document.new(@response.body), []
|
||||
xml.elements.each(expression) { |e| matches << e.text }
|
||||
if matches.empty? then
|
||||
msg = build_message(message, "<?> not found in document", expression)
|
||||
flunk(msg)
|
||||
return
|
||||
elsif matches.length < 2 then
|
||||
matches = matches.first
|
||||
end
|
||||
|
||||
msg = build_message(message, "<?> found <?>, not <?>", expression, matches, expected)
|
||||
assert_block(msg) { matches == expected }
|
||||
end
|
||||
deprecate :assert_template_xpath_match => "you should use assert_tag, instead"
|
||||
|
||||
# Assert the template object with the given name is an Active Record descendant and is valid.
|
||||
def assert_valid_record(key = nil, message = nil) #:nodoc:
|
||||
record = find_record_in_template(key)
|
||||
msg = build_message(message, "Active Record is invalid <?>)", record.errors.full_messages)
|
||||
assert_block(msg) { record.valid? }
|
||||
end
|
||||
deprecate :assert_valid_record => "use assert(assigns(key).valid?)"
|
||||
|
||||
# Assert the template object with the given name is an Active Record descendant and is invalid.
|
||||
def assert_invalid_record(key = nil, message = nil) #:nodoc:
|
||||
record = find_record_in_template(key)
|
||||
msg = build_message(message, "Active Record is valid)")
|
||||
assert_block(msg) { !record.valid? }
|
||||
end
|
||||
deprecate :assert_invalid_record => "use assert(!assigns(key).valid?)"
|
||||
|
||||
# Assert the template object with the given name is an Active Record descendant and the specified column(s) are valid.
|
||||
def assert_valid_column_on_record(key = nil, columns = "", message = nil) #:nodoc:
|
||||
record = find_record_in_template(key)
|
||||
record.send(:validate)
|
||||
|
||||
cols = glue_columns(columns)
|
||||
cols.delete_if { |col| !record.errors.invalid?(col) }
|
||||
msg = build_message(message, "Active Record has invalid columns <?>)", cols.join(",") )
|
||||
assert_block(msg) { cols.empty? }
|
||||
end
|
||||
deprecate :assert_valid_column_on_record => "use assert(!record.errors.invalid?(column)) instead"
|
||||
|
||||
# Assert the template object with the given name is an Active Record descendant and the specified column(s) are invalid.
|
||||
def assert_invalid_column_on_record(key = nil, columns = "", message = nil) #:nodoc:
|
||||
record = find_record_in_template(key)
|
||||
record.send(:validate)
|
||||
|
||||
cols = glue_columns(columns)
|
||||
cols.delete_if { |col| record.errors.invalid?(col) }
|
||||
msg = build_message(message, "Active Record has valid columns <?>)", cols.join(",") )
|
||||
assert_block(msg) { cols.empty? }
|
||||
end
|
||||
deprecate :assert_invalid_column_on_record => "use assert(record.errors.invalid?(column)) instead"
|
||||
|
||||
private
|
||||
def glue_columns(columns)
|
||||
cols = []
|
||||
cols << columns if columns.class == String
|
||||
cols += columns if columns.class == Array
|
||||
cols
|
||||
end
|
||||
|
||||
def find_record_in_template(key = nil)
|
||||
assert_not_nil assigns(key)
|
||||
record = @response.template_objects[key]
|
||||
|
||||
assert_not_nil(record)
|
||||
assert_kind_of ActiveRecord::Base, record
|
||||
|
||||
return record
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
25
vendor/rails/actionpack/lib/action_controller/assertions/dom_assertions.rb
vendored
Normal file
25
vendor/rails/actionpack/lib/action_controller/assertions/dom_assertions.rb
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
module ActionController
|
||||
module Assertions
|
||||
module DomAssertions
|
||||
# test 2 html strings to be equivalent, i.e. identical up to reordering of attributes
|
||||
def assert_dom_equal(expected, actual, message="")
|
||||
clean_backtrace do
|
||||
expected_dom = HTML::Document.new(expected).root
|
||||
actual_dom = HTML::Document.new(actual).root
|
||||
full_message = build_message(message, "<?> expected to be == to\n<?>.", expected_dom.to_s, actual_dom.to_s)
|
||||
assert_block(full_message) { expected_dom == actual_dom }
|
||||
end
|
||||
end
|
||||
|
||||
# negated form of +assert_dom_equivalent+
|
||||
def assert_dom_not_equal(expected, actual, message="")
|
||||
clean_backtrace do
|
||||
expected_dom = HTML::Document.new(expected).root
|
||||
actual_dom = HTML::Document.new(actual).root
|
||||
full_message = build_message(message, "<?> expected to be != to\n<?>.", expected_dom.to_s, actual_dom.to_s)
|
||||
assert_block(full_message) { expected_dom != actual_dom }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
12
vendor/rails/actionpack/lib/action_controller/assertions/model_assertions.rb
vendored
Normal file
12
vendor/rails/actionpack/lib/action_controller/assertions/model_assertions.rb
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
module ActionController
|
||||
module Assertions
|
||||
module ModelAssertions
|
||||
# ensures that the passed record is valid by active record standards. returns the error messages if not
|
||||
def assert_valid(record)
|
||||
clean_backtrace do
|
||||
assert record.valid?, record.errors.full_messages.join("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
140
vendor/rails/actionpack/lib/action_controller/assertions/response_assertions.rb
vendored
Normal file
140
vendor/rails/actionpack/lib/action_controller/assertions/response_assertions.rb
vendored
Normal file
|
@ -0,0 +1,140 @@
|
|||
require 'rexml/document'
|
||||
require File.dirname(__FILE__) + "/../vendor/html-scanner/html/document"
|
||||
|
||||
module ActionController
|
||||
module Assertions
|
||||
module ResponseAssertions
|
||||
# Asserts that the response is one of the following types:
|
||||
#
|
||||
# * <tt>:success</tt>: Status code was 200
|
||||
# * <tt>:redirect</tt>: Status code was in the 300-399 range
|
||||
# * <tt>:missing</tt>: Status code was 404
|
||||
# * <tt>:error</tt>: Status code was in the 500-599 range
|
||||
#
|
||||
# You can also pass an explicit status number like assert_response(501)
|
||||
# or its symbolic equivalent assert_response(:not_implemented).
|
||||
# See ActionController::StatusCodes for a full list.
|
||||
def assert_response(type, message = nil)
|
||||
clean_backtrace do
|
||||
if [ :success, :missing, :redirect, :error ].include?(type) && @response.send("#{type}?")
|
||||
assert_block("") { true } # to count the assertion
|
||||
elsif type.is_a?(Fixnum) && @response.response_code == type
|
||||
assert_block("") { true } # to count the assertion
|
||||
elsif type.is_a?(Symbol) && @response.response_code == ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[type]
|
||||
assert_block("") { true } # to count the assertion
|
||||
else
|
||||
assert_block(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code)) { false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Assert that the redirection options passed in match those of the redirect called in the latest action. This match can be partial,
|
||||
# such that assert_redirected_to(:controller => "weblog") will also match the redirection of
|
||||
# redirect_to(:controller => "weblog", :action => "show") and so on.
|
||||
def assert_redirected_to(options = {}, message=nil)
|
||||
clean_backtrace do
|
||||
assert_response(:redirect, message)
|
||||
return true if options == @response.redirected_to
|
||||
ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
|
||||
|
||||
begin
|
||||
url = {}
|
||||
original = { :expected => options, :actual => @response.redirected_to.is_a?(Symbol) ? @response.redirected_to : @response.redirected_to.dup }
|
||||
original.each do |key, value|
|
||||
if value.is_a?(Symbol)
|
||||
value = @controller.respond_to?(value, true) ? @controller.send(value) : @controller.send("hash_for_#{value}_url")
|
||||
end
|
||||
|
||||
unless value.is_a?(Hash)
|
||||
request = case value
|
||||
when NilClass then nil
|
||||
when /^\w+:\/\// then recognized_request_for(%r{^(\w+://.*?(/|$|\?))(.*)$} =~ value ? $3 : nil)
|
||||
else recognized_request_for(value)
|
||||
end
|
||||
value = request.path_parameters if request
|
||||
end
|
||||
|
||||
if value.is_a?(Hash) # stringify 2 levels of hash keys
|
||||
if name = value.delete(:use_route)
|
||||
route = ActionController::Routing::Routes.named_routes[name]
|
||||
value.update(route.parameter_shell)
|
||||
end
|
||||
|
||||
value.stringify_keys!
|
||||
value.values.select { |v| v.is_a?(Hash) }.collect { |v| v.stringify_keys! }
|
||||
if key == :expected && value['controller'] == @controller.controller_name && original[:actual].is_a?(Hash)
|
||||
original[:actual].stringify_keys!
|
||||
value.delete('controller') if original[:actual]['controller'].nil? || original[:actual]['controller'] == value['controller']
|
||||
end
|
||||
end
|
||||
|
||||
if value.respond_to?(:[]) && value['controller']
|
||||
if key == :actual && value['controller'].first != '/' && !value['controller'].include?('/')
|
||||
new_controller_path = ActionController::Routing.controller_relative_to(value['controller'], @controller.class.controller_path)
|
||||
value['controller'] = new_controller_path if value['controller'] != new_controller_path && ActionController::Routing.possible_controllers.include?(new_controller_path)
|
||||
end
|
||||
value['controller'] = value['controller'][1..-1] if value['controller'].first == '/' # strip leading hash
|
||||
end
|
||||
url[key] = value
|
||||
end
|
||||
|
||||
|
||||
@response_diff = url[:expected].diff(url[:actual]) if url[:actual]
|
||||
msg = build_message(message, "response is not a redirection to all of the options supplied (redirection is <?>), difference: <?>",
|
||||
url[:actual], @response_diff)
|
||||
|
||||
assert_block(msg) do
|
||||
url[:expected].keys.all? do |k|
|
||||
if k == :controller then url[:expected][k] == ActionController::Routing.controller_relative_to(url[:actual][k], @controller.class.controller_path)
|
||||
else parameterize(url[:expected][k]) == parameterize(url[:actual][k])
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue ActionController::RoutingError # routing failed us, so match the strings only.
|
||||
msg = build_message(message, "expected a redirect to <?>, found one to <?>", options, @response.redirect_url)
|
||||
url_regexp = %r{^(\w+://.*?(/|$|\?))(.*)$}
|
||||
eurl, epath, url, path = [options, @response.redirect_url].collect do |url|
|
||||
u, p = (url_regexp =~ url) ? [$1, $3] : [nil, url]
|
||||
[u, (p.first == '/') ? p : '/' + p]
|
||||
end.flatten
|
||||
|
||||
assert_equal(eurl, url, msg) if eurl && url
|
||||
assert_equal(epath, path, msg) if epath && path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Asserts that the request was rendered with the appropriate template file.
|
||||
def assert_template(expected = nil, message=nil)
|
||||
clean_backtrace do
|
||||
rendered = expected ? @response.rendered_file(!expected.include?('/')) : @response.rendered_file
|
||||
msg = build_message(message, "expecting <?> but rendering with <?>", expected, rendered)
|
||||
assert_block(msg) do
|
||||
if expected.nil?
|
||||
!@response.rendered_with_file?
|
||||
else
|
||||
expected == rendered
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def recognized_request_for(path, request_method = nil)
|
||||
path = "/#{path}" unless path.first == '/'
|
||||
|
||||
# Assume given controller
|
||||
request = ActionController::TestRequest.new({}, {}, nil)
|
||||
request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method
|
||||
request.path = path
|
||||
|
||||
ActionController::Routing::Routes.recognize(request)
|
||||
request
|
||||
end
|
||||
|
||||
def parameterize(value)
|
||||
value.respond_to?(:to_param) ? value.to_param : value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
98
vendor/rails/actionpack/lib/action_controller/assertions/routing_assertions.rb
vendored
Normal file
98
vendor/rails/actionpack/lib/action_controller/assertions/routing_assertions.rb
vendored
Normal file
|
@ -0,0 +1,98 @@
|
|||
module ActionController
|
||||
module Assertions
|
||||
module RoutingAssertions
|
||||
# Asserts that the routing of the given path was handled correctly and that the parsed options match.
|
||||
#
|
||||
# assert_recognizes({:controller => 'items', :action => 'index'}, 'items') # check the default action
|
||||
# assert_recognizes({:controller => 'items', :action => 'list'}, 'items/list') # check a specific action
|
||||
# assert_recognizes({:controller => 'items', :action => 'list', :id => '1'}, 'items/list/1') # check an action with a parameter
|
||||
#
|
||||
# Pass a hash in the second argument to specify the request method. This is useful for routes
|
||||
# requiring a specific HTTP method. The hash should contain a :path with the incoming request path
|
||||
# and a :method containing the required HTTP verb.
|
||||
#
|
||||
# # assert that POSTing to /items will call the create action on ItemsController
|
||||
# assert_recognizes({:controller => 'items', :action => 'create'}, {:path => 'items', :method => :post})
|
||||
#
|
||||
# You can also pass in "extras" with a hash containing URL parameters that would normally be in the query string. This can be used
|
||||
# to assert that values in the query string string will end up in the params hash correctly. To test query strings you must use the
|
||||
# extras argument, appending the query string on the path directly will not work. For example:
|
||||
#
|
||||
# # assert that a path of '/items/list/1?view=print' returns the correct options
|
||||
# assert_recognizes({:controller => 'items', :action => 'list', :id => '1', :view => 'print'}, 'items/list/1', { :view => "print" })
|
||||
def assert_recognizes(expected_options, path, extras={}, message=nil)
|
||||
if path.is_a? Hash
|
||||
request_method = path[:method]
|
||||
path = path[:path]
|
||||
else
|
||||
request_method = nil
|
||||
end
|
||||
|
||||
clean_backtrace do
|
||||
ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
|
||||
request = recognized_request_for(path, request_method)
|
||||
|
||||
expected_options = expected_options.clone
|
||||
extras.each_key { |key| expected_options.delete key } unless extras.nil?
|
||||
|
||||
expected_options.stringify_keys!
|
||||
routing_diff = expected_options.diff(request.path_parameters)
|
||||
msg = build_message(message, "The recognized options <?> did not match <?>, difference: <?>",
|
||||
request.path_parameters, expected_options, expected_options.diff(request.path_parameters))
|
||||
assert_block(msg) { request.path_parameters == expected_options }
|
||||
end
|
||||
end
|
||||
|
||||
# Asserts that the provided options can be used to generate the provided path. This is the inverse of assert_recognizes.
|
||||
# For example:
|
||||
#
|
||||
# assert_generates("/items", :controller => "items", :action => "index")
|
||||
# assert_generates("/items/list", :controller => "items", :action => "list")
|
||||
# assert_generates("/items/list/1", { :controller => "items", :action => "list", :id => "1" })
|
||||
def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)
|
||||
clean_backtrace do
|
||||
expected_path = "/#{expected_path}" unless expected_path[0] == ?/
|
||||
# Load routes.rb if it hasn't been loaded.
|
||||
ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
|
||||
|
||||
generated_path, extra_keys = ActionController::Routing::Routes.generate_extras(options, defaults)
|
||||
found_extras = options.reject {|k, v| ! extra_keys.include? k}
|
||||
|
||||
msg = build_message(message, "found extras <?>, not <?>", found_extras, extras)
|
||||
assert_block(msg) { found_extras == extras }
|
||||
|
||||
msg = build_message(message, "The generated path <?> did not match <?>", generated_path,
|
||||
expected_path)
|
||||
assert_block(msg) { expected_path == generated_path }
|
||||
end
|
||||
end
|
||||
|
||||
# Asserts that path and options match both ways; in other words, the URL generated from
|
||||
# options is the same as path, and also that the options recognized from path are the same as options. This
|
||||
# essentially combines assert_recognizes and assert_generates into one step.
|
||||
def assert_routing(path, options, defaults={}, extras={}, message=nil)
|
||||
assert_recognizes(options, path, extras, message)
|
||||
|
||||
controller, default_controller = options[:controller], defaults[:controller]
|
||||
if controller && controller.include?(?/) && default_controller && default_controller.include?(?/)
|
||||
options[:controller] = "/#{controller}"
|
||||
end
|
||||
|
||||
assert_generates(path, options, defaults, extras, message)
|
||||
end
|
||||
|
||||
private
|
||||
def recognized_request_for(path, request_method = nil)
|
||||
path = "/#{path}" unless path.first == '/'
|
||||
|
||||
# Assume given controller
|
||||
request = ActionController::TestRequest.new({}, {}, nil)
|
||||
request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method
|
||||
request.path = path
|
||||
|
||||
ActionController::Routing::Routes.recognize(request)
|
||||
request
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
571
vendor/rails/actionpack/lib/action_controller/assertions/selector_assertions.rb
vendored
Normal file
571
vendor/rails/actionpack/lib/action_controller/assertions/selector_assertions.rb
vendored
Normal file
|
@ -0,0 +1,571 @@
|
|||
#--
|
||||
# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
|
||||
# Under MIT and/or CC By license.
|
||||
#++
|
||||
|
||||
require 'rexml/document'
|
||||
require File.dirname(__FILE__) + "/../vendor/html-scanner/html/document"
|
||||
|
||||
module ActionController
|
||||
module Assertions
|
||||
unless const_defined?(:NO_STRIP)
|
||||
NO_STRIP = %w{pre script style textarea}
|
||||
end
|
||||
|
||||
# Adds the #assert_select method for use in Rails functional
|
||||
# test cases.
|
||||
#
|
||||
# Use #assert_select to make assertions on the response HTML of a controller
|
||||
# action. You can also call #assert_select within another #assert_select to
|
||||
# make assertions on elements selected by the enclosing assertion.
|
||||
#
|
||||
# Use #css_select to select elements without making an assertions, either
|
||||
# from the response HTML or elements selected by the enclosing assertion.
|
||||
#
|
||||
# In addition to HTML responses, you can make the following assertions:
|
||||
# * #assert_select_rjs -- Assertions on HTML content of RJS update and
|
||||
# insertion operations.
|
||||
# * #assert_select_encoded -- Assertions on HTML encoded inside XML,
|
||||
# for example for dealing with feed item descriptions.
|
||||
# * #assert_select_email -- Assertions on the HTML body of an e-mail.
|
||||
#
|
||||
# Also see HTML::Selector for learning how to use selectors.
|
||||
module SelectorAssertions
|
||||
# :call-seq:
|
||||
# css_select(selector) => array
|
||||
# css_select(element, selector) => array
|
||||
#
|
||||
# Select and return all matching elements.
|
||||
#
|
||||
# If called with a single argument, uses that argument as a selector
|
||||
# to match all elements of the current page. Returns an empty array
|
||||
# if no match is found.
|
||||
#
|
||||
# If called with two arguments, uses the first argument as the base
|
||||
# element and the second argument as the selector. Attempts to match the
|
||||
# base element and any of its children. Returns an empty array if no
|
||||
# match is found.
|
||||
#
|
||||
# The selector may be a CSS selector expression (+String+), an expression
|
||||
# with substitution values (+Array+) or an HTML::Selector object.
|
||||
#
|
||||
# For example:
|
||||
# forms = css_select("form")
|
||||
# forms.each do |form|
|
||||
# inputs = css_select(form, "input")
|
||||
# ...
|
||||
# end
|
||||
def css_select(*args)
|
||||
# See assert_select to understand what's going on here.
|
||||
arg = args.shift
|
||||
|
||||
if arg.is_a?(HTML::Node)
|
||||
root = arg
|
||||
arg = args.shift
|
||||
elsif arg == nil
|
||||
raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
|
||||
elsif @selected
|
||||
matches = []
|
||||
@selected.each do |selected|
|
||||
subset = css_select(selected, HTML::Selector.new(arg.dup, args.dup))
|
||||
subset.each do |match|
|
||||
matches << match unless matches.any? { |m| m.equal?(match) }
|
||||
end
|
||||
end
|
||||
|
||||
return matches
|
||||
else
|
||||
root = response_from_page_or_rjs
|
||||
end
|
||||
|
||||
case arg
|
||||
when String
|
||||
selector = HTML::Selector.new(arg, args)
|
||||
when Array
|
||||
selector = HTML::Selector.new(*arg)
|
||||
when HTML::Selector
|
||||
selector = arg
|
||||
else raise ArgumentError, "Expecting a selector as the first argument"
|
||||
end
|
||||
|
||||
selector.select(root)
|
||||
end
|
||||
|
||||
# :call-seq:
|
||||
# assert_select(selector, equality?, message?)
|
||||
# assert_select(element, selector, equality?, message?)
|
||||
#
|
||||
# An assertion that selects elements and makes one or more equality tests.
|
||||
#
|
||||
# If the first argument is an element, selects all matching elements
|
||||
# starting from (and including) that element and all its children in
|
||||
# depth-first order.
|
||||
#
|
||||
# If no element if specified, calling #assert_select will select from the
|
||||
# response HTML. Calling #assert_select inside an #assert_select block will
|
||||
# run the assertion for each element selected by the enclosing assertion.
|
||||
#
|
||||
# For example:
|
||||
# assert_select "ol>li" do |elements|
|
||||
# elements.each do |element|
|
||||
# assert_select element, "li"
|
||||
# end
|
||||
# end
|
||||
# Or for short:
|
||||
# assert_select "ol>li" do
|
||||
# assert_select "li"
|
||||
# end
|
||||
#
|
||||
# The selector may be a CSS selector expression (+String+), an expression
|
||||
# with substitution values, or an HTML::Selector object.
|
||||
#
|
||||
# === Equality Tests
|
||||
#
|
||||
# The equality test may be one of the following:
|
||||
# * <tt>true</tt> -- Assertion is true if at least one element selected.
|
||||
# * <tt>false</tt> -- Assertion is true if no element selected.
|
||||
# * <tt>String/Regexp</tt> -- Assertion is true if the text value of at least
|
||||
# one element matches the string or regular expression.
|
||||
# * <tt>Integer</tt> -- Assertion is true if exactly that number of
|
||||
# elements are selected.
|
||||
# * <tt>Range</tt> -- Assertion is true if the number of selected
|
||||
# elements fit the range.
|
||||
# If no equality test specified, the assertion is true if at least one
|
||||
# element selected.
|
||||
#
|
||||
# To perform more than one equality tests, use a hash with the following keys:
|
||||
# * <tt>:text</tt> -- Narrow the selection to elements that have this text
|
||||
# value (string or regexp).
|
||||
# * <tt>:html</tt> -- Narrow the selection to elements that have this HTML
|
||||
# content (string or regexp).
|
||||
# * <tt>:count</tt> -- Assertion is true if the number of selected elements
|
||||
# is equal to this value.
|
||||
# * <tt>:minimum</tt> -- Assertion is true if the number of selected
|
||||
# elements is at least this value.
|
||||
# * <tt>:maximum</tt> -- Assertion is true if the number of selected
|
||||
# elements is at most this value.
|
||||
#
|
||||
# If the method is called with a block, once all equality tests are
|
||||
# evaluated the block is called with an array of all matched elements.
|
||||
#
|
||||
# === Examples
|
||||
#
|
||||
# # At least one form element
|
||||
# assert_select "form"
|
||||
#
|
||||
# # Form element includes four input fields
|
||||
# assert_select "form input", 4
|
||||
#
|
||||
# # Page title is "Welcome"
|
||||
# assert_select "title", "Welcome"
|
||||
#
|
||||
# # Page title is "Welcome" and there is only one title element
|
||||
# assert_select "title", {:count=>1, :text=>"Welcome"},
|
||||
# "Wrong title or more than one title element"
|
||||
#
|
||||
# # Page contains no forms
|
||||
# assert_select "form", false, "This page must contain no forms"
|
||||
#
|
||||
# # Test the content and style
|
||||
# assert_select "body div.header ul.menu"
|
||||
#
|
||||
# # Use substitution values
|
||||
# assert_select "ol>li#?", /item-\d+/
|
||||
#
|
||||
# # All input fields in the form have a name
|
||||
# assert_select "form input" do
|
||||
# assert_select "[name=?]", /.+/ # Not empty
|
||||
# end
|
||||
def assert_select(*args, &block)
|
||||
# Start with optional element followed by mandatory selector.
|
||||
arg = args.shift
|
||||
|
||||
if arg.is_a?(HTML::Node)
|
||||
# First argument is a node (tag or text, but also HTML root),
|
||||
# so we know what we're selecting from.
|
||||
root = arg
|
||||
arg = args.shift
|
||||
elsif arg == nil
|
||||
# This usually happens when passing a node/element that
|
||||
# happens to be nil.
|
||||
raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
|
||||
elsif @selected
|
||||
root = HTML::Node.new(nil)
|
||||
root.children.concat @selected
|
||||
else
|
||||
# Otherwise just operate on the response document.
|
||||
root = response_from_page_or_rjs
|
||||
end
|
||||
|
||||
# First or second argument is the selector: string and we pass
|
||||
# all remaining arguments. Array and we pass the argument. Also
|
||||
# accepts selector itself.
|
||||
case arg
|
||||
when String
|
||||
selector = HTML::Selector.new(arg, args)
|
||||
when Array
|
||||
selector = HTML::Selector.new(*arg)
|
||||
when HTML::Selector
|
||||
selector = arg
|
||||
else raise ArgumentError, "Expecting a selector as the first argument"
|
||||
end
|
||||
|
||||
# Next argument is used for equality tests.
|
||||
equals = {}
|
||||
case arg = args.shift
|
||||
when Hash
|
||||
equals = arg
|
||||
when String, Regexp
|
||||
equals[:text] = arg
|
||||
when Integer
|
||||
equals[:count] = arg
|
||||
when Range
|
||||
equals[:minimum] = arg.begin
|
||||
equals[:maximum] = arg.end
|
||||
when FalseClass
|
||||
equals[:count] = 0
|
||||
when NilClass, TrueClass
|
||||
equals[:minimum] = 1
|
||||
else raise ArgumentError, "I don't understand what you're trying to match"
|
||||
end
|
||||
|
||||
# By default we're looking for at least one match.
|
||||
if equals[:count]
|
||||
equals[:minimum] = equals[:maximum] = equals[:count]
|
||||
else
|
||||
equals[:minimum] = 1 unless equals[:minimum]
|
||||
end
|
||||
|
||||
# Last argument is the message we use if the assertion fails.
|
||||
message = args.shift
|
||||
#- message = "No match made with selector #{selector.inspect}" unless message
|
||||
if args.shift
|
||||
raise ArgumentError, "Not expecting that last argument, you either have too many arguments, or they're the wrong type"
|
||||
end
|
||||
|
||||
matches = selector.select(root)
|
||||
# If text/html, narrow down to those elements that match it.
|
||||
content_mismatch = nil
|
||||
if match_with = equals[:text]
|
||||
matches.delete_if do |match|
|
||||
text = ""
|
||||
stack = match.children.reverse
|
||||
while node = stack.pop
|
||||
if node.tag?
|
||||
stack.concat node.children.reverse
|
||||
else
|
||||
text << node.content
|
||||
end
|
||||
end
|
||||
text.strip! unless NO_STRIP.include?(match.name)
|
||||
unless match_with.is_a?(Regexp) ? (text =~ match_with) : (text == match_with.to_s)
|
||||
content_mismatch ||= build_message(message, "<?> expected but was\n<?>.", match_with, text)
|
||||
true
|
||||
end
|
||||
end
|
||||
elsif match_with = equals[:html]
|
||||
matches.delete_if do |match|
|
||||
html = match.children.map(&:to_s).join
|
||||
html.strip! unless NO_STRIP.include?(match.name)
|
||||
unless match_with.is_a?(Regexp) ? (html =~ match_with) : (html == match_with.to_s)
|
||||
content_mismatch ||= build_message(message, "<?> expected but was\n<?>.", match_with, html)
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
# Expecting foo found bar element only if found zero, not if
|
||||
# found one but expecting two.
|
||||
message ||= content_mismatch if matches.empty?
|
||||
# Test minimum/maximum occurrence.
|
||||
if equals[:minimum]
|
||||
assert matches.size >= equals[:minimum], message ||
|
||||
"Expected at least #{equals[:minimum]} elements, found #{matches.size}."
|
||||
end
|
||||
if equals[:maximum]
|
||||
assert matches.size <= equals[:maximum], message ||
|
||||
"Expected at most #{equals[:maximum]} elements, found #{matches.size}."
|
||||
end
|
||||
|
||||
# If a block is given call that block. Set @selected to allow
|
||||
# nested assert_select, which can be nested several levels deep.
|
||||
if block_given? && !matches.empty?
|
||||
begin
|
||||
in_scope, @selected = @selected, matches
|
||||
yield matches
|
||||
ensure
|
||||
@selected = in_scope
|
||||
end
|
||||
end
|
||||
|
||||
# Returns all matches elements.
|
||||
matches
|
||||
end
|
||||
|
||||
# :call-seq:
|
||||
# assert_select_rjs(id?) { |elements| ... }
|
||||
# assert_select_rjs(statement, id?) { |elements| ... }
|
||||
# assert_select_rjs(:insert, position, id?) { |elements| ... }
|
||||
#
|
||||
# Selects content from the RJS response.
|
||||
#
|
||||
# === Narrowing down
|
||||
#
|
||||
# With no arguments, asserts that one or more elements are updated or
|
||||
# inserted by RJS statements.
|
||||
#
|
||||
# Use the +id+ argument to narrow down the assertion to only statements
|
||||
# that update or insert an element with that identifier.
|
||||
#
|
||||
# Use the first argument to narrow down assertions to only statements
|
||||
# of that type. Possible values are +:replace+, +:replace_html+ and
|
||||
# +:insert_html+.
|
||||
#
|
||||
# Use the argument +:insert+ followed by an insertion position to narrow
|
||||
# down the assertion to only statements that insert elements in that
|
||||
# position. Possible values are +:top+, +:bottom+, +:before+ and +:after+.
|
||||
#
|
||||
# === Using blocks
|
||||
#
|
||||
# Without a block, #assert_select_rjs merely asserts that the response
|
||||
# contains one or more RJS statements that replace or update content.
|
||||
#
|
||||
# With a block, #assert_select_rjs also selects all elements used in
|
||||
# these statements and passes them to the block. Nested assertions are
|
||||
# supported.
|
||||
#
|
||||
# Calling #assert_select_rjs with no arguments and using nested asserts
|
||||
# asserts that the HTML content is returned by one or more RJS statements.
|
||||
# Using #assert_select directly makes the same assertion on the content,
|
||||
# but without distinguishing whether the content is returned in an HTML
|
||||
# or JavaScript.
|
||||
#
|
||||
# === Examples
|
||||
#
|
||||
# # Replacing the element foo.
|
||||
# # page.replace 'foo', ...
|
||||
# assert_select_rjs :replace, "foo"
|
||||
#
|
||||
# # Replacing with the chained RJS proxy.
|
||||
# # page[:foo].replace ...
|
||||
# assert_select_rjs :chained_replace, 'foo'
|
||||
#
|
||||
# # Inserting into the element bar, top position.
|
||||
# assert_select_rjs :insert, :top, "bar"
|
||||
#
|
||||
# # Changing the element foo, with an image.
|
||||
# assert_select_rjs "foo" do
|
||||
# assert_select "img[src=/images/logo.gif""
|
||||
# end
|
||||
#
|
||||
# # RJS inserts or updates a list with four items.
|
||||
# assert_select_rjs do
|
||||
# assert_select "ol>li", 4
|
||||
# end
|
||||
#
|
||||
# # The same, but shorter.
|
||||
# assert_select "ol>li", 4
|
||||
def assert_select_rjs(*args, &block)
|
||||
rjs_type = nil
|
||||
arg = args.shift
|
||||
|
||||
# If the first argument is a symbol, it's the type of RJS statement we're looking
|
||||
# for (update, replace, insertion, etc). Otherwise, we're looking for just about
|
||||
# any RJS statement.
|
||||
if arg.is_a?(Symbol)
|
||||
rjs_type = arg
|
||||
if rjs_type == :insert
|
||||
arg = args.shift
|
||||
insertion = "insert_#{arg}".to_sym
|
||||
raise ArgumentError, "Unknown RJS insertion type #{arg}" unless RJS_STATEMENTS[insertion]
|
||||
statement = "(#{RJS_STATEMENTS[insertion]})"
|
||||
else
|
||||
raise ArgumentError, "Unknown RJS statement type #{rjs_type}" unless RJS_STATEMENTS[rjs_type]
|
||||
statement = "(#{RJS_STATEMENTS[rjs_type]})"
|
||||
end
|
||||
arg = args.shift
|
||||
else
|
||||
statement = "#{RJS_STATEMENTS[:any]}"
|
||||
end
|
||||
|
||||
# Next argument we're looking for is the element identifier. If missing, we pick
|
||||
# any element.
|
||||
if arg.is_a?(String)
|
||||
id = Regexp.quote(arg)
|
||||
arg = args.shift
|
||||
else
|
||||
id = "[^\"]*"
|
||||
end
|
||||
|
||||
pattern =
|
||||
case rjs_type
|
||||
when :chained_replace, :chained_replace_html
|
||||
Regexp.new("\\$\\(\"#{id}\"\\)#{statement}\\(#{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE)
|
||||
else
|
||||
Regexp.new("#{statement}\\(\"#{id}\", #{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE)
|
||||
end
|
||||
|
||||
# Duplicate the body since the next step involves destroying it.
|
||||
matches = nil
|
||||
@response.body.gsub(pattern) do |match|
|
||||
html = unescape_rjs($2)
|
||||
matches ||= []
|
||||
matches.concat HTML::Document.new(html).root.children.select { |n| n.tag? }
|
||||
""
|
||||
end
|
||||
if matches
|
||||
if block_given?
|
||||
begin
|
||||
in_scope, @selected = @selected, matches
|
||||
yield matches
|
||||
ensure
|
||||
@selected = in_scope
|
||||
end
|
||||
end
|
||||
matches
|
||||
else
|
||||
# RJS statement not found.
|
||||
flunk args.shift || "No RJS statement that replaces or inserts HTML content."
|
||||
end
|
||||
end
|
||||
|
||||
# :call-seq:
|
||||
# assert_select_encoded(element?) { |elements| ... }
|
||||
#
|
||||
# Extracts the content of an element, treats it as encoded HTML and runs
|
||||
# nested assertion on it.
|
||||
#
|
||||
# You typically call this method within another assertion to operate on
|
||||
# all currently selected elements. You can also pass an element or array
|
||||
# of elements.
|
||||
#
|
||||
# The content of each element is un-encoded, and wrapped in the root
|
||||
# element +encoded+. It then calls the block with all un-encoded elements.
|
||||
#
|
||||
# === Example
|
||||
#
|
||||
# assert_select_feed :rss, 2.0 do
|
||||
# # Select description element of each feed item.
|
||||
# assert_select "channel>item>description" do
|
||||
# # Run assertions on the encoded elements.
|
||||
# assert_select_encoded do
|
||||
# assert_select "p"
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
def assert_select_encoded(element = nil, &block)
|
||||
case element
|
||||
when Array
|
||||
elements = element
|
||||
when HTML::Node
|
||||
elements = [element]
|
||||
when nil
|
||||
unless elements = @selected
|
||||
raise ArgumentError, "First argument is optional, but must be called from a nested assert_select"
|
||||
end
|
||||
else
|
||||
raise ArgumentError, "Argument is optional, and may be node or array of nodes"
|
||||
end
|
||||
|
||||
fix_content = lambda do |node|
|
||||
# Gets around a bug in the Rails 1.1 HTML parser.
|
||||
node.content.gsub(/<!\[CDATA\[(.*)(\]\]>)?/m) { CGI.escapeHTML($1) }
|
||||
end
|
||||
|
||||
selected = elements.map do |element|
|
||||
text = element.children.select{ |c| not c.tag? }.map{ |c| fix_content[c] }.join
|
||||
root = HTML::Document.new(CGI.unescapeHTML("<encoded>#{text}</encoded>")).root
|
||||
css_select(root, "encoded:root", &block)[0]
|
||||
end
|
||||
|
||||
begin
|
||||
old_selected, @selected = @selected, selected
|
||||
assert_select ":root", &block
|
||||
ensure
|
||||
@selected = old_selected
|
||||
end
|
||||
end
|
||||
|
||||
# :call-seq:
|
||||
# assert_select_email { }
|
||||
#
|
||||
# Extracts the body of an email and runs nested assertions on it.
|
||||
#
|
||||
# You must enable deliveries for this assertion to work, use:
|
||||
# ActionMailer::Base.perform_deliveries = true
|
||||
#
|
||||
# === Example
|
||||
#
|
||||
# assert_select_email do
|
||||
# assert_select "h1", "Email alert"
|
||||
# end
|
||||
def assert_select_email(&block)
|
||||
deliveries = ActionMailer::Base.deliveries
|
||||
assert !deliveries.empty?, "No e-mail in delivery list"
|
||||
|
||||
for delivery in deliveries
|
||||
for part in delivery.parts
|
||||
if part["Content-Type"].to_s =~ /^text\/html\W/
|
||||
root = HTML::Document.new(part.body).root
|
||||
assert_select root, ":root", &block
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
unless const_defined?(:RJS_STATEMENTS)
|
||||
RJS_STATEMENTS = {
|
||||
:replace => /Element\.replace/,
|
||||
:replace_html => /Element\.update/,
|
||||
:chained_replace => /\.replace/,
|
||||
:chained_replace_html => /\.update/,
|
||||
}
|
||||
RJS_INSERTIONS = [:top, :bottom, :before, :after]
|
||||
RJS_INSERTIONS.each do |insertion|
|
||||
RJS_STATEMENTS["insert_#{insertion}".to_sym] = Regexp.new(Regexp.quote("new Insertion.#{insertion.to_s.camelize}"))
|
||||
end
|
||||
RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})")
|
||||
RJS_STATEMENTS[:insert_html] = Regexp.new(RJS_INSERTIONS.collect do |insertion|
|
||||
Regexp.quote("new Insertion.#{insertion.to_s.camelize}")
|
||||
end.join('|'))
|
||||
RJS_PATTERN_HTML = /"((\\"|[^"])*)"/
|
||||
RJS_PATTERN_EVERYTHING = Regexp.new("#{RJS_STATEMENTS[:any]}\\(\"([^\"]*)\", #{RJS_PATTERN_HTML}\\)",
|
||||
Regexp::MULTILINE)
|
||||
RJS_PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/
|
||||
end
|
||||
|
||||
# #assert_select and #css_select call this to obtain the content in the HTML
|
||||
# page, or from all the RJS statements, depending on the type of response.
|
||||
def response_from_page_or_rjs()
|
||||
content_type = @response.headers["Content-Type"]
|
||||
if content_type && content_type =~ /text\/javascript/
|
||||
body = @response.body.dup
|
||||
root = HTML::Node.new(nil)
|
||||
while true
|
||||
next if body.sub!(RJS_PATTERN_EVERYTHING) do |match|
|
||||
html = unescape_rjs($3)
|
||||
matches = HTML::Document.new(html).root.children.select { |n| n.tag? }
|
||||
root.children.concat matches
|
||||
""
|
||||
end
|
||||
break
|
||||
end
|
||||
root
|
||||
else
|
||||
html_document.root
|
||||
end
|
||||
end
|
||||
|
||||
# Unescapes a RJS string.
|
||||
def unescape_rjs(rjs_string)
|
||||
# RJS encodes double quotes and line breaks.
|
||||
unescaped= rjs_string.gsub('\"', '"')
|
||||
unescaped.gsub!('\n', "\n")
|
||||
# RJS encodes non-ascii characters.
|
||||
unescaped.gsub!(RJS_PATTERN_UNICODE_ESCAPED_CHAR) {|u| [$1.hex].pack('U*')}
|
||||
unescaped
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
117
vendor/rails/actionpack/lib/action_controller/assertions/tag_assertions.rb
vendored
Normal file
117
vendor/rails/actionpack/lib/action_controller/assertions/tag_assertions.rb
vendored
Normal file
|
@ -0,0 +1,117 @@
|
|||
require 'rexml/document'
|
||||
require File.dirname(__FILE__) + "/../vendor/html-scanner/html/document"
|
||||
|
||||
module ActionController
|
||||
module Assertions
|
||||
module TagAssertions
|
||||
# Asserts that there is a tag/node/element in the body of the response
|
||||
# that meets all of the given conditions. The +conditions+ parameter must
|
||||
# be a hash of any of the following keys (all are optional):
|
||||
#
|
||||
# * <tt>:tag</tt>: the node type must match the corresponding value
|
||||
# * <tt>:attributes</tt>: a hash. The node's attributes must match the
|
||||
# corresponding values in the hash.
|
||||
# * <tt>:parent</tt>: a hash. The node's parent must match the
|
||||
# corresponding hash.
|
||||
# * <tt>:child</tt>: a hash. At least one of the node's immediate children
|
||||
# must meet the criteria described by the hash.
|
||||
# * <tt>:ancestor</tt>: a hash. At least one of the node's ancestors must
|
||||
# meet the criteria described by the hash.
|
||||
# * <tt>:descendant</tt>: a hash. At least one of the node's descendants
|
||||
# must meet the criteria described by the hash.
|
||||
# * <tt>:sibling</tt>: a hash. At least one of the node's siblings must
|
||||
# meet the criteria described by the hash.
|
||||
# * <tt>:after</tt>: a hash. The node must be after any sibling meeting
|
||||
# the criteria described by the hash, and at least one sibling must match.
|
||||
# * <tt>:before</tt>: a hash. The node must be before any sibling meeting
|
||||
# the criteria described by the hash, and at least one sibling must match.
|
||||
# * <tt>:children</tt>: a hash, for counting children of a node. Accepts
|
||||
# the keys:
|
||||
# * <tt>:count</tt>: either a number or a range which must equal (or
|
||||
# include) the number of children that match.
|
||||
# * <tt>:less_than</tt>: the number of matching children must be less
|
||||
# than this number.
|
||||
# * <tt>:greater_than</tt>: the number of matching children must be
|
||||
# greater than this number.
|
||||
# * <tt>:only</tt>: another hash consisting of the keys to use
|
||||
# to match on the children, and only matching children will be
|
||||
# counted.
|
||||
# * <tt>:content</tt>: the textual content of the node must match the
|
||||
# given value. This will not match HTML tags in the body of a
|
||||
# tag--only text.
|
||||
#
|
||||
# Conditions are matched using the following algorithm:
|
||||
#
|
||||
# * if the condition is a string, it must be a substring of the value.
|
||||
# * if the condition is a regexp, it must match the value.
|
||||
# * if the condition is a number, the value must match number.to_s.
|
||||
# * if the condition is +true+, the value must not be +nil+.
|
||||
# * if the condition is +false+ or +nil+, the value must be +nil+.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# # assert that there is a "span" tag
|
||||
# assert_tag :tag => "span"
|
||||
#
|
||||
# # assert that there is a "span" tag with id="x"
|
||||
# assert_tag :tag => "span", :attributes => { :id => "x" }
|
||||
#
|
||||
# # assert that there is a "span" tag using the short-hand
|
||||
# assert_tag :span
|
||||
#
|
||||
# # assert that there is a "span" tag with id="x" using the short-hand
|
||||
# assert_tag :span, :attributes => { :id => "x" }
|
||||
#
|
||||
# # assert that there is a "span" inside of a "div"
|
||||
# assert_tag :tag => "span", :parent => { :tag => "div" }
|
||||
#
|
||||
# # assert that there is a "span" somewhere inside a table
|
||||
# assert_tag :tag => "span", :ancestor => { :tag => "table" }
|
||||
#
|
||||
# # assert that there is a "span" with at least one "em" child
|
||||
# assert_tag :tag => "span", :child => { :tag => "em" }
|
||||
#
|
||||
# # assert that there is a "span" containing a (possibly nested)
|
||||
# # "strong" tag.
|
||||
# assert_tag :tag => "span", :descendant => { :tag => "strong" }
|
||||
#
|
||||
# # assert that there is a "span" containing between 2 and 4 "em" tags
|
||||
# # as immediate children
|
||||
# assert_tag :tag => "span",
|
||||
# :children => { :count => 2..4, :only => { :tag => "em" } }
|
||||
#
|
||||
# # get funky: assert that there is a "div", with an "ul" ancestor
|
||||
# # and an "li" parent (with "class" = "enum"), and containing a
|
||||
# # "span" descendant that contains text matching /hello world/
|
||||
# assert_tag :tag => "div",
|
||||
# :ancestor => { :tag => "ul" },
|
||||
# :parent => { :tag => "li",
|
||||
# :attributes => { :class => "enum" } },
|
||||
# :descendant => { :tag => "span",
|
||||
# :child => /hello world/ }
|
||||
#
|
||||
# <strong>Please note</strong: #assert_tag and #assert_no_tag only work
|
||||
# with well-formed XHTML. They recognize a few tags as implicitly self-closing
|
||||
# (like br and hr and such) but will not work correctly with tags
|
||||
# that allow optional closing tags (p, li, td). <em>You must explicitly
|
||||
# close all of your tags to use these assertions.</em>
|
||||
def assert_tag(*opts)
|
||||
clean_backtrace do
|
||||
opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
|
||||
tag = find_tag(opts)
|
||||
assert tag, "expected tag, but no tag found matching #{opts.inspect} in:\n#{@response.body.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
# Identical to #assert_tag, but asserts that a matching tag does _not_
|
||||
# exist. (See #assert_tag for a full discussion of the syntax.)
|
||||
def assert_no_tag(*opts)
|
||||
clean_backtrace do
|
||||
opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
|
||||
tag = find_tag(opts)
|
||||
assert !tag, "expected no tag, but found tag matching #{opts.inspect} in:\n#{@response.body.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
65
vendor/rails/actionpack/lib/action_controller/deprecated_dependencies.rb
vendored
Normal file
65
vendor/rails/actionpack/lib/action_controller/deprecated_dependencies.rb
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
module ActionController #:nodoc:
|
||||
module Dependencies #:nodoc:
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
# Deprecated module. The responsibility of loading dependencies belong with Active Support now.
|
||||
module ClassMethods #:nodoc:
|
||||
# Specifies a variable number of models that this controller depends on. Models are normally Active Record classes or a similar
|
||||
# backend for modelling entity classes.
|
||||
def model(*models)
|
||||
require_dependencies(:model, models)
|
||||
depend_on(:model, models)
|
||||
end
|
||||
deprecate :model
|
||||
|
||||
# Specifies a variable number of services that this controller depends on. Services are normally singletons or factories, like
|
||||
# Action Mailer service or a Payment Gateway service.
|
||||
def service(*services)
|
||||
require_dependencies(:service, services)
|
||||
depend_on(:service, services)
|
||||
end
|
||||
deprecate :service
|
||||
|
||||
# Specifies a variable number of observers that are to govern when this controller is handling actions. The observers will
|
||||
# automatically have .instance called on them to make them active on assignment.
|
||||
def observer(*observers)
|
||||
require_dependencies(:observer, observers)
|
||||
depend_on(:observer, observers)
|
||||
instantiate_observers(observers)
|
||||
end
|
||||
deprecate :observer
|
||||
|
||||
# Returns an array of symbols that specify the dependencies on a given layer. For the example at the top, calling
|
||||
# <tt>ApplicationController.dependencies_on(:model)</tt> would return <tt>[:account, :company, :person, :project, :category]</tt>
|
||||
def dependencies_on(layer)
|
||||
read_inheritable_attribute("#{layer}_dependencies")
|
||||
end
|
||||
deprecate :dependencies_on
|
||||
|
||||
def depend_on(layer, dependencies) #:nodoc:
|
||||
write_inheritable_array("#{layer}_dependencies", dependencies)
|
||||
end
|
||||
deprecate :depend_on
|
||||
|
||||
private
|
||||
def instantiate_observers(observers)
|
||||
observers.flatten.each { |observer| Object.const_get(Inflector.classify(observer.to_s)).instance }
|
||||
end
|
||||
|
||||
def require_dependencies(layer, dependencies)
|
||||
dependencies.flatten.each do |dependency|
|
||||
begin
|
||||
require_dependency(dependency.to_s)
|
||||
rescue LoadError => e
|
||||
raise LoadError.new("Missing #{layer} #{dependency}.rb").copy_blame!(e)
|
||||
rescue Exception => exception # error from loaded file
|
||||
exception.blame_file! "=> #{layer} #{dependency}.rb"
|
||||
raise
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
405
vendor/rails/actionpack/lib/action_controller/resources.rb
vendored
Normal file
405
vendor/rails/actionpack/lib/action_controller/resources.rb
vendored
Normal file
|
@ -0,0 +1,405 @@
|
|||
module ActionController
|
||||
module Resources
|
||||
class Resource #:nodoc:
|
||||
attr_reader :collection_methods, :member_methods, :new_methods
|
||||
attr_reader :path_prefix, :name_prefix
|
||||
attr_reader :plural, :singular
|
||||
attr_reader :options
|
||||
|
||||
def initialize(entities, options)
|
||||
@plural = entities
|
||||
@singular = options[:singular] || plural.to_s.singularize
|
||||
|
||||
@options = options
|
||||
|
||||
arrange_actions
|
||||
add_default_actions
|
||||
set_prefixes
|
||||
end
|
||||
|
||||
def controller
|
||||
@controller ||= (options[:controller] || plural).to_s
|
||||
end
|
||||
|
||||
def path
|
||||
@path ||= "#{path_prefix}/#{plural}"
|
||||
end
|
||||
|
||||
def new_path
|
||||
@new_path ||= "#{path}/new"
|
||||
end
|
||||
|
||||
def member_path
|
||||
@member_path ||= "#{path}/:id"
|
||||
end
|
||||
|
||||
def nesting_path_prefix
|
||||
@nesting_path_prefix ||= "#{path}/:#{singular}_id"
|
||||
end
|
||||
|
||||
protected
|
||||
def arrange_actions
|
||||
@collection_methods = arrange_actions_by_methods(options.delete(:collection))
|
||||
@member_methods = arrange_actions_by_methods(options.delete(:member))
|
||||
@new_methods = arrange_actions_by_methods(options.delete(:new))
|
||||
end
|
||||
|
||||
def add_default_actions
|
||||
add_default_action(member_methods, :get, :edit)
|
||||
add_default_action(new_methods, :get, :new)
|
||||
end
|
||||
|
||||
def set_prefixes
|
||||
@path_prefix = options.delete(:path_prefix)
|
||||
@name_prefix = options.delete(:name_prefix)
|
||||
end
|
||||
|
||||
def arrange_actions_by_methods(actions)
|
||||
(actions || {}).inject({}) do |flipped_hash, (key, value)|
|
||||
(flipped_hash[value] ||= []) << key
|
||||
flipped_hash
|
||||
end
|
||||
end
|
||||
|
||||
def add_default_action(collection, method, action)
|
||||
(collection[method] ||= []).unshift(action)
|
||||
end
|
||||
end
|
||||
|
||||
class SingletonResource < Resource #:nodoc:
|
||||
def initialize(entity, options)
|
||||
@plural = @singular = entity
|
||||
@options = options
|
||||
arrange_actions
|
||||
add_default_actions
|
||||
set_prefixes
|
||||
end
|
||||
|
||||
alias_method :member_path, :path
|
||||
alias_method :nesting_path_prefix, :path
|
||||
end
|
||||
|
||||
# Creates named routes for implementing verb-oriented controllers. This is
|
||||
# useful for implementing REST API's, where a single resource has different
|
||||
# behavior based on the HTTP verb (method) used to access it.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# map.resources :messages
|
||||
#
|
||||
# class MessagesController < ActionController::Base
|
||||
# # GET messages_url
|
||||
# def index
|
||||
# # return all messages
|
||||
# end
|
||||
#
|
||||
# # GET new_message_url
|
||||
# def new
|
||||
# # return an HTML form for describing a new message
|
||||
# end
|
||||
#
|
||||
# # POST messages_url
|
||||
# def create
|
||||
# # create a new message
|
||||
# end
|
||||
#
|
||||
# # GET message_url(:id => 1)
|
||||
# def show
|
||||
# # find and return a specific message
|
||||
# end
|
||||
#
|
||||
# # GET edit_message_url(:id => 1)
|
||||
# def edit
|
||||
# # return an HTML form for editing a specific message
|
||||
# end
|
||||
#
|
||||
# # PUT message_url(:id => 1)
|
||||
# def update
|
||||
# # find and update a specific message
|
||||
# end
|
||||
#
|
||||
# # DELETE message_url(:id => 1)
|
||||
# def destroy
|
||||
# # delete a specific message
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# The #resources method sets HTTP method restrictions on the routes it generates. For example, making an
|
||||
# HTTP POST on <tt>new_message_url</tt> will raise a RoutingError exception. The default route in
|
||||
# <tt>config/routes.rb</tt> overrides this and allows invalid HTTP methods for resource routes.
|
||||
#
|
||||
# Along with the routes themselves, #resources generates named routes for use in
|
||||
# controllers and views. <tt>map.resources :messages</tt> produces the following named routes and helpers:
|
||||
#
|
||||
# Named Route Helpers
|
||||
# messages messages_url, hash_for_messages_url,
|
||||
# messages_path, hash_for_messages_path
|
||||
# message message_url(id), hash_for_message_url(id),
|
||||
# message_path(id), hash_for_message_path(id)
|
||||
# new_message new_message_url, hash_for_new_message_url,
|
||||
# new_message_path, hash_for_new_message_path
|
||||
# edit_message edit_message_url(id), hash_for_edit_message_url(id),
|
||||
# edit_message_path(id), hash_for_edit_message_path(id)
|
||||
#
|
||||
# You can use these helpers instead of #url_for or methods that take #url_for parameters:
|
||||
#
|
||||
# redirect_to :controller => 'messages', :action => 'index'
|
||||
# # becomes
|
||||
# redirect_to messages_url
|
||||
#
|
||||
# <%= link_to "edit this message", :controller => 'messages', :action => 'edit', :id => @message.id %>
|
||||
# # becomes
|
||||
# <%= link_to "edit this message", edit_message_url(@message) # calls @message.id automatically
|
||||
#
|
||||
# Since web browsers don't support the PUT and DELETE verbs, you will need to add a parameter '_method' to your
|
||||
# form tags. The form helpers make this a little easier. For an update form with a <tt>@message</tt> object:
|
||||
#
|
||||
# <%= form_tag message_path(@message), :method => :put %>
|
||||
#
|
||||
# or
|
||||
#
|
||||
# <% form_for :message, @message, :url => message_path(@message), :html => {:method => :put} do |f| %>
|
||||
#
|
||||
# The #resources method accepts various options, too, to customize the resulting
|
||||
# routes:
|
||||
# * <tt>:controller</tt> -- specify the controller name for the routes.
|
||||
# * <tt>:singular</tt> -- specify the singular name used in the member routes.
|
||||
# * <tt>:path_prefix</tt> -- set a prefix to the routes with required route variables.
|
||||
# Weblog comments usually belong to a post, so you might use resources like:
|
||||
#
|
||||
# map.resources :articles
|
||||
# map.resources :comments, :path_prefix => '/articles/:article_id'
|
||||
#
|
||||
# You can nest resources calls to set this automatically:
|
||||
#
|
||||
# map.resources :articles do |article|
|
||||
# article.resources :comments
|
||||
# end
|
||||
#
|
||||
# The comment resources work the same, but must now include a value for :article_id.
|
||||
#
|
||||
# comments_url(@article)
|
||||
# comment_url(@article, @comment)
|
||||
#
|
||||
# comments_url(:article_id => @article)
|
||||
# comment_url(:article_id => @article, :id => @comment)
|
||||
#
|
||||
# * <tt>:name_prefix</tt> -- define a prefix for all generated routes, usually ending in an underscore.
|
||||
# Use this if you have named routes that may clash.
|
||||
#
|
||||
# map.resources :tags, :path_prefix => '/books/:book_id', :name_prefix => 'book_'
|
||||
# map.resources :tags, :path_prefix => '/toys/:toy_id', :name_prefix => 'toy_'
|
||||
#
|
||||
# * <tt>:collection</tt> -- add named routes for other actions that operate on the collection.
|
||||
# Takes a hash of <tt>#{action} => #{method}</tt>, where method is <tt>:get</tt>/<tt>:post</tt>/<tt>:put</tt>/<tt>:delete</tt>
|
||||
# or <tt>:any</tt> if the method does not matter. These routes map to a URL like /messages;rss, with a route of rss_messages_url.
|
||||
# * <tt>:member</tt> -- same as :collection, but for actions that operate on a specific member.
|
||||
# * <tt>:new</tt> -- same as :collection, but for actions that operate on the new resource action.
|
||||
#
|
||||
# If <tt>map.resources</tt> is called with multiple resources, they all get the same options applied.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# map.resources :messages, :path_prefix => "/thread/:thread_id"
|
||||
# # --> GET /thread/7/messages/1
|
||||
#
|
||||
# map.resources :messages, :collection => { :rss => :get }
|
||||
# # --> GET /messages;rss (maps to the #rss action)
|
||||
# # also adds a named route called "rss_messages"
|
||||
#
|
||||
# map.resources :messages, :member => { :mark => :post }
|
||||
# # --> POST /messages/1;mark (maps to the #mark action)
|
||||
# # also adds a named route called "mark_message"
|
||||
#
|
||||
# map.resources :messages, :new => { :preview => :post }
|
||||
# # --> POST /messages/new;preview (maps to the #preview action)
|
||||
# # also adds a named route called "preview_new_message"
|
||||
#
|
||||
# map.resources :messages, :new => { :new => :any, :preview => :post }
|
||||
# # --> POST /messages/new;preview (maps to the #preview action)
|
||||
# # also adds a named route called "preview_new_message"
|
||||
# # --> /messages/new can be invoked via any request method
|
||||
#
|
||||
# map.resources :messages, :controller => "categories",
|
||||
# :path_prefix => "/category/:category_id",
|
||||
# :name_prefix => "category_"
|
||||
# # --> GET /categories/7/messages/1
|
||||
# # has named route "category_message"
|
||||
def resources(*entities, &block)
|
||||
options = entities.last.is_a?(Hash) ? entities.pop : { }
|
||||
entities.each { |entity| map_resource entity, options.dup, &block }
|
||||
end
|
||||
|
||||
# Creates named routes for implementing verb-oriented controllers for a singleton resource.
|
||||
# A singleton resource is global to the current user visiting the application, such as a user's
|
||||
# /account profile.
|
||||
#
|
||||
# See map.resources for general conventions. These are the main differences:
|
||||
# - a singular name is given to map.resource. The default controller name is taken from the singular name.
|
||||
# - To specify a custom plural name, use the :plural option. There is no :singular option
|
||||
# - No default index, new, or create routes are created for the singleton resource controller.
|
||||
# - When nesting singleton resources, only the singular name is used as the path prefix (example: 'account/messages/1')
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# map.resource :account
|
||||
#
|
||||
# class AccountController < ActionController::Base
|
||||
# # POST account_url
|
||||
# def create
|
||||
# # create an account
|
||||
# end
|
||||
#
|
||||
# # GET new_account_url
|
||||
# def new
|
||||
# # return an HTML form for describing the new account
|
||||
# end
|
||||
#
|
||||
# # GET account_url
|
||||
# def show
|
||||
# # find and return the account
|
||||
# end
|
||||
#
|
||||
# # GET edit_account_url
|
||||
# def edit
|
||||
# # return an HTML form for editing the account
|
||||
# end
|
||||
#
|
||||
# # PUT account_url
|
||||
# def update
|
||||
# # find and update the account
|
||||
# end
|
||||
#
|
||||
# # DELETE account_url
|
||||
# def destroy
|
||||
# # delete the account
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Along with the routes themselves, #resource generates named routes for use in
|
||||
# controllers and views. <tt>map.resource :account</tt> produces the following named routes and helpers:
|
||||
#
|
||||
# Named Route Helpers
|
||||
# account account_url, hash_for_account_url,
|
||||
# account_path, hash_for_account_path
|
||||
# edit_account edit_account_url, hash_for_edit_account_url,
|
||||
# edit_account_path, hash_for_edit_account_path
|
||||
def resource(*entities, &block)
|
||||
options = entities.last.is_a?(Hash) ? entities.pop : { }
|
||||
entities.each { |entity| map_singleton_resource entity, options.dup, &block }
|
||||
end
|
||||
|
||||
private
|
||||
def map_resource(entities, options = {}, &block)
|
||||
resource = Resource.new(entities, options)
|
||||
|
||||
with_options :controller => resource.controller do |map|
|
||||
map_collection_actions(map, resource)
|
||||
map_default_collection_actions(map, resource)
|
||||
map_new_actions(map, resource)
|
||||
map_member_actions(map, resource)
|
||||
|
||||
if block_given?
|
||||
with_options(:path_prefix => resource.nesting_path_prefix, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def map_singleton_resource(entities, options = {}, &block)
|
||||
resource = SingletonResource.new(entities, options)
|
||||
|
||||
with_options :controller => resource.controller do |map|
|
||||
map_collection_actions(map, resource)
|
||||
map_default_singleton_actions(map, resource)
|
||||
map_new_actions(map, resource)
|
||||
map_member_actions(map, resource)
|
||||
|
||||
if block_given?
|
||||
with_options(:path_prefix => resource.nesting_path_prefix, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def map_collection_actions(map, resource)
|
||||
resource.collection_methods.each do |method, actions|
|
||||
actions.each do |action|
|
||||
action_options = action_options_for(action, resource, method)
|
||||
map.named_route("#{resource.name_prefix}#{action}_#{resource.plural}", "#{resource.path};#{action}", action_options)
|
||||
map.named_route("formatted_#{resource.name_prefix}#{action}_#{resource.plural}", "#{resource.path}.:format;#{action}", action_options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def map_default_collection_actions(map, resource)
|
||||
index_action_options = action_options_for("index", resource)
|
||||
map.named_route("#{resource.name_prefix}#{resource.plural}", resource.path, index_action_options)
|
||||
map.named_route("formatted_#{resource.name_prefix}#{resource.plural}", "#{resource.path}.:format", index_action_options)
|
||||
|
||||
create_action_options = action_options_for("create", resource)
|
||||
map.connect(resource.path, create_action_options)
|
||||
map.connect("#{resource.path}.:format", create_action_options)
|
||||
end
|
||||
|
||||
def map_default_singleton_actions(map, resource)
|
||||
create_action_options = action_options_for("create", resource)
|
||||
map.connect(resource.path, create_action_options)
|
||||
map.connect("#{resource.path}.:format", create_action_options)
|
||||
end
|
||||
|
||||
def map_new_actions(map, resource)
|
||||
resource.new_methods.each do |method, actions|
|
||||
actions.each do |action|
|
||||
action_options = action_options_for(action, resource, method)
|
||||
if action == :new
|
||||
map.named_route("#{resource.name_prefix}new_#{resource.singular}", resource.new_path, action_options)
|
||||
map.named_route("formatted_#{resource.name_prefix}new_#{resource.singular}", "#{resource.new_path}.:format", action_options)
|
||||
else
|
||||
map.named_route("#{resource.name_prefix}#{action}_new_#{resource.singular}", "#{resource.new_path};#{action}", action_options)
|
||||
map.named_route("formatted_#{resource.name_prefix}#{action}_new_#{resource.singular}", "#{resource.new_path}.:format;#{action}", action_options)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def map_member_actions(map, resource)
|
||||
resource.member_methods.each do |method, actions|
|
||||
actions.each do |action|
|
||||
action_options = action_options_for(action, resource, method)
|
||||
map.named_route("#{resource.name_prefix}#{action}_#{resource.singular}", "#{resource.member_path};#{action}", action_options)
|
||||
map.named_route("formatted_#{resource.name_prefix}#{action}_#{resource.singular}", "#{resource.member_path}.:format;#{action}",action_options)
|
||||
end
|
||||
end
|
||||
|
||||
show_action_options = action_options_for("show", resource)
|
||||
map.named_route("#{resource.name_prefix}#{resource.singular}", resource.member_path, show_action_options)
|
||||
map.named_route("formatted_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}.:format", show_action_options)
|
||||
|
||||
update_action_options = action_options_for("update", resource)
|
||||
map.connect(resource.member_path, update_action_options)
|
||||
map.connect("#{resource.member_path}.:format", update_action_options)
|
||||
|
||||
destroy_action_options = action_options_for("destroy", resource)
|
||||
map.connect(resource.member_path, destroy_action_options)
|
||||
map.connect("#{resource.member_path}.:format", destroy_action_options)
|
||||
end
|
||||
|
||||
def conditions_for(method)
|
||||
{ :conditions => method == :any ? {} : { :method => method } }
|
||||
end
|
||||
|
||||
def action_options_for(action, resource, method = nil)
|
||||
default_options = { :action => action.to_s }
|
||||
require_id = resource.kind_of?(SingletonResource) ? {} : { :requirements => { :id => Regexp.new("[^#{Routing::SEPARATORS.join}]+") } }
|
||||
case default_options[:action]
|
||||
when "index", "new" : default_options.merge(conditions_for(method || :get))
|
||||
when "create" : default_options.merge(conditions_for(method || :post))
|
||||
when "show", "edit" : default_options.merge(conditions_for(method || :get)).merge(require_id)
|
||||
when "update" : default_options.merge(conditions_for(method || :put)).merge(require_id)
|
||||
when "destroy" : default_options.merge(conditions_for(method || :delete)).merge(require_id)
|
||||
else default_options.merge(conditions_for(method))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ActionController::Routing::RouteSet::Mapper.send :include, ActionController::Resources
|
88
vendor/rails/actionpack/lib/action_controller/status_codes.rb
vendored
Normal file
88
vendor/rails/actionpack/lib/action_controller/status_codes.rb
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
module ActionController
|
||||
module StatusCodes #:nodoc:
|
||||
# Defines the standard HTTP status codes, by integer, with their
|
||||
# corresponding default message texts.
|
||||
# Source: http://www.iana.org/assignments/http-status-codes
|
||||
STATUS_CODES = {
|
||||
100 => "Continue",
|
||||
101 => "Switching Protocols",
|
||||
102 => "Processing",
|
||||
|
||||
200 => "OK",
|
||||
201 => "Created",
|
||||
202 => "Accepted",
|
||||
203 => "Non-Authoritative Information",
|
||||
204 => "No Content",
|
||||
205 => "Reset Content",
|
||||
206 => "Partial Content",
|
||||
207 => "Multi-Status",
|
||||
226 => "IM Used",
|
||||
|
||||
300 => "Multiple Choices",
|
||||
301 => "Moved Permanently",
|
||||
302 => "Found",
|
||||
303 => "See Other",
|
||||
304 => "Not Modified",
|
||||
305 => "Use Proxy",
|
||||
307 => "Temporary Redirect",
|
||||
|
||||
400 => "Bad Request",
|
||||
401 => "Unauthorized",
|
||||
402 => "Payment Required",
|
||||
403 => "Forbidden",
|
||||
404 => "Not Found",
|
||||
405 => "Method Not Allowed",
|
||||
406 => "Not Acceptable",
|
||||
407 => "Proxy Authentication Required",
|
||||
408 => "Request Timeout",
|
||||
409 => "Conflict",
|
||||
410 => "Gone",
|
||||
411 => "Length Required",
|
||||
412 => "Precondition Failed",
|
||||
413 => "Request Entity Too Large",
|
||||
414 => "Request-URI Too Long",
|
||||
415 => "Unsupported Media Type",
|
||||
416 => "Requested Range Not Satisfiable",
|
||||
417 => "Expectation Failed",
|
||||
422 => "Unprocessable Entity",
|
||||
423 => "Locked",
|
||||
424 => "Failed Dependency",
|
||||
426 => "Upgrade Required",
|
||||
|
||||
500 => "Internal Server Error",
|
||||
501 => "Not Implemented",
|
||||
502 => "Bad Gateway",
|
||||
503 => "Service Unavailable",
|
||||
504 => "Gateway Timeout",
|
||||
505 => "HTTP Version Not Supported",
|
||||
507 => "Insufficient Storage",
|
||||
510 => "Not Extended"
|
||||
}
|
||||
|
||||
# Provides a symbol-to-fixnum lookup for converting a symbol (like
|
||||
# :created or :not_implemented) into its corresponding HTTP status
|
||||
# code (like 200 or 501).
|
||||
SYMBOL_TO_STATUS_CODE = STATUS_CODES.inject({}) do |hash, (code, message)|
|
||||
hash[message.gsub(/ /, "").underscore.to_sym] = code
|
||||
hash
|
||||
end
|
||||
|
||||
# Given a status parameter, determine whether it needs to be converted
|
||||
# to a string. If it is a fixnum, use the STATUS_CODES hash to lookup
|
||||
# the default message. If it is a symbol, use the SYMBOL_TO_STATUS_CODE
|
||||
# hash to convert it.
|
||||
def interpret_status(status)
|
||||
case status
|
||||
when Fixnum then
|
||||
"#{status} #{STATUS_CODES[status]}".strip
|
||||
when Symbol then
|
||||
interpret_status(SYMBOL_TO_STATUS_CODE[status] ||
|
||||
"500 Unknown Status #{status.inspect}")
|
||||
else
|
||||
status.to_s
|
||||
end
|
||||
end
|
||||
private :interpret_status
|
||||
|
||||
end
|
||||
end
|
823
vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
vendored
Normal file
823
vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
vendored
Normal file
|
@ -0,0 +1,823 @@
|
|||
#--
|
||||
# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
|
||||
# Under MIT and/or CC By license.
|
||||
#++
|
||||
|
||||
module HTML
|
||||
|
||||
# Selects HTML elements using CSS 2 selectors.
|
||||
#
|
||||
# The +Selector+ class uses CSS selector expressions to match and select
|
||||
# HTML elements.
|
||||
#
|
||||
# For example:
|
||||
# selector = HTML::Selector.new "form.login[action=/login]"
|
||||
# creates a new selector that matches any +form+ element with the class
|
||||
# +login+ and an attribute +action+ with the value <tt>/login</tt>.
|
||||
#
|
||||
# === Matching Elements
|
||||
#
|
||||
# Use the #match method to determine if an element matches the selector.
|
||||
#
|
||||
# For simple selectors, the method returns an array with that element,
|
||||
# or +nil+ if the element does not match. For complex selectors (see below)
|
||||
# the method returns an array with all matched elements, of +nil+ if no
|
||||
# match found.
|
||||
#
|
||||
# For example:
|
||||
# if selector.match(element)
|
||||
# puts "Element is a login form"
|
||||
# end
|
||||
#
|
||||
# === Selecting Elements
|
||||
#
|
||||
# Use the #select method to select all matching elements starting with
|
||||
# one element and going through all children in depth-first order.
|
||||
#
|
||||
# This method returns an array of all matching elements, an empty array
|
||||
# if no match is found
|
||||
#
|
||||
# For example:
|
||||
# selector = HTML::Selector.new "input[type=text]"
|
||||
# matches = selector.select(element)
|
||||
# matches.each do |match|
|
||||
# puts "Found text field with name #{match.attributes['name']}"
|
||||
# end
|
||||
#
|
||||
# === Expressions
|
||||
#
|
||||
# Selectors can match elements using any of the following criteria:
|
||||
# * <tt>name</tt> -- Match an element based on its name (tag name).
|
||||
# For example, <tt>p</tt> to match a paragraph. You can use <tt>*</tt>
|
||||
# to match any element.
|
||||
# * <tt>#</tt><tt>id</tt> -- Match an element based on its identifier (the
|
||||
# <tt>id</tt> attribute). For example, <tt>#</tt><tt>page</tt>.
|
||||
# * <tt>.class</tt> -- Match an element based on its class name, all
|
||||
# class names if more than one specified.
|
||||
# * <tt>[attr]</tt> -- Match an element that has the specified attribute.
|
||||
# * <tt>[attr=value]</tt> -- Match an element that has the specified
|
||||
# attribute and value. (More operators are supported see below)
|
||||
# * <tt>:pseudo-class</tt> -- Match an element based on a pseudo class,
|
||||
# such as <tt>:nth-child</tt> and <tt>:empty</tt>.
|
||||
# * <tt>:not(expr)</tt> -- Match an element that does not match the
|
||||
# negation expression.
|
||||
#
|
||||
# When using a combination of the above, the element name comes first
|
||||
# followed by identifier, class names, attributes, pseudo classes and
|
||||
# negation in any order. Do not seprate these parts with spaces!
|
||||
# Space separation is used for descendant selectors.
|
||||
#
|
||||
# For example:
|
||||
# selector = HTML::Selector.new "form.login[action=/login]"
|
||||
# The matched element must be of type +form+ and have the class +login+.
|
||||
# It may have other classes, but the class +login+ is required to match.
|
||||
# It must also have an attribute called +action+ with the value
|
||||
# <tt>/login</tt>.
|
||||
#
|
||||
# This selector will match the following element:
|
||||
# <form class="login form" method="post" action="/login">
|
||||
# but will not match the element:
|
||||
# <form method="post" action="/logout">
|
||||
#
|
||||
# === Attribute Values
|
||||
#
|
||||
# Several operators are supported for matching attributes:
|
||||
# * <tt>name</tt> -- The element must have an attribute with that name.
|
||||
# * <tt>name=value</tt> -- The element must have an attribute with that
|
||||
# name and value.
|
||||
# * <tt>name^=value</tt> -- The attribute value must start with the
|
||||
# specified value.
|
||||
# * <tt>name$=value</tt> -- The attribute value must end with the
|
||||
# specified value.
|
||||
# * <tt>name*=value</tt> -- The attribute value must contain the
|
||||
# specified value.
|
||||
# * <tt>name~=word</tt> -- The attribute value must contain the specified
|
||||
# word (space separated).
|
||||
# * <tt>name|=word</tt> -- The attribute value must start with specified
|
||||
# word.
|
||||
#
|
||||
# For example, the following two selectors match the same element:
|
||||
# #my_id
|
||||
# [id=my_id]
|
||||
# and so do the following two selectors:
|
||||
# .my_class
|
||||
# [class~=my_class]
|
||||
#
|
||||
# === Alternatives, siblings, children
|
||||
#
|
||||
# Complex selectors use a combination of expressions to match elements:
|
||||
# * <tt>expr1 expr2</tt> -- Match any element against the second expression
|
||||
# if it has some parent element that matches the first expression.
|
||||
# * <tt>expr1 > expr2</tt> -- Match any element against the second expression
|
||||
# if it is the child of an element that matches the first expression.
|
||||
# * <tt>expr1 + expr2</tt> -- Match any element against the second expression
|
||||
# if it immediately follows an element that matches the first expression.
|
||||
# * <tt>expr1 ~ expr2</tt> -- Match any element against the second expression
|
||||
# that comes after an element that matches the first expression.
|
||||
# * <tt>expr1, expr2</tt> -- Match any element against the first expression,
|
||||
# or against the second expression.
|
||||
#
|
||||
# Since children and sibling selectors may match more than one element given
|
||||
# the first element, the #match method may return more than one match.
|
||||
#
|
||||
# === Pseudo classes
|
||||
#
|
||||
# Pseudo classes were introduced in CSS 3. They are most often used to select
|
||||
# elements in a given position:
|
||||
# * <tt>:root</tt> -- Match the element only if it is the root element
|
||||
# (no parent element).
|
||||
# * <tt>:empty</tt> -- Match the element only if it has no child elements,
|
||||
# and no text content.
|
||||
# * <tt>:only-child</tt> -- Match the element if it is the only child (element)
|
||||
# of its parent element.
|
||||
# * <tt>:only-of-type</tt> -- Match the element if it is the only child (element)
|
||||
# of its parent element and its type.
|
||||
# * <tt>:first-child</tt> -- Match the element if it is the first child (element)
|
||||
# of its parent element.
|
||||
# * <tt>:first-of-type</tt> -- Match the element if it is the first child (element)
|
||||
# of its parent element of its type.
|
||||
# * <tt>:last-child</tt> -- Match the element if it is the last child (element)
|
||||
# of its parent element.
|
||||
# * <tt>:last-of-type</tt> -- Match the element if it is the last child (element)
|
||||
# of its parent element of its type.
|
||||
# * <tt>:nth-child(b)</tt> -- Match the element if it is the b-th child (element)
|
||||
# of its parent element. The value <tt>b</tt> specifies its index, starting with 1.
|
||||
# * <tt>:nth-child(an+b)</tt> -- Match the element if it is the b-th child (element)
|
||||
# in each group of <tt>a</tt> child elements of its parent element.
|
||||
# * <tt>:nth-child(-an+b)</tt> -- Match the element if it is the first child (element)
|
||||
# in each group of <tt>a</tt> child elements, up to the first <tt>b</tt> child
|
||||
# elements of its parent element.
|
||||
# * <tt>:nth-child(odd)</tt> -- Match element in the odd position (i.e. first, third).
|
||||
# Same as <tt>:nth-child(2n+1)</tt>.
|
||||
# * <tt>:nth-child(even)</tt> -- Match element in the even position (i.e. second,
|
||||
# fourth). Same as <tt>:nth-child(2n+2)</tt>.
|
||||
# * <tt>:nth-of-type(..)</tt> -- As above, but only counts elements of its type.
|
||||
# * <tt>:nth-last-child(..)</tt> -- As above, but counts from the last child.
|
||||
# * <tt>:nth-last-of-type(..)</tt> -- As above, but counts from the last child and
|
||||
# only elements of its type.
|
||||
# * <tt>:not(selector)</tt> -- Match the element only if the element does not
|
||||
# match the simple selector.
|
||||
#
|
||||
# As you can see, <tt>:nth-child<tt> pseudo class and its varient can get quite
|
||||
# tricky and the CSS specification doesn't do a much better job explaining it.
|
||||
# But after reading the examples and trying a few combinations, it's easy to
|
||||
# figure out.
|
||||
#
|
||||
# For example:
|
||||
# table tr:nth-child(odd)
|
||||
# Selects every second row in the table starting with the first one.
|
||||
#
|
||||
# div p:nth-child(4)
|
||||
# Selects the fourth paragraph in the +div+, but not if the +div+ contains
|
||||
# other elements, since those are also counted.
|
||||
#
|
||||
# div p:nth-of-type(4)
|
||||
# Selects the fourth paragraph in the +div+, counting only paragraphs, and
|
||||
# ignoring all other elements.
|
||||
#
|
||||
# div p:nth-of-type(-n+4)
|
||||
# Selects the first four paragraphs, ignoring all others.
|
||||
#
|
||||
# And you can always select an element that matches one set of rules but
|
||||
# not another using <tt>:not</tt>. For example:
|
||||
# p:not(.post)
|
||||
# Matches all paragraphs that do not have the class <tt>.post</tt>.
|
||||
#
|
||||
# === Substitution Values
|
||||
#
|
||||
# You can use substitution with identifiers, class names and element values.
|
||||
# A substitution takes the form of a question mark (<tt>?</tt>) and uses the
|
||||
# next value in the argument list following the CSS expression.
|
||||
#
|
||||
# The substitution value may be a string or a regular expression. All other
|
||||
# values are converted to strings.
|
||||
#
|
||||
# For example:
|
||||
# selector = HTML::Selector.new "#?", /^\d+$/
|
||||
# matches any element whose identifier consists of one or more digits.
|
||||
#
|
||||
# See http://www.w3.org/TR/css3-selectors/
|
||||
class Selector
|
||||
|
||||
|
||||
# An invalid selector.
|
||||
class InvalidSelectorError < StandardError #:nodoc:
|
||||
end
|
||||
|
||||
|
||||
class << self
|
||||
|
||||
# :call-seq:
|
||||
# Selector.for_class(cls) => selector
|
||||
#
|
||||
# Creates a new selector for the given class name.
|
||||
def for_class(cls)
|
||||
self.new([".?", cls])
|
||||
end
|
||||
|
||||
|
||||
# :call-seq:
|
||||
# Selector.for_id(id) => selector
|
||||
#
|
||||
# Creates a new selector for the given id.
|
||||
def for_id(id)
|
||||
self.new(["#?", id])
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
# :call-seq:
|
||||
# Selector.new(string, [values ...]) => selector
|
||||
#
|
||||
# Creates a new selector from a CSS 2 selector expression.
|
||||
#
|
||||
# The first argument is the selector expression. All other arguments
|
||||
# are used for value substitution.
|
||||
#
|
||||
# Throws InvalidSelectorError is the selector expression is invalid.
|
||||
def initialize(selector, *values)
|
||||
raise ArgumentError, "CSS expression cannot be empty" if selector.empty?
|
||||
@source = ""
|
||||
values = values[0] if values.size == 1 && values[0].is_a?(Array)
|
||||
# We need a copy to determine if we failed to parse, and also
|
||||
# preserve the original pass by-ref statement.
|
||||
statement = selector.strip.dup
|
||||
# Create a simple selector, along with negation.
|
||||
simple_selector(statement, values).each { |name, value| instance_variable_set("@#{name}", value) }
|
||||
|
||||
# Alternative selector.
|
||||
if statement.sub!(/^\s*,\s*/, "")
|
||||
second = Selector.new(statement, values)
|
||||
(@alternates ||= []) << second
|
||||
# If there are alternate selectors, we group them in the top selector.
|
||||
if alternates = second.instance_variable_get(:@alternates)
|
||||
second.instance_variable_set(:@alternates, nil)
|
||||
@alternates.concat alternates
|
||||
end
|
||||
@source << " , " << second.to_s
|
||||
# Sibling selector: create a dependency into second selector that will
|
||||
# match element immediately following this one.
|
||||
elsif statement.sub!(/^\s*\+\s*/, "")
|
||||
second = next_selector(statement, values)
|
||||
@depends = lambda do |element, first|
|
||||
if element = next_element(element)
|
||||
second.match(element, first)
|
||||
end
|
||||
end
|
||||
@source << " + " << second.to_s
|
||||
# Adjacent selector: create a dependency into second selector that will
|
||||
# match all elements following this one.
|
||||
elsif statement.sub!(/^\s*~\s*/, "")
|
||||
second = next_selector(statement, values)
|
||||
@depends = lambda do |element, first|
|
||||
matches = []
|
||||
while element = next_element(element)
|
||||
if subset = second.match(element, first)
|
||||
if first && !subset.empty?
|
||||
matches << subset.first
|
||||
break
|
||||
else
|
||||
matches.concat subset
|
||||
end
|
||||
end
|
||||
end
|
||||
matches.empty? ? nil : matches
|
||||
end
|
||||
@source << " ~ " << second.to_s
|
||||
# Child selector: create a dependency into second selector that will
|
||||
# match a child element of this one.
|
||||
elsif statement.sub!(/^\s*>\s*/, "")
|
||||
second = next_selector(statement, values)
|
||||
@depends = lambda do |element, first|
|
||||
matches = []
|
||||
element.children.each do |child|
|
||||
if child.tag? && subset = second.match(child, first)
|
||||
if first && !subset.empty?
|
||||
matches << subset.first
|
||||
break
|
||||
else
|
||||
matches.concat subset
|
||||
end
|
||||
end
|
||||
end
|
||||
matches.empty? ? nil : matches
|
||||
end
|
||||
@source << " > " << second.to_s
|
||||
# Descendant selector: create a dependency into second selector that
|
||||
# will match all descendant elements of this one. Note,
|
||||
elsif statement =~ /^\s+\S+/ && statement != selector
|
||||
second = next_selector(statement, values)
|
||||
@depends = lambda do |element, first|
|
||||
matches = []
|
||||
stack = element.children.reverse
|
||||
while node = stack.pop
|
||||
next unless node.tag?
|
||||
if subset = second.match(node, first)
|
||||
if first && !subset.empty?
|
||||
matches << subset.first
|
||||
break
|
||||
else
|
||||
matches.concat subset
|
||||
end
|
||||
elsif children = node.children
|
||||
stack.concat children.reverse
|
||||
end
|
||||
end
|
||||
matches.empty? ? nil : matches
|
||||
end
|
||||
@source << " " << second.to_s
|
||||
else
|
||||
# The last selector is where we check that we parsed
|
||||
# all the parts.
|
||||
unless statement.empty? || statement.strip.empty?
|
||||
raise ArgumentError, "Invalid selector: #{statement}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# :call-seq:
|
||||
# match(element, first?) => array or nil
|
||||
#
|
||||
# Matches an element against the selector.
|
||||
#
|
||||
# For a simple selector this method returns an array with the
|
||||
# element if the element matches, nil otherwise.
|
||||
#
|
||||
# For a complex selector (sibling and descendant) this method
|
||||
# returns an array with all matching elements, nil if no match is
|
||||
# found.
|
||||
#
|
||||
# Use +first_only=true+ if you are only interested in the first element.
|
||||
#
|
||||
# For example:
|
||||
# if selector.match(element)
|
||||
# puts "Element is a login form"
|
||||
# end
|
||||
def match(element, first_only = false)
|
||||
# Match element if no element name or element name same as element name
|
||||
if matched = (!@tag_name || @tag_name == element.name)
|
||||
# No match if one of the attribute matches failed
|
||||
for attr in @attributes
|
||||
if element.attributes[attr[0]] !~ attr[1]
|
||||
matched = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Pseudo class matches (nth-child, empty, etc).
|
||||
if matched
|
||||
for pseudo in @pseudo
|
||||
unless pseudo.call(element)
|
||||
matched = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Negation. Same rules as above, but we fail if a match is made.
|
||||
if matched && @negation
|
||||
for negation in @negation
|
||||
if negation[:tag_name] == element.name
|
||||
matched = false
|
||||
else
|
||||
for attr in negation[:attributes]
|
||||
if element.attributes[attr[0]] =~ attr[1]
|
||||
matched = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if matched
|
||||
for pseudo in negation[:pseudo]
|
||||
if pseudo.call(element)
|
||||
matched = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
break unless matched
|
||||
end
|
||||
end
|
||||
|
||||
# If element matched but depends on another element (child,
|
||||
# sibling, etc), apply the dependent matches instead.
|
||||
if matched && @depends
|
||||
matches = @depends.call(element, first_only)
|
||||
else
|
||||
matches = matched ? [element] : nil
|
||||
end
|
||||
|
||||
# If this selector is part of the group, try all the alternative
|
||||
# selectors (unless first_only).
|
||||
if @alternates && (!first_only || !matches)
|
||||
@alternates.each do |alternate|
|
||||
break if matches && first_only
|
||||
if subset = alternate.match(element, first_only)
|
||||
if matches
|
||||
matches.concat subset
|
||||
else
|
||||
matches = subset
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
matches
|
||||
end
|
||||
|
||||
|
||||
# :call-seq:
|
||||
# select(root) => array
|
||||
#
|
||||
# Selects and returns an array with all matching elements, beginning
|
||||
# with one node and traversing through all children depth-first.
|
||||
# Returns an empty array if no match is found.
|
||||
#
|
||||
# The root node may be any element in the document, or the document
|
||||
# itself.
|
||||
#
|
||||
# For example:
|
||||
# selector = HTML::Selector.new "input[type=text]"
|
||||
# matches = selector.select(element)
|
||||
# matches.each do |match|
|
||||
# puts "Found text field with name #{match.attributes['name']}"
|
||||
# end
|
||||
def select(root)
|
||||
matches = []
|
||||
stack = [root]
|
||||
while node = stack.pop
|
||||
if node.tag? && subset = match(node, false)
|
||||
subset.each do |match|
|
||||
matches << match unless matches.any? { |item| item.equal?(match) }
|
||||
end
|
||||
elsif children = node.children
|
||||
stack.concat children.reverse
|
||||
end
|
||||
end
|
||||
matches
|
||||
end
|
||||
|
||||
|
||||
# Similar to #select but returns the first matching element. Returns +nil+
|
||||
# if no element matches the selector.
|
||||
def select_first(root)
|
||||
stack = [root]
|
||||
while node = stack.pop
|
||||
if node.tag? && subset = match(node, true)
|
||||
return subset.first if !subset.empty?
|
||||
elsif children = node.children
|
||||
stack.concat children.reverse
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
|
||||
def to_s #:nodoc:
|
||||
@source
|
||||
end
|
||||
|
||||
|
||||
# Return the next element after this one. Skips sibling text nodes.
|
||||
#
|
||||
# With the +name+ argument, returns the next element with that name,
|
||||
# skipping other sibling elements.
|
||||
def next_element(element, name = nil)
|
||||
if siblings = element.parent.children
|
||||
found = false
|
||||
siblings.each do |node|
|
||||
if node.equal?(element)
|
||||
found = true
|
||||
elsif found && node.tag?
|
||||
return node if (name.nil? || node.name == name)
|
||||
end
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
|
||||
|
||||
# Creates a simple selector given the statement and array of
|
||||
# substitution values.
|
||||
#
|
||||
# Returns a hash with the values +tag_name+, +attributes+,
|
||||
# +pseudo+ (classes) and +negation+.
|
||||
#
|
||||
# Called the first time with +can_negate+ true to allow
|
||||
# negation. Called a second time with false since negation
|
||||
# cannot be negated.
|
||||
def simple_selector(statement, values, can_negate = true)
|
||||
tag_name = nil
|
||||
attributes = []
|
||||
pseudo = []
|
||||
negation = []
|
||||
|
||||
# Element name. (Note that in negation, this can come at
|
||||
# any order, but for simplicity we allow if only first).
|
||||
statement.sub!(/^(\*|[[:alpha:]][\w\-]*)/) do |match|
|
||||
match.strip!
|
||||
tag_name = match.downcase unless match == "*"
|
||||
@source << match
|
||||
"" # Remove
|
||||
end
|
||||
|
||||
# Get identifier, class, attribute name, pseudo or negation.
|
||||
while true
|
||||
# Element identifier.
|
||||
next if statement.sub!(/^#(\?|[\w\-]+)/) do |match|
|
||||
id = $1
|
||||
if id == "?"
|
||||
id = values.shift
|
||||
end
|
||||
@source << "##{id}"
|
||||
id = Regexp.new("^#{Regexp.escape(id.to_s)}$") unless id.is_a?(Regexp)
|
||||
attributes << ["id", id]
|
||||
"" # Remove
|
||||
end
|
||||
|
||||
# Class name.
|
||||
next if statement.sub!(/^\.([\w\-]+)/) do |match|
|
||||
class_name = $1
|
||||
@source << ".#{class_name}"
|
||||
class_name = Regexp.new("(^|\s)#{Regexp.escape(class_name)}($|\s)") unless class_name.is_a?(Regexp)
|
||||
attributes << ["class", class_name]
|
||||
"" # Remove
|
||||
end
|
||||
|
||||
# Attribute value.
|
||||
next if statement.sub!(/^\[\s*([[:alpha:]][\w\-]*)\s*((?:[~|^$*])?=)?\s*('[^']*'|"[^*]"|[^\]]*)\s*\]/) do |match|
|
||||
name, equality, value = $1, $2, $3
|
||||
if value == "?"
|
||||
value = values.shift
|
||||
else
|
||||
# Handle single and double quotes.
|
||||
value.strip!
|
||||
if (value[0] == ?" || value[0] == ?') && value[0] == value[-1]
|
||||
value = value[1..-2]
|
||||
end
|
||||
end
|
||||
@source << "[#{name}#{equality}'#{value}']"
|
||||
attributes << [name.downcase.strip, attribute_match(equality, value)]
|
||||
"" # Remove
|
||||
end
|
||||
|
||||
# Root element only.
|
||||
next if statement.sub!(/^:root/) do |match|
|
||||
pseudo << lambda do |element|
|
||||
element.parent.nil? || !element.parent.tag?
|
||||
end
|
||||
@source << ":root"
|
||||
"" # Remove
|
||||
end
|
||||
|
||||
# Nth-child including last and of-type.
|
||||
next if statement.sub!(/^:nth-(last-)?(child|of-type)\((odd|even|(\d+|\?)|(-?\d*|\?)?n([+\-]\d+|\?)?)\)/) do |match|
|
||||
reverse = $1 == "last-"
|
||||
of_type = $2 == "of-type"
|
||||
@source << ":nth-#{$1}#{$2}("
|
||||
case $3
|
||||
when "odd"
|
||||
pseudo << nth_child(2, 1, of_type, reverse)
|
||||
@source << "odd)"
|
||||
when "even"
|
||||
pseudo << nth_child(2, 2, of_type, reverse)
|
||||
@source << "even)"
|
||||
when /^(\d+|\?)$/ # b only
|
||||
b = ($1 == "?" ? values.shift : $1).to_i
|
||||
pseudo << nth_child(0, b, of_type, reverse)
|
||||
@source << "#{b})"
|
||||
when /^(-?\d*|\?)?n([+\-]\d+|\?)?$/
|
||||
a = ($1 == "?" ? values.shift :
|
||||
$1 == "" ? 1 : $1 == "-" ? -1 : $1).to_i
|
||||
b = ($2 == "?" ? values.shift : $2).to_i
|
||||
pseudo << nth_child(a, b, of_type, reverse)
|
||||
@source << (b >= 0 ? "#{a}n+#{b})" : "#{a}n#{b})")
|
||||
else
|
||||
raise ArgumentError, "Invalid nth-child #{match}"
|
||||
end
|
||||
"" # Remove
|
||||
end
|
||||
# First/last child (of type).
|
||||
next if statement.sub!(/^:(first|last)-(child|of-type)/) do |match|
|
||||
reverse = $1 == "last"
|
||||
of_type = $2 == "of-type"
|
||||
pseudo << nth_child(0, 1, of_type, reverse)
|
||||
@source << ":#{$1}-#{$2}"
|
||||
"" # Remove
|
||||
end
|
||||
# Only child (of type).
|
||||
next if statement.sub!(/^:only-(child|of-type)/) do |match|
|
||||
of_type = $1 == "of-type"
|
||||
pseudo << only_child(of_type)
|
||||
@source << ":only-#{$1}"
|
||||
"" # Remove
|
||||
end
|
||||
|
||||
# Empty: no child elements or meaningful content (whitespaces
|
||||
# are ignored).
|
||||
next if statement.sub!(/^:empty/) do |match|
|
||||
pseudo << lambda do |element|
|
||||
empty = true
|
||||
for child in element.children
|
||||
if child.tag? || !child.content.strip.empty?
|
||||
empty = false
|
||||
break
|
||||
end
|
||||
end
|
||||
empty
|
||||
end
|
||||
@source << ":empty"
|
||||
"" # Remove
|
||||
end
|
||||
# Content: match the text content of the element, stripping
|
||||
# leading and trailing spaces.
|
||||
next if statement.sub!(/^:content\(\s*(\?|'[^']*'|"[^"]*"|[^)]*)\s*\)/) do |match|
|
||||
content = $1
|
||||
if content == "?"
|
||||
content = values.shift
|
||||
elsif (content[0] == ?" || content[0] == ?') && content[0] == content[-1]
|
||||
content = content[1..-2]
|
||||
end
|
||||
@source << ":content('#{content}')"
|
||||
content = Regexp.new("^#{Regexp.escape(content.to_s)}$") unless content.is_a?(Regexp)
|
||||
pseudo << lambda do |element|
|
||||
text = ""
|
||||
for child in element.children
|
||||
unless child.tag?
|
||||
text << child.content
|
||||
end
|
||||
end
|
||||
text.strip =~ content
|
||||
end
|
||||
"" # Remove
|
||||
end
|
||||
|
||||
# Negation. Create another simple selector to handle it.
|
||||
if statement.sub!(/^:not\(\s*/, "")
|
||||
raise ArgumentError, "Double negatives are not missing feature" unless can_negate
|
||||
@source << ":not("
|
||||
negation << simple_selector(statement, values, false)
|
||||
raise ArgumentError, "Negation not closed" unless statement.sub!(/^\s*\)/, "")
|
||||
@source << ")"
|
||||
next
|
||||
end
|
||||
|
||||
# No match: moving on.
|
||||
break
|
||||
end
|
||||
|
||||
# Return hash. The keys are mapped to instance variables.
|
||||
{:tag_name=>tag_name, :attributes=>attributes, :pseudo=>pseudo, :negation=>negation}
|
||||
end
|
||||
|
||||
|
||||
# Create a regular expression to match an attribute value based
|
||||
# on the equality operator (=, ^=, |=, etc).
|
||||
def attribute_match(equality, value)
|
||||
regexp = value.is_a?(Regexp) ? value : Regexp.escape(value.to_s)
|
||||
case equality
|
||||
when "=" then
|
||||
# Match the attribute value in full
|
||||
Regexp.new("^#{regexp}$")
|
||||
when "~=" then
|
||||
# Match a space-separated word within the attribute value
|
||||
Regexp.new("(^|\s)#{regexp}($|\s)")
|
||||
when "^="
|
||||
# Match the beginning of the attribute value
|
||||
Regexp.new("^#{regexp}")
|
||||
when "$="
|
||||
# Match the end of the attribute value
|
||||
Regexp.new("#{regexp}$")
|
||||
when "*="
|
||||
# Match substring of the attribute value
|
||||
regexp.is_a?(Regexp) ? regexp : Regexp.new(regexp)
|
||||
when "|=" then
|
||||
# Match the first space-separated item of the attribute value
|
||||
Regexp.new("^#{regexp}($|\s)")
|
||||
else
|
||||
raise InvalidSelectorError, "Invalid operation/value" unless value.empty?
|
||||
# Match all attributes values (existence check)
|
||||
//
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Returns a lambda that can match an element against the nth-child
|
||||
# pseudo class, given the following arguments:
|
||||
# * +a+ -- Value of a part.
|
||||
# * +b+ -- Value of b part.
|
||||
# * +of_type+ -- True to test only elements of this type (of-type).
|
||||
# * +reverse+ -- True to count in reverse order (last-).
|
||||
def nth_child(a, b, of_type, reverse)
|
||||
# a = 0 means select at index b, if b = 0 nothing selected
|
||||
return lambda { |element| false } if a == 0 && b == 0
|
||||
# a < 0 and b < 0 will never match against an index
|
||||
return lambda { |element| false } if a < 0 && b < 0
|
||||
b = a + b + 1 if b < 0 # b < 0 just picks last element from each group
|
||||
b -= 1 unless b == 0 # b == 0 is same as b == 1, otherwise zero based
|
||||
lambda do |element|
|
||||
# Element must be inside parent element.
|
||||
return false unless element.parent && element.parent.tag?
|
||||
index = 0
|
||||
# Get siblings, reverse if counting from last.
|
||||
siblings = element.parent.children
|
||||
siblings = siblings.reverse if reverse
|
||||
# Match element name if of-type, otherwise ignore name.
|
||||
name = of_type ? element.name : nil
|
||||
found = false
|
||||
for child in siblings
|
||||
# Skip text nodes/comments.
|
||||
if child.tag? && (name == nil || child.name == name)
|
||||
if a == 0
|
||||
# Shortcut when a == 0 no need to go past count
|
||||
if index == b
|
||||
found = child.equal?(element)
|
||||
break
|
||||
end
|
||||
elsif a < 0
|
||||
# Only look for first b elements
|
||||
break if index > b
|
||||
if child.equal?(element)
|
||||
found = (index % a) == 0
|
||||
break
|
||||
end
|
||||
else
|
||||
# Otherwise, break if child found and count == an+b
|
||||
if child.equal?(element)
|
||||
found = (index % a) == b
|
||||
break
|
||||
end
|
||||
end
|
||||
index += 1
|
||||
end
|
||||
end
|
||||
found
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Creates a only child lambda. Pass +of-type+ to only look at
|
||||
# elements of its type.
|
||||
def only_child(of_type)
|
||||
lambda do |element|
|
||||
# Element must be inside parent element.
|
||||
return false unless element.parent && element.parent.tag?
|
||||
name = of_type ? element.name : nil
|
||||
other = false
|
||||
for child in element.parent.children
|
||||
# Skip text nodes/comments.
|
||||
if child.tag? && (name == nil || child.name == name)
|
||||
unless child.equal?(element)
|
||||
other = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
!other
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Called to create a dependent selector (sibling, descendant, etc).
|
||||
# Passes the remainder of the statement that will be reduced to zero
|
||||
# eventually, and array of substitution values.
|
||||
#
|
||||
# This method is called from four places, so it helps to put it here
|
||||
# for resue. The only logic deals with the need to detect comma
|
||||
# separators (alternate) and apply them to the selector group of the
|
||||
# top selector.
|
||||
def next_selector(statement, values)
|
||||
second = Selector.new(statement, values)
|
||||
# If there are alternate selectors, we group them in the top selector.
|
||||
if alternates = second.instance_variable_get(:@alternates)
|
||||
second.instance_variable_set(:@alternates, nil)
|
||||
(@alternates ||= []).concat alternates
|
||||
end
|
||||
second
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
# See HTML::Selector.new
|
||||
def self.selector(statement, *values)
|
||||
Selector.new(statement, *values)
|
||||
end
|
||||
|
||||
|
||||
class Tag
|
||||
|
||||
def select(selector, *values)
|
||||
selector = HTML::Selector.new(selector, values)
|
||||
selector.select(self)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
34
vendor/rails/actionpack/lib/action_view/helpers/deprecated_helper.rb
vendored
Normal file
34
vendor/rails/actionpack/lib/action_view/helpers/deprecated_helper.rb
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
module ActionView
|
||||
module Helpers
|
||||
module PrototypeHelper
|
||||
|
||||
def update_element_function(element_id, options = {}, &block)
|
||||
content = escape_javascript(options[:content] || '')
|
||||
content = escape_javascript(capture(&block)) if block
|
||||
|
||||
javascript_function = case (options[:action] || :update)
|
||||
when :update
|
||||
if options[:position]
|
||||
"new Insertion.#{options[:position].to_s.camelize}('#{element_id}','#{content}')"
|
||||
else
|
||||
"$('#{element_id}').innerHTML = '#{content}'"
|
||||
end
|
||||
|
||||
when :empty
|
||||
"$('#{element_id}').innerHTML = ''"
|
||||
|
||||
when :remove
|
||||
"Element.remove('#{element_id}')"
|
||||
|
||||
else
|
||||
raise ArgumentError, "Invalid action, choose one of :update, :remove, :empty"
|
||||
end
|
||||
|
||||
javascript_function << ";\n"
|
||||
options[:binding] ? concat(javascript_function, options[:binding]) : javascript_function
|
||||
end
|
||||
deprecate :update_element_function => "use RJS instead"
|
||||
|
||||
end
|
||||
end
|
||||
end
|
576
vendor/rails/actionpack/test/controller/assert_select_test.rb
vendored
Normal file
576
vendor/rails/actionpack/test/controller/assert_select_test.rb
vendored
Normal file
|
@ -0,0 +1,576 @@
|
|||
#--
|
||||
# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
|
||||
# Under MIT and/or CC By license.
|
||||
#++
|
||||
|
||||
require File.dirname(__FILE__) + '/../abstract_unit'
|
||||
require File.dirname(__FILE__) + '/fake_controllers'
|
||||
|
||||
|
||||
unless defined?(ActionMailer)
|
||||
begin
|
||||
$:.unshift(File.dirname(__FILE__) + "/../../../actionmailer/lib")
|
||||
require 'action_mailer'
|
||||
rescue LoadError
|
||||
require 'rubygems'
|
||||
gem 'actionmailer'
|
||||
end
|
||||
end
|
||||
|
||||
class AssertSelectTest < Test::Unit::TestCase
|
||||
class AssertSelectController < ActionController::Base
|
||||
def response_with=(content)
|
||||
@content = content
|
||||
end
|
||||
|
||||
def response_with(&block)
|
||||
@update = block
|
||||
end
|
||||
|
||||
def html()
|
||||
render :text=>@content, :layout=>false, :content_type=>Mime::HTML
|
||||
@content = nil
|
||||
end
|
||||
|
||||
def rjs()
|
||||
render :update do |page|
|
||||
@update.call page
|
||||
end
|
||||
@update = nil
|
||||
end
|
||||
|
||||
def xml()
|
||||
render :text=>@content, :layout=>false, :content_type=>Mime::XML
|
||||
@content = nil
|
||||
end
|
||||
|
||||
def rescue_action(e)
|
||||
raise e
|
||||
end
|
||||
end
|
||||
|
||||
class AssertSelectMailer < ActionMailer::Base
|
||||
def test(html)
|
||||
recipients "test <test@test.host>"
|
||||
from "test@test.host"
|
||||
subject "Test e-mail"
|
||||
part :content_type=>"text/html", :body=>html
|
||||
end
|
||||
end
|
||||
|
||||
AssertionFailedError = Test::Unit::AssertionFailedError
|
||||
|
||||
def setup
|
||||
@controller = AssertSelectController.new
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
ActionMailer::Base.delivery_method = :test
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.deliveries = []
|
||||
end
|
||||
|
||||
|
||||
def teardown
|
||||
ActionMailer::Base.deliveries.clear
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Test assert select.
|
||||
#
|
||||
|
||||
def test_assert_select
|
||||
render_html %Q{<div id="1"></div><div id="2"></div>}
|
||||
assert_select "div", 2
|
||||
assert_raises(AssertionFailedError) { assert_select "div", 3 }
|
||||
assert_raises(AssertionFailedError){ assert_select "p" }
|
||||
end
|
||||
|
||||
|
||||
def test_equality_true_false
|
||||
render_html %Q{<div id="1"></div><div id="2"></div>}
|
||||
assert_nothing_raised { assert_select "div" }
|
||||
assert_raises(AssertionFailedError) { assert_select "p" }
|
||||
assert_nothing_raised { assert_select "div", true }
|
||||
assert_raises(AssertionFailedError) { assert_select "p", true }
|
||||
assert_raises(AssertionFailedError) { assert_select "div", false }
|
||||
assert_nothing_raised { assert_select "p", false }
|
||||
end
|
||||
|
||||
|
||||
def test_equality_string_and_regexp
|
||||
render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
|
||||
assert_nothing_raised { assert_select "div", "foo" }
|
||||
assert_raises(AssertionFailedError) { assert_select "div", "bar" }
|
||||
assert_nothing_raised { assert_select "div", :text=>"foo" }
|
||||
assert_raises(AssertionFailedError) { assert_select "div", :text=>"bar" }
|
||||
assert_nothing_raised { assert_select "div", /(foo|bar)/ }
|
||||
assert_raises(AssertionFailedError) { assert_select "div", /foobar/ }
|
||||
assert_nothing_raised { assert_select "div", :text=>/(foo|bar)/ }
|
||||
assert_raises(AssertionFailedError) { assert_select "div", :text=>/foobar/ }
|
||||
assert_raises(AssertionFailedError) { assert_select "p", :text=>/foobar/ }
|
||||
end
|
||||
|
||||
|
||||
def test_equality_of_html
|
||||
render_html %Q{<p>\n<em>"This is <strong>not</strong> a big problem,"</em> he said.\n</p>}
|
||||
text = "\"This is not a big problem,\" he said."
|
||||
html = "<em>\"This is <strong>not</strong> a big problem,\"</em> he said."
|
||||
assert_nothing_raised { assert_select "p", text }
|
||||
assert_raises(AssertionFailedError) { assert_select "p", html }
|
||||
assert_nothing_raised { assert_select "p", :html=>html }
|
||||
assert_raises(AssertionFailedError) { assert_select "p", :html=>text }
|
||||
# No stripping for pre.
|
||||
render_html %Q{<pre>\n<em>"This is <strong>not</strong> a big problem,"</em> he said.\n</pre>}
|
||||
text = "\n\"This is not a big problem,\" he said.\n"
|
||||
html = "\n<em>\"This is <strong>not</strong> a big problem,\"</em> he said.\n"
|
||||
assert_nothing_raised { assert_select "pre", text }
|
||||
assert_raises(AssertionFailedError) { assert_select "pre", html }
|
||||
assert_nothing_raised { assert_select "pre", :html=>html }
|
||||
assert_raises(AssertionFailedError) { assert_select "pre", :html=>text }
|
||||
end
|
||||
|
||||
|
||||
def test_equality_of_instances
|
||||
render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
|
||||
assert_nothing_raised { assert_select "div", 2 }
|
||||
assert_raises(AssertionFailedError) { assert_select "div", 3 }
|
||||
assert_nothing_raised { assert_select "div", 1..2 }
|
||||
assert_raises(AssertionFailedError) { assert_select "div", 3..4 }
|
||||
assert_nothing_raised { assert_select "div", :count=>2 }
|
||||
assert_raises(AssertionFailedError) { assert_select "div", :count=>3 }
|
||||
assert_nothing_raised { assert_select "div", :minimum=>1 }
|
||||
assert_nothing_raised { assert_select "div", :minimum=>2 }
|
||||
assert_raises(AssertionFailedError) { assert_select "div", :minimum=>3 }
|
||||
assert_nothing_raised { assert_select "div", :maximum=>2 }
|
||||
assert_nothing_raised { assert_select "div", :maximum=>3 }
|
||||
assert_raises(AssertionFailedError) { assert_select "div", :maximum=>1 }
|
||||
assert_nothing_raised { assert_select "div", :minimum=>1, :maximum=>2 }
|
||||
assert_raises(AssertionFailedError) { assert_select "div", :minimum=>3, :maximum=>4 }
|
||||
end
|
||||
|
||||
|
||||
def test_substitution_values
|
||||
render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
|
||||
assert_select "div#?", /\d+/ do |elements|
|
||||
assert_equal 2, elements.size
|
||||
end
|
||||
assert_select "div" do
|
||||
assert_select "div#?", /\d+/ do |elements|
|
||||
assert_equal 2, elements.size
|
||||
assert_select "#1"
|
||||
assert_select "#2"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_nested_assert_select
|
||||
render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
|
||||
assert_select "div" do |elements|
|
||||
assert_equal 2, elements.size
|
||||
assert_select elements[0], "#1"
|
||||
assert_select elements[1], "#2"
|
||||
end
|
||||
assert_select "div" do
|
||||
assert_select "div" do |elements|
|
||||
assert_equal 2, elements.size
|
||||
# Testing in a group is one thing
|
||||
assert_select "#1,#2"
|
||||
# Testing individually is another.
|
||||
assert_select "#1"
|
||||
assert_select "#2"
|
||||
assert_select "#3", false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_assert_select_text_match
|
||||
render_html %Q{<div id="1"><span>foo</span></div><div id="2"><span>bar</span></div>}
|
||||
assert_select "div" do
|
||||
assert_nothing_raised { assert_select "div", "foo" }
|
||||
assert_nothing_raised { assert_select "div", "bar" }
|
||||
assert_nothing_raised { assert_select "div", /\w*/ }
|
||||
assert_nothing_raised { assert_select "div", /\w*/, :count=>2 }
|
||||
assert_raises(AssertionFailedError) { assert_select "div", :text=>"foo", :count=>2 }
|
||||
assert_nothing_raised { assert_select "div", :html=>"<span>bar</span>" }
|
||||
assert_nothing_raised { assert_select "div", :html=>"<span>bar</span>" }
|
||||
assert_nothing_raised { assert_select "div", :html=>/\w*/ }
|
||||
assert_nothing_raised { assert_select "div", :html=>/\w*/, :count=>2 }
|
||||
assert_raises(AssertionFailedError) { assert_select "div", :html=>"<span>foo</span>", :count=>2 }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# With single result.
|
||||
def test_assert_select_from_rjs_with_single_result
|
||||
render_rjs do |page|
|
||||
page.replace_html "test", "<div id=\"1\">foo</div>\n<div id=\"2\">foo</div>"
|
||||
end
|
||||
assert_select "div" do |elements|
|
||||
assert elements.size == 2
|
||||
assert_select "#1"
|
||||
assert_select "#2"
|
||||
end
|
||||
assert_select "div#?", /\d+/ do |elements|
|
||||
assert_select "#1"
|
||||
assert_select "#2"
|
||||
end
|
||||
end
|
||||
|
||||
# With multiple results.
|
||||
def test_assert_select_from_rjs_with_multiple_results
|
||||
render_rjs do |page|
|
||||
page.replace_html "test", "<div id=\"1\">foo</div>"
|
||||
page.replace_html "test2", "<div id=\"2\">foo</div>"
|
||||
end
|
||||
assert_select "div" do |elements|
|
||||
assert elements.size == 2
|
||||
assert_select "#1"
|
||||
assert_select "#2"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Test css_select.
|
||||
#
|
||||
|
||||
|
||||
def test_css_select
|
||||
render_html %Q{<div id="1"></div><div id="2"></div>}
|
||||
assert 2, css_select("div").size
|
||||
assert 0, css_select("p").size
|
||||
end
|
||||
|
||||
|
||||
def test_nested_css_select
|
||||
render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
|
||||
assert_select "div#?", /\d+/ do |elements|
|
||||
assert_equal 1, css_select(elements[0], "div").size
|
||||
assert_equal 1, css_select(elements[1], "div").size
|
||||
end
|
||||
assert_select "div" do
|
||||
assert_equal 2, css_select("div").size
|
||||
css_select("div").each do |element|
|
||||
# Testing as a group is one thing
|
||||
assert !css_select("#1,#2").empty?
|
||||
# Testing individually is another
|
||||
assert !css_select("#1").empty?
|
||||
assert !css_select("#2").empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# With one result.
|
||||
def test_css_select_from_rjs_with_single_result
|
||||
render_rjs do |page|
|
||||
page.replace_html "test", "<div id=\"1\">foo</div>\n<div id=\"2\">foo</div>"
|
||||
end
|
||||
assert_equal 2, css_select("div").size
|
||||
assert_equal 1, css_select("#1").size
|
||||
assert_equal 1, css_select("#2").size
|
||||
end
|
||||
|
||||
# With multiple results.
|
||||
def test_css_select_from_rjs_with_multiple_results
|
||||
render_rjs do |page|
|
||||
page.replace_html "test", "<div id=\"1\">foo</div>"
|
||||
page.replace_html "test2", "<div id=\"2\">foo</div>"
|
||||
end
|
||||
|
||||
assert_equal 2, css_select("div").size
|
||||
assert_equal 1, css_select("#1").size
|
||||
assert_equal 1, css_select("#2").size
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Test assert_select_rjs.
|
||||
#
|
||||
|
||||
|
||||
# Test that we can pick up all statements in the result.
|
||||
def test_assert_select_rjs_picks_up_all_statements
|
||||
render_rjs do |page|
|
||||
page.replace "test", "<div id=\"1\">foo</div>"
|
||||
page.replace_html "test2", "<div id=\"2\">foo</div>"
|
||||
page.insert_html :top, "test3", "<div id=\"3\">foo</div>"
|
||||
end
|
||||
|
||||
found = false
|
||||
assert_select_rjs do
|
||||
assert_select "#1"
|
||||
assert_select "#2"
|
||||
assert_select "#3"
|
||||
found = true
|
||||
end
|
||||
assert found
|
||||
end
|
||||
|
||||
# Test that we fail if there is nothing to pick.
|
||||
def test_assert_select_rjs_fails_if_nothing_to_pick
|
||||
render_rjs { }
|
||||
assert_raises(AssertionFailedError) { assert_select_rjs }
|
||||
end
|
||||
|
||||
def test_assert_select_rjs_with_unicode
|
||||
# Test that non-ascii characters (which are converted into \uXXXX in RJS) are decoded correctly.
|
||||
render_rjs do |page|
|
||||
page.replace "test", "<div id=\"1\">\343\203\201\343\202\261\343\203\203\343\203\210</div>"
|
||||
end
|
||||
assert_select_rjs do
|
||||
assert_select "#1", :text => "\343\203\201\343\202\261\343\203\203\343\203\210"
|
||||
assert_select "#1", "\343\203\201\343\202\261\343\203\203\343\203\210"
|
||||
assert_select "#1", Regexp.new("\343\203\201..\343\203\210",0,'U')
|
||||
assert_raises(AssertionFailedError) { assert_select "#1", Regexp.new("\343\203\201.\343\203\210",0,'U') }
|
||||
end
|
||||
end
|
||||
|
||||
def test_assert_select_rjs_with_id
|
||||
# Test that we can pick up all statements in the result.
|
||||
render_rjs do |page|
|
||||
page.replace "test1", "<div id=\"1\">foo</div>"
|
||||
page.replace_html "test2", "<div id=\"2\">foo</div>"
|
||||
page.insert_html :top, "test3", "<div id=\"3\">foo</div>"
|
||||
end
|
||||
assert_select_rjs "test1" do
|
||||
assert_select "div", 1
|
||||
assert_select "#1"
|
||||
end
|
||||
assert_select_rjs "test2" do
|
||||
assert_select "div", 1
|
||||
assert_select "#2"
|
||||
end
|
||||
assert_select_rjs "test3" do
|
||||
assert_select "div", 1
|
||||
assert_select "#3"
|
||||
end
|
||||
assert_raises(AssertionFailedError) { assert_select_rjs "test4" }
|
||||
end
|
||||
|
||||
|
||||
def test_assert_select_rjs_for_replace
|
||||
render_rjs do |page|
|
||||
page.replace "test1", "<div id=\"1\">foo</div>"
|
||||
page.replace_html "test2", "<div id=\"2\">foo</div>"
|
||||
page.insert_html :top, "test3", "<div id=\"3\">foo</div>"
|
||||
end
|
||||
# Replace.
|
||||
assert_select_rjs :replace do
|
||||
assert_select "div", 1
|
||||
assert_select "#1"
|
||||
end
|
||||
assert_select_rjs :replace, "test1" do
|
||||
assert_select "div", 1
|
||||
assert_select "#1"
|
||||
end
|
||||
assert_raises(AssertionFailedError) { assert_select_rjs :replace, "test2" }
|
||||
# Replace HTML.
|
||||
assert_select_rjs :replace_html do
|
||||
assert_select "div", 1
|
||||
assert_select "#2"
|
||||
end
|
||||
assert_select_rjs :replace_html, "test2" do
|
||||
assert_select "div", 1
|
||||
assert_select "#2"
|
||||
end
|
||||
assert_raises(AssertionFailedError) { assert_select_rjs :replace_html, "test1" }
|
||||
end
|
||||
|
||||
def test_assert_select_rjs_for_chained_replace
|
||||
render_rjs do |page|
|
||||
page['test1'].replace "<div id=\"1\">foo</div>"
|
||||
page['test2'].replace_html "<div id=\"2\">foo</div>"
|
||||
page.insert_html :top, "test3", "<div id=\"3\">foo</div>"
|
||||
end
|
||||
# Replace.
|
||||
assert_select_rjs :chained_replace do
|
||||
assert_select "div", 1
|
||||
assert_select "#1"
|
||||
end
|
||||
assert_select_rjs :chained_replace, "test1" do
|
||||
assert_select "div", 1
|
||||
assert_select "#1"
|
||||
end
|
||||
assert_raises(AssertionFailedError) { assert_select_rjs :chained_replace, "test2" }
|
||||
# Replace HTML.
|
||||
assert_select_rjs :chained_replace_html do
|
||||
assert_select "div", 1
|
||||
assert_select "#2"
|
||||
end
|
||||
assert_select_rjs :chained_replace_html, "test2" do
|
||||
assert_select "div", 1
|
||||
assert_select "#2"
|
||||
end
|
||||
assert_raises(AssertionFailedError) { assert_select_rjs :replace_html, "test1" }
|
||||
end
|
||||
|
||||
# Non-positioned insert.
|
||||
def test_assert_select_rjs_for_nonpositioned_insert
|
||||
render_rjs do |page|
|
||||
page.replace "test1", "<div id=\"1\">foo</div>"
|
||||
page.replace_html "test2", "<div id=\"2\">foo</div>"
|
||||
page.insert_html :top, "test3", "<div id=\"3\">foo</div>"
|
||||
end
|
||||
assert_select_rjs :insert_html do
|
||||
assert_select "div", 1
|
||||
assert_select "#3"
|
||||
end
|
||||
assert_select_rjs :insert_html, "test3" do
|
||||
assert_select "div", 1
|
||||
assert_select "#3"
|
||||
end
|
||||
assert_raises(AssertionFailedError) { assert_select_rjs :insert_html, "test1" }
|
||||
end
|
||||
|
||||
# Positioned insert.
|
||||
def test_assert_select_rjs_for_positioned_insert
|
||||
render_rjs do |page|
|
||||
page.insert_html :top, "test1", "<div id=\"1\">foo</div>"
|
||||
page.insert_html :bottom, "test2", "<div id=\"2\">foo</div>"
|
||||
page.insert_html :before, "test3", "<div id=\"3\">foo</div>"
|
||||
page.insert_html :after, "test4", "<div id=\"4\">foo</div>"
|
||||
end
|
||||
assert_select_rjs :insert, :top do
|
||||
assert_select "div", 1
|
||||
assert_select "#1"
|
||||
end
|
||||
assert_select_rjs :insert, :bottom do
|
||||
assert_select "div", 1
|
||||
assert_select "#2"
|
||||
end
|
||||
assert_select_rjs :insert, :before do
|
||||
assert_select "div", 1
|
||||
assert_select "#3"
|
||||
end
|
||||
assert_select_rjs :insert, :after do
|
||||
assert_select "div", 1
|
||||
assert_select "#4"
|
||||
end
|
||||
assert_select_rjs :insert_html do
|
||||
assert_select "div", 4
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Simple selection from a single result.
|
||||
def test_nested_assert_select_rjs_with_single_result
|
||||
render_rjs do |page|
|
||||
page.replace_html "test", "<div id=\"1\">foo</div>\n<div id=\"2\">foo</div>"
|
||||
end
|
||||
|
||||
assert_select_rjs "test" do |elements|
|
||||
assert_equal 2, elements.size
|
||||
assert_select "#1"
|
||||
assert_select "#2"
|
||||
end
|
||||
end
|
||||
|
||||
# Deal with two results.
|
||||
def test_nested_assert_select_rjs_with_two_results
|
||||
render_rjs do |page|
|
||||
page.replace_html "test", "<div id=\"1\">foo</div>"
|
||||
page.replace_html "test2", "<div id=\"2\">foo</div>"
|
||||
end
|
||||
|
||||
assert_select_rjs "test" do |elements|
|
||||
assert_equal 1, elements.size
|
||||
assert_select "#1"
|
||||
end
|
||||
|
||||
assert_select_rjs "test2" do |elements|
|
||||
assert_equal 1, elements.size
|
||||
assert_select "#2"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_feed_item_encoded
|
||||
render_xml <<-EOF
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<item>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<p>Test 1</p>
|
||||
]]>
|
||||
</description>
|
||||
</item>
|
||||
<item>
|
||||
<description>
|
||||
<![CDATA[
|
||||
<p>Test 2</p>
|
||||
]]>
|
||||
</description>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
||||
EOF
|
||||
assert_select "channel item description" do
|
||||
# Test element regardless of wrapper.
|
||||
assert_select_encoded do
|
||||
assert_select "p", :count=>2, :text=>/Test/
|
||||
end
|
||||
# Test through encoded wrapper.
|
||||
assert_select_encoded do
|
||||
assert_select "encoded p", :count=>2, :text=>/Test/
|
||||
end
|
||||
# Use :root instead (recommended)
|
||||
assert_select_encoded do
|
||||
assert_select ":root p", :count=>2, :text=>/Test/
|
||||
end
|
||||
# Test individually.
|
||||
assert_select "description" do |elements|
|
||||
assert_select_encoded elements[0] do
|
||||
assert_select "p", "Test 1"
|
||||
end
|
||||
assert_select_encoded elements[1] do
|
||||
assert_select "p", "Test 2"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Test that we only un-encode element itself.
|
||||
assert_select "channel item" do
|
||||
assert_select_encoded do
|
||||
assert_select "p", 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Test assert_select_email
|
||||
#
|
||||
|
||||
def test_assert_select_email
|
||||
assert_raises(AssertionFailedError) { assert_select_email {} }
|
||||
AssertSelectMailer.deliver_test "<div><p>foo</p><p>bar</p></div>"
|
||||
assert_select_email do
|
||||
assert_select "div:root" do
|
||||
assert_select "p:first-child", "foo"
|
||||
assert_select "p:last-child", "bar"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
def render_html(html)
|
||||
@controller.response_with = html
|
||||
get :html
|
||||
end
|
||||
|
||||
def render_rjs(&block)
|
||||
@controller.response_with &block
|
||||
get :rjs
|
||||
end
|
||||
|
||||
def render_xml(xml)
|
||||
@controller.response_with = xml
|
||||
get :xml
|
||||
end
|
||||
end
|
228
vendor/rails/actionpack/test/controller/caching_test.rb
vendored
Normal file
228
vendor/rails/actionpack/test/controller/caching_test.rb
vendored
Normal file
|
@ -0,0 +1,228 @@
|
|||
require 'fileutils'
|
||||
require File.dirname(__FILE__) + '/../abstract_unit'
|
||||
|
||||
CACHE_DIR = 'test_cache'
|
||||
# Don't change '/../temp/' cavalierly or you might hoze something you don't want hozed
|
||||
FILE_STORE_PATH = File.join(File.dirname(__FILE__), '/../temp/', CACHE_DIR)
|
||||
ActionController::Base.perform_caching = true
|
||||
ActionController::Base.page_cache_directory = FILE_STORE_PATH
|
||||
ActionController::Base.fragment_cache_store = :file_store, FILE_STORE_PATH
|
||||
|
||||
class PageCachingTestController < ActionController::Base
|
||||
caches_page :ok, :no_content, :found, :not_found
|
||||
|
||||
def ok
|
||||
head :ok
|
||||
end
|
||||
|
||||
def no_content
|
||||
head :no_content
|
||||
end
|
||||
|
||||
def found
|
||||
redirect_to :action => 'ok'
|
||||
end
|
||||
|
||||
def not_found
|
||||
head :not_found
|
||||
end
|
||||
end
|
||||
|
||||
class PageCachingTest < Test::Unit::TestCase
|
||||
def setup
|
||||
ActionController::Routing::Routes.draw do |map|
|
||||
map.main '', :controller => 'posts'
|
||||
map.resources :posts
|
||||
map.connect ':controller/:action/:id'
|
||||
end
|
||||
|
||||
@request = ActionController::TestRequest.new
|
||||
@request.host = 'hostname.com'
|
||||
|
||||
@response = ActionController::TestResponse.new
|
||||
@controller = PageCachingTestController.new
|
||||
|
||||
@params = {:controller => 'posts', :action => 'index', :only_path => true, :skip_relative_url_root => true}
|
||||
@rewriter = ActionController::UrlRewriter.new(@request, @params)
|
||||
|
||||
FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
|
||||
FileUtils.mkdir_p(FILE_STORE_PATH)
|
||||
end
|
||||
|
||||
def teardown
|
||||
FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
|
||||
end
|
||||
|
||||
def test_page_caching_resources_saves_to_correct_path_with_extension_even_if_default_route
|
||||
@params[:format] = 'rss'
|
||||
assert_equal '/posts.rss', @rewriter.rewrite(@params)
|
||||
@params[:format] = nil
|
||||
assert_equal '/', @rewriter.rewrite(@params)
|
||||
end
|
||||
|
||||
def test_should_cache_get_with_ok_status
|
||||
get :ok
|
||||
assert_response :ok
|
||||
assert_page_cached :ok, "get with ok status should have been cached"
|
||||
end
|
||||
|
||||
[:ok, :no_content, :found, :not_found].each do |status|
|
||||
[:get, :post, :put, :delete].each do |method|
|
||||
unless method == :get and status == :ok
|
||||
define_method "test_shouldnt_cache_#{method}_with_#{status}_status" do
|
||||
@request.env['REQUEST_METHOD'] = method.to_s.upcase
|
||||
process status
|
||||
assert_response status
|
||||
assert_page_not_cached status, "#{method} with #{status} status shouldn't have been cached"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def assert_page_cached(action, message = "#{action} should have been cached")
|
||||
assert page_cached?(action), message
|
||||
end
|
||||
|
||||
def assert_page_not_cached(action, message = "#{action} shouldn't have been cached")
|
||||
assert !page_cached?(action), message
|
||||
end
|
||||
|
||||
def page_cached?(action)
|
||||
File.exist? "#{FILE_STORE_PATH}/page_caching_test/#{action}.html"
|
||||
end
|
||||
end
|
||||
|
||||
class ActionCachingTestController < ActionController::Base
|
||||
caches_action :index
|
||||
|
||||
def index
|
||||
@cache_this = Time.now.to_f.to_s
|
||||
render :text => @cache_this
|
||||
end
|
||||
|
||||
def expire
|
||||
expire_action :controller => 'action_caching_test', :action => 'index'
|
||||
render :nothing => true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class ActionCachingMockController
|
||||
attr_accessor :mock_url_for
|
||||
attr_accessor :mock_path
|
||||
|
||||
def initialize
|
||||
yield self if block_given?
|
||||
end
|
||||
|
||||
def url_for(*args)
|
||||
@mock_url_for
|
||||
end
|
||||
|
||||
def request
|
||||
mocked_path = @mock_path
|
||||
Object.new.instance_eval(<<-EVAL)
|
||||
def path; '#{@mock_path}' end
|
||||
self
|
||||
EVAL
|
||||
end
|
||||
end
|
||||
|
||||
class ActionCacheTest < Test::Unit::TestCase
|
||||
def setup
|
||||
reset!
|
||||
FileUtils.mkdir_p(FILE_STORE_PATH)
|
||||
@path_class = ActionController::Caching::Actions::ActionCachePath
|
||||
@mock_controller = ActionCachingMockController.new
|
||||
end
|
||||
|
||||
def teardown
|
||||
FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
|
||||
end
|
||||
|
||||
def test_simple_action_cache
|
||||
get :index
|
||||
cached_time = content_to_cache
|
||||
assert_equal cached_time, @response.body
|
||||
reset!
|
||||
|
||||
get :index
|
||||
assert_equal cached_time, @response.body
|
||||
end
|
||||
|
||||
def test_cache_expiration
|
||||
get :index
|
||||
cached_time = content_to_cache
|
||||
reset!
|
||||
|
||||
get :index
|
||||
assert_equal cached_time, @response.body
|
||||
reset!
|
||||
|
||||
get :expire
|
||||
reset!
|
||||
|
||||
get :index
|
||||
new_cached_time = content_to_cache
|
||||
assert_not_equal cached_time, @response.body
|
||||
reset!
|
||||
|
||||
get :index
|
||||
assert_response :success
|
||||
assert_equal new_cached_time, @response.body
|
||||
end
|
||||
|
||||
def test_cache_is_scoped_by_subdomain
|
||||
@request.host = 'jamis.hostname.com'
|
||||
get :index
|
||||
jamis_cache = content_to_cache
|
||||
|
||||
@request.host = 'david.hostname.com'
|
||||
get :index
|
||||
david_cache = content_to_cache
|
||||
assert_not_equal jamis_cache, @response.body
|
||||
|
||||
@request.host = 'jamis.hostname.com'
|
||||
get :index
|
||||
assert_equal jamis_cache, @response.body
|
||||
|
||||
@request.host = 'david.hostname.com'
|
||||
get :index
|
||||
assert_equal david_cache, @response.body
|
||||
end
|
||||
|
||||
def test_xml_version_of_resource_is_treated_as_different_cache
|
||||
@mock_controller.mock_url_for = 'http://example.org/posts/'
|
||||
@mock_controller.mock_path = '/posts/index.xml'
|
||||
path_object = @path_class.new(@mock_controller)
|
||||
assert_equal 'xml', path_object.extension
|
||||
assert_equal 'example.org/posts/index.xml', path_object.path
|
||||
end
|
||||
|
||||
def test_empty_path_is_normalized
|
||||
@mock_controller.mock_url_for = 'http://example.org/'
|
||||
@mock_controller.mock_path = '/'
|
||||
|
||||
assert_equal 'example.org/index', @path_class.path_for(@mock_controller)
|
||||
end
|
||||
|
||||
def test_file_extensions
|
||||
get :index, :id => 'kitten.jpg'
|
||||
get :index, :id => 'kitten.jpg'
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
private
|
||||
def content_to_cache
|
||||
assigns(:cache_this)
|
||||
end
|
||||
|
||||
def reset!
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
@controller = ActionCachingTestController.new
|
||||
@request.host = 'hostname.com'
|
||||
end
|
||||
end
|
139
vendor/rails/actionpack/test/controller/content_type_test.rb
vendored
Normal file
139
vendor/rails/actionpack/test/controller/content_type_test.rb
vendored
Normal file
|
@ -0,0 +1,139 @@
|
|||
require File.dirname(__FILE__) + '/../abstract_unit'
|
||||
|
||||
class ContentTypeController < ActionController::Base
|
||||
def render_content_type_from_body
|
||||
response.content_type = Mime::RSS
|
||||
render :text => "hello world!"
|
||||
end
|
||||
|
||||
def render_defaults
|
||||
render :text => "hello world!"
|
||||
end
|
||||
|
||||
def render_content_type_from_render
|
||||
render :text => "hello world!", :content_type => Mime::RSS
|
||||
end
|
||||
|
||||
def render_charset_from_body
|
||||
response.charset = "utf-16"
|
||||
render :text => "hello world!"
|
||||
end
|
||||
|
||||
def render_default_for_rhtml
|
||||
end
|
||||
|
||||
def render_default_for_rxml
|
||||
end
|
||||
|
||||
def render_default_for_rjs
|
||||
end
|
||||
|
||||
def render_change_for_rxml
|
||||
response.content_type = Mime::HTML
|
||||
render :action => "render_default_for_rxml"
|
||||
end
|
||||
|
||||
def render_default_content_types_for_respond_to
|
||||
respond_to do |format|
|
||||
format.html { render :text => "hello world!" }
|
||||
format.xml { render :action => "render_default_content_types_for_respond_to.rhtml" }
|
||||
format.js { render :text => "hello world!" }
|
||||
format.rss { render :text => "hello world!", :content_type => Mime::XML }
|
||||
end
|
||||
end
|
||||
|
||||
def rescue_action(e) raise end
|
||||
end
|
||||
|
||||
ContentTypeController.template_root = File.dirname(__FILE__) + "/../fixtures/"
|
||||
|
||||
class ContentTypeTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@controller = ContentTypeController.new
|
||||
|
||||
# enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
|
||||
# a more accurate simulation of what happens in "real life".
|
||||
@controller.logger = Logger.new(nil)
|
||||
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
end
|
||||
|
||||
def test_render_defaults
|
||||
get :render_defaults
|
||||
assert_equal "utf-8", @response.charset
|
||||
assert_equal Mime::HTML, @response.content_type
|
||||
end
|
||||
|
||||
def test_render_changed_charset_default
|
||||
ContentTypeController.default_charset = "utf-16"
|
||||
get :render_defaults
|
||||
assert_equal "utf-16", @response.charset
|
||||
assert_equal Mime::HTML, @response.content_type
|
||||
ContentTypeController.default_charset = "utf-8"
|
||||
end
|
||||
|
||||
def test_content_type_from_body
|
||||
get :render_content_type_from_body
|
||||
assert_equal "application/rss+xml", @response.content_type
|
||||
assert_equal "utf-8", @response.charset
|
||||
end
|
||||
|
||||
def test_content_type_from_render
|
||||
get :render_content_type_from_render
|
||||
assert_equal "application/rss+xml", @response.content_type
|
||||
assert_equal "utf-8", @response.charset
|
||||
end
|
||||
|
||||
def test_charset_from_body
|
||||
get :render_charset_from_body
|
||||
assert_equal "utf-16", @response.charset
|
||||
assert_equal Mime::HTML, @response.content_type
|
||||
end
|
||||
|
||||
def test_default_for_rhtml
|
||||
get :render_default_for_rhtml
|
||||
assert_equal Mime::HTML, @response.content_type
|
||||
assert_equal "utf-8", @response.charset
|
||||
end
|
||||
|
||||
def test_default_for_rxml
|
||||
get :render_default_for_rxml
|
||||
assert_equal Mime::XML, @response.content_type
|
||||
assert_equal "utf-8", @response.charset
|
||||
end
|
||||
|
||||
def test_default_for_rjs
|
||||
xhr :post, :render_default_for_rjs
|
||||
assert_equal Mime::JS, @response.content_type
|
||||
assert_equal "utf-8", @response.charset
|
||||
end
|
||||
|
||||
def test_change_for_rxml
|
||||
get :render_change_for_rxml
|
||||
assert_equal Mime::HTML, @response.content_type
|
||||
assert_equal "utf-8", @response.charset
|
||||
end
|
||||
|
||||
def test_render_default_content_types_for_respond_to
|
||||
@request.env["HTTP_ACCEPT"] = Mime::HTML.to_s
|
||||
get :render_default_content_types_for_respond_to
|
||||
assert_equal Mime::HTML, @response.content_type
|
||||
|
||||
@request.env["HTTP_ACCEPT"] = Mime::JS.to_s
|
||||
get :render_default_content_types_for_respond_to
|
||||
assert_equal Mime::JS, @response.content_type
|
||||
end
|
||||
|
||||
def test_render_default_content_types_for_respond_to_with_template
|
||||
@request.env["HTTP_ACCEPT"] = Mime::XML.to_s
|
||||
get :render_default_content_types_for_respond_to
|
||||
assert_equal Mime::XML, @response.content_type
|
||||
end
|
||||
|
||||
def test_render_default_content_types_for_respond_to_with_overwrite
|
||||
@request.env["HTTP_ACCEPT"] = Mime::RSS.to_s
|
||||
get :render_default_content_types_for_respond_to
|
||||
assert_equal Mime::XML, @response.content_type
|
||||
end
|
||||
end
|
48
vendor/rails/actionpack/test/controller/deprecated_instance_variables_test.rb
vendored
Normal file
48
vendor/rails/actionpack/test/controller/deprecated_instance_variables_test.rb
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
require File.dirname(__FILE__) + '/../abstract_unit'
|
||||
|
||||
class DeprecatedControllerInstanceVariablesTest < Test::Unit::TestCase
|
||||
class Target < ActionController::Base
|
||||
def initialize(run = nil)
|
||||
instance_eval(run) if run
|
||||
super()
|
||||
end
|
||||
|
||||
def noop
|
||||
render :nothing => true
|
||||
end
|
||||
|
||||
ActionController::Base::DEPRECATED_INSTANCE_VARIABLES.each do |var|
|
||||
class_eval "def old_#{var}; render :text => @#{var}.to_s end"
|
||||
class_eval "def new_#{var}; render :text => #{var}.to_s end"
|
||||
class_eval "def internal_#{var}; render :text => @_#{var}.to_s end"
|
||||
end
|
||||
|
||||
def rescue_action(e) raise e end
|
||||
end
|
||||
|
||||
def setup
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
@controller = Target.new
|
||||
end
|
||||
|
||||
ActionController::Base::DEPRECATED_INSTANCE_VARIABLES.each do |var|
|
||||
class_eval <<-end_eval, __FILE__, __LINE__
|
||||
def test_old_#{var}_is_deprecated
|
||||
assert_deprecated('@#{var}') { get :old_#{var} }
|
||||
end
|
||||
def test_new_#{var}_isnt_deprecated
|
||||
assert_not_deprecated { get :new_#{var} }
|
||||
end
|
||||
def test_internal_#{var}_isnt_deprecated
|
||||
assert_not_deprecated { get :internal_#{var} }
|
||||
end
|
||||
def test_#{var}_raises_if_already_set
|
||||
assert_raise(RuntimeError) do
|
||||
@controller = Target.new '@#{var} = Object.new'
|
||||
get :noop
|
||||
end
|
||||
end
|
||||
end_eval
|
||||
end
|
||||
end
|
60
vendor/rails/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb
vendored
Normal file
60
vendor/rails/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
require File.dirname(__FILE__) + '/../../abstract_unit'
|
||||
|
||||
class DeprecatedBaseMethodsTest < Test::Unit::TestCase
|
||||
class Target < ActionController::Base
|
||||
def deprecated_symbol_parameter_to_url_for
|
||||
redirect_to(url_for(:home_url, "superstars"))
|
||||
end
|
||||
|
||||
def deprecated_render_parameters
|
||||
render "fun/games/hello_world"
|
||||
end
|
||||
|
||||
def home_url(greeting)
|
||||
"http://example.com/#{greeting}"
|
||||
end
|
||||
|
||||
def raises_name_error
|
||||
this_method_doesnt_exist
|
||||
end
|
||||
|
||||
def rescue_action(e) raise e end
|
||||
end
|
||||
|
||||
Target.template_root = File.dirname(__FILE__) + "/../../fixtures"
|
||||
|
||||
def setup
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
@controller = Target.new
|
||||
end
|
||||
|
||||
def test_deprecated_symbol_parameter_to_url_for
|
||||
assert_deprecated("url_for(:home_url)") do
|
||||
get :deprecated_symbol_parameter_to_url_for
|
||||
end
|
||||
|
||||
assert_redirected_to "http://example.com/superstars"
|
||||
end
|
||||
|
||||
def test_deprecated_render_parameters
|
||||
assert_deprecated("render('fun/games/hello_world')") do
|
||||
get :deprecated_render_parameters
|
||||
end
|
||||
|
||||
assert_equal "Living in a nested world", @response.body
|
||||
end
|
||||
|
||||
def test_log_error_silences_deprecation_warnings
|
||||
get :raises_name_error
|
||||
rescue => e
|
||||
assert_not_deprecated { @controller.send :log_error, e }
|
||||
end
|
||||
|
||||
def test_assertion_failed_error_silences_deprecation_warnings
|
||||
get :raises_name_error
|
||||
rescue => e
|
||||
error = Test::Unit::Error.new('testing ur doodz', e)
|
||||
assert_not_deprecated { error.message }
|
||||
end
|
||||
end
|
154
vendor/rails/actionpack/test/controller/integration_test.rb
vendored
Normal file
154
vendor/rails/actionpack/test/controller/integration_test.rb
vendored
Normal file
|
@ -0,0 +1,154 @@
|
|||
require File.dirname(__FILE__) + '/../abstract_unit'
|
||||
|
||||
$:.unshift File.dirname(__FILE__) + '/../../../railties/lib'
|
||||
require 'action_controller/integration'
|
||||
|
||||
begin # rescue LoadError
|
||||
require 'mocha'
|
||||
require 'stubba'
|
||||
|
||||
# Stub process for testing.
|
||||
module ActionController
|
||||
module Integration
|
||||
class Session
|
||||
def process
|
||||
end
|
||||
|
||||
def generic_url_rewriter
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class SessionTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@session = ActionController::Integration::Session.new
|
||||
end
|
||||
def test_https_bang_works_and_sets_truth_by_default
|
||||
assert !@session.https?
|
||||
@session.https!
|
||||
assert @session.https?
|
||||
@session.https! false
|
||||
assert !@session.https?
|
||||
end
|
||||
|
||||
def test_host!
|
||||
assert_not_equal "glu.ttono.us", @session.host
|
||||
@session.host! "rubyonrails.com"
|
||||
assert_equal "rubyonrails.com", @session.host
|
||||
end
|
||||
|
||||
def test_follow_redirect_raises_when_no_redirect
|
||||
@session.stubs(:redirect?).returns(false)
|
||||
assert_raise(RuntimeError) { @session.follow_redirect! }
|
||||
end
|
||||
|
||||
def test_follow_redirect_calls_get_and_returns_status
|
||||
@session.stubs(:redirect?).returns(true)
|
||||
@session.stubs(:headers).returns({"location" => ["www.google.com"]})
|
||||
@session.stubs(:status).returns(200)
|
||||
@session.expects(:get)
|
||||
assert_equal 200, @session.follow_redirect!
|
||||
end
|
||||
|
||||
def test_get_via_redirect
|
||||
path = "/somepath"; args = {:id => '1'}
|
||||
|
||||
@session.expects(:get).with(path,args)
|
||||
|
||||
redirects = [true, true, false]
|
||||
@session.stubs(:redirect?).returns(lambda { redirects.shift })
|
||||
@session.expects(:follow_redirect!).times(2)
|
||||
|
||||
@session.stubs(:status).returns(200)
|
||||
assert_equal 200, @session.get_via_redirect(path, args)
|
||||
end
|
||||
|
||||
def test_post_via_redirect
|
||||
path = "/somepath"; args = {:id => '1'}
|
||||
|
||||
@session.expects(:post).with(path,args)
|
||||
|
||||
redirects = [true, true, false]
|
||||
@session.stubs(:redirect?).returns(lambda { redirects.shift })
|
||||
@session.expects(:follow_redirect!).times(2)
|
||||
|
||||
@session.stubs(:status).returns(200)
|
||||
assert_equal 200, @session.post_via_redirect(path, args)
|
||||
end
|
||||
|
||||
def test_url_for_with_controller
|
||||
options = {:action => 'show'}
|
||||
mock_controller = mock()
|
||||
mock_controller.expects(:url_for).with(options).returns('/show')
|
||||
@session.stubs(:controller).returns(mock_controller)
|
||||
assert_equal '/show', @session.url_for(options)
|
||||
end
|
||||
|
||||
def test_url_for_without_controller
|
||||
options = {:action => 'show'}
|
||||
mock_rewriter = mock()
|
||||
mock_rewriter.expects(:rewrite).with(options).returns('/show')
|
||||
@session.stubs(:generic_url_rewriter).returns(mock_rewriter)
|
||||
@session.stubs(:controller).returns(nil)
|
||||
assert_equal '/show', @session.url_for(options)
|
||||
end
|
||||
|
||||
def test_redirect_bool_with_status_in_300s
|
||||
@session.stubs(:status).returns 301
|
||||
assert @session.redirect?
|
||||
end
|
||||
|
||||
def test_redirect_bool_with_status_in_200s
|
||||
@session.stubs(:status).returns 200
|
||||
assert !@session.redirect?
|
||||
end
|
||||
|
||||
def test_get
|
||||
path = "/index"; params = "blah"; headers = {:location => 'blah'}
|
||||
@session.expects(:process).with(:get,path,params,headers)
|
||||
@session.get(path,params,headers)
|
||||
end
|
||||
|
||||
def test_post
|
||||
path = "/index"; params = "blah"; headers = {:location => 'blah'}
|
||||
@session.expects(:process).with(:post,path,params,headers)
|
||||
@session.post(path,params,headers)
|
||||
end
|
||||
|
||||
def test_put
|
||||
path = "/index"; params = "blah"; headers = {:location => 'blah'}
|
||||
@session.expects(:process).with(:put,path,params,headers)
|
||||
@session.put(path,params,headers)
|
||||
end
|
||||
|
||||
def test_delete
|
||||
path = "/index"; params = "blah"; headers = {:location => 'blah'}
|
||||
@session.expects(:process).with(:delete,path,params,headers)
|
||||
@session.delete(path,params,headers)
|
||||
end
|
||||
|
||||
def test_head
|
||||
path = "/index"; params = "blah"; headers = {:location => 'blah'}
|
||||
@session.expects(:process).with(:head,path,params,headers)
|
||||
@session.head(path,params,headers)
|
||||
end
|
||||
|
||||
def test_xml_http_request
|
||||
path = "/index"; params = "blah"; headers = {:location => 'blah'}
|
||||
headers_after_xhr = headers.merge(
|
||||
"X-Requested-With" => "XMLHttpRequest",
|
||||
"Accept" => "text/javascript, text/html, application/xml, text/xml, */*"
|
||||
)
|
||||
@session.expects(:post).with(path,params,headers_after_xhr)
|
||||
@session.xml_http_request(path,params,headers)
|
||||
end
|
||||
end
|
||||
|
||||
# TODO
|
||||
# class MockCGITest < Test::Unit::TestCase
|
||||
# end
|
||||
|
||||
rescue LoadError
|
||||
$stderr.puts "Skipping integration tests. `gem install mocha` and try again."
|
||||
end
|
482
vendor/rails/actionpack/test/controller/resources_test.rb
vendored
Normal file
482
vendor/rails/actionpack/test/controller/resources_test.rb
vendored
Normal file
|
@ -0,0 +1,482 @@
|
|||
require File.dirname(__FILE__) + '/../abstract_unit'
|
||||
|
||||
class ResourcesController < ActionController::Base
|
||||
def index() render :nothing => true end
|
||||
alias_method :show, :index
|
||||
def rescue_action(e) raise e end
|
||||
end
|
||||
|
||||
class ThreadsController < ResourcesController; end
|
||||
class MessagesController < ResourcesController; end
|
||||
class CommentsController < ResourcesController; end
|
||||
|
||||
class AccountController < ResourcesController; end
|
||||
class AdminController < ResourcesController; end
|
||||
|
||||
class ResourcesTest < Test::Unit::TestCase
|
||||
def test_should_arrange_actions
|
||||
resource = ActionController::Resources::Resource.new(:messages,
|
||||
:collection => { :rss => :get, :reorder => :post, :csv => :post },
|
||||
:member => { :rss => :get, :atom => :get, :upload => :post, :fix => :post },
|
||||
:new => { :preview => :get, :draft => :get })
|
||||
|
||||
assert_resource_methods [:rss], resource, :collection, :get
|
||||
assert_resource_methods [:csv, :reorder], resource, :collection, :post
|
||||
assert_resource_methods [:edit, :rss, :atom], resource, :member, :get
|
||||
assert_resource_methods [:upload, :fix], resource, :member, :post
|
||||
assert_resource_methods [:new, :preview, :draft], resource, :new, :get
|
||||
end
|
||||
|
||||
def test_should_resource_controller_name_equal_resource_name_by_default
|
||||
resource = ActionController::Resources::Resource.new(:messages, {})
|
||||
assert_equal 'messages', resource.controller
|
||||
end
|
||||
|
||||
def test_should_resource_controller_name_equal_controller_option
|
||||
resource = ActionController::Resources::Resource.new(:messages, :controller => 'posts')
|
||||
assert_equal 'posts', resource.controller
|
||||
end
|
||||
|
||||
def test_should_all_singleton_paths_be_the_same
|
||||
[ :path, :nesting_path_prefix, :member_path ].each do |method|
|
||||
resource = ActionController::Resources::SingletonResource.new(:messages, :path_prefix => 'admin')
|
||||
assert_equal 'admin/messages', resource.send(method)
|
||||
end
|
||||
end
|
||||
|
||||
def test_default_restful_routes
|
||||
with_restful_routing :messages do
|
||||
assert_simply_restful_for :messages
|
||||
end
|
||||
end
|
||||
|
||||
def test_multiple_default_restful_routes
|
||||
with_restful_routing :messages, :comments do
|
||||
assert_simply_restful_for :messages
|
||||
assert_simply_restful_for :comments
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_path_prefix
|
||||
with_restful_routing :messages, :path_prefix => '/thread/:thread_id' do
|
||||
assert_simply_restful_for :messages, :path_prefix => 'thread/5/', :options => { :thread_id => '5' }
|
||||
end
|
||||
end
|
||||
|
||||
def test_multile_with_path_prefix
|
||||
with_restful_routing :messages, :comments, :path_prefix => '/thread/:thread_id' do
|
||||
assert_simply_restful_for :messages, :path_prefix => 'thread/5/', :options => { :thread_id => '5' }
|
||||
assert_simply_restful_for :comments, :path_prefix => 'thread/5/', :options => { :thread_id => '5' }
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_name_prefix
|
||||
with_restful_routing :messages, :name_prefix => 'post_' do
|
||||
assert_simply_restful_for :messages, :name_prefix => 'post_'
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_collection_action
|
||||
rss_options = {:action => 'rss'}
|
||||
rss_path = "/messages;rss"
|
||||
actions = { 'a' => :put, 'b' => :post, 'c' => :delete }
|
||||
|
||||
with_restful_routing :messages, :collection => { :rss => :get }.merge(actions) do
|
||||
assert_restful_routes_for :messages do |options|
|
||||
assert_routing rss_path, options.merge(rss_options)
|
||||
|
||||
actions.each do |action, method|
|
||||
assert_recognizes(options.merge(:action => action), :path => "/messages;#{action}", :method => method)
|
||||
end
|
||||
end
|
||||
|
||||
assert_restful_named_routes_for :messages do |options|
|
||||
assert_named_route rss_path, :rss_messages_path, rss_options
|
||||
actions.keys.each do |action|
|
||||
assert_named_route "/messages;#{action}", "#{action}_messages_path", :action => action
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_member_action
|
||||
[:put, :post].each do |method|
|
||||
with_restful_routing :messages, :member => { :mark => method } do
|
||||
mark_options = {:action => 'mark', :id => '1'}
|
||||
mark_path = "/messages/1;mark"
|
||||
assert_restful_routes_for :messages do |options|
|
||||
assert_recognizes(options.merge(mark_options), :path => mark_path, :method => method)
|
||||
end
|
||||
|
||||
assert_restful_named_routes_for :messages do |options|
|
||||
assert_named_route mark_path, :mark_message_path, mark_options
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_two_member_actions_with_same_method
|
||||
[:put, :post].each do |method|
|
||||
with_restful_routing :messages, :member => { :mark => method, :unmark => method } do
|
||||
%w(mark unmark).each do |action|
|
||||
action_options = {:action => action, :id => '1'}
|
||||
action_path = "/messages/1;#{action}"
|
||||
assert_restful_routes_for :messages do |options|
|
||||
assert_recognizes(options.merge(action_options), :path => action_path, :method => method)
|
||||
end
|
||||
|
||||
assert_restful_named_routes_for :messages do |options|
|
||||
assert_named_route action_path, "#{action}_message_path".to_sym, action_options
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_new_action
|
||||
with_restful_routing :messages, :new => { :preview => :post } do
|
||||
preview_options = {:action => 'preview'}
|
||||
preview_path = "/messages/new;preview"
|
||||
assert_restful_routes_for :messages do |options|
|
||||
assert_recognizes(options.merge(preview_options), :path => preview_path, :method => :post)
|
||||
end
|
||||
|
||||
assert_restful_named_routes_for :messages do |options|
|
||||
assert_named_route preview_path, :preview_new_message_path, preview_options
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_override_new_method
|
||||
with_restful_routing :messages do
|
||||
assert_restful_routes_for :messages do |options|
|
||||
assert_recognizes(options.merge(:action => "new"), :path => "/messages/new", :method => :get)
|
||||
assert_raises(ActionController::RoutingError) do
|
||||
ActionController::Routing::Routes.recognize_path("/messages/new", :method => :post)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
with_restful_routing :messages, :new => { :new => :any } do
|
||||
assert_restful_routes_for :messages do |options|
|
||||
assert_recognizes(options.merge(:action => "new"), :path => "/messages/new", :method => :post)
|
||||
assert_recognizes(options.merge(:action => "new"), :path => "/messages/new", :method => :get)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_nested_restful_routes
|
||||
with_routing do |set|
|
||||
set.draw do |map|
|
||||
map.resources :threads do |map|
|
||||
map.resources :messages do |map|
|
||||
map.resources :comments
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert_simply_restful_for :threads
|
||||
assert_simply_restful_for :messages,
|
||||
:path_prefix => 'threads/1/',
|
||||
:options => { :thread_id => '1' }
|
||||
assert_simply_restful_for :comments,
|
||||
:path_prefix => 'threads/1/messages/2/',
|
||||
:options => { :thread_id => '1', :message_id => '2' }
|
||||
end
|
||||
end
|
||||
|
||||
def test_restful_routes_dont_generate_duplicates
|
||||
with_restful_routing :messages do
|
||||
routes = ActionController::Routing::Routes.routes
|
||||
routes.each do |route|
|
||||
routes.each do |r|
|
||||
next if route === r # skip the comparison instance
|
||||
assert distinct_routes?(route, r), "Duplicate Route: #{route}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_create_singleton_resource_routes
|
||||
with_singleton_resources :account do
|
||||
assert_singleton_restful_for :account
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_create_multiple_singleton_resource_routes
|
||||
with_singleton_resources :account, :admin do
|
||||
assert_singleton_restful_for :account
|
||||
assert_singleton_restful_for :admin
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_create_nested_singleton_resource_routes
|
||||
with_routing do |set|
|
||||
set.draw do |map|
|
||||
map.resource :admin do |admin|
|
||||
admin.resource :account
|
||||
end
|
||||
end
|
||||
|
||||
assert_singleton_restful_for :admin
|
||||
assert_singleton_restful_for :account, :path_prefix => 'admin/'
|
||||
end
|
||||
end
|
||||
|
||||
def test_singleton_resource_with_member_action
|
||||
[:put, :post].each do |method|
|
||||
with_singleton_resources :account, :member => { :reset => method } do
|
||||
reset_options = {:action => 'reset'}
|
||||
reset_path = "/account;reset"
|
||||
assert_singleton_routes_for :account do |options|
|
||||
assert_recognizes(options.merge(reset_options), :path => reset_path, :method => method)
|
||||
end
|
||||
|
||||
assert_singleton_named_routes_for :account do |options|
|
||||
assert_named_route reset_path, :reset_account_path, reset_options
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_singleton_resource_with_two_member_actions_with_same_method
|
||||
[:put, :post].each do |method|
|
||||
with_singleton_resources :account, :member => { :reset => method, :disable => method } do
|
||||
%w(reset disable).each do |action|
|
||||
action_options = {:action => action}
|
||||
action_path = "/account;#{action}"
|
||||
assert_singleton_routes_for :account do |options|
|
||||
assert_recognizes(options.merge(action_options), :path => action_path, :method => method)
|
||||
end
|
||||
|
||||
assert_singleton_named_routes_for :account do |options|
|
||||
assert_named_route action_path, "#{action}_account_path".to_sym, action_options
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_nest_resources_in_singleton_resource
|
||||
with_routing do |set|
|
||||
set.draw do |map|
|
||||
map.resource :account do |account|
|
||||
account.resources :messages
|
||||
end
|
||||
end
|
||||
|
||||
assert_singleton_restful_for :account
|
||||
assert_simply_restful_for :messages, :path_prefix => 'account/'
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_nest_resources_in_singleton_resource_with_path_prefix
|
||||
with_routing do |set|
|
||||
set.draw do |map|
|
||||
map.resource(:account, :path_prefix => ':site_id') do |account|
|
||||
account.resources :messages
|
||||
end
|
||||
end
|
||||
|
||||
assert_singleton_restful_for :account, :path_prefix => '7/', :options => { :site_id => '7' }
|
||||
assert_simply_restful_for :messages, :path_prefix => '7/account/', :options => { :site_id => '7' }
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_nest_singleton_resource_in_resources
|
||||
with_routing do |set|
|
||||
set.draw do |map|
|
||||
map.resources :threads do |thread|
|
||||
thread.resource :admin
|
||||
end
|
||||
end
|
||||
|
||||
assert_simply_restful_for :threads
|
||||
assert_singleton_restful_for :admin, :path_prefix => 'threads/5/', :options => { :thread_id => '5' }
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_not_allow_delete_or_put_on_collection_path
|
||||
controller_name = :messages
|
||||
with_restful_routing controller_name do
|
||||
options = { :controller => controller_name.to_s }
|
||||
collection_path = "/#{controller_name}"
|
||||
|
||||
assert_raises(ActionController::RoutingError) do
|
||||
assert_recognizes(options.merge(:action => 'update'), :path => collection_path, :method => :put)
|
||||
end
|
||||
|
||||
assert_raises(ActionController::RoutingError) do
|
||||
assert_recognizes(options.merge(:action => 'destroy'), :path => collection_path, :method => :delete)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def with_restful_routing(*args)
|
||||
with_routing do |set|
|
||||
set.draw { |map| map.resources(*args) }
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def with_singleton_resources(*args)
|
||||
with_routing do |set|
|
||||
set.draw { |map| map.resource(*args) }
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
# runs assert_restful_routes_for and assert_restful_named_routes for on the controller_name and options, without passing a block.
|
||||
def assert_simply_restful_for(controller_name, options = {})
|
||||
assert_restful_routes_for controller_name, options
|
||||
assert_restful_named_routes_for controller_name, options
|
||||
end
|
||||
|
||||
def assert_singleton_restful_for(singleton_name, options = {})
|
||||
assert_singleton_routes_for singleton_name, options
|
||||
assert_singleton_named_routes_for singleton_name, options
|
||||
end
|
||||
|
||||
def assert_restful_routes_for(controller_name, options = {})
|
||||
(options[:options] ||= {})[:controller] = controller_name.to_s
|
||||
|
||||
collection_path = "/#{options[:path_prefix]}#{controller_name}"
|
||||
member_path = "#{collection_path}/1"
|
||||
new_path = "#{collection_path}/new"
|
||||
edit_member_path = "#{member_path};edit"
|
||||
formatted_edit_member_path = "#{member_path}.xml;edit"
|
||||
|
||||
with_options(options[:options]) do |controller|
|
||||
controller.assert_routing collection_path, :action => 'index'
|
||||
controller.assert_routing new_path, :action => 'new'
|
||||
controller.assert_routing member_path, :action => 'show', :id => '1'
|
||||
controller.assert_routing edit_member_path, :action => 'edit', :id => '1'
|
||||
controller.assert_routing "#{collection_path}.xml", :action => 'index', :format => 'xml'
|
||||
controller.assert_routing "#{new_path}.xml", :action => 'new', :format => 'xml'
|
||||
controller.assert_routing "#{member_path}.xml", :action => 'show', :id => '1', :format => 'xml'
|
||||
controller.assert_routing formatted_edit_member_path, :action => 'edit', :id => '1', :format => 'xml'
|
||||
end
|
||||
|
||||
assert_recognizes(options[:options].merge(:action => 'index'), :path => collection_path, :method => :get)
|
||||
assert_recognizes(options[:options].merge(:action => 'new'), :path => new_path, :method => :get)
|
||||
assert_recognizes(options[:options].merge(:action => 'create'), :path => collection_path, :method => :post)
|
||||
assert_recognizes(options[:options].merge(:action => 'show', :id => '1'), :path => member_path, :method => :get)
|
||||
assert_recognizes(options[:options].merge(:action => 'edit', :id => '1'), :path => edit_member_path, :method => :get)
|
||||
assert_recognizes(options[:options].merge(:action => 'update', :id => '1'), :path => member_path, :method => :put)
|
||||
assert_recognizes(options[:options].merge(:action => 'destroy', :id => '1'), :path => member_path, :method => :delete)
|
||||
|
||||
assert_recognizes(options[:options].merge(:action => 'index', :format => 'xml'), :path => "#{collection_path}.xml", :method => :get)
|
||||
assert_recognizes(options[:options].merge(:action => 'new', :format => 'xml'), :path => "#{new_path}.xml", :method => :get)
|
||||
assert_recognizes(options[:options].merge(:action => 'create', :format => 'xml'), :path => "#{collection_path}.xml", :method => :post)
|
||||
assert_recognizes(options[:options].merge(:action => 'show', :id => '1', :format => 'xml'), :path => "#{member_path}.xml", :method => :get)
|
||||
assert_recognizes(options[:options].merge(:action => 'edit', :id => '1', :format => 'xml'), :path => formatted_edit_member_path, :method => :get)
|
||||
assert_recognizes(options[:options].merge(:action => 'update', :id => '1', :format => 'xml'), :path => "#{member_path}.xml", :method => :put)
|
||||
assert_recognizes(options[:options].merge(:action => 'destroy', :id => '1', :format => 'xml'), :path => "#{member_path}.xml", :method => :delete)
|
||||
|
||||
yield options[:options] if block_given?
|
||||
end
|
||||
|
||||
# test named routes like foo_path and foos_path map to the correct options.
|
||||
def assert_restful_named_routes_for(controller_name, singular_name = nil, options = {})
|
||||
if singular_name.is_a?(Hash)
|
||||
options = singular_name
|
||||
singular_name = nil
|
||||
end
|
||||
singular_name ||= controller_name.to_s.singularize
|
||||
(options[:options] ||= {})[:controller] = controller_name.to_s
|
||||
@controller = "#{controller_name.to_s.camelize}Controller".constantize.new
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
get :index, options[:options]
|
||||
options[:options].delete :action
|
||||
|
||||
full_prefix = "/#{options[:path_prefix]}#{controller_name}"
|
||||
name_prefix = options[:name_prefix]
|
||||
|
||||
assert_named_route "#{full_prefix}", "#{name_prefix}#{controller_name}_path", options[:options]
|
||||
assert_named_route "#{full_prefix}/new", "#{name_prefix}new_#{singular_name}_path", options[:options]
|
||||
assert_named_route "#{full_prefix}/1", "#{name_prefix}#{singular_name}_path", options[:options].merge(:id => '1')
|
||||
assert_named_route "#{full_prefix}/1;edit", "#{name_prefix}edit_#{singular_name}_path", options[:options].merge(:id => '1')
|
||||
assert_named_route "#{full_prefix}.xml", "formatted_#{name_prefix}#{controller_name}_path", options[:options].merge( :format => 'xml')
|
||||
assert_named_route "#{full_prefix}/new.xml", "formatted_#{name_prefix}new_#{singular_name}_path", options[:options].merge( :format => 'xml')
|
||||
assert_named_route "#{full_prefix}/1.xml", "formatted_#{name_prefix}#{singular_name}_path", options[:options].merge(:id => '1', :format => 'xml')
|
||||
assert_named_route "#{full_prefix}/1.xml;edit", "formatted_#{name_prefix}edit_#{singular_name}_path", options[:options].merge(:id => '1', :format => 'xml')
|
||||
yield options[:options] if block_given?
|
||||
end
|
||||
|
||||
def assert_singleton_routes_for(singleton_name, options = {})
|
||||
(options[:options] ||= {})[:controller] ||= singleton_name.to_s
|
||||
|
||||
full_path = "/#{options[:path_prefix]}#{singleton_name}"
|
||||
new_path = "#{full_path}/new"
|
||||
edit_path = "#{full_path};edit"
|
||||
formatted_edit_path = "#{full_path}.xml;edit"
|
||||
|
||||
with_options options[:options] do |controller|
|
||||
controller.assert_routing full_path, :action => 'show'
|
||||
controller.assert_routing new_path, :action => 'new'
|
||||
controller.assert_routing edit_path, :action => 'edit'
|
||||
controller.assert_routing "#{full_path}.xml", :action => 'show', :format => 'xml'
|
||||
controller.assert_routing "#{new_path}.xml", :action => 'new', :format => 'xml'
|
||||
controller.assert_routing formatted_edit_path, :action => 'edit', :format => 'xml'
|
||||
end
|
||||
|
||||
assert_recognizes(options[:options].merge(:action => 'show'), :path => full_path, :method => :get)
|
||||
assert_recognizes(options[:options].merge(:action => 'new'), :path => new_path, :method => :get)
|
||||
assert_recognizes(options[:options].merge(:action => 'edit'), :path => edit_path, :method => :get)
|
||||
assert_recognizes(options[:options].merge(:action => 'create'), :path => full_path, :method => :post)
|
||||
assert_recognizes(options[:options].merge(:action => 'update'), :path => full_path, :method => :put)
|
||||
assert_recognizes(options[:options].merge(:action => 'destroy'), :path => full_path, :method => :delete)
|
||||
|
||||
assert_recognizes(options[:options].merge(:action => 'show', :format => 'xml'), :path => "#{full_path}.xml", :method => :get)
|
||||
assert_recognizes(options[:options].merge(:action => 'new', :format => 'xml'), :path => "#{new_path}.xml", :method => :get)
|
||||
assert_recognizes(options[:options].merge(:action => 'edit', :format => 'xml'), :path => formatted_edit_path, :method => :get)
|
||||
assert_recognizes(options[:options].merge(:action => 'create', :format => 'xml'), :path => "#{full_path}.xml", :method => :post)
|
||||
assert_recognizes(options[:options].merge(:action => 'update', :format => 'xml'), :path => "#{full_path}.xml", :method => :put)
|
||||
assert_recognizes(options[:options].merge(:action => 'destroy', :format => 'xml'), :path => "#{full_path}.xml", :method => :delete)
|
||||
|
||||
yield options[:options] if block_given?
|
||||
end
|
||||
|
||||
def assert_singleton_named_routes_for(singleton_name, options = {})
|
||||
(options[:options] ||= {})[:controller] ||= singleton_name.to_s
|
||||
@controller = "#{options[:options][:controller].camelize}Controller".constantize.new
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
get :show, options[:options]
|
||||
options[:options].delete :action
|
||||
|
||||
full_path = "/#{options[:path_prefix]}#{singleton_name}"
|
||||
|
||||
assert_named_route "#{full_path}", "#{singleton_name}_path", options[:options]
|
||||
assert_named_route "#{full_path}/new", "new_#{singleton_name}_path", options[:options]
|
||||
assert_named_route "#{full_path};edit", "edit_#{singleton_name}_path", options[:options]
|
||||
assert_named_route "#{full_path}.xml", "formatted_#{singleton_name}_path", options[:options].merge(:format => 'xml')
|
||||
assert_named_route "#{full_path}/new.xml", "formatted_new_#{singleton_name}_path", options[:options].merge(:format => 'xml')
|
||||
assert_named_route "#{full_path}.xml;edit", "formatted_edit_#{singleton_name}_path", options[:options].merge(:format => 'xml')
|
||||
end
|
||||
|
||||
def assert_named_route(expected, route, options)
|
||||
actual = @controller.send(route, options) rescue $!.class.name
|
||||
assert_equal expected, actual, "Error on route: #{route}(#{options.inspect})"
|
||||
end
|
||||
|
||||
def assert_resource_methods(expected, resource, action_method, method)
|
||||
assert_equal expected.length, resource.send("#{action_method}_methods")[method].size, "#{resource.send("#{action_method}_methods")[method].inspect}"
|
||||
expected.each do |action|
|
||||
assert resource.send("#{action_method}_methods")[method].include?(action),
|
||||
"#{method} not in #{action_method} methods: #{resource.send("#{action_method}_methods")[method].inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
def distinct_routes? (r1, r2)
|
||||
if r1.conditions == r2.conditions and r1.requirements == r2.requirements then
|
||||
if r1.segments.collect(&:to_s) == r2.segments.collect(&:to_s) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
end
|
628
vendor/rails/actionpack/test/controller/selector_test.rb
vendored
Normal file
628
vendor/rails/actionpack/test/controller/selector_test.rb
vendored
Normal file
|
@ -0,0 +1,628 @@
|
|||
#--
|
||||
# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
|
||||
# Under MIT and/or CC By license.
|
||||
#++
|
||||
|
||||
require File.dirname(__FILE__) + '/../abstract_unit'
|
||||
require File.dirname(__FILE__) + '/fake_controllers'
|
||||
|
||||
class SelectorTest < Test::Unit::TestCase
|
||||
#
|
||||
# Basic selector: element, id, class, attributes.
|
||||
#
|
||||
|
||||
def test_element
|
||||
parse(%Q{<div id="1"></div><p></p><div id="2"></div>})
|
||||
# Match element by name.
|
||||
select("div")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
assert_equal "2", @matches[1].attributes["id"]
|
||||
# Not case sensitive.
|
||||
select("DIV")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
assert_equal "2", @matches[1].attributes["id"]
|
||||
# Universal match (all elements).
|
||||
select("*")
|
||||
assert_equal 3, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
assert_equal nil, @matches[1].attributes["id"]
|
||||
assert_equal "2", @matches[2].attributes["id"]
|
||||
end
|
||||
|
||||
|
||||
def test_identifier
|
||||
parse(%Q{<div id="1"></div><p></p><div id="2"></div>})
|
||||
# Match element by ID.
|
||||
select("div#1")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
# Match element by ID, substitute value.
|
||||
select("div#?", 2)
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
# Element name does not match ID.
|
||||
select("p#?", 2)
|
||||
assert_equal 0, @matches.size
|
||||
# Use regular expression.
|
||||
select("#?", /\d/)
|
||||
assert_equal 2, @matches.size
|
||||
end
|
||||
|
||||
|
||||
def test_class_name
|
||||
parse(%Q{<div id="1" class=" foo "></div><p id="2" class=" foo bar "></p><div id="3" class="bar"></div>})
|
||||
# Match element with specified class.
|
||||
select("div.foo")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
# Match any element with specified class.
|
||||
select("*.foo")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
assert_equal "2", @matches[1].attributes["id"]
|
||||
# Match elements with other class.
|
||||
select("*.bar")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
assert_equal "3", @matches[1].attributes["id"]
|
||||
# Match only element with both class names.
|
||||
select("*.bar.foo")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
end
|
||||
|
||||
|
||||
def test_attribute
|
||||
parse(%Q{<div id="1"></div><p id="2" title="" bar="foo"></p><div id="3" title="foo"></div>})
|
||||
# Match element with attribute.
|
||||
select("div[title]")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "3", @matches[0].attributes["id"]
|
||||
# Match any element with attribute.
|
||||
select("*[title]")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
assert_equal "3", @matches[1].attributes["id"]
|
||||
# Match alement with attribute value.
|
||||
select("*[title=foo]")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "3", @matches[0].attributes["id"]
|
||||
# Match alement with attribute and attribute value.
|
||||
select("[bar=foo][title]")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
# Not case sensitive.
|
||||
select("[BAR=foo][TiTle]")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
end
|
||||
|
||||
|
||||
def test_attribute_quoted
|
||||
parse(%Q{<div id="1" title="foo"></div><div id="2" title="bar"></div><div id="3" title=" bar "></div>})
|
||||
# Match without quotes.
|
||||
select("[title = bar]")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
# Match with single quotes.
|
||||
select("[title = 'bar' ]")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
# Match with double quotes.
|
||||
select("[title = \"bar\" ]")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
# Match with spaces.
|
||||
select("[title = \" bar \" ]")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "3", @matches[0].attributes["id"]
|
||||
end
|
||||
|
||||
|
||||
def test_attribute_equality
|
||||
parse(%Q{<div id="1" title="foo bar"></div><div id="2" title="barbaz"></div>})
|
||||
# Match (fail) complete value.
|
||||
select("[title=bar]")
|
||||
assert_equal 0, @matches.size
|
||||
# Match space-separate word.
|
||||
select("[title~=foo]")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
select("[title~=bar]")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
# Match beginning of value.
|
||||
select("[title^=ba]")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
# Match end of value.
|
||||
select("[title$=ar]")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
# Match text in value.
|
||||
select("[title*=bar]")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
assert_equal "2", @matches[1].attributes["id"]
|
||||
# Match first space separated word.
|
||||
select("[title|=foo]")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
select("[title|=bar]")
|
||||
assert_equal 0, @matches.size
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Selector composition: groups, sibling, children
|
||||
#
|
||||
|
||||
|
||||
def test_selector_group
|
||||
parse(%Q{<h1 id="1"></h1><h2 id="2"></h2><h3 id="3"></h3>})
|
||||
# Simple group selector.
|
||||
select("h1,h3")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
assert_equal "3", @matches[1].attributes["id"]
|
||||
select("h1 , h3")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
assert_equal "3", @matches[1].attributes["id"]
|
||||
# Complex group selector.
|
||||
parse(%Q{<h1 id="1"><a href="foo"></a></h1><h2 id="2"><a href="bar"></a></h2><h3 id="2"><a href="baz"></a></h3>})
|
||||
select("h1 a, h3 a")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "foo", @matches[0].attributes["href"]
|
||||
assert_equal "baz", @matches[1].attributes["href"]
|
||||
# And now for the three selector challange.
|
||||
parse(%Q{<h1 id="1"><a href="foo"></a></h1><h2 id="2"><a href="bar"></a></h2><h3 id="2"><a href="baz"></a></h3>})
|
||||
select("h1 a, h2 a, h3 a")
|
||||
assert_equal 3, @matches.size
|
||||
assert_equal "foo", @matches[0].attributes["href"]
|
||||
assert_equal "bar", @matches[1].attributes["href"]
|
||||
assert_equal "baz", @matches[2].attributes["href"]
|
||||
end
|
||||
|
||||
|
||||
def test_sibling_selector
|
||||
parse(%Q{<h1 id="1"></h1><h2 id="2"></h2><h3 id="3"></h3>})
|
||||
# Test next sibling.
|
||||
select("h1+*")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
select("h1+h2")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
select("h1+h3")
|
||||
assert_equal 0, @matches.size
|
||||
select("*+h3")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "3", @matches[0].attributes["id"]
|
||||
# Test any sibling.
|
||||
select("h1~*")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
assert_equal "3", @matches[1].attributes["id"]
|
||||
select("h2~*")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "3", @matches[0].attributes["id"]
|
||||
end
|
||||
|
||||
|
||||
def test_children_selector
|
||||
parse(%Q{<div><p id="1"><span id="2"></span></p></div><div><p id="3"><span id="4" class="foo"></span></p></div>})
|
||||
# Test child selector.
|
||||
select("div>p")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
assert_equal "3", @matches[1].attributes["id"]
|
||||
select("div>span")
|
||||
assert_equal 0, @matches.size
|
||||
select("div>p#3")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "3", @matches[0].attributes["id"]
|
||||
select("div>p>span")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
assert_equal "4", @matches[1].attributes["id"]
|
||||
# Test descendant selector.
|
||||
select("div p")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
assert_equal "3", @matches[1].attributes["id"]
|
||||
select("div span")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
assert_equal "4", @matches[1].attributes["id"]
|
||||
select("div *#3")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "3", @matches[0].attributes["id"]
|
||||
select("div *#4")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "4", @matches[0].attributes["id"]
|
||||
# This is here because it failed before when whitespaces
|
||||
# were not properly stripped.
|
||||
select("div .foo")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "4", @matches[0].attributes["id"]
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Pseudo selectors: root, nth-child, empty, content, etc
|
||||
#
|
||||
|
||||
|
||||
def test_root_selector
|
||||
parse(%Q{<div id="1"><div id="2"></div></div>})
|
||||
# Can only find element if it's root.
|
||||
select(":root")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
select("#1:root")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
select("#2:root")
|
||||
assert_equal 0, @matches.size
|
||||
# Opposite for nth-child.
|
||||
select("#1:nth-child(1)")
|
||||
assert_equal 0, @matches.size
|
||||
end
|
||||
|
||||
|
||||
def test_nth_child_odd_even
|
||||
parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
|
||||
# Test odd nth children.
|
||||
select("tr:nth-child(odd)")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
assert_equal "3", @matches[1].attributes["id"]
|
||||
# Test even nth children.
|
||||
select("tr:nth-child(even)")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
assert_equal "4", @matches[1].attributes["id"]
|
||||
end
|
||||
|
||||
|
||||
def test_nth_child_a_is_zero
|
||||
parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
|
||||
# Test the third child.
|
||||
select("tr:nth-child(0n+3)")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "3", @matches[0].attributes["id"]
|
||||
# Same but an can be omitted when zero.
|
||||
select("tr:nth-child(3)")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "3", @matches[0].attributes["id"]
|
||||
# Second element (but not every second element).
|
||||
select("tr:nth-child(0n+2)")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
# Before first and past last returns nothing.:
|
||||
assert_raises(ArgumentError) { select("tr:nth-child(-1)") }
|
||||
select("tr:nth-child(0)")
|
||||
assert_equal 0, @matches.size
|
||||
select("tr:nth-child(5)")
|
||||
assert_equal 0, @matches.size
|
||||
end
|
||||
|
||||
|
||||
def test_nth_child_a_is_one
|
||||
parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
|
||||
# a is group of one, pick every element in group.
|
||||
select("tr:nth-child(1n+0)")
|
||||
assert_equal 4, @matches.size
|
||||
# Same but a can be omitted when one.
|
||||
select("tr:nth-child(n+0)")
|
||||
assert_equal 4, @matches.size
|
||||
# Same but b can be omitted when zero.
|
||||
select("tr:nth-child(n)")
|
||||
assert_equal 4, @matches.size
|
||||
end
|
||||
|
||||
|
||||
def test_nth_child_b_is_zero
|
||||
parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
|
||||
# If b is zero, pick the n-th element (here each one).
|
||||
select("tr:nth-child(n+0)")
|
||||
assert_equal 4, @matches.size
|
||||
# If b is zero, pick the n-th element (here every second).
|
||||
select("tr:nth-child(2n+0)")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
assert_equal "3", @matches[1].attributes["id"]
|
||||
# If a and b are both zero, no element selected.
|
||||
select("tr:nth-child(0n+0)")
|
||||
assert_equal 0, @matches.size
|
||||
select("tr:nth-child(0)")
|
||||
assert_equal 0, @matches.size
|
||||
end
|
||||
|
||||
|
||||
def test_nth_child_a_is_negative
|
||||
parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
|
||||
# Since a is -1, picks the first three elements.
|
||||
select("tr:nth-child(-n+3)")
|
||||
assert_equal 3, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
assert_equal "2", @matches[1].attributes["id"]
|
||||
assert_equal "3", @matches[2].attributes["id"]
|
||||
# Since a is -2, picks the first in every second of first four elements.
|
||||
select("tr:nth-child(-2n+3)")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
assert_equal "3", @matches[1].attributes["id"]
|
||||
# Since a is -2, picks the first in every second of first three elements.
|
||||
select("tr:nth-child(-2n+2)")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
end
|
||||
|
||||
|
||||
def test_nth_child_b_is_negative
|
||||
parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
|
||||
# Select last of four.
|
||||
select("tr:nth-child(4n-1)")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "4", @matches[0].attributes["id"]
|
||||
# Select first of four.
|
||||
select("tr:nth-child(4n-4)")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
# Select last of every second.
|
||||
select("tr:nth-child(2n-1)")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
assert_equal "4", @matches[1].attributes["id"]
|
||||
# Select nothing since an+b always < 0
|
||||
select("tr:nth-child(-1n-1)")
|
||||
assert_equal 0, @matches.size
|
||||
end
|
||||
|
||||
|
||||
def test_nth_child_substitution_values
|
||||
parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
|
||||
# Test with ?n?.
|
||||
select("tr:nth-child(?n?)", 2, 1)
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
assert_equal "3", @matches[1].attributes["id"]
|
||||
select("tr:nth-child(?n?)", 2, 2)
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
assert_equal "4", @matches[1].attributes["id"]
|
||||
select("tr:nth-child(?n?)", 4, 2)
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
# Test with ? (b only).
|
||||
select("tr:nth-child(?)", 3)
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "3", @matches[0].attributes["id"]
|
||||
select("tr:nth-child(?)", 5)
|
||||
assert_equal 0, @matches.size
|
||||
end
|
||||
|
||||
|
||||
def test_nth_last_child
|
||||
parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
|
||||
# Last two elements.
|
||||
select("tr:nth-last-child(-n+2)")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "3", @matches[0].attributes["id"]
|
||||
assert_equal "4", @matches[1].attributes["id"]
|
||||
# All old elements counting from last one.
|
||||
select("tr:nth-last-child(odd)")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
assert_equal "4", @matches[1].attributes["id"]
|
||||
end
|
||||
|
||||
|
||||
def test_nth_of_type
|
||||
parse(%Q{<table><thead></thead><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
|
||||
# First two elements.
|
||||
select("tr:nth-of-type(-n+2)")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
assert_equal "2", @matches[1].attributes["id"]
|
||||
# All old elements counting from last one.
|
||||
select("tr:nth-last-of-type(odd)")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
assert_equal "4", @matches[1].attributes["id"]
|
||||
end
|
||||
|
||||
|
||||
def test_first_and_last
|
||||
parse(%Q{<table><thead></thead><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>})
|
||||
# First child.
|
||||
select("tr:first-child")
|
||||
assert_equal 0, @matches.size
|
||||
select(":first-child")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "thead", @matches[0].name
|
||||
# First of type.
|
||||
select("tr:first-of-type")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
select("thead:first-of-type")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "thead", @matches[0].name
|
||||
select("div:first-of-type")
|
||||
assert_equal 0, @matches.size
|
||||
# Last child.
|
||||
select("tr:last-child")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "4", @matches[0].attributes["id"]
|
||||
# Last of type.
|
||||
select("tr:last-of-type")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "4", @matches[0].attributes["id"]
|
||||
select("thead:last-of-type")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "thead", @matches[0].name
|
||||
select("div:last-of-type")
|
||||
assert_equal 0, @matches.size
|
||||
end
|
||||
|
||||
|
||||
def test_first_and_last
|
||||
# Only child.
|
||||
parse(%Q{<table><tr></tr></table>})
|
||||
select("table:only-child")
|
||||
assert_equal 0, @matches.size
|
||||
select("tr:only-child")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "tr", @matches[0].name
|
||||
parse(%Q{<table><tr></tr><tr></tr></table>})
|
||||
select("tr:only-child")
|
||||
assert_equal 0, @matches.size
|
||||
# Only of type.
|
||||
parse(%Q{<table><thead></thead><tr></tr><tr></tr></table>})
|
||||
select("thead:only-of-type")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "thead", @matches[0].name
|
||||
select("td:only-of-type")
|
||||
assert_equal 0, @matches.size
|
||||
end
|
||||
|
||||
|
||||
def test_empty
|
||||
parse(%Q{<table><tr></tr></table>})
|
||||
select("table:empty")
|
||||
assert_equal 0, @matches.size
|
||||
select("tr:empty")
|
||||
assert_equal 1, @matches.size
|
||||
parse(%Q{<div> </div>})
|
||||
select("div:empty")
|
||||
assert_equal 1, @matches.size
|
||||
end
|
||||
|
||||
|
||||
def test_content
|
||||
parse(%Q{<div> </div>})
|
||||
select("div:content()")
|
||||
assert_equal 1, @matches.size
|
||||
parse(%Q{<div>something </div>})
|
||||
select("div:content()")
|
||||
assert_equal 0, @matches.size
|
||||
select("div:content(something)")
|
||||
assert_equal 1, @matches.size
|
||||
select("div:content( 'something' )")
|
||||
assert_equal 1, @matches.size
|
||||
select("div:content( \"something\" )")
|
||||
assert_equal 1, @matches.size
|
||||
select("div:content(?)", "something")
|
||||
assert_equal 1, @matches.size
|
||||
select("div:content(?)", /something/)
|
||||
assert_equal 1, @matches.size
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Test negation.
|
||||
#
|
||||
|
||||
|
||||
def test_element_negation
|
||||
parse(%Q{<p></p><div></div>})
|
||||
select("*")
|
||||
assert_equal 2, @matches.size
|
||||
select("*:not(p)")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "div", @matches[0].name
|
||||
select("*:not(div)")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "p", @matches[0].name
|
||||
select("*:not(span)")
|
||||
assert_equal 2, @matches.size
|
||||
end
|
||||
|
||||
|
||||
def test_id_negation
|
||||
parse(%Q{<p id="1"></p><p id="2"></p>})
|
||||
select("p")
|
||||
assert_equal 2, @matches.size
|
||||
select(":not(#1)")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
select(":not(#2)")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
end
|
||||
|
||||
|
||||
def test_class_name_negation
|
||||
parse(%Q{<p class="foo"></p><p class="bar"></p>})
|
||||
select("p")
|
||||
assert_equal 2, @matches.size
|
||||
select(":not(.foo)")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "bar", @matches[0].attributes["class"]
|
||||
select(":not(.bar)")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "foo", @matches[0].attributes["class"]
|
||||
end
|
||||
|
||||
|
||||
def test_attribute_negation
|
||||
parse(%Q{<p title="foo"></p><p title="bar"></p>})
|
||||
select("p")
|
||||
assert_equal 2, @matches.size
|
||||
select(":not([title=foo])")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "bar", @matches[0].attributes["title"]
|
||||
select(":not([title=bar])")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "foo", @matches[0].attributes["title"]
|
||||
end
|
||||
|
||||
|
||||
def test_pseudo_class_negation
|
||||
parse(%Q{<div><p id="1"></p><p id="2"></p></div>})
|
||||
select("p")
|
||||
assert_equal 2, @matches.size
|
||||
select("p:not(:first-child)")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
select("p:not(:nth-child(2))")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
end
|
||||
|
||||
|
||||
def test_negation_details
|
||||
parse(%Q{<p id="1"></p><p id="2"></p><p id="3"></p>})
|
||||
assert_raises(ArgumentError) { select(":not(") }
|
||||
assert_raises(ArgumentError) { select(":not(:not())") }
|
||||
select("p:not(#1):not(#3)")
|
||||
assert_equal 1, @matches.size
|
||||
assert_equal "2", @matches[0].attributes["id"]
|
||||
end
|
||||
|
||||
|
||||
def test_select_from_element
|
||||
parse(%Q{<div><p id="1"></p><p id="2"></p></div>})
|
||||
select("div")
|
||||
@matches = @matches[0].select("p")
|
||||
assert_equal 2, @matches.size
|
||||
assert_equal "1", @matches[0].attributes["id"]
|
||||
assert_equal "2", @matches[1].attributes["id"]
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
|
||||
def parse(html)
|
||||
@html = HTML::Document.new(html).root
|
||||
end
|
||||
|
||||
def select(*selector)
|
||||
@matches = HTML.selector(*selector).select(@html)
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
<hello>world</hello>
|
1
vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rhtml.rhtml
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rhtml.rhtml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<%= 'hello world!' %>
|
1
vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rjs.rjs
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rjs.rjs
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
page.alert 'hello world!'
|
1
vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rxml.rxml
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rxml.rxml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
xml.p "Hello world!"
|
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<%= @cookies[:test] %>
|
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<%= cookies[:test] %>
|
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<%= @flash[:test] %>
|
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_flash_method.rhtml
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_flash_method.rhtml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<%= flash[:test] %>
|
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<%= @headers[:test] %>
|
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_headers_method.rhtml
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_headers_method.rhtml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<%= headers[:test] %>
|
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<%= @params[:test] %>
|
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_params_method.rhtml
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_params_method.rhtml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<%= params[:test] %>
|
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<%= @request.method %>
|
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_request_method.rhtml
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_request_method.rhtml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<%= request.method %>
|
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<%= @response.body %>
|
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_response_method.rhtml
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_response_method.rhtml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<%= response.body %>
|
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<%= @session[:test] %>
|
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_session_method.rhtml
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_session_method.rhtml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<%= session[:test] %>
|
1
vendor/rails/actionpack/test/fixtures/public/javascripts/application.js
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/public/javascripts/application.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
# Test file for javascript_include_tag
|
1
vendor/rails/actionpack/test/fixtures/test/_hello.rxml
vendored
Normal file
1
vendor/rails/actionpack/test/fixtures/test/_hello.rxml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
xm.hello
|
3
vendor/rails/actionpack/test/fixtures/test/hello_world_container.rxml
vendored
Normal file
3
vendor/rails/actionpack/test/fixtures/test/hello_world_container.rxml
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
xml.test do
|
||||
render :partial => 'hello', :locals => { :xm => xml }
|
||||
end
|
36
vendor/rails/actionpack/test/template/deprecated_helper_test.rb
vendored
Normal file
36
vendor/rails/actionpack/test/template/deprecated_helper_test.rb
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
require File.dirname(__FILE__) + '/../abstract_unit'
|
||||
|
||||
class DeprecatedHelperTest < Test::Unit::TestCase
|
||||
include ActionView::Helpers::JavaScriptHelper
|
||||
include ActionView::Helpers::CaptureHelper
|
||||
|
||||
def test_update_element_function
|
||||
assert_deprecated 'update_element_function' do
|
||||
|
||||
assert_equal %($('myelement').innerHTML = 'blub';\n),
|
||||
update_element_function('myelement', :content => 'blub')
|
||||
assert_equal %($('myelement').innerHTML = 'blub';\n),
|
||||
update_element_function('myelement', :action => :update, :content => 'blub')
|
||||
assert_equal %($('myelement').innerHTML = '';\n),
|
||||
update_element_function('myelement', :action => :empty)
|
||||
assert_equal %(Element.remove('myelement');\n),
|
||||
update_element_function('myelement', :action => :remove)
|
||||
|
||||
assert_equal %(new Insertion.Bottom('myelement','blub');\n),
|
||||
update_element_function('myelement', :position => 'bottom', :content => 'blub')
|
||||
assert_equal %(new Insertion.Bottom('myelement','blub');\n),
|
||||
update_element_function('myelement', :action => :update, :position => :bottom, :content => 'blub')
|
||||
|
||||
_erbout = ""
|
||||
assert_equal %($('myelement').innerHTML = 'test';\n),
|
||||
update_element_function('myelement') { _erbout << "test" }
|
||||
|
||||
_erbout = ""
|
||||
assert_equal %($('myelement').innerHTML = 'blockstuff';\n),
|
||||
update_element_function('myelement', :content => 'paramstuff') { _erbout << "blockstuff" }
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
43
vendor/rails/actionpack/test/template/deprecated_instance_variables_test.rb
vendored
Normal file
43
vendor/rails/actionpack/test/template/deprecated_instance_variables_test.rb
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
require File.dirname(__FILE__) + '/../abstract_unit'
|
||||
|
||||
class DeprecatedViewInstanceVariablesTest < Test::Unit::TestCase
|
||||
class DeprecatedInstanceVariablesController < ActionController::Base
|
||||
self.template_root = "#{File.dirname(__FILE__)}/../fixtures/"
|
||||
|
||||
def self.controller_path; 'deprecated_instance_variables' end
|
||||
|
||||
ActionController::Base::DEPRECATED_INSTANCE_VARIABLES.each do |var|
|
||||
class_eval <<-end_eval
|
||||
def old_#{var}_inline; render :inline => '<%= @#{var}.to_s %>' end
|
||||
def new_#{var}_inline; render :inline => '<%= #{var}.to_s %>' end
|
||||
def old_#{var}_partial; render :partial => '#{var}_ivar' end
|
||||
def new_#{var}_partial; render :partial => '#{var}_method' end
|
||||
end_eval
|
||||
end
|
||||
|
||||
def rescue_action(e) raise e end
|
||||
end
|
||||
|
||||
def setup
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
@controller = DeprecatedInstanceVariablesController.new
|
||||
end
|
||||
|
||||
ActionController::Base::DEPRECATED_INSTANCE_VARIABLES.each do |var|
|
||||
class_eval <<-end_eval, __FILE__, __LINE__
|
||||
def test_old_#{var}_is_deprecated
|
||||
assert_deprecated('@#{var}') { get :old_#{var}_inline }
|
||||
end
|
||||
def test_new_#{var}_isnt_deprecated
|
||||
assert_not_deprecated { get :new_#{var}_inline }
|
||||
end
|
||||
def test_old_#{var}_partial_is_deprecated
|
||||
assert_deprecated('@#{var}') { get :old_#{var}_partial }
|
||||
end
|
||||
def test_new_#{var}_partial_isnt_deprecated
|
||||
assert_not_deprecated { get :new_#{var}_partial }
|
||||
end
|
||||
end_eval
|
||||
end
|
||||
end
|
75
vendor/rails/activerecord/lib/active_record/attribute_methods.rb
vendored
Normal file
75
vendor/rails/activerecord/lib/active_record/attribute_methods.rb
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
module ActiveRecord
|
||||
module AttributeMethods #:nodoc:
|
||||
DEFAULT_SUFFIXES = %w(= ? _before_type_cast)
|
||||
|
||||
def self.included(base)
|
||||
base.extend ClassMethods
|
||||
base.attribute_method_suffix *DEFAULT_SUFFIXES
|
||||
end
|
||||
|
||||
# Declare and check for suffixed attribute methods.
|
||||
module ClassMethods
|
||||
# Declare a method available for all attributes with the given suffix.
|
||||
# Uses method_missing and respond_to? to rewrite the method
|
||||
# #{attr}#{suffix}(*args, &block)
|
||||
# to
|
||||
# attribute#{suffix}(#{attr}, *args, &block)
|
||||
#
|
||||
# An attribute#{suffix} instance method must exist and accept at least
|
||||
# the attr argument.
|
||||
#
|
||||
# For example:
|
||||
# class Person < ActiveRecord::Base
|
||||
# attribute_method_suffix '_changed?'
|
||||
#
|
||||
# private
|
||||
# def attribute_changed?(attr)
|
||||
# ...
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# person = Person.find(1)
|
||||
# person.name_changed? # => false
|
||||
# person.name = 'Hubert'
|
||||
# person.name_changed? # => true
|
||||
def attribute_method_suffix(*suffixes)
|
||||
attribute_method_suffixes.concat suffixes
|
||||
rebuild_attribute_method_regexp
|
||||
end
|
||||
|
||||
# Returns MatchData if method_name is an attribute method.
|
||||
def match_attribute_method?(method_name)
|
||||
rebuild_attribute_method_regexp unless defined?(@@attribute_method_regexp) && @@attribute_method_regexp
|
||||
@@attribute_method_regexp.match(method_name)
|
||||
end
|
||||
|
||||
private
|
||||
# Suffixes a, ?, c become regexp /(a|\?|c)$/
|
||||
def rebuild_attribute_method_regexp
|
||||
suffixes = attribute_method_suffixes.map { |s| Regexp.escape(s) }
|
||||
@@attribute_method_regexp = /(#{suffixes.join('|')})$/.freeze
|
||||
end
|
||||
|
||||
# Default to =, ?, _before_type_cast
|
||||
def attribute_method_suffixes
|
||||
@@attribute_method_suffixes ||= []
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
# Handle *? for method_missing.
|
||||
def attribute?(attribute_name)
|
||||
query_attribute(attribute_name)
|
||||
end
|
||||
|
||||
# Handle *= for method_missing.
|
||||
def attribute=(attribute_name, value)
|
||||
write_attribute(attribute_name, value)
|
||||
end
|
||||
|
||||
# Handle *_before_type_cast for method_missing.
|
||||
def attribute_before_type_cast(attribute_name)
|
||||
read_attribute_before_type_cast(attribute_name)
|
||||
end
|
||||
end
|
||||
end
|
861
vendor/rails/activerecord/lib/active_record/connection_adapters/frontbase_adapter.rb
vendored
Normal file
861
vendor/rails/activerecord/lib/active_record/connection_adapters/frontbase_adapter.rb
vendored
Normal file
|
@ -0,0 +1,861 @@
|
|||
# Requires FrontBase Ruby bindings (gem install ruby-frontbase)
|
||||
|
||||
require 'active_record/connection_adapters/abstract_adapter'
|
||||
|
||||
FB_TRACE = false
|
||||
|
||||
module ActiveRecord
|
||||
|
||||
class Base
|
||||
class << self
|
||||
# Establishes a connection to the database that's used by all Active Record objects.
|
||||
def frontbase_connection(config) # :nodoc:
|
||||
# FrontBase only supports one unnamed sequence per table
|
||||
define_attr_method(:set_sequence_name, :sequence_name, &Proc.new {|*args| nil})
|
||||
|
||||
config = config.symbolize_keys
|
||||
database = config[:database]
|
||||
port = config[:port]
|
||||
host = config[:host]
|
||||
username = config[:username]
|
||||
password = config[:password]
|
||||
dbpassword = config[:dbpassword]
|
||||
session_name = config[:session_name]
|
||||
|
||||
dbpassword = '' if dbpassword.nil?
|
||||
|
||||
# Turn off colorization since it makes tail/less output difficult
|
||||
self.colorize_logging = false
|
||||
|
||||
require_library_or_gem 'frontbase' unless self.class.const_defined? :FBSQL_Connect
|
||||
|
||||
# Check bindings version
|
||||
version = "0.0.0"
|
||||
version = FBSQL_Connect::FB_BINDINGS_VERSION if defined? FBSQL_Connect::FB_BINDINGS_VERSION
|
||||
|
||||
if ActiveRecord::ConnectionAdapters::FrontBaseAdapter.compare_versions(version,"1.0.0") == -1
|
||||
raise AdapterNotFound,
|
||||
'The FrontBase adapter requires ruby-frontbase version 1.0.0 or greater; you appear ' <<
|
||||
"to be running an older version (#{version}) -- please update ruby-frontbase (gem install ruby-frontbase)."
|
||||
end
|
||||
connection = FBSQL_Connect.connect(host, port, database, username, password, dbpassword, session_name)
|
||||
ConnectionAdapters::FrontBaseAdapter.new(connection, logger, [host, port, database, username, password, dbpassword, session_name], config)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ConnectionAdapters
|
||||
|
||||
# From EOF Documentation....
|
||||
# buffer should have space for EOUniqueBinaryKeyLength (12) bytes.
|
||||
# Assigns a world-wide unique ID made up of:
|
||||
# < Sequence [2], ProcessID [2] , Time [4], IP Addr [4] >
|
||||
|
||||
class TwelveByteKey < String #:nodoc:
|
||||
@@mutex = Mutex.new
|
||||
@@sequence_number = rand(65536)
|
||||
@@key_cached_pid_component = nil
|
||||
@@key_cached_ip_component = nil
|
||||
|
||||
def initialize(string = nil)
|
||||
# Generate a unique key
|
||||
if string.nil?
|
||||
new_key = replace('_' * 12)
|
||||
|
||||
new_key[0..1] = self.class.key_sequence_component
|
||||
new_key[2..3] = self.class.key_pid_component
|
||||
new_key[4..7] = self.class.key_time_component
|
||||
new_key[8..11] = self.class.key_ip_component
|
||||
new_key
|
||||
else
|
||||
if string.size == 24
|
||||
string.gsub!(/[[:xdigit:]]{2}/) { |x| x.hex.chr }
|
||||
end
|
||||
raise "string is not 12 bytes long" unless string.size == 12
|
||||
super(string)
|
||||
end
|
||||
end
|
||||
|
||||
def inspect
|
||||
unpack("H*").first.upcase
|
||||
end
|
||||
|
||||
alias_method :to_s, :inspect
|
||||
|
||||
private
|
||||
|
||||
class << self
|
||||
def key_sequence_component
|
||||
seq = nil
|
||||
@@mutex.synchronize do
|
||||
seq = @@sequence_number
|
||||
@@sequence_number = (@@sequence_number + 1) % 65536
|
||||
end
|
||||
|
||||
sequence_component = "__"
|
||||
sequence_component[0] = seq >> 8
|
||||
sequence_component[1] = seq
|
||||
sequence_component
|
||||
end
|
||||
|
||||
def key_pid_component
|
||||
if @@key_cached_pid_component.nil?
|
||||
@@mutex.synchronize do
|
||||
pid = $$
|
||||
pid_component = "__"
|
||||
pid_component[0] = pid >> 8
|
||||
pid_component[1] = pid
|
||||
@@key_cached_pid_component = pid_component
|
||||
end
|
||||
end
|
||||
@@key_cached_pid_component
|
||||
end
|
||||
|
||||
def key_time_component
|
||||
time = Time.new.to_i
|
||||
time_component = "____"
|
||||
time_component[0] = (time & 0xFF000000) >> 24
|
||||
time_component[1] = (time & 0x00FF0000) >> 16
|
||||
time_component[2] = (time & 0x0000FF00) >> 8
|
||||
time_component[3] = (time & 0x000000FF)
|
||||
time_component
|
||||
end
|
||||
|
||||
def key_ip_component
|
||||
if @@key_cached_ip_component.nil?
|
||||
@@mutex.synchronize do
|
||||
old_lookup_flag = BasicSocket.do_not_reverse_lookup
|
||||
BasicSocket.do_not_reverse_lookup = true
|
||||
udpsocket = UDPSocket.new
|
||||
udpsocket.connect("17.112.152.32",1)
|
||||
ip_string = udpsocket.addr[3]
|
||||
BasicSocket.do_not_reverse_lookup = old_lookup_flag
|
||||
packed = Socket.pack_sockaddr_in(0,ip_string)
|
||||
addr_subset = packed[4..7]
|
||||
ip = addr_subset[0] << 24 | addr_subset[1] << 16 | addr_subset[2] << 8 | addr_subset[3]
|
||||
ip_component = "____"
|
||||
ip_component[0] = (ip & 0xFF000000) >> 24
|
||||
ip_component[1] = (ip & 0x00FF0000) >> 16
|
||||
ip_component[2] = (ip & 0x0000FF00) >> 8
|
||||
ip_component[3] = (ip & 0x000000FF)
|
||||
@@key_cached_ip_component = ip_component
|
||||
end
|
||||
end
|
||||
@@key_cached_ip_component
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class FrontBaseColumn < Column #:nodoc:
|
||||
attr_reader :fb_autogen
|
||||
|
||||
def initialize(base, name, type, typename, limit, precision, scale, default, nullable)
|
||||
|
||||
@base = base
|
||||
@name = name
|
||||
@type = simplified_type(type,typename,limit)
|
||||
@limit = limit
|
||||
@precision = precision
|
||||
@scale = scale
|
||||
@default = default
|
||||
@null = nullable == "YES"
|
||||
@text = [:string, :text].include? @type
|
||||
@number = [:float, :integer, :decimal].include? @type
|
||||
@fb_autogen = false
|
||||
|
||||
if @default
|
||||
@default.gsub!(/^'(.*)'$/,'\1') if @text
|
||||
@fb_autogen = @default.include?("SELECT UNIQUE FROM")
|
||||
case @type
|
||||
when :boolean
|
||||
@default = @default == "TRUE"
|
||||
when :binary
|
||||
if @default != "X''"
|
||||
buffer = ""
|
||||
@default.scan(/../) { |h| buffer << h.hex.chr }
|
||||
@default = buffer
|
||||
else
|
||||
@default = ""
|
||||
end
|
||||
else
|
||||
@default = type_cast(@default)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Casts value (which is a String) to an appropriate instance.
|
||||
def type_cast(value)
|
||||
if type == :twelvebytekey
|
||||
ActiveRecord::ConnectionAdapters::TwelveByteKey.new(value)
|
||||
else
|
||||
super(value)
|
||||
end
|
||||
end
|
||||
|
||||
def type_cast_code(var_name)
|
||||
if type == :twelvebytekey
|
||||
"ActiveRecord::ConnectionAdapters::TwelveByteKey.new(#{var_name})"
|
||||
else
|
||||
super(var_name)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def simplified_type(field_type, type_name,limit)
|
||||
ret_type = :string
|
||||
puts "typecode: [#{field_type}] [#{type_name}]" if FB_TRACE
|
||||
|
||||
# 12 byte primary keys are a special case that Apple's EOF
|
||||
# used heavily. Optimize for this case
|
||||
if field_type == 11 && limit == 96
|
||||
ret_type = :twelvebytekey # BIT(96)
|
||||
else
|
||||
ret_type = case field_type
|
||||
when 1 then :boolean # BOOLEAN
|
||||
when 2 then :integer # INTEGER
|
||||
when 4 then :float # FLOAT
|
||||
when 10 then :string # CHARACTER VARYING
|
||||
when 11 then :bitfield # BIT
|
||||
when 13 then :date # DATE
|
||||
when 14 then :time # TIME
|
||||
when 16 then :timestamp # TIMESTAMP
|
||||
when 20 then :text # CLOB
|
||||
when 21 then :binary # BLOB
|
||||
when 22 then :integer # TINYINT
|
||||
else
|
||||
puts "ERROR: Unknown typecode: [#{field_type}] [#{type_name}]"
|
||||
end
|
||||
end
|
||||
puts "ret_type: #{ret_type.inspect}" if FB_TRACE
|
||||
ret_type
|
||||
end
|
||||
end
|
||||
|
||||
class FrontBaseAdapter < AbstractAdapter
|
||||
|
||||
class << self
|
||||
def compare_versions(v1, v2)
|
||||
v1_seg = v1.split(".")
|
||||
v2_seg = v2.split(".")
|
||||
0.upto([v1_seg.length,v2_seg.length].min) do |i|
|
||||
step = (v1_seg[i].to_i <=> v2_seg[i].to_i)
|
||||
return step unless step == 0
|
||||
end
|
||||
return v1_seg.length <=> v2_seg.length
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(connection, logger, connection_options, config)
|
||||
super(connection, logger)
|
||||
@connection_options, @config = connection_options, config
|
||||
@transaction_mode = :pessimistic
|
||||
|
||||
# Start out in auto-commit mode
|
||||
self.rollback_db_transaction
|
||||
|
||||
# threaded_connections_test.rb will fail unless we set the session
|
||||
# to optimistic locking mode
|
||||
# set_pessimistic_transactions
|
||||
# execute "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, LOCKING OPTIMISTIC"
|
||||
end
|
||||
|
||||
# Returns the human-readable name of the adapter. Use mixed case - one
|
||||
# can always use downcase if needed.
|
||||
def adapter_name #:nodoc:
|
||||
'FrontBase'
|
||||
end
|
||||
|
||||
# Does this adapter support migrations? Backend specific, as the
|
||||
# abstract adapter always returns +false+.
|
||||
def supports_migrations? #:nodoc:
|
||||
true
|
||||
end
|
||||
|
||||
def native_database_types #:nodoc:
|
||||
{
|
||||
:primary_key => "INTEGER DEFAULT UNIQUE PRIMARY KEY",
|
||||
:string => { :name => "VARCHAR", :limit => 255 },
|
||||
:text => { :name => "CLOB" },
|
||||
:integer => { :name => "INTEGER" },
|
||||
:float => { :name => "FLOAT" },
|
||||
:decimal => { :name => "DECIMAL" },
|
||||
:datetime => { :name => "TIMESTAMP" },
|
||||
:timestamp => { :name => "TIMESTAMP" },
|
||||
:time => { :name => "TIME" },
|
||||
:date => { :name => "DATE" },
|
||||
:binary => { :name => "BLOB" },
|
||||
:boolean => { :name => "BOOLEAN" },
|
||||
:twelvebytekey => { :name => "BYTE", :limit => 12}
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
# QUOTING ==================================================
|
||||
|
||||
# Quotes the column value to help prevent
|
||||
# {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
|
||||
def quote(value, column = nil)
|
||||
return value.quoted_id if value.respond_to?(:quoted_id)
|
||||
|
||||
retvalue = "<INVALID>"
|
||||
|
||||
puts "quote(#{value.inspect}(#{value.class}),#{column.type.inspect})" if FB_TRACE
|
||||
# If a column was passed in, use column type information
|
||||
unless value.nil?
|
||||
if column
|
||||
retvalue = case column.type
|
||||
when :string
|
||||
if value.kind_of?(String)
|
||||
"'#{quote_string(value.to_s)}'" # ' (for ruby-mode)
|
||||
else
|
||||
"'#{quote_string(value.to_yaml)}'"
|
||||
end
|
||||
when :integer
|
||||
if value.kind_of?(TrueClass)
|
||||
'1'
|
||||
elsif value.kind_of?(FalseClass)
|
||||
'0'
|
||||
else
|
||||
value.to_i.to_s
|
||||
end
|
||||
when :float
|
||||
value.to_f.to_s
|
||||
when :decimal
|
||||
value.to_d.to_s("F")
|
||||
when :datetime, :timestamp
|
||||
"TIMESTAMP '#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
|
||||
when :time
|
||||
"TIME '#{value.strftime("%H:%M:%S")}'"
|
||||
when :date
|
||||
"DATE '#{value.strftime("%Y-%m-%d")}'"
|
||||
when :twelvebytekey
|
||||
value = value.to_s.unpack("H*").first unless value.kind_of?(TwelveByteKey)
|
||||
"X'#{value.to_s}'"
|
||||
when :boolean
|
||||
value = quoted_true if value.kind_of?(TrueClass)
|
||||
value = quoted_false if value.kind_of?(FalseClass)
|
||||
value
|
||||
when :binary
|
||||
blob_handle = @connection.create_blob(value.to_s)
|
||||
puts "SQL -> Insert #{value.to_s.length} byte blob as #{retvalue}" if FB_TRACE
|
||||
blob_handle.handle
|
||||
when :text
|
||||
if value.kind_of?(String)
|
||||
clobdata = value.to_s # ' (for ruby-mode)
|
||||
else
|
||||
clobdata = value.to_yaml
|
||||
end
|
||||
clob_handle = @connection.create_clob(clobdata)
|
||||
puts "SQL -> Insert #{value.to_s.length} byte clob as #{retvalue}" if FB_TRACE
|
||||
clob_handle.handle
|
||||
else
|
||||
raise "*** UNKNOWN TYPE: #{column.type.inspect}"
|
||||
end # case
|
||||
# Since we don't have column type info, make a best guess based
|
||||
# on the Ruby class of the value
|
||||
else
|
||||
retvalue = case value
|
||||
when ActiveRecord::ConnectionAdapters::TwelveByteKey
|
||||
s = value.unpack("H*").first
|
||||
"X'#{s}'"
|
||||
when String
|
||||
if column && column.type == :binary
|
||||
s = value.unpack("H*").first
|
||||
"X'#{s}'"
|
||||
elsif column && [:integer, :float, :decimal].include?(column.type)
|
||||
value.to_s
|
||||
else
|
||||
"'#{quote_string(value)}'" # ' (for ruby-mode)
|
||||
end
|
||||
when NilClass
|
||||
"NULL"
|
||||
when TrueClass
|
||||
(column && column.type == :integer ? '1' : quoted_true)
|
||||
when FalseClass
|
||||
(column && column.type == :integer ? '0' : quoted_false)
|
||||
when Float, Fixnum, Bignum, BigDecimal
|
||||
value.to_s
|
||||
when Time, Date, DateTime
|
||||
if column
|
||||
case column.type
|
||||
when :date
|
||||
"DATE '#{value.strftime("%Y-%m-%d")}'"
|
||||
when :time
|
||||
"TIME '#{value.strftime("%H:%M:%S")}'"
|
||||
when :timestamp
|
||||
"TIMESTAMP '#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
|
||||
else
|
||||
raise NotImplementedError, "Unknown column type!"
|
||||
end # case
|
||||
else # Column wasn't passed in, so try to guess the right type
|
||||
if value.kind_of? Date
|
||||
"DATE '#{value.strftime("%Y-%m-%d")}'"
|
||||
else
|
||||
if [:hour, :min, :sec].all? {|part| value.send(:part).zero? }
|
||||
"TIME '#{value.strftime("%H:%M:%S")}'"
|
||||
else
|
||||
"TIMESTAMP '#{quoted_date(value)}'"
|
||||
end
|
||||
end
|
||||
end #if column
|
||||
else
|
||||
"'#{quote_string(value.to_yaml)}'"
|
||||
end #case
|
||||
end
|
||||
else
|
||||
retvalue = "NULL"
|
||||
end
|
||||
|
||||
retvalue
|
||||
end # def
|
||||
|
||||
# Quotes a string, escaping any ' (single quote) characters.
|
||||
def quote_string(s)
|
||||
s.gsub(/'/, "''") # ' (for ruby-mode)
|
||||
end
|
||||
|
||||
def quote_column_name(name) #:nodoc:
|
||||
%( "#{name}" )
|
||||
end
|
||||
|
||||
def quoted_true
|
||||
"true"
|
||||
end
|
||||
|
||||
def quoted_false
|
||||
"false"
|
||||
end
|
||||
|
||||
|
||||
# CONNECTION MANAGEMENT ====================================
|
||||
|
||||
def active?
|
||||
true if @connection.status == 1
|
||||
rescue => e
|
||||
false
|
||||
end
|
||||
|
||||
def reconnect!
|
||||
@connection.close rescue nil
|
||||
@connection = FBSQL_Connect.connect(*@connection_options.first(7))
|
||||
end
|
||||
|
||||
# Close this connection
|
||||
def disconnect!
|
||||
@connection.close rescue nil
|
||||
@active = false
|
||||
end
|
||||
|
||||
# DATABASE STATEMENTS ======================================
|
||||
|
||||
# Returns an array of record hashes with the column names as keys and
|
||||
# column values as values.
|
||||
def select_all(sql, name = nil) #:nodoc:
|
||||
fbsql = cleanup_fb_sql(sql)
|
||||
return_value = []
|
||||
fbresult = execute(sql, name)
|
||||
puts "select_all SQL -> #{fbsql}" if FB_TRACE
|
||||
columns = fbresult.columns
|
||||
|
||||
fbresult.each do |row|
|
||||
puts "SQL <- #{row.inspect}" if FB_TRACE
|
||||
hashed_row = {}
|
||||
colnum = 0
|
||||
row.each do |col|
|
||||
hashed_row[columns[colnum]] = col
|
||||
if col.kind_of?(FBSQL_LOB)
|
||||
hashed_row[columns[colnum]] = col.read
|
||||
end
|
||||
colnum += 1
|
||||
end
|
||||
puts "raw row: #{hashed_row.inspect}" if FB_TRACE
|
||||
return_value << hashed_row
|
||||
end
|
||||
return_value
|
||||
end
|
||||
|
||||
def select_one(sql, name = nil) #:nodoc:
|
||||
fbsql = cleanup_fb_sql(sql)
|
||||
return_value = []
|
||||
fbresult = execute(fbsql, name)
|
||||
puts "SQL -> #{fbsql}" if FB_TRACE
|
||||
columns = fbresult.columns
|
||||
|
||||
fbresult.each do |row|
|
||||
puts "SQL <- #{row.inspect}" if FB_TRACE
|
||||
hashed_row = {}
|
||||
colnum = 0
|
||||
row.each do |col|
|
||||
hashed_row[columns[colnum]] = col
|
||||
if col.kind_of?(FBSQL_LOB)
|
||||
hashed_row[columns[colnum]] = col.read
|
||||
end
|
||||
colnum += 1
|
||||
end
|
||||
return_value << hashed_row
|
||||
break
|
||||
end
|
||||
fbresult.clear
|
||||
return_value.first
|
||||
end
|
||||
|
||||
def query(sql, name = nil) #:nodoc:
|
||||
fbsql = cleanup_fb_sql(sql)
|
||||
puts "SQL(query) -> #{fbsql}" if FB_TRACE
|
||||
log(fbsql, name) { @connection.query(fbsql) }
|
||||
rescue => e
|
||||
puts "FB Exception: #{e.inspect}" if FB_TRACE
|
||||
raise e
|
||||
end
|
||||
|
||||
def execute(sql, name = nil) #:nodoc:
|
||||
fbsql = cleanup_fb_sql(sql)
|
||||
puts "SQL(execute) -> #{fbsql}" if FB_TRACE
|
||||
log(fbsql, name) { @connection.query(fbsql) }
|
||||
rescue ActiveRecord::StatementInvalid => e
|
||||
if e.message.scan(/Table name - \w* - exists/).empty?
|
||||
puts "FB Exception: #{e.inspect}" if FB_TRACE
|
||||
raise e
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the last auto-generated ID from the affected table.
|
||||
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
|
||||
puts "SQL -> #{sql.inspect}" if FB_TRACE
|
||||
execute(sql, name)
|
||||
id_value || pk
|
||||
end
|
||||
|
||||
# Executes the update statement and returns the number of rows affected.
|
||||
def update(sql, name = nil) #:nodoc:
|
||||
puts "SQL -> #{sql.inspect}" if FB_TRACE
|
||||
execute(sql, name).num_rows
|
||||
end
|
||||
|
||||
alias_method :delete, :update #:nodoc:
|
||||
|
||||
def set_pessimistic_transactions
|
||||
if @transaction_mode == :optimistic
|
||||
execute "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, LOCKING PESSIMISTIC, READ WRITE"
|
||||
@transaction_mode = :pessimistic
|
||||
end
|
||||
end
|
||||
|
||||
def set_optimistic_transactions
|
||||
if @transaction_mode == :pessimistic
|
||||
execute "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, LOCKING OPTIMISTIC"
|
||||
@transaction_mode = :optimistic
|
||||
end
|
||||
end
|
||||
|
||||
def begin_db_transaction #:nodoc:
|
||||
execute "SET COMMIT FALSE" rescue nil
|
||||
end
|
||||
|
||||
def commit_db_transaction #:nodoc:
|
||||
execute "COMMIT"
|
||||
ensure
|
||||
execute "SET COMMIT TRUE"
|
||||
end
|
||||
|
||||
def rollback_db_transaction #:nodoc:
|
||||
execute "ROLLBACK"
|
||||
ensure
|
||||
execute "SET COMMIT TRUE"
|
||||
end
|
||||
|
||||
def add_limit_offset!(sql, options) #:nodoc:
|
||||
if limit = options[:limit]
|
||||
offset = options[:offset] || 0
|
||||
|
||||
# Here is the full syntax FrontBase supports:
|
||||
# (from gclem@frontbase.com)
|
||||
#
|
||||
# TOP <limit - unsigned integer>
|
||||
# TOP ( <offset expr>, <limit expr>)
|
||||
|
||||
# "TOP 0" is not allowed, so we have
|
||||
# to use a cheap trick.
|
||||
if limit.zero?
|
||||
case sql
|
||||
when /WHERE/i
|
||||
sql.sub!(/WHERE/i, 'WHERE 0 = 1 AND ')
|
||||
when /ORDER\s+BY/i
|
||||
sql.sub!(/ORDER\s+BY/i, 'WHERE 0 = 1 ORDER BY')
|
||||
else
|
||||
sql << 'WHERE 0 = 1'
|
||||
end
|
||||
else
|
||||
if offset.zero?
|
||||
sql.replace sql.gsub("SELECT ","SELECT TOP #{limit} ")
|
||||
else
|
||||
sql.replace sql.gsub("SELECT ","SELECT TOP(#{offset},#{limit}) ")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def prefetch_primary_key?(table_name = nil)
|
||||
true
|
||||
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)
|
||||
unique = select_value("SELECT UNIQUE FROM #{sequence_name}","Next Sequence Value")
|
||||
# The test cases cannot handle a zero primary key
|
||||
unique.zero? ? select_value("SELECT UNIQUE FROM #{sequence_name}","Next Sequence Value") : unique
|
||||
end
|
||||
|
||||
def default_sequence_name(table, column)
|
||||
table
|
||||
end
|
||||
|
||||
# Set the sequence to the max value of the table's column.
|
||||
def reset_sequence!(table, column, sequence = nil)
|
||||
klasses = classes_for_table_name(table)
|
||||
klass = klasses.nil? ? nil : klasses.first
|
||||
pk = klass.primary_key unless klass.nil?
|
||||
if pk && klass.columns_hash[pk].type == :integer
|
||||
execute("SET UNIQUE FOR #{klass.table_name}(#{pk})")
|
||||
end
|
||||
end
|
||||
|
||||
def classes_for_table_name(table)
|
||||
ActiveRecord::Base.send(:subclasses).select {|klass| klass.table_name == table}
|
||||
end
|
||||
|
||||
def reset_pk_sequence!(table, pk = nil, sequence = nil)
|
||||
klasses = classes_for_table_name(table)
|
||||
klass = klasses.nil? ? nil : klasses.first
|
||||
pk = klass.primary_key unless klass.nil?
|
||||
if pk && klass.columns_hash[pk].type == :integer
|
||||
mpk = select_value("SELECT MAX(#{pk}) FROM #{table}")
|
||||
execute("SET UNIQUE FOR #{klass.table_name}(#{pk})")
|
||||
end
|
||||
end
|
||||
|
||||
# SCHEMA STATEMENTS ========================================
|
||||
|
||||
def structure_dump #:nodoc:
|
||||
select_all("SHOW TABLES").inject('') do |structure, table|
|
||||
structure << select_one("SHOW CREATE TABLE #{table.to_a.first.last}")["Create Table"] << ";\n\n"
|
||||
end
|
||||
end
|
||||
|
||||
def recreate_database(name) #:nodoc:
|
||||
drop_database(name)
|
||||
create_database(name)
|
||||
end
|
||||
|
||||
def create_database(name) #:nodoc:
|
||||
execute "CREATE DATABASE #{name}"
|
||||
end
|
||||
|
||||
def drop_database(name) #:nodoc:
|
||||
execute "DROP DATABASE #{name}"
|
||||
end
|
||||
|
||||
def current_database
|
||||
select_value('SELECT "CATALOG_NAME" FROM INFORMATION_SCHEMA.CATALOGS').downcase
|
||||
end
|
||||
|
||||
def tables(name = nil) #:nodoc:
|
||||
select_values(<<-SQL, nil)
|
||||
SELECT "TABLE_NAME"
|
||||
FROM INFORMATION_SCHEMA.TABLES AS T0,
|
||||
INFORMATION_SCHEMA.SCHEMATA AS T1
|
||||
WHERE T0.SCHEMA_PK = T1.SCHEMA_PK
|
||||
AND "SCHEMA_NAME" = CURRENT_SCHEMA
|
||||
SQL
|
||||
end
|
||||
|
||||
def indexes(table_name, name = nil)#:nodoc:
|
||||
indexes = []
|
||||
current_index = nil
|
||||
sql = <<-SQL
|
||||
SELECT INDEX_NAME, T2.ORDINAL_POSITION, INDEX_COLUMN_COUNT, INDEX_TYPE,
|
||||
"COLUMN_NAME", IS_NULLABLE
|
||||
FROM INFORMATION_SCHEMA.TABLES AS T0,
|
||||
INFORMATION_SCHEMA.INDEXES AS T1,
|
||||
INFORMATION_SCHEMA.INDEX_COLUMN_USAGE AS T2,
|
||||
INFORMATION_SCHEMA.COLUMNS AS T3
|
||||
WHERE T0."TABLE_NAME" = '#{table_name}'
|
||||
AND INDEX_TYPE <> 0
|
||||
AND T0.TABLE_PK = T1.TABLE_PK
|
||||
AND T0.TABLE_PK = T2.TABLE_PK
|
||||
AND T0.TABLE_PK = T3.TABLE_PK
|
||||
AND T1.INDEXES_PK = T2.INDEX_PK
|
||||
AND T2.COLUMN_PK = T3.COLUMN_PK
|
||||
ORDER BY INDEX_NAME, T2.ORDINAL_POSITION
|
||||
SQL
|
||||
|
||||
columns = []
|
||||
query(sql).each do |row|
|
||||
index_name = row[0]
|
||||
ord_position = row[1]
|
||||
ndx_colcount = row[2]
|
||||
index_type = row[3]
|
||||
column_name = row[4]
|
||||
|
||||
is_unique = index_type == 1
|
||||
|
||||
columns << column_name
|
||||
if ord_position == ndx_colcount
|
||||
indexes << IndexDefinition.new(table_name, index_name, is_unique , columns)
|
||||
columns = []
|
||||
end
|
||||
end
|
||||
indexes
|
||||
end
|
||||
|
||||
def columns(table_name, name = nil)#:nodoc:
|
||||
sql = <<-SQL
|
||||
SELECT "TABLE_NAME", "COLUMN_NAME", ORDINAL_POSITION, IS_NULLABLE, COLUMN_DEFAULT,
|
||||
DATA_TYPE, DATA_TYPE_CODE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION,
|
||||
NUMERIC_PRECISION_RADIX, NUMERIC_SCALE, DATETIME_PRECISION, DATETIME_PRECISION_LEADING
|
||||
FROM INFORMATION_SCHEMA.TABLES T0,
|
||||
INFORMATION_SCHEMA.COLUMNS T1,
|
||||
INFORMATION_SCHEMA.DATA_TYPE_DESCRIPTOR T3
|
||||
WHERE "TABLE_NAME" = '#{table_name}'
|
||||
AND T0.TABLE_PK = T1.TABLE_PK
|
||||
AND T0.TABLE_PK = T3.TABLE_OR_DOMAIN_PK
|
||||
AND T1.COLUMN_PK = T3.COLUMN_NAME_PK
|
||||
ORDER BY T1.ORDINAL_POSITION
|
||||
SQL
|
||||
|
||||
rawresults = query(sql,name)
|
||||
columns = []
|
||||
rawresults.each do |field|
|
||||
args = [base = field[0],
|
||||
name = field[1],
|
||||
typecode = field[6],
|
||||
typestring = field[5],
|
||||
limit = field[7],
|
||||
precision = field[8],
|
||||
scale = field[9],
|
||||
default = field[4],
|
||||
nullable = field[3]]
|
||||
columns << FrontBaseColumn.new(*args)
|
||||
end
|
||||
columns
|
||||
end
|
||||
|
||||
def create_table(name, options = {})
|
||||
table_definition = TableDefinition.new(self)
|
||||
table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false
|
||||
|
||||
yield table_definition
|
||||
|
||||
if options[:force]
|
||||
drop_table(name) rescue nil
|
||||
end
|
||||
|
||||
create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
|
||||
create_sql << "#{name} ("
|
||||
create_sql << table_definition.to_sql
|
||||
create_sql << ") #{options[:options]}"
|
||||
begin_db_transaction
|
||||
execute create_sql
|
||||
commit_db_transaction
|
||||
rescue ActiveRecord::StatementInvalid => e
|
||||
raise e unless e.message.match(/Table name - \w* - exists/)
|
||||
end
|
||||
|
||||
def rename_table(name, new_name)
|
||||
columns = columns(name)
|
||||
pkcol = columns.find {|c| c.fb_autogen}
|
||||
execute "ALTER TABLE NAME #{name} TO #{new_name}"
|
||||
if pkcol
|
||||
change_column_default(new_name,pkcol.name,"UNIQUE")
|
||||
begin_db_transaction
|
||||
mpk = select_value("SELECT MAX(#{pkcol.name}) FROM #{new_name}")
|
||||
mpk = 0 if mpk.nil?
|
||||
execute "SET UNIQUE=#{mpk} FOR #{new_name}"
|
||||
commit_db_transaction
|
||||
end
|
||||
end
|
||||
|
||||
# Drops a table from the database.
|
||||
def drop_table(name, options = {})
|
||||
execute "DROP TABLE #{name} RESTRICT"
|
||||
rescue ActiveRecord::StatementInvalid => e
|
||||
raise e unless e.message.match(/Referenced TABLE - \w* - does not exist/)
|
||||
end
|
||||
|
||||
# Adds a new column to the named table.
|
||||
# See TableDefinition#column for details of the options you can use.
|
||||
def add_column(table_name, column_name, type, options = {})
|
||||
add_column_sql = "ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}"
|
||||
options[:type] = type
|
||||
add_column_options!(add_column_sql, options)
|
||||
execute(add_column_sql)
|
||||
end
|
||||
|
||||
def add_column_options!(sql, options) #:nodoc:
|
||||
default_value = quote(options[:default], options[:column])
|
||||
if options_include_default?(options)
|
||||
if options[:type] == :boolean
|
||||
default_value = options[:default] == 0 ? quoted_false : quoted_true
|
||||
end
|
||||
sql << " DEFAULT #{default_value}"
|
||||
end
|
||||
sql << " NOT NULL" if options[:null] == false
|
||||
end
|
||||
|
||||
# Removes the column from the table definition.
|
||||
# ===== Examples
|
||||
# remove_column(:suppliers, :qualification)
|
||||
def remove_column(table_name, column_name)
|
||||
execute "ALTER TABLE #{table_name} DROP #{column_name} RESTRICT"
|
||||
end
|
||||
|
||||
def remove_index(table_name, options = {}) #:nodoc:
|
||||
if options[:unique]
|
||||
execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{quote_column_name(index_name(table_name, options))} RESTRICT"
|
||||
else
|
||||
execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
|
||||
end
|
||||
end
|
||||
|
||||
def change_column_default(table_name, column_name, default) #:nodoc:
|
||||
execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT #{default}" if default != "NULL"
|
||||
end
|
||||
|
||||
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
||||
change_column_sql = %( ALTER COLUMN "#{table_name}"."#{column_name}" TO #{type_to_sql(type, options[:limit])} )
|
||||
execute(change_column_sql)
|
||||
change_column_sql = %( ALTER TABLE "#{table_name}" ALTER COLUMN "#{column_name}" )
|
||||
|
||||
if options_include_default?(options)
|
||||
default_value = quote(options[:default], options[:column])
|
||||
if type == :boolean
|
||||
default_value = options[:default] == 0 ? quoted_false : quoted_true
|
||||
end
|
||||
change_column_sql << " SET DEFAULT #{default_value}"
|
||||
end
|
||||
|
||||
execute(change_column_sql)
|
||||
|
||||
# change_column_sql = "ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{type_to_sql(type, options[:limit])}"
|
||||
# add_column_options!(change_column_sql, options)
|
||||
# execute(change_column_sql)
|
||||
end
|
||||
|
||||
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
||||
execute %( ALTER COLUMN NAME "#{table_name}"."#{column_name}" TO "#{new_column_name}" )
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Clean up sql to make it something FrontBase can digest
|
||||
def cleanup_fb_sql(sql) #:nodoc:
|
||||
# Turn non-standard != into standard <>
|
||||
cleansql = sql.gsub("!=", "<>")
|
||||
# Strip blank lines and comments
|
||||
cleansql.split("\n").reject { |line| line.match(/^(?:\s*|--.*)$/) } * "\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
106
vendor/rails/activerecord/lib/active_record/locking/optimistic.rb
vendored
Normal file
106
vendor/rails/activerecord/lib/active_record/locking/optimistic.rb
vendored
Normal file
|
@ -0,0 +1,106 @@
|
|||
module ActiveRecord
|
||||
module Locking
|
||||
# Active Records support optimistic locking if the field <tt>lock_version</tt> is present. Each update to the
|
||||
# record increments the lock_version column and the locking facilities ensure that records instantiated twice
|
||||
# will let the last one saved raise a StaleObjectError if the first was also updated. Example:
|
||||
#
|
||||
# p1 = Person.find(1)
|
||||
# p2 = Person.find(1)
|
||||
#
|
||||
# p1.first_name = "Michael"
|
||||
# p1.save
|
||||
#
|
||||
# p2.first_name = "should fail"
|
||||
# p2.save # Raises a ActiveRecord::StaleObjectError
|
||||
#
|
||||
# You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
|
||||
# or otherwise apply the business logic needed to resolve the conflict.
|
||||
#
|
||||
# You must ensure that your database schema defaults the lock_version column to 0.
|
||||
#
|
||||
# This behavior can be turned off by setting <tt>ActiveRecord::Base.lock_optimistically = false</tt>.
|
||||
# To override the name of the lock_version column, invoke the <tt>set_locking_column</tt> method.
|
||||
# This method uses the same syntax as <tt>set_table_name</tt>
|
||||
module Optimistic
|
||||
def self.included(base) #:nodoc:
|
||||
super
|
||||
base.extend ClassMethods
|
||||
|
||||
base.cattr_accessor :lock_optimistically, :instance_writer => false
|
||||
base.lock_optimistically = true
|
||||
|
||||
base.alias_method_chain :update, :lock
|
||||
base.alias_method_chain :attributes_from_column_definition, :lock
|
||||
|
||||
class << base
|
||||
alias_method :locking_column=, :set_locking_column
|
||||
end
|
||||
end
|
||||
|
||||
def locking_enabled? #:nodoc:
|
||||
lock_optimistically && respond_to?(self.class.locking_column)
|
||||
end
|
||||
|
||||
def attributes_from_column_definition_with_lock
|
||||
result = attributes_from_column_definition_without_lock
|
||||
|
||||
# If the locking column has no default value set,
|
||||
# start the lock version at zero. Note we can't use
|
||||
# locking_enabled? at this point as @attributes may
|
||||
# not have been initialized yet
|
||||
|
||||
if lock_optimistically && result.include?(self.class.locking_column)
|
||||
result[self.class.locking_column] ||= 0
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
def update_with_lock #:nodoc:
|
||||
return update_without_lock unless locking_enabled?
|
||||
|
||||
lock_col = self.class.locking_column
|
||||
previous_value = send(lock_col)
|
||||
send(lock_col + '=', previous_value + 1)
|
||||
|
||||
affected_rows = connection.update(<<-end_sql, "#{self.class.name} Update with optimistic locking")
|
||||
UPDATE #{self.class.table_name}
|
||||
SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false))}
|
||||
WHERE #{self.class.primary_key} = #{quote_value(id)}
|
||||
AND #{self.class.quoted_locking_column} = #{quote_value(previous_value)}
|
||||
end_sql
|
||||
|
||||
unless affected_rows == 1
|
||||
raise ActiveRecord::StaleObjectError, "Attempted to update a stale object"
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
DEFAULT_LOCKING_COLUMN = 'lock_version'
|
||||
|
||||
# Set the column to use for optimistic locking. Defaults to lock_version.
|
||||
def set_locking_column(value = nil, &block)
|
||||
define_attr_method :locking_column, value, &block
|
||||
value
|
||||
end
|
||||
|
||||
# The version column used for optimistic locking. Defaults to lock_version.
|
||||
def locking_column
|
||||
reset_locking_column
|
||||
end
|
||||
|
||||
# Quote the column name used for optimistic locking.
|
||||
def quoted_locking_column
|
||||
connection.quote_column_name(locking_column)
|
||||
end
|
||||
|
||||
# Reset the column used for optimistic locking back to the lock_version default.
|
||||
def reset_locking_column
|
||||
set_locking_column DEFAULT_LOCKING_COLUMN
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
77
vendor/rails/activerecord/lib/active_record/locking/pessimistic.rb
vendored
Normal file
77
vendor/rails/activerecord/lib/active_record/locking/pessimistic.rb
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
# Copyright (c) 2006 Shugo Maeda <shugo@ruby-lang.org>
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
module ActiveRecord
|
||||
module Locking
|
||||
# Locking::Pessimistic provides support for row-level locking using
|
||||
# SELECT ... FOR UPDATE and other lock types.
|
||||
#
|
||||
# Pass :lock => true to ActiveRecord::Base.find to obtain an exclusive
|
||||
# lock on the selected rows:
|
||||
# # select * from accounts where id=1 for update
|
||||
# Account.find(1, :lock => true)
|
||||
#
|
||||
# Pass :lock => 'some locking clause' to give a database-specific locking clause
|
||||
# of your own such as 'LOCK IN SHARE MODE' or 'FOR UPDATE NOWAIT'.
|
||||
#
|
||||
# Example:
|
||||
# Account.transaction do
|
||||
# # select * from accounts where name = 'shugo' limit 1 for update
|
||||
# shugo = Account.find(:first, :conditions => "name = 'shugo'", :lock => true)
|
||||
# yuko = Account.find(:first, :conditions => "name = 'yuko'", :lock => true)
|
||||
# shugo.balance -= 100
|
||||
# shugo.save!
|
||||
# yuko.balance += 100
|
||||
# yuko.save!
|
||||
# end
|
||||
#
|
||||
# You can also use ActiveRecord::Base#lock! method to lock one record by id.
|
||||
# This may be better if you don't need to lock every row. Example:
|
||||
# Account.transaction do
|
||||
# # select * from accounts where ...
|
||||
# accounts = Account.find(:all, :conditions => ...)
|
||||
# account1 = accounts.detect { |account| ... }
|
||||
# account2 = accounts.detect { |account| ... }
|
||||
# # select * from accounts where id=? for update
|
||||
# account1.lock!
|
||||
# account2.lock!
|
||||
# account1.balance -= 100
|
||||
# account1.save!
|
||||
# account2.balance += 100
|
||||
# account2.save!
|
||||
# end
|
||||
#
|
||||
# Database-specific information on row locking:
|
||||
# MySQL: http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html
|
||||
# PostgreSQL: http://www.postgresql.org/docs/8.1/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
|
||||
module Pessimistic
|
||||
# Obtain a row lock on this record. Reloads the record to obtain the requested
|
||||
# lock. Pass an SQL locking clause to append the end of the SELECT statement
|
||||
# or pass true for "FOR UPDATE" (the default, an exclusive row lock). Returns
|
||||
# the locked record.
|
||||
def lock!(lock = true)
|
||||
reload(:lock => lock) unless new_record?
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
308
vendor/rails/activerecord/lib/active_record/xml_serialization.rb
vendored
Normal file
308
vendor/rails/activerecord/lib/active_record/xml_serialization.rb
vendored
Normal file
|
@ -0,0 +1,308 @@
|
|||
module ActiveRecord #:nodoc:
|
||||
module XmlSerialization
|
||||
# Builds an XML document to represent the model. Some configuration is
|
||||
# availble through +options+, however more complicated cases should use
|
||||
# override ActiveRecord's to_xml.
|
||||
#
|
||||
# By default the generated XML document will include the processing
|
||||
# instruction and all object's attributes. For example:
|
||||
#
|
||||
# <?xml version="1.0" encoding="UTF-8"?>
|
||||
# <topic>
|
||||
# <title>The First Topic</title>
|
||||
# <author-name>David</author-name>
|
||||
# <id type="integer">1</id>
|
||||
# <approved type="boolean">false</approved>
|
||||
# <replies-count type="integer">0</replies-count>
|
||||
# <bonus-time type="datetime">2000-01-01T08:28:00+12:00</bonus-time>
|
||||
# <written-on type="datetime">2003-07-16T09:28:00+1200</written-on>
|
||||
# <content>Have a nice day</content>
|
||||
# <author-email-address>david@loudthinking.com</author-email-address>
|
||||
# <parent-id></parent-id>
|
||||
# <last-read type="date">2004-04-15</last-read>
|
||||
# </topic>
|
||||
#
|
||||
# This behavior can be controlled with :only, :except,
|
||||
# :skip_instruct, :skip_types and :dasherize. The :only and
|
||||
# :except options are the same as for the #attributes method.
|
||||
# The default is to dasherize all column names, to disable this,
|
||||
# set :dasherize to false. To not have the column type included
|
||||
# in the XML output, set :skip_types to false.
|
||||
#
|
||||
# For instance:
|
||||
#
|
||||
# topic.to_xml(:skip_instruct => true, :except => [ :id, :bonus_time, :written_on, :replies_count ])
|
||||
#
|
||||
# <topic>
|
||||
# <title>The First Topic</title>
|
||||
# <author-name>David</author-name>
|
||||
# <approved type="boolean">false</approved>
|
||||
# <content>Have a nice day</content>
|
||||
# <author-email-address>david@loudthinking.com</author-email-address>
|
||||
# <parent-id></parent-id>
|
||||
# <last-read type="date">2004-04-15</last-read>
|
||||
# </topic>
|
||||
#
|
||||
# To include first level associations use :include
|
||||
#
|
||||
# firm.to_xml :include => [ :account, :clients ]
|
||||
#
|
||||
# <?xml version="1.0" encoding="UTF-8"?>
|
||||
# <firm>
|
||||
# <id type="integer">1</id>
|
||||
# <rating type="integer">1</rating>
|
||||
# <name>37signals</name>
|
||||
# <clients>
|
||||
# <client>
|
||||
# <rating type="integer">1</rating>
|
||||
# <name>Summit</name>
|
||||
# </client>
|
||||
# <client>
|
||||
# <rating type="integer">1</rating>
|
||||
# <name>Microsoft</name>
|
||||
# </client>
|
||||
# </clients>
|
||||
# <account>
|
||||
# <id type="integer">1</id>
|
||||
# <credit-limit type="integer">50</credit-limit>
|
||||
# </account>
|
||||
# </firm>
|
||||
#
|
||||
# To include any methods on the object(s) being called use :methods
|
||||
#
|
||||
# firm.to_xml :methods => [ :calculated_earnings, :real_earnings ]
|
||||
#
|
||||
# <firm>
|
||||
# # ... normal attributes as shown above ...
|
||||
# <calculated-earnings>100000000000000000</calculated-earnings>
|
||||
# <real-earnings>5</real-earnings>
|
||||
# </firm>
|
||||
#
|
||||
# To call any Proc's on the object(s) use :procs. The Proc's
|
||||
# are passed a modified version of the options hash that was
|
||||
# given to #to_xml.
|
||||
#
|
||||
# proc = Proc.new { |options| options[:builder].tag!('abc', 'def') }
|
||||
# firm.to_xml :procs => [ proc ]
|
||||
#
|
||||
# <firm>
|
||||
# # ... normal attributes as shown above ...
|
||||
# <abc>def</abc>
|
||||
# </firm>
|
||||
#
|
||||
# You may override the to_xml method in your ActiveRecord::Base
|
||||
# subclasses if you need to. The general form of doing this is
|
||||
#
|
||||
# class IHaveMyOwnXML < ActiveRecord::Base
|
||||
# def to_xml(options = {})
|
||||
# options[:indent] ||= 2
|
||||
# xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
|
||||
# xml.instruct! unless options[:skip_instruct]
|
||||
# xml.level_one do
|
||||
# xml.tag!(:second_level, 'content')
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
def to_xml(options = {})
|
||||
XmlSerializer.new(self, options).to_s
|
||||
end
|
||||
end
|
||||
|
||||
class XmlSerializer #:nodoc:
|
||||
attr_reader :options
|
||||
|
||||
def initialize(record, options = {})
|
||||
@record, @options = record, options.dup
|
||||
end
|
||||
|
||||
def builder
|
||||
@builder ||= begin
|
||||
options[:indent] ||= 2
|
||||
builder = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
|
||||
|
||||
unless options[:skip_instruct]
|
||||
builder.instruct!
|
||||
options[:skip_instruct] = true
|
||||
end
|
||||
|
||||
builder
|
||||
end
|
||||
end
|
||||
|
||||
def root
|
||||
root = (options[:root] || @record.class.to_s.underscore).to_s
|
||||
dasherize? ? root.dasherize : root
|
||||
end
|
||||
|
||||
def dasherize?
|
||||
!options.has_key?(:dasherize) || options[:dasherize]
|
||||
end
|
||||
|
||||
|
||||
# To replicate the behavior in ActiveRecord#attributes,
|
||||
# :except takes precedence over :only. If :only is not set
|
||||
# for a N level model but is set for the N+1 level models,
|
||||
# then because :except is set to a default value, the second
|
||||
# level model can have both :except and :only set. So if
|
||||
# :only is set, always delete :except.
|
||||
def serializable_attributes
|
||||
attribute_names = @record.attribute_names
|
||||
|
||||
if options[:only]
|
||||
options.delete(:except)
|
||||
attribute_names = attribute_names & Array(options[:only]).collect { |n| n.to_s }
|
||||
else
|
||||
options[:except] = Array(options[:except]) | Array(@record.class.inheritance_column)
|
||||
attribute_names = attribute_names - options[:except].collect { |n| n.to_s }
|
||||
end
|
||||
|
||||
attribute_names.collect { |name| Attribute.new(name, @record) }
|
||||
end
|
||||
|
||||
def serializable_method_attributes
|
||||
Array(options[:methods]).collect { |name| MethodAttribute.new(name.to_s, @record) }
|
||||
end
|
||||
|
||||
|
||||
def add_attributes
|
||||
(serializable_attributes + serializable_method_attributes).each do |attribute|
|
||||
add_tag(attribute)
|
||||
end
|
||||
end
|
||||
|
||||
def add_includes
|
||||
if include_associations = options.delete(:include)
|
||||
root_only_or_except = { :except => options[:except],
|
||||
:only => options[:only] }
|
||||
|
||||
include_has_options = include_associations.is_a?(Hash)
|
||||
|
||||
for association in include_has_options ? include_associations.keys : Array(include_associations)
|
||||
association_options = include_has_options ? include_associations[association] : root_only_or_except
|
||||
|
||||
opts = options.merge(association_options)
|
||||
|
||||
case @record.class.reflect_on_association(association).macro
|
||||
when :has_many, :has_and_belongs_to_many
|
||||
records = @record.send(association).to_a
|
||||
unless records.empty?
|
||||
tag = records.first.class.to_s.underscore.pluralize
|
||||
tag = tag.dasherize if dasherize?
|
||||
|
||||
builder.tag!(tag) do
|
||||
records.each { |r| r.to_xml(opts.merge(:root => association.to_s.singularize)) }
|
||||
end
|
||||
end
|
||||
when :has_one, :belongs_to
|
||||
if record = @record.send(association)
|
||||
record.to_xml(opts.merge(:root => association))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
options[:include] = include_associations
|
||||
end
|
||||
end
|
||||
|
||||
def add_procs
|
||||
if procs = options.delete(:procs)
|
||||
[ *procs ].each do |proc|
|
||||
proc.call(options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def add_tag(attribute)
|
||||
builder.tag!(
|
||||
dasherize? ? attribute.name.dasherize : attribute.name,
|
||||
attribute.value.to_s,
|
||||
attribute.decorations(!options[:skip_types])
|
||||
)
|
||||
end
|
||||
|
||||
def serialize
|
||||
args = [root]
|
||||
if options[:namespace]
|
||||
args << {:xmlns=>options[:namespace]}
|
||||
end
|
||||
|
||||
builder.tag!(*args) do
|
||||
add_attributes
|
||||
add_includes
|
||||
add_procs
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :to_s, :serialize
|
||||
|
||||
class Attribute #:nodoc:
|
||||
attr_reader :name, :value, :type
|
||||
|
||||
def initialize(name, record)
|
||||
@name, @record = name, record
|
||||
|
||||
@type = compute_type
|
||||
@value = compute_value
|
||||
end
|
||||
|
||||
# There is a significant speed improvement if the value
|
||||
# does not need to be escaped, as #tag! escapes all values
|
||||
# to ensure that valid XML is generated. For known binary
|
||||
# values, it is at least an order of magnitude faster to
|
||||
# Base64 encode binary values and directly put them in the
|
||||
# output XML than to pass the original value or the Base64
|
||||
# encoded value to the #tag! method. It definitely makes
|
||||
# no sense to Base64 encode the value and then give it to
|
||||
# #tag!, since that just adds additional overhead.
|
||||
def needs_encoding?
|
||||
![ :binary, :date, :datetime, :boolean, :float, :integer ].include?(type)
|
||||
end
|
||||
|
||||
def decorations(include_types = true)
|
||||
decorations = {}
|
||||
|
||||
if type == :binary
|
||||
decorations[:encoding] = 'base64'
|
||||
end
|
||||
|
||||
if include_types && type != :string
|
||||
decorations[:type] = type
|
||||
end
|
||||
|
||||
decorations
|
||||
end
|
||||
|
||||
protected
|
||||
def compute_type
|
||||
type = @record.class.columns_hash[name].type
|
||||
|
||||
case type
|
||||
when :text
|
||||
:string
|
||||
when :time
|
||||
:datetime
|
||||
else
|
||||
type
|
||||
end
|
||||
end
|
||||
|
||||
def compute_value
|
||||
value = @record.send(name)
|
||||
|
||||
if formatter = Hash::XML_FORMATTING[type.to_s]
|
||||
value ? formatter.call(value) : nil
|
||||
else
|
||||
value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class MethodAttribute < Attribute #:nodoc:
|
||||
protected
|
||||
def compute_type
|
||||
Hash::XML_TYPE_NAMES[@record.send(name).class.name] || :string
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
31
vendor/rails/activerecord/test/active_schema_test_mysql.rb
vendored
Normal file
31
vendor/rails/activerecord/test/active_schema_test_mysql.rb
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
require 'abstract_unit'
|
||||
|
||||
class ActiveSchemaTest < Test::Unit::TestCase
|
||||
def setup
|
||||
ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
|
||||
alias_method :real_execute, :execute
|
||||
def execute(sql, name = nil) return sql end
|
||||
end
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:alias_method, :execute, :real_execute)
|
||||
end
|
||||
|
||||
def test_drop_table
|
||||
assert_equal "DROP TABLE people", drop_table(:people)
|
||||
end
|
||||
|
||||
def test_add_column
|
||||
assert_equal "ALTER TABLE people ADD `last_name` varchar(255)", add_column(:people, :last_name, :string)
|
||||
end
|
||||
|
||||
def test_add_column_with_limit
|
||||
assert_equal "ALTER TABLE people ADD `key` varchar(32)", add_column(:people, :key, :string, :limit => 32)
|
||||
end
|
||||
|
||||
private
|
||||
def method_missing(method_symbol, *arguments)
|
||||
ActiveRecord::Base.connection.send(method_symbol, *arguments)
|
||||
end
|
||||
end
|
81
vendor/rails/activerecord/test/adapter_test_sqlserver.rb
vendored
Normal file
81
vendor/rails/activerecord/test/adapter_test_sqlserver.rb
vendored
Normal file
|
@ -0,0 +1,81 @@
|
|||
require 'abstract_unit'
|
||||
require 'fixtures/default'
|
||||
require 'fixtures/post'
|
||||
require 'fixtures/task'
|
||||
|
||||
class SqlServerAdapterTest < Test::Unit::TestCase
|
||||
fixtures :posts, :tasks
|
||||
|
||||
def setup
|
||||
@connection = ActiveRecord::Base.connection
|
||||
end
|
||||
|
||||
def teardown
|
||||
@connection.execute("SET LANGUAGE us_english")
|
||||
end
|
||||
|
||||
# SQL Server 2000 has a bug where some unambiguous date formats are not
|
||||
# correctly identified if the session language is set to german
|
||||
def test_date_insertion_when_language_is_german
|
||||
@connection.execute("SET LANGUAGE deutsch")
|
||||
|
||||
assert_nothing_raised do
|
||||
Task.create(:starting => Time.utc(2000, 1, 31, 5, 42, 0), :ending => Date.new(2006, 12, 31))
|
||||
end
|
||||
end
|
||||
|
||||
def test_execute_without_block_closes_statement
|
||||
assert_all_statements_used_are_closed do
|
||||
@connection.execute("SELECT 1")
|
||||
end
|
||||
end
|
||||
|
||||
def test_execute_with_block_closes_statement
|
||||
assert_all_statements_used_are_closed do
|
||||
@connection.execute("SELECT 1") do |sth|
|
||||
assert !sth.finished?, "Statement should still be alive within block"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_insert_with_identity_closes_statement
|
||||
assert_all_statements_used_are_closed do
|
||||
@connection.insert("INSERT INTO accounts ([id], [firm_id],[credit_limit]) values (999, 1, 50)")
|
||||
end
|
||||
end
|
||||
|
||||
def test_insert_without_identity_closes_statement
|
||||
assert_all_statements_used_are_closed do
|
||||
@connection.insert("INSERT INTO accounts ([firm_id],[credit_limit]) values (1, 50)")
|
||||
end
|
||||
end
|
||||
|
||||
def test_active_closes_statement
|
||||
assert_all_statements_used_are_closed do
|
||||
@connection.active?
|
||||
end
|
||||
end
|
||||
|
||||
def assert_all_statements_used_are_closed(&block)
|
||||
existing_handles = []
|
||||
ObjectSpace.each_object(DBI::StatementHandle) {|handle| existing_handles << handle}
|
||||
GC.disable
|
||||
|
||||
yield
|
||||
|
||||
used_handles = []
|
||||
ObjectSpace.each_object(DBI::StatementHandle) {|handle| used_handles << handle unless existing_handles.include? handle}
|
||||
|
||||
assert_block "No statements were used within given block" do
|
||||
used_handles.size > 0
|
||||
end
|
||||
|
||||
ObjectSpace.each_object(DBI::StatementHandle) do |handle|
|
||||
assert_block "Statement should have been closed within given block" do
|
||||
handle.finished?
|
||||
end
|
||||
end
|
||||
ensure
|
||||
GC.enable
|
||||
end
|
||||
end
|
126
vendor/rails/activerecord/test/associations/callbacks_test.rb
vendored
Normal file
126
vendor/rails/activerecord/test/associations/callbacks_test.rb
vendored
Normal file
|
@ -0,0 +1,126 @@
|
|||
require 'abstract_unit'
|
||||
require 'fixtures/post'
|
||||
require 'fixtures/comment'
|
||||
require 'fixtures/author'
|
||||
require 'fixtures/category'
|
||||
require 'fixtures/project'
|
||||
require 'fixtures/developer'
|
||||
|
||||
class AssociationCallbacksTest < Test::Unit::TestCase
|
||||
fixtures :posts, :authors, :projects, :developers
|
||||
|
||||
def setup
|
||||
@david = authors(:david)
|
||||
@thinking = posts(:thinking)
|
||||
@authorless = posts(:authorless)
|
||||
assert @david.post_log.empty?
|
||||
end
|
||||
|
||||
def test_adding_macro_callbacks
|
||||
@david.posts_with_callbacks << @thinking
|
||||
assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}"], @david.post_log
|
||||
@david.posts_with_callbacks << @thinking
|
||||
assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}", "before_adding#{@thinking.id}",
|
||||
"after_adding#{@thinking.id}"], @david.post_log
|
||||
end
|
||||
|
||||
def test_adding_with_proc_callbacks
|
||||
@david.posts_with_proc_callbacks << @thinking
|
||||
assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}"], @david.post_log
|
||||
@david.posts_with_proc_callbacks << @thinking
|
||||
assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}", "before_adding#{@thinking.id}",
|
||||
"after_adding#{@thinking.id}"], @david.post_log
|
||||
end
|
||||
|
||||
def test_removing_with_macro_callbacks
|
||||
first_post, second_post = @david.posts_with_callbacks[0, 2]
|
||||
@david.posts_with_callbacks.delete(first_post)
|
||||
assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}"], @david.post_log
|
||||
@david.posts_with_callbacks.delete(second_post)
|
||||
assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}", "before_removing#{second_post.id}",
|
||||
"after_removing#{second_post.id}"], @david.post_log
|
||||
end
|
||||
|
||||
def test_removing_with_proc_callbacks
|
||||
first_post, second_post = @david.posts_with_callbacks[0, 2]
|
||||
@david.posts_with_proc_callbacks.delete(first_post)
|
||||
assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}"], @david.post_log
|
||||
@david.posts_with_proc_callbacks.delete(second_post)
|
||||
assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}", "before_removing#{second_post.id}",
|
||||
"after_removing#{second_post.id}"], @david.post_log
|
||||
end
|
||||
|
||||
def test_multiple_callbacks
|
||||
@david.posts_with_multiple_callbacks << @thinking
|
||||
assert_equal ["before_adding#{@thinking.id}", "before_adding_proc#{@thinking.id}", "after_adding#{@thinking.id}",
|
||||
"after_adding_proc#{@thinking.id}"], @david.post_log
|
||||
@david.posts_with_multiple_callbacks << @thinking
|
||||
assert_equal ["before_adding#{@thinking.id}", "before_adding_proc#{@thinking.id}", "after_adding#{@thinking.id}",
|
||||
"after_adding_proc#{@thinking.id}", "before_adding#{@thinking.id}", "before_adding_proc#{@thinking.id}",
|
||||
"after_adding#{@thinking.id}", "after_adding_proc#{@thinking.id}"], @david.post_log
|
||||
end
|
||||
|
||||
def test_has_and_belongs_to_many_add_callback
|
||||
david = developers(:david)
|
||||
ar = projects(:active_record)
|
||||
assert ar.developers_log.empty?
|
||||
ar.developers_with_callbacks << david
|
||||
assert_equal ["before_adding#{david.id}", "after_adding#{david.id}"], ar.developers_log
|
||||
ar.developers_with_callbacks << david
|
||||
assert_equal ["before_adding#{david.id}", "after_adding#{david.id}", "before_adding#{david.id}",
|
||||
"after_adding#{david.id}"], ar.developers_log
|
||||
end
|
||||
|
||||
def test_has_and_belongs_to_many_remove_callback
|
||||
david = developers(:david)
|
||||
jamis = developers(:jamis)
|
||||
activerecord = projects(:active_record)
|
||||
assert activerecord.developers_log.empty?
|
||||
activerecord.developers_with_callbacks.delete(david)
|
||||
assert_equal ["before_removing#{david.id}", "after_removing#{david.id}"], activerecord.developers_log
|
||||
|
||||
activerecord.developers_with_callbacks.delete(jamis)
|
||||
assert_equal ["before_removing#{david.id}", "after_removing#{david.id}", "before_removing#{jamis.id}",
|
||||
"after_removing#{jamis.id}"], activerecord.developers_log
|
||||
end
|
||||
|
||||
def test_has_and_belongs_to_many_remove_callback_on_clear
|
||||
activerecord = projects(:active_record)
|
||||
assert activerecord.developers_log.empty?
|
||||
if activerecord.developers_with_callbacks.size == 0
|
||||
activerecord.developers << developers(:david)
|
||||
activerecord.developers << developers(:jamis)
|
||||
activerecord.reload
|
||||
assert activerecord.developers_with_callbacks.size == 2
|
||||
end
|
||||
log_array = activerecord.developers_with_callbacks.collect {|d| ["before_removing#{d.id}","after_removing#{d.id}"]}.flatten.sort
|
||||
assert activerecord.developers_with_callbacks.clear
|
||||
assert_equal log_array, activerecord.developers_log.sort
|
||||
end
|
||||
|
||||
def test_dont_add_if_before_callback_raises_exception
|
||||
assert !@david.unchangable_posts.include?(@authorless)
|
||||
begin
|
||||
@david.unchangable_posts << @authorless
|
||||
rescue Exception => e
|
||||
end
|
||||
assert @david.post_log.empty?
|
||||
assert !@david.unchangable_posts.include?(@authorless)
|
||||
@david.reload
|
||||
assert !@david.unchangable_posts.include?(@authorless)
|
||||
end
|
||||
|
||||
def test_push_with_attributes
|
||||
assert_deprecated 'push_with_attributes' do
|
||||
david = developers(:david)
|
||||
activerecord = projects(:active_record)
|
||||
assert activerecord.developers_log.empty?
|
||||
activerecord.developers_with_callbacks.push_with_attributes(david, {})
|
||||
assert_equal ["before_adding#{david.id}", "after_adding#{david.id}"], activerecord.developers_log
|
||||
activerecord.developers_with_callbacks.push_with_attributes(david, {})
|
||||
assert_equal ["before_adding#{david.id}", "after_adding#{david.id}", "before_adding#{david.id}",
|
||||
"after_adding#{david.id}"], activerecord.developers_log
|
||||
end
|
||||
end
|
||||
end
|
||||
|
138
vendor/rails/activerecord/test/associations/cascaded_eager_loading_test.rb
vendored
Normal file
138
vendor/rails/activerecord/test/associations/cascaded_eager_loading_test.rb
vendored
Normal file
|
@ -0,0 +1,138 @@
|
|||
require 'abstract_unit'
|
||||
require 'active_record/acts/list'
|
||||
require 'fixtures/post'
|
||||
require 'fixtures/comment'
|
||||
require 'fixtures/author'
|
||||
require 'fixtures/category'
|
||||
require 'fixtures/categorization'
|
||||
require 'fixtures/mixin'
|
||||
require 'fixtures/company'
|
||||
require 'fixtures/topic'
|
||||
require 'fixtures/reply'
|
||||
|
||||
class CascadedEagerLoadingTest < Test::Unit::TestCase
|
||||
fixtures :authors, :mixins, :companies, :posts, :topics
|
||||
|
||||
def test_eager_association_loading_with_cascaded_two_levels
|
||||
authors = Author.find(:all, :include=>{:posts=>:comments}, :order=>"authors.id")
|
||||
assert_equal 2, authors.size
|
||||
assert_equal 5, authors[0].posts.size
|
||||
assert_equal 1, authors[1].posts.size
|
||||
assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_cascaded_two_levels_and_one_level
|
||||
authors = Author.find(:all, :include=>[{:posts=>:comments}, :categorizations], :order=>"authors.id")
|
||||
assert_equal 2, authors.size
|
||||
assert_equal 5, authors[0].posts.size
|
||||
assert_equal 1, authors[1].posts.size
|
||||
assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
|
||||
assert_equal 1, authors[0].categorizations.size
|
||||
assert_equal 2, authors[1].categorizations.size
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations
|
||||
authors = Author.find(:all, :include=>{:posts=>[:comments, :categorizations]}, :order=>"authors.id")
|
||||
assert_equal 2, authors.size
|
||||
assert_equal 5, authors[0].posts.size
|
||||
assert_equal 1, authors[1].posts.size
|
||||
assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_cascaded_two_levels_and_self_table_reference
|
||||
authors = Author.find(:all, :include=>{:posts=>[:comments, :author]}, :order=>"authors.id")
|
||||
assert_equal 2, authors.size
|
||||
assert_equal 5, authors[0].posts.size
|
||||
assert_equal authors(:david).name, authors[0].name
|
||||
assert_equal [authors(:david).name], authors[0].posts.collect{|post| post.author.name}.uniq
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_cascaded_two_levels_with_condition
|
||||
authors = Author.find(:all, :include=>{:posts=>:comments}, :conditions=>"authors.id=1", :order=>"authors.id")
|
||||
assert_equal 1, authors.size
|
||||
assert_equal 5, authors[0].posts.size
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_acts_as_tree
|
||||
roots = TreeMixin.find(:all, :include=>"children", :conditions=>"mixins.parent_id IS NULL", :order=>"mixins.id")
|
||||
assert_equal [mixins(:tree_1), mixins(:tree2_1), mixins(:tree3_1)], roots
|
||||
assert_no_queries do
|
||||
assert_equal 2, roots[0].children.size
|
||||
assert_equal 0, roots[1].children.size
|
||||
assert_equal 0, roots[2].children.size
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_cascaded_three_levels_by_ping_pong
|
||||
firms = Firm.find(:all, :include=>{:account=>{:firm=>:account}}, :order=>"companies.id")
|
||||
assert_equal 2, firms.size
|
||||
assert_equal firms.first.account, firms.first.account.firm.account
|
||||
assert_equal companies(:first_firm).account, assert_no_queries { firms.first.account.firm.account }
|
||||
assert_equal companies(:first_firm).account.firm.account, assert_no_queries { firms.first.account.firm.account }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_has_many_sti
|
||||
topics = Topic.find(:all, :include => :replies, :order => 'topics.id')
|
||||
assert_equal [topics(:first), topics(:second)], topics
|
||||
assert_no_queries do
|
||||
assert_equal 1, topics[0].replies.size
|
||||
assert_equal 0, topics[1].replies.size
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_sti
|
||||
replies = Reply.find(:all, :include => :topic, :order => 'topics.id')
|
||||
assert_equal [topics(:second)], replies
|
||||
assert_equal topics(:first), assert_no_queries { replies.first.topic }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_multiple_stis_and_order
|
||||
author = Author.find(:first, :include => { :posts => [ :special_comments , :very_special_comment ] }, :order => 'authors.name, comments.body, very_special_comments_posts.body', :conditions => 'posts.id = 4')
|
||||
assert_equal authors(:david), author
|
||||
assert_no_queries do
|
||||
author.posts.first.special_comments
|
||||
author.posts.first.very_special_comment
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_association_loading_of_stis_with_multiple_references
|
||||
authors = Author.find(:all, :include => { :posts => { :special_comments => { :post => [ :special_comments, :very_special_comment ] } } }, :order => 'comments.body, very_special_comments_posts.body', :conditions => 'posts.id = 4')
|
||||
assert_equal [authors(:david)], authors
|
||||
assert_no_queries do
|
||||
authors.first.posts.first.special_comments.first.post.special_comments
|
||||
authors.first.posts.first.special_comments.first.post.very_special_comment
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_recursive_cascading_three_levels_has_many
|
||||
root_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:children=>{:children=>:children}}, :order => 'mixins.id')
|
||||
assert_equal mixins(:recursively_cascaded_tree_4), assert_no_queries { root_node.children.first.children.first.children.first }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_recursive_cascading_three_levels_has_one
|
||||
root_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:first_child=>{:first_child=>:first_child}}, :order => 'mixins.id')
|
||||
assert_equal mixins(:recursively_cascaded_tree_4), assert_no_queries { root_node.first_child.first_child.first_child }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_recursive_cascading_three_levels_belongs_to
|
||||
leaf_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:parent=>{:parent=>:parent}}, :order => 'mixins.id DESC')
|
||||
assert_equal mixins(:recursively_cascaded_tree_1), assert_no_queries { leaf_node.parent.parent.parent }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
require 'fixtures/vertex'
|
||||
require 'fixtures/edge'
|
||||
class CascadedEagerLoadingTest < Test::Unit::TestCase
|
||||
fixtures :edges, :vertices
|
||||
|
||||
def test_eager_association_loading_with_recursive_cascading_four_levels_has_many_through
|
||||
source = Vertex.find(:first, :include=>{:sinks=>{:sinks=>{:sinks=>:sinks}}}, :order => 'vertices.id')
|
||||
assert_equal vertices(:vertex_4), assert_no_queries { source.sinks.first.sinks.first.sinks.first }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_recursive_cascading_four_levels_has_and_belongs_to_many
|
||||
sink = Vertex.find(:first, :include=>{:sources=>{:sources=>{:sources=>:sources}}}, :order => 'vertices.id DESC')
|
||||
assert_equal vertices(:vertex_1), assert_no_queries { sink.sources.first.sources.first.sources.first.sources.first }
|
||||
end
|
||||
end
|
393
vendor/rails/activerecord/test/associations/eager_test.rb
vendored
Normal file
393
vendor/rails/activerecord/test/associations/eager_test.rb
vendored
Normal file
|
@ -0,0 +1,393 @@
|
|||
require 'abstract_unit'
|
||||
require 'fixtures/post'
|
||||
require 'fixtures/comment'
|
||||
require 'fixtures/author'
|
||||
require 'fixtures/category'
|
||||
require 'fixtures/company'
|
||||
require 'fixtures/person'
|
||||
require 'fixtures/reader'
|
||||
|
||||
class EagerAssociationTest < Test::Unit::TestCase
|
||||
fixtures :posts, :comments, :authors, :categories, :categories_posts,
|
||||
:companies, :accounts, :tags, :people, :readers
|
||||
|
||||
def test_loading_with_one_association
|
||||
posts = Post.find(:all, :include => :comments)
|
||||
post = posts.find { |p| p.id == 1 }
|
||||
assert_equal 2, post.comments.size
|
||||
assert post.comments.include?(comments(:greetings))
|
||||
|
||||
post = Post.find(:first, :include => :comments, :conditions => "posts.title = 'Welcome to the weblog'")
|
||||
assert_equal 2, post.comments.size
|
||||
assert post.comments.include?(comments(:greetings))
|
||||
end
|
||||
|
||||
def test_loading_conditions_with_or
|
||||
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
|
||||
|
||||
def test_with_ordering
|
||||
list = Post.find(:all, :include => :comments, :order => "posts.id DESC")
|
||||
[:eager_other, :sti_habtm, :sti_post_and_comments, :sti_comments,
|
||||
:authorless, :thinking, :welcome
|
||||
].each_with_index do |post, index|
|
||||
assert_equal posts(post), list[index]
|
||||
end
|
||||
end
|
||||
|
||||
def test_loading_with_multiple_associations
|
||||
posts = Post.find(:all, :include => [ :comments, :author, :categories ], :order => "posts.id")
|
||||
assert_equal 2, posts.first.comments.size
|
||||
assert_equal 2, posts.first.categories.size
|
||||
assert posts.first.comments.include?(comments(:greetings))
|
||||
end
|
||||
|
||||
def test_loading_from_an_association
|
||||
posts = authors(:david).posts.find(:all, :include => :comments, :order => "posts.id")
|
||||
assert_equal 2, posts.first.comments.size
|
||||
end
|
||||
|
||||
def test_loading_with_no_associations
|
||||
assert_nil Post.find(posts(:authorless).id, :include => :author).author
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to
|
||||
comments = Comment.find(:all, :include => :post)
|
||||
assert_equal 10, comments.length
|
||||
titles = comments.map { |c| c.post.title }
|
||||
assert titles.include?(posts(:welcome).title)
|
||||
assert titles.include?(posts(:sti_post_and_comments).title)
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_limit
|
||||
comments = Comment.find(:all, :include => :post, :limit => 5, :order => 'comments.id')
|
||||
assert_equal 5, comments.length
|
||||
assert_equal [1,2,3,5,6], comments.collect { |c| c.id }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_limit_and_conditions
|
||||
comments = Comment.find(:all, :include => :post, :conditions => 'post_id = 4', :limit => 3, :order => 'comments.id')
|
||||
assert_equal 3, comments.length
|
||||
assert_equal [5,6,7], comments.collect { |c| c.id }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_limit_and_offset
|
||||
comments = Comment.find(:all, :include => :post, :limit => 3, :offset => 2, :order => 'comments.id')
|
||||
assert_equal 3, comments.length
|
||||
assert_equal [3,5,6], comments.collect { |c| c.id }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions
|
||||
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_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, :order => 'posts.id')
|
||||
assert_equal 1, posts.length
|
||||
assert_equal [1], posts.collect { |p| p.id }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_multiple_associations
|
||||
posts = Post.find(:all, :include => [:author, :very_special_comment], :limit => 1, :offset => 1, :order => 'posts.id')
|
||||
assert_equal 1, posts.length
|
||||
assert_equal [2], posts.collect { |p| p.id }
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_through
|
||||
posts_with_comments = people(:michael).posts.find(:all, :include => :comments)
|
||||
posts_with_author = people(:michael).posts.find(:all, :include => :author )
|
||||
posts_with_comments_and_author = people(:michael).posts.find(:all, :include => [ :comments, :author ])
|
||||
assert_equal 2, posts_with_comments.inject(0) { |sum, post| sum += post.comments.size }
|
||||
assert_equal authors(:david), assert_no_queries { posts_with_author.first.author }
|
||||
assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author }
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_through_an_sti_join_model
|
||||
author = Author.find(:first, :include => :special_post_comments, :order => 'authors.id')
|
||||
assert_equal [comments(:does_it_hurt)], assert_no_queries { author.special_post_comments }
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_through_an_sti_join_model_with_conditions_on_both
|
||||
author = Author.find(:first, :include => :special_nonexistant_post_comments, :order => 'authors.id')
|
||||
assert_equal [], author.special_nonexistant_post_comments
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_through_join_model_with_conditions
|
||||
assert_equal Author.find(:first, :include => :hello_post_comments,
|
||||
:order => 'authors.id').hello_post_comments.sort_by(&:id),
|
||||
Author.find(:first, :order => 'authors.id').hello_post_comments.sort_by(&:id)
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit
|
||||
posts = Post.find(:all, :order => 'posts.id asc', :include => [ :author, :comments ], :limit => 2)
|
||||
assert_equal 2, posts.size
|
||||
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
|
||||
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
|
||||
assert_equal 2, posts.size
|
||||
|
||||
count = Post.count(:include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
|
||||
assert_equal count, posts.size
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit_ond_high_offset
|
||||
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => [ "authors.name = ?", 'David' ])
|
||||
assert_equal 0, posts.size
|
||||
end
|
||||
|
||||
def test_count_eager_with_has_many_and_limit_ond_high_offset
|
||||
posts = Post.count(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => [ "authors.name = ?", 'David' ])
|
||||
assert_equal 0, posts
|
||||
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
|
||||
end
|
||||
|
||||
def test_eager_with_has_and_belongs_to_many_and_limit
|
||||
posts = Post.find(:all, :include => :categories, :order => "posts.id", :limit => 3)
|
||||
assert_equal 3, posts.size
|
||||
assert_equal 2, posts[0].categories.size
|
||||
assert_equal 1, posts[1].categories.size
|
||||
assert_equal 0, posts[2].categories.size
|
||||
assert posts[0].categories.include?(categories(:technology))
|
||||
assert posts[1].categories.include?(categories(:general))
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit_and_conditions_on_the_eagers
|
||||
posts = authors(:david).posts.find(:all,
|
||||
:include => :comments,
|
||||
:conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
|
||||
:limit => 2
|
||||
)
|
||||
assert_equal 2, posts.size
|
||||
|
||||
count = Post.count(
|
||||
:include => [ :comments, :author ],
|
||||
:conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
|
||||
:limit => 2
|
||||
)
|
||||
assert_equal count, posts.size
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit_and_scoped_conditions_on_the_eagers
|
||||
posts = nil
|
||||
Post.with_scope(:find => {
|
||||
:include => :comments,
|
||||
:conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'"
|
||||
}) do
|
||||
posts = authors(:david).posts.find(:all, :limit => 2)
|
||||
assert_equal 2, posts.size
|
||||
end
|
||||
|
||||
Post.with_scope(:find => {
|
||||
:include => [ :comments, :author ],
|
||||
:conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')"
|
||||
}) do
|
||||
count = Post.count(:limit => 2)
|
||||
assert_equal count, posts.size
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit_and_scoped_and_explicit_conditions_on_the_eagers
|
||||
Post.with_scope(:find => { :conditions => "1=1" }) do
|
||||
posts = authors(:david).posts.find(:all,
|
||||
:include => :comments,
|
||||
:conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
|
||||
:limit => 2
|
||||
)
|
||||
assert_equal 2, posts.size
|
||||
|
||||
count = Post.count(
|
||||
:include => [ :comments, :author ],
|
||||
:conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
|
||||
:limit => 2
|
||||
)
|
||||
assert_equal count, posts.size
|
||||
end
|
||||
end
|
||||
def test_eager_association_loading_with_habtm
|
||||
posts = Post.find(:all, :include => :categories, :order => "posts.id")
|
||||
assert_equal 2, posts[0].categories.size
|
||||
assert_equal 1, posts[1].categories.size
|
||||
assert_equal 0, posts[2].categories.size
|
||||
assert posts[0].categories.include?(categories(:technology))
|
||||
assert posts[1].categories.include?(categories(:general))
|
||||
end
|
||||
|
||||
def test_eager_with_inheritance
|
||||
posts = SpecialPost.find(:all, :include => [ :comments ])
|
||||
end
|
||||
|
||||
def test_eager_has_one_with_association_inheritance
|
||||
post = Post.find(4, :include => [ :very_special_comment ])
|
||||
assert_equal "VerySpecialComment", post.very_special_comment.class.to_s
|
||||
end
|
||||
|
||||
def test_eager_has_many_with_association_inheritance
|
||||
post = Post.find(4, :include => [ :special_comments ])
|
||||
post.special_comments.each do |special_comment|
|
||||
assert_equal "SpecialComment", special_comment.class.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_habtm_with_association_inheritance
|
||||
post = Post.find(6, :include => [ :special_categories ])
|
||||
assert_equal 1, post.special_categories.size
|
||||
post.special_categories.each do |special_category|
|
||||
assert_equal "SpecialCategory", special_category.class.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_with_has_one_dependent_does_not_destroy_dependent
|
||||
assert_not_nil companies(:first_firm).account
|
||||
f = Firm.find(:first, :include => :account,
|
||||
:conditions => ["companies.name = ?", "37signals"])
|
||||
assert_not_nil f.account
|
||||
assert_equal companies(:first_firm, :reload).account, f.account
|
||||
end
|
||||
|
||||
def test_eager_with_invalid_association_reference
|
||||
assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
|
||||
post = Post.find(6, :include=> :monkeys )
|
||||
}
|
||||
assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
|
||||
post = Post.find(6, :include=>[ :monkeys ])
|
||||
}
|
||||
assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
|
||||
post = Post.find(6, :include=>[ 'monkeys' ])
|
||||
}
|
||||
assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys, :elephants") {
|
||||
post = Post.find(6, :include=>[ :monkeys, :elephants ])
|
||||
}
|
||||
end
|
||||
|
||||
def find_all_ordered(className, include=nil)
|
||||
className.find(:all, :order=>"#{className.table_name}.#{className.primary_key}", :include=>include)
|
||||
end
|
||||
|
||||
def test_limited_eager_with_order
|
||||
assert_equal [posts(:thinking), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title)', :limit => 2, :offset => 1)
|
||||
assert_equal [posts(:sti_post_and_comments), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC', :limit => 2, :offset => 1)
|
||||
end
|
||||
|
||||
def test_limited_eager_with_multiple_order_columns
|
||||
assert_equal [posts(:thinking), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title), posts.id', :limit => 2, :offset => 1)
|
||||
assert_equal [posts(:sti_post_and_comments), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC, posts.id', :limit => 2, :offset => 1)
|
||||
end
|
||||
|
||||
def test_eager_with_multiple_associations_with_same_table_has_many_and_habtm
|
||||
# Eager includes of has many and habtm associations aren't necessarily sorted in the same way
|
||||
def assert_equal_after_sort(item1, item2, item3 = nil)
|
||||
assert_equal(item1.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id})
|
||||
assert_equal(item3.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id}) if item3
|
||||
end
|
||||
# Test regular association, association with conditions, association with
|
||||
# STI, and association with conditions assured not to be true
|
||||
post_types = [:posts, :other_posts, :special_posts]
|
||||
# test both has_many and has_and_belongs_to_many
|
||||
[Author, Category].each do |className|
|
||||
d1 = find_all_ordered(className)
|
||||
# test including all post types at once
|
||||
d2 = find_all_ordered(className, post_types)
|
||||
d1.each_index do |i|
|
||||
assert_equal(d1[i], d2[i])
|
||||
assert_equal_after_sort(d1[i].posts, d2[i].posts)
|
||||
post_types[1..-1].each do |post_type|
|
||||
# test including post_types together
|
||||
d3 = find_all_ordered(className, [:posts, post_type])
|
||||
assert_equal(d1[i], d3[i])
|
||||
assert_equal_after_sort(d1[i].posts, d3[i].posts)
|
||||
assert_equal_after_sort(d1[i].send(post_type), d2[i].send(post_type), d3[i].send(post_type))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_with_multiple_associations_with_same_table_has_one
|
||||
d1 = find_all_ordered(Firm)
|
||||
d2 = find_all_ordered(Firm, :account)
|
||||
d1.each_index do |i|
|
||||
assert_equal(d1[i], d2[i])
|
||||
assert_equal(d1[i].account, d2[i].account)
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_with_multiple_associations_with_same_table_belongs_to
|
||||
firm_types = [:firm, :firm_with_basic_id, :firm_with_other_name, :firm_with_condition]
|
||||
d1 = find_all_ordered(Client)
|
||||
d2 = find_all_ordered(Client, firm_types)
|
||||
d1.each_index do |i|
|
||||
assert_equal(d1[i], d2[i])
|
||||
firm_types.each { |type| assert_equal(d1[i].send(type), d2[i].send(type)) }
|
||||
end
|
||||
end
|
||||
def test_eager_with_valid_association_as_string_not_symbol
|
||||
assert_nothing_raised { Post.find(:all, :include => 'comments') }
|
||||
end
|
||||
|
||||
def test_preconfigured_includes_with_belongs_to
|
||||
author = posts(:welcome).author_with_posts
|
||||
assert_equal 5, author.posts.size
|
||||
end
|
||||
|
||||
def test_preconfigured_includes_with_has_one
|
||||
comment = posts(:sti_comments).very_special_comment_with_post
|
||||
assert_equal posts(:sti_comments), comment.post
|
||||
end
|
||||
|
||||
def test_preconfigured_includes_with_has_many
|
||||
posts = authors(:david).posts_with_comments
|
||||
one = posts.detect { |p| p.id == 1 }
|
||||
assert_equal 5, posts.size
|
||||
assert_equal 2, one.comments.size
|
||||
end
|
||||
|
||||
def test_preconfigured_includes_with_habtm
|
||||
posts = authors(:david).posts_with_categories
|
||||
one = posts.detect { |p| p.id == 1 }
|
||||
assert_equal 5, posts.size
|
||||
assert_equal 2, one.categories.size
|
||||
end
|
||||
|
||||
def test_preconfigured_includes_with_has_many_and_habtm
|
||||
posts = authors(:david).posts_with_comments_and_categories
|
||||
one = posts.detect { |p| p.id == 1 }
|
||||
assert_equal 5, posts.size
|
||||
assert_equal 2, one.comments.size
|
||||
assert_equal 2, one.categories.size
|
||||
end
|
||||
|
||||
def test_count_with_include
|
||||
if current_adapter?(:SQLServerAdapter, :SybaseAdapter)
|
||||
assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "len(comments.body) > 15")
|
||||
else
|
||||
assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(comments.body) > 15")
|
||||
end
|
||||
end
|
||||
end
|
42
vendor/rails/activerecord/test/associations/extension_test.rb
vendored
Normal file
42
vendor/rails/activerecord/test/associations/extension_test.rb
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
require 'abstract_unit'
|
||||
require 'fixtures/post'
|
||||
require 'fixtures/comment'
|
||||
require 'fixtures/project'
|
||||
require 'fixtures/developer'
|
||||
|
||||
class AssociationsExtensionsTest < Test::Unit::TestCase
|
||||
fixtures :projects, :developers, :developers_projects, :comments, :posts
|
||||
|
||||
def test_extension_on_has_many
|
||||
assert_equal comments(:more_greetings), posts(:welcome).comments.find_most_recent
|
||||
end
|
||||
|
||||
def test_extension_on_habtm
|
||||
assert_equal projects(:action_controller), developers(:david).projects.find_most_recent
|
||||
end
|
||||
|
||||
def test_named_extension_on_habtm
|
||||
assert_equal projects(:action_controller), developers(:david).projects_extended_by_name.find_most_recent
|
||||
end
|
||||
|
||||
def test_named_two_extensions_on_habtm
|
||||
assert_equal projects(:action_controller), developers(:david).projects_extended_by_name_twice.find_most_recent
|
||||
assert_equal projects(:active_record), developers(:david).projects_extended_by_name_twice.find_least_recent
|
||||
end
|
||||
|
||||
def test_marshalling_extensions
|
||||
david = developers(:david)
|
||||
assert_equal projects(:action_controller), david.projects.find_most_recent
|
||||
|
||||
david = Marshal.load(Marshal.dump(david))
|
||||
assert_equal projects(:action_controller), david.projects.find_most_recent
|
||||
end
|
||||
|
||||
def test_marshalling_named_extensions
|
||||
david = developers(:david)
|
||||
assert_equal projects(:action_controller), david.projects_extended_by_name.find_most_recent
|
||||
|
||||
david = Marshal.load(Marshal.dump(david))
|
||||
assert_equal projects(:action_controller), david.projects_extended_by_name.find_most_recent
|
||||
end
|
||||
end
|
480
vendor/rails/activerecord/test/associations/join_model_test.rb
vendored
Normal file
480
vendor/rails/activerecord/test/associations/join_model_test.rb
vendored
Normal file
|
@ -0,0 +1,480 @@
|
|||
require 'abstract_unit'
|
||||
require 'fixtures/tag'
|
||||
require 'fixtures/tagging'
|
||||
require 'fixtures/post'
|
||||
require 'fixtures/comment'
|
||||
require 'fixtures/author'
|
||||
require 'fixtures/category'
|
||||
require 'fixtures/categorization'
|
||||
|
||||
class AssociationsJoinModelTest < Test::Unit::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
fixtures :posts, :authors, :categories, :categorizations, :comments, :tags, :taggings, :author_favorites
|
||||
|
||||
def test_has_many
|
||||
assert authors(:david).categories.include?(categories(:general))
|
||||
end
|
||||
|
||||
def test_has_many_inherited
|
||||
assert authors(:mary).categories.include?(categories(:sti_test))
|
||||
end
|
||||
|
||||
def test_inherited_has_many
|
||||
assert categories(:sti_test).authors.include?(authors(:mary))
|
||||
end
|
||||
|
||||
def test_has_many_uniq_through_join_model
|
||||
assert_equal 2, authors(:mary).categorized_posts.size
|
||||
assert_equal 1, authors(:mary).unique_categorized_posts.size
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many
|
||||
assert posts(:welcome).taggings.include?(taggings(:welcome_general))
|
||||
end
|
||||
|
||||
def test_polymorphic_has_one
|
||||
assert_equal taggings(:welcome_general), posts(:welcome).tagging
|
||||
end
|
||||
|
||||
def test_polymorphic_belongs_to
|
||||
assert_equal posts(:welcome), posts(:welcome).taggings.first.taggable
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_going_through_join_model
|
||||
assert_equal tags(:general), tag = posts(:welcome).tags.first
|
||||
assert_no_queries do
|
||||
tag.tagging
|
||||
end
|
||||
end
|
||||
|
||||
def test_count_polymorphic_has_many
|
||||
assert_equal 1, posts(:welcome).taggings.count
|
||||
assert_equal 1, posts(:welcome).tags.count
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_going_through_join_model_with_find
|
||||
assert_equal tags(:general), tag = posts(:welcome).tags.find(:first)
|
||||
assert_no_queries do
|
||||
tag.tagging
|
||||
end
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_going_through_join_model_with_include_on_source_reflection
|
||||
assert_equal tags(:general), tag = posts(:welcome).funky_tags.first
|
||||
assert_no_queries do
|
||||
tag.tagging
|
||||
end
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_going_through_join_model_with_include_on_source_reflection_with_find
|
||||
assert_equal tags(:general), tag = posts(:welcome).funky_tags.find(:first)
|
||||
assert_no_queries do
|
||||
tag.tagging
|
||||
end
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_going_through_join_model_with_disabled_include
|
||||
assert_equal tags(:general), tag = posts(:welcome).tags.add_joins_and_select.first
|
||||
assert_queries 1 do
|
||||
tag.tagging
|
||||
end
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_going_through_join_model_with_custom_select_and_joins
|
||||
assert_equal tags(:general), tag = posts(:welcome).tags.add_joins_and_select.first
|
||||
tag.author_id
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_going_through_join_model_with_custom_foreign_key
|
||||
assert_equal tags(:misc), taggings(:welcome_general).super_tag
|
||||
assert_equal tags(:misc), posts(:welcome).super_tags.first
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_create_model_with_inheritance_and_custom_base_class
|
||||
post = SubStiPost.create :title => 'SubStiPost', :body => 'SubStiPost body'
|
||||
assert_instance_of SubStiPost, post
|
||||
|
||||
tagging = tags(:misc).taggings.create(:taggable => post)
|
||||
assert_equal "SubStiPost", tagging.taggable_type
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_going_through_join_model_with_inheritance
|
||||
assert_equal tags(:general), posts(:thinking).tags.first
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_going_through_join_model_with_inheritance_with_custom_class_name
|
||||
assert_equal tags(:general), posts(:thinking).funky_tags.first
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_create_model_with_inheritance
|
||||
post = posts(:thinking)
|
||||
assert_instance_of SpecialPost, post
|
||||
|
||||
tagging = tags(:misc).taggings.create(:taggable => post)
|
||||
assert_equal "Post", tagging.taggable_type
|
||||
end
|
||||
|
||||
def test_polymorphic_has_one_create_model_with_inheritance
|
||||
tagging = tags(:misc).create_tagging(:taggable => posts(:thinking))
|
||||
assert_equal "Post", tagging.taggable_type
|
||||
end
|
||||
|
||||
def test_set_polymorphic_has_many
|
||||
tagging = tags(:misc).taggings.create
|
||||
posts(:thinking).taggings << tagging
|
||||
assert_equal "Post", tagging.taggable_type
|
||||
end
|
||||
|
||||
def test_set_polymorphic_has_one
|
||||
tagging = tags(:misc).taggings.create
|
||||
posts(:thinking).tagging = tagging
|
||||
assert_equal "Post", tagging.taggable_type
|
||||
end
|
||||
|
||||
def test_create_polymorphic_has_many_with_scope
|
||||
old_count = posts(:welcome).taggings.count
|
||||
tagging = posts(:welcome).taggings.create(:tag => tags(:misc))
|
||||
assert_equal "Post", tagging.taggable_type
|
||||
assert_equal old_count+1, posts(:welcome).taggings.count
|
||||
end
|
||||
|
||||
def test_create_bang_polymorphic_with_has_many_scope
|
||||
old_count = posts(:welcome).taggings.count
|
||||
tagging = posts(:welcome).taggings.create!(:tag => tags(:misc))
|
||||
assert_equal "Post", tagging.taggable_type
|
||||
assert_equal old_count+1, posts(:welcome).taggings.count
|
||||
end
|
||||
|
||||
def test_create_polymorphic_has_one_with_scope
|
||||
old_count = Tagging.count
|
||||
tagging = posts(:welcome).tagging.create(:tag => tags(:misc))
|
||||
assert_equal "Post", tagging.taggable_type
|
||||
assert_equal old_count+1, Tagging.count
|
||||
end
|
||||
|
||||
def test_delete_polymorphic_has_many_with_delete_all
|
||||
assert_equal 1, posts(:welcome).taggings.count
|
||||
posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyDeleteAll'
|
||||
post = find_post_with_dependency(1, :has_many, :taggings, :delete_all)
|
||||
|
||||
old_count = Tagging.count
|
||||
post.destroy
|
||||
assert_equal old_count-1, Tagging.count
|
||||
assert_equal 0, posts(:welcome).taggings.count
|
||||
end
|
||||
|
||||
def test_delete_polymorphic_has_many_with_destroy
|
||||
assert_equal 1, posts(:welcome).taggings.count
|
||||
posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyDestroy'
|
||||
post = find_post_with_dependency(1, :has_many, :taggings, :destroy)
|
||||
|
||||
old_count = Tagging.count
|
||||
post.destroy
|
||||
assert_equal old_count-1, Tagging.count
|
||||
assert_equal 0, posts(:welcome).taggings.count
|
||||
end
|
||||
|
||||
def test_delete_polymorphic_has_many_with_nullify
|
||||
assert_equal 1, posts(:welcome).taggings.count
|
||||
posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyNullify'
|
||||
post = find_post_with_dependency(1, :has_many, :taggings, :nullify)
|
||||
|
||||
old_count = Tagging.count
|
||||
post.destroy
|
||||
assert_equal old_count, Tagging.count
|
||||
assert_equal 0, posts(:welcome).taggings.count
|
||||
end
|
||||
|
||||
def test_delete_polymorphic_has_one_with_destroy
|
||||
assert posts(:welcome).tagging
|
||||
posts(:welcome).tagging.update_attribute :taggable_type, 'PostWithHasOneDestroy'
|
||||
post = find_post_with_dependency(1, :has_one, :tagging, :destroy)
|
||||
|
||||
old_count = Tagging.count
|
||||
post.destroy
|
||||
assert_equal old_count-1, Tagging.count
|
||||
assert_nil posts(:welcome).tagging(true)
|
||||
end
|
||||
|
||||
def test_delete_polymorphic_has_one_with_nullify
|
||||
assert posts(:welcome).tagging
|
||||
posts(:welcome).tagging.update_attribute :taggable_type, 'PostWithHasOneNullify'
|
||||
post = find_post_with_dependency(1, :has_one, :tagging, :nullify)
|
||||
|
||||
old_count = Tagging.count
|
||||
post.destroy
|
||||
assert_equal old_count, Tagging.count
|
||||
assert_nil posts(:welcome).tagging(true)
|
||||
end
|
||||
|
||||
def test_has_many_with_piggyback
|
||||
assert_equal "2", categories(:sti_test).authors.first.post_id.to_s
|
||||
end
|
||||
|
||||
def test_include_has_many_through
|
||||
posts = Post.find(:all, :order => 'posts.id')
|
||||
posts_with_authors = Post.find(:all, :include => :authors, :order => 'posts.id')
|
||||
assert_equal posts.length, posts_with_authors.length
|
||||
posts.length.times do |i|
|
||||
assert_equal posts[i].authors.length, assert_no_queries { posts_with_authors[i].authors.length }
|
||||
end
|
||||
end
|
||||
|
||||
def test_include_polymorphic_has_one
|
||||
post = Post.find_by_id(posts(:welcome).id, :include => :tagging)
|
||||
tagging = taggings(:welcome_general)
|
||||
assert_no_queries do
|
||||
assert_equal tagging, post.tagging
|
||||
end
|
||||
end
|
||||
|
||||
def test_include_polymorphic_has_many_through
|
||||
posts = Post.find(:all, :order => 'posts.id')
|
||||
posts_with_tags = Post.find(:all, :include => :tags, :order => 'posts.id')
|
||||
assert_equal posts.length, posts_with_tags.length
|
||||
posts.length.times do |i|
|
||||
assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length }
|
||||
end
|
||||
end
|
||||
|
||||
def test_include_polymorphic_has_many
|
||||
posts = Post.find(:all, :order => 'posts.id')
|
||||
posts_with_taggings = Post.find(:all, :include => :taggings, :order => 'posts.id')
|
||||
assert_equal posts.length, posts_with_taggings.length
|
||||
posts.length.times do |i|
|
||||
assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length }
|
||||
end
|
||||
end
|
||||
|
||||
def test_has_many_find_all
|
||||
assert_equal [categories(:general)], authors(:david).categories.find(:all)
|
||||
end
|
||||
|
||||
def test_has_many_find_first
|
||||
assert_equal categories(:general), authors(:david).categories.find(:first)
|
||||
end
|
||||
|
||||
def test_has_many_with_hash_conditions
|
||||
assert_equal categories(:general), authors(:david).categories_like_general.find(:first)
|
||||
end
|
||||
|
||||
def test_has_many_find_conditions
|
||||
assert_equal categories(:general), authors(:david).categories.find(:first, :conditions => "categories.name = 'General'")
|
||||
assert_equal nil, authors(:david).categories.find(:first, :conditions => "categories.name = 'Technology'")
|
||||
end
|
||||
|
||||
def test_has_many_class_methods_called_by_method_missing
|
||||
assert_equal categories(:general), authors(:david).categories.find_all_by_name('General').first
|
||||
# assert_equal nil, authors(:david).categories.find_by_name('Technology')
|
||||
end
|
||||
|
||||
def test_has_many_going_through_join_model_with_custom_foreign_key
|
||||
assert_equal [], posts(:thinking).authors
|
||||
assert_equal [authors(:mary)], posts(:authorless).authors
|
||||
end
|
||||
|
||||
def test_belongs_to_polymorphic_with_counter_cache
|
||||
assert_equal 0, posts(:welcome)[:taggings_count]
|
||||
tagging = posts(:welcome).taggings.create(:tag => tags(:general))
|
||||
assert_equal 1, posts(:welcome, :reload)[:taggings_count]
|
||||
tagging.destroy
|
||||
assert posts(:welcome, :reload)[:taggings_count].zero?
|
||||
end
|
||||
|
||||
def test_unavailable_through_reflection
|
||||
assert_raises (ActiveRecord::HasManyThroughAssociationNotFoundError) { authors(:david).nothings }
|
||||
end
|
||||
|
||||
def test_has_many_through_join_model_with_conditions
|
||||
assert_equal [], posts(:welcome).invalid_taggings
|
||||
assert_equal [], posts(:welcome).invalid_tags
|
||||
end
|
||||
|
||||
def test_has_many_polymorphic
|
||||
assert_raises ActiveRecord::HasManyThroughAssociationPolymorphicError do
|
||||
assert_equal [posts(:welcome), posts(:thinking)], tags(:general).taggables
|
||||
end
|
||||
assert_raises ActiveRecord::EagerLoadPolymorphicError do
|
||||
assert_equal [posts(:welcome), posts(:thinking)], tags(:general).taggings.find(:all, :include => :taggable)
|
||||
end
|
||||
end
|
||||
|
||||
def test_has_many_through_has_many_find_all
|
||||
assert_equal comments(:greetings), authors(:david).comments.find(:all, :order => 'comments.id').first
|
||||
end
|
||||
|
||||
def test_has_many_through_has_many_find_all_with_custom_class
|
||||
assert_equal comments(:greetings), authors(:david).funky_comments.find(:all, :order => 'comments.id').first
|
||||
end
|
||||
|
||||
def test_has_many_through_has_many_find_first
|
||||
assert_equal comments(:greetings), authors(:david).comments.find(:first, :order => 'comments.id')
|
||||
end
|
||||
|
||||
def test_has_many_through_has_many_find_conditions
|
||||
options = { :conditions => "comments.#{QUOTED_TYPE}='SpecialComment'", :order => 'comments.id' }
|
||||
assert_equal comments(:does_it_hurt), authors(:david).comments.find(:first, options)
|
||||
end
|
||||
|
||||
def test_has_many_through_has_many_find_by_id
|
||||
assert_equal comments(:more_greetings), authors(:david).comments.find(2)
|
||||
end
|
||||
|
||||
def test_has_many_through_polymorphic_has_one
|
||||
assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).tagging }
|
||||
end
|
||||
|
||||
def test_has_many_through_polymorphic_has_many
|
||||
assert_equal [taggings(:welcome_general), taggings(:thinking_general)], authors(:david).taggings.uniq.sort_by { |t| t.id }
|
||||
end
|
||||
|
||||
def test_include_has_many_through_polymorphic_has_many
|
||||
author = Author.find_by_id(authors(:david).id, :include => :taggings)
|
||||
expected_taggings = [taggings(:welcome_general), taggings(:thinking_general)]
|
||||
assert_no_queries do
|
||||
assert_equal expected_taggings, author.taggings.uniq.sort_by { |t| t.id }
|
||||
end
|
||||
end
|
||||
|
||||
def test_has_many_through_has_many_through
|
||||
assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).tags }
|
||||
end
|
||||
|
||||
def test_has_many_through_habtm
|
||||
assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).post_categories }
|
||||
end
|
||||
|
||||
def test_eager_load_has_many_through_has_many
|
||||
author = Author.find :first, :conditions => ['name = ?', 'David'], :include => :comments, :order => 'comments.id'
|
||||
SpecialComment.new; VerySpecialComment.new
|
||||
assert_no_queries do
|
||||
assert_equal [1,2,3,5,6,7,8,9,10], author.comments.collect(&:id)
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_load_has_many_through_has_many_with_conditions
|
||||
post = Post.find(:first, :include => :invalid_tags)
|
||||
assert_no_queries do
|
||||
post.invalid_tags
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_belongs_to_and_has_one_not_singularized
|
||||
assert_nothing_raised do
|
||||
Author.find(:first, :include => :author_address)
|
||||
AuthorAddress.find(:first, :include => :author)
|
||||
end
|
||||
end
|
||||
|
||||
def test_self_referential_has_many_through
|
||||
assert_equal [authors(:mary)], authors(:david).favorite_authors
|
||||
assert_equal [], authors(:mary).favorite_authors
|
||||
end
|
||||
|
||||
def test_add_to_self_referential_has_many_through
|
||||
new_author = Author.create(:name => "Bob")
|
||||
authors(:david).author_favorites.create :favorite_author => new_author
|
||||
assert_equal new_author, authors(:david).reload.favorite_authors.first
|
||||
end
|
||||
|
||||
def test_has_many_through_uses_correct_attributes
|
||||
assert_nil posts(:thinking).tags.find_by_name("General").attributes["tag_id"]
|
||||
end
|
||||
|
||||
def test_raise_error_when_adding_new_record_to_has_many_through
|
||||
assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).tags << tags(:general).clone }
|
||||
assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).clone.tags << tags(:general) }
|
||||
assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).tags.build }
|
||||
end
|
||||
|
||||
def test_create_associate_when_adding_to_has_many_through
|
||||
count = posts(:thinking).tags.count
|
||||
push = Tag.create!(:name => 'pushme')
|
||||
post_thinking = posts(:thinking)
|
||||
assert_nothing_raised { post_thinking.tags << push }
|
||||
assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
|
||||
message = "Expected a Tag in tags collection, got #{wrong.class}.")
|
||||
assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
|
||||
message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
|
||||
assert_equal(count + 1, post_thinking.tags.size)
|
||||
assert_equal(count + 1, post_thinking.tags(true).size)
|
||||
|
||||
assert_nothing_raised { post_thinking.tags.create!(:name => 'foo') }
|
||||
assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
|
||||
message = "Expected a Tag in tags collection, got #{wrong.class}.")
|
||||
assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
|
||||
message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
|
||||
assert_equal(count + 2, post_thinking.tags.size)
|
||||
assert_equal(count + 2, post_thinking.tags(true).size)
|
||||
|
||||
assert_nothing_raised { post_thinking.tags.concat(Tag.create!(:name => 'abc'), Tag.create!(:name => 'def')) }
|
||||
assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
|
||||
message = "Expected a Tag in tags collection, got #{wrong.class}.")
|
||||
assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
|
||||
message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
|
||||
assert_equal(count + 4, post_thinking.tags.size)
|
||||
assert_equal(count + 4, post_thinking.tags(true).size)
|
||||
end
|
||||
|
||||
def test_adding_junk_to_has_many_through_should_raise_type_mismatch
|
||||
assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:thinking).tags << "Uhh what now?" }
|
||||
end
|
||||
|
||||
def test_adding_to_has_many_through_should_return_self
|
||||
tags = posts(:thinking).tags
|
||||
assert_equal tags, posts(:thinking).tags.push(tags(:general))
|
||||
end
|
||||
|
||||
def test_delete_associate_when_deleting_from_has_many_through
|
||||
count = posts(:thinking).tags.count
|
||||
tags_before = posts(:thinking).tags
|
||||
tag = Tag.create!(:name => 'doomed')
|
||||
post_thinking = posts(:thinking)
|
||||
post_thinking.tags << tag
|
||||
assert_equal(count + 1, post_thinking.tags(true).size)
|
||||
|
||||
assert_nothing_raised { post_thinking.tags.delete(tag) }
|
||||
assert_equal(count, post_thinking.tags.size)
|
||||
assert_equal(count, post_thinking.tags(true).size)
|
||||
assert_equal(tags_before.sort, post_thinking.tags.sort)
|
||||
end
|
||||
|
||||
def test_delete_associate_when_deleting_from_has_many_through_with_multiple_tags
|
||||
count = posts(:thinking).tags.count
|
||||
tags_before = posts(:thinking).tags
|
||||
doomed = Tag.create!(:name => 'doomed')
|
||||
doomed2 = Tag.create!(:name => 'doomed2')
|
||||
quaked = Tag.create!(:name => 'quaked')
|
||||
post_thinking = posts(:thinking)
|
||||
post_thinking.tags << doomed << doomed2
|
||||
assert_equal(count + 2, post_thinking.tags(true).size)
|
||||
|
||||
assert_nothing_raised { post_thinking.tags.delete(doomed, doomed2, quaked) }
|
||||
assert_equal(count, post_thinking.tags.size)
|
||||
assert_equal(count, post_thinking.tags(true).size)
|
||||
assert_equal(tags_before.sort, post_thinking.tags.sort)
|
||||
end
|
||||
|
||||
def test_deleting_junk_from_has_many_through_should_raise_type_mismatch
|
||||
assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:thinking).tags.delete("Uhh what now?") }
|
||||
end
|
||||
|
||||
def test_has_many_through_sum_uses_calculations
|
||||
assert_nothing_raised { authors(:david).comments.sum(:post_id) }
|
||||
end
|
||||
|
||||
def test_has_many_through_has_many_with_sti
|
||||
assert_equal [comments(:does_it_hurt)], authors(:david).special_post_comments
|
||||
end
|
||||
|
||||
private
|
||||
# create dynamic Post models to allow different dependency options
|
||||
def find_post_with_dependency(post_id, association, association_name, dependency)
|
||||
class_name = "PostWith#{association.to_s.classify}#{dependency.to_s.classify}"
|
||||
Post.find(post_id).update_attribute :type, class_name
|
||||
klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
|
||||
klass.set_table_name 'posts'
|
||||
klass.send(association, association_name, :as => :taggable, :dependent => dependency)
|
||||
klass.find(post_id)
|
||||
end
|
||||
end
|
49
vendor/rails/activerecord/test/attribute_methods_test.rb
vendored
Executable file
49
vendor/rails/activerecord/test/attribute_methods_test.rb
vendored
Executable file
|
@ -0,0 +1,49 @@
|
|||
require 'abstract_unit'
|
||||
|
||||
class AttributeMethodsTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@old_suffixes = ActiveRecord::Base.send(:attribute_method_suffixes).dup
|
||||
@target = Class.new(ActiveRecord::Base)
|
||||
@target.table_name = 'topics'
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveRecord::Base.send(:attribute_method_suffixes).clear
|
||||
ActiveRecord::Base.attribute_method_suffix *@old_suffixes
|
||||
end
|
||||
|
||||
|
||||
def test_match_attribute_method_query_returns_match_data
|
||||
assert_not_nil md = @target.match_attribute_method?('title=')
|
||||
assert_equal 'title', md.pre_match
|
||||
assert_equal ['='], md.captures
|
||||
|
||||
%w(_hello_world ist! _maybe?).each do |suffix|
|
||||
@target.class_eval "def attribute#{suffix}(*args) args end"
|
||||
@target.attribute_method_suffix suffix
|
||||
|
||||
assert_not_nil md = @target.match_attribute_method?("title#{suffix}")
|
||||
assert_equal 'title', md.pre_match
|
||||
assert_equal [suffix], md.captures
|
||||
end
|
||||
end
|
||||
|
||||
def test_declared_attribute_method_affects_respond_to_and_method_missing
|
||||
topic = @target.new(:title => 'Budget')
|
||||
assert topic.respond_to?('title')
|
||||
assert_equal 'Budget', topic.title
|
||||
assert !topic.respond_to?('title_hello_world')
|
||||
assert_raise(NoMethodError) { topic.title_hello_world }
|
||||
|
||||
%w(_hello_world _it! _candidate= able?).each do |suffix|
|
||||
@target.class_eval "def attribute#{suffix}(*args) args end"
|
||||
@target.attribute_method_suffix suffix
|
||||
|
||||
meth = "title#{suffix}"
|
||||
assert topic.respond_to?(meth)
|
||||
assert_equal ['title'], topic.send(meth)
|
||||
assert_equal ['title', 'a'], topic.send(meth, 'a')
|
||||
assert_equal ['title', 1, 2, 3], topic.send(meth, 1, 2, 3)
|
||||
end
|
||||
end
|
||||
end
|
8
vendor/rails/activerecord/test/connection_test_firebird.rb
vendored
Normal file
8
vendor/rails/activerecord/test/connection_test_firebird.rb
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
require 'abstract_unit'
|
||||
|
||||
class ConnectionTest < Test::Unit::TestCase
|
||||
def test_charset_properly_set
|
||||
fb_conn = ActiveRecord::Base.connection.instance_variable_get(:@connection)
|
||||
assert_equal 'UTF8', fb_conn.database.character_set
|
||||
end
|
||||
end
|
27
vendor/rails/activerecord/test/connections/native_frontbase/connection.rb
vendored
Normal file
27
vendor/rails/activerecord/test/connections/native_frontbase/connection.rb
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
puts 'Using native Frontbase'
|
||||
require_dependency 'fixtures/course'
|
||||
require 'logger'
|
||||
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
|
||||
ActiveRecord::Base.configurations = {
|
||||
'arunit' => {
|
||||
:adapter => 'frontbase',
|
||||
:host => 'localhost',
|
||||
:username => 'rails',
|
||||
:password => '',
|
||||
:database => 'activerecord_unittest',
|
||||
:session_name => "unittest-#{$$}"
|
||||
},
|
||||
'arunit2' => {
|
||||
:adapter => 'frontbase',
|
||||
:host => 'localhost',
|
||||
:username => 'rails',
|
||||
:password => '',
|
||||
:database => 'activerecord_unittest2',
|
||||
:session_name => "unittest-#{$$}"
|
||||
}
|
||||
}
|
||||
|
||||
ActiveRecord::Base.establish_connection 'arunit'
|
||||
Course.establish_connection 'arunit2'
|
52
vendor/rails/activerecord/test/datatype_test_postgresql.rb
vendored
Normal file
52
vendor/rails/activerecord/test/datatype_test_postgresql.rb
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
require 'abstract_unit'
|
||||
|
||||
class PostgresqlDatatype < ActiveRecord::Base
|
||||
end
|
||||
|
||||
class PGDataTypeTest < Test::Unit::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
TABLE_NAME = 'postgresql_datatypes'
|
||||
COLUMNS = [
|
||||
'id SERIAL PRIMARY KEY',
|
||||
'commission_by_quarter INTEGER[]',
|
||||
'nicknames TEXT[]'
|
||||
]
|
||||
|
||||
def setup
|
||||
@connection = ActiveRecord::Base.connection
|
||||
@connection.execute "CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})"
|
||||
@connection.execute "INSERT INTO #{TABLE_NAME} (commission_by_quarter, nicknames) VALUES ( '{35000,21000,18000,17000}', '{foo,bar,baz}' )"
|
||||
@first = PostgresqlDatatype.find( 1 )
|
||||
end
|
||||
|
||||
def teardown
|
||||
@connection.execute "DROP TABLE #{TABLE_NAME}"
|
||||
end
|
||||
|
||||
def test_data_type_of_array_types
|
||||
assert_equal :string, @first.column_for_attribute("commission_by_quarter").type
|
||||
assert_equal :string, @first.column_for_attribute("nicknames").type
|
||||
end
|
||||
|
||||
def test_array_values
|
||||
assert_equal '{35000,21000,18000,17000}', @first.commission_by_quarter
|
||||
assert_equal '{foo,bar,baz}', @first.nicknames
|
||||
end
|
||||
|
||||
def test_update_integer_array
|
||||
new_value = '{32800,95000,29350,17000}'
|
||||
assert @first.commission_by_quarter = new_value
|
||||
assert @first.save
|
||||
assert @first.reload
|
||||
assert_equal @first.commission_by_quarter, new_value
|
||||
end
|
||||
|
||||
def test_update_text_array
|
||||
new_value = '{robby,robert,rob,robbie}'
|
||||
assert @first.nicknames = new_value
|
||||
assert @first.save
|
||||
assert @first.reload
|
||||
assert_equal @first.nicknames, new_value
|
||||
end
|
||||
end
|
25
vendor/rails/activerecord/test/empty_date_time_test.rb
vendored
Normal file
25
vendor/rails/activerecord/test/empty_date_time_test.rb
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
require 'abstract_unit'
|
||||
require 'fixtures/topic'
|
||||
require 'fixtures/task'
|
||||
|
||||
class EmptyDateTimeTest < Test::Unit::TestCase
|
||||
def test_assign_empty_date_time
|
||||
task = Task.new
|
||||
task.starting = ''
|
||||
task.ending = nil
|
||||
assert_nil task.starting
|
||||
assert_nil task.ending
|
||||
end
|
||||
|
||||
def test_assign_empty_date
|
||||
topic = Topic.new
|
||||
topic.last_read = ''
|
||||
assert_nil topic.last_read
|
||||
end
|
||||
|
||||
def test_assign_empty_time
|
||||
topic = Topic.new
|
||||
topic.bonus_time = ''
|
||||
assert_nil topic.bonus_time
|
||||
end
|
||||
end
|
31
vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.drop.sql
vendored
Normal file
31
vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.drop.sql
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
DROP TABLE accounts CASCADE;
|
||||
DROP TABLE funny_jokes CASCADE;
|
||||
DROP TABLE companies CASCADE;
|
||||
DROP TABLE topics CASCADE;
|
||||
DROP TABLE developers CASCADE;
|
||||
DROP TABLE projects CASCADE;
|
||||
DROP TABLE developers_projects CASCADE;
|
||||
DROP TABLE orders CASCADE;
|
||||
DROP TABLE customers CASCADE;
|
||||
DROP TABLE movies CASCADE;
|
||||
DROP TABLE subscribers CASCADE;
|
||||
DROP TABLE booleantests CASCADE;
|
||||
DROP TABLE auto_id_tests CASCADE;
|
||||
DROP TABLE entrants CASCADE;
|
||||
DROP TABLE colnametests CASCADE;
|
||||
DROP TABLE mixins CASCADE;
|
||||
DROP TABLE people CASCADE;
|
||||
DROP TABLE readers CASCADE;
|
||||
DROP TABLE binaries CASCADE;
|
||||
DROP TABLE computers CASCADE;
|
||||
DROP TABLE posts CASCADE;
|
||||
DROP TABLE comments CASCADE;
|
||||
DROP TABLE authors CASCADE;
|
||||
DROP TABLE tasks CASCADE;
|
||||
DROP TABLE categories CASCADE;
|
||||
DROP TABLE categories_posts CASCADE;
|
||||
DROP TABLE fk_test_has_fk CASCADE;
|
||||
DROP TABLE fk_test_has_pk CASCADE;
|
||||
DROP TABLE keyboards CASCADE;
|
||||
DROP TABLE legacy_things CASCADE;
|
||||
DROP TABLE numeric_data CASCADE;
|
262
vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.sql
vendored
Normal file
262
vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.sql
vendored
Normal file
|
@ -0,0 +1,262 @@
|
|||
CREATE TABLE accounts (
|
||||
id integer DEFAULT unique,
|
||||
firm_id integer,
|
||||
credit_limit integer,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR accounts(id);
|
||||
|
||||
CREATE TABLE funny_jokes (
|
||||
id integer DEFAULT unique,
|
||||
firm_id integer default NULL,
|
||||
name character varying(50),
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR funny_jokes(id);
|
||||
|
||||
CREATE TABLE companies (
|
||||
id integer DEFAULT unique,
|
||||
"type" character varying(50),
|
||||
"ruby_type" character varying(50),
|
||||
firm_id integer,
|
||||
name character varying(50),
|
||||
client_of integer,
|
||||
rating integer default 1,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR companies(id);
|
||||
|
||||
CREATE TABLE topics (
|
||||
id integer DEFAULT unique,
|
||||
title character varying(255),
|
||||
author_name character varying(255),
|
||||
author_email_address character varying(255),
|
||||
written_on timestamp,
|
||||
bonus_time time,
|
||||
last_read date,
|
||||
content varchar(65536),
|
||||
approved boolean default true,
|
||||
replies_count integer default 0,
|
||||
parent_id integer,
|
||||
"type" character varying(50),
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR topics(id);
|
||||
|
||||
CREATE TABLE developers (
|
||||
id integer DEFAULT unique,
|
||||
name character varying(100),
|
||||
salary integer DEFAULT 70000,
|
||||
created_at timestamp,
|
||||
updated_at timestamp,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR developers(id);
|
||||
|
||||
CREATE TABLE projects (
|
||||
id integer DEFAULT unique,
|
||||
name character varying(100),
|
||||
type varchar(255),
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR projects(id);
|
||||
|
||||
CREATE TABLE developers_projects (
|
||||
developer_id integer NOT NULL,
|
||||
project_id integer NOT NULL,
|
||||
joined_on date,
|
||||
access_level integer default 1
|
||||
);
|
||||
|
||||
CREATE TABLE orders (
|
||||
id integer DEFAULT unique,
|
||||
name character varying(100),
|
||||
billing_customer_id integer,
|
||||
shipping_customer_id integer,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR orders(id);
|
||||
|
||||
CREATE TABLE customers (
|
||||
id integer DEFAULT unique,
|
||||
name character varying(100),
|
||||
balance integer default 0,
|
||||
address_street character varying(100),
|
||||
address_city character varying(100),
|
||||
address_country character varying(100),
|
||||
gps_location character varying(100),
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR customers(id);
|
||||
|
||||
CREATE TABLE movies (
|
||||
movieid integer DEFAULT unique,
|
||||
name varchar(65536),
|
||||
PRIMARY KEY (movieid)
|
||||
);
|
||||
SET UNIQUE FOR movies(movieid);
|
||||
|
||||
CREATE TABLE subscribers (
|
||||
nick varchar(65536) NOT NULL,
|
||||
name varchar(65536),
|
||||
PRIMARY KEY (nick)
|
||||
);
|
||||
|
||||
CREATE TABLE booleantests (
|
||||
id integer DEFAULT unique,
|
||||
value boolean,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR booleantests(id);
|
||||
|
||||
CREATE TABLE auto_id_tests (
|
||||
auto_id integer DEFAULT unique,
|
||||
value integer,
|
||||
PRIMARY KEY (auto_id)
|
||||
);
|
||||
SET UNIQUE FOR auto_id_tests(auto_id);
|
||||
|
||||
CREATE TABLE entrants (
|
||||
id integer DEFAULT unique,
|
||||
name varchar(65536),
|
||||
course_id integer,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR entrants(id);
|
||||
|
||||
CREATE TABLE colnametests (
|
||||
id integer DEFAULT unique,
|
||||
"references" integer NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR colnametests(id);
|
||||
|
||||
CREATE TABLE mixins (
|
||||
id integer DEFAULT unique,
|
||||
parent_id integer,
|
||||
type character varying(100),
|
||||
pos integer,
|
||||
lft integer,
|
||||
rgt integer,
|
||||
root_id integer,
|
||||
created_at timestamp,
|
||||
updated_at timestamp,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR mixins(id);
|
||||
|
||||
CREATE TABLE people (
|
||||
id integer DEFAULT unique,
|
||||
first_name varchar(65536),
|
||||
lock_version integer default 0,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR people(id);
|
||||
|
||||
CREATE TABLE readers (
|
||||
id integer DEFAULT unique,
|
||||
post_id INTEGER NOT NULL,
|
||||
person_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR readers(id);
|
||||
|
||||
CREATE TABLE binaries (
|
||||
id integer DEFAULT unique,
|
||||
data BLOB,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR binaries(id);
|
||||
|
||||
CREATE TABLE computers (
|
||||
id integer DEFAULT unique,
|
||||
developer integer NOT NULL,
|
||||
"extendedWarranty" integer NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR computers(id);
|
||||
|
||||
CREATE TABLE posts (
|
||||
id integer DEFAULT unique,
|
||||
author_id integer,
|
||||
title varchar(255),
|
||||
type varchar(255),
|
||||
body varchar(65536),
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR posts(id);
|
||||
|
||||
CREATE TABLE comments (
|
||||
id integer DEFAULT unique,
|
||||
post_id integer,
|
||||
type varchar(255),
|
||||
body varchar(65536),
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR comments(id);
|
||||
|
||||
CREATE TABLE authors (
|
||||
id integer DEFAULT unique,
|
||||
name varchar(255) default NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR authors(id);
|
||||
|
||||
CREATE TABLE tasks (
|
||||
id integer DEFAULT unique,
|
||||
starting timestamp,
|
||||
ending timestamp,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR tasks(id);
|
||||
|
||||
CREATE TABLE categories (
|
||||
id integer DEFAULT unique,
|
||||
name varchar(255),
|
||||
type varchar(255),
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
SET UNIQUE FOR categories(id);
|
||||
|
||||
CREATE TABLE categories_posts (
|
||||
category_id integer NOT NULL,
|
||||
post_id integer NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE fk_test_has_pk (
|
||||
id INTEGER NOT NULL PRIMARY KEY
|
||||
);
|
||||
SET UNIQUE FOR fk_test_has_pk(id);
|
||||
|
||||
CREATE TABLE fk_test_has_fk (
|
||||
id INTEGER NOT NULL PRIMARY KEY,
|
||||
fk_id INTEGER NOT NULL REFERENCES fk_test_has_fk(id)
|
||||
);
|
||||
SET UNIQUE FOR fk_test_has_fk(id);
|
||||
|
||||
CREATE TABLE keyboards (
|
||||
key_number integer DEFAULT unique,
|
||||
"name" character varying(50),
|
||||
PRIMARY KEY (key_number)
|
||||
);
|
||||
SET UNIQUE FOR keyboards(key_number);
|
||||
|
||||
create table "legacy_things"
|
||||
(
|
||||
"id" int,
|
||||
"tps_report_number" int default NULL,
|
||||
"version" int default 0 not null,
|
||||
primary key ("id")
|
||||
);
|
||||
SET UNIQUE FOR legacy_things(id);
|
||||
|
||||
CREATE TABLE "numeric_data" (
|
||||
"id" integer NOT NULL
|
||||
"bank_balance" DECIMAL(10,2),
|
||||
"big_bank_balance" DECIMAL(15,2),
|
||||
"world_population" DECIMAL(10),
|
||||
"my_house_population" DECIMAL(2),
|
||||
"decimal_number_with_default" DECIMAL(3,2) DEFAULT 2.78,
|
||||
primary key ("id")
|
||||
);
|
||||
SET UNIQUE FOR numeric_data(id);
|
1
vendor/rails/activerecord/test/fixtures/db_definitions/frontbase2.drop.sql
vendored
Normal file
1
vendor/rails/activerecord/test/fixtures/db_definitions/frontbase2.drop.sql
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
DROP TABLE courses CASCADE;
|
4
vendor/rails/activerecord/test/fixtures/db_definitions/frontbase2.sql
vendored
Normal file
4
vendor/rails/activerecord/test/fixtures/db_definitions/frontbase2.sql
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
CREATE TABLE courses (
|
||||
id integer DEFAULT unique,
|
||||
name varchar(100)
|
||||
);
|
5
vendor/rails/activerecord/test/fixtures/edge.rb
vendored
Normal file
5
vendor/rails/activerecord/test/fixtures/edge.rb
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
# This class models an edge in a directed graph.
|
||||
class Edge < ActiveRecord::Base
|
||||
belongs_to :source, :class_name => 'Vertex', :foreign_key => 'source_id'
|
||||
belongs_to :sink, :class_name => 'Vertex', :foreign_key => 'sink_id'
|
||||
end
|
6
vendor/rails/activerecord/test/fixtures/edges.yml
vendored
Normal file
6
vendor/rails/activerecord/test/fixtures/edges.yml
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
<% (1..4).each do |id| %>
|
||||
edge_<%= id %>:
|
||||
id: <%= id %>
|
||||
source_id: <%= id %>
|
||||
sink_id: <%= id + 1 %>
|
||||
<% end %>
|
15
vendor/rails/activerecord/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb
vendored
Normal file
15
vendor/rails/activerecord/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
class GiveMeBigNumbers < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :big_numbers do |table|
|
||||
table.column :bank_balance, :decimal, :precision => 10, :scale => 2
|
||||
table.column :big_bank_balance, :decimal, :precision => 15, :scale => 2
|
||||
table.column :world_population, :decimal, :precision => 10
|
||||
table.column :my_house_population, :decimal, :precision => 2
|
||||
table.column :value_of_e, :decimal
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :big_numbers
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
class PeopleHaveMiddleNames < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column "people", "middle_name", :string
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column "people", "middle_name"
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
class PeopleHaveLastNames < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column "people", "last_name", :string
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column "people", "last_name"
|
||||
end
|
||||
end
|
12
vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb
vendored
Normal file
12
vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
class WeNeedReminders < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table("reminders") do |t|
|
||||
t.column :content, :text
|
||||
t.column :remind_at, :datetime
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table "reminders"
|
||||
end
|
||||
end
|
12
vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb
vendored
Normal file
12
vendor/rails/activerecord/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
class InnocentJointable < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table("people_reminders", :id => false) do |t|
|
||||
t.column :reminder_id, :integer
|
||||
t.column :person_id, :integer
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table "people_reminders"
|
||||
end
|
||||
end
|
9
vendor/rails/activerecord/test/fixtures/vertex.rb
vendored
Normal file
9
vendor/rails/activerecord/test/fixtures/vertex.rb
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
# This class models a vertex in a directed graph.
|
||||
class Vertex < ActiveRecord::Base
|
||||
has_many :sink_edges, :class_name => 'Edge', :foreign_key => 'source_id'
|
||||
has_many :sinks, :through => :sink_edges, :source => :sink
|
||||
|
||||
has_and_belongs_to_many :sources,
|
||||
:class_name => 'Vertex', :join_table => 'edges',
|
||||
:foreign_key => 'sink_id', :association_foreign_key => 'source_id'
|
||||
end
|
4
vendor/rails/activerecord/test/fixtures/vertices.yml
vendored
Normal file
4
vendor/rails/activerecord/test/fixtures/vertices.yml
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
<% (1..5).each do |id| %>
|
||||
vertex_<%= id %>:
|
||||
id: <%= id %>
|
||||
<% end %>
|
124
vendor/rails/activerecord/test/migration_test_firebird.rb
vendored
Normal file
124
vendor/rails/activerecord/test/migration_test_firebird.rb
vendored
Normal file
|
@ -0,0 +1,124 @@
|
|||
require 'abstract_unit'
|
||||
require 'fixtures/course'
|
||||
|
||||
class FirebirdMigrationTest < Test::Unit::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
def setup
|
||||
# using Course connection for tests -- need a db that doesn't already have a BOOLEAN domain
|
||||
@connection = Course.connection
|
||||
@fireruby_connection = @connection.instance_variable_get(:@connection)
|
||||
end
|
||||
|
||||
def teardown
|
||||
@connection.drop_table :foo rescue nil
|
||||
@connection.execute("DROP DOMAIN D_BOOLEAN") rescue nil
|
||||
end
|
||||
|
||||
def test_create_table_with_custom_sequence_name
|
||||
assert_nothing_raised do
|
||||
@connection.create_table(:foo, :sequence => 'foo_custom_seq') do |f|
|
||||
f.column :bar, :string
|
||||
end
|
||||
end
|
||||
assert !sequence_exists?('foo_seq')
|
||||
assert sequence_exists?('foo_custom_seq')
|
||||
|
||||
assert_nothing_raised { @connection.drop_table(:foo, :sequence => 'foo_custom_seq') }
|
||||
assert !sequence_exists?('foo_custom_seq')
|
||||
ensure
|
||||
FireRuby::Generator.new('foo_custom_seq', @fireruby_connection).drop rescue nil
|
||||
end
|
||||
|
||||
def test_create_table_without_sequence
|
||||
assert_nothing_raised do
|
||||
@connection.create_table(:foo, :sequence => false) do |f|
|
||||
f.column :bar, :string
|
||||
end
|
||||
end
|
||||
assert !sequence_exists?('foo_seq')
|
||||
assert_nothing_raised { @connection.drop_table :foo }
|
||||
|
||||
assert_nothing_raised do
|
||||
@connection.create_table(:foo, :id => false) do |f|
|
||||
f.column :bar, :string
|
||||
end
|
||||
end
|
||||
assert !sequence_exists?('foo_seq')
|
||||
assert_nothing_raised { @connection.drop_table :foo }
|
||||
end
|
||||
|
||||
def test_create_table_with_boolean_column
|
||||
assert !boolean_domain_exists?
|
||||
assert_nothing_raised do
|
||||
@connection.create_table :foo do |f|
|
||||
f.column :bar, :string
|
||||
f.column :baz, :boolean
|
||||
end
|
||||
end
|
||||
assert boolean_domain_exists?
|
||||
end
|
||||
|
||||
def test_add_boolean_column
|
||||
assert !boolean_domain_exists?
|
||||
@connection.create_table :foo do |f|
|
||||
f.column :bar, :string
|
||||
end
|
||||
|
||||
assert_nothing_raised { @connection.add_column :foo, :baz, :boolean }
|
||||
assert boolean_domain_exists?
|
||||
assert_equal :boolean, @connection.columns(:foo).find { |c| c.name == "baz" }.type
|
||||
end
|
||||
|
||||
def test_change_column_to_boolean
|
||||
assert !boolean_domain_exists?
|
||||
# Manually create table with a SMALLINT column, which can be changed to a BOOLEAN
|
||||
@connection.execute "CREATE TABLE foo (bar SMALLINT)"
|
||||
assert_equal :integer, @connection.columns(:foo).find { |c| c.name == "bar" }.type
|
||||
|
||||
assert_nothing_raised { @connection.change_column :foo, :bar, :boolean }
|
||||
assert boolean_domain_exists?
|
||||
assert_equal :boolean, @connection.columns(:foo).find { |c| c.name == "bar" }.type
|
||||
end
|
||||
|
||||
def test_rename_table_with_data_and_index
|
||||
@connection.create_table :foo do |f|
|
||||
f.column :baz, :string, :limit => 50
|
||||
end
|
||||
100.times { |i| @connection.execute "INSERT INTO foo VALUES (GEN_ID(foo_seq, 1), 'record #{i+1}')" }
|
||||
@connection.add_index :foo, :baz
|
||||
|
||||
assert_nothing_raised { @connection.rename_table :foo, :bar }
|
||||
assert !@connection.tables.include?("foo")
|
||||
assert @connection.tables.include?("bar")
|
||||
assert_equal "index_bar_on_baz", @connection.indexes("bar").first.name
|
||||
assert_equal 100, FireRuby::Generator.new("bar_seq", @fireruby_connection).last
|
||||
assert_equal 100, @connection.select_one("SELECT COUNT(*) FROM bar")["count"]
|
||||
ensure
|
||||
@connection.drop_table :bar rescue nil
|
||||
end
|
||||
|
||||
def test_renaming_table_with_fk_constraint_raises_error
|
||||
@connection.create_table :parent do |p|
|
||||
p.column :name, :string
|
||||
end
|
||||
@connection.create_table :child do |c|
|
||||
c.column :parent_id, :integer
|
||||
end
|
||||
@connection.execute "ALTER TABLE child ADD CONSTRAINT fk_child_parent FOREIGN KEY(parent_id) REFERENCES parent(id)"
|
||||
assert_raise(ActiveRecord::ActiveRecordError) { @connection.rename_table :child, :descendant }
|
||||
ensure
|
||||
@connection.drop_table :child rescue nil
|
||||
@connection.drop_table :descendant rescue nil
|
||||
@connection.drop_table :parent rescue nil
|
||||
end
|
||||
|
||||
private
|
||||
def boolean_domain_exists?
|
||||
!@connection.select_one("SELECT 1 FROM rdb$fields WHERE rdb$field_name = 'D_BOOLEAN'").nil?
|
||||
end
|
||||
|
||||
def sequence_exists?(sequence_name)
|
||||
FireRuby::Generator.exists?(sequence_name, @fireruby_connection)
|
||||
end
|
||||
end
|
75
vendor/rails/activerecord/test/schema_authorization_test_postgresql.rb
vendored
Normal file
75
vendor/rails/activerecord/test/schema_authorization_test_postgresql.rb
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
require 'abstract_unit'
|
||||
|
||||
class SchemaThing < ActiveRecord::Base
|
||||
end
|
||||
|
||||
class SchemaAuthorizationTest < Test::Unit::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
TABLE_NAME = 'schema_things'
|
||||
COLUMNS = [
|
||||
'id serial primary key',
|
||||
'name character varying(50)'
|
||||
]
|
||||
USERS = ['rails_pg_schema_user1', 'rails_pg_schema_user2']
|
||||
|
||||
def setup
|
||||
@connection = ActiveRecord::Base.connection
|
||||
@connection.execute "SET search_path TO '$user',public"
|
||||
set_session_auth
|
||||
USERS.each do |u|
|
||||
@connection.execute "CREATE ROLE #{u}"
|
||||
@connection.execute "CREATE SCHEMA AUTHORIZATION #{u}"
|
||||
set_session_auth u
|
||||
@connection.execute "CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})"
|
||||
@connection.execute "INSERT INTO #{TABLE_NAME} (name) VALUES ('#{u}')"
|
||||
set_session_auth
|
||||
end
|
||||
end
|
||||
|
||||
def teardown
|
||||
set_session_auth
|
||||
@connection.execute "RESET search_path"
|
||||
USERS.each do |u|
|
||||
@connection.execute "DROP SCHEMA #{u} CASCADE"
|
||||
@connection.execute "DROP ROLE #{u}"
|
||||
end
|
||||
end
|
||||
|
||||
def test_schema_invisible
|
||||
assert_raise(ActiveRecord::StatementInvalid) do
|
||||
set_session_auth
|
||||
@connection.execute "SELECT * FROM #{TABLE_NAME}"
|
||||
end
|
||||
end
|
||||
|
||||
def test_schema_uniqueness
|
||||
assert_nothing_raised do
|
||||
set_session_auth
|
||||
USERS.each do |u|
|
||||
set_session_auth u
|
||||
assert_equal u, @connection.select_value("SELECT name FROM #{TABLE_NAME} WHERE id = 1")
|
||||
set_session_auth
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_sequence_schema_caching
|
||||
assert_nothing_raised do
|
||||
USERS.each do |u|
|
||||
set_session_auth u
|
||||
st = SchemaThing.new :name => 'TEST1'
|
||||
st.save!
|
||||
st = SchemaThing.new :id => 5, :name => 'TEST2'
|
||||
st.save!
|
||||
set_session_auth
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def set_session_auth auth = nil
|
||||
@connection.execute "SET SESSION AUTHORIZATION #{auth || 'default'}"
|
||||
end
|
||||
|
||||
end
|
23
vendor/rails/activerecord/test/table_name_test_sqlserver.rb
vendored
Normal file
23
vendor/rails/activerecord/test/table_name_test_sqlserver.rb
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
require 'abstract_unit'
|
||||
require "#{File.dirname(__FILE__)}/../lib/active_record/schema"
|
||||
|
||||
if ActiveRecord::Base.connection.supports_migrations?
|
||||
class Order < ActiveRecord::Base
|
||||
self.table_name = '[order]'
|
||||
end
|
||||
|
||||
class TableNameTest < Test::Unit::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
# Ensures Model.columns works when using SQLServer escape characters.
|
||||
# Enables legacy schemas using SQL reserved words as table names.
|
||||
# Should work with table names with spaces as well ('table name').
|
||||
def test_escaped_table_name
|
||||
assert_nothing_raised do
|
||||
ActiveRecord::Base.connection.select_all 'SELECT * FROM [order]'
|
||||
end
|
||||
assert_equal '[order]', Order.table_name
|
||||
assert_equal 5, Order.columns.length
|
||||
end
|
||||
end
|
||||
end
|
125
vendor/rails/activerecord/test/xml_serialization_test.rb
vendored
Normal file
125
vendor/rails/activerecord/test/xml_serialization_test.rb
vendored
Normal file
|
@ -0,0 +1,125 @@
|
|||
require 'abstract_unit'
|
||||
require 'fixtures/post'
|
||||
require 'fixtures/author'
|
||||
|
||||
class Contact < ActiveRecord::Base
|
||||
# mock out self.columns so no pesky db is needed for these tests
|
||||
def self.columns() @columns ||= []; end
|
||||
def self.column(name, sql_type = nil, default = nil, null = true)
|
||||
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
|
||||
end
|
||||
|
||||
column :name, :string
|
||||
column :age, :integer
|
||||
column :avatar, :binary
|
||||
column :created_at, :datetime
|
||||
column :awesome, :boolean
|
||||
end
|
||||
|
||||
class XmlSerializationTest < Test::Unit::TestCase
|
||||
def test_should_serialize_default_root
|
||||
@xml = Contact.new.to_xml
|
||||
assert_match %r{^<contact>}, @xml
|
||||
assert_match %r{</contact>$}, @xml
|
||||
end
|
||||
|
||||
def test_should_serialize_default_root_with_namespace
|
||||
@xml = Contact.new.to_xml :namespace=>"http://xml.rubyonrails.org/contact"
|
||||
assert_match %r{^<contact xmlns="http://xml.rubyonrails.org/contact">}, @xml
|
||||
assert_match %r{</contact>$}, @xml
|
||||
end
|
||||
|
||||
def test_should_serialize_custom_root
|
||||
@xml = Contact.new.to_xml :root => 'xml_contact'
|
||||
assert_match %r{^<xml-contact>}, @xml
|
||||
assert_match %r{</xml-contact>$}, @xml
|
||||
end
|
||||
|
||||
def test_should_allow_undasherized_tags
|
||||
@xml = Contact.new.to_xml :root => 'xml_contact', :dasherize => false
|
||||
assert_match %r{^<xml_contact>}, @xml
|
||||
assert_match %r{</xml_contact>$}, @xml
|
||||
assert_match %r{<created_at}, @xml
|
||||
end
|
||||
|
||||
def test_should_allow_attribute_filtering
|
||||
@xml = Contact.new.to_xml :only => [:age, :name]
|
||||
assert_match %r{<name}, @xml
|
||||
assert_match %r{<age}, @xml
|
||||
assert_no_match %r{<created-at}, @xml
|
||||
|
||||
@xml = Contact.new.to_xml :except => [:age, :name]
|
||||
assert_no_match %r{<name}, @xml
|
||||
assert_no_match %r{<age}, @xml
|
||||
assert_match %r{<created-at}, @xml
|
||||
end
|
||||
end
|
||||
|
||||
class DefaultXmlSerializationTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@xml = Contact.new(:name => 'aaron stack', :age => 25, :avatar => 'binarydata', :created_at => Time.utc(2006, 8, 1), :awesome => false).to_xml
|
||||
end
|
||||
|
||||
def test_should_serialize_string
|
||||
assert_match %r{<name>aaron stack</name>}, @xml
|
||||
end
|
||||
|
||||
def test_should_serialize_integer
|
||||
assert_match %r{<age type="integer">25</age>}, @xml
|
||||
end
|
||||
|
||||
def test_should_serialize_binary
|
||||
assert_match %r{YmluYXJ5ZGF0YQ==\n</avatar>}, @xml
|
||||
assert_match %r{<avatar(.*)(type="binary")}, @xml
|
||||
assert_match %r{<avatar(.*)(encoding="base64")}, @xml
|
||||
end
|
||||
|
||||
def test_should_serialize_datetime
|
||||
assert_match %r{<created-at type=\"datetime\">2006-08-01T00:00:00Z</created-at>}, @xml
|
||||
end
|
||||
|
||||
def test_should_serialize_boolean
|
||||
assert_match %r{<awesome type=\"boolean\">false</awesome>}, @xml
|
||||
end
|
||||
end
|
||||
|
||||
class NilXmlSerializationTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@xml = Contact.new.to_xml(:root => 'xml_contact')
|
||||
end
|
||||
|
||||
def test_should_serialize_string
|
||||
assert_match %r{<name></name>}, @xml
|
||||
end
|
||||
|
||||
def test_should_serialize_integer
|
||||
assert_match %r{<age type="integer"></age>}, @xml
|
||||
end
|
||||
|
||||
def test_should_serialize_binary
|
||||
assert_match %r{></avatar>}, @xml
|
||||
assert_match %r{<avatar(.*)(type="binary")}, @xml
|
||||
assert_match %r{<avatar(.*)(encoding="base64")}, @xml
|
||||
end
|
||||
|
||||
def test_should_serialize_datetime
|
||||
assert_match %r{<created-at type=\"datetime\"></created-at>}, @xml
|
||||
end
|
||||
|
||||
def test_should_serialize_boolean
|
||||
assert_match %r{<awesome type=\"boolean\"></awesome>}, @xml
|
||||
end
|
||||
end
|
||||
|
||||
class DatabaseConnectedXmlSerializationTest < Test::Unit::TestCase
|
||||
fixtures :authors, :posts
|
||||
# to_xml used to mess with the hash the user provided which
|
||||
# caused the builder to be reused
|
||||
def test_passing_hash_shouldnt_reuse_builder
|
||||
options = {:include=>:posts}
|
||||
david = authors(:david)
|
||||
first_xml_size = david.to_xml(options).size
|
||||
second_xml_size = david.to_xml(options).size
|
||||
assert_equal first_xml_size, second_xml_size
|
||||
end
|
||||
end
|
20
vendor/rails/activesupport/MIT-LICENSE
vendored
Normal file
20
vendor/rails/activesupport/MIT-LICENSE
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2005-2006 David Heinemeier Hansson
|
||||
|
||||
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.
|
55
vendor/rails/activesupport/lib/active_support/core_ext/array/grouping.rb
vendored
Normal file
55
vendor/rails/activesupport/lib/active_support/core_ext/array/grouping.rb
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
module ActiveSupport #:nodoc:
|
||||
module CoreExtensions #:nodoc:
|
||||
module Array #:nodoc:
|
||||
module Grouping
|
||||
# Iterate over an array in groups of a certain size, padding any remaining
|
||||
# slots with specified value (<tt>nil</tt> by default) unless it is
|
||||
# <tt>false</tt>.
|
||||
#
|
||||
# E.g.
|
||||
#
|
||||
# %w(1 2 3 4 5 6 7).in_groups_of(3) {|g| p g}
|
||||
# ["1", "2", "3"]
|
||||
# ["4", "5", "6"]
|
||||
# ["7", nil, nil]
|
||||
#
|
||||
# %w(1 2 3).in_groups_of(2, ' ') {|g| p g}
|
||||
# ["1", "2"]
|
||||
# ["3", " "]
|
||||
#
|
||||
# %w(1 2 3).in_groups_of(2, false) {|g| p g}
|
||||
# ["1", "2"]
|
||||
# ["3"]
|
||||
def in_groups_of(number, fill_with = nil, &block)
|
||||
require 'enumerator'
|
||||
collection = dup
|
||||
collection << fill_with until collection.size.modulo(number).zero? unless fill_with == false
|
||||
grouped_collection = [] unless block_given?
|
||||
collection.each_slice(number) do |group|
|
||||
block_given? ? yield(group) : grouped_collection << group
|
||||
end
|
||||
grouped_collection unless block_given?
|
||||
end
|
||||
|
||||
# Divide the array into one or more subarrays based on a delimiting +value+
|
||||
# or the result of an optional block.
|
||||
#
|
||||
# ex.
|
||||
#
|
||||
# [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]]
|
||||
# (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]]
|
||||
def split(value = nil, &block)
|
||||
block ||= Proc.new { |e| e == value }
|
||||
inject([[]]) do |results, element|
|
||||
if block.call(element)
|
||||
results << []
|
||||
else
|
||||
results.last << element
|
||||
end
|
||||
results
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
3
vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal.rb
vendored
Normal file
3
vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal.rb
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
require 'bigdecimal'
|
||||
|
||||
require File.dirname(__FILE__) + '/bigdecimal/formatting.rb'
|
7
vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal/formatting.rb
vendored
Normal file
7
vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal/formatting.rb
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
class BigDecimal #:nodoc:
|
||||
|
||||
alias :_original_to_s :to_s
|
||||
def to_s(format="F")
|
||||
_original_to_s(format)
|
||||
end
|
||||
end
|
58
vendor/rails/activesupport/lib/active_support/core_ext/module/aliasing.rb
vendored
Normal file
58
vendor/rails/activesupport/lib/active_support/core_ext/module/aliasing.rb
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
class Module
|
||||
# Encapsulates the common pattern of:
|
||||
#
|
||||
# alias_method :foo_without_feature, :foo
|
||||
# alias_method :foo, :foo_with_feature
|
||||
#
|
||||
# With this, you simply do:
|
||||
#
|
||||
# alias_method_chain :foo, :feature
|
||||
#
|
||||
# And both aliases are set up for you.
|
||||
#
|
||||
# Query and bang methods (foo?, foo!) keep the same punctuation:
|
||||
#
|
||||
# alias_method_chain :foo?, :feature
|
||||
#
|
||||
# is equivalent to
|
||||
#
|
||||
# alias_method :foo_without_feature?, :foo?
|
||||
# alias_method :foo?, :foo_with_feature?
|
||||
#
|
||||
# so you can safely chain foo, foo?, and foo! with the same feature.
|
||||
def alias_method_chain(target, feature)
|
||||
# Strip out punctuation on predicates or bang methods since
|
||||
# e.g. target?_without_feature is not a valid method name.
|
||||
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
|
||||
yield(aliased_target, punctuation) if block_given?
|
||||
alias_method "#{aliased_target}_without_#{feature}#{punctuation}", target
|
||||
alias_method target, "#{aliased_target}_with_#{feature}#{punctuation}"
|
||||
end
|
||||
|
||||
# Allows you to make aliases for attributes, which includes
|
||||
# getter, setter, and query methods.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# class Content < ActiveRecord::Base
|
||||
# # has a title attribute
|
||||
# end
|
||||
#
|
||||
# class Email < ActiveRecord::Base
|
||||
# alias_attribute :subject, :title
|
||||
# end
|
||||
#
|
||||
# e = Email.find(1)
|
||||
# e.title # => "Superstars"
|
||||
# e.subject # => "Superstars"
|
||||
# e.subject? # => true
|
||||
# e.subject = "Megastars"
|
||||
# e.title # => "Megastars"
|
||||
def alias_attribute(new_name, old_name)
|
||||
module_eval <<-STR, __FILE__, __LINE__+1
|
||||
def #{new_name}; #{old_name}; end
|
||||
def #{new_name}?; #{old_name}?; end
|
||||
def #{new_name}=(v); self.#{old_name} = v; end
|
||||
STR
|
||||
end
|
||||
end
|
31
vendor/rails/activesupport/lib/active_support/core_ext/module/attr_internal.rb
vendored
Normal file
31
vendor/rails/activesupport/lib/active_support/core_ext/module/attr_internal.rb
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
class Module
|
||||
# Declare an attribute reader backed by an internally-named instance variable.
|
||||
def attr_internal_reader(*attrs)
|
||||
attrs.each do |attr|
|
||||
module_eval "def #{attr}() #{attr_internal_ivar_name(attr)} end"
|
||||
end
|
||||
end
|
||||
|
||||
# Declare an attribute writer backed by an internally-named instance variable.
|
||||
def attr_internal_writer(*attrs)
|
||||
attrs.each do |attr|
|
||||
module_eval "def #{attr}=(v) #{attr_internal_ivar_name(attr)} = v end"
|
||||
end
|
||||
end
|
||||
|
||||
# Declare attributes backed by 'internal' instance variables names.
|
||||
def attr_internal_accessor(*attrs)
|
||||
attr_internal_reader(*attrs)
|
||||
attr_internal_writer(*attrs)
|
||||
end
|
||||
|
||||
alias_method :attr_internal, :attr_internal_accessor
|
||||
|
||||
private
|
||||
mattr_accessor :attr_internal_naming_format
|
||||
self.attr_internal_naming_format = '@_%s'
|
||||
|
||||
def attr_internal_ivar_name(attr)
|
||||
attr_internal_naming_format % attr
|
||||
end
|
||||
end
|
17
vendor/rails/activesupport/lib/active_support/core_ext/name_error.rb
vendored
Normal file
17
vendor/rails/activesupport/lib/active_support/core_ext/name_error.rb
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Add a +missing_name+ method to NameError instances.
|
||||
class NameError < StandardError #:nodoc:
|
||||
# Add a method to obtain the missing name from a NameError.
|
||||
def missing_name
|
||||
$1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ message
|
||||
end
|
||||
|
||||
# Was this exception raised because the given name was missing?
|
||||
def missing_name?(name)
|
||||
if name.is_a? Symbol
|
||||
last_name = (missing_name || '').split('::').last
|
||||
last_name == name.to_s
|
||||
else
|
||||
missing_name == name.to_s
|
||||
end
|
||||
end
|
||||
end
|
42
vendor/rails/activesupport/lib/active_support/core_ext/string/unicode.rb
vendored
Normal file
42
vendor/rails/activesupport/lib/active_support/core_ext/string/unicode.rb
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
module ActiveSupport #:nodoc:
|
||||
module CoreExtensions #:nodoc:
|
||||
module String #:nodoc:
|
||||
# Define methods for handeling unicode data.
|
||||
module Unicode
|
||||
# +chars+ is a Unicode safe proxy for string methods. It creates and returns an instance of the
|
||||
# ActiveSupport::Multibyte::Chars class which encapsulates the original string. A Unicode safe version of all
|
||||
# the String methods are defined on this proxy class. Undefined methods are forwarded to String, so all of the
|
||||
# string overrides can also be called through the +chars+ proxy.
|
||||
#
|
||||
# name = 'Claus Müller'
|
||||
# name.reverse #=> "rell??M sualC"
|
||||
# name.length #=> 13
|
||||
#
|
||||
# name.chars.reverse.to_s #=> "rellüM sualC"
|
||||
# name.chars.length #=> 12
|
||||
#
|
||||
#
|
||||
# All the methods on the chars proxy which normally return a string will return a Chars object. This allows
|
||||
# method chaining on the result of any of these methods.
|
||||
#
|
||||
# name.chars.reverse.length #=> 12
|
||||
#
|
||||
# The Char object tries to be as interchangeable with String objects as possible: sorting and comparing between
|
||||
# String and Char work like expected. The bang! methods change the internal string representation in the Chars
|
||||
# object. Interoperability problems can be resolved easily with a +to_s+ call.
|
||||
#
|
||||
# For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars and
|
||||
# ActiveSupport::Multibyte::Handlers::UTF8Handler
|
||||
def chars
|
||||
ActiveSupport::Multibyte::Chars.new(self)
|
||||
end
|
||||
|
||||
# Returns true if the string has UTF-8 semantics (a String used for purely byte resources is unlikely to have
|
||||
# them), returns false otherwise.
|
||||
def is_utf8?
|
||||
ActiveSupport::Multibyte::Handlers::UTF8Handler.consumes?(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
201
vendor/rails/activesupport/lib/active_support/deprecation.rb
vendored
Normal file
201
vendor/rails/activesupport/lib/active_support/deprecation.rb
vendored
Normal file
|
@ -0,0 +1,201 @@
|
|||
require 'yaml'
|
||||
|
||||
module ActiveSupport
|
||||
module Deprecation #:nodoc:
|
||||
mattr_accessor :debug
|
||||
self.debug = false
|
||||
|
||||
# Choose the default warn behavior according to RAILS_ENV.
|
||||
# Ignore deprecation warnings in production.
|
||||
DEFAULT_BEHAVIORS = {
|
||||
'test' => Proc.new { |message, callstack|
|
||||
$stderr.puts(message)
|
||||
$stderr.puts callstack.join("\n ") if debug
|
||||
},
|
||||
'development' => Proc.new { |message, callstack|
|
||||
RAILS_DEFAULT_LOGGER.warn message
|
||||
RAILS_DEFAULT_LOGGER.debug callstack.join("\n ") if debug
|
||||
}
|
||||
}
|
||||
|
||||
class << self
|
||||
def warn(message = nil, callstack = caller)
|
||||
behavior.call(deprecation_message(callstack, message), callstack) if behavior && !silenced?
|
||||
end
|
||||
|
||||
def default_behavior
|
||||
if defined?(RAILS_ENV)
|
||||
DEFAULT_BEHAVIORS[RAILS_ENV.to_s]
|
||||
else
|
||||
DEFAULT_BEHAVIORS['test']
|
||||
end
|
||||
end
|
||||
|
||||
# Have deprecations been silenced?
|
||||
def silenced?
|
||||
@silenced = false unless defined?(@silenced)
|
||||
@silenced
|
||||
end
|
||||
|
||||
# Silence deprecation warnings within the block.
|
||||
def silence
|
||||
old_silenced, @silenced = @silenced, true
|
||||
yield
|
||||
ensure
|
||||
@silenced = old_silenced
|
||||
end
|
||||
|
||||
attr_writer :silenced
|
||||
|
||||
|
||||
private
|
||||
def deprecation_message(callstack, message = nil)
|
||||
message ||= "You are using deprecated behavior which will be removed from Rails 2.0."
|
||||
"DEPRECATION WARNING: #{message} See http://www.rubyonrails.org/deprecation for details. #{deprecation_caller_message(callstack)}"
|
||||
end
|
||||
|
||||
def deprecation_caller_message(callstack)
|
||||
file, line, method = extract_callstack(callstack)
|
||||
if file
|
||||
if line && method
|
||||
"(called from #{method} at #{file}:#{line})"
|
||||
else
|
||||
"(called from #{file}:#{line})"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def extract_callstack(callstack)
|
||||
if md = callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
|
||||
md.captures
|
||||
else
|
||||
callstack.first
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Behavior is a block that takes a message argument.
|
||||
mattr_accessor :behavior
|
||||
self.behavior = default_behavior
|
||||
|
||||
# Warnings are not silenced by default.
|
||||
self.silenced = false
|
||||
|
||||
module ClassMethods #:nodoc:
|
||||
# Declare that a method has been deprecated.
|
||||
def deprecate(*method_names)
|
||||
options = method_names.last.is_a?(Hash) ? method_names.pop : {}
|
||||
method_names = method_names + options.keys
|
||||
method_names.each do |method_name|
|
||||
alias_method_chain(method_name, :deprecation) do |target, punctuation|
|
||||
class_eval(<<-EOS, __FILE__, __LINE__)
|
||||
def #{target}_with_deprecation#{punctuation}(*args, &block)
|
||||
::ActiveSupport::Deprecation.warn(self.class.deprecated_method_warning(:#{method_name}, #{options[method_name].inspect}), caller)
|
||||
#{target}_without_deprecation#{punctuation}(*args, &block)
|
||||
end
|
||||
EOS
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def deprecated_method_warning(method_name, message=nil)
|
||||
warning = "#{method_name} is deprecated and will be removed from Rails #{deprecation_horizon}"
|
||||
case message
|
||||
when Symbol then "#{warning} (use #{message} instead)"
|
||||
when String then "#{warning} (#{message})"
|
||||
else warning
|
||||
end
|
||||
end
|
||||
|
||||
def deprecation_horizon
|
||||
'2.0'
|
||||
end
|
||||
end
|
||||
|
||||
module Assertions #:nodoc:
|
||||
def assert_deprecated(match = nil, &block)
|
||||
result, warnings = collect_deprecations(&block)
|
||||
assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
|
||||
if match
|
||||
match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp)
|
||||
assert warnings.any? { |w| w =~ match }, "No deprecation warning matched #{match}: #{warnings.join(', ')}"
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
def assert_not_deprecated(&block)
|
||||
result, deprecations = collect_deprecations(&block)
|
||||
assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
|
||||
result
|
||||
end
|
||||
|
||||
private
|
||||
def collect_deprecations
|
||||
old_behavior = ActiveSupport::Deprecation.behavior
|
||||
deprecations = []
|
||||
ActiveSupport::Deprecation.behavior = Proc.new do |message, callstack|
|
||||
deprecations << message
|
||||
end
|
||||
result = yield
|
||||
[result, deprecations]
|
||||
ensure
|
||||
ActiveSupport::Deprecation.behavior = old_behavior
|
||||
end
|
||||
end
|
||||
|
||||
# Stand-in for @request, @attributes, @params, etc which emits deprecation
|
||||
# warnings on any method call (except #inspect).
|
||||
class DeprecatedInstanceVariableProxy #:nodoc:
|
||||
instance_methods.each { |m| undef_method m unless m =~ /^__/ }
|
||||
|
||||
def initialize(instance, method, var = "@#{method}")
|
||||
@instance, @method, @var = instance, method, var
|
||||
end
|
||||
|
||||
# Don't give a deprecation warning on inspect since test/unit and error
|
||||
# logs rely on it for diagnostics.
|
||||
def inspect
|
||||
target.inspect
|
||||
end
|
||||
|
||||
private
|
||||
def method_missing(called, *args, &block)
|
||||
warn caller, called, args
|
||||
target.__send__(called, *args, &block)
|
||||
end
|
||||
|
||||
def target
|
||||
@instance.__send__(@method)
|
||||
end
|
||||
|
||||
def warn(callstack, called, args)
|
||||
ActiveSupport::Deprecation.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Module
|
||||
include ActiveSupport::Deprecation::ClassMethods
|
||||
end
|
||||
|
||||
require 'test/unit/error'
|
||||
|
||||
module Test
|
||||
module Unit
|
||||
class TestCase
|
||||
include ActiveSupport::Deprecation::Assertions
|
||||
end
|
||||
|
||||
class Error # :nodoc:
|
||||
# Silence warnings when reporting test errors.
|
||||
def message_with_silenced_deprecation
|
||||
ActiveSupport::Deprecation.silence do
|
||||
message_without_silenced_deprecation
|
||||
end
|
||||
end
|
||||
|
||||
alias_method_chain :message, :silenced_deprecation
|
||||
end
|
||||
end
|
||||
end
|
7
vendor/rails/activesupport/lib/active_support/multibyte.rb
vendored
Normal file
7
vendor/rails/activesupport/lib/active_support/multibyte.rb
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
module ActiveSupport::Multibyte #:nodoc:
|
||||
DEFAULT_NORMALIZATION_FORM = :kc
|
||||
NORMALIZATIONS_FORMS = [:c, :kc, :d, :kd]
|
||||
UNICODE_VERSION = '5.0.0'
|
||||
end
|
||||
|
||||
require 'active_support/multibyte/chars'
|
129
vendor/rails/activesupport/lib/active_support/multibyte/chars.rb
vendored
Normal file
129
vendor/rails/activesupport/lib/active_support/multibyte/chars.rb
vendored
Normal file
|
@ -0,0 +1,129 @@
|
|||
require 'active_support/multibyte/handlers/utf8_handler'
|
||||
require 'active_support/multibyte/handlers/passthru_handler'
|
||||
|
||||
# Encapsulates all the functionality related to the Chars proxy.
|
||||
module ActiveSupport::Multibyte #:nodoc:
|
||||
# Chars enables you to work transparently with multibyte encodings in the Ruby String class without having extensive
|
||||
# knowledge about the encoding. A Chars object accepts a string upon initialization and proxies String methods in an
|
||||
# encoding safe manner. All the normal String methods are also implemented on the proxy.
|
||||
#
|
||||
# String methods are proxied through the Chars object, and can be accessed through the +chars+ method. Methods
|
||||
# which would normally return a String object now return a Chars object so methods can be chained.
|
||||
#
|
||||
# "The Perfect String ".chars.downcase.strip.normalize #=> "the perfect string"
|
||||
#
|
||||
# Chars objects are perfectly interchangeable with String objects as long as no explicit class checks are made.
|
||||
# If certain methods do explicitly check the class, call +to_s+ before you pass chars objects to them.
|
||||
#
|
||||
# bad.explicit_checking_method "T".chars.downcase.to_s
|
||||
#
|
||||
# The actual operations on the string are delegated to handlers. Theoretically handlers can be implemented for
|
||||
# any encoding, but the default handler handles UTF-8. This handler is set during initialization, if you want to
|
||||
# use you own handler, you can set it on the Chars class. Look at the UTF8Handler source for an example how to
|
||||
# implement your own handler. If you your own handler to work on anything but UTF-8 you probably also
|
||||
# want to override Chars#handler.
|
||||
#
|
||||
# ActiveSupport::Multibyte::Chars.handler = MyHandler
|
||||
#
|
||||
# Note that a few methods are defined on Chars instead of the handler because they are defined on Object or Kernel
|
||||
# and method_missing can't catch them.
|
||||
class Chars
|
||||
|
||||
attr_reader :string # The contained string
|
||||
alias_method :to_s, :string
|
||||
|
||||
include Comparable
|
||||
|
||||
# The magic method to make String and Chars comparable
|
||||
def to_str
|
||||
# Using any other ways of overriding the String itself will lead you all the way from infinite loops to
|
||||
# core dumps. Don't go there.
|
||||
@string
|
||||
end
|
||||
|
||||
# Create a new Chars instance.
|
||||
def initialize(str)
|
||||
@string = (str.string rescue str)
|
||||
end
|
||||
|
||||
# Returns -1, 0 or +1 depending on whether the Chars object is to be sorted before, equal or after the
|
||||
# object on the right side of the operation. It accepts any object that implements +to_s+. See String.<=>
|
||||
# for more details.
|
||||
def <=>(other); @string <=> other.to_s; end
|
||||
|
||||
# Works just like String#split, with the exception that the items in the resulting list are Chars
|
||||
# instances instead of String. This makes chaining methods easier.
|
||||
def split(*args)
|
||||
@string.split(*args).map { |i| i.chars }
|
||||
end
|
||||
|
||||
# Gsub works exactly the same as gsub on a normal string.
|
||||
def gsub(*a, &b); @string.gsub(*a, &b).chars; end
|
||||
|
||||
# Like String.=~ only it returns the character offset (in codepoints) instead of the byte offset.
|
||||
def =~(other)
|
||||
handler.translate_offset(@string, @string =~ other)
|
||||
end
|
||||
|
||||
# Try to forward all undefined methods to the handler, when a method is not defined on the handler, send it to
|
||||
# the contained string. Method_missing is also responsible for making the bang! methods destructive.
|
||||
def method_missing(m, *a, &b)
|
||||
begin
|
||||
# Simulate methods with a ! at the end because we can't touch the enclosed string from the handlers.
|
||||
if m.to_s =~ /^(.*)\!$/
|
||||
result = handler.send($1, @string, *a, &b)
|
||||
if result == @string
|
||||
result = nil
|
||||
else
|
||||
@string.replace result
|
||||
end
|
||||
else
|
||||
result = handler.send(m, @string, *a, &b)
|
||||
end
|
||||
rescue NoMethodError
|
||||
result = @string.send(m, *a, &b)
|
||||
rescue Handlers::EncodingError
|
||||
@string.replace handler.tidy_bytes(@string)
|
||||
retry
|
||||
end
|
||||
|
||||
if result.kind_of?(String)
|
||||
result.chars
|
||||
else
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
# Set the handler class for the Char objects.
|
||||
def self.handler=(klass)
|
||||
@@handler = klass
|
||||
end
|
||||
|
||||
# Returns the proper handler for the contained string depending on $KCODE and the encoding of the string. This
|
||||
# method is used internally to always redirect messages to the proper classes depending on the context.
|
||||
def handler
|
||||
if utf8_pragma?
|
||||
@@handler
|
||||
else
|
||||
ActiveSupport::Multibyte::Handlers::PassthruHandler
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# +utf8_pragma+ checks if it can send this string to the handlers. It makes sure @string isn't nil and $KCODE is
|
||||
# set to 'UTF8'.
|
||||
def utf8_pragma?
|
||||
!@string.nil? && ($KCODE == 'UTF8')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# When we can load the utf8proc library, override normalization with the faster methods
|
||||
begin
|
||||
require 'utf8proc_native'
|
||||
require 'active_support/multibyte/handlers/utf8_handler_proc'
|
||||
ActiveSupport::Multibyte::Chars.handler = ActiveSupport::Multibyte::Handlers::UTF8HandlerProc
|
||||
rescue LoadError
|
||||
ActiveSupport::Multibyte::Chars.handler = ActiveSupport::Multibyte::Handlers::UTF8Handler
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue