diff options
author | Emilio Tagua <miloops@gmail.com> | 2009-08-10 18:07:33 -0300 |
---|---|---|
committer | Emilio Tagua <miloops@gmail.com> | 2009-08-10 18:07:33 -0300 |
commit | 0e2fbd80e2420329738b891240d44a056cea1de4 (patch) | |
tree | 5b16755670be58e168b5e86e2cdcb43ee5aa3918 /actionpack | |
parent | eb3ae44ccaff1dc63eb31bf86d8db07c88ddc413 (diff) | |
parent | 600a89f2082beadf4af9fe140a1a2ae56386cd49 (diff) | |
download | rails-0e2fbd80e2420329738b891240d44a056cea1de4.tar.gz rails-0e2fbd80e2420329738b891240d44a056cea1de4.tar.bz2 rails-0e2fbd80e2420329738b891240d44a056cea1de4.zip |
Merge commit 'rails/master'
Conflicts:
activerecord/lib/active_record/calculations.rb
activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
Diffstat (limited to 'actionpack')
40 files changed, 600 insertions, 136 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 75de1fe2a6..30f3f31563 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* Introduce grouped_collection_select helper. #1249 [Dan Codeape, Erik Ostrom] + * Make sure javascript_include_tag/stylesheet_link_tag does not append ".js" or ".css" onto external urls. #1664 [Matthew Rudy Jacobs] * Ruby 1.9: fix Content-Length for multibyte send_data streaming. #2661 [Sava Chankov] diff --git a/actionpack/examples/minimal.rb b/actionpack/examples/minimal.rb index 7106149fa2..a9015da053 100644 --- a/actionpack/examples/minimal.rb +++ b/actionpack/examples/minimal.rb @@ -3,13 +3,19 @@ ENV['RAILS_ENV'] ||= 'production' ENV['NO_RELOAD'] ||= '1' $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" +$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../activesupport/lib" require 'action_controller' require 'action_controller/new_base' if ENV['NEW'] +require 'action_view' require 'benchmark' class Runner - def initialize(app) - @app = app + def initialize(app, output) + @app, @output = app, output + end + + def puts(*) + super if @output end def call(env) @@ -18,16 +24,22 @@ class Runner end def report(env, response) + return unless ENV["DEBUG"] out = env['rack.errors'] out.puts response[0], response[1].to_yaml, '---' response[2].each { |part| out.puts part } out.puts '---' end - def self.run(app, n, label = nil) - puts '=' * label.size, label, '=' * label.size if label - env = { 'n' => n, 'rack.input' => StringIO.new(''), 'rack.errors' => $stdout } - t = Benchmark.realtime { new(app).call(env) } + def self.puts(*) + super if @output + end + + def self.run(app, n, label, output = true) + @output = output + puts label, '=' * label.size if label + env = Rack::MockRequest.env_for("/").merge('n' => n, 'rack.input' => StringIO.new(''), 'rack.errors' => $stdout) + t = Benchmark.realtime { new(app, output).call(env) } puts "%d ms / %d req = %.1f usec/req" % [10**3 * t, n, 10**6 * t / n] puts end @@ -36,10 +48,38 @@ end N = (ENV['N'] || 1000).to_i +module ActionController::Rails2Compatibility + instance_methods.each do |name| + remove_method name + end +end + class BasePostController < ActionController::Base + append_view_path "#{File.dirname(__FILE__)}/views" + + def overhead + self.response_body = '' + end + def index render :text => '' end + + def partial + render :partial => "/partial" + end + + def many_partials + render :partial => "/many_partials" + end + + def partial_collection + render :partial => "/collection", :collection => [1,2,3,4,5,6,7,8,9,10] + end + + def show_template + render :template => "template" + end end OK = [200, {}, []] @@ -51,6 +91,28 @@ class HttpPostController < ActionController::Metal end end -Runner.run(MetalPostController, N, 'metal') -Runner.run(HttpPostController.action(:index), N, 'http') if defined? HttpPostController -Runner.run(BasePostController.action(:index), N, 'base') +unless ENV["PROFILE"] + Runner.run(BasePostController.action(:overhead), N, 'overhead', false) + Runner.run(BasePostController.action(:index), N, 'index', false) + Runner.run(BasePostController.action(:partial), N, 'partial', false) + Runner.run(BasePostController.action(:many_partials), N, 'many_partials', false) + Runner.run(BasePostController.action(:partial_collection), N, 'collection', false) + Runner.run(BasePostController.action(:show_template), N, 'template', false) + + (ENV["M"] || 1).to_i.times do + Runner.run(BasePostController.action(:overhead), N, 'overhead') + Runner.run(BasePostController.action(:index), N, 'index') + Runner.run(BasePostController.action(:partial), N, 'partial') + Runner.run(BasePostController.action(:many_partials), N, 'many_partials') + Runner.run(BasePostController.action(:partial_collection), N, 'collection') + Runner.run(BasePostController.action(:show_template), N, 'template') + end +else + Runner.run(BasePostController.action(:many_partials), N, 'many_partials') + require "ruby-prof" + RubyProf.start + Runner.run(BasePostController.action(:many_partials), N, 'many_partials') + result = RubyProf.stop + printer = RubyProf::CallStackPrinter.new(result) + printer.print(File.open("output.html", "w")) +end
\ No newline at end of file diff --git a/actionpack/examples/views/_collection.erb b/actionpack/examples/views/_collection.erb new file mode 100644 index 0000000000..bcfe958e2c --- /dev/null +++ b/actionpack/examples/views/_collection.erb @@ -0,0 +1 @@ +<%= collection %>
\ No newline at end of file diff --git a/actionpack/examples/views/_hello.erb b/actionpack/examples/views/_hello.erb new file mode 100644 index 0000000000..5ab2f8a432 --- /dev/null +++ b/actionpack/examples/views/_hello.erb @@ -0,0 +1 @@ +Hello
\ No newline at end of file diff --git a/actionpack/examples/views/_many_partials.erb b/actionpack/examples/views/_many_partials.erb new file mode 100644 index 0000000000..7e379d46f5 --- /dev/null +++ b/actionpack/examples/views/_many_partials.erb @@ -0,0 +1,10 @@ +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %>
\ No newline at end of file diff --git a/actionpack/examples/views/_partial.erb b/actionpack/examples/views/_partial.erb new file mode 100644 index 0000000000..3ca8e80b52 --- /dev/null +++ b/actionpack/examples/views/_partial.erb @@ -0,0 +1,10 @@ +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> diff --git a/actionpack/examples/views/template.html.erb b/actionpack/examples/views/template.html.erb index 3108e9ad70..5ab2f8a432 100644 --- a/actionpack/examples/views/template.html.erb +++ b/actionpack/examples/views/template.html.erb @@ -1 +1 @@ -Hello <%= @name %>
\ No newline at end of file +Hello
\ No newline at end of file diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb index 04eaa02441..f3072fad74 100644 --- a/actionpack/lib/abstract_controller/helpers.rb +++ b/actionpack/lib/abstract_controller/helpers.rb @@ -4,8 +4,16 @@ module AbstractController include RenderingController + def self.next_serial + @helper_serial ||= 0 + @helper_serial += 1 + end + included do extlib_inheritable_accessor(:_helpers) { Module.new } + extlib_inheritable_accessor(:_helper_serial) do + AbstractController::Helpers.next_serial + end end module ClassMethods @@ -58,6 +66,8 @@ module AbstractController # of the helper module. Any methods defined in the block # will be helpers. def helper(*args, &block) + self._helper_serial = AbstractController::Helpers.next_serial + 1 + args.flatten.each do |arg| case arg when Module diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 61f1c715c8..698189bd46 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -41,7 +41,7 @@ module ActionController module ImplicitRender def send_action(*) ret = super - default_render unless performed? + default_render unless response_body ret end diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb index 23e7b1b3af..f94d1c669c 100644 --- a/actionpack/lib/action_controller/metal/compatibility.rb +++ b/actionpack/lib/action_controller/metal/compatibility.rb @@ -64,6 +64,8 @@ module ActionController cattr_accessor :ip_spoofing_check self.ip_spoofing_check = true + + cattr_accessor :trusted_proxies end # For old tests diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 525787bf92..2b62a1be85 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -141,7 +141,7 @@ module ActionController end def decode_credentials(request) - ActiveSupport::Base64.decode64(authorization(request).split.last || '') + ActiveSupport::Base64.decode64(authorization(request).split(' ', 2).last || '') end def encode_credentials(user_name, password) @@ -197,9 +197,10 @@ module ActionController return false unless password method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD'] + uri = credentials[:uri][0,1] == '/' ? request.request_uri : request.url [true, false].any? do |password_is_ha1| - expected = expected_response(method, request.env['REQUEST_URI'], credentials, password, password_is_ha1) + expected = expected_response(method, uri, credentials, password, password_is_ha1) expected == credentials[:response] end end diff --git a/actionpack/lib/action_controller/routing/resources.rb b/actionpack/lib/action_controller/routing/resources.rb index 2dee0a3d87..4862cf7115 100644 --- a/actionpack/lib/action_controller/routing/resources.rb +++ b/actionpack/lib/action_controller/routing/resources.rb @@ -320,9 +320,10 @@ module ActionController # notes.resources :attachments # end # - # * <tt>:path_names</tt> - Specify different names for the 'new' and 'edit' actions. For example: + # * <tt>:path_names</tt> - Specify different path names for the actions. For example: # # new_products_path == '/productos/nuevo' - # map.resources :products, :as => 'productos', :path_names => { :new => 'nuevo', :edit => 'editar' } + # # bids_product_path(1) == '/productos/1/licitacoes' + # map.resources :products, :as => 'productos', :member => { :bids => :get }, :path_names => { :new => 'nuevo', :bids => 'licitacoes' } # # You can also set default action names from an environment, like this: # config.action_controller.resources_path_names = { :new => 'nuevo', :edit => 'editar' } @@ -528,13 +529,13 @@ module ActionController resource = Resource.new(entities, options) with_options :controller => resource.controller do |map| + map_associations(resource, options) + map_collection_actions(map, resource) map_default_collection_actions(map, resource) map_new_actions(map, resource) map_member_actions(map, resource) - map_associations(resource, options) - if block_given? with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block) end @@ -589,7 +590,10 @@ module ActionController resource.collection_methods.each do |method, actions| actions.each do |action| [method].flatten.each do |m| - map_resource_routes(map, resource, action, "#{resource.path}#{resource.action_separator}#{action}", "#{action}_#{resource.name_prefix}#{resource.plural}", m) + action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash) + action_path ||= action + + map_resource_routes(map, resource, action, "#{resource.path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.name_prefix}#{resource.plural}", m) end end end diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 040a7e2cb6..09f6024d39 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -407,9 +407,24 @@ module ActionController # don't use the recalled keys when determining which routes to check routes = routes_by_controller[controller][action][options.reject {|k,v| !v}.keys.sort_by { |x| x.object_id }] - routes.each do |route| + routes[1].each_with_index do |route, index| results = route.__send__(method, options, merged, expire_on) - return results if results && (!results.is_a?(Array) || results.first) + if results && (!results.is_a?(Array) || results.first) + + # Compare results with Rails 3.0 behavior + if routes[0][index] != route + routes[0].each do |route2| + new_results = route2.__send__(method, options, merged, expire_on) + if new_results && (!new_results.is_a?(Array) || new_results.first) + ActiveSupport::Deprecation.warn "The URL you generated will use the first matching route in routes.rb rather than the \"best\" match. " + + "In Rails 3.0 #{new_results} would of been generated instead of #{results}" + break + end + end + end + + return results + end end end @@ -448,7 +463,10 @@ module ActionController @routes_by_controller ||= Hash.new do |controller_hash, controller| controller_hash[controller] = Hash.new do |action_hash, action| action_hash[action] = Hash.new do |key_hash, keys| - key_hash[keys] = routes_for_controller_and_action_and_keys(controller, action, keys) + key_hash[keys] = [ + routes_for_controller_and_action_and_keys(controller, action, keys), + deprecated_routes_for_controller_and_action_and_keys(controller, action, keys) + ] end end end @@ -460,17 +478,16 @@ module ActionController merged = options if expire_on[:controller] action = merged[:action] || 'index' - routes_by_controller[controller][action][merged.keys] + routes_by_controller[controller][action][merged.keys][1] end - def routes_for_controller_and_action(controller, action) - selected = routes.select do |route| + def routes_for_controller_and_action_and_keys(controller, action, keys) + routes.select do |route| route.matches_controller_and_action? controller, action end - (selected.length == routes.length) ? routes : selected end - def routes_for_controller_and_action_and_keys(controller, action, keys) + def deprecated_routes_for_controller_and_action_and_keys(controller, action, keys) selected = routes.select do |route| route.matches_controller_and_action? controller, action end diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 5f9463eb91..4190fa21cd 100755 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -246,7 +246,7 @@ module ActionDispatch remote_addr_list = @env['REMOTE_ADDR'] && @env['REMOTE_ADDR'].scan(/[^,\s]+/) unless remote_addr_list.blank? - not_trusted_addrs = remote_addr_list.reject {|addr| addr =~ TRUSTED_PROXIES} + not_trusted_addrs = remote_addr_list.reject {|addr| addr =~ TRUSTED_PROXIES || addr =~ ActionController::Base.trusted_proxies} return not_trusted_addrs.first unless not_trusted_addrs.empty? end remote_ips = @env['HTTP_X_FORWARDED_FOR'] && @env['HTTP_X_FORWARDED_FOR'].split(',') @@ -265,7 +265,7 @@ EOM end if remote_ips - while remote_ips.size > 1 && TRUSTED_PROXIES =~ remote_ips.last.strip + while remote_ips.size > 1 && (TRUSTED_PROXIES =~ remote_ips.last.strip || ActionController::Base.trusted_proxies =~ remote_ips.last.strip) remote_ips.pop end diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb index dd75cda6b9..d22adfa749 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb @@ -345,14 +345,17 @@ module ActionDispatch # # Use the first argument to narrow down assertions to only statements # of that type. Possible values are <tt>:replace</tt>, <tt>:replace_html</tt>, - # <tt>:show</tt>, <tt>:hide</tt>, <tt>:toggle</tt>, <tt>:remove</tt> and - # <tt>:insert_html</tt>. + # <tt>:show</tt>, <tt>:hide</tt>, <tt>:toggle</tt>, <tt>:remove</tta>, + # <tt>:insert_html</tt> and <tt>:redirect</tt>. # # Use the argument <tt>:insert</tt> followed by an insertion position to narrow # down the assertion to only statements that insert elements in that # position. Possible values are <tt>:top</tt>, <tt>:bottom</tt>, <tt>:before</tt> # and <tt>:after</tt>. # + # Use the argument <tt>:redirect</tt> follwed by a path to check that an statement + # which redirects to the specified path is generated. + # # Using the <tt>:remove</tt> statement, you will be able to pass a block, but it will # be ignored as there is no HTML passed for this statement. # @@ -399,6 +402,9 @@ module ActionDispatch # # # The same, but shorter. # assert_select "ol>li", 4 + # + # # Checking for a redirect. + # assert_select_rjs :redirect, root_path def assert_select_rjs(*args, &block) rjs_type = args.first.is_a?(Symbol) ? args.shift : nil id = args.first.is_a?(String) ? args.shift : nil @@ -576,7 +582,8 @@ module ActionDispatch :chained_replace => "\\$\\(#{RJS_ANY_ID}\\)\\.replace\\(#{RJS_PATTERN_HTML}\\)", :chained_replace_html => "\\$\\(#{RJS_ANY_ID}\\)\\.update\\(#{RJS_PATTERN_HTML}\\)", :replace_html => "Element\\.update\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)", - :replace => "Element\\.replace\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)" + :replace => "Element\\.replace\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)", + :redirect => "window.location.href = #{RJS_ANY_ID}" } [:remove, :show, :hide, :toggle].each do |action| RJS_STATEMENTS[action] = "Element\\.#{action}\\(#{RJS_ANY_ID}\\)" diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 7932f01ebb..c171a5a8f5 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -164,6 +164,9 @@ module ActionView #:nodoc: # # See the ActionView::Helpers::PrototypeHelper::GeneratorMethods documentation for more details. class Base + module Subclasses + end + include Helpers, Rendering, Partials, ::ERB::Util extend ActiveSupport::Memoizable @@ -195,7 +198,9 @@ module ActionView #:nodoc: attr_internal :request, :layout - delegate :controller_path, :to => :controller, :allow_nil => true + def controller_path + @controller_path ||= controller && controller.controller_path + end delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers, :flash, :action_name, :controller_name, :to => :controller @@ -210,30 +215,35 @@ module ActionView #:nodoc: ActionView::PathSet.new(Array(value)) end + extlib_inheritable_accessor :helpers attr_reader :helpers - class ProxyModule < Module - def initialize(receiver) - @receiver = receiver - end + def self.for_controller(controller) + @views ||= {} - def include(*args) - super(*args) - @receiver.extend(*args) - end - end + # TODO: Decouple this so helpers are a separate concern in AV just like + # they are in AC. + if controller.class.respond_to?(:_helper_serial) + klass = @views[controller.class._helper_serial] ||= Class.new(self) do + Subclasses.const_set(controller.class.name.gsub(/::/, '__'), self) - def self.for_controller(controller) - new(controller.class.view_paths, {}, controller).tap do |view| - view.helpers.include(controller._helpers) if controller.respond_to?(:_helpers) + if controller.respond_to?(:_helpers) + include controller._helpers + self.helpers = controller._helpers + end + end + else + klass = self end + + klass.new(controller.class.view_paths, {}, controller) end def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil, formats = nil)#:nodoc: @formats = formats || [:html] @assigns = assigns_for_first_render.each { |key, value| instance_variable_set("@#{key}", value) } @controller = controller - @helpers = ProxyModule.new(self) + @helpers = self.class.helpers || Module.new @_content_for = Hash.new {|h,k| h[k] = "" } self.view_paths = view_paths end @@ -248,7 +258,7 @@ module ActionView #:nodoc: def with_template(current_template) _evaluate_assigns_and_ivars last_template, self.template = template, current_template - last_formats, self.formats = formats, [current_template.mime_type.to_sym] + Mime::SET.symbols + last_formats, self.formats = formats, current_template.formats yield ensure self.template, self.formats = last_template, last_formats diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb index dc4497581c..9951e11a37 100644 --- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb +++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb @@ -98,7 +98,7 @@ module ActionView options[:schema_date] = "2005" # The Atom spec copyright date end - xml = options[:xml] || eval("xml", block.binding) + xml = options.delete(:xml) || eval("xml", block.binding) xml.instruct! if options[:instruct] options[:instruct].each do |target,attrs| diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index bde600f6ed..81029102b1 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -738,6 +738,7 @@ module ActionView options = options.stringify_keys tag_value = options.delete("value") name_and_id = options.dup + name_and_id["id"] = name_and_id["for"] add_default_name_and_id_for_value(tag_value, name_and_id) options.delete("index") options["for"] ||= name_and_id["id"] @@ -936,6 +937,10 @@ module ActionView @model_name ||= Struct.new(:partial_path).new(name.demodulize.underscore.sub!(/_builder$/, '')) end + def to_model + self + end + def initialize(object_name, object, template, options, proc) @nested_child_index = {} @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 8cb5882aab..4620a52272 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -162,6 +162,60 @@ module ActionView InstanceTag.new(object, method, self, options.delete(:object)).to_collection_select_tag(collection, value_method, text_method, options, html_options) end + + # Returns <tt><select></tt>, <tt><optgroup></tt> and <tt><option></tt> tags for the collection of existing return values of + # +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will + # be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt> + # or <tt>:include_blank</tt> in the +options+ hash. + # + # Parameters: + # * +object+ - The instance of the class to be used for the select tag + # * +method+ - The attribute of +object+ corresponding to the select tag + # * +collection+ - An array of objects representing the <tt><optgroup></tt> tags. + # * +group_method+ - The name of a method which, when called on a member of +collection+, returns an + # array of child objects representing the <tt><option></tt> tags. + # * +group_label_method+ - The name of a method which, when called on a member of +collection+, returns a + # string to be used as the +label+ attribute for its <tt><optgroup></tt> tag. + # * +option_key_method+ - The name of a method which, when called on a child object of a member of + # +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag. + # * +option_value_method+ - The name of a method which, when called on a child object of a member of + # +collection+, returns a value to be used as the contents of its <tt><option></tt> tag. + # + # Example object structure for use with this method: + # class Continent < ActiveRecord::Base + # has_many :countries + # # attribs: id, name + # end + # class Country < ActiveRecord::Base + # belongs_to :continent + # # attribs: id, name, continent_id + # end + # class City < ActiveRecord::Base + # belongs_to :country + # # attribs: id, name, country_id + # end + # + # Sample usage: + # grouped_collection_select(:city, :country_id, @continents, :countries, :name, :id, :name) + # + # Possible output: + # <select name="city[country_id]"> + # <optgroup label="Africa"> + # <option value="1">South Africa</option> + # <option value="3">Somalia</option> + # </optgroup> + # <optgroup label="Europe"> + # <option value="7" selected="selected">Denmark</option> + # <option value="2">Ireland</option> + # </optgroup> + # </select> + # + def grouped_collection_select(object, method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {}) + InstanceTag.new(object, method, self, options.delete(:object)).to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options) + end + + + # Return select and option tags for the given object and method, using # #time_zone_options_for_select to generate the list of option tags. # @@ -490,6 +544,15 @@ module ActionView ) end + def to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options) + html_options = html_options.stringify_keys + add_default_name_and_id(html_options) + value = value(object) + content_tag( + "select", add_options(option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value), options, value), html_options + ) + end + def to_time_zone_select_tag(priority_zones, options, html_options) html_options = html_options.stringify_keys add_default_name_and_id(html_options) @@ -524,6 +587,10 @@ module ActionView @template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options)) end + def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {}) + @template.grouped_collection_select(@object_name, method, collection, group_method, group_label_method, option_key_method, option_value_method, objectify_options(options), @default_options.merge(html_options)) + end + def time_zone_select(method, priority_zones = nil, options = {}, html_options = {}) @template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options)) end diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index c3ce4c671e..1d92bcb763 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -33,13 +33,15 @@ module ActionView end # Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt> - # (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "..."). + # (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "...") + # for a total length not exceeding <tt>:length</tt>. + # # Pass a <tt>:separator</tt> to truncate +text+ at a natural break. # # ==== Examples # # truncate("Once upon a time in a world far far away") - # # => Once upon a time in a world f... + # # => Once upon a time in a world... # # truncate("Once upon a time in a world far far away", :separator => ' ') # # => Once upon a time in a world... @@ -48,19 +50,19 @@ module ActionView # # => Once upon a... # # truncate("And they found that many people were sleeping better.", :length => 25, "(clipped)") - # # => And they found that many (clipped) + # # => And they found t(clipped) # - # truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length => 15) - # # => And they found... (continued) + # truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length => 25) + # # => And they f... (continued) # # You can still use <tt>truncate</tt> with the old API that accepts the # +length+ as its optional second and the +ellipsis+ as its # optional third parameter: # truncate("Once upon a time in a world far far away", 14) - # # => Once upon a time in a world f... + # # => Once upon a... # - # truncate("And they found that many people were sleeping better.", 15, "... (continued)") - # # => And they found... (continued) + # truncate("And they found that many people were sleeping better.", 25, "... (continued)") + # # => And they f... (continued) def truncate(text, *args) options = args.extract_options! unless args.empty? @@ -239,12 +241,20 @@ module ActionView # # textilize("Visit the Rails website "here":http://www.rubyonrails.org/.) # # => "<p>Visit the Rails website <a href="http://www.rubyonrails.org/">here</a>.</p>" - def textilize(text) + # + # textilize("This is worded <strong>strongly</strong>") + # # => "<p>This is worded <strong>strongly</strong></p>" + # + # textilize("This is worded <strong>strongly</strong>", :filter_html) + # # => "<p>This is worded <strong>strongly</strong></p>" + # + def textilize(text, *options) + options ||= [:hard_breaks] + if text.blank? "" else - textilized = RedCloth.new(text, [ :hard_breaks ]) - textilized.hard_breaks = true if textilized.respond_to?(:hard_breaks=) + textilized = RedCloth.new(text, options) textilized.to_html end end diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index c5a6d1f084..b07304e361 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -568,7 +568,7 @@ module ActionView when confirm && popup "if (#{confirm_javascript_function(confirm)}) { #{popup_javascript_function(popup)} };return false;" when confirm && method - "if (#{confirm_javascript_function(confirm)}) { #{method_javascript_function(method)} };return false;" + "if (#{confirm_javascript_function(confirm)}) { #{method_javascript_function(method, url, href)} };return false;" when confirm "return #{confirm_javascript_function(confirm)};" when method diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb index 986d3af454..64f08c447d 100644 --- a/actionpack/lib/action_view/render/partials.rb +++ b/actionpack/lib/action_view/render/partials.rb @@ -177,71 +177,70 @@ module ActionView @partial_names ||= Hash.new {|h,k| h[k] = ActiveSupport::ConcurrentHash.new } end - def initialize(view_context, options, formats) - object = options[:partial] - - @view, @formats = view_context, formats - @options = options || {} - - if object.is_a?(String) - @path = object - elsif - @object = object - @path = partial_path unless collection - end - - @locals = options[:locals] || {} - @object ||= @options[:object] || @locals[:object] - @layout = options[:layout] + def self.formats + @formats ||= Hash.new {|h,k| h[k] = Hash.new{|h,k| h[k] = Hash.new {|h,k| h[k] = {}}}} end - def render(&block) - template = find if @path + def initialize(view_context, options, block) + partial = options[:partial] - if collection - render_collection(template, &block) - else - render_object(template, &block) - end - end + @view = view_context + @options = options + @locals = options[:locals] || {} + @block = block - def render_template(template, &block) - @options[:_template] = template - @locals[:object] = @locals[template.variable_name] = @object - @locals[@options[:as]] = @object if @options[:as] + # Set up some instance variables to speed up memoizing + @partial_names = self.class.partial_names[@view.controller.class] + @templates = self.class.formats + @format = view_context.formats - content = @view._render_single_template(template, @locals, &block) - return content if block_given? || !@layout - find(@layout).render(@view, @locals) { content } + # Set up the object and path + @object = partial.is_a?(String) ? options[:object] : partial + @path = partial_path(partial) end - def render_object(template, &block) - @object ||= @locals[template.variable_name] - render_template(template, &block) + def render + return render_collection if collection + + template = find_template + render_template(template, @object || @locals[template.variable_name]) end - def render_collection(passed_template = nil, &block) - @options[:_template] = passed_template + def render_collection + # Even if no template is rendered, this will ensure that the MIME type + # for the empty response is the same as the provided template + @options[:_template] = default_template = find_template return nil if collection.blank? if @options.key?(:spacer_template) - spacer = @view.render_partial( - :partial => @options[:spacer_template], :_details => @options[:_details]) + spacer = find_template(@options[:spacer_template]).render(@view, @locals) end - index = 0 + segments = [] - collection.map do |@object| - @path = partial_path - template = passed_template || find + collection.each_with_index do |object, index| + template = default_template || find_template(partial_path(object)) @locals[template.counter_name] = index - index += 1 + segments << render_template(template, object) + end - render_template(template, &block) - end.join(spacer) + segments.join(spacer) end + def render_template(template, object = @object) + @options[:_template] ||= template + + # TODO: is locals[:object] really necessary? + @locals[:object] = @locals[template.variable_name] = object + @locals[@options[:as]] = object if @options[:as] + + content = @view._render_single_template(template, @locals, &@block) + return content if @block || !@options[:layout] + find_template(@options[:layout]).render(@view, @locals) { content } + end + + private def collection @collection ||= if @object.respond_to?(:to_ary) @@ -251,21 +250,22 @@ module ActionView end end - def find(path = @path) - prefix = @view.controller.controller_path unless path.include?(?/) - @view.find(path, {:formats => @view.formats}, prefix, true) + def find_template(path = @path) + return if !path + @templates[path][@view.controller_path][@format][I18n.locale] ||= begin + prefix = @view.controller.controller_path unless path.include?(?/) + @view.find(path, {:formats => @view.formats}, prefix, true) + end end def partial_path(object = @object) - self.class.partial_names[@view.controller.class][object.class] ||= begin - return nil unless object.class.respond_to?(:model_name) + return object if object.is_a?(String) + @partial_names[object.class] ||= begin + return nil unless object.respond_to?(:to_model) - name = object.class.model_name - path = @view.controller_path - if path && path.include?(?/) - File.join(File.dirname(path), name.partial_path) - else - name.partial_path + object.to_model.class.model_name.partial_path.dup.tap do |partial| + path = @view.controller_path + partial.insert(0, "#{File.dirname(path)}/") if path.include?(?/) end end end @@ -279,7 +279,7 @@ module ActionView end def _render_partial(options, &block) #:nodoc: - PartialRenderer.new(self, options, formats).render(&block) + PartialRenderer.new(self, options, block).render end end diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template/template.rb index 4145045e2d..33d3f79ad3 100644 --- a/actionpack/lib/action_view/template/template.rb +++ b/actionpack/lib/action_view/template/template.rb @@ -7,19 +7,22 @@ require "action_view/template/resolver" module ActionView class Template extend TemplateHandlers - attr_reader :source, :identifier, :handler, :mime_type, :details + attr_reader :source, :identifier, :handler, :mime_type, :formats, :details def initialize(source, identifier, handler, details) @source = source @identifier = identifier @handler = handler @details = details + @method_names = {} format = details.delete(:format) || begin # TODO: Clean this up handler.respond_to?(:default_format) ? handler.default_format.to_sym.to_s : "html" end @mime_type = Mime::Type.lookup_by_extension(format.to_s) + @formats = [format.to_sym] + @formats << :html if format == :js @details[:formats] = Array.wrap(format.to_sym) end @@ -30,12 +33,12 @@ module ActionView # TODO: Figure out how to abstract this def variable_name - identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym + @variable_name ||= identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym end # TODO: Figure out how to abstract this def counter_name - "#{variable_name}_counter".to_sym + @counter_name ||= "#{variable_name}_counter".to_sym end # TODO: kill hax @@ -90,7 +93,8 @@ module ActionView def build_method_name(locals) # TODO: is locals.keys.hash reliably the same? - "_render_template_#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_") + @method_names[locals.keys.hash] ||= + "_render_template_#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_") end end end diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb index 81944ff546..9f12e5e0a8 100644 --- a/actionpack/lib/action_view/template/text.rb +++ b/actionpack/lib/action_view/template/text.rb @@ -15,7 +15,9 @@ module ActionView #:nodoc: def render(*) self end def mime_type() @content_type end - + + def formats() [mime_type] end + def partial?() false end end end diff --git a/actionpack/test/controller/assert_select_test.rb b/actionpack/test/controller/assert_select_test.rb index ad17d1288b..2e77d2f8ad 100644 --- a/actionpack/test/controller/assert_select_test.rb +++ b/actionpack/test/controller/assert_select_test.rb @@ -257,6 +257,13 @@ class AssertSelectTest < ActionController::TestCase end assert_raise(Assertion) {assert_select_rjs :insert, :top, "test2"} end + + def test_assert_select_rjs_for_redirect_to + render_rjs do |page| + page.redirect_to '/' + end + assert_select_rjs :redirect, '/' + end def test_elect_with_xml_namespace_attributes render_html %Q{<link xlink:href="http://nowhere.com"></link>} diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 68529cc8f7..346fa09414 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -51,7 +51,7 @@ class PageCachingTest < ActionController::TestCase ActionController::Routing::Routes.clear! ActionController::Routing::Routes.draw do |map| - map.main '', :controller => 'posts' + map.main '', :controller => 'posts', :format => nil map.formatted_posts 'posts.:format', :controller => 'posts' map.resources :posts map.connect ':controller/:action/:id' diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb index fbc94a0df7..23688ca584 100644 --- a/actionpack/test/controller/http_basic_authentication_test.rb +++ b/actionpack/test/controller/http_basic_authentication_test.rb @@ -4,6 +4,7 @@ class HttpBasicAuthenticationTest < ActionController::TestCase class DummyController < ActionController::Base before_filter :authenticate, :only => :index before_filter :authenticate_with_request, :only => :display + before_filter :authenticate_long_credentials, :only => :show def index render :text => "Hello Secret" @@ -12,6 +13,10 @@ class HttpBasicAuthenticationTest < ActionController::TestCase def display render :text => 'Definitely Maybe' end + + def show + render :text => 'Only for loooooong credentials' + end private @@ -28,6 +33,12 @@ class HttpBasicAuthenticationTest < ActionController::TestCase request_http_basic_authentication("SuperSecret") end end + + def authenticate_long_credentials + authenticate_or_request_with_http_basic do |username, password| + username == '1234567890123456789012345678901234567890' && password == '1234567890123456789012345678901234567890' + end + end end AUTH_HEADERS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION', 'REDIRECT_X_HTTP_AUTHORIZATION'] @@ -42,6 +53,13 @@ class HttpBasicAuthenticationTest < ActionController::TestCase assert_response :success assert_equal 'Hello Secret', @response.body, "Authentication failed for request header #{header}" end + test "successful authentication with #{header.downcase} and long credentials" do + @request.env[header] = encode_credentials('1234567890123456789012345678901234567890', '1234567890123456789012345678901234567890') + get :show + + assert_response :success + assert_equal 'Only for loooooong credentials', @response.body, "Authentication failed for request header #{header} and long credentials" + end end AUTH_HEADERS.each do |header| @@ -52,6 +70,13 @@ class HttpBasicAuthenticationTest < ActionController::TestCase assert_response :unauthorized assert_equal "HTTP Basic: Access denied.\n", @response.body, "Authentication didn't fail for request header #{header}" end + test "unsuccessful authentication with #{header.downcase} and long credentials" do + @request.env[header] = encode_credentials('h4x0rh4x0rh4x0rh4x0rh4x0rh4x0rh4x0rh4x0r', 'worldworldworldworldworldworldworldworld') + get :show + + assert_response :unauthorized + assert_equal "HTTP Basic: Access denied.\n", @response.body, "Authentication didn't fail for request header #{header} and long credentials" + end end test "authentication request without credential" do diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index 58f3b88075..7e9a2625f1 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -136,7 +136,7 @@ class HttpDigestAuthenticationTest < ActionController::TestCase assert_equal 'Definitely Maybe', @response.body end - test "authentication request with request-uri that doesn't match credentials digest-uri" do + test "authentication request with request-uri that doesn't match credentials digest-uri" do @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please') @request.env['REQUEST_URI'] = "/http_digest_authentication_test/dummy_digest/altered/uri" get :display @@ -145,10 +145,33 @@ class HttpDigestAuthenticationTest < ActionController::TestCase assert_equal "Authentication Failed", @response.body end - test "authentication request with absolute uri" do - @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:uri => "http://test.host/http_digest_authentication_test/dummy_digest/display", + test "authentication request with absolute request uri (as in webrick)" do + @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please') + @request.env['REQUEST_URI'] = "http://test.host/http_digest_authentication_test/dummy_digest" + + get :display + + assert_response :success + assert assigns(:logged_in) + assert_equal 'Definitely Maybe', @response.body + end + + test "authentication request with absolute uri in credentials (as in IE)" do + @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:url => "http://test.host/http_digest_authentication_test/dummy_digest", :username => 'pretty', :password => 'please') - @request.env['REQUEST_URI'] = "http://test.host/http_digest_authentication_test/dummy_digest/display" + + get :display + + assert_response :success + assert assigns(:logged_in) + assert_equal 'Definitely Maybe', @response.body + end + + test "authentication request with absolute uri in both request and credentials (as in Webrick with IE)" do + @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:url => "http://test.host/http_digest_authentication_test/dummy_digest", + :username => 'pretty', :password => 'please') + @request.env['REQUEST_URI'] = "http://test.host/http_digest_authentication_test/dummy_digest" + get :display assert_response :success @@ -202,11 +225,11 @@ class HttpDigestAuthenticationTest < ActionController::TestCase credentials = decode_credentials(@response.headers['WWW-Authenticate']) credentials.merge!(options) - credentials.reverse_merge!(:uri => "#{@request.env['REQUEST_URI']}") + credentials.merge!(:uri => @request.env['REQUEST_URI'].to_s) ActionController::HttpAuthentication::Digest.encode_credentials(method, credentials, password, options[:password_is_ha1]) end def decode_credentials(header) ActionController::HttpAuthentication::Digest.decode_credentials(@response.headers['WWW-Authenticate']) end -end
\ No newline at end of file +end diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 9546fdb50d..0c0599679c 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -1237,7 +1237,6 @@ class RenderTest < ActionController::TestCase def test_partial_collection_with_spacer get :partial_collection_with_spacer assert_equal "Hello: davidonly partialHello: mary", @response.body - assert_template :partial => 'test/_partial_only' assert_template :partial => '_customer' end diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index 30ab110ef7..0b639e363d 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -76,6 +76,50 @@ class ResourcesTest < ActionController::TestCase end end + def test_override_paths_for_member_and_collection_methods + collection_methods = { 'rss' => :get, 'reorder' => :post, 'csv' => :post } + member_methods = { 'rss' => :get, :atom => :get, :upload => :post, :fix => :post } + path_names = {:new => 'nuevo', 'rss' => 'canal', :fix => 'corrigir' } + + with_restful_routing :messages, + :collection => collection_methods, + :member => member_methods, + :path_names => path_names do + + assert_restful_routes_for :messages, + :collection => collection_methods, + :member => member_methods, + :path_names => path_names do |options| + member_methods.each do |action, method| + assert_recognizes(options.merge(:action => action.to_s, :id => '1'), + :path => "/messages/1/#{path_names[action] || action}", + :method => method) + end + + collection_methods.each do |action, method| + assert_recognizes(options.merge(:action => action), + :path => "/messages/#{path_names[action] || action}", + :method => method) + end + end + + assert_restful_named_routes_for :messages, + :collection => collection_methods, + :member => member_methods, + :path_names => path_names do |options| + + collection_methods.keys.each do |action| + assert_named_route "/messages/#{path_names[action] || action}", "#{action}_messages_path", :action => action + end + + member_methods.keys.each do |action| + assert_named_route "/messages/1/#{path_names[action] || action}", "#{action}_message_path", :action => action, :id => "1" + end + + end + end + end + def test_override_paths_for_default_restful_actions resource = ActionController::Resources::Resource.new(:messages, :path_names => {:new => 'nuevo', :edit => 'editar'}) diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 5f9ae6292c..2534c232c7 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -1610,7 +1610,7 @@ class RouteTest < Test::Unit::TestCase end end -class RouteSetTest < Test::Unit::TestCase +class RouteSetTest < ActiveSupport::TestCase def set @set ||= ROUTING::RouteSet.new end @@ -2191,8 +2191,10 @@ class RouteSetTest < Test::Unit::TestCase map.connect "/ws/people", :controller => "people", :action => "index", :ws => true end - url = set.generate(:controller => "people", :action => "index", :ws => true) - assert_equal "/ws/people", url + assert_deprecated { + url = set.generate(:controller => "people", :action => "index", :ws => true) + assert_equal "/ws/people", url + } end def test_generate_changes_controller_module diff --git a/actionpack/test/controller/url_rewriter_test.rb b/actionpack/test/controller/url_rewriter_test.rb index 863f8414c5..0e149cf8ae 100644 --- a/actionpack/test/controller/url_rewriter_test.rb +++ b/actionpack/test/controller/url_rewriter_test.rb @@ -304,7 +304,7 @@ class UrlWriterTests < ActionController::TestCase def test_named_routes_with_nil_keys ActionController::Routing::Routes.clear! ActionController::Routing::Routes.draw do |map| - map.main '', :controller => 'posts' + map.main '', :controller => 'posts', :format => nil map.resources :posts map.connect ':controller/:action/:id' end @@ -314,9 +314,9 @@ class UrlWriterTests < ActionController::TestCase controller = kls.new params = {:action => :index, :controller => :posts, :format => :xml} - assert_equal("http://www.basecamphq.com/posts.xml", controller.send(:url_for, params)) + assert_equal("http://www.basecamphq.com/posts.xml", controller.send(:url_for, params)) params[:format] = nil - assert_equal("http://www.basecamphq.com/", controller.send(:url_for, params)) + assert_equal("http://www.basecamphq.com/", controller.send(:url_for, params)) ensure ActionController::Routing::Routes.load! end diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 8ebf9aa186..f3500fca34 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -72,6 +72,34 @@ class RequestTest < ActiveSupport::TestCase assert_equal '9.9.9.9', request.remote_ip end + test "remote ip with user specified trusted proxies" do + ActionController::Base.trusted_proxies = /^67\.205\.106\.73$/i + + request = stub_request 'REMOTE_ADDR' => '67.205.106.73', + 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' + assert_equal '3.4.5.6', request.remote_ip + + request = stub_request 'REMOTE_ADDR' => '172.16.0.1,67.205.106.73', + 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' + assert_equal '3.4.5.6', request.remote_ip + + request = stub_request 'REMOTE_ADDR' => '67.205.106.73,172.16.0.1', + 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' + assert_equal '3.4.5.6', request.remote_ip + + request = stub_request 'REMOTE_ADDR' => '67.205.106.74,172.16.0.1', + 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' + assert_equal '67.205.106.74', request.remote_ip + + request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,67.205.106.73' + assert_equal 'unknown', request.remote_ip + + request = stub_request 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 3.4.5.6, 10.0.0.1, 67.205.106.73' + assert_equal '3.4.5.6', request.remote_ip + + ActionController::Base.trusted_proxies = nil + end + test "domains" do request = stub_request 'HTTP_HOST' => 'www.rubyonrails.org' assert_equal "rubyonrails.org", request.domain diff --git a/actionpack/test/fixtures/test/greeting.xml.erb b/actionpack/test/fixtures/test/greeting.xml.erb new file mode 100644 index 0000000000..62fb0293f0 --- /dev/null +++ b/actionpack/test/fixtures/test/greeting.xml.erb @@ -0,0 +1 @@ +<p>This is grand!</p> diff --git a/actionpack/test/template/atom_feed_helper_test.rb b/actionpack/test/template/atom_feed_helper_test.rb index 3acaecd142..6a5fb0acff 100644 --- a/actionpack/test/template/atom_feed_helper_test.rb +++ b/actionpack/test/template/atom_feed_helper_test.rb @@ -157,6 +157,26 @@ class ScrollsController < ActionController::Base end end EOT + FEEDS["provide_builder"] = <<-'EOT' + # we pass in the new_xml to the helper so it doesn't + # call anything on the original builder + new_xml = Builder::XmlMarkup.new(:target=>'') + atom_feed(:xml => new_xml) do |feed| + feed.title("My great blog!") + feed.updated((@scrolls.first.created_at)) + + for scroll in @scrolls + feed.entry(scroll) do |entry| + entry.title(scroll.title) + entry.content(scroll.body, :type => 'html') + + entry.author do |author| + author.name("DHH") + end + end + end + end + EOT def index @scrolls = [ Scroll.new(1, "1", "Hello One", "Something <i>COOL!</i>", Time.utc(2007, 12, 12, 15), Time.utc(2007, 12, 12, 15)), @@ -202,6 +222,15 @@ class AtomFeedTest < ActionController::TestCase end end + def test_providing_builder_to_atom_feed + with_restful_routing(:scrolls) do + get :index, :id=>"provide_builder" + # because we pass in the non-default builder, the content generated by the + # helper should go 'nowhere'. Leaving the response body blank. + assert @response.body.blank? + end + end + def test_entry_with_prefilled_options_should_use_those_instead_of_querying_the_record with_restful_routing(:scrolls) do get :index, :id => "entry_options" diff --git a/actionpack/test/template/body_parts_test.rb b/actionpack/test/template/body_parts_test.rb index bac67c1a7d..defe85107e 100644 --- a/actionpack/test/template/body_parts_test.rb +++ b/actionpack/test/template/body_parts_test.rb @@ -4,7 +4,7 @@ class BodyPartsTest < ActionController::TestCase RENDERINGS = [Object.new, Object.new, Object.new] class TestController < ActionController::Base - def performed?() true end + def response_body() "" end def index RENDERINGS.each do |rendering| diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 2b1d80b1bf..8fd018f86d 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -157,6 +157,22 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal('<label for="my_for">Title</label>', label(:post, :title, nil, "for" => "my_for")) end + def test_label_with_id_attribute_as_symbol + assert_dom_equal('<label for="post_title" id="my_id">Title</label>', label(:post, :title, nil, :id => "my_id")) + end + + def test_label_with_id_attribute_as_string + assert_dom_equal('<label for="post_title" id="my_id">Title</label>', label(:post, :title, nil, "id" => "my_id")) + end + + def test_label_with_for_and_id_attributes_as_symbol + assert_dom_equal('<label for="my_for" id="my_id">Title</label>', label(:post, :title, nil, :for => "my_for", :id => "my_id")) + end + + def test_label_with_for_and_id_attributes_as_string + assert_dom_equal('<label for="my_for" id="my_id">Title</label>', label(:post, :title, nil, "for" => "my_for", "id" => "my_id")) + end + def test_label_for_radio_buttons_with_value assert_dom_equal('<label for="post_title_great_title">The title goes here</label>', label("post", "title", "The title goes here", :value => "great_title")) assert_dom_equal('<label for="post_title_great_title">The title goes here</label>', label("post", "title", "The title goes here", :value => "great title")) diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb index 73624406be..aa40e46aa8 100644 --- a/actionpack/test/template/form_options_helper_test.rb +++ b/actionpack/test/template/form_options_helper_test.rb @@ -763,6 +763,40 @@ class FormOptionsHelperTest < ActionView::TestCase html end + def test_grouped_collection_select + @continents = [ + Continent.new("<Africa>", [Country.new("<sa>", "<South Africa>"), Country.new("so", "Somalia")] ), + Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")] ) + ] + + @post = Post.new + @post.origin = 'dk' + + assert_dom_equal( + %Q{<select id="post_origin" name="post[origin]"><optgroup label="<Africa>"><option value="<sa>"><South Africa></option>\n<option value="so">Somalia</option></optgroup><optgroup label="Europe"><option value="dk" selected="selected">Denmark</option>\n<option value="ie">Ireland</option></optgroup></select>}, + grouped_collection_select("post", "origin", @continents, :countries, :continent_name, :country_id, :country_name) + ) + end + + def test_grouped_collection_select_under_fields_for + @continents = [ + Continent.new("<Africa>", [Country.new("<sa>", "<South Africa>"), Country.new("so", "Somalia")] ), + Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")] ) + ] + + @post = Post.new + @post.origin = 'dk' + + fields_for :post, @post do |f| + concat f.grouped_collection_select("origin", @continents, :countries, :continent_name, :country_id, :country_name) + end + + assert_dom_equal( + %Q{<select id="post_origin" name="post[origin]"><optgroup label="<Africa>"><option value="<sa>"><South Africa></option>\n<option value="so">Somalia</option></optgroup><optgroup label="Europe"><option value="dk" selected="selected">Denmark</option>\n<option value="ie">Ireland</option></optgroup></select>}, + output_buffer + ) + end + private def dummy_posts diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 706b5085f4..08143ba680 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -1,5 +1,10 @@ require 'abstract_unit' require 'testing_sandbox' +begin + require 'redcloth' +rescue LoadError + $stderr.puts "Skipping textilize tests. `gem install RedCloth` to enable." +end class TextHelperTest < ActionView::TestCase tests ActionView::Helpers::TextHelper @@ -528,4 +533,22 @@ class TextHelperTest < ActionView::TestCase assert_equal("red", cycle("red", "blue")) assert_equal(%w{Specialized Fuji Giant}, @cycles) end + + if defined? RedCloth + def test_textilize + assert_equal("<p><strong>This is Textile!</strong> Rejoice!</p>", textilize("*This is Textile!* Rejoice!")) + end + + def test_textilize_with_blank + assert_equal("", textilize("")) + end + + def test_textilize_with_options + assert_equal("<p>This is worded <strong>strongly</strong></p>", textilize("This is worded <strong>strongly</strong>", :filter_html)) + end + + def test_textilize_with_hard_breaks + assert_equal("<p>This is one scary world.<br />\n True.</p>", textilize("This is one scary world.\n True.")) + end + end end diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index 9eeb26831c..0e24fbd24d 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -220,6 +220,14 @@ class UrlHelperTest < ActionView::TestCase ) end + def test_link_tag_using_delete_javascript_and_href_and_confirm + assert_dom_equal( + "<a href='\#' onclick=\"if (confirm('Are you serious?')) { var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = 'http://www.example.com';var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method'); m.setAttribute('value', 'delete'); f.appendChild(m);f.submit(); };return false;\">Destroy</a>", + link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#', :confirm => "Are you serious?"), + "When specifying url, form should be generated with it, but not this.href" + ) + end + def test_link_tag_using_post_javascript_and_popup assert_raise(ActionView::ActionViewError) { link_to("Hello", "http://www.example.com", :popup => true, :method => :post, :confirm => "Are you serious?") } end |