Skip to content

Commit

Permalink
Merge branch 'master' of git@github.com:rails/rails
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremy committed Nov 30, 2008
2 parents 8521ceb + 1182658 commit 9e4621d
Show file tree
Hide file tree
Showing 22 changed files with 386 additions and 215 deletions.
2 changes: 2 additions & 0 deletions actionpack/CHANGELOG
@@ -1,5 +1,7 @@
*2.3.0 [Edge]*

* Added the option to declare an asset_host as an object that responds to call (see http://github.com/dhh/asset-hosting-with-minimum-ssl for an example) [DHH]

* Added support for multiple routes.rb files (useful for plugin engines). This also means that draw will no longer clear the route set, you have to do that by hand (shouldn't make a difference to you unless you're doing some funky stuff) [DHH]

* Dropped formatted_* routes in favor of just passing in :format as an option. This cuts resource routes generation in half #1359 [aaronbatalion]
Expand Down
5 changes: 3 additions & 2 deletions actionpack/lib/action_controller/base.rb
Expand Up @@ -867,8 +867,9 @@ def render(options = nil, extra_options = {}, &block) #:doc:
end
end

response.layout = layout = pick_layout(options)
logger.info("Rendering template within #{layout}") if logger && layout
layout = pick_layout(options)
response.layout = layout.path_without_format_and_extension if layout
logger.info("Rendering template within #{layout.path_without_format_and_extension}") if logger && layout

if content_type = options[:content_type]
response.content_type = content_type.to_s
Expand Down
1 change: 0 additions & 1 deletion actionpack/lib/action_controller/dispatcher.rb
Expand Up @@ -137,7 +137,6 @@ def reload_application
run_callbacks :prepare_dispatch

Routing::Routes.reload
ActionController::Base.view_paths.reload!
ActionView::Helpers::AssetTagHelper::AssetTag::Cache.clear
end

Expand Down
38 changes: 10 additions & 28 deletions actionpack/lib/action_controller/layout.rb
Expand Up @@ -175,21 +175,20 @@ def layout_conditions #:nodoc:
def default_layout(format) #:nodoc:
layout = read_inheritable_attribute(:layout)
return layout unless read_inheritable_attribute(:auto_layout)
@default_layout ||= {}
@default_layout[format] ||= default_layout_with_format(format, layout)
@default_layout[format]
find_layout(layout, format)
end

def layout_list #:nodoc:
Array(view_paths).sum([]) { |path| Dir["#{path}/layouts/**/*"] }
def find_layout(layout, *formats) #:nodoc:
return layout if layout.respond_to?(:render)
view_paths.find_template(layout.to_s =~ /layouts\// ? layout : "layouts/#{layout}", *formats)
end

private
def inherited_with_layout(child)
inherited_without_layout(child)
unless child.name.blank?
layout_match = child.name.underscore.sub(/_controller$/, '').sub(/^controllers\//, '')
child.layout(layout_match, {}, true) unless child.layout_list.grep(%r{layouts/#{layout_match}(\.[a-z][0-9a-z]*)+$}).empty?
child.layout(layout_match, {}, true) if child.find_layout(layout_match, :all)
end
end

Expand All @@ -200,15 +199,6 @@ def add_layout_conditions(conditions)
def normalize_conditions(conditions)
conditions.inject({}) {|hash, (key, value)| hash.merge(key => [value].flatten.map {|action| action.to_s})}
end

def default_layout_with_format(format, layout)
list = layout_list
if list.grep(%r{layouts/#{layout}\.#{format}(\.[a-z][0-9a-z]*)+$}).empty?
(!list.grep(%r{layouts/#{layout}\.([a-z][0-9a-z]*)+$}).empty? && format == :html) ? layout : nil
else
layout
end
end
end

# Returns the name of the active layout. If the layout was specified as a method reference (through a symbol), this method
Expand All @@ -217,20 +207,18 @@ def default_layout_with_format(format, layout)
# weblog/standard, but <tt>layout "standard"</tt> will return layouts/standard.
def active_layout(passed_layout = nil)
layout = passed_layout || self.class.default_layout(default_template_format)

active_layout = case layout
when String then layout
when Symbol then __send__(layout)
when Proc then layout.call(self)
else layout
end

# Explicitly passed layout names with slashes are looked up relative to the template root,
# but auto-discovered layouts derived from a nested controller will contain a slash, though be relative
# to the 'layouts' directory so we have to check the file system to infer which case the layout name came from.
if active_layout
if active_layout.include?('/') && ! layout_directory?(active_layout)
active_layout
if layout = self.class.find_layout(active_layout, @template.template_format)
layout
else
"layouts/#{active_layout}"
raise ActionView::MissingTemplate.new(self.class.view_paths, active_layout)
end
end
end
Expand Down Expand Up @@ -271,12 +259,6 @@ def action_has_layout?
end
end

def layout_directory?(layout_name)
@template.__send__(:_pick_template, "#{File.join('layouts', layout_name)}.#{@template.template_format}") ? true : false
rescue ActionView::MissingTemplate
false
end

def default_template_format
response.template.template_format
end
Expand Down
4 changes: 1 addition & 3 deletions actionpack/lib/action_view/base.rb
Expand Up @@ -322,9 +322,7 @@ def _pick_template(template_path)
end

# OPTIMIZE: Checks to lookup template in view path
if template = self.view_paths["#{template_file_name}.#{template_format}"]
template
elsif template = self.view_paths[template_file_name]
if template = self.view_paths.find_template(template_file_name, template_format)
template
elsif (first_render = @_render_stack.first) && first_render.respond_to?(:format_and_extension) &&
(template = self.view_paths["#{template_file_name}.#{first_render.format_and_extension}"])
Expand Down
16 changes: 12 additions & 4 deletions actionpack/lib/action_view/helpers/asset_tag_helper.rb
Expand Up @@ -80,6 +80,12 @@ module Helpers #:nodoc:
# end
# }
#
# You can also implement a custom asset host object that responds to the call method and tasks one or two parameters just like the proc.
#
# config.action_controller.asset_host = AssetHostingWithMinimumSsl.new(
# "http://asset%d.example.com", "https://asset1.example.com"
# )
#
# === Using asset timestamps
#
# By default, Rails will append all asset paths with that asset's timestamp. This allows you to set a cache-expiration date for the
Expand Down Expand Up @@ -359,6 +365,7 @@ def stylesheet_path(source)
# compressed by gzip (leading to faster transfers). Caching will only happen if ActionController::Base.perform_caching
# is set to true (which is the case by default for the Rails production environment, but not for the development
# environment). Examples:

#
# ==== Examples
# stylesheet_link_tag :all, :cache => true # when ActionController::Base.perform_caching is false =>
Expand Down Expand Up @@ -585,7 +592,7 @@ def compute_public_path(source)
source
else
CacheGuard.synchronize do
Cache[@cache_key] ||= begin
Cache[@cache_key + [source]] ||= begin
source += ".#{extension}" if missing_extension?(source) || file_exists_with_extension?(source)
source = "/#{directory}/#{source}" unless source[0] == ?/
source = rewrite_asset_path(source)
Expand Down Expand Up @@ -629,11 +636,12 @@ def prepend_asset_host(source)
# Pick an asset host for this source. Returns +nil+ if no host is set,
# the host if no wildcard is set, the host interpolated with the
# numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
# or the value returned from invoking the proc if it's a proc.
# or the value returned from invoking the proc if it's a proc or the value from
# invoking call if it's an object responding to call.
def compute_asset_host(source)
if host = ActionController::Base.asset_host
if host.is_a?(Proc)
case host.arity
if host.is_a?(Proc) || host.respond_to?(:call)
case host.is_a?(Proc) ? host.arity : host.method(:call).arity
when 2
host.call(source, request)
else
Expand Down
67 changes: 50 additions & 17 deletions actionpack/lib/action_view/paths.rb
Expand Up @@ -40,18 +40,10 @@ def unshift(*objs)
end

class Path #:nodoc:
def self.eager_load_templates!
@eager_load_templates = true
end

def self.eager_load_templates?
@eager_load_templates || false
end

attr_reader :path, :paths
delegate :to_s, :to_str, :hash, :inspect, :to => :path

def initialize(path, load = true)
def initialize(path, load = false)
raise ArgumentError, "path already is a Path class" if path.is_a?(Path)
@path = path.freeze
reload! if load
Expand All @@ -65,9 +57,35 @@ 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)
raise "Unloaded view path! #{@path}" unless @loaded
@paths[path]
if loaded?
@paths[path]
else
Dir.glob("#{@path}/#{path}*").each do |file|
template = create_template(file)
if path == template.path_without_extension || path == template.path
return template
end
end
nil
end
end

def loaded?
Expand All @@ -84,9 +102,7 @@ def reload!
@paths = {}

templates_in_path do |template|
# Eager load memoized methods and freeze cached template
template.freeze if self.class.eager_load_templates?

template.load!
@paths[template.path] = template
@paths[template.path_without_extension] ||= template
end
Expand All @@ -98,11 +114,13 @@ def reload!
private
def templates_in_path
(Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file|
unless File.directory?(file)
yield Template.new(file.split("#{self}/").last, self)
end
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
Expand All @@ -121,5 +139,20 @@ def [](template_path)
end
nil
end

def find_template(path, *formats)
if formats && formats.first == :all
formats = Mime::EXTENSION_LOOKUP.values.map(&:to_sym)
end
formats.each do |format|
if template = self["#{path}.#{format}"]
return template
end
end
if template = self[path]
return template
end
nil
end
end
end
2 changes: 1 addition & 1 deletion actionpack/lib/action_view/renderable.rb
Expand Up @@ -96,7 +96,7 @@ def #{render_symbol}(local_assigns)
# 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)
!(ActionView::PathSet::Path.eager_load_templates? && Base::CompiledTemplates.method_defined?(symbol))
!Base::CompiledTemplates.method_defined?(symbol) || !loaded?
end
end
end
18 changes: 18 additions & 0 deletions actionpack/lib/action_view/template.rb
Expand Up @@ -57,6 +57,11 @@ def relative_path
end
memoize :relative_path

def mtime
File.mtime(filename)
end
memoize :mtime

def source
File.read(filename)
end
Expand All @@ -79,6 +84,19 @@ def render_template(view, local_assigns = {})
end
end

def stale?
File.mtime(filename) > mtime
end

def loaded?
@loaded
end

def load!
@loaded = true
freeze
end

private
def valid_extension?(extension)
Template.template_handler_extensions.include?(extension)
Expand Down
2 changes: 1 addition & 1 deletion actionpack/test/abstract_unit.rb
Expand Up @@ -31,8 +31,8 @@
ActionController::Routing::Routes.reload rescue nil

FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
ActionView::PathSet::Path.eager_load_templates!
ActionController::Base.view_paths = FIXTURE_LOAD_PATH
ActionController::Base.view_paths.load

def uses_mocha(test_name)
yield
Expand Down
14 changes: 7 additions & 7 deletions actionpack/test/controller/layout_test.rb
Expand Up @@ -3,6 +3,10 @@
# The view_paths array must be set on Base and not LayoutTest so that LayoutTest's inherited
# method has access to the view_paths array when looking for a layout to automatically assign.
old_load_paths = ActionController::Base.view_paths

ActionView::Template::register_template_handler :mab,
lambda { |template| template.source.inspect }

ActionController::Base.view_paths = [ File.dirname(__FILE__) + '/../fixtures/layout_tests/' ]

class LayoutTest < ActionController::Base
Expand Down Expand Up @@ -31,9 +35,6 @@ class ControllerNameSpace::NestedController < LayoutTest
class MultipleExtensions < LayoutTest
end

ActionView::Template::register_template_handler :mab,
lambda { |template| template.source.inspect }

class LayoutAutoDiscoveryTest < ActionController::TestCase
def setup
@request.host = "www.nextangle.com"
Expand All @@ -52,10 +53,9 @@ def test_controller_name_layout_name_match
end

def test_third_party_template_library_auto_discovers_layout
ThirdPartyTemplateLibraryController.view_paths.reload!
@controller = ThirdPartyTemplateLibraryController.new
get :hello
assert_equal 'layouts/third_party_template_library', @controller.active_layout
assert_equal 'layouts/third_party_template_library.mab', @controller.active_layout.to_s
assert_equal 'layouts/third_party_template_library', @response.layout
assert_response :success
assert_equal 'Mab', @response.body
Expand All @@ -64,14 +64,14 @@ def test_third_party_template_library_auto_discovers_layout
def test_namespaced_controllers_auto_detect_layouts
@controller = ControllerNameSpace::NestedController.new
get :hello
assert_equal 'layouts/controller_name_space/nested', @controller.active_layout
assert_equal 'layouts/controller_name_space/nested', @controller.active_layout.to_s
assert_equal 'controller_name_space/nested.rhtml hello.rhtml', @response.body
end

def test_namespaced_controllers_auto_detect_layouts
@controller = MultipleExtensions.new
get :hello
assert_equal 'layouts/multiple_extensions', @controller.active_layout
assert_equal 'layouts/multiple_extensions.html.erb', @controller.active_layout.to_s
assert_equal 'multiple_extensions.html.erb hello.rhtml', @response.body.strip
end
end
Expand Down

0 comments on commit 9e4621d

Please sign in to comment.