diff options
-rw-r--r-- | Gemfile | 3 | ||||
-rw-r--r-- | actionpack/examples/performance.rb | 185 | ||||
-rw-r--r-- | actionpack/examples/views/_collection.erb | 3 | ||||
-rw-r--r-- | actionpack/examples/views/_hello.erb | 1 | ||||
-rw-r--r-- | actionpack/examples/views/_hundred_partials.erb | 3 | ||||
-rw-r--r-- | actionpack/examples/views/_partial.erb | 10 | ||||
-rw-r--r-- | actionpack/examples/views/_ten_partials.erb | 10 | ||||
-rw-r--r-- | actionpack/examples/views/hashes/_hash.erb | 3 | ||||
-rw-r--r-- | actionpack/examples/views/my_hashes/_my_hash.erb | 3 | ||||
-rw-r--r-- | actionpack/examples/views/template.html.erb | 1 | ||||
-rw-r--r-- | actionpack/lib/abstract_controller/layouts.rb | 57 | ||||
-rw-r--r-- | actionpack/lib/abstract_controller/rendering.rb | 7 | ||||
-rw-r--r-- | actionpack/lib/abstract_controller/view_paths.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_view/lookup_context.rb | 19 | ||||
-rw-r--r-- | actionpack/lib/action_view/renderer/template_renderer.rb | 21 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/resolver.rb | 2 | ||||
-rw-r--r-- | activerecord/examples/performance.rb | 2 |
17 files changed, 276 insertions, 56 deletions
@@ -53,9 +53,6 @@ platforms :mri do end platforms :ruby do - if ENV["RB_FSEVENT"] - gem "rb-fsevent" - end gem "json" gem "yajl-ruby" gem "nokogiri", ">= 1.4.5" diff --git a/actionpack/examples/performance.rb b/actionpack/examples/performance.rb new file mode 100644 index 0000000000..994e745bb0 --- /dev/null +++ b/actionpack/examples/performance.rb @@ -0,0 +1,185 @@ +ENV['RAILS_ENV'] ||= 'production' + +require File.expand_path('../../../load_paths', __FILE__) +require 'action_pack' +require 'action_controller' +require 'action_view' +require 'active_model' +require 'benchmark' + +MyHash = Class.new(Hash) + +Hash.class_eval do + extend ActiveModel::Naming + include ActiveModel::Conversion +end + +class Runner + def initialize(app, output) + @app, @output = app, output + end + + def puts(*) + super if @output + end + + def call(env) + env['n'].to_i.times { @app.call(env) } + @app.call(env).tap { |response| report(env, response) } + 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.puts(*) + super if @output + end + + def self.print(*) + super if @output + end + + def self.app_and_env_for(action, n) + env = Rack::MockRequest.env_for("/") + env.merge!('n' => n, 'rack.input' => StringIO.new(''), 'rack.errors' => $stdout) + app = lambda { |env| BasePostController.action(action).call(env) } + return app, env + end + + $ran = [] + + def self.run(action, n, output = true) + print "." + STDOUT.flush + @output = output + label = action.to_s + app, env = app_and_env_for(action, n) + t = Benchmark.realtime { new(app, output).call(env) } + $ran << [label, (t * 1000).to_i.to_s] if output + end + + def self.done + puts + header, content = "", "" + $ran.each do |k,v| + size = [k.size, v.size].max + 1 + header << format("%#{size}s", k) + content << format("%#{size}s", v) + end + puts header + puts content + end +end + +ActionController::Base.logger = nil +ActionController::Base.config.compile_methods! +ActionView::Resolver.caching = ENV["RAILS_ENV"] == "production" + +class BasePostController < ActionController::Base + append_view_path "#{File.dirname(__FILE__)}/views" + + def overhead + self.response_body = '' + end + + def index + render :text => '' + end + + $OBJECT = {:name => "Hello my name is omg", :address => "333 omg"} + + def partial + render :partial => "/collection", :object => $OBJECT + end + + def partial_10 + render :partial => "/ten_partials" + end + + def partial_100 + render :partial => "/hundred_partials" + end + + $COLLECTION1 = [] + 10.times do |i| + $COLLECTION1 << { :name => "Hello my name is omg", :address => "333 omg" } + end + + def coll_10 + render :partial => "/collection", :collection => $COLLECTION1 + end + + $COLLECTION2 = [] + 100.times do |i| + $COLLECTION2 << { :name => "Hello my name is omg", :address => "333 omg" } + end + + def coll_100 + render :partial => "/collection", :collection => $COLLECTION2 + end + + def uniq_100 + render :partial => $COLLECTION2 + end + + $COLLECTION3 = [] + 50.times do |i| + $COLLECTION3 << {:name => "Hello my name is omg", :address => "333 omg"} + $COLLECTION3 << MyHash.new(:name => "Hello my name is omg", :address => "333 omg") + end + + def diff_100 + render :partial => $COLLECTION3 + end + + def template_1 + render :template => "template" + end + + module Foo + def omg + "omg" + end + end + + helper Foo +end + +N = (ENV['N'] || 1000).to_i +# ActionController::Base.use_accept_header = false + +def run_all!(times, verbose) + Runner.run(:overhead, times, verbose) + Runner.run(:index, times, verbose) + Runner.run(:template_1, times, verbose) + Runner.run(:partial, times, verbose) + Runner.run(:partial_10, times, verbose) + Runner.run(:coll_10, times, verbose) + Runner.run(:partial_100, times, verbose) + Runner.run(:coll_100, times, verbose) + Runner.run(:uniq_100, times, verbose) + Runner.run(:diff_100, times, verbose) +end + +unless ENV["PROFILE"] + run_all!(1, false) + + (ENV["M"] || 1).to_i.times do + $ran = [] + run_all!(N, true) + Runner.done + end +else + Runner.run(ENV["PROFILE"].to_sym, 1, false) + require "ruby-prof" + RubyProf.start + Runner.run(ENV["PROFILE"].to_sym, N, true) + 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..cee3fe64c0 --- /dev/null +++ b/actionpack/examples/views/_collection.erb @@ -0,0 +1,3 @@ +<%= collection[:name] %> +<%= collection[:address] %> +<%= omg %>
\ 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/_hundred_partials.erb b/actionpack/examples/views/_hundred_partials.erb new file mode 100644 index 0000000000..35c2a6c9d3 --- /dev/null +++ b/actionpack/examples/views/_hundred_partials.erb @@ -0,0 +1,3 @@ +<% 100.times do %> + <%= render :partial => "/collection", :object => $OBJECT %> +<% end %>
\ 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/_ten_partials.erb b/actionpack/examples/views/_ten_partials.erb new file mode 100644 index 0000000000..fd02991e22 --- /dev/null +++ b/actionpack/examples/views/_ten_partials.erb @@ -0,0 +1,10 @@ +<%= render :partial => '/collection', :object => $OBJECT %> +<%= render :partial => '/collection', :object => $OBJECT %> +<%= render :partial => '/collection', :object => $OBJECT %> +<%= render :partial => '/collection', :object => $OBJECT %> +<%= render :partial => '/collection', :object => $OBJECT %> +<%= render :partial => '/collection', :object => $OBJECT %> +<%= render :partial => '/collection', :object => $OBJECT %> +<%= render :partial => '/collection', :object => $OBJECT %> +<%= render :partial => '/collection', :object => $OBJECT %> +<%= render :partial => '/collection', :object => $OBJECT %>
\ No newline at end of file diff --git a/actionpack/examples/views/hashes/_hash.erb b/actionpack/examples/views/hashes/_hash.erb new file mode 100644 index 0000000000..c100a290bd --- /dev/null +++ b/actionpack/examples/views/hashes/_hash.erb @@ -0,0 +1,3 @@ +<%= hash[:name] %> +<%= hash[:address] %> +<%= omg %>
\ No newline at end of file diff --git a/actionpack/examples/views/my_hashes/_my_hash.erb b/actionpack/examples/views/my_hashes/_my_hash.erb new file mode 100644 index 0000000000..e25d84101a --- /dev/null +++ b/actionpack/examples/views/my_hashes/_my_hash.erb @@ -0,0 +1,3 @@ +<%= my_hash[:name] %> +<%= my_hash[:address] %> +<%= omg %>
\ No newline at end of file diff --git a/actionpack/examples/views/template.html.erb b/actionpack/examples/views/template.html.erb new file mode 100644 index 0000000000..5ab2f8a432 --- /dev/null +++ b/actionpack/examples/views/template.html.erb @@ -0,0 +1 @@ +Hello
\ No newline at end of file diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb index 79edd5418e..8356d822da 100644 --- a/actionpack/lib/abstract_controller/layouts.rb +++ b/actionpack/lib/abstract_controller/layouts.rb @@ -168,11 +168,12 @@ module AbstractController included do class_attribute :_layout_conditions remove_possible_method :_layout_conditions - delegate :_layout_conditions, :to => :'self.class' self._layout_conditions = {} _write_layout_method end + delegate :_layout_conditions, :to => "self.class" + module ClassMethods def inherited(klass) super @@ -188,7 +189,7 @@ module AbstractController # # ==== Returns # * <tt> Boolean</tt> - True if the action has a layout, false otherwise. - def action_has_layout? + def conditional_layout? return unless super conditions = _layout_conditions @@ -244,7 +245,13 @@ module AbstractController def _write_layout_method remove_possible_method(:_layout) - prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"] + prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"] + name_clause = if name + <<-RUBY + lookup_context.find_all("#{_implied_layout_name}", #{prefixes.inspect}).first || super + RUBY + end + layout_definition = case defined?(@_layout) ? @_layout : nil when String @_layout.inspect @@ -265,27 +272,15 @@ module AbstractController when true raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil" when nil - if name - <<-RUBY - if template_exists?("#{_implied_layout_name}", #{prefixes.inspect}) - "#{_implied_layout_name}" - else - super - end - RUBY - end + name_clause end self.class_eval <<-RUBY, __FILE__, __LINE__ + 1 def _layout - if action_has_layout? + if conditional_layout? #{layout_definition} - elsif self.class.name - if template_exists?("#{_implied_layout_name}", #{prefixes.inspect}) - "#{_implied_layout_name}" - else - super - end + else + #{name_clause} end end RUBY @@ -298,8 +293,7 @@ module AbstractController if _include_layout?(options) layout = options.key?(:layout) ? options.delete(:layout) : :default - value = _layout_for_option(layout) - options[:layout] = (value =~ /\blayouts/ ? value : "layouts/#{value}") if value + options[:layout] = _layout_for_option(layout) end end @@ -314,6 +308,10 @@ module AbstractController @_action_has_layout end + def conditional_layout? + true + end + private # This will be overwritten by _write_layout_method @@ -325,9 +323,10 @@ module AbstractController # * <tt>name</tt> - The name of the template def _layout_for_option(name) case name - when String then name - when true then _default_layout(true) - when :default then _default_layout(false) + when String then _normalize_layout(name) + when Proc then name + when true then Proc.new { _default_layout(true) } + when :default then Proc.new { _default_layout(false) } when false, nil then nil else raise ArgumentError, @@ -335,6 +334,10 @@ module AbstractController end end + def _normalize_layout(value) + value.is_a?(String) && value !~ /\blayouts/ ? "layouts/#{value}" : value + end + # Returns the default layout for this controller. # Optionally raises an exception if the layout could not be found. # @@ -346,17 +349,17 @@ module AbstractController # * <tt>template</tt> - The template object for the default layout (or nil) def _default_layout(require_layout = false) begin - layout_name = _layout + value = _layout if action_has_layout? rescue NameError => e raise e, "Could not render layout: #{e.message}" end - if require_layout && action_has_layout? && !layout_name + if require_layout && action_has_layout? && !value raise ArgumentError, "There was no default layout for #{self.class} in #{view_paths.inspect}" end - layout_name + _normalize_layout(value) end def _include_layout?(options) diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index 41fdc11196..d95770c049 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -66,12 +66,7 @@ module AbstractController end def view_context_class - @_view_context_class || self.class.view_context_class - end - - def initialize(*) - @_view_context_class = nil - super + @_view_context_class ||= self.class.view_context_class end # An instance of a view class. The default view class is ActionView::Base diff --git a/actionpack/lib/abstract_controller/view_paths.rb b/actionpack/lib/abstract_controller/view_paths.rb index e8394447a7..96118b940f 100644 --- a/actionpack/lib/abstract_controller/view_paths.rb +++ b/actionpack/lib/abstract_controller/view_paths.rb @@ -10,7 +10,7 @@ module AbstractController self._view_paths.freeze end - delegate :find_template, :template_exists?, :view_paths, :formats, :formats=, + delegate :template_exists?, :view_paths, :formats, :formats=, :locale, :locale=, :to => :lookup_context module ClassMethods diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb index fa4bf70f77..f586708d54 100644 --- a/actionpack/lib/action_view/lookup_context.rb +++ b/actionpack/lib/action_view/lookup_context.rb @@ -20,7 +20,7 @@ module ActionView def self.register_detail(name, options = {}, &block) self.registered_details << name - initialize = registered_details.map { |n| "self.#{n} = details[:#{n}]" } + initialize = registered_details.map { |n| "@details[:#{n}] = details[:#{n}] || default_#{n}" } Accessors.send :define_method, :"default_#{name}", &block Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1 @@ -29,7 +29,7 @@ module ActionView end def #{name}=(value) - value = Array.wrap(value.presence || default_#{name}) + value = value.present? ? Array.wrap(value) : default_#{name} _set_detail(:#{name}, value) if value != @details[:#{name}] end @@ -153,14 +153,14 @@ module ActionView "" end - parts = name.split('/') - name = parts.pop + prefixes = nil if prefixes.blank? + parts = name.split('/') + name = parts.pop - prefixes = if prefixes.blank? - [parts.join('/')] - else - prefixes.map { |prefix| [prefix, *parts].compact.join('/') } - end + return name, prefixes || [""] if parts.empty? + + parts = parts.join('/') + prefixes = prefixes ? prefixes.map { |p| "#{p}/#{parts}" } : [parts] return name, prefixes end @@ -227,7 +227,6 @@ module ActionView end # A method which only uses the first format in the formats array for layout lookup. - # This method plays straight with instance variables for performance reasons. def with_layout_format if formats.size == 1 yield diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb index ac91d333ba..6bf91de05a 100644 --- a/actionpack/lib/action_view/renderer/template_renderer.rb +++ b/actionpack/lib/action_view/renderer/template_renderer.rb @@ -58,14 +58,21 @@ module ActionView # context object. If no layout is found, it checks if at least a layout with # the given name exists across all details before raising the error. def find_layout(layout, keys) - begin - with_layout_format do - layout =~ /^\// ? - with_fallbacks { find_template(layout, nil, false, keys, @details) } : find_template(layout, nil, false, keys, @details) + with_layout_format { resolve_layout(layout, keys) } + end + + def resolve_layout(layout, keys) + case layout + when String + if layout =~ /^\// + with_fallbacks { find_template(layout, nil, false, keys, @details) } + else + find_template(layout, nil, false, keys, @details) end - rescue ActionView::MissingTemplate - all_details = @details.merge(:formats => @lookup_context.default_formats) - raise unless template_exists?(layout, nil, false, keys, all_details) + when Proc + resolve_layout(layout.call, keys) + else + layout end end end diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index f855ea257c..eb80a865b5 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -24,7 +24,7 @@ module ActionView end end - cattr_accessor :caching + class_attribute :caching self.caching = true class << self diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb index 0f62e819ee..d29af85278 100644 --- a/activerecord/examples/performance.rb +++ b/activerecord/examples/performance.rb @@ -1,6 +1,6 @@ TIMES = (ENV['N'] || 10000).to_i -require 'rubygems' +require File.expand_path('../../../load_paths', __FILE__) require "active_record" conn = { :adapter => 'sqlite3', :database => ':memory:' } |