From 2e053aec9bafa8735d70886f36dea06ea10dc4ce Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 22 Dec 2008 14:48:32 -0800 Subject: Don't construct object deprecation proxy if unneeded --- actionpack/lib/action_view/renderable_partial.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/renderable_partial.rb b/actionpack/lib/action_view/renderable_partial.rb index d92ff1b8d3..3ea836fa25 100644 --- a/actionpack/lib/action_view/renderable_partial.rb +++ b/actionpack/lib/action_view/renderable_partial.rb @@ -25,12 +25,11 @@ module ActionView end def render_partial(view, object = nil, local_assigns = {}, as = nil) - object ||= local_assigns[:object] || - local_assigns[variable_name] + object ||= local_assigns[:object] || local_assigns[variable_name] - if view.respond_to?(:controller) + if object.nil? && view.respond_to?(:controller) ivar = :"@#{variable_name}" - object ||= + object = if view.controller.instance_variable_defined?(ivar) ActiveSupport::Deprecation::DeprecatedObjectProxy.new( view.controller.instance_variable_get(ivar), -- cgit v1.2.3 From 5138f755ff31a8f317d649a6f256c74bc371db70 Mon Sep 17 00:00:00 2001 From: Mark Reginald James Date: Sun, 28 Dec 2008 01:15:48 +0000 Subject: Fixed incorrect parsing of query parameters with mixed-depth nesting inside an array [#1622 state:resolved] Signed-off-by: Frederick Cheung --- actionpack/lib/action_controller/url_encoded_pair_parser.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/url_encoded_pair_parser.rb b/actionpack/lib/action_controller/url_encoded_pair_parser.rb index bea96c711d..9883ad0d85 100644 --- a/actionpack/lib/action_controller/url_encoded_pair_parser.rb +++ b/actionpack/lib/action_controller/url_encoded_pair_parser.rb @@ -70,11 +70,12 @@ module ActionController top[-1][key] = value else top << {key => value}.with_indifferent_access - push top.last - value = top[key] end + push top.last + return top[key] else top << value + return value end elsif top.is_a? Hash key = CGI.unescape(key) @@ -84,12 +85,10 @@ module ActionController else raise ArgumentError, "Don't know what to do: top is #{top.inspect}" end - - return value end def type_conflict!(klass, value) raise TypeError, "Conflicting types for parameter containers. Expected an instance of #{klass} but found an instance of #{value.class}. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value. (The parameters received were #{value.inspect}.)" end end -end \ No newline at end of file +end -- cgit v1.2.3 From 45dee3842d68359a189fe7c0729359bd5a905ea4 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 28 Dec 2008 15:13:16 -0600 Subject: HTTP Digest authentication [#1230 state:resolved] --- .../lib/action_controller/http_authentication.rb | 191 ++++++++++++++++++++- actionpack/lib/action_controller/integration.rb | 82 +++++++++ 2 files changed, 271 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/http_authentication.rb b/actionpack/lib/action_controller/http_authentication.rb index 2ed810db7d..3cb5829eca 100644 --- a/actionpack/lib/action_controller/http_authentication.rb +++ b/actionpack/lib/action_controller/http_authentication.rb @@ -55,7 +55,31 @@ module ActionController # end # end # - # + # Simple Digest example. Note the block must return the user's password so the framework + # can appropriately hash it to check the user's credentials. Returning nil will cause authentication to fail. + # + # class PostsController < ApplicationController + # Users = {"dhh" => "secret"} + # + # before_filter :authenticate, :except => [ :index ] + # + # def index + # render :text => "Everyone can see me!" + # end + # + # def edit + # render :text => "I'm only accessible if you know the password" + # end + # + # private + # def authenticate + # authenticate_or_request_with_http_digest(realm) do |user_name| + # Users[user_name] + # end + # end + # end + # + # # In your integration tests, you can do something like this: # # def test_access_granted_from_xml @@ -108,7 +132,10 @@ module ActionController end def decode_credentials(request) - ActiveSupport::Base64.decode64(authorization(request).split.last || '') + # Properly decode credentials spanning a new-line + auth = authorization(request) + auth.slice!('Basic ') + ActiveSupport::Base64.decode64(auth || '') end def encode_credentials(user_name, password) @@ -120,5 +147,165 @@ module ActionController controller.__send__ :render, :text => "HTTP Basic: Access denied.\n", :status => :unauthorized end end + + module Digest + extend self + + module ControllerMethods + def authenticate_or_request_with_http_digest(realm = "Application", &password_procedure) + begin + authenticate_with_http_digest!(realm, &password_procedure) + rescue ActionController::HttpAuthentication::Error => e + msg = e.message + msg = "#{msg} expected '#{e.expected}' was '#{e.was}'" unless e.expected.nil? + raise msg if e.fatal? + request_http_digest_authentication(realm, msg) + end + end + + # Authenticate using HTTP Digest, throwing ActionController::HttpAuthentication::Error on failure. + # This allows more detailed analysis of authentication failures + # to be relayed to the client. + def authenticate_with_http_digest!(realm = "Application", &login_procedure) + HttpAuthentication::Digest.authenticate(self, realm, &login_procedure) + end + + # Authenticate with HTTP Digest, returns true or false + def authenticate_with_http_digest(realm = "Application", &login_procedure) + HttpAuthentication::Digest.authenticate(self, realm, &login_procedure) rescue false + end + + # Render output including the HTTP Digest authentication header + def request_http_digest_authentication(realm = "Application", message = nil) + HttpAuthentication::Digest.authentication_request(self, realm, message) + end + + # Add HTTP Digest authentication header to result headers + def http_digest_authentication_header(realm = "Application") + HttpAuthentication::Digest.authentication_header(self, realm) + end + end + + # Raises error unless authentictaion succeeds, returns true otherwise + def authenticate(controller, realm, &password_procedure) + raise Error.new(false), "No authorization header found" unless authorization(controller.request) + validate_digest_response(controller, realm, &password_procedure) + true + end + + def authorization(request) + request.env['HTTP_AUTHORIZATION'] || + request.env['X-HTTP_AUTHORIZATION'] || + request.env['X_HTTP_AUTHORIZATION'] || + request.env['REDIRECT_X_HTTP_AUTHORIZATION'] + end + + # Raises error unless the request credentials response value matches the expected value. + def validate_digest_response(controller, realm, &password_procedure) + credentials = decode_credentials(controller.request) + + # Check the nonce, opaque and realm. + # Ignore nc, as we have no way to validate the number of times this nonce has been used + validate_nonce(controller.request, credentials[:nonce]) + raise Error.new(false, realm, credentials[:realm]), "Realm doesn't match" unless realm == credentials[:realm] + raise Error.new(true, opaque(controller.request), credentials[:opaque]),"Opaque doesn't match" unless opaque(controller.request) == credentials[:opaque] + + password = password_procedure.call(credentials[:username]) + raise Error.new(false), "No password" if password.nil? + expected = expected_response(controller.request.env['REQUEST_METHOD'], controller.request.url, credentials, password) + raise Error.new(false, expected, credentials[:response]), "Invalid response" unless expected == credentials[:response] + end + + # Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+ + def expected_response(http_method, uri, credentials, password) + ha1 = ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':')) + ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase,uri].join(':')) + ::Digest::MD5.hexdigest([ha1,credentials[:nonce], credentials[:nc], credentials[:cnonce],credentials[:qop],ha2].join(':')) + end + + def encode_credentials(http_method, credentials, password) + credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password) + "Digest " + credentials.sort_by {|x| x[0].to_s }.inject([]) {|a, v| a << "#{v[0]}='#{v[1]}'" }.join(', ') + end + + def decode_credentials(request) + authorization(request).to_s.gsub(/^Digest\s+/,'').split(',').inject({}) do |hash, pair| + key, value = pair.split('=', 2) + hash[key.strip.to_sym] = value.to_s.gsub(/^"|"$/,'').gsub(/'/, '') + hash + end + end + + def authentication_header(controller, realm) + controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce(controller.request)}", opaque="#{opaque(controller.request)}") + end + + def authentication_request(controller, realm, message = "HTTP Digest: Access denied") + authentication_header(controller, realm) + controller.send! :render, :text => message, :status => :unauthorized + end + + # Uses an MD5 digest based on time to generate a value to be used only once. + # + # A server-specified data string which should be uniquely generated each time a 401 response is made. + # It is recommended that this string be base64 or hexadecimal data. + # Specifically, since the string is passed in the header lines as a quoted string, the double-quote character is not allowed. + # + # The contents of the nonce are implementation dependent. + # The quality of the implementation depends on a good choice. + # A nonce might, for example, be constructed as the base 64 encoding of + # + # => time-stamp H(time-stamp ":" ETag ":" private-key) + # + # where time-stamp is a server-generated time or other non-repeating value, + # ETag is the value of the HTTP ETag header associated with the requested entity, + # and private-key is data known only to the server. + # With a nonce of this form a server would recalculate the hash portion after receiving the client authentication header and + # reject the request if it did not match the nonce from that header or + # if the time-stamp value is not recent enough. In this way the server can limit the time of the nonce's validity. + # The inclusion of the ETag prevents a replay request for an updated version of the resource. + # (Note: including the IP address of the client in the nonce would appear to offer the server the ability + # to limit the reuse of the nonce to the same client that originally got it. + # However, that would break proxy farms, where requests from a single user often go through different proxies in the farm. + # Also, IP address spoofing is not that hard.) + # + # An implementation might choose not to accept a previously used nonce or a previously used digest, in order to + # protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for + # POST or PUT requests and a time-stamp for GET requests. For more details on the issues involved see Section 4 + # of this document. + # + # The nonce is opaque to the client. + def nonce(request, time = Time.now) + session_id = request.is_a?(String) ? request : request.session.session_id + t = time.to_i + hashed = [t, session_id] + digest = ::Digest::MD5.hexdigest(hashed.join(":")) + Base64.encode64("#{t}:#{digest}").gsub("\n", '') + end + + def validate_nonce(request, value) + t = Base64.decode64(value).split(":").first.to_i + raise Error.new(true), "Stale Nonce" if (t - Time.now.to_i).abs > 10 * 60 + n = nonce(request, t) + raise Error.new(true, value, n), "Bad Nonce" unless n == value + end + + # Opaque based on digest of session_id + def opaque(request) + session_id = request.is_a?(String) ? request : request.session.session_id + @opaque ||= Base64.encode64(::Digest::MD5::hexdigest(session_id)).gsub("\n", '') + end + end + + class Error < RuntimeError + attr_accessor :expected, :was + def initialize(fatal = false, expected = nil, was = nil) + @fatal = fatal + @expected = expected + @was = was + end + + def fatal?; @fatal; end + end end end diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index 71e2524e81..a8e54c2fc7 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -68,6 +68,15 @@ module ActionController # A running counter of the number of requests processed. attr_accessor :request_count + # Nonce value for Digest Authentication, implicitly set on response with WWW-Authentication + attr_accessor :nonce + + # Opaque value for Digest Authentication, implicitly set on response with WWW-Authentication + attr_accessor :opaque + + # Opaque value for Authentication, implicitly set on response with WWW-Authentication + attr_accessor :realm + class MultiPartNeededException < Exception end @@ -243,6 +252,53 @@ module ActionController end alias xhr :xml_http_request + def request_with_noauth(http_method, uri, parameters, headers) + process_with_auth http_method, uri, parameters, headers + end + + # Performs a request with the given http_method and parameters, including HTTP Basic authorization headers. + # See get() for more details on paramters and headers. + # + # You can perform GET, POST, PUT, DELETE, and HEAD requests with #get_with_basic, #post_with_basic, + # #put_with_basic, #delete_with_basic, and #head_with_basic. + def request_with_basic(http_method, uri, parameters, headers, user_name, password) + process_with_auth http_method, uri, parameters, headers.merge(:authorization => ActionController::HttpAuthentication::Basic.encode_credentials(user_name, password)) + end + + # Performs a request with the given http_method and parameters, including HTTP Digest authorization headers. + # See get() for more details on paramters and headers. + # + # You can perform GET, POST, PUT, DELETE, and HEAD requests with #get_with_digest, #post_with_digest, + # #put_with_digest, #delete_with_digest, and #head_with_digest. + def request_with_digest(http_method, uri, parameters, headers, user_name, password) + # Realm, Nonce, and Opaque taken from previoius 401 response + + credentials = { + :username => user_name, + :realm => @realm, + :nonce => @nonce, + :qop => "auth", + :nc => "00000001", + :cnonce => "0a4f113b", + :opaque => @opaque, + :uri => uri + } + + raise "Digest request without previous 401 response" if @opaque.nil? + + process_with_auth http_method, uri, parameters, headers.merge(:authorization => ActionController::HttpAuthentication::Digest.encode_credentials(http_method, credentials, password)) + end + + # def get_with_basic, def post_with_basic, def put_with_basic, def delete_with_basic, def head_with_basic + # def get_with_digest, def post_with_digest, def put_with_digest, def delete_with_digest, def head_with_digest + [:get, :post, :put, :delete, :head].each do |method| + [:noauth, :basic, :digest].each do |auth_type| + define_method("#{method}_with_#{auth_type}") do |uri, parameters, headers, *auth| + send("request_with_#{auth_type}", method, uri, parameters, headers, *auth) + end + end + end + # Returns the URL for the given options, according to the rules specified # in the application's routes. def url_for(options) @@ -364,6 +420,32 @@ module ActionController return status end + # Same as process, but handles authentication returns to perform + # Basic or Digest authentication + def process_with_auth(method, path, parameters = nil, headers = nil) + status = process(method, path, parameters, headers) + + if status == 401 + # Extract authentication information from response + auth_data = @response.headers['WWW-Authenticate'] + if /^Basic /.match(auth_data) + # extract realm, to be used in subsequent request + @realm = auth_header.split(' ')[1] + elsif /^Digest/.match(auth_data) + creds = auth_data.to_s.gsub(/^Digest\s+/,'').split(',').inject({}) do |hash, pair| + key, value = pair.split('=', 2) + hash[key.strip.to_sym] = value.to_s.gsub(/^"|"$/,'').gsub(/'/, '') + hash + end + @realm = creds[:realm] + @nonce = creds[:nonce] + @opaque = creds[:opaque] + end + end + + return status + end + # Encode the cookies hash in a format suitable for passing to a # request. def encode_cookies -- cgit v1.2.3 From 5d89605c11cc54acadfdd76ccd226d38989ec600 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 28 Dec 2008 15:31:03 -0600 Subject: Make router and controller classes better rack citizens --- actionpack/lib/action_controller/base.rb | 7 +++++++ actionpack/lib/action_controller/dispatcher.rb | 11 ++++------- actionpack/lib/action_controller/request.rb | 4 ++-- actionpack/lib/action_controller/rescue.rb | 4 +++- actionpack/lib/action_controller/routing/route_set.rb | 6 ++++++ 5 files changed, 22 insertions(+), 10 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 5b83494eb4..da3d1f46ee 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -382,6 +382,13 @@ module ActionController #:nodoc: attr_accessor :action_name class << self + def call(env) + # HACK: For global rescue to have access to the original request and response + request = env["actioncontroller.rescue.request"] ||= Request.new(env) + response = env["actioncontroller.rescue.response"] ||= Response.new + process(request, response) + end + # Factory for the standard create, process loop where the controller is discarded after processing. def process(request, response) #:nodoc: new.process(request, response) diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index 4dc76e1b49..c4e7357b81 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -60,11 +60,10 @@ module ActionController def dispatch begin run_callbacks :before_dispatch - controller = Routing::Routes.recognize(@request) - controller.process(@request, @response).to_a + Routing::Routes.call(@env) rescue Exception => exception if controller ||= (::ApplicationController rescue Base) - controller.process_with_exception(@request, @response, exception).to_a + controller.call_with_exception(@env, exception).to_a else raise exception end @@ -83,8 +82,7 @@ module ActionController end def _call(env) - @request = Request.new(env) - @response = Response.new + @env = env dispatch end @@ -110,8 +108,7 @@ module ActionController def checkin_connections # Don't return connection (and peform implicit rollback) if this request is a part of integration test - # TODO: This callback should have direct access to env - return if @request.key?("rack.test") + return if @env.key?("rack.test") ActiveRecord::Base.clear_active_connections! end end diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index 3390324162..ba27c0d294 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -398,7 +398,7 @@ EOM end def path_parameters=(parameters) #:nodoc: - @path_parameters = parameters + @env["routing_args"] = parameters @symbolized_path_parameters = @parameters = nil end @@ -414,7 +414,7 @@ EOM # # See symbolized_path_parameters for symbolized keys. def path_parameters - @path_parameters ||= {} + @env["routing_args"] ||= {} end def body diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb index 5ef79a36ce..3a5e5071bb 100644 --- a/actionpack/lib/action_controller/rescue.rb +++ b/actionpack/lib/action_controller/rescue.rb @@ -59,7 +59,9 @@ module ActionController #:nodoc: end module ClassMethods - def process_with_exception(request, response, exception) #:nodoc: + def call_with_exception(env, exception) #:nodoc: + request = env["actioncontroller.rescue.request"] + response = env["actioncontroller.rescue.response"] new.process(request, response, :rescue_action, exception) end end diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 5975977365..06aef6e169 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -427,6 +427,12 @@ module ActionController end end + def call(env) + request = Request.new(env) + app = Routing::Routes.recognize(request) + app.call(env).to_a + end + def recognize(request) params = recognize_path(request.path, extract_request_environment(request)) request.path_parameters = params.with_indifferent_access -- cgit v1.2.3 From c20c72e3d9321f8c00587aab479d962e80b02c35 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 28 Dec 2008 15:34:59 -0600 Subject: Use rack namespace for routing args --- actionpack/lib/action_controller/request.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index ba27c0d294..822955d1db 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -398,7 +398,7 @@ EOM end def path_parameters=(parameters) #:nodoc: - @env["routing_args"] = parameters + @env["rack.routing_args"] = parameters @symbolized_path_parameters = @parameters = nil end @@ -414,7 +414,7 @@ EOM # # See symbolized_path_parameters for symbolized keys. def path_parameters - @env["routing_args"] ||= {} + @env["rack.routing_args"] ||= {} end def body -- cgit v1.2.3 From 558ab327b733717f4a8de3ed62b8dcd62e9ff9c3 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 29 Dec 2008 19:27:19 -0600 Subject: Clean up view path cruft and split path implementations into Template::Path and Template::EagerPath --- actionpack/lib/action_controller/base.rb | 12 +-- actionpack/lib/action_controller/rescue.rb | 4 +- actionpack/lib/action_view/base.rb | 2 +- actionpack/lib/action_view/inline_template.rb | 2 +- actionpack/lib/action_view/paths.rb | 107 +------------------------- actionpack/lib/action_view/renderable.rb | 9 +-- actionpack/lib/action_view/template.rb | 85 +++++++++++++++++++- 7 files changed, 95 insertions(+), 126 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index da3d1f46ee..bc18dfaec8 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -869,7 +869,7 @@ module ActionController #:nodoc: validate_render_arguments(options, extra_options, block_given?) if options.nil? - options = { :template => default_template.filename, :layout => true } + options = { :template => default_template, :layout => true } elsif options == :update options = extra_options.merge({ :update => true }) elsif options.is_a?(String) || options.is_a?(Symbol) @@ -1125,7 +1125,7 @@ module ActionController #:nodoc: end # Sets the etag, last_modified, or both on the response and renders a - # "304 Not Modified" response if the request is already fresh. + # "304 Not Modified" response if the request is already fresh. # # Example: # @@ -1133,8 +1133,8 @@ module ActionController #:nodoc: # @article = Article.find(params[:id]) # fresh_when(:etag => @article, :last_modified => @article.created_at.utc) # end - # - # This will render the show template if the request isn't sending a matching etag or + # + # This will render the show template if the request isn't sending a matching etag or # If-Modified-Since header and just a "304 Not Modified" response if there's a match. def fresh_when(options) options.assert_valid_keys(:etag, :last_modified) @@ -1239,7 +1239,7 @@ module ActionController #:nodoc: log_processing_for_parameters end end - + def log_processing_for_request_id request_id = "\n\nProcessing #{self.class.name}\##{action_name} " request_id << "to #{params[:format]} " if params[:format] @@ -1251,7 +1251,7 @@ module ActionController #:nodoc: def log_processing_for_parameters parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup parameters = parameters.except!(:controller, :action, :format, :_method) - + logger.info " Parameters: #{parameters.inspect}" unless parameters.empty? end diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb index 3a5e5071bb..de35b53872 100644 --- a/actionpack/lib/action_controller/rescue.rb +++ b/actionpack/lib/action_controller/rescue.rb @@ -38,8 +38,8 @@ module ActionController #:nodoc: 'ActionView::TemplateError' => 'template_error' } - RESCUES_TEMPLATE_PATH = ActionView::PathSet::Path.new( - File.join(File.dirname(__FILE__), "templates"), true) + RESCUES_TEMPLATE_PATH = ActionView::Template::EagerPath.new( + File.join(File.dirname(__FILE__), "templates")) def self.included(base) #:nodoc: base.cattr_accessor :rescue_responses diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 8958e61e9d..70a0ba91a7 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -225,7 +225,7 @@ module ActionView #:nodoc: end # Returns the result of a render that's dictated by the options hash. The primary options are: - # + # # * :partial - See ActionView::Partials. # * :update - Calls update_page with the block given. # * :file - Renders an explicit template file (this used to be the old default), add :locals to pass in those. diff --git a/actionpack/lib/action_view/inline_template.rb b/actionpack/lib/action_view/inline_template.rb index 5e00cef13f..54efa543c8 100644 --- a/actionpack/lib/action_view/inline_template.rb +++ b/actionpack/lib/action_view/inline_template.rb @@ -12,7 +12,7 @@ module ActionView #:nodoc: private # Always recompile inline templates - def recompile?(local_assigns) + def recompile? true end end diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index b030156889..19207e7262 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -2,7 +2,7 @@ module ActionView #:nodoc: class PathSet < Array #:nodoc: def self.type_cast(obj) if obj.is_a?(String) - Path.new(obj) + Template::EagerPath.new(obj) else obj end @@ -32,111 +32,6 @@ module ActionView #:nodoc: super(*objs.map { |obj| self.class.type_cast(obj) }) end - class Path #:nodoc: - attr_reader :path, :paths - delegate :hash, :inspect, :to => :path - - def initialize(path, load = false) - raise ArgumentError, "path already is a Path class" if path.is_a?(Path) - @path = path.freeze - reload! if load - end - - def to_s - if defined?(RAILS_ROOT) - path.to_s.sub(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '') - else - path.to_s - end - end - - def to_str - path.to_str - end - - def ==(path) - to_str == path.to_str - end - - def eql?(path) - to_str == path.to_str - end - - # Returns a ActionView::Template object for the given path string. The - # input path should be relative to the view path directory, - # +hello/index.html.erb+. This method also has a special exception to - # match partial file names without a handler extension. So - # +hello/index.html+ will match the first template it finds with a - # known template extension, +hello/index.html.erb+. Template extensions - # should not be confused with format extensions +html+, +js+, +xml+, - # etc. A format must be supplied to match a formated file. +hello/index+ - # will never match +hello/index.html.erb+. - # - # This method also has two different implementations, one that is "lazy" - # and makes file system calls every time and the other is cached, - # "eager" which looks up the template in an in memory index. The "lazy" - # version is designed for development where you want to automatically - # find new templates between requests. The "eager" version is designed - # for production mode and it is much faster but requires more time - # upfront to build the file index. - def [](path) - if loaded? - @paths[path] - else - Dir.glob("#{@path}/#{path}*").each do |file| - template = create_template(file) - if template.accessible_paths.include?(path) - return template - end - end - nil - end - end - - def loaded? - @loaded ? true : false - end - - def load - reload! unless loaded? - self - end - - # Rebuild load path directory cache - def reload! - @paths = {} - - templates_in_path do |template| - template.load! - template.accessible_paths.each do |path| - @paths[path] = template - end - end - - @paths.freeze - @loaded = true - end - - private - def templates_in_path - (Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file| - yield create_template(file) unless File.directory?(file) - end - end - - def create_template(file) - Template.new(file.split("#{self}/").last, self) - end - end - - def load - each { |path| path.load } - end - - def reload! - each { |path| path.reload! } - end - def find_template(original_template_path, format = nil) return original_template_path if original_template_path.respond_to?(:render) template_path = original_template_path.sub(/^\//, '') diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb index d8e72f1179..153e14f68b 100644 --- a/actionpack/lib/action_view/renderable.rb +++ b/actionpack/lib/action_view/renderable.rb @@ -60,7 +60,7 @@ module ActionView def compile(local_assigns) render_symbol = method_name(local_assigns) - if recompile?(render_symbol) + if !Base::CompiledTemplates.method_defined?(render_symbol) || recompile? compile!(render_symbol, local_assigns) end end @@ -89,11 +89,8 @@ module ActionView end end - # Method to check whether template compilation is necessary. - # The template will be compiled if the file has not been compiled yet, or - # if local_assigns has a new key, which isn't supported by the compiled code yet. - def recompile?(symbol) - !Base::CompiledTemplates.method_defined?(symbol) || !loaded? + def recompile? + false end end end diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 5b384d0e4d..88ee07d95b 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -1,5 +1,83 @@ module ActionView #:nodoc: class Template + class Path + attr_reader :path, :paths + delegate :hash, :inspect, :to => :path + + def initialize(path) + raise ArgumentError, "path already is a Path class" if path.is_a?(Path) + @path = path.freeze + end + + def to_s + if defined?(RAILS_ROOT) + path.to_s.sub(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '') + else + path.to_s + end + end + + def to_str + path.to_str + end + + def ==(path) + to_str == path.to_str + end + + def eql?(path) + to_str == path.to_str + end + + # Returns a ActionView::Template object for the given path string. The + # input path should be relative to the view path directory, + # +hello/index.html.erb+. This method also has a special exception to + # match partial file names without a handler extension. So + # +hello/index.html+ will match the first template it finds with a + # known template extension, +hello/index.html.erb+. Template extensions + # should not be confused with format extensions +html+, +js+, +xml+, + # etc. A format must be supplied to match a formated file. +hello/index+ + # will never match +hello/index.html.erb+. + def [](path) + templates_in_path do |template| + if template.accessible_paths.include?(path) + return template + end + end + nil + end + + private + def templates_in_path + (Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file| + yield create_template(file) unless File.directory?(file) + end + end + + def create_template(file) + Template.new(file.split("#{self}/").last, self) + end + end + + class EagerPath < Path + def initialize(path) + super + + @paths = {} + templates_in_path do |template| + template.load! + template.accessible_paths.each do |path| + @paths[path] = template + end + end + @paths.freeze + end + + def [](path) + @paths[path] + end + end + extend TemplateHandlers extend ActiveSupport::Memoizable include Renderable @@ -115,13 +193,12 @@ module ActionView #:nodoc: File.mtime(filename) > mtime end - def loaded? - @loaded + def recompile? + !@cached end def load! - @loaded = true - compile({}) + @cached = true freeze end -- cgit v1.2.3 From c69d8c043f8d0a587708fb2247b585ca93df822d Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 30 Dec 2008 15:16:51 -0800 Subject: Fix formatted_* deprecation message --- actionpack/lib/action_controller/routing/route_set.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 06aef6e169..044ace7de1 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -195,8 +195,8 @@ module ActionController def formatted_#{selector}(*args) # def formatted_users_url(*args) ActiveSupport::Deprecation.warn( # ActiveSupport::Deprecation.warn( "formatted_#{selector}() has been deprecated. " + # "formatted_users_url() has been deprecated. " + - "please pass format to the standard" + # "please pass format to the standard" + - "#{selector}() method instead.", caller) # "users_url() method instead.", caller) + "Please pass format to the standard " + # "Please pass format to the standard " + + "#{selector} method instead.", caller) # "users_url method instead.", caller) #{selector}(*args) # users_url(*args) end # end protected :#{selector} # protected :users_url -- cgit v1.2.3 From 2e1132fad8fa2ab58476b9ecc30523ed02a43181 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 30 Dec 2008 18:06:56 -0800 Subject: Test that exceptions raised in filters are properly rescued --- actionpack/lib/action_controller/base.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index bc18dfaec8..aa604395a9 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -1350,9 +1350,12 @@ module ActionController #:nodoc: end Base.class_eval do - include Flash, Filters, Layout, Benchmarking, Rescue, MimeResponds, Helpers - include Cookies, Caching, Verification, Streaming - include SessionManagement, HttpAuthentication::Basic::ControllerMethods - include RecordIdentifier, RequestForgeryProtection, Translation + [ Flash, Filters, Layout, Benchmarking, Rescue, MimeResponds, Helpers, + Cookies, Caching, Verification, Streaming, SessionManagement, + HttpAuthentication::Basic::ControllerMethods, RecordIdentifier, + RequestForgeryProtection, Translation + ].each do |mod| + include mod + end end end -- cgit v1.2.3