diff --git a/vendor/rails/actionmailer/test/abstract_unit.rb b/vendor/rails/actionmailer/test/abstract_unit.rb new file mode 100644 index 00000000..8a30e39a --- /dev/null +++ b/vendor/rails/actionmailer/test/abstract_unit.rb @@ -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 diff --git a/vendor/rails/actionmailer/test/fixtures/first_mailer/share.rhtml b/vendor/rails/actionmailer/test/fixtures/first_mailer/share.rhtml new file mode 100644 index 00000000..da43638c --- /dev/null +++ b/vendor/rails/actionmailer/test/fixtures/first_mailer/share.rhtml @@ -0,0 +1 @@ +first mail diff --git a/vendor/rails/actionmailer/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.rhtml b/vendor/rails/actionmailer/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.rhtml new file mode 100644 index 00000000..897a5065 --- /dev/null +++ b/vendor/rails/actionmailer/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.rhtml @@ -0,0 +1 @@ +Have a lovely picture, from me. Enjoy! \ No newline at end of file diff --git a/vendor/rails/actionmailer/test/fixtures/raw_email_quoted_with_0d0a b/vendor/rails/actionmailer/test/fixtures/raw_email_quoted_with_0d0a new file mode 100644 index 00000000..8a2c25a5 --- /dev/null +++ b/vendor/rails/actionmailer/test/fixtures/raw_email_quoted_with_0d0a @@ -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 diff --git a/vendor/rails/actionmailer/test/fixtures/raw_email_with_partially_quoted_subject b/vendor/rails/actionmailer/test/fixtures/raw_email_with_partially_quoted_subject new file mode 100644 index 00000000..e86108da --- /dev/null +++ b/vendor/rails/actionmailer/test/fixtures/raw_email_with_partially_quoted_subject @@ -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: +Content-Type: text/plain; + charset=EUC-KR; + format=flowed +To: jamis@37signals.com +From: Jamis Buck +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 diff --git a/vendor/rails/actionmailer/test/fixtures/second_mailer/share.rhtml b/vendor/rails/actionmailer/test/fixtures/second_mailer/share.rhtml new file mode 100644 index 00000000..9a540106 --- /dev/null +++ b/vendor/rails/actionmailer/test/fixtures/second_mailer/share.rhtml @@ -0,0 +1 @@ +second mail diff --git a/vendor/rails/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.rhtml.bak b/vendor/rails/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.rhtml.bak new file mode 100644 index 00000000..6940419d --- /dev/null +++ b/vendor/rails/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.rhtml.bak @@ -0,0 +1 @@ +Ignored when searching for implicitly multipart parts. diff --git a/vendor/rails/actionmailer/test/fixtures/test_mailer/signed_up_with_url.rhtml b/vendor/rails/actionmailer/test/fixtures/test_mailer/signed_up_with_url.rhtml new file mode 100644 index 00000000..e8fb65d4 --- /dev/null +++ b/vendor/rails/actionmailer/test/fixtures/test_mailer/signed_up_with_url.rhtml @@ -0,0 +1,3 @@ +Hello there, + +Mr. <%= @recipient %>. Please see our greeting at <%= @welcome_url %> \ No newline at end of file diff --git a/vendor/rails/actionmailer/test/url_test.rb b/vendor/rails/actionmailer/test/url_test.rb new file mode 100644 index 00000000..ded343cf --- /dev/null +++ b/vendor/rails/actionmailer/test/url_test.rb @@ -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 < 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 \ No newline at end of file diff --git a/vendor/rails/actionpack/lib/action_controller/assertions/deprecated_assertions.rb b/vendor/rails/actionpack/lib/action_controller/assertions/deprecated_assertions.rb new file mode 100644 index 00000000..5fd18793 --- /dev/null +++ b/vendor/rails/actionpack/lib/action_controller/assertions/deprecated_assertions.rb @@ -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 diff --git a/vendor/rails/actionpack/lib/action_controller/assertions/dom_assertions.rb b/vendor/rails/actionpack/lib/action_controller/assertions/dom_assertions.rb new file mode 100644 index 00000000..d1eea59e --- /dev/null +++ b/vendor/rails/actionpack/lib/action_controller/assertions/dom_assertions.rb @@ -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 \ No newline at end of file diff --git a/vendor/rails/actionpack/lib/action_controller/assertions/model_assertions.rb b/vendor/rails/actionpack/lib/action_controller/assertions/model_assertions.rb new file mode 100644 index 00000000..a9bcd7db --- /dev/null +++ b/vendor/rails/actionpack/lib/action_controller/assertions/model_assertions.rb @@ -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 \ No newline at end of file diff --git a/vendor/rails/actionpack/lib/action_controller/assertions/response_assertions.rb b/vendor/rails/actionpack/lib/action_controller/assertions/response_assertions.rb new file mode 100644 index 00000000..bcb07e67 --- /dev/null +++ b/vendor/rails/actionpack/lib/action_controller/assertions/response_assertions.rb @@ -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: + # + # * :success: Status code was 200 + # * :redirect: Status code was in the 300-399 range + # * :missing: Status code was 404 + # * :error: 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 diff --git a/vendor/rails/actionpack/lib/action_controller/assertions/routing_assertions.rb b/vendor/rails/actionpack/lib/action_controller/assertions/routing_assertions.rb new file mode 100644 index 00000000..11a649c4 --- /dev/null +++ b/vendor/rails/actionpack/lib/action_controller/assertions/routing_assertions.rb @@ -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 \ No newline at end of file diff --git a/vendor/rails/actionpack/lib/action_controller/assertions/selector_assertions.rb b/vendor/rails/actionpack/lib/action_controller/assertions/selector_assertions.rb new file mode 100644 index 00000000..a5267e20 --- /dev/null +++ b/vendor/rails/actionpack/lib/action_controller/assertions/selector_assertions.rb @@ -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: + # * true -- Assertion is true if at least one element selected. + # * false -- Assertion is true if no element selected. + # * String/Regexp -- Assertion is true if the text value of at least + # one element matches the string or regular expression. + # * Integer -- Assertion is true if exactly that number of + # elements are selected. + # * Range -- 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: + # * :text -- Narrow the selection to elements that have this text + # value (string or regexp). + # * :html -- Narrow the selection to elements that have this HTML + # content (string or regexp). + # * :count -- Assertion is true if the number of selected elements + # is equal to this value. + # * :minimum -- Assertion is true if the number of selected + # elements is at least this value. + # * :maximum -- 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(/)?/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("#{text}")).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 diff --git a/vendor/rails/actionpack/lib/action_controller/assertions/tag_assertions.rb b/vendor/rails/actionpack/lib/action_controller/assertions/tag_assertions.rb new file mode 100644 index 00000000..f5f7a23e --- /dev/null +++ b/vendor/rails/actionpack/lib/action_controller/assertions/tag_assertions.rb @@ -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): + # + # * :tag: the node type must match the corresponding value + # * :attributes: a hash. The node's attributes must match the + # corresponding values in the hash. + # * :parent: a hash. The node's parent must match the + # corresponding hash. + # * :child: a hash. At least one of the node's immediate children + # must meet the criteria described by the hash. + # * :ancestor: a hash. At least one of the node's ancestors must + # meet the criteria described by the hash. + # * :descendant: a hash. At least one of the node's descendants + # must meet the criteria described by the hash. + # * :sibling: a hash. At least one of the node's siblings must + # meet the criteria described by the hash. + # * :after: a hash. The node must be after any sibling meeting + # the criteria described by the hash, and at least one sibling must match. + # * :before: a hash. The node must be before any sibling meeting + # the criteria described by the hash, and at least one sibling must match. + # * :children: a hash, for counting children of a node. Accepts + # the keys: + # * :count: either a number or a range which must equal (or + # include) the number of children that match. + # * :less_than: the number of matching children must be less + # than this number. + # * :greater_than: the number of matching children must be + # greater than this number. + # * :only: another hash consisting of the keys to use + # to match on the children, and only matching children will be + # counted. + # * :content: 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/ } + # + # Please noteYou must explicitly + # close all of your tags to use these assertions. + 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 \ No newline at end of file diff --git a/vendor/rails/actionpack/lib/action_controller/deprecated_dependencies.rb b/vendor/rails/actionpack/lib/action_controller/deprecated_dependencies.rb new file mode 100644 index 00000000..433b9e5a --- /dev/null +++ b/vendor/rails/actionpack/lib/action_controller/deprecated_dependencies.rb @@ -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 + # ApplicationController.dependencies_on(:model) would return [:account, :company, :person, :project, :category] + 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 diff --git a/vendor/rails/actionpack/lib/action_controller/resources.rb b/vendor/rails/actionpack/lib/action_controller/resources.rb new file mode 100644 index 00000000..d55e7f02 --- /dev/null +++ b/vendor/rails/actionpack/lib/action_controller/resources.rb @@ -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 new_message_url will raise a RoutingError exception. The default route in + # config/routes.rb 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. map.resources :messages 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 @message 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: + # * :controller -- specify the controller name for the routes. + # * :singular -- specify the singular name used in the member routes. + # * :path_prefix -- 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) + # + # * :name_prefix -- 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_' + # + # * :collection -- add named routes for other actions that operate on the collection. + # Takes a hash of #{action} => #{method}, where method is :get/:post/:put/:delete + # or :any if the method does not matter. These routes map to a URL like /messages;rss, with a route of rss_messages_url. + # * :member -- same as :collection, but for actions that operate on a specific member. + # * :new -- same as :collection, but for actions that operate on the new resource action. + # + # If map.resources 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. map.resource :account 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 diff --git a/vendor/rails/actionpack/lib/action_controller/status_codes.rb b/vendor/rails/actionpack/lib/action_controller/status_codes.rb new file mode 100644 index 00000000..4977c794 --- /dev/null +++ b/vendor/rails/actionpack/lib/action_controller/status_codes.rb @@ -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 \ No newline at end of file diff --git a/vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb b/vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb new file mode 100644 index 00000000..287afcc3 --- /dev/null +++ b/vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb @@ -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 /login. + # + # === 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: + # * name -- Match an element based on its name (tag name). + # For example, p to match a paragraph. You can use * + # to match any element. + # * #id -- Match an element based on its identifier (the + # id attribute). For example, #page. + # * .class -- Match an element based on its class name, all + # class names if more than one specified. + # * [attr] -- Match an element that has the specified attribute. + # * [attr=value] -- Match an element that has the specified + # attribute and value. (More operators are supported see below) + # * :pseudo-class -- Match an element based on a pseudo class, + # such as :nth-child and :empty. + # * :not(expr) -- 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 + # /login. + # + # This selector will match the following element: + #