From 2bfa477128ef36e5664709684b51c20bebf1ee70 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 17 Dec 2009 12:02:19 -0800 Subject: Fix deprecated :vendored_at option --- Gemfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 87bf43b786..d979add5b3 100644 --- a/Gemfile +++ b/Gemfile @@ -1,9 +1,9 @@ gem "rake", ">= 0.8.7" gem "mocha", ">= 0.9.8" -gem "rails", "3.0.pre", :vendored_at => "railties" +gem "rails", "3.0.pre", :path => "railties" %w(activesupport activemodel actionpack actionmailer activerecord activeresource).each do |lib| - gem lib, '3.0.pre', :vendored_at => lib + gem lib, '3.0.pre', :path => lib end # AR -- cgit v1.2.3 From fa575973b1ad5adb7115a18c4c1c7c31500e73b2 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 17 Dec 2009 16:37:11 -0800 Subject: Added alert/notice from 2-3-stable and refactored redirect_to into just living in Redirector [DHH] --- actionpack/CHANGELOG | 12 +++ actionpack/lib/action_controller/base.rb | 66 ----------------- actionpack/lib/action_controller/metal/flash.rb | 41 +++++++++++ .../lib/action_controller/metal/redirector.rb | 86 ++++++++++++++++++++-- actionpack/test/controller/flash_test.rb | 29 +++++++- .../erb/scaffold/templates/layout.html.erb | 2 +- .../scaffold_controller/templates/controller.rb | 6 +- 7 files changed, 165 insertions(+), 77 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 30f3f31563..3852dfe02b 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,17 @@ *Edge* +* Added :alert, :notice, and :flash as options to ActionController::Base#redirect_to that'll automatically set the proper flash before the redirection [DHH]. Examples: + + flash[:notice] = 'Post was created' + redirect_to(@post) + + ...becomes: + + redirect_to(@post, :notice => 'Post was created') + +* Added ActionController::Base#notice/= and ActionController::Base#alert/= as a convenience accessors in both the controller and the view for flash[:notice]/= and flash[:alert]/= [DHH] + + * 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] diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 48bfbab215..e49be371a6 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -106,71 +106,5 @@ module ActionController options = _normalize_options(action, options, &blk) super(options) end - - # Redirects the browser to the target specified in +options+. This parameter can take one of three forms: - # - # * Hash - The URL will be generated by calling url_for with the +options+. - # * Record - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record. - # * String starting with protocol:// (like http://) - Is passed straight through as the target for redirection. - # * String not containing a protocol - The current protocol and host is prepended to the string. - # * :back - Back to the page that issued the request. Useful for forms that are triggered from multiple places. - # Short-hand for redirect_to(request.env["HTTP_REFERER"]) - # - # Examples: - # redirect_to :action => "show", :id => 5 - # redirect_to post - # redirect_to "http://www.rubyonrails.org" - # redirect_to "/images/screenshot.jpg" - # redirect_to articles_url - # redirect_to :back - # - # The redirection happens as a "302 Moved" header unless otherwise specified. - # - # Examples: - # redirect_to post_url(@post), :status=>:found - # redirect_to :action=>'atom', :status=>:moved_permanently - # redirect_to post_url(@post), :status=>301 - # redirect_to :action=>'atom', :status=>302 - # - # When using redirect_to :back, if there is no referrer, - # RedirectBackError will be raised. You may specify some fallback - # behavior for this case by rescuing RedirectBackError. - def redirect_to(options = {}, response_status = {}) #:doc: - raise ActionControllerError.new("Cannot redirect to nil!") if options.nil? - - status = if options.is_a?(Hash) && options.key?(:status) - _interpret_status(options.delete(:status)) - elsif response_status.key?(:status) - _interpret_status(response_status[:status]) - else - 302 - end - - url = case options - # The scheme name consist of a letter followed by any combination of - # letters, digits, and the plus ("+"), period ("."), or hyphen ("-") - # characters; and is terminated by a colon (":"). - when %r{^\w[\w\d+.-]*:.*} - options - when String - request.protocol + request.host_with_port + options - when :back - raise RedirectBackError unless refer = request.headers["Referer"] - refer - else - url_for(options) - end - - super(url, status) - end - - private - def _interpret_status(status) - if status.is_a?(Symbol) - (ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[status] || 500) - else - status.to_i - end - end end end diff --git a/actionpack/lib/action_controller/metal/flash.rb b/actionpack/lib/action_controller/metal/flash.rb index 9d08ed6081..ae343444e2 100644 --- a/actionpack/lib/action_controller/metal/flash.rb +++ b/actionpack/lib/action_controller/metal/flash.rb @@ -30,6 +30,10 @@ module ActionController #:nodoc: include Session + included do + helper_method :alert, :notice + end + class FlashNow #:nodoc: def initialize(flash) @flash = flash @@ -147,6 +151,27 @@ module ActionController #:nodoc: @_flash end + # Convenience accessor for flash[:alert] + def alert + flash[:alert] + end + + # Convenience accessor for flash[:alert]= + def alert=(message) + flash[:alert] = message + end + + # Convenience accessor for flash[:notice] + def notice + flash[:notice] + end + + # Convenience accessor for flash[:notice]= + def notice=(message) + flash[:notice] = message + end + + protected def process_action(method_name) @_flash = nil @@ -159,5 +184,21 @@ module ActionController #:nodoc: super @_flash = nil end + + def redirect_to(options = {}, response_status_and_flash = {}) #:doc: + if alert = response_status_and_flash.delete(:alert) + flash[:alert] = alert + end + + if notice = response_status_and_flash.delete(:notice) + flash[:notice] = notice + end + + if other_flashes = response_status_and_flash.delete(:flash) + flash.update(other_flashes) + end + + super(options, response_status_and_flash) + end end end diff --git a/actionpack/lib/action_controller/metal/redirector.rb b/actionpack/lib/action_controller/metal/redirector.rb index b55f5e7bfc..a7bd5ee981 100644 --- a/actionpack/lib/action_controller/metal/redirector.rb +++ b/actionpack/lib/action_controller/metal/redirector.rb @@ -11,12 +11,88 @@ module ActionController extend ActiveSupport::Concern include AbstractController::Logger - def redirect_to(url, status) #:doc: + # Redirects the browser to the target specified in +options+. This parameter can take one of three forms: + # + # * Hash - The URL will be generated by calling url_for with the +options+. + # * Record - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record. + # * String starting with protocol:// (like http://) - Is passed straight through as the target for redirection. + # * String not containing a protocol - The current protocol and host is prepended to the string. + # * :back - Back to the page that issued the request. Useful for forms that are triggered from multiple places. + # Short-hand for redirect_to(request.env["HTTP_REFERER"]) + # + # Examples: + # redirect_to :action => "show", :id => 5 + # redirect_to post + # redirect_to "http://www.rubyonrails.org" + # redirect_to "/images/screenshot.jpg" + # redirect_to articles_url + # redirect_to :back + # + # The redirection happens as a "302 Moved" header unless otherwise specified. + # + # Examples: + # redirect_to post_url(@post), :status => :found + # redirect_to :action=>'atom', :status => :moved_permanently + # redirect_to post_url(@post), :status => 301 + # redirect_to :action=>'atom', :status => 302 + # + # It is also possible to assign a flash message as part of the redirection. There are two special accessors for commonly used the flash names + # +alert+ and +notice+ as well as a general purpose +flash+ bucket. + # + # Examples: + # redirect_to post_url(@post), :alert => "Watch it, mister!" + # redirect_to post_url(@post), :status=> :found, :notice => "Pay attention to the road" + # redirect_to post_url(@post), :status => 301, :flash => { :updated_post_id => @post.id } + # redirect_to { :action=>'atom' }, :alert => "Something serious happened" + # + # When using redirect_to :back, if there is no referrer, + # RedirectBackError will be raised. You may specify some fallback + # behavior for this case by rescuing RedirectBackError. + def redirect_to(options = {}, response_status = {}) #:doc: + raise ActionControllerError.new("Cannot redirect to nil!") if options.nil? raise AbstractController::DoubleRenderError if response_body - logger.info("Redirected to #{url}") if logger && logger.info? - self.status = status - self.location = url.gsub(/[\r\n]/, '') - self.response_body = "You are being redirected." + + self.status = _extract_redirect_to_status(options, response_status) + self.location = _compute_redirect_to_location(options) + self.response_body = "You are being redirected." + + logger.info("Redirected to #{location}") if logger && logger.info? end + + private + def _extract_redirect_to_status(options, response_status) + status = if options.is_a?(Hash) && options.key?(:status) + _interpret_status(options.delete(:status)) + elsif response_status.key?(:status) + _interpret_status(response_status[:status]) + else + 302 + end + end + + def _compute_redirect_to_location(options) + case options + # The scheme name consist of a letter followed by any combination of + # letters, digits, and the plus ("+"), period ("."), or hyphen ("-") + # characters; and is terminated by a colon (":"). + when %r{^\w[\w\d+.-]*:.*} + options + when String + request.protocol + request.host_with_port + options + when :back + raise RedirectBackError unless refer = request.headers["Referer"] + refer + else + url_for(options) + end.gsub(/[\r\n]/, '') + end + + def _interpret_status(status) + if status.is_a?(Symbol) + (ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[status] || 500) + else + status.to_i + end + end end end diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb index c448f36cb3..1f5be431ac 100644 --- a/actionpack/test/controller/flash_test.rb +++ b/actionpack/test/controller/flash_test.rb @@ -72,6 +72,18 @@ class FlashTest < ActionController::TestCase redirect_to :action => "std_action" @flash_copy = {}.update(flash) end + + def redirect_with_alert + redirect_to '/nowhere', :alert => "Beware the nowheres!" + end + + def redirect_with_notice + redirect_to '/somewhere', :notice => "Good luck in the somewheres!" + end + + def redirect_with_other_flashes + redirect_to '/wonderland', :flash => { :joyride => "Horses!" } + end end tests TestController @@ -160,4 +172,19 @@ class FlashTest < ActionController::TestCase assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep()) # nothing passed assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep(nil)) # nothing passed end -end + + def test_redirect_to_with_alert + get :redirect_with_alert + assert_equal "Beware the nowheres!", @controller.send(:flash)[:alert] + end + + def test_redirect_to_with_notice + get :redirect_with_notice + assert_equal "Good luck in the somewheres!", @controller.send(:flash)[:notice] + end + + def test_redirect_to_with_other_flashes + get :redirect_with_other_flashes + assert_equal "Horses!", @controller.send(:flash)[:joyride] + end +end \ No newline at end of file diff --git a/railties/lib/rails/generators/erb/scaffold/templates/layout.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/layout.html.erb index 6460e5b599..51c4ad0e2e 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/layout.html.erb +++ b/railties/lib/rails/generators/erb/scaffold/templates/layout.html.erb @@ -7,7 +7,7 @@ -

<%%= flash[:notice] %>

+

<%%= notice %>

<%%= yield %> diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb index 3cc8bbf8e7..874e96a2b4 100644 --- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb +++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb @@ -46,8 +46,7 @@ class <%= controller_class_name %>Controller < ApplicationController respond_to do |format| if @<%= orm_instance.save %> - flash[:notice] = '<%= class_name %> was successfully created.' - format.html { redirect_to(@<%= file_name %>) } + format.html { redirect_to(@<%= file_name %>, :notice => '<%= class_name %> was successfully created.') } format.xml { render :xml => @<%= file_name %>, :status => :created, :location => @<%= file_name %> } else format.html { render :action => "new" } @@ -63,8 +62,7 @@ class <%= controller_class_name %>Controller < ApplicationController respond_to do |format| if @<%= orm_instance.update_attributes("params[:#{file_name}]") %> - flash[:notice] = '<%= class_name %> was successfully updated.' - format.html { redirect_to(@<%= file_name %>) } + format.html { redirect_to(@<%= file_name %>, :notice => '<%= class_name %> was successfully updated.') } format.xml { head :ok } else format.html { render :action => "edit" } -- cgit v1.2.3 From 44fb54fecdab684425bbc3bb15aac9d5c6e34fc8 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 17 Dec 2009 17:53:16 -0800 Subject: Models with no attributes should just have empty hash fixtures [Sam] (Closes #3563) --- .../generators/test_unit/model/templates/fixtures.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml index c21035113e..a30132bc99 100644 --- a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml +++ b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml @@ -11,9 +11,13 @@ two: <%= attribute.name %>: <%= attribute.default %> <% end -%> <% else -%> -# one: -# column: value +# This model initially had no columns defined. If you add columns to the +# model remove the '{}' from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below # -# two: -# column: value -<% end -%> +one: {} +# column: value +# +two: {} +# column: value +<% end -%> \ No newline at end of file -- cgit v1.2.3 From 3b44f35e242cc372749f43f0f24dea0138e6ab8d Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 17 Dec 2009 21:41:07 -0600 Subject: Don't need response prepare! --- actionpack/test/dispatch/response_test.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index 256ed06a45..a0250088a5 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -7,7 +7,6 @@ class ResponseTest < ActiveSupport::TestCase test "simple output" do @response.body = "Hello, World!" - @response.prepare! status, headers, body = @response.to_a assert_equal 200, status @@ -25,7 +24,6 @@ class ResponseTest < ActiveSupport::TestCase test "utf8 output" do @response.body = [1090, 1077, 1089, 1090].pack("U*") - @response.prepare! status, headers, body = @response.to_a assert_equal 200, status @@ -41,7 +39,6 @@ class ResponseTest < ActiveSupport::TestCase @response.body = Proc.new do |response, output| 5.times { |n| output.write(n) } end - @response.prepare! status, headers, body = @response.to_a assert_equal 200, status @@ -59,14 +56,12 @@ class ResponseTest < ActiveSupport::TestCase test "content type" do [204, 304].each do |c| @response.status = c.to_s - @response.prepare! status, headers, body = @response.to_a assert !headers.has_key?("Content-Type"), "#{c} should not have Content-Type header" end [200, 302, 404, 500].each do |c| @response.status = c.to_s - @response.prepare! status, headers, body = @response.to_a assert headers.has_key?("Content-Type"), "#{c} did not have Content-Type header" end @@ -74,7 +69,6 @@ class ResponseTest < ActiveSupport::TestCase test "does not include Status header" do @response.status = "200 OK" - @response.prepare! status, headers, body = @response.to_a assert !headers.has_key?('Status') end @@ -114,13 +108,11 @@ class ResponseTest < ActiveSupport::TestCase test "cookies" do @response.set_cookie("user_name", :value => "david", :path => "/") - @response.prepare! status, headers, body = @response.to_a assert_equal "user_name=david; path=/", headers["Set-Cookie"] assert_equal({"user_name" => "david"}, @response.cookies) @response.set_cookie("login", :value => "foo&bar", :path => "/", :expires => Time.utc(2005, 10, 10,5)) - @response.prepare! status, headers, body = @response.to_a assert_equal "user_name=david; path=/\nlogin=foo%26bar; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT", headers["Set-Cookie"] assert_equal({"login" => "foo&bar", "user_name" => "david"}, @response.cookies) -- cgit v1.2.3 From 2419fae092ec207185f9ed69c2aa1ba1cd53fffe Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 17 Dec 2009 22:10:06 -0600 Subject: Pending tests for AD Response --- actionpack/lib/action_dispatch/http/response.rb | 25 +++--- actionpack/test/dispatch/response_test.rb | 113 ++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 13 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 378fd5e61d..6e63fc0067 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -145,18 +145,6 @@ module ActionDispatch # :nodoc: cattr_accessor(:default_charset) { "utf-8" } - def assign_default_content_type_and_charset! - return if headers[CONTENT_TYPE].present? - - @content_type ||= Mime::HTML - @charset ||= self.class.default_charset - - type = @content_type.to_s.dup - type << "; charset=#{@charset}" unless @sending_file - - headers[CONTENT_TYPE] = type - end - def to_a assign_default_content_type_and_charset! handle_conditional_get! @@ -259,6 +247,18 @@ module ActionDispatch # :nodoc: !@blank && @body.respond_to?(:all?) && @body.all? { |part| part.is_a?(String) } end + def assign_default_content_type_and_charset! + return if headers[CONTENT_TYPE].present? + + @content_type ||= Mime::HTML + @charset ||= self.class.default_charset + + type = @content_type.to_s.dup + type << "; charset=#{@charset}" unless @sending_file + + headers[CONTENT_TYPE] = type + end + DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate" def set_conditional_cache_control! @@ -280,7 +280,6 @@ module ActionDispatch # :nodoc: headers["Cache-Control"] = options.join(", ") end - end end end diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index a0250088a5..59ad2e48bd 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -117,4 +117,117 @@ class ResponseTest < ActiveSupport::TestCase assert_equal "user_name=david; path=/\nlogin=foo%26bar; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT", headers["Set-Cookie"] assert_equal({"login" => "foo&bar", "user_name" => "david"}, @response.cookies) end + + test "read cache control" do + resp = ActionDispatch::Response.new.tap { |resp| + resp.cache_control[:public] = true + resp.etag = '123' + resp.body = 'Hello' + } + resp.to_a + + assert_equal('"202cb962ac59075b964b07152d234b70"', resp.etag) + assert_equal({:public => true}, resp.cache_control) + + assert_equal('public', resp.headers['Cache-Control']) + assert_equal('"202cb962ac59075b964b07152d234b70"', resp.headers['ETag']) + end + + test "read charset and content type" do + resp = ActionDispatch::Response.new.tap { |resp| + resp.charset = 'utf-16' + resp.content_type = Mime::XML + resp.body = 'Hello' + } + resp.to_a + + assert_equal('utf-16', resp.charset) + assert_equal(Mime::XML, resp.content_type) + + assert_equal('application/xml; charset=utf-16', resp.headers['Content-Type']) + end +end + +class ResponseIntegrationTest < ActionDispatch::IntegrationTest + def app + @app + end + + test "response cache control from railsish app" do + @app = lambda { |env| + ActionDispatch::Response.new.tap { |resp| + resp.cache_control[:public] = true + resp.etag = '123' + resp.body = 'Hello' + }.to_a + } + + get '/' + assert_response :success + + assert_equal('public', @response.headers['Cache-Control']) + assert_equal('"202cb962ac59075b964b07152d234b70"', @response.headers['ETag']) + + pending do + assert_equal('"202cb962ac59075b964b07152d234b70"', @response.etag) + assert_equal({:public => true}, @response.cache_control) + end + end + + test "response cache control from rackish app" do + @app = lambda { |env| + [200, + {'ETag' => '"202cb962ac59075b964b07152d234b70"', + 'Cache-Control' => 'public'}, 'Hello'] + } + + get '/' + assert_response :success + + assert_equal('public', @response.headers['Cache-Control']) + assert_equal('"202cb962ac59075b964b07152d234b70"', @response.headers['ETag']) + + pending do + assert_equal('"202cb962ac59075b964b07152d234b70"', @response.etag) + assert_equal({:public => true}, @response.cache_control) + end + end + + test "response charset and content type from railsish app" do + @app = lambda { |env| + ActionDispatch::Response.new.tap { |resp| + resp.charset = 'utf-16' + resp.content_type = Mime::XML + resp.body = 'Hello' + }.to_a + } + + get '/' + assert_response :success + + pending do + assert_equal('utf-16', @response.charset) + assert_equal(Mime::XML, @response.content_type) + end + + assert_equal('application/xml; charset=utf-16', @response.headers['Content-Type']) + end + + test "response charset and content type from rackish app" do + @app = lambda { |env| + [200, + {'Content-Type' => 'application/xml; charset=utf-16'}, + 'Hello'] + } + + get '/' + assert_response :success + + pending do + assert_equal('utf-16', @response.charset) + assert_equal(Mime::XML, @response.content_type) + end + + assert_equal('application/xml; charset=utf-16', @response.headers['Content-Type']) + end end -- cgit v1.2.3 From 8b4735fbd9d5f6bd0c5d04688cc5edcd9b00ccf0 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sun, 20 Dec 2009 14:06:40 -0800 Subject: Add active_support/ruby/shim to the default requirements for AP components --- actionpack/lib/abstract_controller.rb | 2 +- actionpack/lib/action_controller.rb | 2 +- actionpack/lib/action_dispatch.rb | 2 +- actionpack/lib/action_view.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb index 109a3a3385..d13a56a859 100644 --- a/actionpack/lib/abstract_controller.rb +++ b/actionpack/lib/abstract_controller.rb @@ -1,7 +1,7 @@ activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) -require 'active_support' +require 'active_support/ruby/shim' require 'active_support/core_ext/module/attr_internal' require 'active_support/core_ext/module/delegation' diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 37ff10e852..144850da62 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -1,6 +1,6 @@ activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) -require 'active_support' +require 'active_support/ruby/shim' module ActionController extend ActiveSupport::Autoload diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index feed6a8e25..a7be49a273 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -23,7 +23,7 @@ activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) -require 'active_support' +require 'active_support/ruby/shim' require 'rack' diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index c3e42ac0d5..aabe8c4314 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -23,7 +23,7 @@ activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) -require 'active_support' +require 'active_support/ruby/shim' require 'active_support/core_ext/class/attribute_accessors' require 'action_pack' -- cgit v1.2.3 From e48b4c2dd01877ace901e1c186d04605b53b40d0 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sun, 20 Dec 2009 14:07:19 -0800 Subject: :to => redirect() can take a String using 1.9-style interpolation or proc that takes the path parameters as a Hash --- actionpack/lib/action_dispatch/routing/mapper.rb | 14 ++++++++++---- actionpack/test/dispatch/routing_test.rb | 21 +++++++++++++++++++++ activesupport/lib/active_support/ruby/shim.rb | 1 + 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index d480af876d..57e992d7dc 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -132,13 +132,19 @@ module ActionDispatch map_method(:delete, *args, &block) end - def redirect(path, options = {}) + def redirect(*args, &block) + options = args.last.is_a?(Hash) ? args.pop : {} + + path = args.shift || block + path_proc = path.is_a?(Proc) ? path : proc {|params| path % params } status = options[:status] || 301 - lambda { |env| + + lambda do |env| req = Rack::Request.new(env) - url = req.scheme + '://' + req.host + path + params = path_proc.call(env["action_dispatch.request.path_parameters"]) + url = req.scheme + '://' + req.host + params [status, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently']] - } + end end private diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 425796b460..7145a0c530 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -24,6 +24,9 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest match 'account/login', :to => redirect("/login") + match 'account/modulo/:name', :to => redirect("/%{name}s") + match 'account/proc/:name', :to => redirect {|params| "/#{params[:name].pluralize}" } + match 'openid/login', :via => [:get, :post], :to => "openid#login" controller(:global) do @@ -145,6 +148,24 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_redirect_modulo + with_test_routes do + get '/account/modulo/name' + assert_equal 301, @response.status + assert_equal 'http://www.example.com/names', @response.headers['Location'] + assert_equal 'Moved Permanently', @response.body + end + end + + def test_redirect_proc + with_test_routes do + get '/account/proc/person' + assert_equal 301, @response.status + assert_equal 'http://www.example.com/people', @response.headers['Location'] + assert_equal 'Moved Permanently', @response.body + end + end + def test_openid with_test_routes do get '/openid/login' diff --git a/activesupport/lib/active_support/ruby/shim.rb b/activesupport/lib/active_support/ruby/shim.rb index f811239077..1e49ccdade 100644 --- a/activesupport/lib/active_support/ruby/shim.rb +++ b/activesupport/lib/active_support/ruby/shim.rb @@ -14,5 +14,6 @@ require 'active_support/core_ext/date_time/conversions' require 'active_support/core_ext/enumerable' require 'active_support/core_ext/process/daemon' require 'active_support/core_ext/string/conversions' +require 'active_support/core_ext/string/interpolation' require 'active_support/core_ext/rexml' require 'active_support/core_ext/time/conversions' -- cgit v1.2.3 From c06aff0a7e42868e9788d6cefe5ce49e93cbf45b Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 20 Dec 2009 14:33:13 -0800 Subject: Added cookies.permanent, cookies.signed, and cookies.permanent.signed accessor for common cookie actions [DHH] --- actionpack/lib/action_controller/metal/cookies.rb | 168 ++++++++++++++++----- actionpack/test/controller/cookie_test.rb | 35 +++++ railties/CHANGELOG | 18 +++ .../initializers/cookie_verification_secret.rb.tt | 7 + 4 files changed, 190 insertions(+), 38 deletions(-) create mode 100644 railties/lib/rails/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt diff --git a/actionpack/lib/action_controller/metal/cookies.rb b/actionpack/lib/action_controller/metal/cookies.rb index 6855ca1478..8db665e929 100644 --- a/actionpack/lib/action_controller/metal/cookies.rb +++ b/actionpack/lib/action_controller/metal/cookies.rb @@ -50,56 +50,148 @@ module ActionController #:nodoc: included do helper_method :cookies + cattr_accessor :cookie_verifier_secret end - protected - # Returns the cookie container, which operates as described above. - def cookies - @cookies ||= CookieJar.build(request, response) + protected + # Returns the cookie container, which operates as described above. + def cookies + @cookies ||= CookieJar.build(request, response) + end end - end - class CookieJar < Hash #:nodoc: - def self.build(request, response) - new.tap do |hash| - hash.update(request.cookies) - hash.response = response + class CookieJar < Hash #:nodoc: + def self.build(request, response) + new.tap do |hash| + hash.update(request.cookies) + hash.response = response + end end - end - attr_accessor :response + attr_accessor :response - # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists. - def [](name) - super(name.to_s) - end + # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists. + def [](name) + super(name.to_s) + end + + # Sets the cookie named +name+. The second argument may be the very cookie + # value, or a hash of options as documented above. + def []=(key, options) + if options.is_a?(Hash) + options.symbolize_keys! + value = options[:value] + else + value = options + options = { :value => value } + end - # Sets the cookie named +name+. The second argument may be the very cookie - # value, or a hash of options as documented above. - def []=(key, options) - if options.is_a?(Hash) + super(key.to_s, value) + + options[:path] ||= "/" + response.set_cookie(key, options) + end + + # Removes the cookie on the client machine by setting the value to an empty string + # and setting its expiration date into the past. Like []=, you can pass in + # an options hash to delete cookies with extra data such as a :path. + def delete(key, options = {}) options.symbolize_keys! - value = options[:value] - else - value = options - options = { :value => value } + options[:path] ||= "/" + value = super(key.to_s) + response.delete_cookie(key, options) + value end - super(key.to_s, value) - - options[:path] ||= "/" - response.set_cookie(key, options) + # Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example: + # + # cookies.permanent[:prefers_open_id] = true + # # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT + # + # This jar is only meant for writing. You'll read permanent cookies through the regular accessor. + # + # This jar allows chaining with the signed jar as well, so you can set permanent, signed cookies. Examples: + # + # cookies.permanent.signed[:remember_me] = current_user.id + # # => Set-Cookie: discount=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT + def permanent + @permanent ||= PermanentCookieJar.new(self) + end + + # Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from + # the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed + # cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will + # be raised. + # + # This jar requires that you set a suitable secret for the verification on ActionController::Base.cookie_verifier_secret. + # + # Example: + # + # cookies.signed[:discount] = 45 + # # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/ + # + # cookies.signed[:discount] # => 45 + def signed + @signed ||= SignedCookieJar.new(self) + end end - - # Removes the cookie on the client machine by setting the value to an empty string - # and setting its expiration date into the past. Like []=, you can pass in - # an options hash to delete cookies with extra data such as a :path. - def delete(key, options = {}) - options.symbolize_keys! - options[:path] ||= "/" - value = super(key.to_s) - response.delete_cookie(key, options) - value + + class PermanentCookieJar < CookieJar #:nodoc: + def initialize(parent_jar) + @parent_jar = parent_jar + end + + def []=(key, options) + if options.is_a?(Hash) + options.symbolize_keys! + else + options = { :value => options } + end + + options[:expires] = 20.years.from_now + @parent_jar[key] = options + end + + def signed + @signed ||= SignedCookieJar.new(self) + end + + def controller + @parent_jar.controller + end + + def method_missing(method, *arguments, &block) + @parent_jar.send(method, *arguments, &block) + end end + + class SignedCookieJar < CookieJar #:nodoc: + def initialize(parent_jar) + unless ActionController::Base.cookie_verifier_secret + raise "You must set ActionController::Base.cookie_verifier_secret to use signed cookies" + end + + @parent_jar = parent_jar + @verifier = ActiveSupport::MessageVerifier.new(ActionController::Base.cookie_verifier_secret) + end + + def [](name) + @verifier.verify(@parent_jar[name]) + end + + def []=(key, options) + if options.is_a?(Hash) + options.symbolize_keys! + options[:value] = @verifier.generate(options[:value]) + else + options = { :value => @verifier.generate(options) } + end + + @parent_jar[key] = options + end + + def method_missing(method, *arguments, &block) + @parent_jar.send(method, *arguments, &block) + end end end diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/controller/cookie_test.rb index 53d4364576..84d5ce6ad4 100644 --- a/actionpack/test/controller/cookie_test.rb +++ b/actionpack/test/controller/cookie_test.rb @@ -1,5 +1,7 @@ require 'abstract_unit' +ActionController::Base.cookie_verifier_secret = "thisISverySECRET123" + class CookieTest < ActionController::TestCase class TestController < ActionController::Base def authenticate @@ -47,6 +49,21 @@ class CookieTest < ActionController::TestCase cookies["user_name"] = { :value => "david", :httponly => true } head :ok end + + def set_permanent_cookie + cookies.permanent[:user_name] = "Jamie" + head :ok + end + + def set_signed_cookie + cookies.signed[:user_id] = 45 + head :ok + end + + def set_permanent_signed_cookie + cookies.permanent.signed[:remember_me] = 100 + head :ok + end end tests TestController @@ -134,6 +151,24 @@ class CookieTest < ActionController::TestCase response = get :authenticate assert response.headers["Set-Cookie"] =~ /user_name=david/ end + + def test_permanent_cookie + get :set_permanent_cookie + assert_match /Jamie/, @response.headers["Set-Cookie"] + assert_match %r(#{20.years.from_now.year}), @response.headers["Set-Cookie"] + end + + def test_signed_cookie + get :set_signed_cookie + assert_equal 45, @controller.send(:cookies).signed[:user_id] + end + + def test_permanent_signed_cookie + get :set_permanent_signed_cookie + assert_match %r(#{20.years.from_now.year}), @response.headers["Set-Cookie"] + assert_equal 100, @controller.send(:cookies).signed[:remember_me] + end + private def assert_cookie_header(expected) diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 66e0d5e9c5..9ef2922133 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,5 +1,23 @@ *Edge* +* Added cookies.permanent, cookies.signed, and cookies.permanent.signed accessor for common cookie actions [DHH]. Examples: + + cookies.permanent[:prefers_open_id] = true + # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT + + cookies.signed[:discount] = 45 + # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/ + + cookies.signed[:discount] + # => 45 (if the cookie was changed, you'll get a InvalidSignature exception) + + cookies.permanent.signed[:remember_me] = current_user.id + # => Set-Cookie: discount=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT + + ...to use the signed cookies, you need to set a secret to ActionController::Base.cookie_verifier_secret (automatically done in config/initializers/cookie_verification_secret.rb for new Rails applications). + +* Added config/initializers/cookie_verification_secret.rb with an auto-generated secret for using ActionController::Base#cookies.signed [DHH] + * Fixed that the debugger wouldn't go into IRB mode because of left-over ARGVs [DHH] * I18n support for plugins. #2325 [Antonio Tapiador, Sven Fuchs] diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt new file mode 100644 index 0000000000..9808ea6a2d --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# Your secret key for verifying the integrity of signed cookies. +# If you change this key, all old signed cookies will become invalid! +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +ActionController::Base.cookie_verification_secret = '<%= app_secret %>'; -- cgit v1.2.3 From 33a6bd390affdeb0b403f513be92a5b1547f6e4e Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sun, 20 Dec 2009 17:03:38 -0800 Subject: Fixes Dependency bug in ActiveSupport --- activesupport/lib/active_support/core_ext/date/calculations.rb | 1 + activesupport/lib/active_support/core_ext/date/conversions.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index 3dd61334d0..2b76b93153 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -1,3 +1,4 @@ +require 'date' require 'active_support/duration' require 'active_support/core_ext/time/zones' diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb index f6c870035b..90ab1eb281 100644 --- a/activesupport/lib/active_support/core_ext/date/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date/conversions.rb @@ -1,3 +1,4 @@ +require 'date' require 'active_support/inflector' class Date -- cgit v1.2.3 From fc9b3e4a45a81b7526f8154049c825e3755903ad Mon Sep 17 00:00:00 2001 From: Taryn East Date: Sun, 20 Dec 2009 18:42:16 -0600 Subject: define_schema for Active Resource Signed-off-by: Joshua Peek --- activeresource/lib/active_resource/base.rb | 144 +++++++- .../lib/active_resource/schema_definition.rb | 58 +++ activeresource/test/cases/base/schema_test.rb | 409 +++++++++++++++++++++ 3 files changed, 609 insertions(+), 2 deletions(-) create mode 100644 activeresource/lib/active_resource/schema_definition.rb create mode 100644 activeresource/test/cases/base/schema_test.rb diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index 18105e8887..60bd573911 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -13,6 +13,7 @@ require 'set' require 'uri' require 'active_resource/exceptions' +require 'active_resource/schema_definition' module ActiveResource # ActiveResource::Base is the main class for mapping RESTful resources as models in a Rails application. @@ -241,6 +242,127 @@ module ActiveResource cattr_accessor :logger class << self + # This will shortly disappear to be replaced by the migration-style + # usage of this for defining a schema. At that point, all the doc + # currenlty on schema= will move back here... + def schema # :nodoc: + @schema ||= nil + end + # Creates a schema for this resource - setting the attributes that are + # known prior to fetching an instance from the remote system. + # + # The schema helps define the set of known_attributes of the + # current resource. + # + # There is no need to specify a schema for your Active Resource. If + # you do not, the known_attributes will be guessed from the + # instance attributes returned when an instance is fetched from the + # remote system. + # + # example: + # class Person < ActiveResource::Base + # define_schema do |s| + # # define each attribute separately + # s.attribute 'name', :string + # + # # or use the convenience methods and pass >=1 attribute names + # s.string 'eye_colour', 'hair_colour' + # s.integer 'age' + # s.float 'height', 'weight' + # + # # unsupported types should be left as strings + # # overload the accessor methods if you need to convert them + # s.attribute 'created_at', 'string' + # end + # end + # + # p = Person.new + # p.respond_to? :name # => true + # p.respond_to? :age # => true + # p.name # => nil + # p.age # => nil + # + # j = Person.find_by_name('John') # John343 + # j.respond_to? :name # => true + # j.respond_to? :age # => true + # j.name # => 'John' + # j.age # => '34' # note this is a string! + # j.num_children # => '3' # note this is a string! + # + # p.num_children # => NoMethodError + # + # Attribute-types must be one of: + # string, integer, float + # + # Note: at present the attribute-type doesn't do anything, but stay + # tuned... + # Shortly it will also *cast* the value of the returned attribute. + # ie: + # j.age # => 34 # cast to an integer + # j.weight # => '65' # still a string! + # + def define_schema + schema_definition = SchemaDefinition.new + yield schema_definition if block_given? + + # skip out if we didn't define anything + return unless schema_definition.attrs.present? + + @schema ||= {}.with_indifferent_access + @known_attributes ||= [] + + schema_definition.attrs.each do |k,v| + @schema[k] = v + @known_attributes << k + end + + schema + end + + + # Alternative, direct way to specify a schema for this + # Resource. define_schema is more flexible, but this is quick + # for a very simple schema. + # + # Pass the schema as a hash with the keys being the attribute-names + # and the value being one of the accepted attribute types (as defined + # in define_schema) + # + # example: + # + # class Person < ActiveResource::Base + # schema = {'name' => :string, 'age' => :integer } + # end + # + # The keys/values can be strings or symbols. They will be converted to + # strings. + # + def schema=(the_schema) + unless the_schema.present? + # purposefully nulling out the schema + @schema = nil + @known_attributes = [] + return + end + + raise ArgumentError, "Expected a hash" unless the_schema.kind_of? Hash + + define_schema do |s| + the_schema.each {|k,v| s.attribute(k,v) } + end + end + + # Returns the list of known attributes for this resource, gathered + # from the provided schema + # Attributes that are known will cause your resource to return 'true' + # when respond_to? is called on them. A known attribute will + # return nil if not set (rather than MethodNotFound); thus + # known attributes can be used with validates_presence_of + # without a getter-method. + def known_attributes + @known_attributes ||= [] + end + # Gets the URI of the REST resources to map for this class. The site variable is required for # Active Resource's mapping to work. def site @@ -776,6 +898,21 @@ module ActiveResource attr_accessor :attributes #:nodoc: attr_accessor :prefix_options #:nodoc: + # If no schema has been defined for the class (see + # ActiveResource::schema=), the default automatic schema is + # generated from the current instance's attributes + def schema + self.class.schema || self.attributes + end + + # This is a list of known attributes for this resource. Either + # gathered fromthe provided schema, or from the attributes + # set on this instance after it has been fetched from the remote system. + def known_attributes + self.class.known_attributes + self.attributes.keys.map(&:to_s) + end + + # Constructor method for \new resources; the optional +attributes+ parameter takes a \hash # of attributes for the \new resource. # @@ -1157,7 +1294,7 @@ module ActiveResource method_name = method.to_s if attributes.nil? super - elsif attributes.has_key?(method_name) + elsif known_attributes.include?(method_name) true elsif method_name =~ /(?:=|\?)$/ && attributes.include?($`) true @@ -1262,7 +1399,10 @@ module ActiveResource attributes[$`] end else - attributes.include?(method_name) ? attributes[method_name] : super + return attributes[method_name] if attributes.include?(method_name) + # not set right now but we know about it + return nil if known_attributes.include?(method_name) + super end end end diff --git a/activeresource/lib/active_resource/schema_definition.rb b/activeresource/lib/active_resource/schema_definition.rb new file mode 100644 index 0000000000..abcbf3ba4e --- /dev/null +++ b/activeresource/lib/active_resource/schema_definition.rb @@ -0,0 +1,58 @@ +require 'active_resource/exceptions' + +module ActiveResource # :nodoc: + class SchemaDefinition # :nodoc: + + # attributes can be known to be one of these types. They are easy to + # cast to/from. + KNOWN_ATTRIBUTE_TYPES = %w( string integer float ) + + # An array of attribute definitions, representing the attributes that + # have been defined. + attr_accessor :attrs + + # The internals of an Active Resource SchemaDefinition are very simple - + # unlike an Active Record TableDefinition (on which it is based). + # It provides a set of convenience methods for people to define their + # schema using the syntax: + # define_schema do |s| + # s.string :foo + # s.integer :bar + # end + # + # The schema stores the name and type of each attribute. That is then + # read out by the define_schema method to populate the actual + # Resource's schema + def initialize + @attrs = {} + end + + def attribute(name, type, options = {}) + raise ArgumentError, "Unknown Attribute type: #{type.inspect} for key: #{name.inspect}" unless type.nil? || SchemaDefinition::KNOWN_ATTRIBUTE_TYPES.include?(type.to_s) + + the_type = type.to_s + # TODO: add defaults + #the_attr = [type.to_s] + #the_attr << options[:default] if options.has_key? :default + @attrs[name.to_s] = the_type + self + end + + # The following are the attribute types supported by Active Resource + # migrations. + # TODO: We should eventually support all of these: + # %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |attr_type| + KNOWN_ATTRIBUTE_TYPES.each do |attr_type| + class_eval <<-EOV + def #{attr_type.to_s}(*args) # def string(*args) + options = args.extract_options! # options = args.extract_options! + attr_names = args # attr_names = args + # + attr_names.each { |name| attribute(name, '#{attr_type}', options) } # attr_names.each { |name| attribute(name, 'string', options) } + end # end + EOV + + end + + end +end diff --git a/activeresource/test/cases/base/schema_test.rb b/activeresource/test/cases/base/schema_test.rb new file mode 100644 index 0000000000..398e7cf539 --- /dev/null +++ b/activeresource/test/cases/base/schema_test.rb @@ -0,0 +1,409 @@ +require 'abstract_unit' +require "fixtures/person" +require "fixtures/street_address" + +######################################################################## +# Testing the schema of your Active Resource models +######################################################################## +class SchemaTest < ActiveModel::TestCase + def setup + @matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person') + @david = { :id => 2, :name => 'David' }.to_xml(:root => 'person') + @greg = { :id => 3, :name => 'Greg' }.to_xml(:root => 'person') + @addy = { :id => 1, :street => '12345 Street', :country => 'Australia' }.to_xml(:root => 'address') + @default_request_headers = { 'Content-Type' => 'application/xml' } + @rick = { :name => "Rick", :age => 25 }.to_xml(:root => "person") + @people = [{ :id => 1, :name => 'Matz' }, { :id => 2, :name => 'David' }].to_xml(:root => 'people') + @people_david = [{ :id => 2, :name => 'David' }].to_xml(:root => 'people') + @addresses = [{ :id => 1, :street => '12345 Street', :country => 'Australia' }].to_xml(:root => 'addresses') + + ActiveResource::HttpMock.respond_to do |mock| + mock.get "/people/1.xml", {}, @matz + mock.get "/people/2.xml", {}, @david + mock.get "/people/Greg.xml", {}, @greg + mock.get "/people/4.xml", {'key' => 'value'}, nil, 404 + mock.get "/people/5.xml", {}, @rick + mock.put "/people/1.xml", {}, nil, 204 + mock.delete "/people/1.xml", {}, nil, 200 + mock.delete "/people/2.xml", {}, nil, 400 + mock.get "/people/99.xml", {}, nil, 404 + mock.post "/people.xml", {}, @rick, 201, 'Location' => '/people/5.xml' + mock.get "/people.xml", {}, @people + mock.get "/people/1/addresses.xml", {}, @addresses + mock.get "/people/1/addresses/1.xml", {}, @addy + mock.get "/people/1/addresses/2.xml", {}, nil, 404 + mock.get "/people/2/addresses/1.xml", {}, nil, 404 + mock.get "/people/Greg/addresses/1.xml", {}, @addy + mock.put "/people/1/addresses/1.xml", {}, nil, 204 + mock.delete "/people/1/addresses/1.xml", {}, nil, 200 + mock.post "/people/1/addresses.xml", {}, nil, 201, 'Location' => '/people/1/addresses/5' + mock.get "/people//addresses.xml", {}, nil, 404 + mock.get "/people//addresses/1.xml", {}, nil, 404 + mock.put "/people//addressaddresseses/1.xml", {}, nil, 404 + mock.delete "/people//addresses/1.xml", {}, nil, 404 + mock.post "/people//addresses.xml", {}, nil, 404 + mock.head "/people/1.xml", {}, nil, 200 + mock.head "/people/Greg.xml", {}, nil, 200 + mock.head "/people/99.xml", {}, nil, 404 + mock.head "/people/1/addresses/1.xml", {}, nil, 200 + mock.head "/people/1/addresses/2.xml", {}, nil, 404 + mock.head "/people/2/addresses/1.xml", {}, nil, 404 + mock.head "/people/Greg/addresses/1.xml", {}, nil, 200 + end + + Person.user = nil + Person.password = nil + end + def teardown + Person.schema = nil # hack to stop test bleedthrough... + end + + + ##################################################### + # Passing in a schema directly and returning it + #### + + test "schema on a new model should be empty" do + assert Person.schema.blank?, "should have a blank class schema" + assert Person.new.schema.blank?, "should have a blank instance schema" + end + + test "schema should only accept a hash" do + ["blahblah", ['one','two'], [:age, :name], Person.new].each do |bad_schema| + assert_raises(ArgumentError,"should only accept a hash (or nil), but accepted: #{bad_schema.inspect}") do + Person.schema = bad_schema + end + end + end + + test "schema should accept a simple hash" do + new_schema = {'age' => 'integer', 'name' => 'string'} + + assert_nothing_raised { Person.schema = new_schema } + assert_equal new_schema, Person.schema + end + + test "schema should accept a hash with simple values" do + new_schema = {'age' => 'integer', 'name' => 'string', 'height' => 'float', 'mydatetime' => 'string'} + + assert_nothing_raised { Person.schema = new_schema } + assert_equal new_schema, Person.schema + end + + test "schema should accept all known attribute types as values" do + # I'd prefer to use here... + ActiveResource::SchemaDefinition::KNOWN_ATTRIBUTE_TYPES.each do |the_type| + assert_nothing_raised("should have accepted #{the_type.inspect}"){ Person.schema = {'my_key' => the_type }} + end + end + + test "schema should not accept unknown values" do + bad_values = [ :oogle, :blob, 'thing'] + + bad_values.each do |bad_value| + assert_raises(ArgumentError,"should only accept a known attribute type, but accepted: #{bad_value.inspect}") do + Person.schema = {'key' => bad_value} + end + end + end + + test "schema should accept nil and remove the schema" do + new_schema = {'age' => 'integer', 'name' => 'string'} + assert_nothing_raised { Person.schema = new_schema } + assert_equal new_schema, Person.schema # sanity check + + + assert_nothing_raised { Person.schema = nil } + assert_nil Person.schema, "should have nulled out the schema, but still had: #{Person.schema.inspect}" + end + + + test "schema should be with indifferent access" do + new_schema = {:age => :integer, 'name' => 'string'} + new_schema_syms = new_schema.keys + + assert_nothing_raised { Person.schema = new_schema } + new_schema_syms.each do |col| + assert Person.new.respond_to?(col.to_s), "should respond to the schema's string key, but failed on: #{col.to_s}" + assert Person.new.respond_to?(col.to_sym), "should respond to the schema's symbol key, but failed on: #{col.to_sym}" + end + end + + + test "schema on a fetched resource should return all the attributes of that model instance" do + p = Person.find(1) + s = p.schema + + assert s.present?, "should have found a non-empty schema!" + + p.attributes.each do |the_attr, val| + assert s.has_key?(the_attr), "should have found attr: #{the_attr} in schema, but only had: #{s.inspect}" + end + end + + test "with two instances, default schema should match the attributes of the individual instances - even if they differ" do + matz = Person.find(1) + rick = Person.find(5) + + m_attrs = matz.attributes.keys.sort + r_attrs = rick.attributes.keys.sort + + assert_not_equal m_attrs, r_attrs, "should have different attributes on each model" + + assert_not_equal matz.schema, rick.schema, "should have had different schemas too" + end + + test "defining a schema should return it when asked" do + assert Person.schema.blank?, "should have a blank class schema" + new_schema = {'name' => 'string', 'age' => 'integer', 'height' => 'float', 'weight' => 'float'} + assert_nothing_raised { + Person.schema = new_schema + assert_equal new_schema, Person.schema, "should have saved the schema on the class" + assert_equal new_schema, Person.new.schema, "should have mde the schema available to every instance" + } + end + + test "defining a schema, then fetching a model should still match the defined schema" do + # sanity checks + assert Person.schema.blank?, "should have a blank class schema" + new_schema = {'name' => 'string', 'age' => 'integer', 'height' => 'float', 'weight' => 'float'} + + matz = Person.find(1) + assert !matz.schema.blank?, "should have some sort of schema on an instance variable" + assert_not_equal new_schema, matz.schema, "should not have the class-level schema until it's been added to the class!" + + assert_nothing_raised { + Person.schema = new_schema + assert_equal new_schema, matz.schema, "class-level schema should override instance-level schema" + } + end + + + ##################################################### + # Using the define_schema syntax + #### + + test "should be able to use define_schema" do + assert Person.respond_to?(:define_schema), "should at least respond to the define_schema method" + + assert_nothing_raised("Should allow the define_schema to take a block") do + Person.define_schema do |s| + assert s.kind_of?(ActiveResource::SchemaDefinition), "the 's' should be a schema definition or we're way off track..." + end + end + end + + test "schema definition should store and return attribute set" do + assert_nothing_raised do + Person.define_schema do |s| + assert s.respond_to?(:attrs), "should return attributes in theory" + s.attribute :foo, :string + assert_equal({'foo' => 'string' }, s.attrs, "should return attributes in practice") + end + end + end + + test "should be able to add attributes through define_schema" do + assert_nothing_raised do + Person.define_schema do |s| + assert s.attribute('foo', 'string'), "should take a simple attribute" + assert s.attrs.has_key?('foo'), "should have saved the attribute name" + assert_equal 'string', s.attrs['foo'], "should have saved the attribute type" + end + end + end + + test "should convert symbol attributes to strings" do + assert_nothing_raised do + Person.define_schema do |s| + assert s.attribute(:foo, :integer), "should take a simple attribute as symbols" + assert s.attrs.has_key?('foo'), "should have saved the attribute name as a string" + assert_equal 'integer', s.attrs['foo'], "should have saved the attribute type as a string" + end + end + end + + test "should be able to add all known attribute types" do + Person.define_schema do |s| + ActiveResource::SchemaDefinition::KNOWN_ATTRIBUTE_TYPES.each do |the_type| + assert_nothing_raised do + assert s.attribute('foo', the_type), "should take a simple attribute of type: #{the_type}" + assert s.attrs.has_key?('foo'), "should have saved the attribute name" + assert_equal the_type.to_s, s.attrs['foo'], "should have saved the attribute type of: #{the_type}" + end + end + end + end + + test "attributes should not accept unknown values" do + bad_values = [ :oogle, :blob, 'thing'] + + Person.define_schema do |s| + bad_values.each do |bad_value| + assert_raises(ArgumentError,"should only accept a known attribute type, but accepted: #{bad_value.inspect}") do + s.attribute 'key', bad_value + end + assert !s.respond_to?(bad_value), "should only respond to a known attribute type, but accepted: #{bad_value.inspect}" + assert_raises(NoMethodError,"should only have methods for known attribute types, but accepted: #{bad_value.inspect}") do + s.send bad_value, 'key' + end + end + end + end + + + test "should accept attribute types as the type's name as the method" do + Person.define_schema do |s| + ActiveResource::SchemaDefinition::KNOWN_ATTRIBUTE_TYPES.each do |the_type| + assert s.respond_to?(the_type), "should recognise the attribute-type: #{the_type} as a method" + assert_nothing_raised("should take the method #{the_type} with the attribute name") do + s.send(the_type,'foo') # eg s.string :foo + end + assert s.attrs.has_key?('foo'), "should now have saved the attribute name" + assert_equal the_type.to_s, s.attrs['foo'], "should have saved the attribute type of: #{the_type}" + end + end + end + + test "should accept multiple attribute names for an attribute method" do + names = ['foo','bar','baz'] + Person.define_schema do |s| + assert_nothing_raised("should take strings with multiple attribute names as params") do + s.string( *names) + end + names.each do |the_name| + assert s.attrs.has_key?(the_name), "should now have saved the attribute name: #{the_name}" + assert_equal 'string', s.attrs[the_name], "should have saved the attribute as a string" + end + end + end + + ##################################################### + # What a schema does for us + #### + + # respond_to? + + test "should respond positively to attributes that are only in the schema" do + new_attr_name = :my_new_schema_attribute + new_attr_name_two = :another_new_schema_attribute + assert Person.schema.blank?, "sanity check - should have a blank class schema" + + assert !Person.new.respond_do?(new_attr_name), "sanity check - should not respond to the brand-new attribute yet" + assert !Person.new.respond_do?(new_attr_name_two), "sanity check - should not respond to the brand-new attribute yet" + + assert_nothing_raised do + Person.schema = {new_attr_name.to_s => 'string'} + Person.define_schema {|s| s.string new_attr_name_two } + end + + assert Person.new.respond_to?(new_attr_name), "should respond to the attribute in a passed-in schema, but failed on: #{new_attr_name}" + assert Person.new.respond_to?(new_attr_name_two), "should respond to the attribute from the define_schema, but failed on: #{new_attr_name_two}" + end + + test "should not care about ordering of schema definitions" do + new_attr_name = :my_new_schema_attribute + new_attr_name_two = :another_new_schema_attribute + + assert Person.schema.blank?, "sanity check - should have a blank class schema" + + assert !Person.new.respond_do?(new_attr_name), "sanity check - should not respond to the brand-new attribute yet" + assert !Person.new.respond_do?(new_attr_name_two), "sanity check - should not respond to the brand-new attribute yet" + + assert_nothing_raised do + Person.define_schema {|s| s.string new_attr_name_two } + Person.schema = {new_attr_name.to_s => 'string'} + end + + assert Person.new.respond_to?(new_attr_name), "should respond to the attribute in a passed-in schema, but failed on: #{new_attr_name}" + assert Person.new.respond_to?(new_attr_name_two), "should respond to the attribute from the define_schema, but failed on: #{new_attr_name_two}" + end + + # method_missing effects + + test "should not give method_missing for attribute only in schema" do + new_attr_name = :another_new_schema_attribute + new_attr_name_two = :another_new_schema_attribute + + assert Person.schema.blank?, "sanity check - should have a blank class schema" + + assert_raises(NoMethodError, "should not have found the attribute: #{new_attr_name} as a method") do + Person.new.send(new_attr_name) + end + assert_raises(NoMethodError, "should not have found the attribute: #{new_attr_name_two} as a method") do + Person.new.send(new_attr_name_two) + end + + Person.schema = {new_attr_name.to_s => :float} + Person.define_schema {|s| s.string new_attr_name_two } + + assert_nothing_raised do + Person.new.send(new_attr_name) + Person.new.send(new_attr_name_two) + end + end + + + ######## + # Known attributes + # + # Attributes can be known to be attributes even if they aren't actually + # 'set' on a particular instance. + # This will only differ from 'attributes' if a schema has been set. + + test "new model should have no known attributes" do + assert Person.known_attributes.blank?, "should have no known attributes" + assert Person.new.known_attributes.blank?, "should have no known attributes on a new instance" + end + + test "setting schema should set known attributes on class and instance" do + new_schema = {'age' => 'integer', 'name' => 'string'} + + assert_nothing_raised { Person.schema = new_schema } + + assert_equal new_schema.keys, Person.known_attributes + assert_equal new_schema.keys, Person.new.known_attributes + end + + test "known attributes on a fetched resource should return all the attributes of the instance" do + p = Person.find(1) + attrs = p.known_attributes + + assert attrs.present?, "should have found some attributes!" + + p.attributes.each do |the_attr, val| + assert attrs.include?(the_attr), "should have found attr: #{the_attr} in known attributes, but only had: #{attrs.inspect}" + end + end + + test "with two instances, known attributes should match the attributes of the individual instances - even if they differ" do + matz = Person.find(1) + rick = Person.find(5) + + m_attrs = matz.attributes.keys.sort + r_attrs = rick.attributes.keys.sort + + assert_not_equal m_attrs, r_attrs, "should have different attributes on each model" + + assert_not_equal matz.known_attributes, rick.known_attributes, "should have had different known attributes too" + end + + test "setting schema then fetching should add schema attributes to the intance attributes" do + # an attribute in common with fetched instance and one that isn't + new_schema = {'name' => 'string', 'my_strange_attribute' => 'string'} + + assert_nothing_raised { Person.schema = new_schema } + + matz = Person.find(1) + known_attrs = matz.known_attributes + + matz.attributes.keys.each do |the_attr| + assert known_attrs.include?(the_attr), "should have found instance attr: #{the_attr} in known attributes, but only had: #{known_attrs.inspect}" + end + new_schema.keys.each do |the_attr| + assert known_attrs.include?(the_attr), "should have found schema attr: #{the_attr} in known attributes, but only had: #{known_attrs.inspect}" + end + end + + +end -- cgit v1.2.3 From 669c5eec445ff097b765c387b92ae1f174134f75 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 20 Dec 2009 18:44:13 -0600 Subject: Rename SchemaDefinition => Schema --- activeresource/lib/active_resource.rb | 3 +- activeresource/lib/active_resource/base.rb | 15 +++--- activeresource/lib/active_resource/schema.rb | 58 ++++++++++++++++++++++ .../lib/active_resource/schema_definition.rb | 58 ---------------------- activeresource/test/cases/base/schema_test.rb | 48 +++++++++--------- 5 files changed, 91 insertions(+), 91 deletions(-) create mode 100644 activeresource/lib/active_resource/schema.rb delete mode 100644 activeresource/lib/active_resource/schema_definition.rb diff --git a/activeresource/lib/active_resource.rb b/activeresource/lib/active_resource.rb index 84baf4227a..3e4a1dd4a1 100644 --- a/activeresource/lib/active_resource.rb +++ b/activeresource/lib/active_resource.rb @@ -37,7 +37,8 @@ module ActiveResource autoload :Connection autoload :CustomMethods autoload :Formats + autoload :HttpMock autoload :Observing + autoload :Schema autoload :Validations - autoload :HttpMock end diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index 60bd573911..b39f8fbd48 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -13,7 +13,6 @@ require 'set' require 'uri' require 'active_resource/exceptions' -require 'active_resource/schema_definition' module ActiveResource # ActiveResource::Base is the main class for mapping RESTful resources as models in a Rails application. @@ -270,9 +269,9 @@ module ActiveResource # s.integer 'age' # s.float 'height', 'weight' # - # # unsupported types should be left as strings + # # unsupported types should be left as strings # # overload the accessor methods if you need to convert them - # s.attribute 'created_at', 'string' + # s.attribute 'created_at', 'string' # end # end # @@ -295,14 +294,14 @@ module ActiveResource # string, integer, float # # Note: at present the attribute-type doesn't do anything, but stay - # tuned... + # tuned... # Shortly it will also *cast* the value of the returned attribute. # ie: # j.age # => 34 # cast to an integer # j.weight # => '65' # still a string! # def define_schema - schema_definition = SchemaDefinition.new + schema_definition = Schema.new yield schema_definition if block_given? # skip out if we didn't define anything @@ -317,7 +316,7 @@ module ActiveResource end schema - end + end # Alternative, direct way to specify a schema for this @@ -326,7 +325,7 @@ module ActiveResource # # Pass the schema as a hash with the keys being the attribute-names # and the value being one of the accepted attribute types (as defined - # in define_schema) + # in define_schema) # # example: # @@ -342,7 +341,7 @@ module ActiveResource # purposefully nulling out the schema @schema = nil @known_attributes = [] - return + return end raise ArgumentError, "Expected a hash" unless the_schema.kind_of? Hash diff --git a/activeresource/lib/active_resource/schema.rb b/activeresource/lib/active_resource/schema.rb new file mode 100644 index 0000000000..498b00ffef --- /dev/null +++ b/activeresource/lib/active_resource/schema.rb @@ -0,0 +1,58 @@ +require 'active_resource/exceptions' + +module ActiveResource # :nodoc: + class Schema # :nodoc: + + # attributes can be known to be one of these types. They are easy to + # cast to/from. + KNOWN_ATTRIBUTE_TYPES = %w( string integer float ) + + # An array of attribute definitions, representing the attributes that + # have been defined. + attr_accessor :attrs + + # The internals of an Active Resource Schema are very simple - + # unlike an Active Record TableDefinition (on which it is based). + # It provides a set of convenience methods for people to define their + # schema using the syntax: + # define_schema do |s| + # s.string :foo + # s.integer :bar + # end + # + # The schema stores the name and type of each attribute. That is then + # read out by the define_schema method to populate the actual + # Resource's schema + def initialize + @attrs = {} + end + + def attribute(name, type, options = {}) + raise ArgumentError, "Unknown Attribute type: #{type.inspect} for key: #{name.inspect}" unless type.nil? || Schema::KNOWN_ATTRIBUTE_TYPES.include?(type.to_s) + + the_type = type.to_s + # TODO: add defaults + #the_attr = [type.to_s] + #the_attr << options[:default] if options.has_key? :default + @attrs[name.to_s] = the_type + self + end + + # The following are the attribute types supported by Active Resource + # migrations. + # TODO: We should eventually support all of these: + # %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |attr_type| + KNOWN_ATTRIBUTE_TYPES.each do |attr_type| + class_eval <<-EOV + def #{attr_type.to_s}(*args) # def string(*args) + options = args.extract_options! # options = args.extract_options! + attr_names = args # attr_names = args + # + attr_names.each { |name| attribute(name, '#{attr_type}', options) } # attr_names.each { |name| attribute(name, 'string', options) } + end # end + EOV + + end + + end +end diff --git a/activeresource/lib/active_resource/schema_definition.rb b/activeresource/lib/active_resource/schema_definition.rb deleted file mode 100644 index abcbf3ba4e..0000000000 --- a/activeresource/lib/active_resource/schema_definition.rb +++ /dev/null @@ -1,58 +0,0 @@ -require 'active_resource/exceptions' - -module ActiveResource # :nodoc: - class SchemaDefinition # :nodoc: - - # attributes can be known to be one of these types. They are easy to - # cast to/from. - KNOWN_ATTRIBUTE_TYPES = %w( string integer float ) - - # An array of attribute definitions, representing the attributes that - # have been defined. - attr_accessor :attrs - - # The internals of an Active Resource SchemaDefinition are very simple - - # unlike an Active Record TableDefinition (on which it is based). - # It provides a set of convenience methods for people to define their - # schema using the syntax: - # define_schema do |s| - # s.string :foo - # s.integer :bar - # end - # - # The schema stores the name and type of each attribute. That is then - # read out by the define_schema method to populate the actual - # Resource's schema - def initialize - @attrs = {} - end - - def attribute(name, type, options = {}) - raise ArgumentError, "Unknown Attribute type: #{type.inspect} for key: #{name.inspect}" unless type.nil? || SchemaDefinition::KNOWN_ATTRIBUTE_TYPES.include?(type.to_s) - - the_type = type.to_s - # TODO: add defaults - #the_attr = [type.to_s] - #the_attr << options[:default] if options.has_key? :default - @attrs[name.to_s] = the_type - self - end - - # The following are the attribute types supported by Active Resource - # migrations. - # TODO: We should eventually support all of these: - # %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |attr_type| - KNOWN_ATTRIBUTE_TYPES.each do |attr_type| - class_eval <<-EOV - def #{attr_type.to_s}(*args) # def string(*args) - options = args.extract_options! # options = args.extract_options! - attr_names = args # attr_names = args - # - attr_names.each { |name| attribute(name, '#{attr_type}', options) } # attr_names.each { |name| attribute(name, 'string', options) } - end # end - EOV - - end - - end -end diff --git a/activeresource/test/cases/base/schema_test.rb b/activeresource/test/cases/base/schema_test.rb index 398e7cf539..4a6e8bfbdb 100644 --- a/activeresource/test/cases/base/schema_test.rb +++ b/activeresource/test/cases/base/schema_test.rb @@ -92,7 +92,7 @@ class SchemaTest < ActiveModel::TestCase test "schema should accept all known attribute types as values" do # I'd prefer to use here... - ActiveResource::SchemaDefinition::KNOWN_ATTRIBUTE_TYPES.each do |the_type| + ActiveResource::Schema::KNOWN_ATTRIBUTE_TYPES.each do |the_type| assert_nothing_raised("should have accepted #{the_type.inspect}"){ Person.schema = {'my_key' => the_type }} end end @@ -171,13 +171,13 @@ class SchemaTest < ActiveModel::TestCase matz = Person.find(1) assert !matz.schema.blank?, "should have some sort of schema on an instance variable" assert_not_equal new_schema, matz.schema, "should not have the class-level schema until it's been added to the class!" - + assert_nothing_raised { Person.schema = new_schema assert_equal new_schema, matz.schema, "class-level schema should override instance-level schema" } end - + ##################################################### # Using the define_schema syntax @@ -188,11 +188,11 @@ class SchemaTest < ActiveModel::TestCase assert_nothing_raised("Should allow the define_schema to take a block") do Person.define_schema do |s| - assert s.kind_of?(ActiveResource::SchemaDefinition), "the 's' should be a schema definition or we're way off track..." + assert s.kind_of?(ActiveResource::Schema), "the 's' should be a schema definition or we're way off track..." end end end - + test "schema definition should store and return attribute set" do assert_nothing_raised do Person.define_schema do |s| @@ -202,13 +202,13 @@ class SchemaTest < ActiveModel::TestCase end end end - + test "should be able to add attributes through define_schema" do assert_nothing_raised do Person.define_schema do |s| assert s.attribute('foo', 'string'), "should take a simple attribute" assert s.attrs.has_key?('foo'), "should have saved the attribute name" - assert_equal 'string', s.attrs['foo'], "should have saved the attribute type" + assert_equal 'string', s.attrs['foo'], "should have saved the attribute type" end end end @@ -218,23 +218,23 @@ class SchemaTest < ActiveModel::TestCase Person.define_schema do |s| assert s.attribute(:foo, :integer), "should take a simple attribute as symbols" assert s.attrs.has_key?('foo'), "should have saved the attribute name as a string" - assert_equal 'integer', s.attrs['foo'], "should have saved the attribute type as a string" + assert_equal 'integer', s.attrs['foo'], "should have saved the attribute type as a string" end end end - + test "should be able to add all known attribute types" do Person.define_schema do |s| - ActiveResource::SchemaDefinition::KNOWN_ATTRIBUTE_TYPES.each do |the_type| + ActiveResource::Schema::KNOWN_ATTRIBUTE_TYPES.each do |the_type| assert_nothing_raised do assert s.attribute('foo', the_type), "should take a simple attribute of type: #{the_type}" assert s.attrs.has_key?('foo'), "should have saved the attribute name" - assert_equal the_type.to_s, s.attrs['foo'], "should have saved the attribute type of: #{the_type}" + assert_equal the_type.to_s, s.attrs['foo'], "should have saved the attribute type of: #{the_type}" end end end end - + test "attributes should not accept unknown values" do bad_values = [ :oogle, :blob, 'thing'] @@ -251,20 +251,20 @@ class SchemaTest < ActiveModel::TestCase end end - + test "should accept attribute types as the type's name as the method" do Person.define_schema do |s| - ActiveResource::SchemaDefinition::KNOWN_ATTRIBUTE_TYPES.each do |the_type| + ActiveResource::Schema::KNOWN_ATTRIBUTE_TYPES.each do |the_type| assert s.respond_to?(the_type), "should recognise the attribute-type: #{the_type} as a method" assert_nothing_raised("should take the method #{the_type} with the attribute name") do s.send(the_type,'foo') # eg s.string :foo end assert s.attrs.has_key?('foo'), "should now have saved the attribute name" - assert_equal the_type.to_s, s.attrs['foo'], "should have saved the attribute type of: #{the_type}" + assert_equal the_type.to_s, s.attrs['foo'], "should have saved the attribute type of: #{the_type}" end end end - + test "should accept multiple attribute names for an attribute method" do names = ['foo','bar','baz'] Person.define_schema do |s| @@ -273,7 +273,7 @@ class SchemaTest < ActiveModel::TestCase end names.each do |the_name| assert s.attrs.has_key?(the_name), "should now have saved the attribute name: #{the_name}" - assert_equal 'string', s.attrs[the_name], "should have saved the attribute as a string" + assert_equal 'string', s.attrs[the_name], "should have saved the attribute as a string" end end end @@ -282,13 +282,13 @@ class SchemaTest < ActiveModel::TestCase # What a schema does for us #### - # respond_to? + # respond_to? test "should respond positively to attributes that are only in the schema" do new_attr_name = :my_new_schema_attribute new_attr_name_two = :another_new_schema_attribute assert Person.schema.blank?, "sanity check - should have a blank class schema" - + assert !Person.new.respond_do?(new_attr_name), "sanity check - should not respond to the brand-new attribute yet" assert !Person.new.respond_do?(new_attr_name_two), "sanity check - should not respond to the brand-new attribute yet" @@ -306,7 +306,7 @@ class SchemaTest < ActiveModel::TestCase new_attr_name_two = :another_new_schema_attribute assert Person.schema.blank?, "sanity check - should have a blank class schema" - + assert !Person.new.respond_do?(new_attr_name), "sanity check - should not respond to the brand-new attribute yet" assert !Person.new.respond_do?(new_attr_name_two), "sanity check - should not respond to the brand-new attribute yet" @@ -328,10 +328,10 @@ class SchemaTest < ActiveModel::TestCase assert Person.schema.blank?, "sanity check - should have a blank class schema" assert_raises(NoMethodError, "should not have found the attribute: #{new_attr_name} as a method") do - Person.new.send(new_attr_name) + Person.new.send(new_attr_name) end assert_raises(NoMethodError, "should not have found the attribute: #{new_attr_name_two} as a method") do - Person.new.send(new_attr_name_two) + Person.new.send(new_attr_name_two) end Person.schema = {new_attr_name.to_s => :float} @@ -348,9 +348,9 @@ class SchemaTest < ActiveModel::TestCase # Known attributes # # Attributes can be known to be attributes even if they aren't actually - # 'set' on a particular instance. + # 'set' on a particular instance. # This will only differ from 'attributes' if a schema has been set. - + test "new model should have no known attributes" do assert Person.known_attributes.blank?, "should have no known attributes" assert Person.new.known_attributes.blank?, "should have no known attributes on a new instance" -- cgit v1.2.3 From c0ad3f6cc618f42eae0c5d5ceefde32ff3342c20 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 20 Dec 2009 18:48:01 -0600 Subject: Rename define_schema => schema --- activeresource/lib/active_resource/base.rb | 43 +++++++++++++-------------- activeresource/lib/active_resource/schema.rb | 4 +-- activeresource/test/cases/base/schema_test.rb | 36 +++++++++++----------- 3 files changed, 40 insertions(+), 43 deletions(-) diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index b39f8fbd48..d07571e1d7 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -241,12 +241,6 @@ module ActiveResource cattr_accessor :logger class << self - # This will shortly disappear to be replaced by the migration-style - # usage of this for defining a schema. At that point, all the doc - # currenlty on schema= will move back here... - def schema # :nodoc: - @schema ||= nil - end # Creates a schema for this resource - setting the attributes that are # known prior to fetching an instance from the remote system. # @@ -260,7 +254,7 @@ module ActiveResource # # example: # class Person < ActiveResource::Base - # define_schema do |s| + # schema do |s| # # define each attribute separately # s.attribute 'name', :string # @@ -300,32 +294,35 @@ module ActiveResource # j.age # => 34 # cast to an integer # j.weight # => '65' # still a string! # - def define_schema - schema_definition = Schema.new - yield schema_definition if block_given? + def schema(&block) + if block_given? + schema_definition = Schema.new + yield schema_definition - # skip out if we didn't define anything - return unless schema_definition.attrs.present? + # skip out if we didn't define anything + return unless schema_definition.attrs.present? - @schema ||= {}.with_indifferent_access - @known_attributes ||= [] + @schema ||= {}.with_indifferent_access + @known_attributes ||= [] - schema_definition.attrs.each do |k,v| - @schema[k] = v - @known_attributes << k - end + schema_definition.attrs.each do |k,v| + @schema[k] = v + @known_attributes << k + end - schema + schema + else + @schema ||= nil + end end - # Alternative, direct way to specify a schema for this - # Resource. define_schema is more flexible, but this is quick + # Resource. schema is more flexible, but this is quick # for a very simple schema. # # Pass the schema as a hash with the keys being the attribute-names # and the value being one of the accepted attribute types (as defined - # in define_schema) + # in schema) # # example: # @@ -346,7 +343,7 @@ module ActiveResource raise ArgumentError, "Expected a hash" unless the_schema.kind_of? Hash - define_schema do |s| + schema do |s| the_schema.each {|k,v| s.attribute(k,v) } end end diff --git a/activeresource/lib/active_resource/schema.rb b/activeresource/lib/active_resource/schema.rb index 498b00ffef..6f0b229145 100644 --- a/activeresource/lib/active_resource/schema.rb +++ b/activeresource/lib/active_resource/schema.rb @@ -15,13 +15,13 @@ module ActiveResource # :nodoc: # unlike an Active Record TableDefinition (on which it is based). # It provides a set of convenience methods for people to define their # schema using the syntax: - # define_schema do |s| + # schema do |s| # s.string :foo # s.integer :bar # end # # The schema stores the name and type of each attribute. That is then - # read out by the define_schema method to populate the actual + # read out by the schema method to populate the actual # Resource's schema def initialize @attrs = {} diff --git a/activeresource/test/cases/base/schema_test.rb b/activeresource/test/cases/base/schema_test.rb index 4a6e8bfbdb..98a8b98da1 100644 --- a/activeresource/test/cases/base/schema_test.rb +++ b/activeresource/test/cases/base/schema_test.rb @@ -180,14 +180,14 @@ class SchemaTest < ActiveModel::TestCase ##################################################### - # Using the define_schema syntax + # Using the schema syntax #### - test "should be able to use define_schema" do - assert Person.respond_to?(:define_schema), "should at least respond to the define_schema method" + test "should be able to use schema" do + assert Person.respond_to?(:schema), "should at least respond to the schema method" - assert_nothing_raised("Should allow the define_schema to take a block") do - Person.define_schema do |s| + assert_nothing_raised("Should allow the schema to take a block") do + Person.schema do |s| assert s.kind_of?(ActiveResource::Schema), "the 's' should be a schema definition or we're way off track..." end end @@ -195,7 +195,7 @@ class SchemaTest < ActiveModel::TestCase test "schema definition should store and return attribute set" do assert_nothing_raised do - Person.define_schema do |s| + Person.schema do |s| assert s.respond_to?(:attrs), "should return attributes in theory" s.attribute :foo, :string assert_equal({'foo' => 'string' }, s.attrs, "should return attributes in practice") @@ -203,9 +203,9 @@ class SchemaTest < ActiveModel::TestCase end end - test "should be able to add attributes through define_schema" do + test "should be able to add attributes through schema" do assert_nothing_raised do - Person.define_schema do |s| + Person.schema do |s| assert s.attribute('foo', 'string'), "should take a simple attribute" assert s.attrs.has_key?('foo'), "should have saved the attribute name" assert_equal 'string', s.attrs['foo'], "should have saved the attribute type" @@ -215,7 +215,7 @@ class SchemaTest < ActiveModel::TestCase test "should convert symbol attributes to strings" do assert_nothing_raised do - Person.define_schema do |s| + Person.schema do |s| assert s.attribute(:foo, :integer), "should take a simple attribute as symbols" assert s.attrs.has_key?('foo'), "should have saved the attribute name as a string" assert_equal 'integer', s.attrs['foo'], "should have saved the attribute type as a string" @@ -224,7 +224,7 @@ class SchemaTest < ActiveModel::TestCase end test "should be able to add all known attribute types" do - Person.define_schema do |s| + Person.schema do |s| ActiveResource::Schema::KNOWN_ATTRIBUTE_TYPES.each do |the_type| assert_nothing_raised do assert s.attribute('foo', the_type), "should take a simple attribute of type: #{the_type}" @@ -238,7 +238,7 @@ class SchemaTest < ActiveModel::TestCase test "attributes should not accept unknown values" do bad_values = [ :oogle, :blob, 'thing'] - Person.define_schema do |s| + Person.schema do |s| bad_values.each do |bad_value| assert_raises(ArgumentError,"should only accept a known attribute type, but accepted: #{bad_value.inspect}") do s.attribute 'key', bad_value @@ -253,7 +253,7 @@ class SchemaTest < ActiveModel::TestCase test "should accept attribute types as the type's name as the method" do - Person.define_schema do |s| + Person.schema do |s| ActiveResource::Schema::KNOWN_ATTRIBUTE_TYPES.each do |the_type| assert s.respond_to?(the_type), "should recognise the attribute-type: #{the_type} as a method" assert_nothing_raised("should take the method #{the_type} with the attribute name") do @@ -267,7 +267,7 @@ class SchemaTest < ActiveModel::TestCase test "should accept multiple attribute names for an attribute method" do names = ['foo','bar','baz'] - Person.define_schema do |s| + Person.schema do |s| assert_nothing_raised("should take strings with multiple attribute names as params") do s.string( *names) end @@ -294,11 +294,11 @@ class SchemaTest < ActiveModel::TestCase assert_nothing_raised do Person.schema = {new_attr_name.to_s => 'string'} - Person.define_schema {|s| s.string new_attr_name_two } + Person.schema {|s| s.string new_attr_name_two } end assert Person.new.respond_to?(new_attr_name), "should respond to the attribute in a passed-in schema, but failed on: #{new_attr_name}" - assert Person.new.respond_to?(new_attr_name_two), "should respond to the attribute from the define_schema, but failed on: #{new_attr_name_two}" + assert Person.new.respond_to?(new_attr_name_two), "should respond to the attribute from the schema, but failed on: #{new_attr_name_two}" end test "should not care about ordering of schema definitions" do @@ -311,12 +311,12 @@ class SchemaTest < ActiveModel::TestCase assert !Person.new.respond_do?(new_attr_name_two), "sanity check - should not respond to the brand-new attribute yet" assert_nothing_raised do - Person.define_schema {|s| s.string new_attr_name_two } + Person.schema {|s| s.string new_attr_name_two } Person.schema = {new_attr_name.to_s => 'string'} end assert Person.new.respond_to?(new_attr_name), "should respond to the attribute in a passed-in schema, but failed on: #{new_attr_name}" - assert Person.new.respond_to?(new_attr_name_two), "should respond to the attribute from the define_schema, but failed on: #{new_attr_name_two}" + assert Person.new.respond_to?(new_attr_name_two), "should respond to the attribute from the schema, but failed on: #{new_attr_name_two}" end # method_missing effects @@ -335,7 +335,7 @@ class SchemaTest < ActiveModel::TestCase end Person.schema = {new_attr_name.to_s => :float} - Person.define_schema {|s| s.string new_attr_name_two } + Person.schema {|s| s.string new_attr_name_two } assert_nothing_raised do Person.new.send(new_attr_name) -- cgit v1.2.3 From 2e9c7759984573944592fb1bda5aeb7c58edba55 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 20 Dec 2009 19:03:22 -0600 Subject: Use instance_eval for schema block --- activeresource/lib/active_resource/base.rb | 18 ++--- activeresource/lib/active_resource/schema.rb | 11 +-- activeresource/test/cases/base/schema_test.rb | 102 ++++++++++++++------------ 3 files changed, 69 insertions(+), 62 deletions(-) diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index d07571e1d7..b833e9c8ce 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -254,18 +254,18 @@ module ActiveResource # # example: # class Person < ActiveResource::Base - # schema do |s| + # schema do # # define each attribute separately - # s.attribute 'name', :string + # attribute 'name', :string # # # or use the convenience methods and pass >=1 attribute names - # s.string 'eye_colour', 'hair_colour' - # s.integer 'age' - # s.float 'height', 'weight' + # string 'eye_colour', 'hair_colour' + # integer 'age' + # float 'height', 'weight' # # # unsupported types should be left as strings # # overload the accessor methods if you need to convert them - # s.attribute 'created_at', 'string' + # attribute 'created_at', 'string' # end # end # @@ -297,7 +297,7 @@ module ActiveResource def schema(&block) if block_given? schema_definition = Schema.new - yield schema_definition + schema_definition.instance_eval(&block) # skip out if we didn't define anything return unless schema_definition.attrs.present? @@ -343,8 +343,8 @@ module ActiveResource raise ArgumentError, "Expected a hash" unless the_schema.kind_of? Hash - schema do |s| - the_schema.each {|k,v| s.attribute(k,v) } + schema do + the_schema.each {|k,v| attribute(k,v) } end end diff --git a/activeresource/lib/active_resource/schema.rb b/activeresource/lib/active_resource/schema.rb index 6f0b229145..4ca83e404d 100644 --- a/activeresource/lib/active_resource/schema.rb +++ b/activeresource/lib/active_resource/schema.rb @@ -2,7 +2,6 @@ require 'active_resource/exceptions' module ActiveResource # :nodoc: class Schema # :nodoc: - # attributes can be known to be one of these types. They are easy to # cast to/from. KNOWN_ATTRIBUTE_TYPES = %w( string integer float ) @@ -15,9 +14,9 @@ module ActiveResource # :nodoc: # unlike an Active Record TableDefinition (on which it is based). # It provides a set of convenience methods for people to define their # schema using the syntax: - # schema do |s| - # s.string :foo - # s.integer :bar + # schema do + # string :foo + # integer :bar # end # # The schema stores the name and type of each attribute. That is then @@ -44,15 +43,13 @@ module ActiveResource # :nodoc: # %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |attr_type| KNOWN_ATTRIBUTE_TYPES.each do |attr_type| class_eval <<-EOV - def #{attr_type.to_s}(*args) # def string(*args) + def #{attr_type.to_s}(*args) # def string(*args) options = args.extract_options! # options = args.extract_options! attr_names = args # attr_names = args # attr_names.each { |name| attribute(name, '#{attr_type}', options) } # attr_names.each { |name| attribute(name, 'string', options) } end # end EOV - end - end end diff --git a/activeresource/test/cases/base/schema_test.rb b/activeresource/test/cases/base/schema_test.rb index 98a8b98da1..d1afb9f439 100644 --- a/activeresource/test/cases/base/schema_test.rb +++ b/activeresource/test/cases/base/schema_test.rb @@ -187,50 +187,57 @@ class SchemaTest < ActiveModel::TestCase assert Person.respond_to?(:schema), "should at least respond to the schema method" assert_nothing_raised("Should allow the schema to take a block") do - Person.schema do |s| - assert s.kind_of?(ActiveResource::Schema), "the 's' should be a schema definition or we're way off track..." - end + Person.schema { } end end test "schema definition should store and return attribute set" do assert_nothing_raised do - Person.schema do |s| - assert s.respond_to?(:attrs), "should return attributes in theory" - s.attribute :foo, :string - assert_equal({'foo' => 'string' }, s.attrs, "should return attributes in practice") + s = nil + Person.schema do + s = self + attribute :foo, :string end + assert s.respond_to?(:attrs), "should return attributes in theory" + assert_equal({'foo' => 'string' }, s.attrs, "should return attributes in practice") end end test "should be able to add attributes through schema" do assert_nothing_raised do - Person.schema do |s| - assert s.attribute('foo', 'string'), "should take a simple attribute" - assert s.attrs.has_key?('foo'), "should have saved the attribute name" - assert_equal 'string', s.attrs['foo'], "should have saved the attribute type" + s = nil + Person.schema do + s = self + attribute('foo', 'string') end + assert s.attrs.has_key?('foo'), "should have saved the attribute name" + assert_equal 'string', s.attrs['foo'], "should have saved the attribute type" end end test "should convert symbol attributes to strings" do assert_nothing_raised do - Person.schema do |s| - assert s.attribute(:foo, :integer), "should take a simple attribute as symbols" - assert s.attrs.has_key?('foo'), "should have saved the attribute name as a string" - assert_equal 'integer', s.attrs['foo'], "should have saved the attribute type as a string" + s = nil + Person.schema do + attribute(:foo, :integer) + s = self end + + assert s.attrs.has_key?('foo'), "should have saved the attribute name as a string" + assert_equal 'integer', s.attrs['foo'], "should have saved the attribute type as a string" end end test "should be able to add all known attribute types" do - Person.schema do |s| + assert_nothing_raised do ActiveResource::Schema::KNOWN_ATTRIBUTE_TYPES.each do |the_type| - assert_nothing_raised do - assert s.attribute('foo', the_type), "should take a simple attribute of type: #{the_type}" - assert s.attrs.has_key?('foo'), "should have saved the attribute name" - assert_equal the_type.to_s, s.attrs['foo'], "should have saved the attribute type of: #{the_type}" + s = nil + Person.schema do + s = self + attribute('foo', the_type) end + assert s.attrs.has_key?('foo'), "should have saved the attribute name" + assert_equal the_type.to_s, s.attrs['foo'], "should have saved the attribute type of: #{the_type}" end end end @@ -238,14 +245,18 @@ class SchemaTest < ActiveModel::TestCase test "attributes should not accept unknown values" do bad_values = [ :oogle, :blob, 'thing'] - Person.schema do |s| - bad_values.each do |bad_value| - assert_raises(ArgumentError,"should only accept a known attribute type, but accepted: #{bad_value.inspect}") do - s.attribute 'key', bad_value + bad_values.each do |bad_value| + s = nil + assert_raises(ArgumentError,"should only accept a known attribute type, but accepted: #{bad_value.inspect}") do + Person.schema do + s = self + attribute 'key', bad_value end - assert !s.respond_to?(bad_value), "should only respond to a known attribute type, but accepted: #{bad_value.inspect}" - assert_raises(NoMethodError,"should only have methods for known attribute types, but accepted: #{bad_value.inspect}") do - s.send bad_value, 'key' + end + assert !self.respond_to?(bad_value), "should only respond to a known attribute type, but accepted: #{bad_value.inspect}" + assert_raises(NoMethodError,"should only have methods for known attribute types, but accepted: #{bad_value.inspect}") do + Person.schema do + send bad_value, 'key' end end end @@ -253,28 +264,27 @@ class SchemaTest < ActiveModel::TestCase test "should accept attribute types as the type's name as the method" do - Person.schema do |s| - ActiveResource::Schema::KNOWN_ATTRIBUTE_TYPES.each do |the_type| - assert s.respond_to?(the_type), "should recognise the attribute-type: #{the_type} as a method" - assert_nothing_raised("should take the method #{the_type} with the attribute name") do - s.send(the_type,'foo') # eg s.string :foo - end - assert s.attrs.has_key?('foo'), "should now have saved the attribute name" - assert_equal the_type.to_s, s.attrs['foo'], "should have saved the attribute type of: #{the_type}" + ActiveResource::Schema::KNOWN_ATTRIBUTE_TYPES.each do |the_type| + s = nil + Person.schema do + s = self + send(the_type,'foo') end + assert s.attrs.has_key?('foo'), "should now have saved the attribute name" + assert_equal the_type.to_s, s.attrs['foo'], "should have saved the attribute type of: #{the_type}" end end test "should accept multiple attribute names for an attribute method" do names = ['foo','bar','baz'] - Person.schema do |s| - assert_nothing_raised("should take strings with multiple attribute names as params") do - s.string( *names) - end - names.each do |the_name| - assert s.attrs.has_key?(the_name), "should now have saved the attribute name: #{the_name}" - assert_equal 'string', s.attrs[the_name], "should have saved the attribute as a string" - end + s = nil + Person.schema do + s = self + string(*names) + end + names.each do |the_name| + assert s.attrs.has_key?(the_name), "should now have saved the attribute name: #{the_name}" + assert_equal 'string', s.attrs[the_name], "should have saved the attribute as a string" end end @@ -294,7 +304,7 @@ class SchemaTest < ActiveModel::TestCase assert_nothing_raised do Person.schema = {new_attr_name.to_s => 'string'} - Person.schema {|s| s.string new_attr_name_two } + Person.schema { string new_attr_name_two } end assert Person.new.respond_to?(new_attr_name), "should respond to the attribute in a passed-in schema, but failed on: #{new_attr_name}" @@ -311,7 +321,7 @@ class SchemaTest < ActiveModel::TestCase assert !Person.new.respond_do?(new_attr_name_two), "sanity check - should not respond to the brand-new attribute yet" assert_nothing_raised do - Person.schema {|s| s.string new_attr_name_two } + Person.schema { string new_attr_name_two } Person.schema = {new_attr_name.to_s => 'string'} end @@ -335,7 +345,7 @@ class SchemaTest < ActiveModel::TestCase end Person.schema = {new_attr_name.to_s => :float} - Person.schema {|s| s.string new_attr_name_two } + Person.schema { string new_attr_name_two } assert_nothing_raised do Person.new.send(new_attr_name) -- cgit v1.2.3 From bdccffc40eee1e11b7e5f3516bffa4c46bf97b30 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 20 Dec 2009 19:03:47 -0600 Subject: Remove annoying and useless meta comments --- activeresource/lib/active_resource/schema.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/activeresource/lib/active_resource/schema.rb b/activeresource/lib/active_resource/schema.rb index 4ca83e404d..8368b652c2 100644 --- a/activeresource/lib/active_resource/schema.rb +++ b/activeresource/lib/active_resource/schema.rb @@ -43,12 +43,12 @@ module ActiveResource # :nodoc: # %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |attr_type| KNOWN_ATTRIBUTE_TYPES.each do |attr_type| class_eval <<-EOV - def #{attr_type.to_s}(*args) # def string(*args) - options = args.extract_options! # options = args.extract_options! - attr_names = args # attr_names = args - # - attr_names.each { |name| attribute(name, '#{attr_type}', options) } # attr_names.each { |name| attribute(name, 'string', options) } - end # end + def #{attr_type.to_s}(*args) + options = args.extract_options! + attr_names = args + + attr_names.each { |name| attribute(name, '#{attr_type}', options) } + end EOV end end -- cgit v1.2.3 From 83f4d86a9330533ec9af21ba18b4ab28011b8981 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 20 Dec 2009 17:15:31 -0800 Subject: Rename the RenderingController module to just plain Rendering --- actionmailer/lib/action_mailer/base.rb | 2 +- actionpack/lib/abstract_controller.rb | 2 +- actionpack/lib/abstract_controller/helpers.rb | 2 +- actionpack/lib/abstract_controller/layouts.rb | 2 +- actionpack/lib/abstract_controller/rendering.rb | 203 +++++++++++++++++++++ .../abstract_controller/rendering_controller.rb | 203 --------------------- actionpack/lib/action_controller.rb | 2 +- actionpack/lib/action_controller/base.rb | 2 +- actionpack/lib/action_controller/metal/layouts.rb | 2 +- .../lib/action_controller/metal/rendering.rb | 57 ++++++ .../metal/rendering_controller.rb | 57 ------ .../lib/action_controller/metal/streaming.rb | 2 +- .../lib/action_controller/metal/verification.rb | 2 +- .../test/abstract/abstract_controller_test.rb | 2 +- actionpack/test/abstract/helper_test.rb | 2 +- actionpack/test/abstract/layouts_test.rb | 2 +- actionpack/test/abstract/localized_cache_test.rb | 2 +- actionpack/test/abstract/render_test.rb | 2 +- 18 files changed, 274 insertions(+), 274 deletions(-) create mode 100644 actionpack/lib/abstract_controller/rendering.rb delete mode 100644 actionpack/lib/abstract_controller/rendering_controller.rb create mode 100644 actionpack/lib/action_controller/metal/rendering.rb delete mode 100644 actionpack/lib/action_controller/metal/rendering_controller.rb diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index b5239ac0cd..a69838fe43 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -253,7 +253,7 @@ module ActionMailer #:nodoc: class Base include AdvAttrAccessor, PartContainer, Quoting, Utils - include AbstractController::RenderingController + include AbstractController::Rendering include AbstractController::LocalizedCache include AbstractController::Layouts diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb index d13a56a859..c15a1da98a 100644 --- a/actionpack/lib/abstract_controller.rb +++ b/actionpack/lib/abstract_controller.rb @@ -15,6 +15,6 @@ module AbstractController autoload :Layouts autoload :LocalizedCache autoload :Logger - autoload :RenderingController + autoload :Rendering end end diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb index d3b492ad09..1d898d1a4c 100644 --- a/actionpack/lib/abstract_controller/helpers.rb +++ b/actionpack/lib/abstract_controller/helpers.rb @@ -4,7 +4,7 @@ module AbstractController module Helpers extend ActiveSupport::Concern - include RenderingController + include Rendering def self.next_serial @helper_serial ||= 0 diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb index c71cef42b2..46760bba7c 100644 --- a/actionpack/lib/abstract_controller/layouts.rb +++ b/actionpack/lib/abstract_controller/layouts.rb @@ -2,7 +2,7 @@ module AbstractController module Layouts extend ActiveSupport::Concern - include RenderingController + include Rendering included do extlib_inheritable_accessor(:_layout_conditions) { Hash.new } diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb new file mode 100644 index 0000000000..8ef2526df0 --- /dev/null +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -0,0 +1,203 @@ +require "abstract_controller/base" +require "abstract_controller/logger" + +module AbstractController + class DoubleRenderError < Error + DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"." + + def initialize(message = nil) + super(message || DEFAULT_MESSAGE) + end + end + + module Rendering + extend ActiveSupport::Concern + + include AbstractController::Logger + + included do + attr_internal :formats + extlib_inheritable_accessor :_view_paths + self._view_paths ||= ActionView::PathSet.new + end + + # Initialize controller with nil formats. + def initialize(*) #:nodoc: + @_formats = nil + super + end + + # An instance of a view class. The default view class is ActionView::Base + # + # The view class must have the following methods: + # View.for_controller[controller] Create a new ActionView instance for a + # controller + # View#render_partial[options] + # - responsible for setting options[:_template] + # - Returns String with the rendered partial + # options:: see _render_partial in ActionView::Base + # View#render_template[template, layout, options, partial] + # - Returns String with the rendered template + # template:: The template to render + # layout:: The layout to render around the template + # options:: See _render_template_with_layout in ActionView::Base + # partial:: Whether or not the template to render is a partial + # + # Override this method in a to change the default behavior. + def view_context + @_view_context ||= ActionView::Base.for_controller(self) + end + + # Mostly abstracts the fact that calling render twice is a DoubleRenderError. + # Delegates render_to_body and sticks the result in self.response_body. + def render(*args) + if response_body + raise AbstractController::DoubleRenderError, "OMG" + end + + self.response_body = render_to_body(*args) + end + + # Raw rendering of a template to a Rack-compatible body. + # + # ==== Options + # _partial_object:: The object that is being rendered. If this + # exists, we are in the special case of rendering an object as a partial. + # + # :api: plugin + def render_to_body(options = {}) + # TODO: Refactor so we can just use the normal template logic for this + if options.key?(:partial) + view_context.render_partial(options) + else + _determine_template(options) + _render_template(options) + end + end + + # Raw rendering of a template to a string. Just convert the results of + # render_to_body into a String. + # + # :api: plugin + def render_to_string(options = {}) + AbstractController::Rendering.body_to_s(render_to_body(options)) + end + + # Renders the template from an object. + # + # ==== Options + # _template:: The template to render + # _layout:: The layout to wrap the template in (optional) + # _partial:: Whether or not the template to be rendered is a partial + def _render_template(options) + view_context.render_template(options) + end + + # The list of view paths for this controller. See ActionView::ViewPathSet for + # more details about writing custom view paths. + def view_paths + _view_paths + end + + # Return a string representation of a Rack-compatible response body. + def self.body_to_s(body) + if body.respond_to?(:to_str) + body + else + strings = [] + body.each { |part| strings << part.to_s } + body.close if body.respond_to?(:close) + strings.join + end + end + + private + + # Take in a set of options and determine the template to render + # + # ==== Options + # _template:: If this is provided, the search is over + # _template_name<#to_s>:: The name of the template to look up. Otherwise, + # use the current action name. + # _prefix:: The prefix to look inside of. In a file system, this corresponds + # to a directory. + # _partial:: Whether or not the file to look up is a partial + def _determine_template(options) + if options.key?(:text) + options[:_template] = ActionView::Template::Text.new(options[:text], format_for_text) + elsif options.key?(:inline) + handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb") + template = ActionView::Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {}) + options[:_template] = template + elsif options.key?(:template) + options[:_template_name] = options[:template] + elsif options.key?(:file) + options[:_template_name] = options[:file] + end + + name = (options[:_template_name] || action_name).to_s + + options[:_template] ||= with_template_cache(name) do + find_template(name, { :formats => formats }, options) + end + end + + def find_template(name, details, options) + view_paths.find(name, details, options[:_prefix], options[:_partial]) + end + + def template_exists?(name, details, options) + view_paths.exists?(name, details, options[:_prefix], options[:_partial]) + end + + def with_template_cache(name) + yield + end + + def format_for_text + Mime[:text] + end + + module ClassMethods + def clear_template_caches! + end + + # Append a path to the list of view paths for this controller. + # + # ==== Parameters + # path:: If a String is provided, it gets converted into + # the default view path. You may also provide a custom view path + # (see ActionView::ViewPathSet for more information) + def append_view_path(path) + self.view_paths << path + end + + # Prepend a path to the list of view paths for this controller. + # + # ==== Parameters + # path:: If a String is provided, it gets converted into + # the default view path. You may also provide a custom view path + # (see ActionView::ViewPathSet for more information) + def prepend_view_path(path) + clear_template_caches! + self.view_paths.unshift(path) + end + + # A list of all of the default view paths for this controller. + def view_paths + self._view_paths + end + + # Set the view paths. + # + # ==== Parameters + # paths:: If a ViewPathSet is provided, use that; + # otherwise, process the parameter into a ViewPathSet. + def view_paths=(paths) + clear_template_caches! + self._view_paths = paths.is_a?(ActionView::PathSet) ? + paths : ActionView::Base.process_view_paths(paths) + end + end + end +end diff --git a/actionpack/lib/abstract_controller/rendering_controller.rb b/actionpack/lib/abstract_controller/rendering_controller.rb deleted file mode 100644 index 7f2243d4ef..0000000000 --- a/actionpack/lib/abstract_controller/rendering_controller.rb +++ /dev/null @@ -1,203 +0,0 @@ -require "abstract_controller/base" -require "abstract_controller/logger" - -module AbstractController - class DoubleRenderError < Error - DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"." - - def initialize(message = nil) - super(message || DEFAULT_MESSAGE) - end - end - - module RenderingController - extend ActiveSupport::Concern - - include AbstractController::Logger - - included do - attr_internal :formats - extlib_inheritable_accessor :_view_paths - self._view_paths ||= ActionView::PathSet.new - end - - # Initialize controller with nil formats. - def initialize(*) #:nodoc: - @_formats = nil - super - end - - # An instance of a view class. The default view class is ActionView::Base - # - # The view class must have the following methods: - # View.for_controller[controller] Create a new ActionView instance for a - # controller - # View#render_partial[options] - # - responsible for setting options[:_template] - # - Returns String with the rendered partial - # options:: see _render_partial in ActionView::Base - # View#render_template[template, layout, options, partial] - # - Returns String with the rendered template - # template:: The template to render - # layout:: The layout to render around the template - # options:: See _render_template_with_layout in ActionView::Base - # partial:: Whether or not the template to render is a partial - # - # Override this method in a to change the default behavior. - def view_context - @_view_context ||= ActionView::Base.for_controller(self) - end - - # Mostly abstracts the fact that calling render twice is a DoubleRenderError. - # Delegates render_to_body and sticks the result in self.response_body. - def render(*args) - if response_body - raise AbstractController::DoubleRenderError, "OMG" - end - - self.response_body = render_to_body(*args) - end - - # Raw rendering of a template to a Rack-compatible body. - # - # ==== Options - # _partial_object:: The object that is being rendered. If this - # exists, we are in the special case of rendering an object as a partial. - # - # :api: plugin - def render_to_body(options = {}) - # TODO: Refactor so we can just use the normal template logic for this - if options.key?(:partial) - view_context.render_partial(options) - else - _determine_template(options) - _render_template(options) - end - end - - # Raw rendering of a template to a string. Just convert the results of - # render_to_body into a String. - # - # :api: plugin - def render_to_string(options = {}) - AbstractController::RenderingController.body_to_s(render_to_body(options)) - end - - # Renders the template from an object. - # - # ==== Options - # _template:: The template to render - # _layout:: The layout to wrap the template in (optional) - # _partial:: Whether or not the template to be rendered is a partial - def _render_template(options) - view_context.render_template(options) - end - - # The list of view paths for this controller. See ActionView::ViewPathSet for - # more details about writing custom view paths. - def view_paths - _view_paths - end - - # Return a string representation of a Rack-compatible response body. - def self.body_to_s(body) - if body.respond_to?(:to_str) - body - else - strings = [] - body.each { |part| strings << part.to_s } - body.close if body.respond_to?(:close) - strings.join - end - end - - private - - # Take in a set of options and determine the template to render - # - # ==== Options - # _template:: If this is provided, the search is over - # _template_name<#to_s>:: The name of the template to look up. Otherwise, - # use the current action name. - # _prefix:: The prefix to look inside of. In a file system, this corresponds - # to a directory. - # _partial:: Whether or not the file to look up is a partial - def _determine_template(options) - if options.key?(:text) - options[:_template] = ActionView::Template::Text.new(options[:text], format_for_text) - elsif options.key?(:inline) - handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb") - template = ActionView::Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {}) - options[:_template] = template - elsif options.key?(:template) - options[:_template_name] = options[:template] - elsif options.key?(:file) - options[:_template_name] = options[:file] - end - - name = (options[:_template_name] || action_name).to_s - - options[:_template] ||= with_template_cache(name) do - find_template(name, { :formats => formats }, options) - end - end - - def find_template(name, details, options) - view_paths.find(name, details, options[:_prefix], options[:_partial]) - end - - def template_exists?(name, details, options) - view_paths.exists?(name, details, options[:_prefix], options[:_partial]) - end - - def with_template_cache(name) - yield - end - - def format_for_text - Mime[:text] - end - - module ClassMethods - def clear_template_caches! - end - - # Append a path to the list of view paths for this controller. - # - # ==== Parameters - # path:: If a String is provided, it gets converted into - # the default view path. You may also provide a custom view path - # (see ActionView::ViewPathSet for more information) - def append_view_path(path) - self.view_paths << path - end - - # Prepend a path to the list of view paths for this controller. - # - # ==== Parameters - # path:: If a String is provided, it gets converted into - # the default view path. You may also provide a custom view path - # (see ActionView::ViewPathSet for more information) - def prepend_view_path(path) - clear_template_caches! - self.view_paths.unshift(path) - end - - # A list of all of the default view paths for this controller. - def view_paths - self._view_paths - end - - # Set the view paths. - # - # ==== Parameters - # paths:: If a ViewPathSet is provided, use that; - # otherwise, process the parameter into a ViewPathSet. - def view_paths=(paths) - clear_template_caches! - self._view_paths = paths.is_a?(ActionView::PathSet) ? - paths : ActionView::Base.process_view_paths(paths) - end - end - end -end diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 144850da62..6794596ae6 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -25,7 +25,7 @@ module ActionController autoload :RackConvenience autoload :Compatibility autoload :Redirector - autoload :RenderingController + autoload :Rendering autoload :RenderOptions autoload :Rescue autoload :Responder diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index e49be371a6..ee10584548 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -9,7 +9,7 @@ module ActionController include ActionController::HideActions include ActionController::UrlFor include ActionController::Redirector - include ActionController::RenderingController + include ActionController::Rendering include ActionController::RenderOptions::All include ActionController::Layouts include ActionController::ConditionalGet diff --git a/actionpack/lib/action_controller/metal/layouts.rb b/actionpack/lib/action_controller/metal/layouts.rb index cc7088248a..f44498a884 100644 --- a/actionpack/lib/action_controller/metal/layouts.rb +++ b/actionpack/lib/action_controller/metal/layouts.rb @@ -158,7 +158,7 @@ module ActionController module Layouts extend ActiveSupport::Concern - include ActionController::RenderingController + include ActionController::Rendering include AbstractController::Layouts module ClassMethods diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb new file mode 100644 index 0000000000..20eb524e50 --- /dev/null +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -0,0 +1,57 @@ +module ActionController + module Rendering + extend ActiveSupport::Concern + + included do + include AbstractController::Rendering + include AbstractController::LocalizedCache + end + + def process_action(*) + self.formats = request.formats.map {|x| x.to_sym} + super + end + + def render(options) + super + self.content_type ||= options[:_template].mime_type.to_s + response_body + end + + def render_to_body(options) + _process_options(options) + + if options.key?(:partial) + options[:partial] = action_name if options[:partial] == true + options[:_details] = {:formats => formats} + end + + super + end + + private + def _prefix + controller_path + end + + def _determine_template(options) + if (options.keys & [:partial, :file, :template, :text, :inline]).empty? + options[:_template_name] ||= options[:action] + options[:_prefix] = _prefix + end + + super + end + + def format_for_text + formats.first + end + + def _process_options(options) + status, content_type, location = options.values_at(:status, :content_type, :location) + self.status = status if status + self.content_type = content_type if content_type + self.headers["Location"] = url_for(location) if location + end + end +end diff --git a/actionpack/lib/action_controller/metal/rendering_controller.rb b/actionpack/lib/action_controller/metal/rendering_controller.rb deleted file mode 100644 index 237299cd30..0000000000 --- a/actionpack/lib/action_controller/metal/rendering_controller.rb +++ /dev/null @@ -1,57 +0,0 @@ -module ActionController - module RenderingController - extend ActiveSupport::Concern - - included do - include AbstractController::RenderingController - include AbstractController::LocalizedCache - end - - def process_action(*) - self.formats = request.formats.map {|x| x.to_sym} - super - end - - def render(options) - super - self.content_type ||= options[:_template].mime_type.to_s - response_body - end - - def render_to_body(options) - _process_options(options) - - if options.key?(:partial) - options[:partial] = action_name if options[:partial] == true - options[:_details] = {:formats => formats} - end - - super - end - - private - def _prefix - controller_path - end - - def _determine_template(options) - if (options.keys & [:partial, :file, :template, :text, :inline]).empty? - options[:_template_name] ||= options[:action] - options[:_prefix] = _prefix - end - - super - end - - def format_for_text - formats.first - end - - def _process_options(options) - status, content_type, location = options.values_at(:status, :content_type, :location) - self.status = status if status - self.content_type = content_type if content_type - self.headers["Location"] = url_for(location) if location - end - end -end diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb index 43c661bef4..288b5d7c99 100644 --- a/actionpack/lib/action_controller/metal/streaming.rb +++ b/actionpack/lib/action_controller/metal/streaming.rb @@ -4,7 +4,7 @@ module ActionController #:nodoc: module Streaming extend ActiveSupport::Concern - include ActionController::RenderingController + include ActionController::Rendering DEFAULT_SEND_FILE_OPTIONS = { :type => 'application/octet-stream'.freeze, diff --git a/actionpack/lib/action_controller/metal/verification.rb b/actionpack/lib/action_controller/metal/verification.rb index 500cced539..cbd169b641 100644 --- a/actionpack/lib/action_controller/metal/verification.rb +++ b/actionpack/lib/action_controller/metal/verification.rb @@ -2,7 +2,7 @@ module ActionController #:nodoc: module Verification #:nodoc: extend ActiveSupport::Concern - include AbstractController::Callbacks, Session, Flash, RenderingController + include AbstractController::Callbacks, Session, Flash, Rendering # This module provides a class-level method for specifying that certain # actions are guarded against being called without certain prerequisites diff --git a/actionpack/test/abstract/abstract_controller_test.rb b/actionpack/test/abstract/abstract_controller_test.rb index 524381509d..4ad87d9762 100644 --- a/actionpack/test/abstract/abstract_controller_test.rb +++ b/actionpack/test/abstract/abstract_controller_test.rb @@ -28,7 +28,7 @@ module AbstractController # Test Render mixin # ==== class RenderingController < AbstractController::Base - include ::AbstractController::RenderingController + include ::AbstractController::Rendering def _prefix() end diff --git a/actionpack/test/abstract/helper_test.rb b/actionpack/test/abstract/helper_test.rb index efcd68e5c8..ade29140ba 100644 --- a/actionpack/test/abstract/helper_test.rb +++ b/actionpack/test/abstract/helper_test.rb @@ -6,7 +6,7 @@ module AbstractController module Testing class ControllerWithHelpers < AbstractController::Base - include AbstractController::RenderingController + include AbstractController::Rendering include Helpers def with_module diff --git a/actionpack/test/abstract/layouts_test.rb b/actionpack/test/abstract/layouts_test.rb index 5028c19b80..df73d948f0 100644 --- a/actionpack/test/abstract/layouts_test.rb +++ b/actionpack/test/abstract/layouts_test.rb @@ -6,7 +6,7 @@ module AbstractControllerTests # Base controller for these tests class Base < AbstractController::Base - include AbstractController::RenderingController + include AbstractController::Rendering include AbstractController::Layouts self.view_paths = [ActionView::FixtureResolver.new( diff --git a/actionpack/test/abstract/localized_cache_test.rb b/actionpack/test/abstract/localized_cache_test.rb index 6f9bb693f7..8b0b0fff03 100644 --- a/actionpack/test/abstract/localized_cache_test.rb +++ b/actionpack/test/abstract/localized_cache_test.rb @@ -4,7 +4,7 @@ module AbstractController module Testing class CachedController < AbstractController::Base - include AbstractController::RenderingController + include AbstractController::Rendering include AbstractController::LocalizedCache self.view_paths = [ActionView::FixtureResolver.new( diff --git a/actionpack/test/abstract/render_test.rb b/actionpack/test/abstract/render_test.rb index 331cb6f769..be0478b638 100644 --- a/actionpack/test/abstract/render_test.rb +++ b/actionpack/test/abstract/render_test.rb @@ -4,7 +4,7 @@ module AbstractController module Testing class ControllerRenderer < AbstractController::Base - include AbstractController::RenderingController + include AbstractController::Rendering self.view_paths = [ActionView::FixtureResolver.new( "default.erb" => "With Default", -- cgit v1.2.3 From 9b41e1e4d8b9d159254dc4ad4bbd3207f1b49eb5 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 20 Dec 2009 17:25:13 -0800 Subject: Renamed Redirector to Redirecting (its a module, not a class) --- actionpack/lib/action_controller.rb | 2 +- actionpack/lib/action_controller/base.rb | 2 +- actionpack/lib/action_controller/metal.rb | 2 +- .../lib/action_controller/metal/redirecting.rb | 98 ++++++++++++++++++++++ .../lib/action_controller/metal/redirector.rb | 98 ---------------------- 5 files changed, 101 insertions(+), 101 deletions(-) create mode 100644 actionpack/lib/action_controller/metal/redirecting.rb delete mode 100644 actionpack/lib/action_controller/metal/redirector.rb diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 6794596ae6..514b4e7fe9 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -24,7 +24,7 @@ module ActionController autoload :MimeResponds autoload :RackConvenience autoload :Compatibility - autoload :Redirector + autoload :Redirecting autoload :Rendering autoload :RenderOptions autoload :Rescue diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index ee10584548..ec64cc1a39 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -8,7 +8,7 @@ module ActionController include ActionController::Helpers include ActionController::HideActions include ActionController::UrlFor - include ActionController::Redirector + include ActionController::Redirecting include ActionController::Rendering include ActionController::RenderOptions::All include ActionController::Layouts diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index 60b3f9a89b..a1d857d2ce 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -58,7 +58,7 @@ module ActionController # Basic implementations for content_type=, location=, and headers are # provided to reduce the dependency on the RackConvenience module - # in Renderer and Redirector. + # in Rendering and Redirecting. def content_type=(type) headers["Content-Type"] = type.to_s diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb new file mode 100644 index 0000000000..d101f920e3 --- /dev/null +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -0,0 +1,98 @@ +module ActionController + class RedirectBackError < AbstractController::Error #:nodoc: + DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].' + + def initialize(message = nil) + super(message || DEFAULT_MESSAGE) + end + end + + module Redirecting + extend ActiveSupport::Concern + include AbstractController::Logger + + # Redirects the browser to the target specified in +options+. This parameter can take one of three forms: + # + # * Hash - The URL will be generated by calling url_for with the +options+. + # * Record - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record. + # * String starting with protocol:// (like http://) - Is passed straight through as the target for redirection. + # * String not containing a protocol - The current protocol and host is prepended to the string. + # * :back - Back to the page that issued the request. Useful for forms that are triggered from multiple places. + # Short-hand for redirect_to(request.env["HTTP_REFERER"]) + # + # Examples: + # redirect_to :action => "show", :id => 5 + # redirect_to post + # redirect_to "http://www.rubyonrails.org" + # redirect_to "/images/screenshot.jpg" + # redirect_to articles_url + # redirect_to :back + # + # The redirection happens as a "302 Moved" header unless otherwise specified. + # + # Examples: + # redirect_to post_url(@post), :status => :found + # redirect_to :action=>'atom', :status => :moved_permanently + # redirect_to post_url(@post), :status => 301 + # redirect_to :action=>'atom', :status => 302 + # + # It is also possible to assign a flash message as part of the redirection. There are two special accessors for commonly used the flash names + # +alert+ and +notice+ as well as a general purpose +flash+ bucket. + # + # Examples: + # redirect_to post_url(@post), :alert => "Watch it, mister!" + # redirect_to post_url(@post), :status=> :found, :notice => "Pay attention to the road" + # redirect_to post_url(@post), :status => 301, :flash => { :updated_post_id => @post.id } + # redirect_to { :action=>'atom' }, :alert => "Something serious happened" + # + # When using redirect_to :back, if there is no referrer, + # RedirectBackError will be raised. You may specify some fallback + # behavior for this case by rescuing RedirectBackError. + def redirect_to(options = {}, response_status = {}) #:doc: + raise ActionControllerError.new("Cannot redirect to nil!") if options.nil? + raise AbstractController::DoubleRenderError if response_body + + self.status = _extract_redirect_to_status(options, response_status) + self.location = _compute_redirect_to_location(options) + self.response_body = "You are being redirected." + + logger.info("Redirected to #{location}") if logger && logger.info? + end + + private + def _extract_redirect_to_status(options, response_status) + status = if options.is_a?(Hash) && options.key?(:status) + _interpret_status(options.delete(:status)) + elsif response_status.key?(:status) + _interpret_status(response_status[:status]) + else + 302 + end + end + + def _compute_redirect_to_location(options) + case options + # The scheme name consist of a letter followed by any combination of + # letters, digits, and the plus ("+"), period ("."), or hyphen ("-") + # characters; and is terminated by a colon (":"). + when %r{^\w[\w\d+.-]*:.*} + options + when String + request.protocol + request.host_with_port + options + when :back + raise RedirectBackError unless refer = request.headers["Referer"] + refer + else + url_for(options) + end.gsub(/[\r\n]/, '') + end + + def _interpret_status(status) + if status.is_a?(Symbol) + (ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[status] || 500) + else + status.to_i + end + end + end +end diff --git a/actionpack/lib/action_controller/metal/redirector.rb b/actionpack/lib/action_controller/metal/redirector.rb deleted file mode 100644 index a7bd5ee981..0000000000 --- a/actionpack/lib/action_controller/metal/redirector.rb +++ /dev/null @@ -1,98 +0,0 @@ -module ActionController - class RedirectBackError < AbstractController::Error #:nodoc: - DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].' - - def initialize(message = nil) - super(message || DEFAULT_MESSAGE) - end - end - - module Redirector - extend ActiveSupport::Concern - include AbstractController::Logger - - # Redirects the browser to the target specified in +options+. This parameter can take one of three forms: - # - # * Hash - The URL will be generated by calling url_for with the +options+. - # * Record - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record. - # * String starting with protocol:// (like http://) - Is passed straight through as the target for redirection. - # * String not containing a protocol - The current protocol and host is prepended to the string. - # * :back - Back to the page that issued the request. Useful for forms that are triggered from multiple places. - # Short-hand for redirect_to(request.env["HTTP_REFERER"]) - # - # Examples: - # redirect_to :action => "show", :id => 5 - # redirect_to post - # redirect_to "http://www.rubyonrails.org" - # redirect_to "/images/screenshot.jpg" - # redirect_to articles_url - # redirect_to :back - # - # The redirection happens as a "302 Moved" header unless otherwise specified. - # - # Examples: - # redirect_to post_url(@post), :status => :found - # redirect_to :action=>'atom', :status => :moved_permanently - # redirect_to post_url(@post), :status => 301 - # redirect_to :action=>'atom', :status => 302 - # - # It is also possible to assign a flash message as part of the redirection. There are two special accessors for commonly used the flash names - # +alert+ and +notice+ as well as a general purpose +flash+ bucket. - # - # Examples: - # redirect_to post_url(@post), :alert => "Watch it, mister!" - # redirect_to post_url(@post), :status=> :found, :notice => "Pay attention to the road" - # redirect_to post_url(@post), :status => 301, :flash => { :updated_post_id => @post.id } - # redirect_to { :action=>'atom' }, :alert => "Something serious happened" - # - # When using redirect_to :back, if there is no referrer, - # RedirectBackError will be raised. You may specify some fallback - # behavior for this case by rescuing RedirectBackError. - def redirect_to(options = {}, response_status = {}) #:doc: - raise ActionControllerError.new("Cannot redirect to nil!") if options.nil? - raise AbstractController::DoubleRenderError if response_body - - self.status = _extract_redirect_to_status(options, response_status) - self.location = _compute_redirect_to_location(options) - self.response_body = "You are being redirected." - - logger.info("Redirected to #{location}") if logger && logger.info? - end - - private - def _extract_redirect_to_status(options, response_status) - status = if options.is_a?(Hash) && options.key?(:status) - _interpret_status(options.delete(:status)) - elsif response_status.key?(:status) - _interpret_status(response_status[:status]) - else - 302 - end - end - - def _compute_redirect_to_location(options) - case options - # The scheme name consist of a letter followed by any combination of - # letters, digits, and the plus ("+"), period ("."), or hyphen ("-") - # characters; and is terminated by a colon (":"). - when %r{^\w[\w\d+.-]*:.*} - options - when String - request.protocol + request.host_with_port + options - when :back - raise RedirectBackError unless refer = request.headers["Referer"] - refer - else - url_for(options) - end.gsub(/[\r\n]/, '') - end - - def _interpret_status(status) - if status.is_a?(Symbol) - (ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[status] || 500) - else - status.to_i - end - end - end -end -- cgit v1.2.3 From b4ecb5555100cc67011637d261e5de30f5b7fcba Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sun, 20 Dec 2009 17:33:31 -0800 Subject: Missing acts_like dependency --- activesupport/lib/active_support/core_ext/time/calculations.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index 4f4492f0fd..703b89ffd0 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -1,4 +1,5 @@ require 'active_support/duration' +require 'active_support/core_ext/date/acts_like' require 'active_support/core_ext/date/calculations' class Time -- cgit v1.2.3 From 0f8a5c7954bfc134f46eeb72c4cc8744825cbb5a Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 20 Dec 2009 20:00:04 -0600 Subject: Merge Session stuff into RackConvenience --- actionpack/lib/action_controller.rb | 1 - actionpack/lib/action_controller/base.rb | 1 - actionpack/lib/action_controller/metal/flash.rb | 10 ++-- .../action_controller/metal/rack_convenience.rb | 1 + .../metal/request_forgery_protection.rb | 32 +++++----- actionpack/lib/action_controller/metal/session.rb | 15 ----- .../lib/action_controller/metal/verification.rb | 70 +++++++++++----------- 7 files changed, 56 insertions(+), 74 deletions(-) delete mode 100644 actionpack/lib/action_controller/metal/session.rb diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 514b4e7fe9..2e4d8d20ef 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -29,7 +29,6 @@ module ActionController autoload :RenderOptions autoload :Rescue autoload :Responder - autoload :Session autoload :SessionManagement autoload :UrlFor autoload :Verification diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index ec64cc1a39..d24c01c983 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -26,7 +26,6 @@ module ActionController include ActionController::Compatibility include ActionController::Cookies - include ActionController::Session include ActionController::Flash include ActionController::Verification include ActionController::RequestForgeryProtection diff --git a/actionpack/lib/action_controller/metal/flash.rb b/actionpack/lib/action_controller/metal/flash.rb index ae343444e2..581ff6109e 100644 --- a/actionpack/lib/action_controller/metal/flash.rb +++ b/actionpack/lib/action_controller/metal/flash.rb @@ -28,8 +28,6 @@ module ActionController #:nodoc: module Flash extend ActiveSupport::Concern - include Session - included do helper_method :alert, :notice end @@ -155,7 +153,7 @@ module ActionController #:nodoc: def alert flash[:alert] end - + # Convenience accessor for flash[:alert]= def alert=(message) flash[:alert] = message @@ -165,7 +163,7 @@ module ActionController #:nodoc: def notice flash[:notice] end - + # Convenience accessor for flash[:notice]= def notice=(message) flash[:notice] = message @@ -193,11 +191,11 @@ module ActionController #:nodoc: if notice = response_status_and_flash.delete(:notice) flash[:notice] = notice end - + if other_flashes = response_status_and_flash.delete(:flash) flash.update(other_flashes) end - + super(options, response_status_and_flash) end end diff --git a/actionpack/lib/action_controller/metal/rack_convenience.rb b/actionpack/lib/action_controller/metal/rack_convenience.rb index 131d20114d..f0837c10e8 100644 --- a/actionpack/lib/action_controller/metal/rack_convenience.rb +++ b/actionpack/lib/action_controller/metal/rack_convenience.rb @@ -3,6 +3,7 @@ module ActionController extend ActiveSupport::Concern included do + delegate :session, :reset_session, :to => "@_request" delegate :headers, :status=, :location=, :content_type=, :status, :location, :content_type, :to => "@_response" attr_internal :request diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index 173df79ee7..2826b1e34c 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -5,7 +5,7 @@ module ActionController #:nodoc: module RequestForgeryProtection extend ActiveSupport::Concern - include AbstractController::Helpers, Session + include AbstractController::Helpers included do # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ @@ -19,31 +19,31 @@ module ActionController #:nodoc: helper_method :form_authenticity_token helper_method :protect_against_forgery? end - - # Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current - # web application, not a forged link from another site, is done by embedding a token based on a random + + # Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current + # web application, not a forged link from another site, is done by embedding a token based on a random # string stored in the session (which an attacker wouldn't know) in all forms and Ajax requests generated - # by Rails and then verifying the authenticity of that token in the controller. Only HTML/JavaScript - # requests are checked, so this will not protect your XML API (presumably you'll have a different - # authentication scheme there anyway). Also, GET requests are not protected as these should be + # by Rails and then verifying the authenticity of that token in the controller. Only HTML/JavaScript + # requests are checked, so this will not protect your XML API (presumably you'll have a different + # authentication scheme there anyway). Also, GET requests are not protected as these should be # idempotent anyway. # # This is turned on with the protect_from_forgery method, which will check the token and raise an - # ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the + # ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the # error message in production by editing public/422.html. A call to this method in ApplicationController is # generated by default in post-Rails 2.0 applications. # - # The token parameter is named authenticity_token by default. If you are generating an HTML form - # manually (without the use of Rails' form_for, form_tag or other helpers), you have to - # include a hidden field named like that and set its value to what is returned by + # The token parameter is named authenticity_token by default. If you are generating an HTML form + # manually (without the use of Rails' form_for, form_tag or other helpers), you have to + # include a hidden field named like that and set its value to what is returned by # form_authenticity_token. # - # Request forgery protection is disabled by default in test environment. If you are upgrading from Rails + # Request forgery protection is disabled by default in test environment. If you are upgrading from Rails # 1.x, add this to config/environments/test.rb: # # # Disable request forgery protection in test environment # config.action_controller.allow_forgery_protection = false - # + # # == Learn more about CSRF (Cross-Site Request Forgery) attacks # # Here are some resources: @@ -52,11 +52,11 @@ module ActionController #:nodoc: # # Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application. # There are a few guidelines you should follow: - # + # # * Keep your GET requests safe and idempotent. More reading material: # * http://www.xml.com/pub/a/2002/04/24/deviant.html # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1 - # * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look + # * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look # for "Expires: at end of session" # module ClassMethods @@ -92,7 +92,7 @@ module ActionController #:nodoc: # * is it a GET request? Gets should be safe and idempotent # * Does the form_authenticity_token match the given token value from the params? def verified_request? - !protect_against_forgery? || request.forgery_whitelisted? || + !protect_against_forgery? || request.forgery_whitelisted? || form_authenticity_token == params[request_forgery_protection_token] end diff --git a/actionpack/lib/action_controller/metal/session.rb b/actionpack/lib/action_controller/metal/session.rb deleted file mode 100644 index bcedd6e1c7..0000000000 --- a/actionpack/lib/action_controller/metal/session.rb +++ /dev/null @@ -1,15 +0,0 @@ -module ActionController - module Session - extend ActiveSupport::Concern - - include RackConvenience - - def session - @_request.session - end - - def reset_session - @_request.reset_session - end - end -end diff --git a/actionpack/lib/action_controller/metal/verification.rb b/actionpack/lib/action_controller/metal/verification.rb index cbd169b641..bce942b588 100644 --- a/actionpack/lib/action_controller/metal/verification.rb +++ b/actionpack/lib/action_controller/metal/verification.rb @@ -2,7 +2,7 @@ module ActionController #:nodoc: module Verification #:nodoc: extend ActiveSupport::Concern - include AbstractController::Callbacks, Session, Flash, Rendering + include AbstractController::Callbacks, Flash, Rendering # This module provides a class-level method for specifying that certain # actions are guarded against being called without certain prerequisites @@ -35,7 +35,7 @@ module ActionController #:nodoc: # :add_flash => { "alert" => "Failed to create your message" }, # :redirect_to => :category_url # - # Note that these prerequisites are not business rules. They do not examine + # Note that these prerequisites are not business rules. They do not examine # the content of the session or the parameters. That level of validation should # be encapsulated by your domain model or helper methods in the controller. module ClassMethods @@ -43,40 +43,40 @@ module ActionController #:nodoc: # the user is redirected to a different action. The +options+ parameter # is a hash consisting of the following key/value pairs: # - # :params:: - # a single key or an array of keys that must be in the params + # :params:: + # a single key or an array of keys that must be in the params # hash in order for the action(s) to be safely called. - # :session:: - # a single key or an array of keys that must be in the session + # :session:: + # a single key or an array of keys that must be in the session # in order for the action(s) to be safely called. - # :flash:: - # a single key or an array of keys that must be in the flash in order + # :flash:: + # a single key or an array of keys that must be in the flash in order # for the action(s) to be safely called. - # :method:: - # a single key or an array of keys--any one of which must match the - # current request method in order for the action(s) to be safely called. - # (The key should be a symbol: :get or :post, for + # :method:: + # a single key or an array of keys--any one of which must match the + # current request method in order for the action(s) to be safely called. + # (The key should be a symbol: :get or :post, for # example.) - # :xhr:: - # true/false option to ensure that the request is coming from an Ajax - # call or not. - # :add_flash:: - # a hash of name/value pairs that should be merged into the session's + # :xhr:: + # true/false option to ensure that the request is coming from an Ajax + # call or not. + # :add_flash:: + # a hash of name/value pairs that should be merged into the session's # flash if the prerequisites cannot be satisfied. - # :add_headers:: - # a hash of name/value pairs that should be merged into the response's + # :add_headers:: + # a hash of name/value pairs that should be merged into the response's # headers hash if the prerequisites cannot be satisfied. - # :redirect_to:: - # the redirection parameters to be used when redirecting if the - # prerequisites cannot be satisfied. You can redirect either to named + # :redirect_to:: + # the redirection parameters to be used when redirecting if the + # prerequisites cannot be satisfied. You can redirect either to named # route or to the action in some controller. - # :render:: + # :render:: # the render parameters to be used when the prerequisites cannot be satisfied. - # :only:: - # only apply this verification to the actions specified in the associated + # :only:: + # only apply this verification to the actions specified in the associated # array (may also be a single value). - # :except:: - # do not apply this verification to the actions specified in the associated + # :except:: + # do not apply this verification to the actions specified in the associated # array (may also be a single value). def verify(options={}) before_filter :only => options[:only], :except => options[:except] do @@ -94,31 +94,31 @@ module ActionController #:nodoc: apply_remaining_actions(options) unless performed? end end - + def prereqs_invalid?(options) # :nodoc: - verify_presence_of_keys_in_hash_flash_or_params(options) || - verify_method(options) || + verify_presence_of_keys_in_hash_flash_or_params(options) || + verify_method(options) || verify_request_xhr_status(options) end - + def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc: [*options[:params] ].find { |v| v && params[v.to_sym].nil? } || [*options[:session]].find { |v| session[v].nil? } || [*options[:flash] ].find { |v| flash[v].nil? } end - + def verify_method(options) # :nodoc: [*options[:method]].all? { |v| request.method != v.to_sym } if options[:method] end - + def verify_request_xhr_status(options) # :nodoc: request.xhr? != options[:xhr] unless options[:xhr].nil? end - + def apply_redirect_to(redirect_to_option) # :nodoc: (redirect_to_option.is_a?(Symbol) && redirect_to_option != :back) ? self.__send__(redirect_to_option) : redirect_to_option end - + def apply_remaining_actions(options) # :nodoc: case when options[:render] ; render(options[:render]) -- cgit v1.2.3 From 29c8a43056f40759a8c64cbcbd4e71d4283b233d Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 20 Dec 2009 20:05:26 -0600 Subject: Rename RackConvenience => RackDelegation --- actionpack/lib/action_controller.rb | 2 +- actionpack/lib/action_controller/base.rb | 2 +- actionpack/lib/action_controller/metal.rb | 6 ++--- .../lib/action_controller/metal/conditional_get.rb | 2 +- actionpack/lib/action_controller/metal/cookies.rb | 2 +- .../action_controller/metal/rack_convenience.rb | 28 ---------------------- .../lib/action_controller/metal/rack_delegation.rb | 28 ++++++++++++++++++++++ actionpack/lib/action_controller/metal/testing.rb | 2 +- actionpack/lib/action_controller/metal/url_for.rb | 2 +- 9 files changed, 37 insertions(+), 37 deletions(-) delete mode 100644 actionpack/lib/action_controller/metal/rack_convenience.rb create mode 100644 actionpack/lib/action_controller/metal/rack_delegation.rb diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 2e4d8d20ef..029887c96e 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -22,7 +22,7 @@ module ActionController autoload :HideActions autoload :Layouts autoload :MimeResponds - autoload :RackConvenience + autoload :RackDelegation autoload :Compatibility autoload :Redirecting autoload :Rendering diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index d24c01c983..6eda653cb2 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -13,7 +13,7 @@ module ActionController include ActionController::RenderOptions::All include ActionController::Layouts include ActionController::ConditionalGet - include ActionController::RackConvenience + include ActionController::RackDelegation include ActionController::Benchmarking include ActionController::Configuration diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index a1d857d2ce..8433be2320 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -45,7 +45,7 @@ module ActionController # The details below can be overridden to support a specific # Request and Response object. The default ActionController::Base - # implementation includes RackConvenience, which makes a request + # implementation includes RackDelegation, which makes a request # and response object available. You might wish to control the # environment and response manually for performance reasons. @@ -57,8 +57,8 @@ module ActionController end # Basic implementations for content_type=, location=, and headers are - # provided to reduce the dependency on the RackConvenience module - # in Rendering and Redirecting. + # provided to reduce the dependency on the RackDelegation module + # in Renderer and Redirector. def content_type=(type) headers["Content-Type"] = type.to_s diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb index 5156fbc1d5..61e7ece90d 100644 --- a/actionpack/lib/action_controller/metal/conditional_get.rb +++ b/actionpack/lib/action_controller/metal/conditional_get.rb @@ -2,7 +2,7 @@ module ActionController module ConditionalGet extend ActiveSupport::Concern - include RackConvenience + include RackDelegation include Head # Sets the etag, last_modified, or both on the response and renders a diff --git a/actionpack/lib/action_controller/metal/cookies.rb b/actionpack/lib/action_controller/metal/cookies.rb index 6855ca1478..e27374e4c4 100644 --- a/actionpack/lib/action_controller/metal/cookies.rb +++ b/actionpack/lib/action_controller/metal/cookies.rb @@ -46,7 +46,7 @@ module ActionController #:nodoc: module Cookies extend ActiveSupport::Concern - include RackConvenience + include RackDelegation included do helper_method :cookies diff --git a/actionpack/lib/action_controller/metal/rack_convenience.rb b/actionpack/lib/action_controller/metal/rack_convenience.rb deleted file mode 100644 index f0837c10e8..0000000000 --- a/actionpack/lib/action_controller/metal/rack_convenience.rb +++ /dev/null @@ -1,28 +0,0 @@ -module ActionController - module RackConvenience - extend ActiveSupport::Concern - - included do - delegate :session, :reset_session, :to => "@_request" - delegate :headers, :status=, :location=, :content_type=, - :status, :location, :content_type, :to => "@_response" - attr_internal :request - end - - def dispatch(action, env) - @_request = ActionDispatch::Request.new(env) - @_response = ActionDispatch::Response.new - @_response.request = request - super - end - - def params - @_params ||= @_request.parameters - end - - def response_body=(body) - response.body = body if response - super - end - end -end diff --git a/actionpack/lib/action_controller/metal/rack_delegation.rb b/actionpack/lib/action_controller/metal/rack_delegation.rb new file mode 100644 index 0000000000..5141918499 --- /dev/null +++ b/actionpack/lib/action_controller/metal/rack_delegation.rb @@ -0,0 +1,28 @@ +module ActionController + module RackDelegation + extend ActiveSupport::Concern + + included do + delegate :session, :reset_session, :to => "@_request" + delegate :headers, :status=, :location=, :content_type=, + :status, :location, :content_type, :to => "@_response" + attr_internal :request + end + + def dispatch(action, env) + @_request = ActionDispatch::Request.new(env) + @_response = ActionDispatch::Response.new + @_response.request = request + super + end + + def params + @_params ||= @_request.parameters + end + + def response_body=(body) + response.body = body if response + super + end + end +end diff --git a/actionpack/lib/action_controller/metal/testing.rb b/actionpack/lib/action_controller/metal/testing.rb index a4a1116d9e..c193a5eff4 100644 --- a/actionpack/lib/action_controller/metal/testing.rb +++ b/actionpack/lib/action_controller/metal/testing.rb @@ -2,7 +2,7 @@ module ActionController module Testing extend ActiveSupport::Concern - include RackConvenience + include RackDelegation # OMG MEGA HAX def process_with_new_base_test(request, response) diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb index 14c6523045..8c3810ebcb 100644 --- a/actionpack/lib/action_controller/metal/url_for.rb +++ b/actionpack/lib/action_controller/metal/url_for.rb @@ -2,7 +2,7 @@ module ActionController module UrlFor extend ActiveSupport::Concern - include RackConvenience + include RackDelegation # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in # the form of a hash, just like the one you would use for url_for directly. Example: -- cgit v1.2.3 From 36c13cc07a45cbfa5d06c89001a092c70b07e253 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 20 Dec 2009 18:15:20 -0800 Subject: Rename RenderOptions to Renderers --- actionpack/lib/action_controller.rb | 2 +- actionpack/lib/action_controller/base.rb | 2 +- .../lib/action_controller/metal/render_options.rb | 92 ---------------------- .../lib/action_controller/metal/renderers.rb | 91 +++++++++++++++++++++ 4 files changed, 93 insertions(+), 94 deletions(-) delete mode 100644 actionpack/lib/action_controller/metal/render_options.rb create mode 100644 actionpack/lib/action_controller/metal/renderers.rb diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 514b4e7fe9..e479ded8b3 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -26,7 +26,7 @@ module ActionController autoload :Compatibility autoload :Redirecting autoload :Rendering - autoload :RenderOptions + autoload :Renderers autoload :Rescue autoload :Responder autoload :Session diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index ec64cc1a39..d84705434d 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -10,7 +10,7 @@ module ActionController include ActionController::UrlFor include ActionController::Redirecting include ActionController::Rendering - include ActionController::RenderOptions::All + include ActionController::Renderers::All include ActionController::Layouts include ActionController::ConditionalGet include ActionController::RackConvenience diff --git a/actionpack/lib/action_controller/metal/render_options.rb b/actionpack/lib/action_controller/metal/render_options.rb deleted file mode 100644 index b6a7ca0eda..0000000000 --- a/actionpack/lib/action_controller/metal/render_options.rb +++ /dev/null @@ -1,92 +0,0 @@ -module ActionController - - def self.add_renderer(key, &block) - RenderOptions.add(key, &block) - end - - module RenderOptions - extend ActiveSupport::Concern - - included do - extlib_inheritable_accessor :_renderers - self._renderers = {} - end - - module ClassMethods - def _write_render_options - renderers = _renderers.map do |name, value| - <<-RUBY_EVAL - if options.key?(:#{name}) - _process_options(options) - return _render_option_#{name}(options[:#{name}], options) - end - RUBY_EVAL - end - - class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def _handle_render_options(options) - #{renderers.join} - end - RUBY_EVAL - end - - def use_renderers(*args) - args.each do |key| - _renderers[key] = RENDERERS[key] - end - _write_render_options - end - alias use_renderer use_renderers - end - - def render_to_body(options) - _handle_render_options(options) || super - end - - RENDERERS = {} - def self.add(key, &block) - define_method("_render_option_#{key}", &block) - RENDERERS[key] = block - All._write_render_options - end - - module All - extend ActiveSupport::Concern - include RenderOptions - - INCLUDED = [] - included do - self._renderers = RENDERERS - _write_render_options - INCLUDED << self - end - - def self._write_render_options - INCLUDED.each(&:_write_render_options) - end - end - - add :json do |json, options| - json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str) - json = "#{options[:callback]}(#{json})" unless options[:callback].blank? - self.content_type ||= Mime::JSON - self.response_body = json - end - - add :js do |js, options| - self.content_type ||= Mime::JS - self.response_body = js.respond_to?(:to_js) ? js.to_js : js - end - - add :xml do |xml, options| - self.content_type ||= Mime::XML - self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml : xml - end - - add :update do |proc, options| - generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(view_context, &proc) - self.content_type = Mime::JS - self.response_body = generator.to_s - end - end -end diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb new file mode 100644 index 0000000000..c1ba47927a --- /dev/null +++ b/actionpack/lib/action_controller/metal/renderers.rb @@ -0,0 +1,91 @@ +module ActionController + def self.add_renderer(key, &block) + Renderers.add(key, &block) + end + + module Renderers + extend ActiveSupport::Concern + + included do + extlib_inheritable_accessor :_renderers + self._renderers = {} + end + + module ClassMethods + def _write_render_options + renderers = _renderers.map do |name, value| + <<-RUBY_EVAL + if options.key?(:#{name}) + _process_options(options) + return _render_option_#{name}(options[:#{name}], options) + end + RUBY_EVAL + end + + class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 + def _handle_render_options(options) + #{renderers.join} + end + RUBY_EVAL + end + + def use_renderers(*args) + args.each do |key| + _renderers[key] = RENDERERS[key] + end + _write_render_options + end + alias use_renderer use_renderers + end + + def render_to_body(options) + _handle_render_options(options) || super + end + + RENDERERS = {} + def self.add(key, &block) + define_method("_render_option_#{key}", &block) + RENDERERS[key] = block + All._write_render_options + end + + module All + extend ActiveSupport::Concern + include Renderers + + INCLUDED = [] + included do + self._renderers = RENDERERS + _write_render_options + INCLUDED << self + end + + def self._write_render_options + INCLUDED.each(&:_write_render_options) + end + end + + add :json do |json, options| + json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str) + json = "#{options[:callback]}(#{json})" unless options[:callback].blank? + self.content_type ||= Mime::JSON + self.response_body = json + end + + add :js do |js, options| + self.content_type ||= Mime::JS + self.response_body = js.respond_to?(:to_js) ? js.to_js : js + end + + add :xml do |xml, options| + self.content_type ||= Mime::XML + self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml : xml + end + + add :update do |proc, options| + generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(view_context, &proc) + self.content_type = Mime::JS + self.response_body = generator.to_s + end + end +end -- cgit v1.2.3 From cf9d6a95e805bdddfa9c6b541631d51b3165bf23 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 20 Dec 2009 18:30:50 -0800 Subject: Added ActionDispatch::Request#authorization to access the http authentication header regardless of its proxy hiding [DHH] --- actionpack/CHANGELOG | 2 ++ actionpack/lib/action_dispatch/http/request.rb | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 3852dfe02b..782b4229fb 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* Added ActionDispatch::Request#authorization to access the http authentication header regardless of its proxy hiding [DHH] + * Added :alert, :notice, and :flash as options to ActionController::Base#redirect_to that'll automatically set the proper flash before the redirection [DHH]. Examples: flash[:notice] = 'Post was created' diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 7d1f5a4504..bc17cadb38 100755 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -465,6 +465,15 @@ EOM session['flash'] || {} end + # Returns the authorization header regardless of whether it was specified directly or through one of the + # proxy alternatives. + def authorization + @env['HTTP_AUTHORIZATION'] || + @env['X-HTTP_AUTHORIZATION'] || + @env['X_HTTP_AUTHORIZATION'] || + @env['REDIRECT_X_HTTP_AUTHORIZATION'] + end + # Receives an array of mimes and return the first user sent mime that # matches the order array. # -- cgit v1.2.3 From eeda059818ae95620a7c7b86ad0ff2fa621cbe88 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 20 Dec 2009 18:37:09 -0800 Subject: Just a little tidying --- .../action_dispatch/middleware/params_parser.rb | 46 +++++++++++----------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb index 32ccb5c931..8970ccaf07 100644 --- a/actionpack/lib/action_dispatch/middleware/params_parser.rb +++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb @@ -31,41 +31,39 @@ module ActionDispatch return false unless strategy case strategy - when Proc - strategy.call(request.raw_post) - when :xml_simple, :xml_node - request.body.size == 0 ? {} : Hash.from_xml(request.body).with_indifferent_access - when :yaml - YAML.load(request.body) - when :json - if request.body.size == 0 - {} - else - data = ActiveSupport::JSON.decode(request.body) - data = {:_json => data} unless data.is_a?(Hash) - data.with_indifferent_access - end + when Proc + strategy.call(request.raw_post) + when :xml_simple, :xml_node + request.body.size == 0 ? {} : Hash.from_xml(request.body).with_indifferent_access + when :yaml + YAML.load(request.body) + when :json + if request.body.size == 0 + {} else - false + data = ActiveSupport::JSON.decode(request.body) + data = {:_json => data} unless data.is_a?(Hash) + data.with_indifferent_access + end + else + false end rescue Exception => e # YAML, XML or Ruby code block errors logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}" raise - { "body" => request.raw_post, - "content_type" => request.content_type, + { "body" => request.raw_post, + "content_type" => request.content_type, "content_length" => request.content_length, - "exception" => "#{e.message} (#{e.class})", - "backtrace" => e.backtrace } + "exception" => "#{e.message} (#{e.class})", + "backtrace" => e.backtrace } end def content_type_from_legacy_post_data_format_header(env) if x_post_format = env['HTTP_X_POST_DATA_FORMAT'] case x_post_format.to_s.downcase - when 'yaml' - return Mime::YAML - when 'xml' - return Mime::XML + when 'yaml' then return Mime::YAML + when 'xml' then return Mime::XML end end @@ -76,4 +74,4 @@ module ActionDispatch defined?(Rails.logger) ? Rails.logger : Logger.new($stderr) end end -end +end \ No newline at end of file -- cgit v1.2.3 From 17f66473bce0decb2e5ff23142d4046056ccca1b Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sun, 20 Dec 2009 18:50:47 -0800 Subject: AC::Head now doesn't have an unfulfilled Rendering dependency, and instead works just fine standalone (which means that ConditionalGet also doesn't have a Rendering dependency) --- actionpack/lib/abstract_controller/base.rb | 1 + actionpack/lib/abstract_controller/rendering.rb | 1 - actionpack/lib/action_controller/base.rb | 2 +- actionpack/lib/action_controller/metal.rb | 5 +++++ actionpack/lib/action_controller/metal/head.rb | 7 ++++++- actionpack/lib/action_controller/metal/redirecting.rb | 12 ++---------- actionpack/lib/action_dispatch/http/response.rb | 2 +- actionpack/lib/action_dispatch/http/status_codes.rb | 8 ++++++++ 8 files changed, 24 insertions(+), 14 deletions(-) diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index 905d04e20d..9d57c52429 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -5,6 +5,7 @@ module AbstractController class Base attr_internal :response_body attr_internal :action_name + attr_internal :formats class << self attr_reader :abstract diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index 8ef2526df0..f4e1580977 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -16,7 +16,6 @@ module AbstractController include AbstractController::Logger included do - attr_internal :formats extlib_inheritable_accessor :_view_paths self._view_paths ||= ActionView::PathSet.new end diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index e6cde7fd35..452f0cd4f0 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -89,7 +89,7 @@ module ActionController end if options[:status] - options[:status] = _interpret_status(options[:status]) + options[:status] = ActionDispatch::StatusCodes[options[:status]] end options[:update] = blk if block_given? diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index 8433be2320..93a19f8f93 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -68,6 +68,10 @@ module ActionController headers["Location"] = url end + def status=(status) + @_status = ActionDispatch::StatusCodes[status] + end + # :api: private def dispatch(name, env) @_env = env @@ -92,6 +96,7 @@ module ActionController def initialize(controller, action) @controller, @action = controller, action + @_formats = [Mime::HTML] end def call(env) diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb index 68fa0a0402..c82d9cf369 100644 --- a/actionpack/lib/action_controller/metal/head.rb +++ b/actionpack/lib/action_controller/metal/head.rb @@ -1,5 +1,7 @@ module ActionController module Head + include UrlFor + # Return a response that has no content (merely headers). The options # argument is interpreted to be a hash of header names and values. # This allows you to easily return a response that consists only of @@ -21,7 +23,10 @@ module ActionController headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s end - render :nothing => true, :status => status, :location => location + self.status = status + self.location = url_for(location) if location + self.content_type = Mime[formats.first] + self.response_body = " " end end end \ No newline at end of file diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index d101f920e3..39dc23024c 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -62,9 +62,9 @@ module ActionController private def _extract_redirect_to_status(options, response_status) status = if options.is_a?(Hash) && options.key?(:status) - _interpret_status(options.delete(:status)) + ActionDispatch::StatusCodes[options.delete(:status)] elsif response_status.key?(:status) - _interpret_status(response_status[:status]) + ActionDispatch::StatusCodes[response_status[:status]] else 302 end @@ -86,13 +86,5 @@ module ActionController url_for(options) end.gsub(/[\r\n]/, '') end - - def _interpret_status(status) - if status.is_a?(Symbol) - (ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[status] || 500) - else - status.to_i - end - end end end diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 6e63fc0067..6dc563264f 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -60,7 +60,7 @@ module ActionDispatch # :nodoc: end def status=(status) - @status = status.to_i + @status = ActionDispatch::StatusCodes[status] end # The response code of the request diff --git a/actionpack/lib/action_dispatch/http/status_codes.rb b/actionpack/lib/action_dispatch/http/status_codes.rb index ea1475e827..3d6ee685ea 100644 --- a/actionpack/lib/action_dispatch/http/status_codes.rb +++ b/actionpack/lib/action_dispatch/http/status_codes.rb @@ -14,6 +14,14 @@ module ActionDispatch 510 => "Not Extended" }).freeze + def self.[](status) + if status.is_a?(Symbol) + SYMBOL_TO_STATUS_CODE[status] || 500 + else + status.to_i + end + end + # Provides a symbol-to-fixnum lookup for converting a symbol (like # :created or :not_implemented) into its corresponding HTTP status # code (like 200 or 501). -- cgit v1.2.3 From 15f95621d5b472959215cdc258491934e5f05f2e Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 20 Dec 2009 21:11:42 -0600 Subject: We don't need AD parse_config --- actionpack/lib/action_dispatch.rb | 1 - actionpack/lib/action_dispatch/http/utils.rb | 20 -------------------- 2 files changed, 21 deletions(-) delete mode 100644 actionpack/lib/action_dispatch/http/utils.rb diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index a7be49a273..48b5652a89 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -38,7 +38,6 @@ module ActionDispatch autoload :Request autoload :Response autoload :StatusCodes - autoload :Utils end deferrable do diff --git a/actionpack/lib/action_dispatch/http/utils.rb b/actionpack/lib/action_dispatch/http/utils.rb deleted file mode 100644 index e04a39935e..0000000000 --- a/actionpack/lib/action_dispatch/http/utils.rb +++ /dev/null @@ -1,20 +0,0 @@ -module ActionDispatch - module Utils - # TODO: Pull this into rack core - # http://github.com/halorgium/rack/commit/feaf071c1de743fbd10bc316830180a9af607278 - def parse_config(config) - if config =~ /\.ru$/ - cfgfile = ::File.read(config) - if cfgfile[/^#\\(.*)/] - opts.parse! $1.split(/\s+/) - end - inner_app = eval "Rack::Builder.new {( " + cfgfile + "\n )}.to_app", - nil, config - else - require config - inner_app = Object.const_get(::File.basename(config, '.rb').capitalize) - end - end - module_function :parse_config - end -end -- cgit v1.2.3 From 3506cb730b89dee8a5067329b5aac707708426db Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 20 Dec 2009 19:40:18 -0800 Subject: Mark the wild controller route as legacy an comment it out --- .../lib/rails/generators/rails/app/templates/config/routes.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb index e0f9ca8a12..635ee8d87a 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb @@ -52,9 +52,7 @@ ActionController::Routing::Routes.draw do |map| # See how all your routes lay out with "rake routes" - # Install the default route as the lowest priority. - # Note: The default route make all actions in every controller accessible - # via GET requests. You should consider removing or commenting it out if - # you're using named routes and resources. - match ':controller(/:action(/:id(.:format)))' + # This is a legacy wild controller route that's not recommended for RESTful applications. + # Note: This route will make all actions in every controller accessible via GET requests. + # match ':controller(/:action(/:id(.:format)))' end -- cgit v1.2.3 From 3ff9e9ee147b682cb13aed4c057e750228892f42 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 20 Dec 2009 20:37:36 -0800 Subject: Its now possible to use match 'stuff' => 'what#stuff' instead of using the :to for simple routes --- actionpack/lib/action_dispatch/routing/mapper.rb | 10 +++++++--- actionpack/test/dispatch/routing_test.rb | 16 +++++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 57e992d7dc..46163706c3 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -39,9 +39,13 @@ module ActionDispatch end def match(*args) - options = args.extract_options! - - path = args.first + if args.one? && args.first.is_a?(Hash) + path = args.first.keys.first + options = { :to => args.first.values.first } + else + path = args.first + options = args.extract_options! + end conditions, defaults = {}, {} diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 7145a0c530..7058bc2ea0 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -22,6 +22,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest delete 'logout', :to => :destroy, :as => :logout end + match 'account/logout' => redirect("/logout") match 'account/login', :to => redirect("/login") match 'account/modulo/:name', :to => redirect("/%{name}s") @@ -37,11 +38,11 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end constraints(:ip => /192\.168\.1\.\d\d\d/) do - get 'admin', :to => "queenbee#index" + get 'admin' => "queenbee#index" end constraints ::TestRoutingMapper::IpRestrictor do - get 'admin/accounts', :to => "queenbee#accounts" + get 'admin/accounts' => "queenbee#accounts" end resources :projects, :controller => :project do @@ -85,7 +86,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end - match 'sprockets.js', :to => ::TestRoutingMapper::SprocketsApp + match 'sprockets.js' => ::TestRoutingMapper::SprocketsApp match 'people/:id/update', :to => 'people#update', :as => :update_person match '/projects/:project_id/people/:id/update', :to => 'people#update', :as => :update_project_person @@ -148,6 +149,15 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_logout_redirect_without_to + with_test_routes do + get '/account/logout' + assert_equal 301, @response.status + assert_equal 'http://www.example.com/logout', @response.headers['Location'] + assert_equal 'Moved Permanently', @response.body + end + end + def test_redirect_modulo with_test_routes do get '/account/modulo/name' -- cgit v1.2.3 From f9a4cf15627ba0c905bf0ab3de2b49b515ac197d Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 20 Dec 2009 20:45:18 -0800 Subject: Show the new short-form in an example --- railties/lib/rails/generators/rails/app/templates/config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb index 635ee8d87a..0d1b6bab4f 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb @@ -3,7 +3,7 @@ ActionController::Routing::Routes.draw do |map| # first created -> highest priority. # Sample of regular route: - # match 'products/:id', :to => 'catalog#view' + # match 'products/:id' => 'catalog#view' # Keep in mind you can assign values other than :controller and :action # Sample of named route: -- cgit v1.2.3 From f09ad263cabe2e781c1994b85375fee8deba4317 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 20 Dec 2009 20:50:25 -0800 Subject: Turn filter_parameter_logging on by default for password and password_confirmation and remove contentless comments --- .../rails/app/templates/app/controllers/application_controller.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb index 6635a3f487..e7991fff92 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb +++ b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb @@ -2,9 +2,7 @@ # Likewise, all the methods added will be available for all controllers. class ApplicationController < ActionController::Base - helper :all # include all helpers, all the time - protect_from_forgery # See ActionController::RequestForgeryProtection for details - - # Scrub sensitive parameters from your log - # filter_parameter_logging :password + helper :all + protect_from_forgery + filter_parameter_logging :password, :password_confirmation end -- cgit v1.2.3 From bf03ddc6365c8ea83647b5f315a34aa5c6b65df1 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 21 Dec 2009 13:18:11 -0800 Subject: Missed changelog entry for :inverse_of --- activerecord/CHANGELOG | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 1f7a95a53a..be4d197f99 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,12 @@ *Edge* +* Association inverses for belongs_to, has_one, and has_many. Optimization to reduce database queries. #3533 [Murray Steele] + + # post.comments sets each comment's post without needing to :include + class Post < ActiveRecord::Base + has_many :comments, :inverse_of => :post + end + * MySQL: add_ and change_column support positioning. #3286 [Ben Marini] * Reset your Active Record counter caches with the reset_counter_cache class method. #1211 [Mike Breen, Gabe da Silveira] -- cgit v1.2.3 From 3e33985e19a51a4ae824bc5765cbab84f5090021 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 21 Dec 2009 17:27:30 -0600 Subject: Update CI bundler --- ci/geminstaller.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/geminstaller.yml b/ci/geminstaller.yml index 776ae8d98d..3dc2000bf9 100644 --- a/ci/geminstaller.yml +++ b/ci/geminstaller.yml @@ -5,4 +5,4 @@ gems: - name: rubygems-update version: >= 1.3.5 - name: bundler - version: >= 0.6.0 + version: >= 0.7.1 -- cgit v1.2.3 From f82e1046f893c80c6234495f1bca28b4fb6520c9 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 21 Dec 2009 17:29:59 -0600 Subject: reset_session needs to be a real method so flash can override it --- .../lib/action_controller/metal/rack_delegation.rb | 6 +++++- actionpack/test/controller/flash_test.rb | 24 +++++++++++----------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/actionpack/lib/action_controller/metal/rack_delegation.rb b/actionpack/lib/action_controller/metal/rack_delegation.rb index 5141918499..833475cff7 100644 --- a/actionpack/lib/action_controller/metal/rack_delegation.rb +++ b/actionpack/lib/action_controller/metal/rack_delegation.rb @@ -3,7 +3,7 @@ module ActionController extend ActiveSupport::Concern included do - delegate :session, :reset_session, :to => "@_request" + delegate :session, :to => "@_request" delegate :headers, :status=, :location=, :content_type=, :status, :location, :content_type, :to => "@_response" attr_internal :request @@ -24,5 +24,9 @@ module ActionController response.body = body if response super end + + def reset_session + @_request.reset_session + end end end diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb index 1f5be431ac..a9b60386f1 100644 --- a/actionpack/test/controller/flash_test.rb +++ b/actionpack/test/controller/flash_test.rb @@ -34,7 +34,7 @@ class FlashTest < ActionController::TestCase flash.keep render :inline => "hello" end - + def use_flash_and_update_it flash.update("this" => "hello again") @flash_copy = {}.update flash @@ -76,11 +76,11 @@ class FlashTest < ActionController::TestCase def redirect_with_alert redirect_to '/nowhere', :alert => "Beware the nowheres!" end - + def redirect_with_notice redirect_to '/somewhere', :notice => "Good luck in the somewheres!" end - + def redirect_with_other_flashes redirect_to '/wonderland', :flash => { :joyride => "Horses!" } end @@ -101,7 +101,7 @@ class FlashTest < ActionController::TestCase def test_keep_flash get :set_flash - + get :use_flash_and_keep_it assert_equal "hello", assigns["flash_copy"]["that"] assert_equal "hello", assigns["flashy"] @@ -112,7 +112,7 @@ class FlashTest < ActionController::TestCase get :use_flash assert_nil assigns["flash_copy"]["that"], "On third flash" end - + def test_flash_now get :set_flash_now assert_equal "hello", assigns["flash_copy"]["that"] @@ -123,8 +123,8 @@ class FlashTest < ActionController::TestCase assert_nil assigns["flash_copy"]["that"] assert_nil assigns["flash_copy"]["foo"] assert_nil assigns["flashy"] - end - + end + def test_update_flash get :set_flash get :use_flash_and_update_it @@ -140,7 +140,7 @@ class FlashTest < ActionController::TestCase assert_equal "hello", assigns["flashy_that"] assert_equal "good-bye", assigns["flashy_this"] assert_nil assigns["flashy_that_reset"] - end + end def test_does_not_set_the_session_if_the_flash_is_empty get :std_action @@ -165,24 +165,24 @@ class FlashTest < ActionController::TestCase assert_equal(:foo_indeed, flash.discard(:foo)) # valid key passed assert_nil flash.discard(:unknown) # non existant key passed assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard()) # nothing passed - assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard(nil)) # nothing passed + assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard(nil)) # nothing passed assert_equal(:foo_indeed, flash.keep(:foo)) # valid key passed assert_nil flash.keep(:unknown) # non existant key passed assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep()) # nothing passed - assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep(nil)) # nothing passed + assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep(nil)) # nothing passed end def test_redirect_to_with_alert get :redirect_with_alert assert_equal "Beware the nowheres!", @controller.send(:flash)[:alert] end - + def test_redirect_to_with_notice get :redirect_with_notice assert_equal "Good luck in the somewheres!", @controller.send(:flash)[:notice] end - + def test_redirect_to_with_other_flashes get :redirect_with_other_flashes assert_equal "Horses!", @controller.send(:flash)[:joyride] -- cgit v1.2.3 From 715dd10961cefd1d8039cb81f47788d0a5b97d0d Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 21 Dec 2009 17:34:53 -0600 Subject: Less annoying RoutingError message --- actionpack/lib/action_dispatch/routing/route_set.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index bf2443c1be..a4dc5e0956 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -5,7 +5,7 @@ module ActionDispatch module Routing class RouteSet #:nodoc: NotFound = lambda { |env| - raise ActionController::RoutingError, "No route matches #{env['PATH_INFO'].inspect} with #{env.inspect}" + raise ActionController::RoutingError, "No route matches #{env['PATH_INFO'].inspect}" } PARAMETERS_KEY = 'action_dispatch.request.path_parameters' @@ -426,7 +426,7 @@ module ActionDispatch end end - raise ActionController::RoutingError, "No route matches #{path.inspect} with #{environment.inspect}" + raise ActionController::RoutingError, "No route matches #{path.inspect}" end end end -- cgit v1.2.3 From 880688a499b4066d6654a4cd11b76d14cc62865e Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 21 Dec 2009 17:47:03 -0600 Subject: Default route was removed from default route config, patch up failing tests. --- railties/test/application/routing_test.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb index decde056fd..1add941ee0 100644 --- a/railties/test/application/routing_test.rb +++ b/railties/test/application/routing_test.rb @@ -28,6 +28,12 @@ module ApplicationTests end RUBY + app_file 'config/routes.rb', <<-RUBY + ActionController::Routing::Routes.draw do |map| + match ':controller(/:action)' + end + RUBY + get '/foo' assert_equal 'foo', last_response.body end @@ -49,6 +55,12 @@ module ApplicationTests end RUBY + app_file 'config/routes.rb', <<-RUBY + ActionController::Routing::Routes.draw do |map| + match ':controller(/:action)' + end + RUBY + get '/foo' assert_equal 'foo', last_response.body @@ -75,6 +87,12 @@ module ApplicationTests end RUBY + app_file 'config/routes.rb', <<-RUBY + ActionController::Routing::Routes.draw do |map| + match ':controller(/:action)' + end + RUBY + get '/foo' assert_equal 'foo', last_response.body -- cgit v1.2.3 From cf66d16bdfcb46c217ea06c05b8e0c5dfff73889 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 21 Dec 2009 15:49:52 -0800 Subject: Its cookie_verifier_secret --- .../app/templates/config/initializers/cookie_verification_secret.rb.tt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt index 9808ea6a2d..9f05cd5a31 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt @@ -4,4 +4,4 @@ # If you change this key, all old signed cookies will become invalid! # Make sure the secret is at least 30 characters and all random, # no regular words or you'll be exposed to dictionary attacks. -ActionController::Base.cookie_verification_secret = '<%= app_secret %>'; +ActionController::Base.cookie_verifier_secret = '<%= app_secret %>'; -- cgit v1.2.3 From 36624b2c709925bcabb49d12082b9dd9d28c4c5c Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 21 Dec 2009 15:55:59 -0800 Subject: Give the builtin controllers their own routes.rb now that the legacy catch-all is gone --- railties/builtin/routes.rb | 3 +++ railties/lib/rails/application.rb | 1 + railties/lib/rails/configuration.rb | 4 ++++ 3 files changed, 8 insertions(+) create mode 100644 railties/builtin/routes.rb diff --git a/railties/builtin/routes.rb b/railties/builtin/routes.rb new file mode 100644 index 0000000000..26a0daaa8c --- /dev/null +++ b/railties/builtin/routes.rb @@ -0,0 +1,3 @@ +ActionController::Routing::Routes.draw do |map| + match '/rails/info/properties' => "rails::info#properties" +end \ No newline at end of file diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index e65c20de2c..498fd6a723 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -418,6 +418,7 @@ module Rails initializer :initialize_routing do next unless configuration.frameworks.include?(:action_controller) route_configuration_files << configuration.routes_configuration_file + route_configuration_files << configuration.builtin_routes_configuration_file reload_routes! end # diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb index 0fa42091dd..bf5b9478cc 100644 --- a/railties/lib/rails/configuration.rb +++ b/railties/lib/rails/configuration.rb @@ -158,6 +158,10 @@ module Rails @routes_configuration_file ||= File.join(root, 'config', 'routes.rb') end + def builtin_routes_configuration_file + @builtin_routes_configuration_file ||= File.join(RAILTIES_PATH, 'builtin', 'routes.rb') + end + def controller_paths @controller_paths ||= begin paths = [File.join(root, 'app', 'controllers')] -- cgit v1.2.3 From a110ff0fca45c6cc74c0da5d8dcdabeed43e78b1 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 21 Dec 2009 16:03:04 -0800 Subject: Dont introspect inline templates for the logger and cleanup a few styling issues --- actionpack/lib/abstract_controller/rendering.rb | 9 ++++----- actionpack/lib/action_view/render/rendering.rb | 22 +++++++++------------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index f4e1580977..64a8a5f241 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -125,8 +125,8 @@ module AbstractController if options.key?(:text) options[:_template] = ActionView::Template::Text.new(options[:text], format_for_text) elsif options.key?(:inline) - handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb") - template = ActionView::Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {}) + handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb") + template = ActionView::Template.new(options[:inline], "inline template", handler, {}) options[:_template] = template elsif options.key?(:template) options[:_template_name] = options[:template] @@ -194,9 +194,8 @@ module AbstractController # otherwise, process the parameter into a ViewPathSet. def view_paths=(paths) clear_template_caches! - self._view_paths = paths.is_a?(ActionView::PathSet) ? - paths : ActionView::Base.process_view_paths(paths) + self._view_paths = paths.is_a?(ActionView::PathSet) ? paths : ActionView::Base.process_view_paths(paths) end end end -end +end \ No newline at end of file diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb index 7006a5b968..d4d16b4d98 100644 --- a/actionpack/lib/action_view/render/rendering.rb +++ b/actionpack/lib/action_view/render/rendering.rb @@ -78,12 +78,12 @@ module ActionView end def _render_inline(inline, layout, options) - handler = Template.handler_class_for_extension(options[:type] || "erb") - template = Template.new(options[:inline], - "inline #{options[:inline].inspect}", handler, {}) + handler = Template.handler_class_for_extension(options[:type] || "erb") + template = Template.new(options[:inline], "inline template", handler, {}) - locals = options[:locals] + locals = options[:locals] content = template.render(self, locals) + _render_text(content, layout, locals) end @@ -91,6 +91,7 @@ module ActionView content = layout.render(self, locals) do |*name| _layout_for(*name) { content } end if layout + content end @@ -113,21 +114,16 @@ module ActionView msg end - locals = options[:locals] || {} - - content = if partial - _render_partial_object(template, options) - else - template.render(self, locals) - end - + locals = options[:locals] || {} + content = partial ? _render_partial_object(template, options) : template.render(self, locals) @_content_for[:layout] = content if layout @_layout = layout.identifier logger.info("Rendering template within #{layout.inspect}") if logger - content = layout.render(self, locals) {|*name| _layout_for(*name) } + content = layout.render(self, locals) { |*name| _layout_for(*name) } end + content end end -- cgit v1.2.3 From b0b4ae970c1cb586235bcfbc669d43475c7fe684 Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Mon, 21 Dec 2009 16:03:20 -0800 Subject: test.rb, dev.rb, and production.rb just reopen the Application class; no more hax required --- railties/lib/rails/application.rb | 25 +++++----------- .../templates/config/environments/development.rb | 17 ----------- .../config/environments/development.rb.tt | 19 +++++++++++++ .../templates/config/environments/production.rb | 31 -------------------- .../templates/config/environments/production.rb.tt | 33 ++++++++++++++++++++++ .../app/templates/config/environments/test.rb | 27 ------------------ .../app/templates/config/environments/test.rb.tt | 29 +++++++++++++++++++ railties/test/initializer/initialize_i18n_test.rb | 11 ++++---- railties/test/initializer/path_test.rb | 8 +++--- railties/test/isolation/abstract_unit.rb | 7 +++++ 10 files changed, 104 insertions(+), 103 deletions(-) delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/environments/development.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/environments/production.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt delete mode 100644 railties/lib/rails/generators/rails/app/templates/config/environments/test.rb create mode 100644 railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 498fd6a723..d83c65da8d 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -9,7 +9,13 @@ module Rails end def new - @instance ||= super + @instance ||= begin + begin + require config.environment_path + rescue LoadError + end + super + end end def config @@ -119,23 +125,6 @@ module Rails @app.call(env) end - - # Loads the environment specified by Configuration#environment_path, which - # is typically one of development, test, or production. - initializer :load_environment do - next unless File.file?(config.environment_path) - - config = self.config - - Kernel.class_eval do - meth = instance_method(:config) if Object.respond_to?(:config) - define_method(:config) { config } - require config.environment_path - remove_method :config - define_method(:config, &meth) if meth - end - end - # Set the $LOAD_PATH based on the value of # Configuration#load_paths. Duplicates are removed. initializer :set_load_path do diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb deleted file mode 100644 index 85c9a6080e..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb +++ /dev/null @@ -1,17 +0,0 @@ -# Settings specified here will take precedence over those in config/environment.rb - -# In the development environment your application's code is reloaded on -# every request. This slows down response time but is perfect for development -# since you don't have to restart the webserver when you make code changes. -config.cache_classes = false - -# Log error messages when you accidentally call methods on nil. -config.whiny_nils = true - -# Show full error reports and disable caching -config.action_controller.consider_all_requests_local = true -config.action_view.debug_rjs = true -config.action_controller.perform_caching = false - -# Don't care if the mailer can't send -config.action_mailer.raise_delivery_errors = false \ No newline at end of file diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt new file mode 100644 index 0000000000..2b3940d47f --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt @@ -0,0 +1,19 @@ +class <%= app_const %> + # Settings specified here will take precedence over those in config/environment.rb + + # In the development environment your application's code is reloaded on + # every request. This slows down response time but is perfect for development + # since you don't have to restart the webserver when you make code changes. + config.cache_classes = false + + # Log error messages when you accidentally call methods on nil. + config.whiny_nils = true + + # Show full error reports and disable caching + config.action_controller.consider_all_requests_local = true + config.action_view.debug_rjs = true + config.action_controller.perform_caching = false + + # Don't care if the mailer can't send + config.action_mailer.raise_delivery_errors = false +end \ No newline at end of file diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb deleted file mode 100644 index 377b9207c7..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb +++ /dev/null @@ -1,31 +0,0 @@ -# Settings specified here will take precedence over those in config/environment.rb - -# The production environment is meant for finished, "live" apps. -# Code is not reloaded between requests -config.cache_classes = true - -# Full error reports are disabled and caching is turned on -config.action_controller.consider_all_requests_local = false -config.action_controller.perform_caching = true - -# See everything in the log (default is :info) -# config.log_level = :debug - -# Use a different logger for distributed setups -# config.logger = SyslogLogger.new - -# Use a different cache store in production -# config.cache_store = :mem_cache_store - -# Disable Rails's static asset server -# In production, Apache or nginx will already do this -config.serve_static_assets = false - -# Enable serving of images, stylesheets, and javascripts from an asset server -# config.action_controller.asset_host = "http://assets.example.com" - -# Disable delivery errors, bad email addresses will be ignored -# config.action_mailer.raise_delivery_errors = false - -# Enable threaded mode -# config.threadsafe! diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt new file mode 100644 index 0000000000..eff5801526 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt @@ -0,0 +1,33 @@ +class <%= app_const %> + # Settings specified here will take precedence over those in config/environment.rb + + # The production environment is meant for finished, "live" apps. + # Code is not reloaded between requests + config.cache_classes = true + + # Full error reports are disabled and caching is turned on + config.action_controller.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # See everything in the log (default is :info) + # config.log_level = :debug + + # Use a different logger for distributed setups + # config.logger = SyslogLogger.new + + # Use a different cache store in production + # config.cache_store = :mem_cache_store + + # Disable Rails's static asset server + # In production, Apache or nginx will already do this + config.serve_static_assets = false + + # Enable serving of images, stylesheets, and javascripts from an asset server + # config.action_controller.asset_host = "http://assets.example.com" + + # Disable delivery errors, bad email addresses will be ignored + # config.action_mailer.raise_delivery_errors = false + + # Enable threaded mode + # config.threadsafe! +end \ No newline at end of file diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb deleted file mode 100644 index 496eb9572b..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb +++ /dev/null @@ -1,27 +0,0 @@ -# Settings specified here will take precedence over those in config/environment.rb - -# The test environment is used exclusively to run your application's -# test suite. You never need to work with it otherwise. Remember that -# your test database is "scratch space" for the test suite and is wiped -# and recreated between test runs. Don't rely on the data there! -config.cache_classes = true - -# Log error messages when you accidentally call methods on nil. -config.whiny_nils = true - -# Show full error reports and disable caching -config.action_controller.consider_all_requests_local = true -config.action_controller.perform_caching = false - -# Disable request forgery protection in test environment -config.action_controller.allow_forgery_protection = false - -# Tell Action Mailer not to deliver emails to the real world. -# The :test delivery method accumulates sent emails in the -# ActionMailer::Base.deliveries array. -config.action_mailer.delivery_method = :test - -# Use SQL instead of Active Record's schema dumper when creating the test database. -# This is necessary if your schema can't be completely dumped by the schema dumper, -# like if you have constraints or database-specific column types -# config.active_record.schema_format = :sql \ No newline at end of file diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt new file mode 100644 index 0000000000..3246c7b5f5 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt @@ -0,0 +1,29 @@ +class <%= app_const %> + # Settings specified here will take precedence over those in config/environment.rb + + # The test environment is used exclusively to run your application's + # test suite. You never need to work with it otherwise. Remember that + # your test database is "scratch space" for the test suite and is wiped + # and recreated between test runs. Don't rely on the data there! + config.cache_classes = true + + # Log error messages when you accidentally call methods on nil. + config.whiny_nils = true + + # Show full error reports and disable caching + config.action_controller.consider_all_requests_local = true + config.action_controller.perform_caching = false + + # Disable request forgery protection in test environment + config.action_controller.allow_forgery_protection = false + + # Tell Action Mailer not to deliver emails to the real world. + # The :test delivery method accumulates sent emails in the + # ActionMailer::Base.deliveries array. + config.action_mailer.delivery_method = :test + + # Use SQL instead of Active Record's schema dumper when creating the test database. + # This is necessary if your schema can't be completely dumped by the schema dumper, + # like if you have constraints or database-specific column types + # config.active_record.schema_format = :sql +end \ No newline at end of file diff --git a/railties/test/initializer/initialize_i18n_test.rb b/railties/test/initializer/initialize_i18n_test.rb index 076816d73b..472566378d 100644 --- a/railties/test/initializer/initialize_i18n_test.rb +++ b/railties/test/initializer/initialize_i18n_test.rb @@ -7,16 +7,15 @@ module InitializerTests def setup build_app boot_rails - require "rails" end # test_config_defaults_and_settings_should_be_added_to_i18n_defaults test "i18n config defaults and settings should be added to i18n defaults" do - Rails::Initializer.run do |c| - c.root = app_path - c.i18n.load_path << "my/other/locale.yml" - end - Rails.initialize! + add_to_config <<-RUBY + config.root = "#{app_path}" + config.i18n.load_path << "my/other/locale.yml" + RUBY + require "#{app_path}/config/environment" #{RAILS_FRAMEWORK_ROOT}/railties/test/fixtures/plugins/engines/engine/config/locales/en.yml assert_equal %W( diff --git a/railties/test/initializer/path_test.rb b/railties/test/initializer/path_test.rb index 1b58a58555..fa66ebcd83 100644 --- a/railties/test/initializer/path_test.rb +++ b/railties/test/initializer/path_test.rb @@ -7,14 +7,14 @@ class PathsTest < Test::Unit::TestCase build_app boot_rails require "rails" - Rails::Initializer.run do |config| - config.root = app_path + add_to_config <<-RUBY + config.root = "#{app_path}" config.frameworks = [:action_controller, :action_view, :action_mailer, :active_record] config.after_initialize do ActionController::Base.session_store = nil end - end - Rails.initialize! + RUBY + require "#{app_path}/config/environment" @paths = Rails.application.config.paths end diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index ba8b35d5cc..6562a31d9e 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -89,6 +89,13 @@ module TestHelpers end end + routes = File.read("#{app_path}/config/routes.rb") + if routes =~ /(\n\s*end\s*)\Z/ + File.open("#{app_path}/config/routes.rb", 'w') do |f| + f.puts $` + "\nmatch ':controller(/:action(/:id))(.:format)'\n" + $1 + end + end + add_to_config 'config.action_controller.session = { :key => "_myapp_session", :secret => "bac838a849c1d5c4de2e6a50af826079" }' end -- cgit v1.2.3 From d982fe2b2fcc4e197087901774e2f21467c3cec1 Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Mon, 21 Dec 2009 16:35:54 -0800 Subject: Replace reopening the class with App.configure as an alias to class_eval --- railties/lib/rails/application.rb | 4 ++++ .../rails/app/templates/config/environments/development.rb.tt | 2 +- .../rails/app/templates/config/environments/production.rb.tt | 2 +- .../generators/rails/app/templates/config/environments/test.rb.tt | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index d83c65da8d..695a1d7c87 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -66,6 +66,10 @@ module Rails self.class.config end + class << self + alias configure class_eval + end + def root config.root end diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt index 2b3940d47f..b10103b436 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt @@ -1,4 +1,4 @@ -class <%= app_const %> +<%= app_const %>.configure do # Settings specified here will take precedence over those in config/environment.rb # In the development environment your application's code is reloaded on diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt index eff5801526..543a40108c 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt @@ -1,4 +1,4 @@ -class <%= app_const %> +<%= app_const %>.configure do # Settings specified here will take precedence over those in config/environment.rb # The production environment is meant for finished, "live" apps. diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt index 3246c7b5f5..428fa35633 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt @@ -1,4 +1,4 @@ -class <%= app_const %> +<%= app_const %>.configure do # Settings specified here will take precedence over those in config/environment.rb # The test environment is used exclusively to run your application's -- cgit v1.2.3 From a43a9c81cf3d85e8dca6afdd92307ce153643ebe Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 21 Dec 2009 16:41:02 -0800 Subject: Dont need to specify password_confirmation, that happens automatically --- .../rails/app/templates/app/controllers/application_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb index e7991fff92..9889b52893 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb +++ b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb @@ -4,5 +4,5 @@ class ApplicationController < ActionController::Base helper :all protect_from_forgery - filter_parameter_logging :password, :password_confirmation + filter_parameter_logging :password end -- cgit v1.2.3 From be225adafbec267353fa7260179b0ce5a72e4283 Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Mon, 21 Dec 2009 16:48:44 -0800 Subject: Fix ActionMailer. The fact that ActionMailer::Base does not inherit from AbstractController::Base is either a bug or we need to re-evaluate the requirements of the mixins. --- actionmailer/lib/action_mailer/base.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index a69838fe43..40aff7f0d8 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -271,6 +271,8 @@ module ActionMailer #:nodoc: class_inheritable_accessor :view_paths self.view_paths = [] + attr_internal :formats + cattr_accessor :logger @@raise_delivery_errors = true @@ -452,6 +454,7 @@ module ActionMailer #:nodoc: # remain uninitialized (useful when you only need to invoke the "receive" # method, for instance). def initialize(method_name=nil, *parameters) #:nodoc: + @_formats = [] @_response_body = nil super() create!(method_name, *parameters) if method_name -- cgit v1.2.3 From 76e732a7be690f09d1e5d6c97138a6eb7d263423 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 21 Dec 2009 16:53:52 -0800 Subject: Fix the documentation for root :to. It should use a fully qualified controller#action syntax (Closes #3606) --- railties/lib/rails/generators/rails/app/templates/config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb index 0d1b6bab4f..e28ff42bdd 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb @@ -48,7 +48,7 @@ ActionController::Routing::Routes.draw do |map| # You can have the root of your site routed with "root" # just remember to delete public/index.html. - # root :to => "welcome" + # root :to => "welcome#index" # See how all your routes lay out with "rake routes" -- cgit v1.2.3 From 2e571e8f99e5e2712c0bc2558df8d62996204b03 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 21 Dec 2009 16:57:23 -0800 Subject: Blog -> Blog::Application. Leave the toplevel module up for grabs. --- .../rails/generators/rails/app/app_generator.rb | 2 +- .../rails/app/templates/config/application.rb | 62 +++++++++++----------- railties/test/application/configuration_test.rb | 2 +- railties/test/isolation/abstract_unit.rb | 2 +- railties/test/plugins/configuration_test.rb | 4 +- 5 files changed, 37 insertions(+), 35 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index ae18fa843b..6f71f7005f 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -182,7 +182,7 @@ module Rails::Generators end def app_const - @app_const ||= app_name.classify + @app_const ||= "#{app_name.classify}::Application" end def app_secret diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb index 2c17de2a23..15dc553e53 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb @@ -1,41 +1,43 @@ require File.expand_path('../boot', __FILE__) -class <%= app_const %> < Rails::Application - # Settings in config/environments/* take precedence over those specified here. - # Application configuration should go into files in config/initializers - # -- all .rb files in that directory are automatically loaded. +module <%= app_name.classify %> + class Application < Rails::Application + # Settings in config/environments/* take precedence over those specified here. + # Application configuration should go into files in config/initializers + # -- all .rb files in that directory are automatically loaded. - # Add additional load paths for your own custom dirs - # config.load_paths += %W( #{root}/extras ) + # Add additional load paths for your own custom dirs + # config.load_paths += %W( #{root}/extras ) - # Only load the plugins named here, in the order given (default is alphabetical). - # :all can be used as a placeholder for all plugins not explicitly named - # config.plugins = [ :exception_notification, :ssl_requirement, :all ] + # Only load the plugins named here, in the order given (default is alphabetical). + # :all can be used as a placeholder for all plugins not explicitly named + # config.plugins = [ :exception_notification, :ssl_requirement, :all ] - # Skip frameworks you're not going to use. To use Rails without a database, - # you must remove the Active Record framework. + # Skip frameworks you're not going to use. To use Rails without a database, + # you must remove the Active Record framework. <% if options[:skip_activerecord] -%> - config.frameworks -= [ :active_record ] + config.frameworks -= [ :active_record ] <% else -%> - # config.frameworks -= [ :active_record, :active_resource, :action_mailer ] + # config.frameworks -= [ :active_record, :active_resource, :action_mailer ] - # Activate observers that should always be running - # config.active_record.observers = :cacher, :garbage_collector, :forum_observer + # Activate observers that should always be running + # config.active_record.observers = :cacher, :garbage_collector, :forum_observer <% end -%> - # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. - # Run "rake -D time" for a list of tasks for finding time zone names. - config.time_zone = 'UTC' - - # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. - # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')] - # config.i18n.default_locale = :de - - # Configure generators values. Many other options are available, be sure to - # check the documentation. - # config.generators do |g| - # g.orm :active_record - # g.template_engine :erb - # g.test_framework :test_unit, :fixture => true - # end + # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. + # Run "rake -D time" for a list of tasks for finding time zone names. + config.time_zone = 'UTC' + + # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. + # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')] + # config.i18n.default_locale = :de + + # Configure generators values. Many other options are available, be sure to + # check the documentation. + # config.generators do |g| + # g.orm :active_record + # g.template_engine :erb + # g.test_framework :test_unit, :fixture => true + # end + end end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 89337b7f66..83e1401993 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -60,7 +60,7 @@ module ApplicationTests RUBY require "#{app_path}/config/application" - assert AppTemplate.configuration.action_controller.allow_concurrency + assert AppTemplate::Application.configuration.action_controller.allow_concurrency end test "the application can be marked as threadsafe when there are no frameworks" do diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index 6562a31d9e..c169c80bea 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -135,7 +135,7 @@ module TestHelpers def add_to_config(str) environment = File.read("#{app_path}/config/application.rb") - if environment =~ /(\n\s*end\s*)\Z/ + if environment =~ /(\n\s*end\s*end\s*)\Z/ File.open("#{app_path}/config/application.rb", 'w') do |f| f.puts $` + "\n#{str}\n" + $1 end diff --git a/railties/test/plugins/configuration_test.rb b/railties/test/plugins/configuration_test.rb index edf8bb37f5..5786316d1d 100644 --- a/railties/test/plugins/configuration_test.rb +++ b/railties/test/plugins/configuration_test.rb @@ -21,7 +21,7 @@ module PluginsTest test "plugin configurations are available in the application" do class Foo < Rails::Plugin ; config.foo.greetings = "hello" ; end require "#{app_path}/config/application" - assert_equal "hello", AppTemplate.config.foo.greetings + assert_equal "hello", AppTemplate::Application.config.foo.greetings end test "plugin config merges are deep" do @@ -33,4 +33,4 @@ module PluginsTest assert_equal "bar", MyApp.config.foo.bar end end -end \ No newline at end of file +end -- cgit v1.2.3 From 426348b48403f664cc10e8ec545b640e56c1c090 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 21 Dec 2009 20:15:27 -0600 Subject: Update routes.rb template to use App name --- railties/lib/rails/application.rb | 4 ++++ railties/lib/rails/generators/actions.rb | 2 +- railties/lib/rails/generators/rails/app/app_generator.rb | 6 +++--- .../rails/generators/rails/app/templates/config/routes.rb | 2 +- railties/test/application/initializer_test.rb | 10 ++++++++++ railties/test/application/routing_test.rb | 14 +++++++------- railties/test/initializer/initialize_i18n_test.rb | 1 + railties/test/initializer/path_test.rb | 1 + railties/test/paths_test.rb | 3 +-- 9 files changed, 29 insertions(+), 14 deletions(-) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 498fd6a723..4e21287496 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -41,6 +41,10 @@ module Rails end end + def routes + ActionController::Routing::Routes + end + def call(env) new.call(env) end diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index 2efdf29127..f95b15acce 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -273,7 +273,7 @@ module Rails # def route(routing_code) log :route, routing_code - sentinel = "ActionController::Routing::Routes.draw do |map|" + sentinel = "routes.draw do |map|" in_root do inject_into_file 'config/routes.rb', "\n #{routing_code}\n", { :after => sentinel, :verbose => false } diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index ae18fa843b..b8f2911021 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -62,9 +62,9 @@ module Rails::Generators empty_directory "config" inside "config" do - copy_file "routes.rb" - template "application.rb" - template "environment.rb" + template "routes.rb" + template "application.rb" + template "environment.rb" directory "environments" directory "initializers" diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb index 0d1b6bab4f..1959d3387f 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb @@ -1,4 +1,4 @@ -ActionController::Routing::Routes.draw do |map| +<%= app_const %>.routes.draw do |map| # The priority is based upon order of creation: # first created -> highest priority. diff --git a/railties/test/application/initializer_test.rb b/railties/test/application/initializer_test.rb index fa00d287ca..031fdc2e9f 100644 --- a/railties/test/application/initializer_test.rb +++ b/railties/test/application/initializer_test.rb @@ -15,6 +15,7 @@ module ApplicationTests Rails::Initializer.run do |config| config.root = app_path end + Object.const_set(:AppTemplate, Rails.application) Rails.initialize! assert $:.include?("#{app_path}/app/models") @@ -45,6 +46,7 @@ module ApplicationTests config.root = app_path config.eager_load_paths = "#{app_path}/lib" end + Object.const_set(:AppTemplate, Rails.application) Rails.initialize! @@ -55,6 +57,7 @@ module ApplicationTests app_file "config/environments/development.rb", "$initialize_test_set_from_env = 'success'" assert_nil $initialize_test_set_from_env Rails::Initializer.run { |config| config.root = app_path } + Object.const_set(:AppTemplate, Rails.application) Rails.initialize! assert_equal "success", $initialize_test_set_from_env end @@ -75,6 +78,7 @@ module ApplicationTests config.after_initialize { $test_after_initialize_block1 = "success" } config.after_initialize { $test_after_initialize_block2 = "congratulations" } end + Object.const_set(:AppTemplate, Rails.application) Rails.initialize! assert_equal "success", $test_after_initialize_block1 @@ -88,6 +92,7 @@ module ApplicationTests config.after_initialize # don't pass a block, this is what we're testing! config.after_initialize { $test_after_initialize_block2 = "congratulations" } end + Object.const_set(:AppTemplate, Rails.application) Rails.initialize! assert_equal "success", $test_after_initialize_block1 @@ -100,6 +105,7 @@ module ApplicationTests config.root = app_path config.i18n.default_locale = :de end + Object.const_set(:AppTemplate, Rails.application) Rails.initialize! assert_equal :de, I18n.default_locale @@ -137,6 +143,7 @@ module ApplicationTests config.root = app_path config.action_controller.session_store = :cookie_store end + Object.const_set(:AppTemplate, Rails.application) Rails.initialize! assert !Rails.application.config.middleware.include?(ActiveRecord::SessionStore) @@ -155,6 +162,7 @@ module ApplicationTests c.root = app_path c.action_controller.session_store = :active_record_store end + Object.const_set(:AppTemplate, Rails.application) Rails.initialize! expects = [ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActiveRecord::SessionStore] @@ -179,6 +187,7 @@ module ApplicationTests c.root = app_path c.frameworks -= [:action_view] end + Object.const_set(:AppTemplate, Rails.application) Rails.initialize! assert_equal nil, ActionMailer::Base.template_root @@ -189,6 +198,7 @@ module ApplicationTests Rails::Initializer.run do |c| c.root = app_path end + Object.const_set(:AppTemplate, Rails.application) Rails.initialize! assert_instance_of Pathname, Rails.root end diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb index 1add941ee0..7803794307 100644 --- a/railties/test/application/routing_test.rb +++ b/railties/test/application/routing_test.rb @@ -29,7 +29,7 @@ module ApplicationTests RUBY app_file 'config/routes.rb', <<-RUBY - ActionController::Routing::Routes.draw do |map| + AppTemplate.routes.draw do |map| match ':controller(/:action)' end RUBY @@ -56,7 +56,7 @@ module ApplicationTests RUBY app_file 'config/routes.rb', <<-RUBY - ActionController::Routing::Routes.draw do |map| + AppTemplate.routes.draw do |map| match ':controller(/:action)' end RUBY @@ -88,7 +88,7 @@ module ApplicationTests RUBY app_file 'config/routes.rb', <<-RUBY - ActionController::Routing::Routes.draw do |map| + AppTemplate.routes.draw do |map| match ':controller(/:action)' end RUBY @@ -110,7 +110,7 @@ module ApplicationTests RUBY app_file 'config/routes.rb', <<-RUBY - ActionController::Routing::Routes.draw do |map| + AppTemplate.routes.draw do |map| match 'foo', :to => 'foo#index' end RUBY @@ -125,7 +125,7 @@ module ApplicationTests RUBY plugin.write 'config/routes.rb', <<-RUBY - ActionController::Routing::Routes.draw do |map| + AppTemplate.routes.draw do |map| match 'bar', :to => 'bar#index' end RUBY @@ -152,7 +152,7 @@ module ApplicationTests RUBY app_file 'config/routes.rb', <<-RUBY - ActionController::Routing::Routes.draw do |map| + AppTemplate.routes.draw do |map| match 'foo', :to => 'foo#bar' end RUBY @@ -161,7 +161,7 @@ module ApplicationTests assert_equal 'bar', last_response.body app_file 'config/routes.rb', <<-RUBY - ActionController::Routing::Routes.draw do |map| + AppTemplate.routes.draw do |map| match 'foo', :to => 'foo#baz' end RUBY diff --git a/railties/test/initializer/initialize_i18n_test.rb b/railties/test/initializer/initialize_i18n_test.rb index 076816d73b..4642e53cfc 100644 --- a/railties/test/initializer/initialize_i18n_test.rb +++ b/railties/test/initializer/initialize_i18n_test.rb @@ -16,6 +16,7 @@ module InitializerTests c.root = app_path c.i18n.load_path << "my/other/locale.yml" end + Object.const_set(:AppTemplate, Rails.application) Rails.initialize! #{RAILS_FRAMEWORK_ROOT}/railties/test/fixtures/plugins/engines/engine/config/locales/en.yml diff --git a/railties/test/initializer/path_test.rb b/railties/test/initializer/path_test.rb index 1b58a58555..3724655c0f 100644 --- a/railties/test/initializer/path_test.rb +++ b/railties/test/initializer/path_test.rb @@ -14,6 +14,7 @@ class PathsTest < Test::Unit::TestCase ActionController::Base.session_store = nil end end + Object.const_set(:AppTemplate, Rails.application) Rails.initialize! @paths = Rails.application.config.paths end diff --git a/railties/test/paths_test.rb b/railties/test/paths_test.rb index c724799d64..d60d6177f6 100644 --- a/railties/test/paths_test.rb +++ b/railties/test/paths_test.rb @@ -2,7 +2,6 @@ require 'abstract_unit' require 'rails/paths' class PathsTest < ActiveSupport::TestCase - def setup @root = Rails::Application::Root.new("/foo/bar") end @@ -228,4 +227,4 @@ class PathsTest < ActiveSupport::TestCase @root.app.eager_load! assert_equal ["/foo/bar/app"], @root.load_paths end -end \ No newline at end of file +end -- cgit v1.2.3 From 8e48a5ef0ca488b2264acd2b38bdae14970c011f Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 21 Dec 2009 21:49:36 -0800 Subject: Add test for root --- actionpack/test/dispatch/routing_test.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 7058bc2ea0..1c7822358d 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -109,6 +109,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest scope ':access_token', :constraints => { :access_token => /\w{5,5}/ } do resources :rooms end + + root :to => 'projects#index' end end @@ -458,6 +460,13 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_root + with_test_routes do + get '/' + assert_equal 'projects#index', @response.body + end + end + private def with_test_routes real_routes, temp_routes = ActionController::Routing::Routes, Routes -- cgit v1.2.3 From 4964d3b02cd5c87d821ab7d41d243154c727185d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 22 Dec 2009 20:17:27 +0100 Subject: Make ActionMailer::Base inherit from AbstractController::Base Signed-off-by: Yehuda Katz --- actionmailer/lib/action_mailer/base.rb | 60 +++++++----------------- actionmailer/test/mail_service_test.rb | 8 ++-- actionmailer/test/url_test.rb | 4 +- actionpack/lib/abstract_controller/base.rb | 8 ++-- actionpack/lib/abstract_controller/logger.rb | 28 ----------- actionpack/lib/action_controller.rb | 1 + actionpack/lib/action_controller/base.rb | 1 + actionpack/lib/action_controller/metal/logger.rb | 34 ++++++++++++++ 8 files changed, 63 insertions(+), 81 deletions(-) create mode 100644 actionpack/lib/action_controller/metal/logger.rb diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 40aff7f0d8..b2c355c7ae 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -250,31 +250,21 @@ module ActionMailer #:nodoc: # ["text/html", "text/enriched", "text/plain"]. Items that appear first in the array have higher priority in the mail client # and appear last in the mime encoded message. You can also pick a different order from inside a method with # +implicit_parts_order+. - class Base + class Base < AbstractController::Base include AdvAttrAccessor, PartContainer, Quoting, Utils include AbstractController::Rendering include AbstractController::LocalizedCache include AbstractController::Layouts - include AbstractController::Helpers - helper ActionMailer::MailHelper - if Object.const_defined?(:ActionController) - include ActionController::UrlWriter - end + helper ActionMailer::MailHelper + include ActionController::UrlWriter include ActionMailer::DeprecatedBody private_class_method :new #:nodoc: - class_inheritable_accessor :view_paths - self.view_paths = [] - - attr_internal :formats - - cattr_accessor :logger - @@raise_delivery_errors = true cattr_accessor :raise_delivery_errors @@ -346,24 +336,13 @@ module ActionMailer #:nodoc: # have multiple mailer methods share the same template. adv_attr_accessor :template - # The mail and action_name instances referenced by this mailer. - attr_reader :mail, :action_name - - # Where the response body is stored. - attr_internal :response_body - # Override the mailer name, which defaults to an inflected version of the # mailer's class name. If you want to use a template in a non-standard # location, you can use this to specify that location. - attr_writer :mailer_name + adv_attr_accessor :mailer_name - def mailer_name(value = nil) - if value - @mailer_name = value - else - @mailer_name || self.class.mailer_name - end - end + # Expose the internal mail + attr_reader :mail # Alias controller_path to mailer_name so render :partial in views work. alias :controller_path :mailer_name @@ -453,18 +432,16 @@ module ActionMailer #:nodoc: # will be initialized according to the named method. If not, the mailer will # remain uninitialized (useful when you only need to invoke the "receive" # method, for instance). - def initialize(method_name=nil, *parameters) #:nodoc: - @_formats = [] - @_response_body = nil + def initialize(method_name=nil, *args) #:nodoc: super() - create!(method_name, *parameters) if method_name + process(method_name, *args) if method_name end - # Initialize the mailer via the given +method_name+. The body will be + # Process the mailer via the given +method_name+. The body will be # rendered and a new TMail::Mail object created. - def create!(method_name, *parameters) #:nodoc: + def process(method_name, *args) #:nodoc: initialize_defaults(method_name) - __send__(method_name, *parameters) + super # Create e-mail parts create_parts @@ -473,7 +450,7 @@ module ActionMailer #:nodoc: @subject ||= I18n.t(:subject, :scope => [:actionmailer, mailer_name, method_name], :default => method_name.humanize) - # build the mail object itself + # Build the mail object itself @mail = create_mail end @@ -488,7 +465,7 @@ module ActionMailer #:nodoc: logger.debug "\n#{mail.encoded}" end - ActiveSupport::Notifications.instrument(:deliver_mail, :mail => @mail) do + ActiveSupport::Notifications.instrument(:deliver_mail, :mail => mail) do begin self.delivery_method.perform_delivery(mail) if perform_deliveries rescue Exception => e # Net::SMTP errors or sendmail pipe errors @@ -510,23 +487,18 @@ module ActionMailer #:nodoc: @implicit_parts_order ||= @@default_implicit_parts_order.dup @mime_version ||= @@default_mime_version.dup if @@default_mime_version - @mailer_name ||= self.class.mailer_name + @mailer_name ||= self.class.mailer_name.dup @template ||= method_name - @action_name = @template @parts ||= [] @headers ||= {} @sent_on ||= Time.now - ActiveSupport::Deprecation.silence do - super # Run deprecation hooks - end + super # Run deprecation hooks end def create_parts - ActiveSupport::Deprecation.silence do - super # Run deprecation hooks - end + super # Run deprecation hooks if String === response_body @parts.unshift Part.new( diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index 697265b8ec..1cd3bc77c4 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -34,13 +34,13 @@ class TestMailer < ActionMailer::Base def from_with_name from "System " recipients "root@loudthinking.com" - body "Nothing to see here." + render :text => "Nothing to see here." end def from_without_name from "system@loudthinking.com" recipients "root@loudthinking.com" - body "Nothing to see here." + render :text => "Nothing to see here." end def cc_bcc(recipient) @@ -301,6 +301,7 @@ class TestMailer < ActionMailer::Base render :text => "testing" end + # This tests body calls accepeting a hash, which is deprecated. def body_ivar(recipient) recipients recipient subject "Body as a local variable" @@ -1043,7 +1044,8 @@ EOF end def test_body_is_stored_as_an_ivar - mail = TestMailer.create_body_ivar(@recipient) + mail = nil + ActiveSupport::Deprecation.silence { mail = TestMailer.create_body_ivar(@recipient) } assert_equal "body: foo\nbar: baz", mail.body end diff --git a/actionmailer/test/url_test.rb b/actionmailer/test/url_test.rb index 18eb355e7d..1140613132 100644 --- a/actionmailer/test/url_test.rb +++ b/actionmailer/test/url_test.rb @@ -12,8 +12,8 @@ class TestMailer < ActionMailer::Base @from = "system@loudthinking.com" @sent_on = Time.local(2004, 12, 12) - @body["recipient"] = recipient - @body["welcome_url"] = url_for :host => "example.com", :controller => "welcome", :action => "greeting" + @recipient = recipient + @welcome_url = url_for :host => "example.com", :controller => "welcome", :action => "greeting" end class < self, :action => action) do - super - end - - if logger - log = DelayedLog.new do - "\n\nProcessing #{self.class.name}\##{action_name} " \ - "to #{request.formats} (for #{request_origin}) " \ - "[#{request.method.to_s.upcase}]" - end - - logger.info(log) - end - - result - end - - private - # Returns the request origin with the IP and time. This needs to be cached, - # otherwise we would get different results for each time it calls. - def request_origin - @request_origin ||= "#{request.remote_ip} at #{Time.now.to_s(:db)}" - end end end diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index e31b795cd2..8b3a444cda 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -21,6 +21,7 @@ module ActionController autoload :Helpers autoload :HideActions autoload :Layouts + autoload :Logger autoload :MimeResponds autoload :RackDelegation autoload :Compatibility diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 452f0cd4f0..f75b6ed641 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -14,6 +14,7 @@ module ActionController include ActionController::Layouts include ActionController::ConditionalGet include ActionController::RackDelegation + include ActionController::Logger include ActionController::Benchmarking include ActionController::Configuration diff --git a/actionpack/lib/action_controller/metal/logger.rb b/actionpack/lib/action_controller/metal/logger.rb new file mode 100644 index 0000000000..956d7dd371 --- /dev/null +++ b/actionpack/lib/action_controller/metal/logger.rb @@ -0,0 +1,34 @@ +require 'abstract_controller/logger' + +module ActionController + module Logger + # Override process_action in the AbstractController::Base + # to log details about the method. + def process_action(action) + result = ActiveSupport::Notifications.instrument(:process_action, + :controller => self, :action => action) do + super + end + + if logger + log = AbstractController::Logger::DelayedLog.new do + "\n\nProcessing #{self.class.name}\##{action_name} " \ + "to #{request.formats} (for #{request_origin}) " \ + "[#{request.method.to_s.upcase}]" + end + + logger.info(log) + end + + result + end + + private + + # Returns the request origin with the IP and time. This needs to be cached, + # otherwise we would get different results for each time it calls. + def request_origin + @request_origin ||= "#{request.remote_ip} at #{Time.now.to_s(:db)}" + end + end +end -- cgit v1.2.3 From 17b6053083767e5747c0f522b83a69e268d9d69e Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 22 Dec 2009 15:45:10 -0600 Subject: Float on rack/master in preparation for rack 1.1 gem release --- Gemfile | 2 +- actionpack/actionpack.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index d979add5b3..7ef6c2e6f2 100644 --- a/Gemfile +++ b/Gemfile @@ -13,7 +13,7 @@ gem "pg", ">= 0.8.0" gem "mysql", ">= 2.8.1" # AP -gem "rack", "1.0.1", :git => "git://github.com/rails/rack.git" +gem "rack", "1.1.0", :git => "git://github.com/rack/rack.git" gem "RedCloth", ">= 4.2.2" if ENV['CI'] diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index 23d478fd44..1294cd4993 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -16,7 +16,7 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', '= 3.0.pre') s.add_dependency('activemodel', '= 3.0.pre') - s.add_dependency('rack', '~> 1.0.1') + s.add_dependency('rack', '~> 1.1.0') s.add_dependency('rack-test', '~> 0.5.0') s.add_dependency('rack-mount', '~> 0.3.2') s.add_dependency('erubis', '~> 2.6.5') -- cgit v1.2.3 From a1bf2f96ce6f086de8519e344ee7e396ae3da9bb Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 22 Dec 2009 16:08:03 -0600 Subject: AD::StatusCodes support is now part of rack --- actionpack/lib/action_controller/base.rb | 2 +- actionpack/lib/action_controller/metal.rb | 2 +- .../lib/action_controller/metal/redirecting.rb | 8 +++--- actionpack/lib/action_dispatch.rb | 1 - actionpack/lib/action_dispatch/http/response.rb | 4 +-- .../lib/action_dispatch/http/status_codes.rb | 33 ---------------------- .../action_dispatch/middleware/show_exceptions.rb | 2 +- .../action_dispatch/testing/assertions/response.rb | 2 +- actionpack/test/controller/render_test.rb | 4 +-- 9 files changed, 12 insertions(+), 46 deletions(-) delete mode 100644 actionpack/lib/action_dispatch/http/status_codes.rb diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index f75b6ed641..dbba69f637 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -90,7 +90,7 @@ module ActionController end if options[:status] - options[:status] = ActionDispatch::StatusCodes[options[:status]] + options[:status] = Rack::Utils.status_code(options[:status]) end options[:update] = blk if block_given? diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index 93a19f8f93..b436de9878 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -69,7 +69,7 @@ module ActionController end def status=(status) - @_status = ActionDispatch::StatusCodes[status] + @_status = Rack::Utils.status_code(status) end # :api: private diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index 39dc23024c..7a2f9a6fc5 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -58,18 +58,18 @@ module ActionController logger.info("Redirected to #{location}") if logger && logger.info? end - + private def _extract_redirect_to_status(options, response_status) status = if options.is_a?(Hash) && options.key?(:status) - ActionDispatch::StatusCodes[options.delete(:status)] + Rack::Utils.status_code(options.delete(:status)) elsif response_status.key?(:status) - ActionDispatch::StatusCodes[response_status[:status]] + Rack::Utils.status_code(response_status[:status]) else 302 end end - + def _compute_redirect_to_location(options) case options # The scheme name consist of a letter followed by any combination of diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 48b5652a89..ed04980ab0 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -37,7 +37,6 @@ module ActionDispatch autoload_under 'http' do autoload :Request autoload :Response - autoload :StatusCodes end deferrable do diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 6dc563264f..8524bbd993 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -60,7 +60,7 @@ module ActionDispatch # :nodoc: end def status=(status) - @status = ActionDispatch::StatusCodes[status] + @status = Rack::Utils.status_code(status) end # The response code of the request @@ -74,7 +74,7 @@ module ActionDispatch # :nodoc: end def message - StatusCodes::STATUS_CODES[@status] + Rack::Utils::HTTP_STATUS_CODES[@status] end alias_method :status_message, :message diff --git a/actionpack/lib/action_dispatch/http/status_codes.rb b/actionpack/lib/action_dispatch/http/status_codes.rb deleted file mode 100644 index 3d6ee685ea..0000000000 --- a/actionpack/lib/action_dispatch/http/status_codes.rb +++ /dev/null @@ -1,33 +0,0 @@ -require 'active_support/inflector' - -module ActionDispatch - module StatusCodes #:nodoc: - STATUS_CODES = Rack::Utils::HTTP_STATUS_CODES.merge({ - 102 => "Processing", - 207 => "Multi-Status", - 226 => "IM Used", - 422 => "Unprocessable Entity", - 423 => "Locked", - 424 => "Failed Dependency", - 426 => "Upgrade Required", - 507 => "Insufficient Storage", - 510 => "Not Extended" - }).freeze - - def self.[](status) - if status.is_a?(Symbol) - SYMBOL_TO_STATUS_CODE[status] || 500 - else - status.to_i - end - end - - # Provides a symbol-to-fixnum lookup for converting a symbol (like - # :created or :not_implemented) into its corresponding HTTP status - # code (like 200 or 501). - SYMBOL_TO_STATUS_CODE = STATUS_CODES.inject({}) { |hash, (code, message)| - hash[ActiveSupport::Inflector.underscore(message.gsub(/ /, "")).to_sym] = code - hash - }.freeze - end -end diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index bd87764f5b..5c7f78b0e4 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -101,7 +101,7 @@ module ActionDispatch end def status_code(exception) - ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[@@rescue_responses[exception.class.name]] + Rack::Utils.status_code(@@rescue_responses[exception.class.name]) end def render(status, body) diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb index 501a7c4dfb..5686bbdbde 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/response.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb @@ -28,7 +28,7 @@ module ActionDispatch assert_block("") { true } # to count the assertion elsif type.is_a?(Fixnum) && @response.response_code == type assert_block("") { true } # to count the assertion - elsif type.is_a?(Symbol) && @response.response_code == ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[type] + elsif type.is_a?(Symbol) && @response.response_code == Rack::Utils::SYMBOL_TO_STATUS_CODE[type] assert_block("") { true } # to count the assertion else assert_block(build_message(message, "Expected response to be a , but was ", type, @response.response_code)) { false } diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index f26b15d2e0..54f2739d38 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -1125,7 +1125,7 @@ class RenderTest < ActionController::TestCase assert !@response.headers.include?('Content-Length') assert_response :no_content - ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE.each do |status, code| + Rack::Utils::SYMBOL_TO_STATUS_CODE.each do |status, code| get :head_with_symbolic_status, :status => status.to_s assert_equal code, @response.response_code assert_response status @@ -1133,7 +1133,7 @@ class RenderTest < ActionController::TestCase end def test_head_with_integer_status - ActionDispatch::StatusCodes::STATUS_CODES.each do |code, message| + Rack::Utils::HTTP_STATUS_CODES.each do |code, message| get :head_with_integer_status, :status => code.to_s assert_equal message, @response.message end -- cgit v1.2.3 From df7faef68e833efeef0dca3e07e0355f5042bb36 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 22 Dec 2009 16:09:41 -0600 Subject: Referer and user agent are in Rack::Request --- actionpack/lib/action_dispatch/http/request.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index bc17cadb38..6e8a5dcb8a 100755 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -18,7 +18,7 @@ module ActionDispatch HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM - HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env| + HTTP_NEGOTIATE HTTP_PRAGMA ].each do |env| define_method(env.sub(/^HTTP_/n, '').downcase) do @env[env] end -- cgit v1.2.3 From 2d0c703c922758dc36df8a20a56894484013b0f1 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 22 Dec 2009 16:18:22 -0600 Subject: Use Rack::Runtime middleware so the reported time includes the entire middleware stack --- actionpack/lib/action_controller/metal/benchmarking.rb | 1 - railties/lib/rails/application.rb | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_controller/metal/benchmarking.rb b/actionpack/lib/action_controller/metal/benchmarking.rb index e58df69172..f73f635b0d 100644 --- a/actionpack/lib/action_controller/metal/benchmarking.rb +++ b/actionpack/lib/action_controller/metal/benchmarking.rb @@ -53,7 +53,6 @@ module ActionController #:nodoc: log_message << " [#{complete_request_uri rescue "unknown"}]" logger.info(log_message) - response.headers["X-Runtime"] = "%.0f" % ms else super end diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index e6f2d30429..d7a89ba2be 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -208,6 +208,7 @@ module Rails initializer :initialize_middleware_stack do if config.frameworks.include?(:action_controller) config.middleware.use(::Rack::Lock, :if => lambda { ActionController::Base.allow_concurrency }) + config.middleware.use(::Rack::Runtime) config.middleware.use(ActionDispatch::ShowExceptions, lambda { ActionController::Base.consider_all_requests_local }) config.middleware.use(ActionDispatch::Callbacks, lambda { ActionController::Dispatcher.prepare_each_request }) config.middleware.use(lambda { ActionController::Base.session_store }, lambda { ActionController::Base.session_options }) -- cgit v1.2.3 From b1aee9f4eebdae4fad38572359649c097c731b77 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 22 Dec 2009 17:11:21 -0600 Subject: All AD modules are "deferrable" --- .../lib/action_controller/metal/rack_delegation.rb | 3 +++ actionpack/lib/action_dispatch.rb | 12 +++++------- .../action_dispatch/middleware/params_parser.rb | 1 + .../middleware/session/abstract_store.rb | 1 + .../middleware/session/cookie_store.rb | 9 +++++---- .../action_dispatch/middleware/show_exceptions.rb | 3 ++- .../lib/action_dispatch/testing/assertions/dom.rb | 4 +++- .../action_dispatch/testing/assertions/selector.rb | 22 ++++++++++++---------- .../lib/action_dispatch/testing/assertions/tag.rb | 2 ++ .../lib/action_view/helpers/sanitize_helper.rb | 1 + 10 files changed, 35 insertions(+), 23 deletions(-) diff --git a/actionpack/lib/action_controller/metal/rack_delegation.rb b/actionpack/lib/action_controller/metal/rack_delegation.rb index 833475cff7..bb55383631 100644 --- a/actionpack/lib/action_controller/metal/rack_delegation.rb +++ b/actionpack/lib/action_controller/metal/rack_delegation.rb @@ -1,3 +1,6 @@ +require 'action_dispatch/http/request' +require 'action_dispatch/http/response' + module ActionController module RackDelegation extend ActiveSupport::Concern diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index ed04980ab0..4e04e2a17c 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -34,12 +34,12 @@ end module ActionDispatch extend ActiveSupport::Autoload - autoload_under 'http' do - autoload :Request - autoload :Response - end - deferrable do + autoload_under 'http' do + autoload :Request + autoload :Response + end + autoload_under 'middleware' do autoload :Callbacks autoload :ParamsParser @@ -71,8 +71,6 @@ module ActionDispatch autoload :TestResponse end end - - autoload :HTML, 'action_controller/vendor/html-scanner' end autoload :Mime, 'action_dispatch/http/mime_type' diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb index 8970ccaf07..534390d4aa 100644 --- a/actionpack/lib/action_dispatch/middleware/params_parser.rb +++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb @@ -1,4 +1,5 @@ require 'active_support/json' +require 'action_dispatch/http/request' module ActionDispatch class ParamsParser diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index c5c06f74a2..7d4f0998ce 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -1,4 +1,5 @@ require 'rack/utils' +require 'rack/request' module ActionDispatch module Session diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index bd552b458a..f27f22c7e7 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb @@ -1,4 +1,5 @@ -require "active_support/core_ext/hash/keys" +require 'active_support/core_ext/hash/keys' +require 'rack/request' module ActionDispatch module Session @@ -49,7 +50,7 @@ module ActionDispatch :expire_after => nil, :httponly => true }.freeze - + class OptionsHash < Hash def initialize(by, env, default_options) @session_data = env[CookieStore::ENV_SESSION_KEY] @@ -60,7 +61,7 @@ module ActionDispatch key == :id ? @session_data[:session_id] : super(key) end end - + ENV_SESSION_KEY = "rack.session".freeze ENV_SESSION_OPTIONS_KEY = "rack.session.options".freeze HTTP_SET_COOKIE = "Set-Cookie".freeze @@ -102,7 +103,7 @@ module ActionDispatch def call(env) env[ENV_SESSION_KEY] = AbstractStore::SessionHash.new(self, env) env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options) - + status, headers, body = @app.call(env) session_data = env[ENV_SESSION_KEY] diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 5c7f78b0e4..4ebc8a2ab9 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -1,4 +1,5 @@ -require "active_support/core_ext/exception" +require 'active_support/core_ext/exception' +require 'action_dispatch/http/request' module ActionDispatch class ShowExceptions diff --git a/actionpack/lib/action_dispatch/testing/assertions/dom.rb b/actionpack/lib/action_dispatch/testing/assertions/dom.rb index 9a917f704a..9c215de743 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/dom.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/dom.rb @@ -1,3 +1,5 @@ +require 'action_controller/vendor/html-scanner' + module ActionDispatch module Assertions module DomAssertions @@ -15,7 +17,7 @@ module ActionDispatch assert_block(full_message) { expected_dom == actual_dom } end - + # The negated form of +assert_dom_equivalent+. # # ==== Examples diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb index d22adfa749..c2dc591ff7 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb @@ -1,3 +1,5 @@ +require 'action_controller/vendor/html-scanner' + #-- # Copyright (c) 2006 Assaf Arkin (http://labnotes.org) # Under MIT and/or CC By license. @@ -16,7 +18,7 @@ module ActionDispatch # # Use +css_select+ to select elements without making an assertions, either # from the response HTML or elements selected by the enclosing assertion. - # + # # In addition to HTML responses, you can make the following assertions: # * +assert_select_rjs+ - Assertions on HTML content of RJS update and insertion operations. # * +assert_select_encoded+ - Assertions on HTML encoded inside XML, for example for dealing with feed item descriptions. @@ -53,8 +55,8 @@ module ActionDispatch # end # # # Selects all list items in unordered lists - # items = css_select("ul>li") - # + # items = css_select("ul>li") + # # # Selects all form tags and then all inputs inside the form # forms = css_select("form") # forms.each do |form| @@ -212,7 +214,7 @@ module ActionDispatch # Otherwise just operate on the response document. root = response_from_page_or_rjs end - + # First or second argument is the selector: string and we pass # all remaining arguments. Array and we pass the argument. Also # accepts selector itself. @@ -225,7 +227,7 @@ module ActionDispatch selector = arg else raise ArgumentError, "Expecting a selector as the first argument" end - + # Next argument is used for equality tests. equals = {} case arg = args.shift @@ -315,10 +317,10 @@ module ActionDispatch # Returns all matches elements. matches end - + def count_description(min, max) #:nodoc: pluralize = lambda {|word, quantity| word << (quantity == 1 ? '' : 's')} - + if min && max && (max != min) "between #{min} and #{max} elements" elsif min && !(min == 1 && max == 1) @@ -327,7 +329,7 @@ module ActionDispatch "at most #{max} #{pluralize['element', max]}" end end - + # :call-seq: # assert_select_rjs(id?) { |elements| ... } # assert_select_rjs(statement, id?) { |elements| ... } @@ -344,7 +346,7 @@ module ActionDispatch # that update or insert an element with that identifier. # # Use the first argument to narrow down assertions to only statements - # of that type. Possible values are :replace, :replace_html, + # of that type. Possible values are :replace, :replace_html, # :show, :hide, :toggle, :remove, # :insert_html and :redirect. # @@ -494,7 +496,7 @@ module ActionDispatch # end # end # end - # + # # # # Selects all paragraph tags from within the description of an RSS feed # assert_select_feed :rss, 2.0 do diff --git a/actionpack/lib/action_dispatch/testing/assertions/tag.rb b/actionpack/lib/action_dispatch/testing/assertions/tag.rb index b74dcb1fe4..5c735e61b2 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/tag.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/tag.rb @@ -1,3 +1,5 @@ +require 'action_controller/vendor/html-scanner' + module ActionDispatch module Assertions # Pair of assertions to testing elements in the HTML output of the response. diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb index 69d0d0fb67..f03ffe5ef4 100644 --- a/actionpack/lib/action_view/helpers/sanitize_helper.rb +++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb @@ -1,3 +1,4 @@ +require 'action_controller/vendor/html-scanner' require 'action_view/helpers/tag_helper' module ActionView -- cgit v1.2.3 From ace20bd25e3818b7f29c222643dd445c48b36425 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 22 Dec 2009 17:27:37 -0600 Subject: Flip deferrable autoload convention --- actionmailer/lib/action_mailer.rb | 31 +++--- actionpack/lib/abstract_controller.rb | 16 ++- actionpack/lib/action_controller.rb | 112 ++++++++++---------- actionpack/lib/action_controller/caching.rb | 12 ++- .../lib/action_controller/vendor/html-scanner.rb | 26 ++--- actionpack/lib/action_dispatch.rb | 60 ++++++----- actionpack/lib/action_view.rb | 41 ++++---- actionpack/lib/action_view/template.rb | 14 +-- activemodel/lib/active_model.rb | 42 ++++---- activerecord/lib/active_record.rb | 116 ++++++++++++--------- activeresource/lib/active_resource.rb | 18 ++-- activesupport/lib/active_support.rb | 53 +++++----- .../lib/active_support/dependencies/autoload.rb | 10 +- 13 files changed, 290 insertions(+), 261 deletions(-) diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index f439eb175c..6539451bea 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -30,29 +30,34 @@ require 'action_view' module ActionMailer extend ::ActiveSupport::Autoload - autoload :AdvAttrAccessor - autoload :DeprecatedBody - autoload :Base - autoload :DeliveryMethod - autoload :MailHelper - autoload :Part - autoload :PartContainer - autoload :Quoting - autoload :TestHelper - autoload :Utils + eager_autoload do + autoload :AdvAttrAccessor + autoload :DeprecatedBody + autoload :Base + autoload :DeliveryMethod + autoload :MailHelper + autoload :Part + autoload :PartContainer + autoload :Quoting + autoload :TestHelper + autoload :Utils + end end module Text extend ActiveSupport::Autoload - autoload :Format, 'action_mailer/vendor/text_format' + eager_autoload do + autoload :Format, 'action_mailer/vendor/text_format' + end end module Net extend ActiveSupport::Autoload - autoload :SMTP + eager_autoload do + autoload :SMTP + end end - require 'action_mailer/vendor/tmail' diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb index c15a1da98a..237ab577ba 100644 --- a/actionpack/lib/abstract_controller.rb +++ b/actionpack/lib/abstract_controller.rb @@ -8,13 +8,11 @@ require 'active_support/core_ext/module/delegation' module AbstractController extend ActiveSupport::Autoload - deferrable do - autoload :Base - autoload :Callbacks - autoload :Helpers - autoload :Layouts - autoload :LocalizedCache - autoload :Logger - autoload :Rendering - end + autoload :Base + autoload :Callbacks + autoload :Helpers + autoload :Layouts + autoload :LocalizedCache + autoload :Logger + autoload :Rendering end diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 8b3a444cda..26a85d4de8 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -5,66 +5,66 @@ require 'active_support/ruby/shim' module ActionController extend ActiveSupport::Autoload - deferrable do - autoload :Base - autoload :Caching - autoload :PolymorphicRoutes - autoload :Translation - autoload :Metal - autoload :Middleware + autoload :Base + autoload :Caching + autoload :PolymorphicRoutes + autoload :Translation + autoload :Metal + autoload :Middleware - autoload_under "metal" do - autoload :Benchmarking - autoload :ConditionalGet - autoload :Configuration - autoload :Head - autoload :Helpers - autoload :HideActions - autoload :Layouts - autoload :Logger - autoload :MimeResponds - autoload :RackDelegation - autoload :Compatibility - autoload :Redirecting - autoload :Rendering - autoload :Renderers - autoload :Rescue - autoload :Responder - autoload :SessionManagement - autoload :UrlFor - autoload :Verification - autoload :Flash - autoload :RequestForgeryProtection - autoload :Streaming - autoload :HttpAuthentication - autoload :FilterParameterLogging - autoload :Cookies - end - - autoload :Dispatcher, 'action_controller/dispatch/dispatcher' - autoload :PerformanceTest, 'action_controller/deprecated/performance_test' - autoload :Routing, 'action_controller/deprecated' - autoload :Integration, 'action_controller/deprecated/integration_test' - autoload :IntegrationTest, 'action_controller/deprecated/integration_test' + autoload_under "metal" do + autoload :Benchmarking + autoload :ConditionalGet + autoload :Configuration + autoload :Head + autoload :Helpers + autoload :HideActions + autoload :Layouts + autoload :Logger + autoload :MimeResponds + autoload :RackDelegation + autoload :Compatibility + autoload :Redirecting + autoload :Rendering + autoload :Renderers + autoload :Rescue + autoload :Responder + autoload :SessionManagement + autoload :UrlFor + autoload :Verification + autoload :Flash + autoload :RequestForgeryProtection + autoload :Streaming + autoload :HttpAuthentication + autoload :FilterParameterLogging + autoload :Cookies end - autoload :RecordIdentifier - autoload :UrlRewriter - autoload :UrlWriter, 'action_controller/url_rewriter' + autoload :Dispatcher, 'action_controller/dispatch/dispatcher' + autoload :PerformanceTest, 'action_controller/deprecated/performance_test' + autoload :Routing, 'action_controller/deprecated' + autoload :Integration, 'action_controller/deprecated/integration_test' + autoload :IntegrationTest, 'action_controller/deprecated/integration_test' - # TODO: Don't autoload exceptions, setup explicit - # requires for files that need them - autoload_at "action_controller/metal/exceptions" do - autoload :ActionControllerError - autoload :RenderError - autoload :RoutingError - autoload :MethodNotAllowed - autoload :NotImplemented - autoload :UnknownController - autoload :MissingFile - autoload :RenderError - autoload :SessionOverflowError - autoload :UnknownHttpMethod + eager_autoload do + autoload :RecordIdentifier + autoload :UrlRewriter + autoload :UrlWriter, 'action_controller/url_rewriter' + + # TODO: Don't autoload exceptions, setup explicit + # requires for files that need them + autoload_at "action_controller/metal/exceptions" do + autoload :ActionControllerError + autoload :RenderError + autoload :RoutingError + autoload :MethodNotAllowed + autoload :NotImplemented + autoload :UnknownController + autoload :MissingFile + autoload :RenderError + autoload :SessionOverflowError + autoload :UnknownHttpMethod + end end end diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index ad357cceda..d784138ebe 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -32,11 +32,13 @@ module ActionController #:nodoc: extend ActiveSupport::Concern extend ActiveSupport::Autoload - autoload :Actions - autoload :Fragments - autoload :Pages - autoload :Sweeper, 'action_controller/caching/sweeping' - autoload :Sweeping, 'action_controller/caching/sweeping' + eager_autoload do + autoload :Actions + autoload :Fragments + autoload :Pages + autoload :Sweeper, 'action_controller/caching/sweeping' + autoload :Sweeping, 'action_controller/caching/sweeping' + end included do @@cache_store = nil diff --git a/actionpack/lib/action_controller/vendor/html-scanner.rb b/actionpack/lib/action_controller/vendor/html-scanner.rb index 2cb20ddd05..879b31e60e 100644 --- a/actionpack/lib/action_controller/vendor/html-scanner.rb +++ b/actionpack/lib/action_controller/vendor/html-scanner.rb @@ -3,16 +3,18 @@ $LOAD_PATH << "#{File.dirname(__FILE__)}/html-scanner" module HTML extend ActiveSupport::Autoload - autoload :CDATA, 'html/node' - autoload :Document, 'html/document' - autoload :FullSanitizer, 'html/sanitizer' - autoload :LinkSanitizer, 'html/sanitizer' - autoload :Node, 'html/node' - autoload :Sanitizer, 'html/sanitizer' - autoload :Selector, 'html/selector' - autoload :Tag, 'html/node' - autoload :Text, 'html/node' - autoload :Tokenizer, 'html/tokenizer' - autoload :Version, 'html/version' - autoload :WhiteListSanitizer, 'html/sanitizer' + eager_autoload do + autoload :CDATA, 'html/node' + autoload :Document, 'html/document' + autoload :FullSanitizer, 'html/sanitizer' + autoload :LinkSanitizer, 'html/sanitizer' + autoload :Node, 'html/node' + autoload :Sanitizer, 'html/sanitizer' + autoload :Selector, 'html/selector' + autoload :Tag, 'html/node' + autoload :Text, 'html/node' + autoload :Tokenizer, 'html/tokenizer' + autoload :Version, 'html/version' + autoload :WhiteListSanitizer, 'html/sanitizer' + end end diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 4e04e2a17c..fafcf7dc4e 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -34,42 +34,40 @@ end module ActionDispatch extend ActiveSupport::Autoload - deferrable do - autoload_under 'http' do - autoload :Request - autoload :Response - end + autoload_under 'http' do + autoload :Request + autoload :Response + end - autoload_under 'middleware' do - autoload :Callbacks - autoload :ParamsParser - autoload :Rescue - autoload :ShowExceptions - autoload :Static - autoload :StringCoercion - end + autoload_under 'middleware' do + autoload :Callbacks + autoload :ParamsParser + autoload :Rescue + autoload :ShowExceptions + autoload :Static + autoload :StringCoercion + end - autoload :MiddlewareStack, 'action_dispatch/middleware/stack' - autoload :Routing + autoload :MiddlewareStack, 'action_dispatch/middleware/stack' + autoload :Routing - module Http - autoload :Headers, 'action_dispatch/http/headers' - end + module Http + autoload :Headers, 'action_dispatch/http/headers' + end - module Session - autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store' - autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store' - autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store' - end + module Session + autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store' + autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store' + autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store' + end - autoload_under 'testing' do - autoload :Assertions - autoload :Integration - autoload :PerformanceTest - autoload :TestProcess - autoload :TestRequest - autoload :TestResponse - end + autoload_under 'testing' do + autoload :Assertions + autoload :Integration + autoload :PerformanceTest + autoload :TestProcess + autoload :TestRequest + autoload :TestResponse end end diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index aabe8c4314..f57f9ca229 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -31,27 +31,28 @@ require 'action_pack' module ActionView extend ActiveSupport::Autoload - autoload :Base - autoload :Context - autoload :Template - autoload :Helpers - autoload :SafeBuffer - - - autoload_under "render" do - autoload :Partials - autoload :Rendering + eager_autoload do + autoload :Base + autoload :Context + autoload :Template + autoload :Helpers + autoload :SafeBuffer + + autoload_under "render" do + autoload :Partials + autoload :Rendering + end + + autoload :MissingTemplate, 'action_view/base' + autoload :Resolver, 'action_view/template/resolver' + autoload :PathResolver, 'action_view/template/resolver' + autoload :PathSet, 'action_view/paths' + autoload :FileSystemResolverWithFallback, 'action_view/template/resolver' + + autoload :TemplateError, 'action_view/template/error' + autoload :TemplateHandler, 'action_view/template' + autoload :TemplateHandlers, 'action_view/template' end - - autoload :MissingTemplate, 'action_view/base' - autoload :Resolver, 'action_view/template/resolver' - autoload :PathResolver, 'action_view/template/resolver' - autoload :PathSet, 'action_view/paths' - autoload :FileSystemResolverWithFallback, 'action_view/template/resolver' - - autoload :TemplateError, 'action_view/template/error' - autoload :TemplateHandler, 'action_view/template' - autoload :TemplateHandlers, 'action_view/template' end require 'action_view/erb/util' diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 210ad508f5..a64ee09245 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -7,12 +7,14 @@ require "action_view/template/resolver" module ActionView class Template extend ActiveSupport::Autoload - - autoload :Error - autoload :Handler - autoload :Handlers - autoload :Text - + + eager_autoload do + autoload :Error + autoload :Handler + autoload :Handlers + autoload :Text + end + extend Template::Handlers attr_reader :source, :identifier, :handler, :mime_type, :formats, :details diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index e0de27b96d..46caa53219 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -29,29 +29,33 @@ require 'active_support' module ActiveModel extend ActiveSupport::Autoload - autoload :AttributeMethods - autoload :Conversion - autoload :DeprecatedErrorMethods - autoload :Dirty - autoload :Errors - autoload :Lint - autoload :Name, 'active_model/naming' - autoload :Naming - autoload :Observer, 'active_model/observing' - autoload :Observing - autoload :Serialization - autoload :StateMachine - autoload :Translation - autoload :Validations - autoload :ValidationsRepairHelper - autoload :Validator - autoload :VERSION + eager_autoload do + autoload :AttributeMethods + autoload :Conversion + autoload :DeprecatedErrorMethods + autoload :Dirty + autoload :Errors + autoload :Lint + autoload :Name, 'active_model/naming' + autoload :Naming + autoload :Observer, 'active_model/observing' + autoload :Observing + autoload :Serialization + autoload :StateMachine + autoload :Translation + autoload :Validations + autoload :ValidationsRepairHelper + autoload :Validator + autoload :VERSION + end module Serializers extend ActiveSupport::Autoload - autoload :JSON - autoload :Xml + eager_autoload do + autoload :JSON + autoload :Xml + end end end diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 2376bbd04a..196b87c0ac 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -35,82 +35,94 @@ require 'arel' module ActiveRecord extend ActiveSupport::Autoload - autoload :VERSION - - autoload :ActiveRecordError, 'active_record/base' - autoload :ConnectionNotEstablished, 'active_record/base' - - autoload :Aggregations - autoload :AssociationPreload - autoload :Associations - autoload :AttributeMethods - autoload :Attributes - autoload :AutosaveAssociation - autoload :Relation - autoload :Base - autoload :Batches - autoload :Calculations - autoload :Callbacks - autoload :DynamicFinderMatch - autoload :DynamicScopeMatch - autoload :Migration - autoload :Migrator, 'active_record/migration' - autoload :NamedScope - autoload :NestedAttributes - autoload :Observer - autoload :QueryCache - autoload :Reflection - autoload :Schema - autoload :SchemaDumper - autoload :Serialization - autoload :SessionStore - autoload :StateMachine - autoload :Timestamp - autoload :Transactions - autoload :Types - autoload :Validations + eager_autoload do + autoload :VERSION + + autoload :ActiveRecordError, 'active_record/base' + autoload :ConnectionNotEstablished, 'active_record/base' + + autoload :Aggregations + autoload :AssociationPreload + autoload :Associations + autoload :AttributeMethods + autoload :Attributes + autoload :AutosaveAssociation + autoload :Relation + autoload :Base + autoload :Batches + autoload :Calculations + autoload :Callbacks + autoload :DynamicFinderMatch + autoload :DynamicScopeMatch + autoload :Migration + autoload :Migrator, 'active_record/migration' + autoload :NamedScope + autoload :NestedAttributes + autoload :Observer + autoload :QueryCache + autoload :Reflection + autoload :Schema + autoload :SchemaDumper + autoload :Serialization + autoload :SessionStore + autoload :StateMachine + autoload :Timestamp + autoload :Transactions + autoload :Types + autoload :Validations + end module AttributeMethods extend ActiveSupport::Autoload - autoload :BeforeTypeCast - autoload :Dirty - autoload :PrimaryKey - autoload :Query - autoload :Read - autoload :TimeZoneConversion - autoload :Write + eager_autoload do + autoload :BeforeTypeCast + autoload :Dirty + autoload :PrimaryKey + autoload :Query + autoload :Read + autoload :TimeZoneConversion + autoload :Write + end end module Attributes extend ActiveSupport::Autoload - autoload :Aliasing - autoload :Store - autoload :Typecasting + eager_autoload do + autoload :Aliasing + autoload :Store + autoload :Typecasting + end end module Type extend ActiveSupport::Autoload - autoload :Number, 'active_record/types/number' - autoload :Object, 'active_record/types/object' - autoload :Serialize, 'active_record/types/serialize' - autoload :TimeWithZone, 'active_record/types/time_with_zone' - autoload :Unknown, 'active_record/types/unknown' + eager_autoload do + autoload :Number, 'active_record/types/number' + autoload :Object, 'active_record/types/object' + autoload :Serialize, 'active_record/types/serialize' + autoload :TimeWithZone, 'active_record/types/time_with_zone' + autoload :Unknown, 'active_record/types/unknown' + end end module Locking extend ActiveSupport::Autoload - autoload :Optimistic - autoload :Pessimistic + eager_autoload do + autoload :Optimistic + autoload :Pessimistic + end end module ConnectionAdapters extend ActiveSupport::Autoload - autoload :AbstractAdapter + eager_autoload do + autoload :AbstractAdapter + end end end diff --git a/activeresource/lib/active_resource.rb b/activeresource/lib/active_resource.rb index 3e4a1dd4a1..e0a6ecbcce 100644 --- a/activeresource/lib/active_resource.rb +++ b/activeresource/lib/active_resource.rb @@ -33,12 +33,14 @@ require 'active_model' module ActiveResource extend ActiveSupport::Autoload - autoload :Base - autoload :Connection - autoload :CustomMethods - autoload :Formats - autoload :HttpMock - autoload :Observing - autoload :Schema - autoload :Validations + eager_autoload do + autoload :Base + autoload :Connection + autoload :CustomMethods + autoload :Formats + autoload :HttpMock + autoload :Observing + autoload :Schema + autoload :Validations + end end diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 9e21b3faf3..f2baa5a56a 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -39,31 +39,34 @@ require "active_support/dependencies/autoload" module ActiveSupport extend ActiveSupport::Autoload - autoload :BacktraceCleaner - autoload :Base64 - autoload :BasicObject - autoload :Benchmarkable - autoload :BufferedLogger - autoload :Cache - autoload :Callbacks - autoload :Concern - autoload :Configurable - autoload :DeprecatedCallbacks - autoload :Deprecation - autoload :Gzip - autoload :Inflector - autoload :Memoizable - autoload :MessageEncryptor - autoload :MessageVerifier - autoload :Multibyte - autoload :OptionMerger - autoload :OrderedHash - autoload :OrderedOptions - autoload :Notifications - autoload :Rescuable - autoload :SecureRandom - autoload :StringInquirer - autoload :XmlMini + # TODO: Narrow this list down + eager_autoload do + autoload :BacktraceCleaner + autoload :Base64 + autoload :BasicObject + autoload :Benchmarkable + autoload :BufferedLogger + autoload :Cache + autoload :Callbacks + autoload :Concern + autoload :Configurable + autoload :DeprecatedCallbacks + autoload :Deprecation + autoload :Gzip + autoload :Inflector + autoload :Memoizable + autoload :MessageEncryptor + autoload :MessageVerifier + autoload :Multibyte + autoload :OptionMerger + autoload :OrderedHash + autoload :OrderedOptions + autoload :Notifications + autoload :Rescuable + autoload :SecureRandom + autoload :StringInquirer + autoload :XmlMini + end end require 'active_support/vendor' diff --git a/activesupport/lib/active_support/dependencies/autoload.rb b/activesupport/lib/active_support/dependencies/autoload.rb index 96ab04c61a..44edb89ad5 100644 --- a/activesupport/lib/active_support/dependencies/autoload.rb +++ b/activesupport/lib/active_support/dependencies/autoload.rb @@ -5,13 +5,13 @@ module ActiveSupport @@autoloads = {} @@under_path = nil @@at_path = nil - @@autoload_defer = false + @@eager_autoload = false def autoload(const_name, path = @@at_path) full = [self.name, @@under_path, const_name.to_s, path].compact.join("::") location = path || Inflector.underscore(full) - unless @@autoload_defer + if @@eager_autoload @@autoloads[const_name] = location end super const_name, location @@ -31,11 +31,11 @@ module ActiveSupport @@at_path = old_path end - def deferrable - old_defer, @@autoload_defer = @@autoload_defer, true + def eager_autoload + old_eager, @@eager_autoload = @@eager_autoload, true yield ensure - @@autoload_defer = old_defer + @@eager_autoload = old_eager end def self.eager_autoload! -- cgit v1.2.3 From 2e4e8d156ca1a2f3fe2f587956097621433514f8 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 22 Dec 2009 17:33:00 -0600 Subject: All AM modules are safe to defer --- actionmailer/lib/action_mailer.rb | 40 ++++++---------------- actionmailer/lib/action_mailer/base.rb | 3 ++ actionmailer/lib/action_mailer/delivery_method.rb | 5 ++- .../lib/action_mailer/delivery_method/smtp.rb | 4 +-- 4 files changed, 17 insertions(+), 35 deletions(-) diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 6539451bea..d7bbbbd78c 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -30,34 +30,14 @@ require 'action_view' module ActionMailer extend ::ActiveSupport::Autoload - eager_autoload do - autoload :AdvAttrAccessor - autoload :DeprecatedBody - autoload :Base - autoload :DeliveryMethod - autoload :MailHelper - autoload :Part - autoload :PartContainer - autoload :Quoting - autoload :TestHelper - autoload :Utils - end + autoload :AdvAttrAccessor + autoload :DeprecatedBody + autoload :Base + autoload :DeliveryMethod + autoload :MailHelper + autoload :Part + autoload :PartContainer + autoload :Quoting + autoload :TestHelper + autoload :Utils end - -module Text - extend ActiveSupport::Autoload - - eager_autoload do - autoload :Format, 'action_mailer/vendor/text_format' - end -end - -module Net - extend ActiveSupport::Autoload - - eager_autoload do - autoload :SMTP - end -end - -require 'action_mailer/vendor/tmail' diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index b2c355c7ae..de78e87fb4 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -1,4 +1,7 @@ require 'active_support/core_ext/class' +require 'action_mailer/part' +require 'action_mailer/vendor/text_format' +require 'action_mailer/vendor/tmail' module ActionMailer #:nodoc: # Action Mailer allows you to send email from your application using a mailer model and views. diff --git a/actionmailer/lib/action_mailer/delivery_method.rb b/actionmailer/lib/action_mailer/delivery_method.rb index 29a51afdc3..4f7d3afc3c 100644 --- a/actionmailer/lib/action_mailer/delivery_method.rb +++ b/actionmailer/lib/action_mailer/delivery_method.rb @@ -1,7 +1,7 @@ -require "active_support/core_ext/class" +require 'active_support/core_ext/class' + module ActionMailer module DeliveryMethod - autoload :File, 'action_mailer/delivery_method/file' autoload :Sendmail, 'action_mailer/delivery_method/sendmail' autoload :Smtp, 'action_mailer/delivery_method/smtp' @@ -52,6 +52,5 @@ module ActionMailer superclass_delegating_accessor :settings self.settings = {} end - end end diff --git a/actionmailer/lib/action_mailer/delivery_method/smtp.rb b/actionmailer/lib/action_mailer/delivery_method/smtp.rb index 95c117c9e0..f81d64af36 100644 --- a/actionmailer/lib/action_mailer/delivery_method/smtp.rb +++ b/actionmailer/lib/action_mailer/delivery_method/smtp.rb @@ -1,8 +1,9 @@ +require 'net/smtp' + module ActionMailer module DeliveryMethod # A delivery method implementation which sends via smtp. class Smtp < Method - self.settings = { :address => "localhost", :port => 25, @@ -26,6 +27,5 @@ module ActionMailer end end end - end end -- cgit v1.2.3 From 22752ec27c4eeb50ec12ed2f147f1c066062cabd Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 22 Dec 2009 17:36:29 -0600 Subject: All ARes modules are safe to defer --- activeresource/lib/active_resource.rb | 18 ++++++++---------- activeresource/lib/active_resource/base.rb | 3 +++ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/activeresource/lib/active_resource.rb b/activeresource/lib/active_resource.rb index e0a6ecbcce..3e4a1dd4a1 100644 --- a/activeresource/lib/active_resource.rb +++ b/activeresource/lib/active_resource.rb @@ -33,14 +33,12 @@ require 'active_model' module ActiveResource extend ActiveSupport::Autoload - eager_autoload do - autoload :Base - autoload :Connection - autoload :CustomMethods - autoload :Formats - autoload :HttpMock - autoload :Observing - autoload :Schema - autoload :Validations - end + autoload :Base + autoload :Connection + autoload :CustomMethods + autoload :Formats + autoload :HttpMock + autoload :Observing + autoload :Schema + autoload :Validations end diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index b833e9c8ce..a6243e7011 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -13,6 +13,9 @@ require 'set' require 'uri' require 'active_resource/exceptions' +require 'active_resource/connection' +require 'active_resource/formats' +require 'active_resource/schema' module ActiveResource # ActiveResource::Base is the main class for mapping RESTful resources as models in a Rails application. -- cgit v1.2.3 From f737c2d69bb3659a553c7c0e21e316b1a4a1b98a Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 22 Dec 2009 17:39:41 -0600 Subject: All AMo modules are safe to defer --- activemodel/lib/active_model.rb | 42 +++++++++++++---------------- activemodel/lib/active_model/validations.rb | 1 + 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index 46caa53219..e0de27b96d 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -29,33 +29,29 @@ require 'active_support' module ActiveModel extend ActiveSupport::Autoload - eager_autoload do - autoload :AttributeMethods - autoload :Conversion - autoload :DeprecatedErrorMethods - autoload :Dirty - autoload :Errors - autoload :Lint - autoload :Name, 'active_model/naming' - autoload :Naming - autoload :Observer, 'active_model/observing' - autoload :Observing - autoload :Serialization - autoload :StateMachine - autoload :Translation - autoload :Validations - autoload :ValidationsRepairHelper - autoload :Validator - autoload :VERSION - end + autoload :AttributeMethods + autoload :Conversion + autoload :DeprecatedErrorMethods + autoload :Dirty + autoload :Errors + autoload :Lint + autoload :Name, 'active_model/naming' + autoload :Naming + autoload :Observer, 'active_model/observing' + autoload :Observing + autoload :Serialization + autoload :StateMachine + autoload :Translation + autoload :Validations + autoload :ValidationsRepairHelper + autoload :Validator + autoload :VERSION module Serializers extend ActiveSupport::Autoload - eager_autoload do - autoload :JSON - autoload :Xml - end + autoload :JSON + autoload :Xml end end diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb index 064ec98f3a..a0d64507f9 100644 --- a/activemodel/lib/active_model/validations.rb +++ b/activemodel/lib/active_model/validations.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/hash/keys' +require 'active_model/errors' module ActiveModel module Validations -- cgit v1.2.3 From fe5f66041395f7afa0359af6b075604c63574c85 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 22 Dec 2009 17:25:34 -0800 Subject: Dont encourage __FILE__ bullshit --- actionpack/lib/action_dispatch/testing/integration.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 5c127dfe37..2a5f5dcd5c 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -411,7 +411,7 @@ module ActionDispatch # At its simplest, you simply extend IntegrationTest and write your tests # using the get/post methods: # - # require "#{File.dirname(__FILE__)}/test_helper" + # require "test_helper" # # class ExampleTest < ActionController::IntegrationTest # fixtures :people @@ -435,7 +435,7 @@ module ActionDispatch # powerful testing DSL that is specific for your application. You can even # reference any named routes you happen to have defined! # - # require "#{File.dirname(__FILE__)}/test_helper" + # require "test_helper" # # class AdvancedTest < ActionController::IntegrationTest # fixtures :people, :rooms -- cgit v1.2.3 From ec095456d859da4a09c7401585c211dc0f01fccd Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 22 Dec 2009 17:29:25 -0800 Subject: Dont auto require rubygems, move dep on rack-test to Gemfile --- Gemfile | 1 + railties/lib/rails/test_help.rb | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index d979add5b3..8a5119de8d 100644 --- a/Gemfile +++ b/Gemfile @@ -14,6 +14,7 @@ gem "mysql", ">= 2.8.1" # AP gem "rack", "1.0.1", :git => "git://github.com/rails/rack.git" +gem "rack-test", "0.5.3" gem "RedCloth", ">= 4.2.2" if ENV['CI'] diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb index b89b7b5c27..2601765065 100644 --- a/railties/lib/rails/test_help.rb +++ b/railties/lib/rails/test_help.rb @@ -2,9 +2,8 @@ # so fixtures are loaded to the right database silence_warnings { RAILS_ENV = "test" } -require 'rubygems' -gem "rack", "~> 1.0.0" -gem "rack-test", "~> 0.5.0" +require 'rack' +require 'rack/test' require 'test/unit' require 'active_support/core_ext/kernel/requires' -- cgit v1.2.3 From 74b2e00ce848fac41409eedced1cd671f473b5ce Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 22 Dec 2009 19:44:38 -0600 Subject: Require rack-mount 0.3.3 Fixes "Rack-mount boot time is slow as shit" [#3567 state:resolved] --- actionpack/actionpack.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index 1294cd4993..ab9c7f9c35 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -18,7 +18,7 @@ Gem::Specification.new do |s| s.add_dependency('activemodel', '= 3.0.pre') s.add_dependency('rack', '~> 1.1.0') s.add_dependency('rack-test', '~> 0.5.0') - s.add_dependency('rack-mount', '~> 0.3.2') + s.add_dependency('rack-mount', '~> 0.3.3') s.add_dependency('erubis', '~> 2.6.5') s.require_path = 'lib' -- cgit v1.2.3 From 808cad2bb4f1534a66e20fb5bfedd09e3678e278 Mon Sep 17 00:00:00 2001 From: Dwayne Litzenberger Date: Tue, 22 Dec 2009 15:17:55 -0500 Subject: Fix ActiveSupport::JSON encoding of control characters [\x00-\x1f] According to RFC 4627, only the following Unicode code points are allowed unescaped in JSON: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF However, ActiveSupport::JSON did not escape the range %x00-1f. This caused parse errors when trying to decode the resulting output. [#3345 state:committed] Signed-off-by: Jeremy Kemper --- activesupport/lib/active_support/json/encoding.rb | 13 +++++++++++-- activesupport/test/json/encoding_test.rb | 4 +++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 3c15056c41..c8415d5449 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -65,6 +65,15 @@ module ActiveSupport ESCAPED_CHARS = { + "\x00" => '\u0000', "\x01" => '\u0001', "\x02" => '\u0002', + "\x03" => '\u0003', "\x04" => '\u0004', "\x05" => '\u0005', + "\x06" => '\u0006', "\x07" => '\u0007', "\x0B" => '\u000B', + "\x0E" => '\u000E', "\x0F" => '\u000F', "\x10" => '\u0010', + "\x11" => '\u0011', "\x12" => '\u0012', "\x13" => '\u0013', + "\x14" => '\u0014', "\x15" => '\u0015', "\x16" => '\u0016', + "\x17" => '\u0017', "\x18" => '\u0018', "\x19" => '\u0019', + "\x1A" => '\u001A', "\x1B" => '\u001B', "\x1C" => '\u001C', + "\x1D" => '\u001D', "\x1E" => '\u001E', "\x1F" => '\u001F', "\010" => '\b', "\f" => '\f', "\n" => '\n', @@ -86,9 +95,9 @@ module ActiveSupport def escape_html_entities_in_json=(value) self.escape_regex = \ if @escape_html_entities_in_json = value - /[\010\f\n\r\t"\\><&]/ + /[\x00-\x1F"\\><&]/ else - /[\010\f\n\r\t"\\]/ + /[\x00-\x1F"\\]/ end end diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index 5d81d09f03..cf9a635b5f 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -23,7 +23,9 @@ class TestJSONEncoding < Test::Unit::TestCase StringTests = [[ 'this is the ', %("this is the \\u003Cstring\\u003E")], [ 'a "string" with quotes & an ampersand', %("a \\"string\\" with quotes \\u0026 an ampersand") ], - [ 'http://test.host/posts/1', %("http://test.host/posts/1")]] + [ 'http://test.host/posts/1', %("http://test.host/posts/1")], + [ "Control characters: \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + %("Control characters: \\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000B\\f\\r\\u000E\\u000F\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F") ]] ArrayTests = [[ ['a', 'b', 'c'], %([\"a\",\"b\",\"c\"]) ], [ [1, 'a', :b, nil, false], %([1,\"a\",\"b\",null,false]) ]] -- cgit v1.2.3 From 24e1b5560806be54a931922f109f50800dcbbdf5 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 23 Dec 2009 13:06:53 -0800 Subject: Fix bare string Rack response bodies --- actionpack/test/dispatch/response_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index 59ad2e48bd..02f63f7006 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -178,7 +178,7 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest @app = lambda { |env| [200, {'ETag' => '"202cb962ac59075b964b07152d234b70"', - 'Cache-Control' => 'public'}, 'Hello'] + 'Cache-Control' => 'public'}, ['Hello']] } get '/' @@ -217,7 +217,7 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest @app = lambda { |env| [200, {'Content-Type' => 'application/xml; charset=utf-16'}, - 'Hello'] + ['Hello']] } get '/' -- cgit v1.2.3 From dc677f7665e5ec74b5a313ba656bba19dc0f853d Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Tue, 22 Dec 2009 17:03:23 -0800 Subject: tests pass with requiring the frameworks in rails.rb --- railties/lib/rails.rb | 42 ++++-------------- railties/lib/rails/application.rb | 58 +++++++++++++++---------- railties/lib/rails/core.rb | 34 +++++++++++++++ railties/test/application/configuration_test.rb | 41 +++++++++-------- railties/test/application/generators_test.rb | 30 +++++++------ railties/test/application/initializer_test.rb | 26 ----------- railties/test/application/load_test.rb | 2 - railties/test/isolation/abstract_unit.rb | 8 ++++ 8 files changed, 123 insertions(+), 118 deletions(-) diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb index 85aeb4af24..9fb3cd9f94 100644 --- a/railties/lib/rails.rb +++ b/railties/lib/rails.rb @@ -1,33 +1,9 @@ -require "pathname" - -require 'active_support' -require 'active_support/core_ext/kernel/reporting' -require 'active_support/core_ext/logger' -require 'action_dispatch' - -require 'rails/initializable' -require 'rails/application' -require 'rails/plugin' -require 'rails/railties_path' -require 'rails/version' -require 'rails/rack' -require 'rails/paths' -require 'rails/core' -require 'rails/configuration' -require 'rails/deprecation' -require 'rails/initializer' -require 'rails/ruby_version_check' - -# For Ruby 1.8, this initialization sets $KCODE to 'u' to enable the -# multibyte safe operations. Plugin authors supporting other encodings -# should override this behaviour and set the relevant +default_charset+ -# on ActionController::Base. -# -# For Ruby 1.9, UTF-8 is the default internal and external encoding. -if RUBY_VERSION < '1.9' - $KCODE='u' -else - Encoding.default_external = Encoding::UTF_8 -end - -RAILS_ENV = (ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development").dup unless defined?(RAILS_ENV) +require "rails/core" + +%w(active_model active_record action_controller action_view action_mailer active_resource).each do |framework| + begin + require framework + require "#{framework}/rails" + rescue LoadError + end +end \ No newline at end of file diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index d7a89ba2be..97f72b106b 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -1,3 +1,5 @@ +require "fileutils" + module Rails class Application include Initializable @@ -140,13 +142,13 @@ module Rails $LOAD_PATH.uniq! end - # Requires all frameworks specified by the Configuration#frameworks - # list. By default, all frameworks (Active Record, Active Support, - # Action Pack, Action Mailer, and Active Resource) are loaded. - initializer :require_frameworks do - require 'active_support/all' unless config.active_support.bare - config.frameworks.each { |framework| require(framework.to_s) } - end + # # Requires all frameworks specified by the Configuration#frameworks + # # list. By default, all frameworks (Active Record, Active Support, + # # Action Pack, Action Mailer, and Active Resource) are loaded. + # initializer :require_frameworks do + # require 'active_support/all' unless config.active_support.bare + # config.frameworks.each { |framework| require(framework.to_s) } + # end # Set the paths from which Rails will automatically load source files, and # the load_once paths. @@ -192,7 +194,7 @@ module Rails # this sets the database configuration from Configuration#database_configuration # and then establishes the connection. initializer :initialize_database do - if config.frameworks.include?(:active_record) + if defined?(ActiveRecord) ActiveRecord::Base.configurations = config.database_configuration ActiveRecord::Base.establish_connection end @@ -206,7 +208,7 @@ module Rails end initializer :initialize_middleware_stack do - if config.frameworks.include?(:action_controller) + if defined?(ActionController) config.middleware.use(::Rack::Lock, :if => lambda { ActionController::Base.allow_concurrency }) config.middleware.use(::Rack::Runtime) config.middleware.use(ActionDispatch::ShowExceptions, lambda { ActionController::Base.consider_all_requests_local }) @@ -231,7 +233,7 @@ module Rails end initializer :initialize_framework_caches do - if config.frameworks.include?(:action_controller) + if defined?(ActionController) ActionController::Base.cache_store ||= RAILS_CACHE end end @@ -266,8 +268,12 @@ module Rails # logger is already set, it is not changed, otherwise it is set to use # RAILS_DEFAULT_LOGGER. initializer :initialize_framework_logging do - for framework in ([ :active_record, :action_controller, :action_mailer ] & config.frameworks) - framework.to_s.camelize.constantize.const_get("Base").logger ||= Rails.logger + for framework in [ :active_record, :action_controller, :action_mailer ] + # TODO BEFORE PUSHING: REMOVEZ + begin + framework.to_s.camelize.constantize.const_get("Base").logger ||= Rails.logger + rescue Exception + end end ActiveSupport::Dependencies.logger ||= Rails.logger @@ -302,7 +308,7 @@ module Rails Time.zone_default = zone_default - if config.frameworks.include?(:active_record) + if defined?(ActiveRecord) ActiveRecord::Base.time_zone_aware_attributes = true ActiveRecord::Base.default_timezone = :utc end @@ -326,10 +332,14 @@ module Rails # on each of the corresponding Base classes. initializer :initialize_framework_settings do config.frameworks.each do |framework| - base_class = framework.to_s.camelize.constantize.const_get("Base") + # TODO BEFORE PUSHING: This needs to work differently + begin + base_class = framework.to_s.camelize.constantize.const_get("Base") - config.send(framework).each do |setting, value| - base_class.send("#{setting}=", value) + config.send(framework).each do |setting, value| + base_class.send("#{setting}=", value) + end + rescue Exception end end end @@ -339,16 +349,16 @@ module Rails # paths have already been set, it is not changed, otherwise it is # set to use Configuration#view_path. initializer :initialize_framework_views do - if config.frameworks.include?(:action_view) + if defined?(ActionView) view_path = ActionView::PathSet.type_cast(config.view_path, config.cache_classes) - ActionMailer::Base.template_root = view_path if config.frameworks.include?(:action_mailer) && ActionMailer::Base.view_paths.blank? - ActionController::Base.view_paths = view_path if config.frameworks.include?(:action_controller) && ActionController::Base.view_paths.blank? + + ActionMailer::Base.template_root = view_path if defined?(ActionMailer) && ActionMailer::Base.view_paths.blank? + ActionController::Base.view_paths = view_path if defined?(ActionController) && ActionController::Base.view_paths.blank? end end initializer :initialize_metal do - # TODO: Make Rails and metal work without ActionController - if config.frameworks.include?(:action_controller) + if defined?(ActionController) Rails::Rack::Metal.requested_metals = config.metals config.middleware.insert_before( @@ -375,8 +385,8 @@ module Rails # # Setup database middleware after initializers have run initializer :initialize_database_middleware do - if configuration.frameworks.include?(:active_record) - if configuration.frameworks.include?(:action_controller) && ActionController::Base.session_store && + if defined?(ActiveRecord) + if defined?(ActionController) && ActionController::Base.session_store && ActionController::Base.session_store.name == 'ActiveRecord::SessionStore' configuration.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::ConnectionAdapters::ConnectionManagement configuration.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::QueryCache @@ -422,7 +432,7 @@ module Rails # # # Observers are loaded after plugins in case Observers or observed models are modified by plugins. initializer :load_observers do - if configuration.frameworks.include?(:active_record) + if defined?(ActiveRecord) ActiveRecord::Base.instantiate_observers end end diff --git a/railties/lib/rails/core.rb b/railties/lib/rails/core.rb index a5e51ad04a..da16c5816c 100644 --- a/railties/lib/rails/core.rb +++ b/railties/lib/rails/core.rb @@ -1,3 +1,37 @@ +require "pathname" + +require 'active_support' +require 'active_support/core_ext/kernel/reporting' +require 'active_support/core_ext/logger' +require 'action_dispatch' + +require 'rails/initializable' +require 'rails/application' +require 'rails/plugin' +require 'rails/railties_path' +require 'rails/version' +require 'rails/rack' +require 'rails/paths' +require 'rails/core' +require 'rails/configuration' +require 'rails/deprecation' +require 'rails/initializer' +require 'rails/ruby_version_check' + +# For Ruby 1.8, this initialization sets $KCODE to 'u' to enable the +# multibyte safe operations. Plugin authors supporting other encodings +# should override this behaviour and set the relevant +default_charset+ +# on ActionController::Base. +# +# For Ruby 1.9, UTF-8 is the default internal and external encoding. +if RUBY_VERSION < '1.9' + $KCODE='u' +else + Encoding.default_external = Encoding::UTF_8 +end + +RAILS_ENV = (ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development").dup unless defined?(RAILS_ENV) + module Rails # Needs to be duplicated from Active Support since its needed before Active # Support is available. Here both Options and Hash are namespaced to prevent diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 83e1401993..0f702c7014 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -4,7 +4,16 @@ module ApplicationTests class InitializerTest < Test::Unit::TestCase include ActiveSupport::Testing::Isolation + def new_app + File.expand_path("#{app_path}/../new_app") + end + + def copy_app + FileUtils.cp_r(app_path, new_app) + end + def setup + FileUtils.rm_rf(new_app) if File.directory?(new_app) build_app boot_rails end @@ -15,42 +24,36 @@ module ApplicationTests end test "the application root can be set" do - FileUtils.mkdir_p("#{app_path}/hello") + copy_app add_to_config <<-RUBY - config.frameworks = [] - config.root = '#{app_path}/hello' + config.root = '#{new_app}' RUBY - require "#{app_path}/config/environment" - assert_equal Pathname.new("#{app_path}/hello"), Rails.application.root - end - test "the application root is detected as where config.ru is located" do - add_to_config <<-RUBY - config.frameworks = [] - RUBY - FileUtils.mv "#{app_path}/config.ru", "#{app_path}/config/config.ru" + use_frameworks [] + require "#{app_path}/config/environment" - assert_equal Pathname.new("#{app_path}/config"), Rails.application.root + assert_equal Pathname.new(new_app), Rails.application.root end test "the application root is Dir.pwd if there is no config.ru" do File.delete("#{app_path}/config.ru") - add_to_config <<-RUBY - config.frameworks = [] - RUBY - Dir.chdir("#{app_path}/app") do + use_frameworks [] + + Dir.chdir("#{app_path}") do require "#{app_path}/config/environment" - assert_equal Pathname.new("#{app_path}/app"), Rails.application.root + assert_equal Pathname.new("#{app_path}"), Rails.application.root end end test "config.active_support.bare does not require all of ActiveSupport" do - add_to_config "config.frameworks = []; config.active_support.bare = true" + add_to_config "config.active_support.bare = true" + + use_frameworks [] Dir.chdir("#{app_path}/app") do require "#{app_path}/config/environment" - assert_raises(NoMethodError) { 1.day } + assert_raises(NoMethodError) { [1,2,3].rand } end end diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb index 2ed49d1057..7b27c780aa 100644 --- a/railties/test/application/generators_test.rb +++ b/railties/test/application/generators_test.rb @@ -7,8 +7,6 @@ module ApplicationTests def setup build_app boot_rails - require "rails" - require "rails/generators" end def app_const @@ -16,6 +14,8 @@ module ApplicationTests end def with_config + require "rails" + require "rails/generators" yield app_const.config end @@ -46,14 +46,15 @@ module ApplicationTests end test "generators aliases and options on initialization" do - application = with_config do |c| - c.frameworks = [] - c.generators.rails :aliases => { :test_framework => "-w" } - c.generators.orm :datamapper - c.generators.test_framework :rspec - end + add_to_config <<-RUBY + config.generators.rails :aliases => { :test_framework => "-w" } + config.generators.orm :datamapper + config.generators.test_framework :rspec + RUBY + + require "#{app_path}/config/environment" # Initialize the application - app_const.initialize! + require "rails/generators" Rails::Generators.configure! assert_equal :rspec, Rails::Generators.options[:rails][:test_framework] @@ -61,12 +62,13 @@ module ApplicationTests end test "generators no color on initialization" do - with_config do |c| - c.frameworks = [] - c.generators.colorize_logging = false - end + add_to_config <<-RUBY + config.generators.colorize_logging = false + RUBY + # Initialize the application - app_const.initialize! + require "#{app_path}/config/environment" + require "rails/generators" Rails::Generators.configure! assert_equal Thor::Base.shell, Thor::Shell::Basic diff --git a/railties/test/application/initializer_test.rb b/railties/test/application/initializer_test.rb index b3eff1deb9..8ed8e11c8f 100644 --- a/railties/test/application/initializer_test.rb +++ b/railties/test/application/initializer_test.rb @@ -19,19 +19,6 @@ module ApplicationTests assert $:.include?("#{app_path}/app/models") end - test "adding an unknown framework raises an error" do - add_to_config <<-RUBY - config.root = "#{app_path}" - config.frameworks << :action_foo - RUBY - - require "active_support/core_ext/load_error" - - assert_raises MissingSourceFile do - require "#{app_path}/config/environment" - end - end - test "eager loading loads parent classes before children" do app_file "lib/zoo.rb", <<-ZOO class Zoo ; include ReptileHouse ; end @@ -180,19 +167,6 @@ module ApplicationTests assert !Rails.application.config.middleware.include?(ActiveRecord::SessionStore) end - - # Pathview test - test "load view paths doesn't perform anything when action_view not in frameworks" do - add_to_config <<-RUBY - config.root = "#{app_path}" - config.frameworks -= [:action_view] - RUBY - require "#{app_path}/config/environment" - - assert_equal nil, ActionMailer::Base.template_root - assert_equal [], ActionController::Base.view_paths - end - test "Rails.root should be a Pathname" do add_to_config <<-RUBY config.root = "#{app_path}" diff --git a/railties/test/application/load_test.rb b/railties/test/application/load_test.rb index e17f1ebdb0..689bc77ecf 100644 --- a/railties/test/application/load_test.rb +++ b/railties/test/application/load_test.rb @@ -1,6 +1,4 @@ require "isolation/abstract_unit" -# require "rails" -# require 'action_dispatch' module ApplicationTests class LoadTest < Test::Unit::TestCase diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index c169c80bea..ee0a812b47 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -153,6 +153,14 @@ module TestHelpers app_file("app/controllers/#{name}_controller.rb", contents) end + def use_frameworks(arr) + to_remove = [:actionmailer, + :activemodel, + :activerecord, + :activeresource] - arr + $:.reject! {|path| path =~ %r'/(#{to_remove.join('|')})/' } + end + def boot_rails root = File.expand_path('../../../..', __FILE__) begin -- cgit v1.2.3 From fa8dfc7d014f6768599077b79a874894e13d317f Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Wed, 23 Dec 2009 13:45:55 -0800 Subject: Raise an exception if an initializer is defined without a block --- railties/lib/rails/initializable.rb | 1 + railties/test/initializable_test.rb | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb index add10bd207..8fcb254590 100644 --- a/railties/lib/rails/initializable.rb +++ b/railties/lib/rails/initializable.rb @@ -93,6 +93,7 @@ module Rails end def initializer(name, opts = {}, &blk) + raise ArgumentError, "A block must be passed when defining an initializer" unless blk @initializers ||= [] @initializers << Initializer.new(name, nil, opts, &blk) end diff --git a/railties/test/initializable_test.rb b/railties/test/initializable_test.rb index a9e60680ac..e308cbcb0e 100644 --- a/railties/test/initializable_test.rb +++ b/railties/test/initializable_test.rb @@ -150,6 +150,16 @@ module InitializableTests Word.run_initializers assert_equal "bird", $word end + + test "creating initializer without a block raises an error" do + assert_raise(ArgumentError) do + Class.new do + include Rails::Initializable + + initializer :foo + end + end + end end class BeforeAfter < ActiveSupport::TestCase -- cgit v1.2.3 From 38aeb1528c376f7a058beea6db0a328720b85f01 Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Wed, 23 Dec 2009 14:55:12 -0800 Subject: Moving out some framework specific initializers into the framework libraries. --- Gemfile | 2 + actionpack/lib/action_controller/rails.rb | 27 ++++++++++ activerecord/lib/active_record/rails.rb | 51 ++++++++++++++++++ railties/lib/rails/application.rb | 66 +++-------------------- railties/lib/rails/configuration.rb | 25 +++++---- railties/lib/rails/plugin.rb | 8 +-- railties/test/application/initializer_test.rb | 40 ++++++-------- railties/test/plugins/framework_extension_test.rb | 18 +++++++ 8 files changed, 141 insertions(+), 96 deletions(-) create mode 100644 actionpack/lib/action_controller/rails.rb create mode 100644 activerecord/lib/active_record/rails.rb create mode 100644 railties/test/plugins/framework_extension_test.rb diff --git a/Gemfile b/Gemfile index aaacbce8c6..361074dce6 100644 --- a/Gemfile +++ b/Gemfile @@ -31,3 +31,5 @@ if ENV['CI'] gem "test-unit", ">= 2.0.5" end end + +disable_system_gems \ No newline at end of file diff --git a/actionpack/lib/action_controller/rails.rb b/actionpack/lib/action_controller/rails.rb new file mode 100644 index 0000000000..c2d753f9ef --- /dev/null +++ b/actionpack/lib/action_controller/rails.rb @@ -0,0 +1,27 @@ +module ActionController + class Plugin < Rails::Plugin + plugin_name :action_controller + + initializer "action_controller.set_configs" do |app| + app.config.action_controller.each do |k,v| + ActionController::Base.send "#{k}=", v + end + end + + # TODO: ActionController::Base.logger should delegate to its own config.logger + initializer "action_controller.logger" do + ActionController::Base.logger ||= Rails.logger + end + + # Routing must be initialized after plugins to allow the former to extend the routes + # --- + # If Action Controller is not one of the loaded frameworks (Configuration#frameworks) + # this does nothing. Otherwise, it loads the routing definitions and sets up + # loading module used to lazily load controllers (Configuration#controller_paths). + initializer "action_controller.initialize_routing" do |app| + app.route_configuration_files << app.config.routes_configuration_file + app.route_configuration_files << app.config.builtin_routes_configuration_file + app.reload_routes! + end + end +end \ No newline at end of file diff --git a/activerecord/lib/active_record/rails.rb b/activerecord/lib/active_record/rails.rb new file mode 100644 index 0000000000..4071385563 --- /dev/null +++ b/activerecord/lib/active_record/rails.rb @@ -0,0 +1,51 @@ +# For now, action_controller must always be present with +# rails, so let's make sure that it gets required before +# here. This is needed for correctly setting up the middleware. +# In the future, this might become an optional require. +require "action_controller/rails" + +module ActiveRecord + class Plugin < Rails::Plugin + plugin_name :active_record + + initializer "active_record.set_configs" do |app| + app.config.active_record.each do |k,v| + ActiveRecord::Base.send "#{k}=", v + end + end + + # This sets the database configuration from Configuration#database_configuration + # and then establishes the connection. + initializer "active_record.initialize_database" do |app| + ActiveRecord::Base.configurations = app.config.database_configuration + ActiveRecord::Base.establish_connection + end + + initializer "active_record.initialize_timezone" do + ActiveRecord::Base.time_zone_aware_attributes = true + ActiveRecord::Base.default_timezone = :utc + end + + # Setup database middleware after initializers have run + initializer "active_record.initialize_database_middleware" do |app| + middleware = app.config.middleware + if middleware.include?(ActiveRecord::SessionStore) + middleware.insert_before ActiveRecord::SessionStore, ActiveRecord::ConnectionAdapters::ConnectionManagement + middleware.insert_before ActiveRecord::SessionStore, ActiveRecord::QueryCache + else + middleware.use ActiveRecord::ConnectionAdapters::ConnectionManagement + middleware.use ActiveRecord::QueryCache + end + end + + initializer "active_record.load_observers" do + ActiveRecord::Base.instantiate_observers + end + + # TODO: ActiveRecord::Base.logger should delegate to its own config.logger + initializer "active_record.logger" do + ActiveRecord::Base.logger ||= Rails.logger + end + + end +end \ No newline at end of file diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 97f72b106b..9a5656fb1d 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -21,11 +21,7 @@ module Rails end def config - @config ||= begin - config = Configuration.new - Plugin.plugins.each { |p| config.merge(p.config) } - config - end + @config ||= Configuration.new(Plugin::Configuration.default) end # TODO: change the plugin loader to use config @@ -122,10 +118,11 @@ module Rails initializers end + # TODO: Fix this method def plugins @plugins ||= begin plugin_names = config.plugins || [:all] - Plugin.plugins.select { |p| plugin_names.include?(p.plugin_name) } + + Plugin.plugins.select { |p| plugin_names.include?(:all) || plugin_names.include?(p.plugin_name) } + Plugin::Vendored.all(config.plugins || [:all], config.paths.vendor.plugins) end end @@ -189,20 +186,9 @@ module Rails end end - # This initialization routine does nothing unless :active_record - # is one of the frameworks to load (Configuration#frameworks). If it is, - # this sets the database configuration from Configuration#database_configuration - # and then establishes the connection. - initializer :initialize_database do - if defined?(ActiveRecord) - ActiveRecord::Base.configurations = config.database_configuration - ActiveRecord::Base.establish_connection - end - end - # Include middleware to serve up static assets initializer :initialize_static_server do - if config.frameworks.include?(:action_controller) && config.serve_static_assets + if defined?(ActionController) && config.serve_static_assets config.middleware.use(ActionDispatch::Static, Rails.public_path) end end @@ -268,7 +254,7 @@ module Rails # logger is already set, it is not changed, otherwise it is set to use # RAILS_DEFAULT_LOGGER. initializer :initialize_framework_logging do - for framework in [ :active_record, :action_controller, :action_mailer ] + for framework in [ :action_controller, :action_mailer ] # TODO BEFORE PUSHING: REMOVEZ begin framework.to_s.camelize.constantize.const_get("Base").logger ||= Rails.logger @@ -293,7 +279,7 @@ module Rails require('active_support/whiny_nil') if config.whiny_nils end - # Sets the default value for Time.zone, and turns on ActiveRecord::Base#time_zone_aware_attributes. + # Sets the default value for Time.zone # If assigned value cannot be matched to a TimeZone, an exception will be raised. initializer :initialize_time_zone do if config.time_zone @@ -307,11 +293,6 @@ module Rails end Time.zone_default = zone_default - - if defined?(ActiveRecord) - ActiveRecord::Base.time_zone_aware_attributes = true - ActiveRecord::Base.default_timezone = :utc - end end end @@ -331,7 +312,7 @@ module Rails # (Configuration#frameworks). The available settings map to the accessors # on each of the corresponding Base classes. initializer :initialize_framework_settings do - config.frameworks.each do |framework| + (config.frameworks - [:active_record, :action_controller]).each do |framework| # TODO BEFORE PUSHING: This needs to work differently begin base_class = framework.to_s.camelize.constantize.const_get("Base") @@ -383,20 +364,6 @@ module Rails end end - # # Setup database middleware after initializers have run - initializer :initialize_database_middleware do - if defined?(ActiveRecord) - if defined?(ActionController) && ActionController::Base.session_store && - ActionController::Base.session_store.name == 'ActiveRecord::SessionStore' - configuration.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::ConnectionAdapters::ConnectionManagement - configuration.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::QueryCache - else - configuration.middleware.use ActiveRecord::ConnectionAdapters::ConnectionManagement - configuration.middleware.use ActiveRecord::QueryCache - end - end - end - # TODO: Make a DSL way to limit an initializer to a particular framework # # Prepare dispatcher callbacks and run 'prepare' callbacks @@ -418,25 +385,6 @@ module Rails end end - # Routing must be initialized after plugins to allow the former to extend the routes - # --- - # If Action Controller is not one of the loaded frameworks (Configuration#frameworks) - # this does nothing. Otherwise, it loads the routing definitions and sets up - # loading module used to lazily load controllers (Configuration#controller_paths). - initializer :initialize_routing do - next unless configuration.frameworks.include?(:action_controller) - route_configuration_files << configuration.routes_configuration_file - route_configuration_files << configuration.builtin_routes_configuration_file - reload_routes! - end - # - # # Observers are loaded after plugins in case Observers or observed models are modified by plugins. - initializer :load_observers do - if defined?(ActiveRecord) - ActiveRecord::Base.instantiate_observers - end - end - # Eager load application classes initializer :load_application_classes do next if $rails_rake_task diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb index bf5b9478cc..086f67a419 100644 --- a/railties/lib/rails/configuration.rb +++ b/railties/lib/rails/configuration.rb @@ -5,22 +5,26 @@ module Rails # configuration class while this bit is being cleaned up. class Plugin::Configuration - def initialize - @options = Hash.new { |h,k| h[k] = ActiveSupport::OrderedOptions.new } + def self.default + @default ||= new end - def middleware - @middleware ||= ActionDispatch::MiddlewareStack.new + attr_reader :middleware + + def initialize(base = nil) + if base + @options = base.options.dup + @middleware = base.middleware.dup + else + @options = Hash.new { |h,k| h[k] = ActiveSupport::OrderedOptions.new } + @middleware = ActionDispatch::MiddlewareStack.new + end end def respond_to?(name) super || name.to_s =~ config_key_regexp end - def merge(config) - @options = config.options.merge(@options) - end - protected attr_reader :options @@ -41,8 +45,7 @@ module Rails end def config_keys - ([ :active_support, :active_record, :action_controller, - :action_view, :action_mailer, :active_resource ] + + ([ :active_support, :action_view, :action_mailer, :active_resource ] + Plugin.plugin_names).map { |n| n.to_s }.uniq end end @@ -60,7 +63,7 @@ module Rails :log_level, :log_path, :paths, :routes_configuration_file, :view_path - def initialize + def initialize(base = nil) super @load_once_paths = [] @after_initialize_blocks = [] diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb index 90dc1ad8dd..0699affea7 100644 --- a/railties/lib/rails/plugin.rb +++ b/railties/lib/rails/plugin.rb @@ -2,8 +2,10 @@ module Rails class Plugin include Initializable - def self.plugin_name - @plugin_name || name.demodulize.underscore + def self.plugin_name(plugin_name = nil) + @plugin_name ||= name.demodulize.underscore + @plugin_name = plugin_name if plugin_name + @plugin_name end def self.inherited(klass) @@ -20,7 +22,7 @@ module Rails end def self.config - @config ||= Configuration.new + Configuration.default end class Vendored < Plugin diff --git a/railties/test/application/initializer_test.rb b/railties/test/application/initializer_test.rb index 8ed8e11c8f..2ecc3e9e2d 100644 --- a/railties/test/application/initializer_test.rb +++ b/railties/test/application/initializer_test.rb @@ -135,21 +135,9 @@ module ApplicationTests assert !Rails.application.config.middleware.include?(ActiveRecord::SessionStore) end - test "database middleware doesn't initialize when activerecord is not in frameworks" do - add_to_config <<-RUBY - config.root = "#{app_path}" - config.frameworks = [] - RUBY - require "#{app_path}/config/environment" - - assert_equal [], Rails.application.config.middleware - end - test "database middleware initializes when session store is active record" do - add_to_config <<-RUBY - config.root = "#{app_path}" - config.action_controller.session_store = :active_record_store - RUBY + add_to_config "config.action_controller.session_store = :active_record_store" + require "#{app_path}/config/environment" expects = [ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActiveRecord::SessionStore] @@ -157,22 +145,28 @@ module ApplicationTests assert_equal expects, middleware & expects end - test "ensure database middleware doesn't use action_controller on initializing" do + test "Rails.root should be a Pathname" do add_to_config <<-RUBY config.root = "#{app_path}" - config.frameworks -= [:action_controller] - config.action_controller.session_store = :active_record_store RUBY require "#{app_path}/config/environment" + assert_instance_of Pathname, Rails.root + end + end - assert !Rails.application.config.middleware.include?(ActiveRecord::SessionStore) + class InitializerCustomFrameworkExtensionsTest < Test::Unit::TestCase + include ActiveSupport::Testing::Isolation + + def setup + build_app + boot_rails end - test "Rails.root should be a Pathname" do - add_to_config <<-RUBY - config.root = "#{app_path}" - RUBY + + test "database middleware doesn't initialize when activerecord is not in frameworks" do + use_frameworks [] require "#{app_path}/config/environment" - assert_instance_of Pathname, Rails.root + + assert !defined?(ActiveRecord) end end end \ No newline at end of file diff --git a/railties/test/plugins/framework_extension_test.rb b/railties/test/plugins/framework_extension_test.rb new file mode 100644 index 0000000000..87e19cadce --- /dev/null +++ b/railties/test/plugins/framework_extension_test.rb @@ -0,0 +1,18 @@ +require "isolation/abstract_unit" + +module PluginsTest + class FrameworkExtensionTest < Test::Unit::TestCase + def setup + build_app + boot_rails + end + + test "active_record extensions are applied to ActiveRecord" do + add_to_config "config.active_record.table_name_prefix = 'tbl_'" + + require "#{app_path}/config/environment" + + assert_equal 'tbl_', ActiveRecord::Base.table_name_prefix + end + end +end \ No newline at end of file -- cgit v1.2.3 From 83be262b4b2e415fe9be319eb6b187bcf415fb6d Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Wed, 23 Dec 2009 16:14:34 -0800 Subject: Isolation tests intentionally avoid loading any state (because they're often testing things that have their own load path semantics that should not be polluted), so rack/test is not yet on the load path. Moving require "rack/test" into the setup means and after boot_rails means that it'll be required after the laod path has been altered to add in the Rails vendor/gems --- railties/test/application/routing_test.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb index 7b1df7f45d..ece9b13f8a 100644 --- a/railties/test/application/routing_test.rb +++ b/railties/test/application/routing_test.rb @@ -1,18 +1,18 @@ require 'isolation/abstract_unit' -require 'rack/test' module ApplicationTests class RoutingTest < Test::Unit::TestCase include ActiveSupport::Testing::Isolation - include Rack::Test::Methods def setup build_app + boot_rails + require 'rack/test' + extend Rack::Test::Methods end def app @app ||= begin - boot_rails require "#{app_path}/config/environment" Rails.application -- cgit v1.2.3 From 61af34b001b295af36c346d245a65d74de0e1f97 Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Wed, 23 Dec 2009 16:26:10 -0800 Subject: Make /rails/info/properties work again. Also, the mocked up tests were passing so we added a test that actually tested this functionality. --- railties/builtin/routes.rb | 2 +- railties/test/application/routing_test.rb | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/railties/builtin/routes.rb b/railties/builtin/routes.rb index 26a0daaa8c..ef9d9e756d 100644 --- a/railties/builtin/routes.rb +++ b/railties/builtin/routes.rb @@ -1,3 +1,3 @@ ActionController::Routing::Routes.draw do |map| - match '/rails/info/properties' => "rails::info#properties" + match '/rails/info/properties' => "rails/info#properties" end \ No newline at end of file diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb index ece9b13f8a..49c548ad5c 100644 --- a/railties/test/application/routing_test.rb +++ b/railties/test/application/routing_test.rb @@ -19,6 +19,11 @@ module ApplicationTests end end + test "rails/info/properties" do + get "/rails/info/properties" + assert_equal 200, last_response.status + end + test "simple controller" do controller :foo, <<-RUBY class FooController < ActionController::Base -- cgit v1.2.3 From 94bb3316353ace661a83563f44a9c47baf438f26 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 23 Dec 2009 17:11:17 -0800 Subject: Shift more responsibility from application class to its singleton instance. Treat instantiation and boot as separate steps. Use app.config rather than app.configuration. --- .../lib/action_dispatch/routing/route_set.rb | 2 +- railties/lib/rails/application.rb | 123 ++++++++------------- railties/lib/rails/commands/server.rb | 10 +- .../rails/generators/rails/app/templates/config.ru | 2 +- .../rails/app/templates/script/console.tt | 2 +- .../rails/app/templates/script/dbconsole.tt | 2 +- .../rails/app/templates/script/server.tt | 2 +- 7 files changed, 56 insertions(+), 87 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index a4dc5e0956..498ad3268c 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -273,7 +273,7 @@ module ActionDispatch # TODO: Move this into Railties if defined?(Rails.application) # Find namespaces in controllers/ directory - Rails.application.configuration.controller_paths.each do |load_path| + Rails.application.config.controller_paths.each do |load_path| load_path = File.expand_path(load_path) Dir["#{load_path}/**/*_controller.rb"].collect do |path| namespaces << File.dirname(path).sub(/#{load_path}\/?/, '') diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index d7a89ba2be..c594b8a31d 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -1,21 +1,17 @@ +require 'active_support/core_ext/module/delegation' + module Rails class Application include Initializable class << self - # Stub out App initialize - def initialize! - new - end + attr_writer :config + alias configure class_eval + delegate :initialize!, :load_tasks, :to => :instance - def new - @instance ||= begin - begin - require config.environment_path - rescue LoadError - end - super - end + private :new + def instance + @instance ||= new end def config @@ -26,66 +22,29 @@ module Rails end end - # TODO: change the plugin loader to use config - alias configuration config - - def config=(config) - @config = config - end - - def root - config.root - end - - def load_tasks - require "rails/tasks" - Dir["#{root}/vendor/plugins/*/**/tasks/**/*.rake"].sort.each { |ext| load ext } - Dir["#{root}/lib/tasks/**/*.rake"].sort.each { |ext| load ext } - task :environment do - $rails_rake_task = true - initialize! - end - end - def routes ActionController::Routing::Routes end - - def call(env) - new.call(env) - end end + delegate :config, :routes, :to => :'self.class' + delegate :root, :middleware, :to => :config attr_reader :route_configuration_files def initialize + require_environment Rails.application ||= self - @route_configuration_files = [] - - run_initializers(self) - end - - def config - self.class.config - end - - class << self - alias configure class_eval - end - - def root - config.root end - alias configuration config - - def middleware - config.middleware + def initialize! + run_initializers(self) + self end - def routes - ActionController::Routing::Routes + def require_environment + require config.environment_path + rescue LoadError end def routes_changed_at @@ -114,6 +73,16 @@ module Rails routes.disable_clear_and_finalize = false end + def load_tasks + require "rails/tasks" + Dir["#{root}/vendor/plugins/*/**/tasks/**/*.rake"].sort.each { |ext| load ext } + Dir["#{root}/lib/tasks/**/*.rake"].sort.each { |ext| load ext } + task :environment do + $rails_rake_task = true + initialize! + end + end + def initializers initializers = super plugins.each { |p| initializers += p.initializers } @@ -170,7 +139,7 @@ module Rails # Create tmp directories initializer :ensure_tmp_directories_exist do %w(cache pids sessions sockets).each do |dir_to_make| - FileUtils.mkdir_p(File.join(config.root, 'tmp', dir_to_make)) + FileUtils.mkdir_p(File.join(root, 'tmp', dir_to_make)) end end @@ -361,28 +330,28 @@ module Rails # # already called abort() unless $gems_rake_task is set # return unless gems_dependencies_loaded initializer :load_application_initializers do - Dir["#{configuration.root}/config/initializers/**/*.rb"].sort.each do |initializer| + Dir["#{root}/config/initializers/**/*.rb"].sort.each do |initializer| load(initializer) end end # Fires the user-supplied after_initialize block (Configuration#after_initialize) initializer :after_initialize do - configuration.after_initialize_blocks.each do |block| + config.after_initialize_blocks.each do |block| block.call end end # # Setup database middleware after initializers have run initializer :initialize_database_middleware do - if configuration.frameworks.include?(:active_record) - if configuration.frameworks.include?(:action_controller) && ActionController::Base.session_store && + if config.frameworks.include?(:active_record) + if config.frameworks.include?(:action_controller) && ActionController::Base.session_store && ActionController::Base.session_store.name == 'ActiveRecord::SessionStore' - configuration.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::ConnectionAdapters::ConnectionManagement - configuration.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::QueryCache + config.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::ConnectionAdapters::ConnectionManagement + config.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::QueryCache else - configuration.middleware.use ActiveRecord::ConnectionAdapters::ConnectionManagement - configuration.middleware.use ActiveRecord::QueryCache + config.middleware.use ActiveRecord::ConnectionAdapters::ConnectionManagement + config.middleware.use ActiveRecord::QueryCache end end end @@ -391,11 +360,11 @@ module Rails # # Prepare dispatcher callbacks and run 'prepare' callbacks initializer :prepare_dispatcher do - next unless configuration.frameworks.include?(:action_controller) + next unless config.frameworks.include?(:action_controller) require 'rails/dispatcher' unless defined?(::Dispatcher) - Dispatcher.define_dispatcher_callbacks(configuration.cache_classes) + Dispatcher.define_dispatcher_callbacks(config.cache_classes) - unless configuration.cache_classes + unless config.cache_classes # Setup dev mode route reloading routes_last_modified = routes_changed_at reload_routes = lambda do @@ -414,15 +383,15 @@ module Rails # this does nothing. Otherwise, it loads the routing definitions and sets up # loading module used to lazily load controllers (Configuration#controller_paths). initializer :initialize_routing do - next unless configuration.frameworks.include?(:action_controller) - route_configuration_files << configuration.routes_configuration_file - route_configuration_files << configuration.builtin_routes_configuration_file + next unless config.frameworks.include?(:action_controller) + route_configuration_files << config.routes_configuration_file + route_configuration_files << config.builtin_routes_configuration_file reload_routes! end # # # Observers are loaded after plugins in case Observers or observed models are modified by plugins. initializer :load_observers do - if configuration.frameworks.include?(:active_record) + if config.frameworks.include?(:active_record) ActiveRecord::Base.instantiate_observers end end @@ -431,8 +400,8 @@ module Rails initializer :load_application_classes do next if $rails_rake_task - if configuration.cache_classes - configuration.eager_load_paths.each do |load_path| + if config.cache_classes + config.eager_load_paths.each do |load_path| matcher = /\A#{Regexp.escape(load_path)}(.*)\.rb\Z/ Dir.glob("#{load_path}/**/*.rb").sort.each do |file| require_dependency file.sub(matcher, '\1') @@ -443,7 +412,7 @@ module Rails # Disable dependency loading during request cycle initializer :disable_dependency_loading do - if configuration.cache_classes && !configuration.dependency_loading + if config.cache_classes && !config.dependency_loading ActiveSupport::Dependencies.unhook! end end diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb index 3687b4460e..57b7c6a49c 100644 --- a/railties/lib/rails/commands/server.rb +++ b/railties/lib/rails/commands/server.rb @@ -41,9 +41,9 @@ module Rails new(app).start end - def initialize(app_const) + def initialize(app) super() # Call Rack::Server#initialize without passing any options to use. - @app_const = app_const + @app = app end def start @@ -69,7 +69,7 @@ module Rails end def log_path - "#{File.expand_path(@app_const.root)}/log/#{options[:environment]}.log" + "#{File.expand_path(@app.root)}/log/#{options[:environment]}.log" end def default_options @@ -77,10 +77,10 @@ module Rails :Port => 3000, :Host => "0.0.0.0", :environment => (ENV['RAILS_ENV'] || "development").dup, - :rack_file => "#{@app_const.root}/config.ru", + :rack_file => "#{@app.root}/config.ru", :daemonize => false, :debugger => false, - :pid => "#{@app_const.root}/tmp/pids/server.pid", + :pid => "#{@app.root}/tmp/pids/server.pid", :AccessLog => [] } end diff --git a/railties/lib/rails/generators/rails/app/templates/config.ru b/railties/lib/rails/generators/rails/app/templates/config.ru index f3bf3d6117..acb8435446 100644 --- a/railties/lib/rails/generators/rails/app/templates/config.ru +++ b/railties/lib/rails/generators/rails/app/templates/config.ru @@ -2,4 +2,4 @@ require ::File.expand_path('../config/environment', __FILE__) # Dispatch the request -run <%= app_const%> +run <%= app_const %>.instance diff --git a/railties/lib/rails/generators/rails/app/templates/script/console.tt b/railties/lib/rails/generators/rails/app/templates/script/console.tt index 4262439e52..9ddd4cfe62 100755 --- a/railties/lib/rails/generators/rails/app/templates/script/console.tt +++ b/railties/lib/rails/generators/rails/app/templates/script/console.tt @@ -1,3 +1,3 @@ require File.expand_path('../../config/application', __FILE__) require 'rails/commands/console' -Rails::Console.start(<%= app_const %>) +Rails::Console.start(<%= app_const %>.instance) diff --git a/railties/lib/rails/generators/rails/app/templates/script/dbconsole.tt b/railties/lib/rails/generators/rails/app/templates/script/dbconsole.tt index 9dfa24c378..96e0bc191b 100755 --- a/railties/lib/rails/generators/rails/app/templates/script/dbconsole.tt +++ b/railties/lib/rails/generators/rails/app/templates/script/dbconsole.tt @@ -1,3 +1,3 @@ require File.expand_path('../../config/application', __FILE__) require 'rails/commands/dbconsole' -Rails::DBConsole.start(<%= app_const %>) \ No newline at end of file +Rails::DBConsole.start(<%= app_const %>.instance) diff --git a/railties/lib/rails/generators/rails/app/templates/script/server.tt b/railties/lib/rails/generators/rails/app/templates/script/server.tt index d98f677475..380dc42cb5 100755 --- a/railties/lib/rails/generators/rails/app/templates/script/server.tt +++ b/railties/lib/rails/generators/rails/app/templates/script/server.tt @@ -1,3 +1,3 @@ require File.expand_path('../../config/application', __FILE__) require 'rails/commands/server' -Rails::Server.start(<%= app_const %>) +Rails::Server.start(<%= app_const %>.instance) -- cgit v1.2.3 From 1ee50e58f6eb429872dfeabeb0708a8065ff34de Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 23 Dec 2009 17:14:21 -0800 Subject: Fix Rack::Lock middleware condition: use *unless* we allow concurrency --- railties/lib/rails/application.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index c594b8a31d..e950d72586 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -176,7 +176,7 @@ module Rails initializer :initialize_middleware_stack do if config.frameworks.include?(:action_controller) - config.middleware.use(::Rack::Lock, :if => lambda { ActionController::Base.allow_concurrency }) + config.middleware.use(::Rack::Lock, :if => lambda { !ActionController::Base.allow_concurrency }) config.middleware.use(::Rack::Runtime) config.middleware.use(ActionDispatch::ShowExceptions, lambda { ActionController::Base.consider_all_requests_local }) config.middleware.use(ActionDispatch::Callbacks, lambda { ActionController::Dispatcher.prepare_each_request }) -- cgit v1.2.3 From d2bd71a145ddc5e3e3750edc9a09eab742aaf02a Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Wed, 23 Dec 2009 17:01:07 -0800 Subject: Finish moving config.frameworks-dependent code to the framework plugin --- actionmailer/lib/action_mailer/rails.rb | 24 +++++ actionpack/lib/action_controller/notifications.rb | 10 -- actionpack/lib/action_controller/rails.rb | 75 +++++++++++++++ activerecord/lib/active_record/notifications.rb | 5 - activerecord/lib/active_record/rails.rb | 8 ++ railties/lib/rails/application.rb | 112 +--------------------- railties/test/application/routing_test.rb | 2 + 7 files changed, 110 insertions(+), 126 deletions(-) create mode 100644 actionmailer/lib/action_mailer/rails.rb delete mode 100644 actionpack/lib/action_controller/notifications.rb delete mode 100644 activerecord/lib/active_record/notifications.rb diff --git a/actionmailer/lib/action_mailer/rails.rb b/actionmailer/lib/action_mailer/rails.rb new file mode 100644 index 0000000000..a3573cdea7 --- /dev/null +++ b/actionmailer/lib/action_mailer/rails.rb @@ -0,0 +1,24 @@ +require "action_mailer" + +module ActionMailer + class Plugin < Rails::Plugin + plugin_name :action_mailer + + initializer "action_mailer.set_configs" do |app| + app.config.action_mailer.each do |k,v| + ActionMailer::Base.send "#{k}=", v + end + end + + # TODO: ActionController::Base.logger should delegate to its own config.logger + initializer "action_mailer.logger" do + ActionMailer::Base.logger ||= Rails.logger + end + + initializer "action_mailer.view_paths" do |app| + # TODO: this should be combined with the logic for default config.action_mailer.view_paths + view_path = ActionView::PathSet.type_cast(app.config.view_path, app.config.cache_classes) + ActionMailer::Base.template_root = view_path if ActionMailer::Base.view_paths.blank? + end + end +end \ No newline at end of file diff --git a/actionpack/lib/action_controller/notifications.rb b/actionpack/lib/action_controller/notifications.rb deleted file mode 100644 index 1a4f29e0e2..0000000000 --- a/actionpack/lib/action_controller/notifications.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'active_support/notifications' - -ActiveSupport::Notifications.subscribe(/(read|write|cache|expire|exist)_(fragment|page)\??/) do |*args| - event = ActiveSupport::Notifications::Event.new(*args) - - if logger = ActionController::Base.logger - human_name = event.name.to_s.humanize - logger.info("#{human_name} (%.1fms)" % event.duration) - end -end diff --git a/actionpack/lib/action_controller/rails.rb b/actionpack/lib/action_controller/rails.rb index c2d753f9ef..36a52b3149 100644 --- a/actionpack/lib/action_controller/rails.rb +++ b/actionpack/lib/action_controller/rails.rb @@ -23,5 +23,80 @@ module ActionController app.route_configuration_files << app.config.builtin_routes_configuration_file app.reload_routes! end + + # Include middleware to serve up static assets + initializer "action_controller.initialize_static_server" do |app| + if app.config.serve_static_assets + app.config.middleware.use(ActionDispatch::Static, Rails.public_path) + end + end + + initializer "action_controller.initialize_middleware_stack" do |app| + middleware = app.config.middleware + middleware.use(::Rack::Lock, :if => lambda { ActionController::Base.allow_concurrency }) + middleware.use(::Rack::Runtime) + middleware.use(ActionDispatch::ShowExceptions, lambda { ActionController::Base.consider_all_requests_local }) + middleware.use(ActionDispatch::Callbacks, lambda { ActionController::Dispatcher.prepare_each_request }) + middleware.use(lambda { ActionController::Base.session_store }, lambda { ActionController::Base.session_options }) + middleware.use(ActionDispatch::ParamsParser) + middleware.use(::Rack::MethodOverride) + middleware.use(::Rack::Head) + middleware.use(ActionDispatch::StringCoercion) + end + + initializer "action_controller.initialize_framework_caches" do + ActionController::Base.cache_store ||= RAILS_CACHE + end + + # Sets +ActionController::Base#view_paths+ and +ActionMailer::Base#template_root+ + # (but only for those frameworks that are to be loaded). If the framework's + # paths have already been set, it is not changed, otherwise it is + # set to use Configuration#view_path. + initializer "action_controller.initialize_framework_views" do |app| + # TODO: this should be combined with the logic for default config.action_controller.view_paths + view_path = ActionView::PathSet.type_cast(app.config.view_path, app.config.cache_classes) + ActionController::Base.view_paths = view_path if ActionController::Base.view_paths.blank? + end + + initializer "action_controller.initialize_metal" do |app| + Rails::Rack::Metal.requested_metals = app.config.metals + + app.config.middleware.insert_before(:"ActionDispatch::ParamsParser", + Rails::Rack::Metal, :if => Rails::Rack::Metal.metals.any?) + end + + # # Prepare dispatcher callbacks and run 'prepare' callbacks + initializer "action_controller.prepare_dispatcher" do |app| + # TODO: This used to say unless defined?(Dispatcher). Find out why and fix. + require 'rails/dispatcher' + + Dispatcher.define_dispatcher_callbacks(app.config.cache_classes) + + unless app.config.cache_classes + # Setup dev mode route reloading + routes_last_modified = app.routes_changed_at + reload_routes = lambda do + unless app.routes_changed_at == routes_last_modified + routes_last_modified = app.routes_changed_at + app.reload_routes! + end + end + ActionDispatch::Callbacks.before_dispatch { |callbacks| reload_routes.call } + end + end + + initializer "action_controller.notifications" do |app| + require 'active_support/notifications' + + ActiveSupport::Notifications.subscribe(/(read|write|cache|expire|exist)_(fragment|page)\??/) do |*args| + event = ActiveSupport::Notifications::Event.new(*args) + + if logger = ActionController::Base.logger + human_name = event.name.to_s.humanize + logger.info("#{human_name} (%.1fms)" % event.duration) + end + end + end + end end \ No newline at end of file diff --git a/activerecord/lib/active_record/notifications.rb b/activerecord/lib/active_record/notifications.rb deleted file mode 100644 index 562a5b91f4..0000000000 --- a/activerecord/lib/active_record/notifications.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'active_support/notifications' - -ActiveSupport::Notifications.subscribe("sql") do |name, before, after, result, instrumenter_id, payload| - ActiveRecord::Base.connection.log_info(payload[:sql], name, after - before) -end diff --git a/activerecord/lib/active_record/rails.rb b/activerecord/lib/active_record/rails.rb index 4071385563..ddbc555113 100644 --- a/activerecord/lib/active_record/rails.rb +++ b/activerecord/lib/active_record/rails.rb @@ -47,5 +47,13 @@ module ActiveRecord ActiveRecord::Base.logger ||= Rails.logger end + initializer "active_record.notifications" do + require 'active_support/notifications' + + ActiveSupport::Notifications.subscribe("sql") do |name, before, after, result, instrumenter_id, payload| + ActiveRecord::Base.connection.log_info(payload[:sql], name, after - before) + end + end + end end \ No newline at end of file diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 9a5656fb1d..8ba24af793 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -139,14 +139,6 @@ module Rails $LOAD_PATH.uniq! end - # # Requires all frameworks specified by the Configuration#frameworks - # # list. By default, all frameworks (Active Record, Active Support, - # # Action Pack, Action Mailer, and Active Resource) are loaded. - # initializer :require_frameworks do - # require 'active_support/all' unless config.active_support.bare - # config.frameworks.each { |framework| require(framework.to_s) } - # end - # Set the paths from which Rails will automatically load source files, and # the load_once paths. initializer :set_autoload_paths do @@ -177,34 +169,7 @@ module Rails # Used by Passenger to ensure everything's loaded before forking and # to avoid autoload race conditions in JRuby. initializer :preload_frameworks do - if config.preload_frameworks - config.frameworks.each do |framework| - # String#classify and #constantize aren't available yet. - toplevel = Object.const_get(framework.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }) - toplevel.load_all! if toplevel.respond_to?(:load_all!) - end - end - end - - # Include middleware to serve up static assets - initializer :initialize_static_server do - if defined?(ActionController) && config.serve_static_assets - config.middleware.use(ActionDispatch::Static, Rails.public_path) - end - end - - initializer :initialize_middleware_stack do - if defined?(ActionController) - config.middleware.use(::Rack::Lock, :if => lambda { ActionController::Base.allow_concurrency }) - config.middleware.use(::Rack::Runtime) - config.middleware.use(ActionDispatch::ShowExceptions, lambda { ActionController::Base.consider_all_requests_local }) - config.middleware.use(ActionDispatch::Callbacks, lambda { ActionController::Dispatcher.prepare_each_request }) - config.middleware.use(lambda { ActionController::Base.session_store }, lambda { ActionController::Base.session_options }) - config.middleware.use(ActionDispatch::ParamsParser) - config.middleware.use(::Rack::MethodOverride) - config.middleware.use(::Rack::Head) - config.middleware.use(ActionDispatch::StringCoercion) - end + ActiveSupport::Autoload.eager_load! if config.preload_frameworks end initializer :initialize_cache do @@ -218,12 +183,6 @@ module Rails end end - initializer :initialize_framework_caches do - if defined?(ActionController) - ActionController::Base.cache_store ||= RAILS_CACHE - end - end - initializer :initialize_logger do # if the environment has explicitly defined a logger, use it next if Rails.logger @@ -254,14 +213,6 @@ module Rails # logger is already set, it is not changed, otherwise it is set to use # RAILS_DEFAULT_LOGGER. initializer :initialize_framework_logging do - for framework in [ :action_controller, :action_mailer ] - # TODO BEFORE PUSHING: REMOVEZ - begin - framework.to_s.camelize.constantize.const_get("Base").logger ||= Rails.logger - rescue Exception - end - end - ActiveSupport::Dependencies.logger ||= Rails.logger Rails.cache.logger ||= Rails.logger end @@ -308,46 +259,6 @@ module Rails end end - # Initializes framework-specific settings for each of the loaded frameworks - # (Configuration#frameworks). The available settings map to the accessors - # on each of the corresponding Base classes. - initializer :initialize_framework_settings do - (config.frameworks - [:active_record, :action_controller]).each do |framework| - # TODO BEFORE PUSHING: This needs to work differently - begin - base_class = framework.to_s.camelize.constantize.const_get("Base") - - config.send(framework).each do |setting, value| - base_class.send("#{setting}=", value) - end - rescue Exception - end - end - end - - # Sets +ActionController::Base#view_paths+ and +ActionMailer::Base#template_root+ - # (but only for those frameworks that are to be loaded). If the framework's - # paths have already been set, it is not changed, otherwise it is - # set to use Configuration#view_path. - initializer :initialize_framework_views do - if defined?(ActionView) - view_path = ActionView::PathSet.type_cast(config.view_path, config.cache_classes) - - ActionMailer::Base.template_root = view_path if defined?(ActionMailer) && ActionMailer::Base.view_paths.blank? - ActionController::Base.view_paths = view_path if defined?(ActionController) && ActionController::Base.view_paths.blank? - end - end - - initializer :initialize_metal do - if defined?(ActionController) - Rails::Rack::Metal.requested_metals = config.metals - - config.middleware.insert_before( - :"ActionDispatch::ParamsParser", - Rails::Rack::Metal, :if => Rails::Rack::Metal.metals.any?) - end - end - # # bail out if gems are missing - note that check_gem_dependencies will have # # already called abort() unless $gems_rake_task is set # return unless gems_dependencies_loaded @@ -364,27 +275,6 @@ module Rails end end - # TODO: Make a DSL way to limit an initializer to a particular framework - - # # Prepare dispatcher callbacks and run 'prepare' callbacks - initializer :prepare_dispatcher do - next unless configuration.frameworks.include?(:action_controller) - require 'rails/dispatcher' unless defined?(::Dispatcher) - Dispatcher.define_dispatcher_callbacks(configuration.cache_classes) - - unless configuration.cache_classes - # Setup dev mode route reloading - routes_last_modified = routes_changed_at - reload_routes = lambda do - unless routes_changed_at == routes_last_modified - routes_last_modified = routes_changed_at - reload_routes! - end - end - ActionDispatch::Callbacks.before_dispatch { |callbacks| reload_routes.call } - end - end - # Eager load application classes initializer :load_application_classes do next if $rails_rake_task diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb index 49c548ad5c..725dd06929 100644 --- a/railties/test/application/routing_test.rb +++ b/railties/test/application/routing_test.rb @@ -171,6 +171,8 @@ module ApplicationTests end RUBY + sleep 0.1 + get '/foo' assert_equal 'baz', last_response.body end -- cgit v1.2.3 From 4d3602a8c4b38052c70655cd7d9dea42ae10ea8d Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 23 Dec 2009 17:42:30 -0800 Subject: Routing: fix that route shorthand shouldn't ignore other options. Raise if :as option is given to root method since its name is always 'root' --- actionpack/lib/action_dispatch/routing/mapper.rb | 6 ++++-- actionpack/test/dispatch/routing_test.rb | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 46163706c3..40e30bca6f 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -35,13 +35,15 @@ module ActionDispatch end def root(options = {}) + raise "Can't rename root to #{options[:as].inspect}: root is always named 'root'" if options.include?(:as) match '/', options.merge(:as => :root) end def match(*args) if args.one? && args.first.is_a?(Hash) - path = args.first.keys.first - options = { :to => args.first.values.first } + options = args.first + path = options.keys.first + options[:to] = options.delete(path) else path = args.first options = args.extract_options! diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 1c7822358d..7ca85a4201 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -22,7 +22,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest delete 'logout', :to => :destroy, :as => :logout end - match 'account/logout' => redirect("/logout") + match 'account/logout' => redirect("/logout"), :as => :logout_redirect match 'account/login', :to => redirect("/login") match 'account/modulo/:name', :to => redirect("/%{name}s") @@ -109,7 +109,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest scope ':access_token', :constraints => { :access_token => /\w{5,5}/ } do resources :rooms end - + root :to => 'projects#index' end end @@ -153,6 +153,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest def test_logout_redirect_without_to with_test_routes do + assert_equal '/account/logout', logout_redirect_path get '/account/logout' assert_equal 301, @response.status assert_equal 'http://www.example.com/logout', @response.headers['Location'] @@ -462,6 +463,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest def test_root with_test_routes do + assert_equal '/', root_path get '/' assert_equal 'projects#index', @response.body end -- cgit v1.2.3 From d926fb62e83d22b34d58f27a6d743cfa5f4c5c7b Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Wed, 23 Dec 2009 16:14:34 -0800 Subject: Isolation tests intentionally avoid loading any state (because they're often testing things that have their own load path semantics that should not be polluted), so rack/test is not yet on the load path. Moving require "rack/test" into the setup means and after boot_rails means that it'll be required after the laod path has been altered to add in the Rails vendor/gems --- railties/test/application/routing_test.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb index 7b1df7f45d..ece9b13f8a 100644 --- a/railties/test/application/routing_test.rb +++ b/railties/test/application/routing_test.rb @@ -1,18 +1,18 @@ require 'isolation/abstract_unit' -require 'rack/test' module ApplicationTests class RoutingTest < Test::Unit::TestCase include ActiveSupport::Testing::Isolation - include Rack::Test::Methods def setup build_app + boot_rails + require 'rack/test' + extend Rack::Test::Methods end def app @app ||= begin - boot_rails require "#{app_path}/config/environment" Rails.application -- cgit v1.2.3 From 7a6f73e79b850e05d8d9639310159679ef872fb7 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 23 Dec 2009 17:47:38 -0800 Subject: Fix reference to Application#configuration to use #config --- railties/test/application/configuration_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 83e1401993..d60e0b904c 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -60,7 +60,7 @@ module ApplicationTests RUBY require "#{app_path}/config/application" - assert AppTemplate::Application.configuration.action_controller.allow_concurrency + assert AppTemplate::Application.config.action_controller.allow_concurrency end test "the application can be marked as threadsafe when there are no frameworks" do -- cgit v1.2.3 From 9653599a798b66fe19b70cd8ed33b3d344b26883 Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Wed, 23 Dec 2009 18:59:49 -0800 Subject: Remove the ActionView::Base autoload because it creates crazy circular autoload insanity --- actionpack/lib/action_view.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index f57f9ca229..8ce6e82524 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -32,7 +32,6 @@ module ActionView extend ActiveSupport::Autoload eager_autoload do - autoload :Base autoload :Context autoload :Template autoload :Helpers @@ -56,5 +55,6 @@ module ActionView end require 'action_view/erb/util' +require 'action_view/base' I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml" -- cgit v1.2.3 From af5c3c852e43fc95b4c4344f61c9c8fc2210b0ca Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Wed, 23 Dec 2009 19:00:20 -0800 Subject: Require active_support/all unless specifically requested to be left out. --- railties/lib/rails/application.rb | 4 ++++ railties/test/application/configuration_test.rb | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 8ba24af793..711509738d 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -132,6 +132,10 @@ module Rails @app.call(env) end + initializer :load_all_active_support do + require "active_support/all" unless config.active_support.bare + end + # Set the $LOAD_PATH based on the value of # Configuration#load_paths. Duplicates are removed. initializer :set_load_path do diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 0f702c7014..e71b4c0cdb 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -46,6 +46,12 @@ module ApplicationTests end end + test "if there's no config.active_support.bare, all of ActiveSupport is required" do + use_frameworks [] + require "#{app_path}/config/environment" + assert_nothing_raised { [1,2,3].rand } + end + test "config.active_support.bare does not require all of ActiveSupport" do add_to_config "config.active_support.bare = true" -- cgit v1.2.3 From c4d6d50a46a0fe53f75aadc306212b5437e22ed8 Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Wed, 23 Dec 2009 19:10:44 -0800 Subject: Rackup config.ru from the correct directory --- railties/test/application/load_test.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/railties/test/application/load_test.rb b/railties/test/application/load_test.rb index 689bc77ecf..1c5811b07a 100644 --- a/railties/test/application/load_test.rb +++ b/railties/test/application/load_test.rb @@ -20,8 +20,10 @@ module ApplicationTests end test "config.ru can be racked up" do - @app = rackup - assert_welcome get("/") + Dir.chdir app_path do + @app = rackup + assert_welcome get("/") + end end test "Rails.application is available after config.ru has been racked up" do -- cgit v1.2.3 From aa3565f3a6327c947ded314525ba1d0674d5a71e Mon Sep 17 00:00:00 2001 From: Sam Ruby Date: Wed, 23 Dec 2009 23:33:14 -0500 Subject: Allow named_routes to be used with root, and with new DSL short-form. The real use case it to make all of the following act the same: root 'store#index', :as => 'store' match '/' => 'store#index', :as => 'store' match '/', :to => 'store#index', :as => 'store' The test case provided deviates from this in order to demonstrate all three forms in a single set of test routes. Signed-off-by: Jeremy Kemper --- actionpack/lib/action_dispatch/routing/mapper.rb | 15 +++++++-------- actionpack/test/dispatch/routing_test.rb | 9 +++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 40e30bca6f..3eadb0e9fe 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -35,18 +35,17 @@ module ActionDispatch end def root(options = {}) - raise "Can't rename root to #{options[:as].inspect}: root is always named 'root'" if options.include?(:as) - match '/', options.merge(:as => :root) + match '/', options.reverse_merge(:as => :root) end def match(*args) - if args.one? && args.first.is_a?(Hash) - options = args.first - path = options.keys.first - options[:to] = options.delete(path) + options = args.extract_options! + + if args.empty? + path, to = options.find {|name,value| name.is_a?(String)} + options.merge!(:to => to).delete(path) if path else - path = args.first - options = args.extract_options! + path = args.first end conditions, defaults = {}, {} diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 7ca85a4201..82231cb3d9 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -111,6 +111,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end root :to => 'projects#index' + match '/info' => 'projects#info', :as => 'info' end end @@ -469,6 +470,14 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_index + with_test_routes do + assert_equal '/info', info_path + get '/info' + assert_equal 'projects#info', @response.body + end + end + private def with_test_routes real_routes, temp_routes = ActionController::Routing::Routes, Routes -- cgit v1.2.3 From 46b376962f064077734773c7e1eea5881e5d5696 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 23 Dec 2009 23:30:59 -0800 Subject: Fix new schema test dependency on Hash#to_xml --- activeresource/test/cases/base/schema_test.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/activeresource/test/cases/base/schema_test.rb b/activeresource/test/cases/base/schema_test.rb index d1afb9f439..d9dc679941 100644 --- a/activeresource/test/cases/base/schema_test.rb +++ b/activeresource/test/cases/base/schema_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/core_ext/hash/conversions' require "fixtures/person" require "fixtures/street_address" -- cgit v1.2.3 From 2b7256a42e63640d6e94fe80ee67093ed0f06e4c Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 24 Dec 2009 15:23:39 -0800 Subject: Extract Mapping class from monster match method --- actionpack/lib/action_dispatch/routing/mapper.rb | 227 ++++++++++++++--------- actionpack/test/dispatch/routing_test.rb | 13 +- 2 files changed, 149 insertions(+), 91 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 46163706c3..a6b32e0152 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -19,9 +19,9 @@ module ActionDispatch @constraints.each { |constraint| if constraint.respond_to?(:matches?) && !constraint.matches?(req) - return [417, {}, []] + return [ 417, {}, [] ] elsif constraint.respond_to?(:call) && !constraint.call(req) - return [417, {}, []] + return [ 417, {}, [] ] end } @@ -29,94 +29,138 @@ module ActionDispatch end end - module Base - def initialize(set) - @set = set + class Mapping + def initialize(set, scope, args) + @set, @scope = set, scope + @path, @options = extract_path_and_options(args) end - - def root(options = {}) - match '/', options.merge(:as => :root) + + def to_route + [ app, conditions, requirements, defaults, @options[:as] ] end - - def match(*args) - if args.one? && args.first.is_a?(Hash) - path = args.first.keys.first - options = { :to => args.first.values.first } - else - path = args.first + + private + def extract_path_and_options(args) options = args.extract_options! - end - - conditions, defaults = {}, {} - - path = nil if path == "" - path = "#{@scope[:path]}#{path}" if @scope[:path] - path = Rack::Mount::Utils.normalize_path(path) if path - - raise ArgumentError, "path is required" unless path - constraints = options[:constraints] || {} - unless constraints.is_a?(Hash) - block, constraints = constraints, {} + if args.empty? + path, to = options.find { |name, value| name.is_a?(String) } + options.merge!(:to => to).delete(path) if path + else + path = args.first + end + + [ normalize_path(path), options ] end - blocks = ((@scope[:blocks] || []) + [block]).compact - constraints = (@scope[:constraints] || {}).merge(constraints) - options.each { |k, v| constraints[k] = v if v.is_a?(Regexp) } - conditions[:path_info] = path - requirements = constraints.dup + def normalize_path(path) + path = nil if path == "" + path = "#{@scope[:path]}#{path}" if @scope[:path] + path = Rack::Mount::Utils.normalize_path(path) if path - path_regexp = Rack::Mount::Strexp.compile(path, constraints, SEPARATORS) - segment_keys = Rack::Mount::RegexpWithNamedGroups.new(path_regexp).names - constraints.reject! { |k, v| segment_keys.include?(k.to_s) } - conditions.merge!(constraints) + raise ArgumentError, "path is required" unless path + + path + end - requirements[:controller] ||= @set.controller_constraints - if via = options[:via] - via = Array(via).map { |m| m.to_s.upcase } - conditions[:request_method] = Regexp.union(*via) + def app + Constraints.new( + to.respond_to?(:call) ? to : Routing::RouteSet::Dispatcher.new(:defaults => defaults), + blocks + ) end - defaults[:controller] ||= @scope[:controller].to_s if @scope[:controller] - - app = initialize_app_endpoint(options, defaults) - validate_defaults!(app, defaults, segment_keys) - app = Constraints.new(app, blocks) + def conditions + { :path_info => @path }.merge(constraints).merge(request_method_condition) + end + + def requirements + @requirements ||= returning(@options[:constraints] || {}) do |requirements| + requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints] + @options.each { |k, v| requirements[k] = v if v.is_a?(Regexp) } + requirements[:controller] ||= @set.controller_constraints + end + end - @set.add_route(app, conditions, requirements, defaults, options[:as]) + def defaults + @defaults ||= if to.respond_to?(:call) + { } + else + defaults = case to + when String + controller, action = to.split('#') + { :controller => controller, :action => action } + when Symbol + { :controller => default_controller, :action => to.to_s } + else + { :controller => default_controller } + end + + if defaults[:controller].blank? && segment_keys.exclude?("controller") + raise ArgumentError, "missing :controller" + end + + if defaults[:action].blank? && segment_keys.exclude?("action") + raise ArgumentError, "missing :action" + end + + defaults + end + end - self - end + + def blocks + if @options[:constraints].present? && !@options[:constraints].is_a?(Hash) + block = @options[:constraints] + else + block = nil + end + + ((@scope[:blocks] || []) + [ block ]).compact + end + + def constraints + @constraints ||= requirements.reject { |k, v| segment_keys.include?(k.to_s) || k == :controller } + end - private - def initialize_app_endpoint(options, defaults) - app = nil - - if options[:to].respond_to?(:call) - app = options[:to] - defaults.delete(:controller) - defaults.delete(:action) - elsif options[:to].is_a?(String) - defaults[:controller], defaults[:action] = options[:to].split('#') - elsif options[:to].is_a?(Symbol) - defaults[:action] = options[:to].to_s + def request_method_condition + if via = @options[:via] + via = Array(via).map { |m| m.to_s.upcase } + { :request_method => Regexp.union(*via) } + else + { } end + end + + def segment_keys + @segment_keys ||= Rack::Mount::RegexpWithNamedGroups.new( + Rack::Mount::Strexp.compile(@path, requirements, SEPARATORS) + ).names + end - app || Routing::RouteSet::Dispatcher.new(:defaults => defaults) + def to + @options[:to] + end + + def default_controller + @scope[:controller].to_s if @scope[:controller] end + end - def validate_defaults!(app, defaults, segment_keys) - return unless app.is_a?(Routing::RouteSet::Dispatcher) + module Base + def initialize(set) + @set = set + end - unless defaults.include?(:controller) || segment_keys.include?("controller") - raise ArgumentError, "missing :controller" - end + def root(options = {}) + match '/', options.reverse_merge(:as => :root) + end - unless defaults.include?(:action) || segment_keys.include?("action") - raise ArgumentError, "missing :action" - end - end + def match(*args) + @set.add_route(*Mapping.new(@set, @scope, args).to_route) + self + end end module HttpHelpers @@ -139,15 +183,16 @@ module ActionDispatch def redirect(*args, &block) options = args.last.is_a?(Hash) ? args.pop : {} - path = args.shift || block - path_proc = path.is_a?(Proc) ? path : proc {|params| path % params } - status = options[:status] || 301 + path = args.shift || block + path_proc = path.is_a?(Proc) ? path : proc { |params| path % params } + status = options[:status] || 301 lambda do |env| - req = Rack::Request.new(env) + req = Rack::Request.new(env) params = path_proc.call(env["action_dispatch.request.path_parameters"]) - url = req.scheme + '://' + req.host + params - [status, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently']] + url = req.scheme + '://' + req.host + params + + [ status, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently'] ] end end @@ -211,11 +256,11 @@ module ActionDispatch self ensure - @scope[:path] = path if path_set + @scope[:path] = path if path_set @scope[:name_prefix] = name_prefix if name_prefix_set - @scope[:controller] = controller if controller_set - @scope[:options] = options - @scope[:blocks] = blocks + @scope[:controller] = controller if controller_set + @scope[:options] = options + @scope[:blocks] = blocks @scope[:constraints] = constraints end @@ -311,12 +356,12 @@ module ActionDispatch with_scope_level(:resource, resource) do yield if block_given? - get "(.:format)", :to => :show, :as => resource.member_name - post "(.:format)", :to => :create - put "(.:format)", :to => :update - delete "(.:format)", :to => :destroy - get "/new(.:format)", :to => :new, :as => "new_#{resource.singular}" - get "/edit(.:format)", :to => :edit, :as => "edit_#{resource.singular}" + get "(.:format)", :to => :show, :as => resource.member_name + post "(.:format)", :to => :create + put "(.:format)", :to => :update + delete "(.:format)", :to => :destroy + get "/new(.:format)", :to => :new, :as => "new_#{resource.singular}" + get "/edit(.:format)", :to => :edit, :as => "edit_#{resource.singular}" end end @@ -346,8 +391,9 @@ module ActionDispatch yield if block_given? with_scope_level(:collection) do - get "(.:format)", :to => :index, :as => resource.collection_name + get "(.:format)", :to => :index, :as => resource.collection_name post "(.:format)", :to => :create + with_exclusive_name_prefix :new do get "/new(.:format)", :to => :new, :as => resource.singular end @@ -355,9 +401,10 @@ module ActionDispatch with_scope_level(:member) do scope("/:id") do - get "(.:format)", :to => :show, :as => resource.member_name - put "(.:format)", :to => :update + get "(.:format)", :to => :show, :as => resource.member_name + put "(.:format)", :to => :update delete "(.:format)", :to => :destroy + with_exclusive_name_prefix :edit do get "/edit(.:format)", :to => :edit, :as => resource.singular end @@ -473,4 +520,4 @@ module ActionDispatch include Resources end end -end +end \ No newline at end of file diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 1c7822358d..f7f93290df 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -22,7 +22,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest delete 'logout', :to => :destroy, :as => :logout end - match 'account/logout' => redirect("/logout") + match 'account/logout' => redirect("/logout"), :as => :logout_redirect match 'account/login', :to => redirect("/login") match 'account/modulo/:name', :to => redirect("/%{name}s") @@ -110,6 +110,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest resources :rooms end + match '/info' => 'projects#info', :as => 'info' root :to => 'projects#index' end end @@ -153,6 +154,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest def test_logout_redirect_without_to with_test_routes do + assert_equal '/account/logout', logout_redirect_path get '/account/logout' assert_equal 301, @response.status assert_equal 'http://www.example.com/logout', @response.headers['Location'] @@ -462,10 +464,19 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest def test_root with_test_routes do + assert_equal '/', root_path get '/' assert_equal 'projects#index', @response.body end end + + def test_index + with_test_routes do + assert_equal '/info', info_path + get '/info' + assert_equal 'projects#info', @response.body + end + end private def with_test_routes -- cgit v1.2.3 From b9c0a1665531d797e890efdf2fcace8a03961fe0 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 24 Dec 2009 16:08:03 -0800 Subject: The new routes shortform now also works for :as --- railties/lib/rails/generators/rails/app/templates/config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb index ac916e9d90..d6c0365c04 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb @@ -7,7 +7,7 @@ # Keep in mind you can assign values other than :controller and :action # Sample of named route: - # match 'products/:id/purchase', :to => 'catalog#purchase', :as => :purchase + # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase # This route can be invoked with purchase_url(:id => product.id) # Sample resource route (maps HTTP verbs to controller actions automatically): -- cgit v1.2.3 From 6ce5982afa4e368a3baf9c8049824fd0c6d2d8fb Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 24 Dec 2009 16:13:50 -0800 Subject: Stray carrier return --- actionpack/lib/action_view/safe_buffer.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/actionpack/lib/action_view/safe_buffer.rb b/actionpack/lib/action_view/safe_buffer.rb index 09f44ab26f..6be05b9e1e 100644 --- a/actionpack/lib/action_view/safe_buffer.rb +++ b/actionpack/lib/action_view/safe_buffer.rb @@ -1,4 +1,3 @@ - module ActionView #:nodoc: class SafeBuffer < String def <<(value) -- cgit v1.2.3 From 0a365d63f6fc99ce63781a15aefda87c4074108d Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 24 Dec 2009 20:32:53 -0800 Subject: Translated strings in the view are assumed html_safe (Closes #3401) --- actionpack/lib/action_view/helpers/translation_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index 564f12c955..35c431d78d 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -12,7 +12,7 @@ module ActionView # prepend the key with a period, nothing is converted. def translate(key, options = {}) options[:raise] = true - I18n.translate(scope_key_by_partial(key), options) + I18n.translate(scope_key_by_partial(key), options).html_safe! rescue I18n::MissingTranslationData => e keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope]) content_tag('span', keys.join(', '), :class => 'translation_missing') -- cgit v1.2.3 From baaaf2acaa58748bf9dc49859d1bca3ed273dc65 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Thu, 24 Dec 2009 21:46:47 -0800 Subject: Dead code --- actionpack/lib/action_view/template.rb | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index a64ee09245..d46c989d11 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -116,21 +116,6 @@ module ActionView end end - class LocalsKey - @hash_keys = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = {} } } - - def self.get(*locals) - @hash_keys[*locals] ||= new(klass, format, locale) - end - - attr_accessor :hash - def initialize(klass, format, locale) - @hash = locals.hash - end - - alias_method :eql?, :equal? - end - def build_method_name(locals) # TODO: is locals.keys.hash reliably the same? @method_names[locals.keys.hash] ||= -- cgit v1.2.3 From f3b072189a6a77717f99e38403392a68f5818a49 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Thu, 24 Dec 2009 21:49:50 -0800 Subject: Instead of marking raw text in templates as safe, and then putting them through String#<< which checks if the String is safe, use safe_concat, which uses the original (internal) String#<< and leaves the safe flag as is. Results in a significant performance improvement. --- actionpack/lib/action_view/template/handlers/erb.rb | 3 ++- activesupport/lib/active_support/core_ext/string/output_safety.rb | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb index f8e6376589..93a4315108 100644 --- a/actionpack/lib/action_view/template/handlers/erb.rb +++ b/actionpack/lib/action_view/template/handlers/erb.rb @@ -10,7 +10,8 @@ module ActionView end def add_text(src, text) - src << "@output_buffer << ('" << escape_text(text) << "'.html_safe!);" + return if text.empty? + src << "@output_buffer.safe_concat('" << escape_text(text) << "');" end def add_expr_literal(src, code) diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index a2a88eb7df..9db563f78b 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -23,6 +23,7 @@ class String end alias original_concat << + alias safe_concat << def <<(other) result = original_concat(other) unless html_safe? && also_html_safe?(other) -- cgit v1.2.3 From 3b1642c23cb433dde3d96f0b70dfdc66d15f6713 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Thu, 24 Dec 2009 22:44:21 -0800 Subject: Simplify and improve the performance of output_safety --- .../core_ext/string/output_safety.rb | 28 +++++----------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 9db563f78b..3e6ab0ebd2 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -1,7 +1,6 @@ class String - def html_safe? - defined?(@_rails_html_safe) && @_rails_html_safe - end + attr_accessor :_rails_html_safe + alias html_safe? _rails_html_safe def html_safe! @_rails_html_safe = true @@ -15,31 +14,16 @@ class String alias original_plus + def +(other) result = original_plus(other) - if html_safe? && also_html_safe?(other) - result.html_safe! - else - result - end + result._rails_html_safe = html_safe? && other.html_safe? + result end alias original_concat << alias safe_concat << def <<(other) + @_rails_html_safe = false unless other.html_safe? result = original_concat(other) - unless html_safe? && also_html_safe?(other) - @_rails_html_safe = false - end - result end - remove_method :concat - def concat(other) - self << other - end - - private - def also_html_safe?(other) - other.respond_to?(:html_safe?) && other.html_safe? - end - + alias concat << end \ No newline at end of file -- cgit v1.2.3 From 1c66f85eb6cd80053cf60889634a8c39c21b11a1 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Thu, 24 Dec 2009 23:48:35 -0800 Subject: This code was needed to work around http://redmine.ruby-lang.org/issues/show/2494. Since that bug is now closed in Ruby, we can remove it. --- activesupport/lib/active_support/whiny_nil.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/activesupport/lib/active_support/whiny_nil.rb b/activesupport/lib/active_support/whiny_nil.rb index c3ed659d6b..4f6ff7d3b5 100644 --- a/activesupport/lib/active_support/whiny_nil.rb +++ b/activesupport/lib/active_support/whiny_nil.rb @@ -43,10 +43,7 @@ class NilClass private def method_missing(method, *args, &block) - # Ruby 1.9.2: disallow explicit coercion via method_missing. - if method == :to_ary || method == :to_str - raise NoMethodError, "undefined method `#{method}' for nil:NilClass" - elsif klass = METHOD_CLASS_MAP[method] + if klass = METHOD_CLASS_MAP[method] raise_nil_warning_for klass, method, caller else super -- cgit v1.2.3 From 135d32c8bd91608c12a394ca5dbd65f358bbce42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 25 Dec 2009 15:15:13 +0100 Subject: Move i18n to Gemfile. --- Gemfile | 5 +- activesupport/lib/active_support/vendor.rb | 2 +- .../active_support/vendor/i18n-0.1.3/MIT-LICENSE | 20 - .../vendor/i18n-0.1.3/README.textile | 20 - .../lib/active_support/vendor/i18n-0.1.3/Rakefile | 5 - .../active_support/vendor/i18n-0.1.3/i18n.gemspec | 27 - .../active_support/vendor/i18n-0.1.3/lib/i18n.rb | 204 -------- .../vendor/i18n-0.1.3/lib/i18n/backend/simple.rb | 215 -------- .../vendor/i18n-0.1.3/lib/i18n/exceptions.rb | 53 -- .../active_support/vendor/i18n-0.1.3/test/all.rb | 5 - .../vendor/i18n-0.1.3/test/i18n_exceptions_test.rb | 99 ---- .../vendor/i18n-0.1.3/test/i18n_test.rb | 124 ----- .../vendor/i18n-0.1.3/test/locale/en.rb | 1 - .../vendor/i18n-0.1.3/test/locale/en.yml | 3 - .../vendor/i18n-0.1.3/test/simple_backend_test.rb | 567 --------------------- 15 files changed, 5 insertions(+), 1345 deletions(-) delete mode 100755 activesupport/lib/active_support/vendor/i18n-0.1.3/MIT-LICENSE delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.1.3/README.textile delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.1.3/Rakefile delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.1.3/i18n.gemspec delete mode 100755 activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/backend/simple.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/exceptions.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.1.3/test/all.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_exceptions_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.1.3/test/locale/en.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.1.3/test/locale/en.yml delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.1.3/test/simple_backend_test.rb diff --git a/Gemfile b/Gemfile index 361074dce6..4ffa9e00e8 100644 --- a/Gemfile +++ b/Gemfile @@ -6,6 +6,9 @@ gem "rails", "3.0.pre", :path => "railties" gem lib, '3.0.pre', :path => lib end +# AS +gem "i18n", ">= 0.3.0" + # AR gem "arel", "0.2.pre", :git => "git://github.com/rails/arel.git" gem "sqlite3-ruby", ">= 1.2.5" @@ -32,4 +35,4 @@ if ENV['CI'] end end -disable_system_gems \ No newline at end of file +disable_system_gems diff --git a/activesupport/lib/active_support/vendor.rb b/activesupport/lib/active_support/vendor.rb index eb5080888c..1e46491d83 100644 --- a/activesupport/lib/active_support/vendor.rb +++ b/activesupport/lib/active_support/vendor.rb @@ -4,7 +4,7 @@ def ActiveSupport.requirable?(file) $LOAD_PATH.any? { |p| Dir.glob("#{p}/#{file}.*").any? } end -[%w(builder 2.1.2), %w(i18n 0.1.3), %w(memcache-client 1.7.5), %w(tzinfo 0.3.15)].each do |lib, version| +[%w(builder 2.1.2), %w(memcache-client 1.7.5), %w(tzinfo 0.3.15)].each do |lib, version| # If the lib is not already requirable unless ActiveSupport.requirable? lib # Try to activate a gem ~> satisfying the requested version first. diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/MIT-LICENSE b/activesupport/lib/active_support/vendor/i18n-0.1.3/MIT-LICENSE deleted file mode 100755 index ed8e9ee66d..0000000000 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/MIT-LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2008 The Ruby I18n team - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/README.textile b/activesupport/lib/active_support/vendor/i18n-0.1.3/README.textile deleted file mode 100644 index a07fc8426d..0000000000 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/README.textile +++ /dev/null @@ -1,20 +0,0 @@ -h1. Ruby I18n gem - -I18n and localization solution for Ruby. - -For information please refer to http://rails-i18n.org - -h2. Authors - -* "Matt Aimonetti":http://railsontherun.com -* "Sven Fuchs":http://www.artweb-design.de -* "Joshua Harvey":http://www.workingwithrails.com/person/759-joshua-harvey -* "Saimon Moore":http://saimonmoore.net -* "Stephan Soller":http://www.arkanis-development.de - -h2. License - -MIT License. See the included MIT-LICENCE file. - - - diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/Rakefile b/activesupport/lib/active_support/vendor/i18n-0.1.3/Rakefile deleted file mode 100644 index 2164e13e69..0000000000 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/Rakefile +++ /dev/null @@ -1,5 +0,0 @@ -task :default => [:test] - -task :test do - ruby "test/all.rb" -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/i18n.gemspec b/activesupport/lib/active_support/vendor/i18n-0.1.3/i18n.gemspec deleted file mode 100644 index f102689a6f..0000000000 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/i18n.gemspec +++ /dev/null @@ -1,27 +0,0 @@ -Gem::Specification.new do |s| - s.name = "i18n" - s.version = "0.1.3" - s.date = "2009-01-09" - s.summary = "Internationalization support for Ruby" - s.email = "rails-i18n@googlegroups.com" - s.homepage = "http://rails-i18n.org" - s.description = "Add Internationalization support to your Ruby application." - s.has_rdoc = false - s.authors = ['Sven Fuchs', 'Joshua Harvey', 'Matt Aimonetti', 'Stephan Soller', 'Saimon Moore'] - s.files = [ - 'i18n.gemspec', - 'lib/i18n/backend/simple.rb', - 'lib/i18n/exceptions.rb', - 'lib/i18n.rb', - 'MIT-LICENSE', - 'README.textile' - ] - s.test_files = [ - 'test/all.rb', - 'test/i18n_exceptions_test.rb', - 'test/i18n_test.rb', - 'test/locale/en.rb', - 'test/locale/en.yml', - 'test/simple_backend_test.rb' - ] -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n.rb deleted file mode 100755 index 1b49debc05..0000000000 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n.rb +++ /dev/null @@ -1,204 +0,0 @@ -#-- -# Authors:: Matt Aimonetti (http://railsontherun.com/), -# Sven Fuchs (http://www.artweb-design.de), -# Joshua Harvey (http://www.workingwithrails.com/person/759-joshua-harvey), -# Saimon Moore (http://saimonmoore.net), -# Stephan Soller (http://www.arkanis-development.de/) -# Copyright:: Copyright (c) 2008 The Ruby i18n Team -# License:: MIT -#++ - -module I18n - autoload :ArgumentError, 'i18n/exceptions' - module Backend - autoload :Simple, 'i18n/backend/simple' - end - - @@backend = nil - @@load_path = nil - @@default_locale = :'en' - @@exception_handler = :default_exception_handler - - class << self - # Returns the current backend. Defaults to +Backend::Simple+. - def backend - @@backend ||= Backend::Simple.new - end - - # Sets the current backend. Used to set a custom backend. - def backend=(backend) - @@backend = backend - end - - # Returns the current default locale. Defaults to :'en' - def default_locale - @@default_locale - end - - # Sets the current default locale. Used to set a custom default locale. - def default_locale=(locale) - @@default_locale = locale - end - - # Returns the current locale. Defaults to I18n.default_locale. - def locale - Thread.current[:locale] ||= default_locale - end - - # Sets the current locale pseudo-globally, i.e. in the Thread.current hash. - def locale=(locale) - Thread.current[:locale] = locale - end - - # Returns an array of locales for which translations are available - def available_locales - backend.available_locales - end - - # Sets the exception handler. - def exception_handler=(exception_handler) - @@exception_handler = exception_handler - end - - # Allow clients to register paths providing translation data sources. The - # backend defines acceptable sources. - # - # E.g. the provided SimpleBackend accepts a list of paths to translation - # files which are either named *.rb and contain plain Ruby Hashes or are - # named *.yml and contain YAML data. So for the SimpleBackend clients may - # register translation files like this: - # I18n.load_path << 'path/to/locale/en.yml' - def load_path - @@load_path ||= [] - end - - # Sets the load path instance. Custom implementations are expected to - # behave like a Ruby Array. - def load_path=(load_path) - @@load_path = load_path - end - - # Tells the backend to reload translations. Used in situations like the - # Rails development environment. Backends can implement whatever strategy - # is useful. - def reload! - backend.reload! - end - - # Translates, pluralizes and interpolates a given key using a given locale, - # scope, and default, as well as interpolation values. - # - # *LOOKUP* - # - # Translation data is organized as a nested hash using the upper-level keys - # as namespaces. E.g., ActionView ships with the translation: - # :date => {:formats => {:short => "%b %d"}}. - # - # Translations can be looked up at any level of this hash using the key argument - # and the scope option. E.g., in this example I18n.t :date - # returns the whole translations hash {:formats => {:short => "%b %d"}}. - # - # Key can be either a single key or a dot-separated key (both Strings and Symbols - # work). E.g., the short format can be looked up using both: - # I18n.t 'date.formats.short' - # I18n.t :'date.formats.short' - # - # Scope can be either a single key, a dot-separated key or an array of keys - # or dot-separated keys. Keys and scopes can be combined freely. So these - # examples will all look up the same short date format: - # I18n.t 'date.formats.short' - # I18n.t 'formats.short', :scope => 'date' - # I18n.t 'short', :scope => 'date.formats' - # I18n.t 'short', :scope => %w(date formats) - # - # *INTERPOLATION* - # - # Translations can contain interpolation variables which will be replaced by - # values passed to #translate as part of the options hash, with the keys matching - # the interpolation variable names. - # - # E.g., with a translation :foo => "foo {{bar}}" the option - # value for the key +bar+ will be interpolated into the translation: - # I18n.t :foo, :bar => 'baz' # => 'foo baz' - # - # *PLURALIZATION* - # - # Translation data can contain pluralized translations. Pluralized translations - # are arrays of singluar/plural versions of translations like ['Foo', 'Foos']. - # - # Note that I18n::Backend::Simple only supports an algorithm for English - # pluralization rules. Other algorithms can be supported by custom backends. - # - # This returns the singular version of a pluralized translation: - # I18n.t :foo, :count => 1 # => 'Foo' - # - # These both return the plural version of a pluralized translation: - # I18n.t :foo, :count => 0 # => 'Foos' - # I18n.t :foo, :count => 2 # => 'Foos' - # - # The :count option can be used both for pluralization and interpolation. - # E.g., with the translation - # :foo => ['{{count}} foo', '{{count}} foos'], count will - # be interpolated to the pluralized translation: - # I18n.t :foo, :count => 1 # => '1 foo' - # - # *DEFAULTS* - # - # This returns the translation for :foo or default if no translation was found: - # I18n.t :foo, :default => 'default' - # - # This returns the translation for :foo or the translation for :bar if no - # translation for :foo was found: - # I18n.t :foo, :default => :bar - # - # Returns the translation for :foo or the translation for :bar - # or default if no translations for :foo and :bar were found. - # I18n.t :foo, :default => [:bar, 'default'] - # - # BULK LOOKUP - # - # This returns an array with the translations for :foo and :bar. - # I18n.t [:foo, :bar] - # - # Can be used with dot-separated nested keys: - # I18n.t [:'baz.foo', :'baz.bar'] - # - # Which is the same as using a scope option: - # I18n.t [:foo, :bar], :scope => :baz - def translate(key, options = {}) - locale = options.delete(:locale) || I18n.locale - backend.translate(locale, key, options) - rescue I18n::ArgumentError => e - raise e if options[:raise] - send(@@exception_handler, e, locale, key, options) - end - alias :t :translate - - # Localizes certain objects, such as dates and numbers to local formatting. - def localize(object, options = {}) - locale = options[:locale] || I18n.locale - format = options[:format] || :default - backend.localize(locale, object, format) - end - alias :l :localize - - protected - # Handles exceptions raised in the backend. All exceptions except for - # MissingTranslationData exceptions are re-raised. When a MissingTranslationData - # was caught and the option :raise is not set the handler returns an error - # message string containing the key/scope. - def default_exception_handler(exception, locale, key, options) - return exception.message if MissingTranslationData === exception - raise exception - end - - # Merges the given locale, key and scope into a single array of keys. - # Splits keys that contain dots into multiple keys. Makes sure all - # keys are Symbols. - def normalize_translation_keys(locale, key, scope) - keys = [locale] + Array(scope) + [key] - keys = keys.map { |k| k.to_s.split(/\./) } - keys.flatten.map { |k| k.to_sym } - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/backend/simple.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/backend/simple.rb deleted file mode 100644 index c32cc76f34..0000000000 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/backend/simple.rb +++ /dev/null @@ -1,215 +0,0 @@ -require 'i18n/exceptions' - -module I18n - module Backend - class Simple - INTERPOLATION_RESERVED_KEYS = %w(scope default) - MATCH = /(\\\\)?\{\{([^\}]+)\}\}/ - - # Accepts a list of paths to translation files. Loads translations from - # plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml - # for details. - def load_translations(*filenames) - filenames.each { |filename| load_file(filename) } - end - - # Stores translations for the given locale in memory. - # This uses a deep merge for the translations hash, so existing - # translations will be overwritten by new ones only at the deepest - # level of the hash. - def store_translations(locale, data) - merge_translations(locale, data) - end - - def translate(locale, key, options = {}) - raise InvalidLocale.new(locale) if locale.nil? - return key.map { |k| translate(locale, k, options) } if key.is_a? Array - - reserved = :scope, :default - count, scope, default = options.values_at(:count, *reserved) - options.delete(:default) - values = options.reject { |name, value| reserved.include?(name) } - - entry = lookup(locale, key, scope) - if entry.nil? - entry = default(locale, default, options) - if entry.nil? - raise(I18n::MissingTranslationData.new(locale, key, options)) - end - end - entry = pluralize(locale, entry, count) - entry = interpolate(locale, entry, values) - entry - end - - # Acts the same as +strftime+, but returns a localized version of the - # formatted date string. Takes a key from the date/time formats - # translations as a format argument (e.g., :short in :'date.formats'). - def localize(locale, object, format = :default) - raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime) - - type = object.respond_to?(:sec) ? 'time' : 'date' - # TODO only translate these if format is a String? - formats = translate(locale, :"#{type}.formats") - format = formats[format.to_sym] if formats && formats[format.to_sym] - # TODO raise exception unless format found? - format = format.to_s.dup - - # TODO only translate these if the format string is actually present - # TODO check which format strings are present, then bulk translate then, then replace them - format.gsub!(/%a/, translate(locale, :"date.abbr_day_names")[object.wday]) - format.gsub!(/%A/, translate(locale, :"date.day_names")[object.wday]) - format.gsub!(/%b/, translate(locale, :"date.abbr_month_names")[object.mon]) - format.gsub!(/%B/, translate(locale, :"date.month_names")[object.mon]) - format.gsub!(/%p/, translate(locale, :"time.#{object.hour < 12 ? :am : :pm}")) if object.respond_to? :hour - object.strftime(format) - end - - def initialized? - @initialized ||= false - end - - # Returns an array of locales for which translations are available - def available_locales - init_translations unless initialized? - translations.keys - end - - def reload! - @initialized = false - @translations = nil - end - - protected - def init_translations - load_translations(*I18n.load_path.flatten) - @initialized = true - end - - def translations - @translations ||= {} - end - - # Looks up a translation from the translations hash. Returns nil if - # eiher key is nil, or locale, scope or key do not exist as a key in the - # nested translations hash. Splits keys or scopes containing dots - # into multiple keys, i.e. currency.format is regarded the same as - # %w(currency format). - def lookup(locale, key, scope = []) - return unless key - init_translations unless initialized? - keys = I18n.send(:normalize_translation_keys, locale, key, scope) - keys.inject(translations) do |result, k| - if (x = result[k.to_sym]).nil? - return nil - else - x - end - end - end - - # Evaluates a default translation. - # If the given default is a String it is used literally. If it is a Symbol - # it will be translated with the given options. If it is an Array the first - # translation yielded will be returned. - # - # I.e., default(locale, [:foo, 'default']) will return +default+ if - # translate(locale, :foo) does not yield a result. - def default(locale, default, options = {}) - case default - when String then default - when Symbol then translate locale, default, options - when Array then default.each do |obj| - result = default(locale, obj, options.dup) and return result - end and nil - end - rescue MissingTranslationData - nil - end - - # Picks a translation from an array according to English pluralization - # rules. It will pick the first translation if count is not equal to 1 - # and the second translation if it is equal to 1. Other backends can - # implement more flexible or complex pluralization rules. - def pluralize(locale, entry, count) - return entry unless entry.is_a?(Hash) and count - # raise InvalidPluralizationData.new(entry, count) unless entry.is_a?(Hash) - key = :zero if count == 0 && entry.has_key?(:zero) - key ||= count == 1 ? :one : :other - raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key) - entry[key] - end - - # Interpolates values into a given string. - # - # interpolate "file {{file}} opened by \\{{user}}", :file => 'test.txt', :user => 'Mr. X' - # # => "file test.txt opened by {{user}}" - # - # Note that you have to double escape the \\ when you want to escape - # the {{...}} key in a string (once for the string and once for the - # interpolation). - def interpolate(locale, string, values = {}) - return string unless string.is_a?(String) - - string.gsub(MATCH) do - escaped, pattern, key = $1, $2, $2.to_sym - - if escaped - pattern - elsif INTERPOLATION_RESERVED_KEYS.include?(pattern) - raise ReservedInterpolationKey.new(pattern, string) - elsif !values.include?(key) - raise MissingInterpolationArgument.new(pattern, string) - else - values[key].to_s - end - end - end - - # Loads a single translations file by delegating to #load_rb or - # #load_yml depending on the file extension and directly merges the - # data to the existing translations. Raises I18n::UnknownFileType - # for all other file extensions. - def load_file(filename) - type = File.extname(filename).tr('.', '').downcase - raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}") - data = send :"load_#{type}", filename # TODO raise a meaningful exception if this does not yield a Hash - data.each { |locale, d| merge_translations(locale, d) } - end - - # Loads a plain Ruby translations file. eval'ing the file must yield - # a Hash containing translation data with locales as toplevel keys. - def load_rb(filename) - eval(IO.read(filename), binding, filename) - end - - # Loads a YAML translations file. The data must have locales as - # toplevel keys. - def load_yml(filename) - require 'yaml' unless defined? :YAML - YAML::load(IO.read(filename)) - end - - # Deep merges the given translations hash with the existing translations - # for the given locale - def merge_translations(locale, data) - locale = locale.to_sym - translations[locale] ||= {} - data = deep_symbolize_keys(data) - - # deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809 - merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 } - translations[locale].merge!(data, &merger) - end - - # Return a new hash with all keys and nested keys converted to symbols. - def deep_symbolize_keys(hash) - hash.inject({}) { |result, (key, value)| - value = deep_symbolize_keys(value) if value.is_a? Hash - result[(key.to_sym rescue key) || key] = value - result - } - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/exceptions.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/exceptions.rb deleted file mode 100644 index 6897055d6d..0000000000 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/exceptions.rb +++ /dev/null @@ -1,53 +0,0 @@ -module I18n - class ArgumentError < ::ArgumentError; end - - class InvalidLocale < ArgumentError - attr_reader :locale - def initialize(locale) - @locale = locale - super "#{locale.inspect} is not a valid locale" - end - end - - class MissingTranslationData < ArgumentError - attr_reader :locale, :key, :options - def initialize(locale, key, options) - @key, @locale, @options = key, locale, options - keys = I18n.send(:normalize_translation_keys, locale, key, options[:scope]) - keys << 'no key' if keys.size < 2 - super "translation missing: #{keys.join(', ')}" - end - end - - class InvalidPluralizationData < ArgumentError - attr_reader :entry, :count - def initialize(entry, count) - @entry, @count = entry, count - super "translation data #{entry.inspect} can not be used with :count => #{count}" - end - end - - class MissingInterpolationArgument < ArgumentError - attr_reader :key, :string - def initialize(key, string) - @key, @string = key, string - super "interpolation argument #{key} missing in #{string.inspect}" - end - end - - class ReservedInterpolationKey < ArgumentError - attr_reader :key, :string - def initialize(key, string) - @key, @string = key, string - super "reserved key #{key.inspect} used in #{string.inspect}" - end - end - - class UnknownFileType < ArgumentError - attr_reader :type, :filename - def initialize(type, filename) - @type, @filename = type, filename - super "can not load translations from #{filename}, the file type #{type} is not known" - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/test/all.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/all.rb deleted file mode 100644 index 353712da49..0000000000 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/test/all.rb +++ /dev/null @@ -1,5 +0,0 @@ -dir = File.dirname(__FILE__) -require dir + '/i18n_test.rb' -require dir + '/simple_backend_test.rb' -require dir + '/i18n_exceptions_test.rb' -# *require* dir + '/custom_backend_test.rb' \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_exceptions_test.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_exceptions_test.rb deleted file mode 100644 index 4e78e71b34..0000000000 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_exceptions_test.rb +++ /dev/null @@ -1,99 +0,0 @@ -$:.unshift "lib" - -require 'rubygems' -require 'test/unit' -require 'i18n' -require 'active_support' - -class I18nExceptionsTest < Test::Unit::TestCase - def test_invalid_locale_stores_locale - force_invalid_locale - rescue I18n::ArgumentError => e - assert_nil e.locale - end - - def test_invalid_locale_message - force_invalid_locale - rescue I18n::ArgumentError => e - assert_equal 'nil is not a valid locale', e.message - end - - def test_missing_translation_data_stores_locale_key_and_options - force_missing_translation_data - rescue I18n::ArgumentError => e - options = {:scope => :bar} - assert_equal 'de', e.locale - assert_equal :foo, e.key - assert_equal options, e.options - end - - def test_missing_translation_data_message - force_missing_translation_data - rescue I18n::ArgumentError => e - assert_equal 'translation missing: de, bar, foo', e.message - end - - def test_invalid_pluralization_data_stores_entry_and_count - force_invalid_pluralization_data - rescue I18n::ArgumentError => e - assert_equal [:bar], e.entry - assert_equal 1, e.count - end - - def test_invalid_pluralization_data_message - force_invalid_pluralization_data - rescue I18n::ArgumentError => e - assert_equal 'translation data [:bar] can not be used with :count => 1', e.message - end - - def test_missing_interpolation_argument_stores_key_and_string - force_missing_interpolation_argument - rescue I18n::ArgumentError => e - assert_equal 'bar', e.key - assert_equal "{{bar}}", e.string - end - - def test_missing_interpolation_argument_message - force_missing_interpolation_argument - rescue I18n::ArgumentError => e - assert_equal 'interpolation argument bar missing in "{{bar}}"', e.message - end - - def test_reserved_interpolation_key_stores_key_and_string - force_reserved_interpolation_key - rescue I18n::ArgumentError => e - assert_equal 'scope', e.key - assert_equal "{{scope}}", e.string - end - - def test_reserved_interpolation_key_message - force_reserved_interpolation_key - rescue I18n::ArgumentError => e - assert_equal 'reserved key "scope" used in "{{scope}}"', e.message - end - - private - def force_invalid_locale - I18n.backend.translate nil, :foo - end - - def force_missing_translation_data - I18n.backend.store_translations 'de', :bar => nil - I18n.backend.translate 'de', :foo, :scope => :bar - end - - def force_invalid_pluralization_data - I18n.backend.store_translations 'de', :foo => [:bar] - I18n.backend.translate 'de', :foo, :count => 1 - end - - def force_missing_interpolation_argument - I18n.backend.store_translations 'de', :foo => "{{bar}}" - I18n.backend.translate 'de', :foo, :baz => 'baz' - end - - def force_reserved_interpolation_key - I18n.backend.store_translations 'de', :foo => "{{scope}}" - I18n.backend.translate 'de', :foo, :baz => 'baz' - end -end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_test.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_test.rb deleted file mode 100644 index 2835ec4eab..0000000000 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_test.rb +++ /dev/null @@ -1,124 +0,0 @@ -$:.unshift "lib" - -require 'rubygems' -require 'test/unit' -require 'i18n' -require 'active_support' - -class I18nTest < Test::Unit::TestCase - def setup - I18n.backend.store_translations :'en', { - :currency => { - :format => { - :separator => '.', - :delimiter => ',', - } - } - } - end - - def test_uses_simple_backend_set_by_default - assert I18n.backend.is_a?(I18n::Backend::Simple) - end - - def test_can_set_backend - assert_nothing_raised{ I18n.backend = self } - assert_equal self, I18n.backend - I18n.backend = I18n::Backend::Simple.new - end - - def test_uses_en_us_as_default_locale_by_default - assert_equal 'en', I18n.default_locale - end - - def test_can_set_default_locale - assert_nothing_raised{ I18n.default_locale = 'de' } - assert_equal 'de', I18n.default_locale - I18n.default_locale = 'en' - end - - def test_uses_default_locale_as_locale_by_default - assert_equal I18n.default_locale, I18n.locale - end - - def test_can_set_locale_to_thread_current - assert_nothing_raised{ I18n.locale = 'de' } - assert_equal 'de', I18n.locale - assert_equal 'de', Thread.current[:locale] - I18n.locale = 'en' - end - - def test_can_set_exception_handler - assert_nothing_raised{ I18n.exception_handler = :custom_exception_handler } - I18n.exception_handler = :default_exception_handler # revert it - end - - def test_uses_custom_exception_handler - I18n.exception_handler = :custom_exception_handler - I18n.expects(:custom_exception_handler) - I18n.translate :bogus - I18n.exception_handler = :default_exception_handler # revert it - end - - def test_delegates_translate_to_backend - I18n.backend.expects(:translate).with 'de', :foo, {} - I18n.translate :foo, :locale => 'de' - end - - def test_delegates_localize_to_backend - I18n.backend.expects(:localize).with 'de', :whatever, :default - I18n.localize :whatever, :locale => 'de' - end - - def test_translate_given_no_locale_uses_i18n_locale - I18n.backend.expects(:translate).with 'en', :foo, {} - I18n.translate :foo - end - - def test_translate_on_nested_symbol_keys_works - assert_equal ".", I18n.t(:'currency.format.separator') - end - - def test_translate_with_nested_string_keys_works - assert_equal ".", I18n.t('currency.format.separator') - end - - def test_translate_with_array_as_scope_works - assert_equal ".", I18n.t(:separator, :scope => ['currency.format']) - end - - def test_translate_with_array_containing_dot_separated_strings_as_scope_works - assert_equal ".", I18n.t(:separator, :scope => ['currency.format']) - end - - def test_translate_with_key_array_and_dot_separated_scope_works - assert_equal [".", ","], I18n.t(%w(separator delimiter), :scope => 'currency.format') - end - - def test_translate_with_dot_separated_key_array_and_scope_works - assert_equal [".", ","], I18n.t(%w(format.separator format.delimiter), :scope => 'currency') - end - - def test_translate_with_options_using_scope_works - I18n.backend.expects(:translate).with('de', :precision, :scope => :"currency.format") - I18n.with_options :locale => 'de', :scope => :'currency.format' do |locale| - locale.t :precision - end - end - - # def test_translate_given_no_args_raises_missing_translation_data - # assert_equal "translation missing: en, no key", I18n.t - # end - - def test_translate_given_a_bogus_key_raises_missing_translation_data - assert_equal "translation missing: en, bogus", I18n.t(:bogus) - end - - def test_localize_nil_raises_argument_error - assert_raise(I18n::ArgumentError) { I18n.l nil } - end - - def test_localize_object_raises_argument_error - assert_raise(I18n::ArgumentError) { I18n.l Object.new } - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/test/locale/en.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/locale/en.rb deleted file mode 100644 index 6044ce10d9..0000000000 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/test/locale/en.rb +++ /dev/null @@ -1 +0,0 @@ -{:'en-Ruby' => {:foo => {:bar => "baz"}}} \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/test/locale/en.yml b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/locale/en.yml deleted file mode 100644 index 0b298c9c0e..0000000000 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/test/locale/en.yml +++ /dev/null @@ -1,3 +0,0 @@ -en-Yaml: - foo: - bar: baz \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/test/simple_backend_test.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/simple_backend_test.rb deleted file mode 100644 index a1696c77f6..0000000000 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/test/simple_backend_test.rb +++ /dev/null @@ -1,567 +0,0 @@ -# encoding: utf-8 -$:.unshift "lib" - -require 'rubygems' -require 'test/unit' -require 'i18n' -require 'time' -require 'yaml' - -module I18nSimpleBackendTestSetup - def setup_backend - # backend_reset_translations! - @backend = I18n::Backend::Simple.new - @backend.store_translations 'en', :foo => {:bar => 'bar', :baz => 'baz'} - @locale_dir = File.dirname(__FILE__) + '/locale' - end - alias :setup :setup_backend - - # def backend_reset_translations! - # I18n::Backend::Simple::ClassMethods.send :class_variable_set, :@@translations, {} - # end - - def backend_get_translations - # I18n::Backend::Simple::ClassMethods.send :class_variable_get, :@@translations - @backend.instance_variable_get :@translations - end - - def add_datetime_translations - @backend.store_translations :'de', { - :date => { - :formats => { - :default => "%d.%m.%Y", - :short => "%d. %b", - :long => "%d. %B %Y", - }, - :day_names => %w(Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag), - :abbr_day_names => %w(So Mo Di Mi Do Fr Sa), - :month_names => %w(Januar Februar März April Mai Juni Juli August September Oktober November Dezember).unshift(nil), - :abbr_month_names => %w(Jan Feb Mar Apr Mai Jun Jul Aug Sep Okt Nov Dez).unshift(nil), - :order => [:day, :month, :year] - }, - :time => { - :formats => { - :default => "%a, %d. %b %Y %H:%M:%S %z", - :short => "%d. %b %H:%M", - :long => "%d. %B %Y %H:%M", - }, - :am => 'am', - :pm => 'pm' - }, - :datetime => { - :distance_in_words => { - :half_a_minute => 'half a minute', - :less_than_x_seconds => { - :one => 'less than 1 second', - :other => 'less than {{count}} seconds' - }, - :x_seconds => { - :one => '1 second', - :other => '{{count}} seconds' - }, - :less_than_x_minutes => { - :one => 'less than a minute', - :other => 'less than {{count}} minutes' - }, - :x_minutes => { - :one => '1 minute', - :other => '{{count}} minutes' - }, - :about_x_hours => { - :one => 'about 1 hour', - :other => 'about {{count}} hours' - }, - :x_days => { - :one => '1 day', - :other => '{{count}} days' - }, - :about_x_months => { - :one => 'about 1 month', - :other => 'about {{count}} months' - }, - :x_months => { - :one => '1 month', - :other => '{{count}} months' - }, - :about_x_years => { - :one => 'about 1 year', - :other => 'about {{count}} year' - }, - :over_x_years => { - :one => 'over 1 year', - :other => 'over {{count}} years' - } - } - } - } - end -end - -class I18nSimpleBackendTranslationsTest < Test::Unit::TestCase - include I18nSimpleBackendTestSetup - - def test_store_translations_adds_translations # no, really :-) - @backend.store_translations :'en', :foo => 'bar' - assert_equal Hash[:'en', {:foo => 'bar'}], backend_get_translations - end - - def test_store_translations_deep_merges_translations - @backend.store_translations :'en', :foo => {:bar => 'bar'} - @backend.store_translations :'en', :foo => {:baz => 'baz'} - assert_equal Hash[:'en', {:foo => {:bar => 'bar', :baz => 'baz'}}], backend_get_translations - end - - def test_store_translations_forces_locale_to_sym - @backend.store_translations 'en', :foo => 'bar' - assert_equal Hash[:'en', {:foo => 'bar'}], backend_get_translations - end - - def test_store_translations_converts_keys_to_symbols - # backend_reset_translations! - @backend.store_translations 'en', 'foo' => {'bar' => 'bar', 'baz' => 'baz'} - assert_equal Hash[:'en', {:foo => {:bar => 'bar', :baz => 'baz'}}], backend_get_translations - end -end - -class I18nSimpleBackendAvailableLocalesTest < Test::Unit::TestCase - def test_available_locales - @backend = I18n::Backend::Simple.new - @backend.store_translations 'de', :foo => 'bar' - @backend.store_translations 'en', :foo => 'foo' - - assert_equal ['de', 'en'], @backend.available_locales.map{|locale| locale.to_s }.sort - end -end - -class I18nSimpleBackendTranslateTest < Test::Unit::TestCase - include I18nSimpleBackendTestSetup - - def test_translate_calls_lookup_with_locale_given - @backend.expects(:lookup).with('de', :bar, [:foo]).returns 'bar' - @backend.translate 'de', :bar, :scope => [:foo] - end - - def test_given_no_keys_it_returns_the_default - assert_equal 'default', @backend.translate('en', nil, :default => 'default') - end - - def test_translate_given_a_symbol_as_a_default_translates_the_symbol - assert_equal 'bar', @backend.translate('en', nil, :scope => [:foo], :default => :bar) - end - - def test_translate_given_an_array_as_default_uses_the_first_match - assert_equal 'bar', @backend.translate('en', :does_not_exist, :scope => [:foo], :default => [:does_not_exist_2, :bar]) - end - - def test_translate_given_an_array_of_inexistent_keys_it_raises_missing_translation_data - assert_raise I18n::MissingTranslationData do - @backend.translate('en', :does_not_exist, :scope => [:foo], :default => [:does_not_exist_2, :does_not_exist_3]) - end - end - - def test_translate_an_array_of_keys_translates_all_of_them - assert_equal %w(bar baz), @backend.translate('en', [:bar, :baz], :scope => [:foo]) - end - - def test_translate_calls_pluralize - @backend.expects(:pluralize).with 'en', 'bar', 1 - @backend.translate 'en', :bar, :scope => [:foo], :count => 1 - end - - def test_translate_calls_interpolate - @backend.expects(:interpolate).with 'en', 'bar', {} - @backend.translate 'en', :bar, :scope => [:foo] - end - - def test_translate_calls_interpolate_including_count_as_a_value - @backend.expects(:interpolate).with 'en', 'bar', {:count => 1} - @backend.translate 'en', :bar, :scope => [:foo], :count => 1 - end - - def test_translate_given_nil_as_a_locale_raises_an_argument_error - assert_raise(I18n::InvalidLocale){ @backend.translate nil, :bar } - end - - def test_translate_with_a_bogus_key_and_no_default_raises_missing_translation_data - assert_raise(I18n::MissingTranslationData){ @backend.translate 'de', :bogus } - end -end - -class I18nSimpleBackendLookupTest < Test::Unit::TestCase - include I18nSimpleBackendTestSetup - - # useful because this way we can use the backend with no key for interpolation/pluralization - def test_lookup_given_nil_as_a_key_returns_nil - assert_nil @backend.send(:lookup, 'en', nil) - end - - def test_lookup_given_nested_keys_looks_up_a_nested_hash_value - assert_equal 'bar', @backend.send(:lookup, 'en', :bar, [:foo]) - end -end - -class I18nSimpleBackendPluralizeTest < Test::Unit::TestCase - include I18nSimpleBackendTestSetup - - def test_pluralize_given_nil_returns_the_given_entry - entry = {:one => 'bar', :other => 'bars'} - assert_equal entry, @backend.send(:pluralize, nil, entry, nil) - end - - def test_pluralize_given_0_returns_zero_string_if_zero_key_given - assert_equal 'zero', @backend.send(:pluralize, nil, {:zero => 'zero', :one => 'bar', :other => 'bars'}, 0) - end - - def test_pluralize_given_0_returns_plural_string_if_no_zero_key_given - assert_equal 'bars', @backend.send(:pluralize, nil, {:one => 'bar', :other => 'bars'}, 0) - end - - def test_pluralize_given_1_returns_singular_string - assert_equal 'bar', @backend.send(:pluralize, nil, {:one => 'bar', :other => 'bars'}, 1) - end - - def test_pluralize_given_2_returns_plural_string - assert_equal 'bars', @backend.send(:pluralize, nil, {:one => 'bar', :other => 'bars'}, 2) - end - - def test_pluralize_given_3_returns_plural_string - assert_equal 'bars', @backend.send(:pluralize, nil, {:one => 'bar', :other => 'bars'}, 3) - end - - def test_interpolate_given_incomplete_pluralization_data_raises_invalid_pluralization_data - assert_raise(I18n::InvalidPluralizationData){ @backend.send(:pluralize, nil, {:one => 'bar'}, 2) } - end - - # def test_interpolate_given_a_string_raises_invalid_pluralization_data - # assert_raise(I18n::InvalidPluralizationData){ @backend.send(:pluralize, nil, 'bar', 2) } - # end - # - # def test_interpolate_given_an_array_raises_invalid_pluralization_data - # assert_raise(I18n::InvalidPluralizationData){ @backend.send(:pluralize, nil, ['bar'], 2) } - # end -end - -class I18nSimpleBackendInterpolateTest < Test::Unit::TestCase - include I18nSimpleBackendTestSetup - - def test_interpolate_given_a_value_hash_interpolates_the_values_to_the_string - assert_equal 'Hi David!', @backend.send(:interpolate, nil, 'Hi {{name}}!', :name => 'David') - end - - def test_interpolate_given_a_value_hash_interpolates_into_unicode_string - assert_equal 'Häi David!', @backend.send(:interpolate, nil, 'Häi {{name}}!', :name => 'David') - end - - def test_interpolate_given_an_unicode_value_hash_interpolates_to_the_string - assert_equal 'Hi ゆきひろ!', @backend.send(:interpolate, nil, 'Hi {{name}}!', :name => 'ゆきひろ') - end - - def test_interpolate_given_an_unicode_value_hash_interpolates_into_unicode_string - assert_equal 'こんにちは、ゆきひろさん!', @backend.send(:interpolate, nil, 'こんにちは、{{name}}さん!', :name => 'ゆきひろ') - end - - if Kernel.const_defined?(:Encoding) - def test_interpolate_given_a_non_unicode_multibyte_value_hash_interpolates_into_a_string_with_the_same_encoding - assert_equal euc_jp('Hi ゆきひろ!'), @backend.send(:interpolate, nil, 'Hi {{name}}!', :name => euc_jp('ゆきひろ')) - end - - def test_interpolate_given_an_unicode_value_hash_into_a_non_unicode_multibyte_string_raises_encoding_compatibility_error - assert_raise(Encoding::CompatibilityError) do - @backend.send(:interpolate, nil, euc_jp('こんにちは、{{name}}さん!'), :name => 'ゆきひろ') - end - end - - def test_interpolate_given_a_non_unicode_multibyte_value_hash_into_an_unicode_string_raises_encoding_compatibility_error - assert_raise(Encoding::CompatibilityError) do - @backend.send(:interpolate, nil, 'こんにちは、{{name}}さん!', :name => euc_jp('ゆきひろ')) - end - end - end - - def test_interpolate_given_nil_as_a_string_returns_nil - assert_nil @backend.send(:interpolate, nil, nil, :name => 'David') - end - - def test_interpolate_given_an_non_string_as_a_string_returns_nil - assert_equal [], @backend.send(:interpolate, nil, [], :name => 'David') - end - - def test_interpolate_given_a_values_hash_with_nil_values_interpolates_the_string - assert_equal 'Hi !', @backend.send(:interpolate, nil, 'Hi {{name}}!', {:name => nil}) - end - - def test_interpolate_given_an_empty_values_hash_raises_missing_interpolation_argument - assert_raise(I18n::MissingInterpolationArgument) { @backend.send(:interpolate, nil, 'Hi {{name}}!', {}) } - end - - def test_interpolate_given_a_string_containing_a_reserved_key_raises_reserved_interpolation_key - assert_raise(I18n::ReservedInterpolationKey) { @backend.send(:interpolate, nil, '{{default}}', {:default => nil}) } - end - - private - - def euc_jp(string) - string.encode!(Encoding::EUC_JP) - end -end - -class I18nSimpleBackendLocalizeDateTest < Test::Unit::TestCase - include I18nSimpleBackendTestSetup - - def setup - @backend = I18n::Backend::Simple.new - add_datetime_translations - @date = Date.new 2008, 1, 1 - end - - def test_translate_given_the_short_format_it_uses_it - assert_equal '01. Jan', @backend.localize('de', @date, :short) - end - - def test_translate_given_the_long_format_it_uses_it - assert_equal '01. Januar 2008', @backend.localize('de', @date, :long) - end - - def test_translate_given_the_default_format_it_uses_it - assert_equal '01.01.2008', @backend.localize('de', @date, :default) - end - - def test_translate_given_a_day_name_format_it_returns_a_day_name - assert_equal 'Dienstag', @backend.localize('de', @date, '%A') - end - - def test_translate_given_an_abbr_day_name_format_it_returns_an_abbrevated_day_name - assert_equal 'Di', @backend.localize('de', @date, '%a') - end - - def test_translate_given_a_month_name_format_it_returns_a_month_name - assert_equal 'Januar', @backend.localize('de', @date, '%B') - end - - def test_translate_given_an_abbr_month_name_format_it_returns_an_abbrevated_month_name - assert_equal 'Jan', @backend.localize('de', @date, '%b') - end - - def test_translate_given_no_format_it_does_not_fail - assert_nothing_raised{ @backend.localize 'de', @date } - end - - def test_translate_given_an_unknown_format_it_does_not_fail - assert_nothing_raised{ @backend.localize 'de', @date, '%x' } - end - - def test_localize_nil_raises_argument_error - assert_raise(I18n::ArgumentError) { @backend.localize 'de', nil } - end - - def test_localize_object_raises_argument_error - assert_raise(I18n::ArgumentError) { @backend.localize 'de', Object.new } - end -end - -class I18nSimpleBackendLocalizeDateTimeTest < Test::Unit::TestCase - include I18nSimpleBackendTestSetup - - def setup - @backend = I18n::Backend::Simple.new - add_datetime_translations - @morning = DateTime.new 2008, 1, 1, 6 - @evening = DateTime.new 2008, 1, 1, 18 - end - - def test_translate_given_the_short_format_it_uses_it - assert_equal '01. Jan 06:00', @backend.localize('de', @morning, :short) - end - - def test_translate_given_the_long_format_it_uses_it - assert_equal '01. Januar 2008 06:00', @backend.localize('de', @morning, :long) - end - - def test_translate_given_the_default_format_it_uses_it - assert_equal 'Di, 01. Jan 2008 06:00:00 +0000', @backend.localize('de', @morning, :default) - end - - def test_translate_given_a_day_name_format_it_returns_the_correct_day_name - assert_equal 'Dienstag', @backend.localize('de', @morning, '%A') - end - - def test_translate_given_an_abbr_day_name_format_it_returns_the_correct_abbrevated_day_name - assert_equal 'Di', @backend.localize('de', @morning, '%a') - end - - def test_translate_given_a_month_name_format_it_returns_the_correct_month_name - assert_equal 'Januar', @backend.localize('de', @morning, '%B') - end - - def test_translate_given_an_abbr_month_name_format_it_returns_the_correct_abbrevated_month_name - assert_equal 'Jan', @backend.localize('de', @morning, '%b') - end - - def test_translate_given_a_meridian_indicator_format_it_returns_the_correct_meridian_indicator - assert_equal 'am', @backend.localize('de', @morning, '%p') - assert_equal 'pm', @backend.localize('de', @evening, '%p') - end - - def test_translate_given_no_format_it_does_not_fail - assert_nothing_raised{ @backend.localize 'de', @morning } - end - - def test_translate_given_an_unknown_format_it_does_not_fail - assert_nothing_raised{ @backend.localize 'de', @morning, '%x' } - end -end - -class I18nSimpleBackendLocalizeTimeTest < Test::Unit::TestCase - include I18nSimpleBackendTestSetup - - def setup - @old_timezone, ENV['TZ'] = ENV['TZ'], 'UTC' - @backend = I18n::Backend::Simple.new - add_datetime_translations - @morning = Time.parse '2008-01-01 6:00 UTC' - @evening = Time.parse '2008-01-01 18:00 UTC' - end - - def teardown - @old_timezone ? ENV['TZ'] = @old_timezone : ENV.delete('TZ') - end - - def test_translate_given_the_short_format_it_uses_it - assert_equal '01. Jan 06:00', @backend.localize('de', @morning, :short) - end - - def test_translate_given_the_long_format_it_uses_it - assert_equal '01. Januar 2008 06:00', @backend.localize('de', @morning, :long) - end - - # TODO Seems to break on Windows because ENV['TZ'] is ignored. What's a better way to do this? - # def test_translate_given_the_default_format_it_uses_it - # assert_equal 'Di, 01. Jan 2008 06:00:00 +0000', @backend.localize('de', @morning, :default) - # end - - def test_translate_given_a_day_name_format_it_returns_the_correct_day_name - assert_equal 'Dienstag', @backend.localize('de', @morning, '%A') - end - - def test_translate_given_an_abbr_day_name_format_it_returns_the_correct_abbrevated_day_name - assert_equal 'Di', @backend.localize('de', @morning, '%a') - end - - def test_translate_given_a_month_name_format_it_returns_the_correct_month_name - assert_equal 'Januar', @backend.localize('de', @morning, '%B') - end - - def test_translate_given_an_abbr_month_name_format_it_returns_the_correct_abbrevated_month_name - assert_equal 'Jan', @backend.localize('de', @morning, '%b') - end - - def test_translate_given_a_meridian_indicator_format_it_returns_the_correct_meridian_indicator - assert_equal 'am', @backend.localize('de', @morning, '%p') - assert_equal 'pm', @backend.localize('de', @evening, '%p') - end - - def test_translate_given_no_format_it_does_not_fail - assert_nothing_raised{ @backend.localize 'de', @morning } - end - - def test_translate_given_an_unknown_format_it_does_not_fail - assert_nothing_raised{ @backend.localize 'de', @morning, '%x' } - end -end - -class I18nSimpleBackendHelperMethodsTest < Test::Unit::TestCase - def setup - @backend = I18n::Backend::Simple.new - end - - def test_deep_symbolize_keys_works - result = @backend.send :deep_symbolize_keys, 'foo' => {'bar' => {'baz' => 'bar'}} - expected = {:foo => {:bar => {:baz => 'bar'}}} - assert_equal expected, result - end -end - -class I18nSimpleBackendLoadTranslationsTest < Test::Unit::TestCase - include I18nSimpleBackendTestSetup - - def test_load_translations_with_unknown_file_type_raises_exception - assert_raise(I18n::UnknownFileType) { @backend.load_translations "#{@locale_dir}/en.xml" } - end - - def test_load_translations_with_ruby_file_type_does_not_raise_exception - assert_nothing_raised { @backend.load_translations "#{@locale_dir}/en.rb" } - end - - def test_load_rb_loads_data_from_ruby_file - data = @backend.send :load_rb, "#{@locale_dir}/en.rb" - assert_equal({:'en-Ruby' => {:foo => {:bar => "baz"}}}, data) - end - - def test_load_rb_loads_data_from_yaml_file - data = @backend.send :load_yml, "#{@locale_dir}/en.yml" - assert_equal({'en-Yaml' => {'foo' => {'bar' => 'baz'}}}, data) - end - - def test_load_translations_loads_from_different_file_formats - @backend = I18n::Backend::Simple.new - @backend.load_translations "#{@locale_dir}/en.rb", "#{@locale_dir}/en.yml" - expected = { - :'en-Ruby' => {:foo => {:bar => "baz"}}, - :'en-Yaml' => {:foo => {:bar => "baz"}} - } - assert_equal expected, backend_get_translations - end -end - -class I18nSimpleBackendLoadPathTest < Test::Unit::TestCase - include I18nSimpleBackendTestSetup - - def teardown - I18n.load_path = [] - end - - def test_nested_load_paths_do_not_break_locale_loading - @backend = I18n::Backend::Simple.new - I18n.load_path = [[File.dirname(__FILE__) + '/locale/en.yml']] - assert_nil backend_get_translations - assert_nothing_raised { @backend.send :init_translations } - assert_not_nil backend_get_translations - end - - def test_adding_arrays_of_filenames_to_load_path_do_not_break_locale_loading - @backend = I18n::Backend::Simple.new - I18n.load_path << Dir[File.dirname(__FILE__) + '/locale/*.{rb,yml}'] - assert_nil backend_get_translations - assert_nothing_raised { @backend.send :init_translations } - assert_not_nil backend_get_translations - end -end - -class I18nSimpleBackendReloadTranslationsTest < Test::Unit::TestCase - include I18nSimpleBackendTestSetup - - def setup - @backend = I18n::Backend::Simple.new - I18n.load_path = [File.dirname(__FILE__) + '/locale/en.yml'] - assert_nil backend_get_translations - @backend.send :init_translations - end - - def teardown - I18n.load_path = [] - end - - def test_setup - assert_not_nil backend_get_translations - end - - def test_reload_translations_unloads_translations - @backend.reload! - assert_nil backend_get_translations - end - - def test_reload_translations_uninitializes_translations - @backend.reload! - assert_equal @backend.initialized?, false - end -end -- cgit v1.2.3 From a8f1ee59868a804ece386264385223d08e122918 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 25 Dec 2009 10:04:26 -0800 Subject: Fix tests --- actionpack/test/template/translation_helper_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb index a20f3c394c..d67d2c7911 100644 --- a/actionpack/test/template/translation_helper_test.rb +++ b/actionpack/test/template/translation_helper_test.rb @@ -9,7 +9,7 @@ class TranslationHelperTest < Test::Unit::TestCase end def test_delegates_to_i18n_setting_the_raise_option - I18n.expects(:translate).with(:foo, :locale => 'en', :raise => true) + I18n.expects(:translate).with(:foo, :locale => 'en', :raise => true).returns("") translate :foo, :locale => 'en' end @@ -26,7 +26,7 @@ class TranslationHelperTest < Test::Unit::TestCase def test_scoping_by_partial expects(:template).returns(stub(:path_without_format_and_extension => "people/index")) - I18n.expects(:translate).with("people.index.foo", :locale => 'en', :raise => true) + I18n.expects(:translate).with("people.index.foo", :locale => 'en', :raise => true).returns("") translate ".foo", :locale => 'en' end end -- cgit v1.2.3 From 7f5d44bac548c9f80e2ea88e191356dbb099593e Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 25 Dec 2009 10:14:44 -0800 Subject: The controller key shouldnt be part of the mapping if its not used --- actionpack/lib/action_dispatch/routing/mapper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index a6b32e0152..b48fc6edc5 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -92,9 +92,9 @@ module ActionDispatch controller, action = to.split('#') { :controller => controller, :action => action } when Symbol - { :controller => default_controller, :action => to.to_s } + { :action => to.to_s }.merge(default_controller ? { :controller => default_controller } : {}) else - { :controller => default_controller } + default_controller ? { :controller => default_controller } : {} end if defaults[:controller].blank? && segment_keys.exclude?("controller") -- cgit v1.2.3 From 2e79ec71a542c2d2e7bedbe12eda0b5e177fb0e0 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 01:31:11 +0530 Subject: Model.scoped now returns a relation if invoked without any arguments Example : posts = Post.scoped posts.size # Fires "select count(*) from posts" and returns the count posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects --- activerecord/lib/active_record/named_scope.rb | 30 +++++++++++++++++---------- activerecord/test/cases/relations_test.rb | 6 ++++++ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index bbe2d1f205..321871104c 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -6,18 +6,26 @@ module ActiveRecord module NamedScope extend ActiveSupport::Concern - # All subclasses of ActiveRecord::Base have one named scope: - # * scoped - which allows for the creation of anonymous \scopes, on the fly: Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions) - # - # These anonymous \scopes tend to be useful when procedurally generating complex queries, where passing - # intermediate values (scopes) around as first-class objects is convenient. - # - # You can define a scope that applies to all finders using ActiveRecord::Base.default_scope. - included do - named_scope :scoped, lambda { |scope| scope } - end - module ClassMethods + # Returns a relation if invoked without any arguments. + # + # posts = Post.scoped + # posts.size # Fires "select count(*) from posts" and returns the count + # posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects + # + # Returns an anonymous named scope if any options are supplied. + # + # shirts = Shirt.scoped(:conditions => {:color => 'red'}) + # shirts = shirts.scoped(:include => :washing_instructions) + # + # Anonymous \scopes tend to be useful when procedurally generating complex queries, where passing + # intermediate values (scopes) around as first-class objects is convenient. + # + # You can define a scope that applies to all finders using ActiveRecord::Base.default_scope. + def scoped(options = {}, &block) + options.present? ? Scope.new(self, options, &block) : arel_table + end + def scopes read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {}) end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 1a2c8030fb..5fa151df45 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -12,6 +12,12 @@ require 'models/company' class RelationTest < ActiveRecord::TestCase fixtures :authors, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :posts, :comments + def test_scoped + topics = Topic.scoped + assert_kind_of ActiveRecord::Relation, topics + assert_equal 4, topics.size + end + def test_finding_with_conditions assert_equal Author.find(:all, :conditions => "name = 'David'"), Author.all.conditions("name = 'David'").to_a end -- cgit v1.2.3 From ee70d1b6ad9b79d2c3d284e78af4e20416575ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 25 Dec 2009 21:35:40 +0100 Subject: adv_attr_accessors in ActionMailer are not sent to the views, use the mailer object if you need to access the subject, recipients, from, etc. --- .../lib/action_mailer/adv_attr_accessor.rb | 32 ++++++++++------------ actionmailer/lib/action_mailer/base.rb | 24 ++++++++-------- actionmailer/lib/action_mailer/mail_helper.rb | 5 ++++ actionmailer/lib/action_mailer/part.rb | 3 +- actionmailer/test/adv_attr_test.rb | 32 +++++++++++++++++----- actionmailer/test/mail_render_test.rb | 25 ++++++++++++++--- 6 files changed, 79 insertions(+), 42 deletions(-) diff --git a/actionmailer/lib/action_mailer/adv_attr_accessor.rb b/actionmailer/lib/action_mailer/adv_attr_accessor.rb index e77029afdd..be6b1feca9 100644 --- a/actionmailer/lib/action_mailer/adv_attr_accessor.rb +++ b/actionmailer/lib/action_mailer/adv_attr_accessor.rb @@ -1,29 +1,25 @@ module ActionMailer module AdvAttrAccessor #:nodoc: - def self.included(base) - base.extend(ClassMethods) - end - - module ClassMethods #:nodoc: - def adv_attr_accessor(*names) - names.each do |name| - ivar = "@#{name}" + def adv_attr_accessor(*names) + names.each do |name| + ivar = "@#{name}" - define_method("#{name}=") do |value| - instance_variable_set(ivar, value) + class_eval <<-ACCESSORS, __FILE__, __LINE__ + 1 + def #{name}=(value) + #{ivar} = value end - define_method(name) do |*parameters| - raise ArgumentError, "expected 0 or 1 parameters" unless parameters.length <= 1 - if parameters.empty? - if instance_variable_names.include?(ivar) - instance_variable_get(ivar) - end + def #{name}(*args) + raise ArgumentError, "expected 0 or 1 parameters" unless args.length <= 1 + if args.empty? + #{ivar} if instance_variable_names.include?(#{ivar.inspect}) else - instance_variable_set(ivar, parameters.first) + #{ivar} = args.first end end - end + ACCESSORS + + self.protected_instance_variables << ivar if self.respond_to?(:protected_instance_variables) end end end diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index de78e87fb4..478762f94f 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -25,7 +25,8 @@ module ActionMailer #:nodoc: # bcc ["bcc@example.com", "Order Watcher "] # from "system@example.com" # subject "New account information" - # body :account => recipient + # + # @account = recipient # end # end # @@ -45,13 +46,6 @@ module ActionMailer #:nodoc: # address. Setting this is useful when you want delivery notifications sent to a different address than # the one in from. # - # The body method has special behavior. It takes a hash which generates an instance variable - # named after each key in the hash containing the value that that key points to. - # - # So, for example, body :account => recipient would result - # in an instance variable @account with the value of recipient being accessible in the - # view. - # # # = Mailer views # @@ -71,7 +65,12 @@ module ActionMailer #:nodoc: # You can even use Action Pack helpers in these views. For example: # # You got a new note! - # <%= truncate(note.body, 25) %> + # <%= truncate(@note.body, 25) %> + # + # If you need to access the subject, from or the recipients in the view, you can do that through mailer object: + # + # You got a new note from <%= mailer.from %>! + # <%= truncate(@note.body, 25) %> # # # = Generating URLs @@ -254,14 +253,15 @@ module ActionMailer #:nodoc: # and appear last in the mime encoded message. You can also pick a different order from inside a method with # +implicit_parts_order+. class Base < AbstractController::Base - include AdvAttrAccessor, PartContainer, Quoting, Utils + include PartContainer, Quoting, Utils + extend AdvAttrAccessor include AbstractController::Rendering include AbstractController::LocalizedCache include AbstractController::Layouts include AbstractController::Helpers - helper ActionMailer::MailHelper + helper ActionMailer::MailHelper include ActionController::UrlWriter include ActionMailer::DeprecatedBody @@ -289,7 +289,7 @@ module ActionMailer #:nodoc: @@default_implicit_parts_order = [ "text/html", "text/enriched", "text/plain" ] cattr_accessor :default_implicit_parts_order - @@protected_instance_variables = [] + @@protected_instance_variables = %w(@parts @mail) cattr_reader :protected_instance_variables # Specify the BCC addresses for the message diff --git a/actionmailer/lib/action_mailer/mail_helper.rb b/actionmailer/lib/action_mailer/mail_helper.rb index 9aa178cdef..a1dec68c5d 100644 --- a/actionmailer/lib/action_mailer/mail_helper.rb +++ b/actionmailer/lib/action_mailer/mail_helper.rb @@ -15,5 +15,10 @@ module ActionMailer formatted end + + # Access the mailer instance. + def mailer #:nodoc: + @controller + end end end \ No newline at end of file diff --git a/actionmailer/lib/action_mailer/part.rb b/actionmailer/lib/action_mailer/part.rb index 2bbb59cdb6..9ff962c39a 100644 --- a/actionmailer/lib/action_mailer/part.rb +++ b/actionmailer/lib/action_mailer/part.rb @@ -4,7 +4,8 @@ module ActionMailer # and add them to the +parts+ list of the mailer, it is easier # to use the helper methods in ActionMailer::PartContainer. class Part - include AdvAttrAccessor, PartContainer, Utils + include PartContainer, Utils + extend AdvAttrAccessor # Represents the body of the part, as a string. This should not be a # Hash (like ActionMailer::Base), but if you want a template to be rendered diff --git a/actionmailer/test/adv_attr_test.rb b/actionmailer/test/adv_attr_test.rb index fd909a5627..f22d733bc5 100644 --- a/actionmailer/test/adv_attr_test.rb +++ b/actionmailer/test/adv_attr_test.rb @@ -1,18 +1,36 @@ require 'abstract_unit' require 'action_mailer/adv_attr_accessor' -class AdvAttrTest < Test::Unit::TestCase +class AdvAttrTest < ActiveSupport::TestCase class Person - include ActionMailer::AdvAttrAccessor + cattr_reader :protected_instance_variables + @@protected_instance_variables = [] + + extend ActionMailer::AdvAttrAccessor adv_attr_accessor :name end + def setup + @person = Person.new + end + def test_adv_attr - bob = Person.new - assert_nil bob.name - bob.name 'Bob' - assert_equal 'Bob', bob.name + assert_nil @person.name + @person.name 'Bob' + assert_equal 'Bob', @person.name + end + + def test_adv_attr_writer + assert_nil @person.name + @person.name = 'Bob' + assert_equal 'Bob', @person.name + end + + def test_raise_an_error_with_multiple_args + assert_raise(ArgumentError) { @person.name('x', 'y') } + end - assert_raise(ArgumentError) {bob.name 'x', 'y'} + def test_ivar_is_added_to_protected_instnace_variables + assert Person.protected_instance_variables.include?('@name') end end diff --git a/actionmailer/test/mail_render_test.rb b/actionmailer/test/mail_render_test.rb index 514f7ed798..0e38a0caba 100644 --- a/actionmailer/test/mail_render_test.rb +++ b/actionmailer/test/mail_render_test.rb @@ -40,13 +40,20 @@ class RenderMailer < ActionMailer::Base from "tester@example.com" end - def included_old_subtemplate(recipient) + def mailer_accessor(recipient) recipients recipient - subject "Including another template in the one being rendered" + subject "Mailer Accessor" from "tester@example.com" - @world = "Earth" - render :inline => "Hello, <%= render \"subtemplate\" %>" + render :inline => "Look, <%= mailer.subject %>!" + end + + def no_instance_variable(recipient) + recipients recipient + subject "No Instance Variable" + from "tester@example.com" + + render :inline => "Look, subject.nil? is <%= @subject.nil? %>!" end def initialize_defaults(method_name) @@ -108,6 +115,16 @@ class RenderHelperTest < Test::Unit::TestCase mail = RenderMailer.deliver_included_subtemplate(@recipient) assert_equal "Hey Ho, let's go!", mail.body.strip end + + def test_mailer_accessor + mail = RenderMailer.deliver_mailer_accessor(@recipient) + assert_equal "Look, Mailer Accessor!", mail.body.strip + end + + def test_no_instance_variable + mail = RenderMailer.deliver_no_instance_variable(@recipient) + assert_equal "Look, subject.nil? is true!", mail.body.strip + end end class FirstSecondHelperTest < Test::Unit::TestCase -- cgit v1.2.3 From 88ba056043e22c4c60dde6b07df897e502f49491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 25 Dec 2009 21:46:01 +0100 Subject: Refactor multiple parts logic and move Utils to PartContainer. --- actionmailer/lib/action_mailer/base.rb | 14 +++----------- actionmailer/lib/action_mailer/part.rb | 14 +++----------- actionmailer/lib/action_mailer/part_container.rb | 20 ++++++++++++++++++-- actionmailer/lib/action_mailer/utils.rb | 7 ------- 4 files changed, 24 insertions(+), 31 deletions(-) delete mode 100644 actionmailer/lib/action_mailer/utils.rb diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 478762f94f..84f5bd23a9 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -253,7 +253,7 @@ module ActionMailer #:nodoc: # and appear last in the mime encoded message. You can also pick a different order from inside a method with # +implicit_parts_order+. class Base < AbstractController::Base - include PartContainer, Quoting, Utils + include PartContainer, Quoting extend AdvAttrAccessor include AbstractController::Rendering @@ -454,7 +454,7 @@ module ActionMailer #:nodoc: :default => method_name.humanize) # Build the mail object itself - @mail = create_mail + create_mail end # Delivers a TMail::Mail object. By default, it delivers the cached mail @@ -582,15 +582,7 @@ module ActionMailer #:nodoc: m.set_content_type(real_content_type, nil, ctype_attrs) m.body = normalize_new_lines(@parts.first.body) else - @parts.each do |p| - part = (TMail::Mail === p ? p : p.to_mail(self)) - m.parts << part - end - - if real_content_type =~ /multipart/ - ctype_attrs.delete "charset" - m.set_content_type(real_content_type, nil, ctype_attrs) - end + setup_multiple_parts(m, real_content_type, ctype_attrs) end @mail = m diff --git a/actionmailer/lib/action_mailer/part.rb b/actionmailer/lib/action_mailer/part.rb index 9ff962c39a..716eaae86e 100644 --- a/actionmailer/lib/action_mailer/part.rb +++ b/actionmailer/lib/action_mailer/part.rb @@ -4,7 +4,7 @@ module ActionMailer # and add them to the +parts+ list of the mailer, it is easier # to use the helper methods in ActionMailer::PartContainer. class Part - include PartContainer, Utils + include PartContainer extend AdvAttrAccessor # Represents the body of the part, as a string. This should not be a @@ -83,16 +83,8 @@ module ActionMailer @parts.unshift Part.new(:charset => charset, :body => @body, :content_type => 'text/plain') @body = nil end - - @parts.each do |p| - prt = (TMail::Mail === p ? p : p.to_mail(defaults)) - part.parts << prt - end - - if real_content_type =~ /multipart/ - ctype_attrs.delete 'charset' - part.set_content_type(real_content_type, nil, ctype_attrs) - end + + setup_multiple_parts(part, real_content_type, ctype_attrs) end headers.each { |k,v| part[k] = v } diff --git a/actionmailer/lib/action_mailer/part_container.rb b/actionmailer/lib/action_mailer/part_container.rb index abfd8f8426..3fe502b1fb 100644 --- a/actionmailer/lib/action_mailer/part_container.rb +++ b/actionmailer/lib/action_mailer/part_container.rb @@ -39,8 +39,24 @@ module ActionMailer end private - - def parse_content_type(defaults=nil) + + def normalize_new_lines(text) #:nodoc: + text.to_s.gsub(/\r\n?/, "\n") + end + + def setup_multiple_parts(mailer, real_content_type, ctype_attrs) #:nodoc: + @parts.each do |p| + part = (TMail::Mail === p ? p : p.to_mail(self)) + mailer.parts << part + end + + if real_content_type =~ /multipart/ + ctype_attrs.delete "charset" + mailer.set_content_type(real_content_type, nil, ctype_attrs) + end + end + + def parse_content_type(defaults=nil) #:nodoc: if content_type.blank? return defaults ? [ defaults.content_type, { 'charset' => defaults.charset } ] : diff --git a/actionmailer/lib/action_mailer/utils.rb b/actionmailer/lib/action_mailer/utils.rb deleted file mode 100644 index 26d2e60aaf..0000000000 --- a/actionmailer/lib/action_mailer/utils.rb +++ /dev/null @@ -1,7 +0,0 @@ -module ActionMailer - module Utils #:nodoc: - def normalize_new_lines(text) - text.to_s.gsub(/\r\n?/, "\n") - end - end -end -- cgit v1.2.3 From 9f1c359a201bfacff7b89cd1e8579bc1bdf541e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 25 Dec 2009 21:59:00 +0100 Subject: Fix whiny_nil tests, improve error messages and make CI happy. --- activesupport/test/whiny_nil_test.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/activesupport/test/whiny_nil_test.rb b/activesupport/test/whiny_nil_test.rb index 009d97940f..1e4f8d854a 100644 --- a/activesupport/test/whiny_nil_test.rb +++ b/activesupport/test/whiny_nil_test.rb @@ -13,38 +13,39 @@ class WhinyNilTest < Test::Unit::TestCase def test_unchanged nil.method_thats_not_in_whiners rescue NoMethodError => nme - assert(nme.message =~ /nil:NilClass/) + assert_match(/nil:NilClass/, nme.message) end def test_active_record nil.save! rescue NoMethodError => nme - assert(!(nme.message =~ /nil:NilClass/)) + assert_no_match(/nil:NilClass/, nme.message) assert_match(/nil\.save!/, nme.message) end def test_array nil.each rescue NoMethodError => nme - assert(!(nme.message =~ /nil:NilClass/)) + assert_no_match(/nil:NilClass/, nme.message) assert_match(/nil\.each/, nme.message) end def test_id nil.id rescue RuntimeError => nme - assert(!(nme.message =~ /nil:NilClass/)) + assert_no_match(/nil:NilClass/, nme.message) end def test_no_to_ary_coercion nil.to_ary rescue NoMethodError => nme - assert(nme.message =~ /nil:NilClass/) + assert_no_match(/nil:NilClass/, nme.message) + assert_match(/nil\.to_ary/, nme.message) end def test_no_to_str_coercion nil.to_str rescue NoMethodError => nme - assert(nme.message =~ /nil:NilClass/) + assert_match(/nil:NilClass/, nme.message) end end -- cgit v1.2.3 From a7fd564ab197a376336ebfa8bceaf14553e7628f Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 02:53:10 +0530 Subject: Add Model.select/group/order/limit/joins/conditions/preload/eager_load class methods returning a lazy relation. Examples : posts = Post.select('id).order('name') # Returns a lazy relation posts.each {|p| puts p.id } # Fires "select id from posts order by name" --- activerecord/lib/active_record/base.rb | 10 ++--- activerecord/lib/active_record/named_scope.rb | 21 +++++++++- activerecord/lib/active_record/relation.rb | 56 ++++++++++++++----------- activerecord/test/cases/relations_test.rb | 60 +++++++++++++++------------ 4 files changed, 88 insertions(+), 59 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 321bba466e..03324b7f24 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -13,6 +13,7 @@ require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/string/behavior' require 'active_support/core_ext/object/metaclass' +require 'active_support/core_ext/module/delegation' module ActiveRecord #:nodoc: # Generic Active Record exception class. @@ -650,6 +651,8 @@ module ActiveRecord #:nodoc: end end + delegate :select, :group, :order, :limit, :joins, :conditions, :preload, :eager_load, :to => :arel_table + # A convenience wrapper for find(:first, *args). You can pass in all the # same arguments to this method as you can to find(:first). def first(*args) @@ -1514,13 +1517,8 @@ module ActiveRecord #:nodoc: "(#{segments.join(') AND (')})" unless segments.empty? end - def arel_table(table = nil) - table = table_name if table.blank? - if @arel_table.nil? || @arel_table.name != table - @arel_table = Relation.new(self, Arel::Table.new(table)) - end - @arel_table + Relation.new(self, Arel::Table.new(table || table_name)) end private diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 321871104c..38d54fa8ec 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -23,7 +23,26 @@ module ActiveRecord # # You can define a scope that applies to all finders using ActiveRecord::Base.default_scope. def scoped(options = {}, &block) - options.present? ? Scope.new(self, options, &block) : arel_table + if options.present? + Scope.new(self, options, &block) + else + if !scoped?(:find) + relation = arel_table + else + relation = construct_finder_arel + include_associations = scope(:find, :include) + + if include_associations.present? + if references_eager_loaded_tables?(options) + relation.eager_load(include_associations) + else + relation.preload(include_associations) + end + end + end + + relation + end end def scopes diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 5f0eec754f..28f04e5d85 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -4,20 +4,20 @@ module ActiveRecord delegate :length, :collect, :find, :map, :each, :to => :to_a attr_reader :relation, :klass - def initialize(klass, relation) + def initialize(klass, relation, readonly = false, preload = [], eager_load = []) @klass, @relation = klass, relation - @readonly = false - @associations_to_preload = [] - @eager_load_associations = [] + @readonly = readonly + @associations_to_preload = preload + @eager_load_associations = eager_load end - def preload(association) - @associations_to_preload += association + def preload(associations) + @associations_to_preload << associations self end - def eager_load(association) - @eager_load_associations += association + def eager_load(associations) + @eager_load_associations += Array.wrap(associations) self end @@ -45,7 +45,7 @@ module ActiveRecord @klass.find_by_sql(@relation.to_sql) end - @klass.send(:preload_associations, records, @associations_to_preload) unless @associations_to_preload.empty? + @associations_to_preload.each {|associations| @klass.send(:preload_associations, records, associations) } records.each { |record| record.readonly! } if @readonly records @@ -57,27 +57,27 @@ module ActiveRecord end def select(selects) - selects.blank? ? self : Relation.new(@klass, @relation.project(selects)) + selects.blank? ? self : create_new_relation(@relation.project(selects)) end def group(groups) - groups.blank? ? self : Relation.new(@klass, @relation.group(groups)) + groups.blank? ? self : create_new_relation(@relation.group(groups)) end def order(orders) - orders.blank? ? self : Relation.new(@klass, @relation.order(orders)) + orders.blank? ? self : create_new_relation(@relation.order(orders)) end def limit(limits) - limits.blank? ? self : Relation.new(@klass, @relation.take(limits)) + limits.blank? ? self : create_new_relation(@relation.take(limits)) end def offset(offsets) - offsets.blank? ? self : Relation.new(@klass, @relation.skip(offsets)) + offsets.blank? ? self : create_new_relation(@relation.skip(offsets)) end def on(join) - join.blank? ? self : Relation.new(@klass, @relation.on(join)) + join.blank? ? self : create_new_relation(@relation.on(join)) end def joins(join, join_type = nil) @@ -96,7 +96,7 @@ module ActiveRecord else @relation.join(join, join_type) end - Relation.new(@klass, join) + create_new_relation(join) end end @@ -105,7 +105,7 @@ module ActiveRecord self else conditions = @klass.send(:merge_conditions, conditions) if [String, Hash, Array].include?(conditions.class) - Relation.new(@klass, @relation.where(conditions)) + create_new_relation(@relation.where(conditions)) end end @@ -114,14 +114,20 @@ module ActiveRecord end private - def method_missing(method, *args, &block) - if @relation.respond_to?(method) - @relation.send(method, *args, &block) - elsif Array.method_defined?(method) - to_a.send(method, *args, &block) - else - super - end + + def method_missing(method, *args, &block) + if @relation.respond_to?(method) + @relation.send(method, *args, &block) + elsif Array.method_defined?(method) + to_a.send(method, *args, &block) + else + super end + end + + def create_new_relation(relation) + Relation.new(@klass, relation, @readonly, @associations_to_preload, @eager_load_associations) + end + end end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 5fa151df45..910b954c8c 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -19,47 +19,49 @@ class RelationTest < ActiveRecord::TestCase end def test_finding_with_conditions - assert_equal Author.find(:all, :conditions => "name = 'David'"), Author.all.conditions("name = 'David'").to_a + assert_equal ["David"], Author.conditions(:name => 'David').map(&:name) + assert_equal ['Mary'], Author.conditions(["name = ?", 'Mary']).map(&:name) end def test_finding_with_order - topics = Topic.all.order('id') + topics = Topic.order('id') assert_equal 4, topics.size assert_equal topics(:first).title, topics.first.title end def test_finding_with_order_and_take - entrants = Entrant.all.order("id ASC").limit(2).to_a + entrants = Entrant.order("id ASC").limit(2).to_a assert_equal(2, entrants.size) assert_equal(entrants(:first).name, entrants.first.name) end def test_finding_with_order_limit_and_offset - entrants = Entrant.all.order("id ASC").limit(2).offset(1) + entrants = Entrant.order("id ASC").limit(2).offset(1) assert_equal(2, entrants.size) assert_equal(entrants(:second).name, entrants.first.name) - entrants = Entrant.all.order("id ASC").limit(2).offset(2) + entrants = Entrant.order("id ASC").limit(2).offset(2) assert_equal(1, entrants.size) assert_equal(entrants(:third).name, entrants.first.name) end def test_finding_with_group - developers = Developer.all.group("salary").select("salary").to_a + developers = Developer.group("salary").select("salary").to_a assert_equal 4, developers.size assert_equal 4, developers.map(&:salary).uniq.size end def test_finding_with_hash_conditions_on_joined_table - firms = DependentFirm.all.joins(:account).conditions({:name => 'RailsCore', :accounts => { :credit_limit => 55..60 }}).to_a + firms = DependentFirm.joins(:account).conditions({:name => 'RailsCore', :accounts => { :credit_limit => 55..60 }}).to_a assert_equal 1, firms.size assert_equal companies(:rails_core), firms.first end def test_find_all_with_join - developers_on_project_one = Developer.all.joins('LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id').conditions('project_id=1').to_a + developers_on_project_one = Developer.joins('LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id'). + conditions('project_id=1').to_a assert_equal 3, developers_on_project_one.length developer_names = developers_on_project_one.map { |d| d.name } @@ -68,11 +70,11 @@ class RelationTest < ActiveRecord::TestCase end def test_find_on_hash_conditions - assert_equal Topic.find(:all, :conditions => {:approved => false}), Topic.all.conditions({ :approved => false }).to_a + assert_equal Topic.find(:all, :conditions => {:approved => false}), Topic.conditions({ :approved => false }).to_a end def test_joins_with_string_array - person_with_reader_and_post = Post.all.joins([ + person_with_reader_and_post = Post.joins([ "INNER JOIN categorizations ON categorizations.post_id = posts.id", "INNER JOIN categories ON categories.id = categorizations.category_id AND categories.type = 'SpecialCategory'" ] @@ -80,8 +82,8 @@ class RelationTest < ActiveRecord::TestCase assert_equal 1, person_with_reader_and_post.size end - def test_relation_responds_to_delegated_methods - relation = Topic.all + def test_scoped_responds_to_delegated_methods + relation = Topic.scoped ["map", "uniq", "sort", "insert", "delete", "update"].each do |method| assert relation.respond_to?(method), "Topic.all should respond to #{method.inspect}" @@ -89,13 +91,14 @@ class RelationTest < ActiveRecord::TestCase end def test_find_with_readonly_option - Developer.all.each { |d| assert !d.readonly? } - Developer.all.readonly.each { |d| assert d.readonly? } - Developer.all(:readonly => true).each { |d| assert d.readonly? } + Developer.scoped.each { |d| assert !d.readonly? } + Developer.scoped.readonly.each { |d| assert d.readonly? } end def test_eager_association_loading_of_stis_with_multiple_references - authors = Author.all(:include => { :posts => { :special_comments => { :post => [ :special_comments, :very_special_comment ] } } }, :order => 'comments.body, very_special_comments_posts.body', :conditions => 'posts.id = 4').to_a + authors = Author.eager_load(:posts => { :special_comments => { :post => [ :special_comments, :very_special_comment ] } }). + order('comments.body, very_special_comments_posts.body').conditions('posts.id = 4').to_a + assert_equal [authors(:david)], authors assert_no_queries do authors.first.posts.first.special_comments.first.post.special_comments @@ -105,50 +108,53 @@ class RelationTest < ActiveRecord::TestCase def test_find_with_included_associations assert_queries(2) do - posts = Post.find(:all, :include => :comments) + posts = Post.preload(:comments) posts.first.comments.first end + assert_queries(2) do - posts = Post.all(:include => :comments).to_a + posts = Post.preload(:comments).to_a posts.first.comments.first end + assert_queries(2) do - posts = Post.find(:all, :include => :author) + posts = Post.preload(:author) posts.first.author end + assert_queries(2) do - posts = Post.all(:include => :author).to_a + posts = Post.preload(:author).to_a posts.first.author end end def test_default_scope_with_conditions_string - assert_equal Developer.find_all_by_name('David').map(&:id).sort, DeveloperCalledDavid.all.to_a.map(&:id).sort + assert_equal Developer.find_all_by_name('David').map(&:id).sort, DeveloperCalledDavid.scoped.to_a.map(&:id).sort assert_equal nil, DeveloperCalledDavid.create!.name end def test_default_scope_with_conditions_hash - assert_equal Developer.find_all_by_name('Jamis').map(&:id).sort, DeveloperCalledJamis.all.map(&:id).sort + assert_equal Developer.find_all_by_name('Jamis').map(&:id).sort, DeveloperCalledJamis.scoped.map(&:id).sort assert_equal 'Jamis', DeveloperCalledJamis.create!.name end - def test_loading_with_one_association - posts = Post.all(:include => :comments) + def test_loading_with_one_association + posts = Post.preload(:comments) post = posts.find { |p| p.id == 1 } assert_equal 2, post.comments.size assert post.comments.include?(comments(:greetings)) - post = Post.find(:first, :include => :comments, :conditions => "posts.title = 'Welcome to the weblog'") + post = Post.conditions("posts.title = 'Welcome to the weblog'").preload(:comments).first assert_equal 2, post.comments.size assert post.comments.include?(comments(:greetings)) - posts = Post.all(:include => :last_comment) + posts = Post.preload(:last_comment) post = posts.find { |p| p.id == 1 } assert_equal Post.find(1).last_comment, post.last_comment end def test_loading_with_one_association_with_non_preload - posts = Post.all(:include => :last_comment, :order => 'comments.id DESC') + posts = Post.eager_load(:last_comment).order('comments.id DESC') post = posts.find { |p| p.id == 1 } assert_equal Post.find(1).last_comment, post.last_comment end -- cgit v1.2.3 From 1a993371805122cf50531b95651c6933d568080e Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 03:10:55 +0530 Subject: No parentheses for assert_equal --- activerecord/test/cases/relations_test.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 910b954c8c..769a920a8b 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -32,19 +32,19 @@ class RelationTest < ActiveRecord::TestCase def test_finding_with_order_and_take entrants = Entrant.order("id ASC").limit(2).to_a - assert_equal(2, entrants.size) - assert_equal(entrants(:first).name, entrants.first.name) + assert_equal 2, entrants.size + assert_equal entrants(:first).name, entrants.first.name end def test_finding_with_order_limit_and_offset entrants = Entrant.order("id ASC").limit(2).offset(1) - assert_equal(2, entrants.size) - assert_equal(entrants(:second).name, entrants.first.name) + assert_equal 2, entrants.size + assert_equal entrants(:second).name, entrants.first.name entrants = Entrant.order("id ASC").limit(2).offset(2) - assert_equal(1, entrants.size) - assert_equal(entrants(:third).name, entrants.first.name) + assert_equal 1, entrants.size + assert_equal entrants(:third).name, entrants.first.name end def test_finding_with_group -- cgit v1.2.3 From 95274b28d95ad85ada25eb0c697ce5650ae488a9 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 03:50:57 +0530 Subject: Rename Model.conditions and relation.conditions to .where --- activerecord/lib/active_record/associations.rb | 6 +++--- .../associations/has_and_belongs_to_many_association.rb | 2 +- .../lib/active_record/associations/has_many_association.rb | 2 +- activerecord/lib/active_record/base.rb | 12 ++++++------ activerecord/lib/active_record/calculations.rb | 2 +- activerecord/lib/active_record/relation.rb | 2 +- activerecord/test/cases/relations_test.rb | 14 +++++++------- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 8dcb3a7711..0ed46046ff 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1717,9 +1717,9 @@ module ActiveRecord select(column_aliases(join_dependency)). group(construct_group(options[:group], options[:having], scope)). order(construct_order(options[:order], scope)). - conditions(construct_conditions(options[:conditions], scope)) + where(construct_conditions(options[:conditions], scope)) - relation = relation.conditions(construct_arel_limited_ids_condition(options, join_dependency)) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) + relation = relation.where(construct_arel_limited_ids_condition(options, join_dependency)) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) relation = relation.limit(construct_limit(options[:limit], scope)) if using_limitable_reflections?(join_dependency.reflections) relation @@ -1757,7 +1757,7 @@ module ActiveRecord end relation = relation.joins(construct_join(options[:joins], scope)). - conditions(construct_conditions(options[:conditions], scope)). + where(construct_conditions(options[:conditions], scope)). group(construct_group(options[:group], options[:having], scope)). order(construct_order(options[:order], scope)). limit(construct_limit(options[:limit], scope)). diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index b01faa5212..9569b0c6f9 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -71,7 +71,7 @@ module ActiveRecord records.each { |record| @owner.connection.delete(interpolate_sql(sql, record)) } else relation = arel_table(@reflection.options[:join_table]) - relation.conditions(relation[@reflection.primary_key_name].eq(@owner.id). + relation.where(relation[@reflection.primary_key_name].eq(@owner.id). and(Arel::Predicates::In.new(relation[@reflection.association_foreign_key], records.map(&:id))) ).delete end diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index cd31b0e211..be74ddfcf0 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -70,7 +70,7 @@ module ActiveRecord @reflection.klass.delete(records.map { |record| record.id }) else relation = arel_table(@reflection.table_name) - relation.conditions(relation[@reflection.primary_key_name].eq(@owner.id). + relation.where(relation[@reflection.primary_key_name].eq(@owner.id). and(Arel::Predicates::In.new(relation[@reflection.klass.primary_key], records.map(&:id))) ).update(relation[@reflection.primary_key_name] => nil) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 03324b7f24..3c41d16f63 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -651,7 +651,7 @@ module ActiveRecord #:nodoc: end end - delegate :select, :group, :order, :limit, :joins, :conditions, :preload, :eager_load, :to => :arel_table + delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :to => :arel_table # A convenience wrapper for find(:first, *args). You can pass in all the # same arguments to this method as you can to find(:first). @@ -885,7 +885,7 @@ module ActiveRecord #:nodoc: relation = arel_table if conditions = construct_conditions(conditions, scope) - relation = relation.conditions(Arel::SqlLiteral.new(conditions)) + relation = relation.where(Arel::SqlLiteral.new(conditions)) end relation = if options.has_key?(:limit) || (scope && scope[:limit]) @@ -948,7 +948,7 @@ module ActiveRecord #:nodoc: # associations or call your before_* or +after_destroy+ callbacks, use the +destroy_all+ method instead. def delete_all(conditions = nil) if conditions - arel_table.conditions(Arel::SqlLiteral.new(construct_conditions(conditions, scope(:find)))).delete + arel_table.where(Arel::SqlLiteral.new(construct_conditions(conditions, scope(:find)))).delete else arel_table.delete end @@ -1689,7 +1689,7 @@ module ActiveRecord #:nodoc: # TODO add lock to Arel relation = arel_table(options[:from]). joins(construct_join(options[:joins], scope)). - conditions(construct_conditions(options[:conditions], scope)). + where(construct_conditions(options[:conditions], scope)). select(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))). group(construct_group(options[:group], options[:having], scope)). order(construct_order(options[:order], scope)). @@ -2564,7 +2564,7 @@ module ActiveRecord #:nodoc: # be made (since they can't be persisted). def destroy unless new_record? - self.class.arel_table.conditions(self.class.arel_table[self.class.primary_key].eq(id)).delete + self.class.arel_table.where(self.class.arel_table[self.class.primary_key].eq(id)).delete end @destroyed = true @@ -2851,7 +2851,7 @@ module ActiveRecord #:nodoc: def update(attribute_names = @attributes.keys) attributes_with_values = arel_attributes_values(false, false, attribute_names) return 0 if attributes_with_values.empty? - self.class.arel_table.conditions(self.class.arel_table[self.class.primary_key].eq(id)).update(attributes_with_values) + self.class.arel_table.where(self.class.arel_table[self.class.primary_key].eq(id)).update(attributes_with_values) end # Creates a record with values matching those of the instance attributes diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 40242333e5..fcba23dc0d 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -148,7 +148,7 @@ module ActiveRecord else relation = arel_table(options[:from]). joins(construct_join(options[:joins], scope)). - conditions(construct_conditions(options[:conditions], scope)). + where(construct_conditions(options[:conditions], scope)). order(options[:order]). limit(options[:limit]). offset(options[:offset]) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 28f04e5d85..6cc2befdf3 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -100,7 +100,7 @@ module ActiveRecord end end - def conditions(conditions) + def where(conditions) if conditions.blank? self else diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 769a920a8b..06e040cd6d 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -19,8 +19,8 @@ class RelationTest < ActiveRecord::TestCase end def test_finding_with_conditions - assert_equal ["David"], Author.conditions(:name => 'David').map(&:name) - assert_equal ['Mary'], Author.conditions(["name = ?", 'Mary']).map(&:name) + assert_equal ["David"], Author.where(:name => 'David').map(&:name) + assert_equal ['Mary'], Author.where(["name = ?", 'Mary']).map(&:name) end def test_finding_with_order @@ -54,14 +54,14 @@ class RelationTest < ActiveRecord::TestCase end def test_finding_with_hash_conditions_on_joined_table - firms = DependentFirm.joins(:account).conditions({:name => 'RailsCore', :accounts => { :credit_limit => 55..60 }}).to_a + firms = DependentFirm.joins(:account).where({:name => 'RailsCore', :accounts => { :credit_limit => 55..60 }}).to_a assert_equal 1, firms.size assert_equal companies(:rails_core), firms.first end def test_find_all_with_join developers_on_project_one = Developer.joins('LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id'). - conditions('project_id=1').to_a + where('project_id=1').to_a assert_equal 3, developers_on_project_one.length developer_names = developers_on_project_one.map { |d| d.name } @@ -70,7 +70,7 @@ class RelationTest < ActiveRecord::TestCase end def test_find_on_hash_conditions - assert_equal Topic.find(:all, :conditions => {:approved => false}), Topic.conditions({ :approved => false }).to_a + assert_equal Topic.find(:all, :conditions => {:approved => false}), Topic.where({ :approved => false }).to_a end def test_joins_with_string_array @@ -97,7 +97,7 @@ class RelationTest < ActiveRecord::TestCase def test_eager_association_loading_of_stis_with_multiple_references authors = Author.eager_load(:posts => { :special_comments => { :post => [ :special_comments, :very_special_comment ] } }). - order('comments.body, very_special_comments_posts.body').conditions('posts.id = 4').to_a + order('comments.body, very_special_comments_posts.body').where('posts.id = 4').to_a assert_equal [authors(:david)], authors assert_no_queries do @@ -144,7 +144,7 @@ class RelationTest < ActiveRecord::TestCase assert_equal 2, post.comments.size assert post.comments.include?(comments(:greetings)) - post = Post.conditions("posts.title = 'Welcome to the weblog'").preload(:comments).first + post = Post.where("posts.title = 'Welcome to the weblog'").preload(:comments).first assert_equal 2, post.comments.size assert post.comments.include?(comments(:greetings)) -- cgit v1.2.3 From a73fa9356feb82b739dd734b2b305f1f08f24337 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 04:07:50 +0530 Subject: Stop supporting blank arguments to AR#relation query methods --- activerecord/lib/active_record/relation.rb | 46 ++++++++++++------------------ 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 6cc2befdf3..5c0b8c6f83 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -57,56 +57,48 @@ module ActiveRecord end def select(selects) - selects.blank? ? self : create_new_relation(@relation.project(selects)) + create_new_relation(@relation.project(selects)) end def group(groups) - groups.blank? ? self : create_new_relation(@relation.group(groups)) + create_new_relation(@relation.group(groups)) end def order(orders) - orders.blank? ? self : create_new_relation(@relation.order(orders)) + create_new_relation(@relation.order(orders)) end def limit(limits) - limits.blank? ? self : create_new_relation(@relation.take(limits)) + create_new_relation(@relation.take(limits)) end def offset(offsets) - offsets.blank? ? self : create_new_relation(@relation.skip(offsets)) + create_new_relation(@relation.skip(offsets)) end def on(join) - join.blank? ? self : create_new_relation(@relation.on(join)) + create_new_relation(@relation.on(join)) end def joins(join, join_type = nil) - if join.blank? - self - else - join = case join - when String - @relation.join(join) - when Hash, Array, Symbol - if @klass.send(:array_of_strings?, join) - @relation.join(join.join(' ')) - else - @relation.join(@klass.send(:build_association_joins, join)) - end + join = case join + when String + @relation.join(join) + when Hash, Array, Symbol + if @klass.send(:array_of_strings?, join) + @relation.join(join.join(' ')) else - @relation.join(join, join_type) - end - create_new_relation(join) + @relation.join(@klass.send(:build_association_joins, join)) + end + else + @relation.join(join, join_type) end + create_new_relation(join) end def where(conditions) - if conditions.blank? - self - else - conditions = @klass.send(:merge_conditions, conditions) if [String, Hash, Array].include?(conditions.class) - create_new_relation(@relation.where(conditions)) - end + conditions = @klass.send(:merge_conditions, conditions) if [String, Hash, Array].include?(conditions.class) + create_new_relation(@relation.where(conditions)) end def respond_to?(method) -- cgit v1.2.3 From feb8b20eb552eee678448233672733c6e7ca9571 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 12:39:44 +0530 Subject: Add Relation#all as an alias for to_a --- activerecord/lib/active_record/relation.rb | 2 ++ activerecord/test/cases/relations_test.rb | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 5c0b8c6f83..9aa45fb294 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -51,6 +51,8 @@ module ActiveRecord records end + alias all to_a + def first @relation = @relation.take(1) to_a.first diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 06e040cd6d..529de4a6cf 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -18,6 +18,12 @@ class RelationTest < ActiveRecord::TestCase assert_equal 4, topics.size end + def test_scoped_all + topics = Topic.scoped.all + assert_kind_of Array, topics + assert_no_queries { assert_equal 4, topics.size } + end + def test_finding_with_conditions assert_equal ["David"], Author.where(:name => 'David').map(&:name) assert_equal ['Mary'], Author.where(["name = ?", 'Mary']).map(&:name) -- cgit v1.2.3 From 5f5aa44d2de9e7d93087d52862502c12722bdae3 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 12:43:49 +0530 Subject: Add missing changelog entries --- activerecord/CHANGELOG | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index be4d197f99..d13a9e61df 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,30 @@ *Edge* +* Rename Model.conditions and relation.conditions to .where. [Pratik Naik] + + Before : + User.conditions(:name => 'lifo') + User.select('id').conditions(["age > ?", 21]) + + Now : + User.where(:name => 'lifo') + User.select('id').where(["age > ?", 21]) + +* Add Model.select/group/order/limit/joins/conditions/preload/eager_load class methods returning a lazy relation. [Pratik Naik] + + Examples : + + posts = Post.select('id).order('name') # Returns a lazy relation + posts.each {|p| puts p.id } # Fires "select id from posts order by name" + +* Model.scoped now returns a relation if invoked without any arguments. [Pratik Naik] + + Example : + + posts = Post.scoped + posts.size # Fires "select count(*) from posts" and returns the count + posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects + * Association inverses for belongs_to, has_one, and has_many. Optimization to reduce database queries. #3533 [Murray Steele] # post.comments sets each comment's post without needing to :include -- cgit v1.2.3 From 187fbe5cbadf9d6fe5009a4a436fac28bcf8c99c Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 12:57:34 +0530 Subject: Add support for multiple arguments to .where finder --- activerecord/lib/active_record/relation.rb | 9 +++++++-- activerecord/test/cases/relations_test.rb | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 9aa45fb294..40ccdd4696 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -98,8 +98,13 @@ module ActiveRecord create_new_relation(join) end - def where(conditions) - conditions = @klass.send(:merge_conditions, conditions) if [String, Hash, Array].include?(conditions.class) + def where(*args) + if [String, Hash, Array].include?(args.first.class) + conditions = @klass.send(:merge_conditions, args.size > 1 ? Array.wrap(args) : args.first) + else + conditions = args.first + end + create_new_relation(@relation.where(conditions)) end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 529de4a6cf..50bd221bb3 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -27,6 +27,7 @@ class RelationTest < ActiveRecord::TestCase def test_finding_with_conditions assert_equal ["David"], Author.where(:name => 'David').map(&:name) assert_equal ['Mary'], Author.where(["name = ?", 'Mary']).map(&:name) + assert_equal ['Mary'], Author.where("name = ?", 'Mary').map(&:name) end def test_finding_with_order -- cgit v1.2.3 From 284d186cf4e055cefb2f8482d016e3bd09e9c341 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 14:26:02 +0530 Subject: Make sure the relations are always immutable --- activerecord/lib/active_record/relation.rb | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 40ccdd4696..3dedf44190 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -12,18 +12,15 @@ module ActiveRecord end def preload(associations) - @associations_to_preload << associations - self + create_new_relation(@relation, @readonly, @associations_to_preload + Array.wrap(associations)) end def eager_load(associations) - @eager_load_associations += Array.wrap(associations) - self + create_new_relation(@relation, @readonly, @associations_to_preload, @eager_load_associations + Array.wrap(associations)) end def readonly - @readonly = true - self + create_new_relation(@relation, true) end def to_a @@ -124,8 +121,8 @@ module ActiveRecord end end - def create_new_relation(relation) - Relation.new(@klass, relation, @readonly, @associations_to_preload, @eager_load_associations) + def create_new_relation(relation, readonly = @readonly, preload = @associations_to_preload, eager_load = @eager_load_associations) + Relation.new(@klass, relation, readonly, preload, eager_load) end end -- cgit v1.2.3 From 9d3d60c64a9ce69b9932f5c543112199e576c2ad Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 14:40:45 +0530 Subject: Ensure preload and eager_load finder methods accept multiple arguments --- activerecord/lib/active_record/relation.rb | 4 ++-- activerecord/test/cases/relations_test.rb | 14 ++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 3dedf44190..c02acba786 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -11,11 +11,11 @@ module ActiveRecord @eager_load_associations = eager_load end - def preload(associations) + def preload(*associations) create_new_relation(@relation, @readonly, @associations_to_preload + Array.wrap(associations)) end - def eager_load(associations) + def eager_load(*associations) create_new_relation(@relation, @readonly, @associations_to_preload, @eager_load_associations + Array.wrap(associations)) end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 50bd221bb3..d0a28c58e0 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -116,22 +116,28 @@ class RelationTest < ActiveRecord::TestCase def test_find_with_included_associations assert_queries(2) do posts = Post.preload(:comments) - posts.first.comments.first + assert posts.first.comments.first end assert_queries(2) do posts = Post.preload(:comments).to_a - posts.first.comments.first + assert posts.first.comments.first end assert_queries(2) do posts = Post.preload(:author) - posts.first.author + assert posts.first.author end assert_queries(2) do posts = Post.preload(:author).to_a - posts.first.author + assert posts.first.author + end + + assert_queries(3) do + posts = Post.preload(:author, :comments).to_a + assert posts.first.author + assert posts.first.comments.first end end -- cgit v1.2.3 From 3c5a7dcaf55f427f3a48e206feb06410d011ca4f Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 15:07:00 +0530 Subject: Cache the loaded relations --- activerecord/lib/active_record/relation.rb | 75 +++++++++++++++++------------- activerecord/test/cases/relations_test.rb | 33 ++++++++++++- 2 files changed, 75 insertions(+), 33 deletions(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index c02acba786..a030ba29fa 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -9,6 +9,7 @@ module ActiveRecord @readonly = readonly @associations_to_preload = preload @eager_load_associations = eager_load + @loaded = false end def preload(*associations) @@ -23,38 +24,6 @@ module ActiveRecord create_new_relation(@relation, true) end - def to_a - records = if @eager_load_associations.any? - catch :invalid_query do - return @klass.send(:find_with_associations, { - :select => @relation.send(:select_clauses).join(', '), - :joins => @relation.joins(relation), - :group => @relation.send(:group_clauses).join(', '), - :order => @relation.send(:order_clauses).join(', '), - :conditions => @relation.send(:where_clauses).join("\n\tAND "), - :limit => @relation.taken, - :offset => @relation.skipped - }, - ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, @eager_load_associations, nil)) - end - [] - else - @klass.find_by_sql(@relation.to_sql) - end - - @associations_to_preload.each {|associations| @klass.send(:preload_associations, records, associations) } - records.each { |record| record.readonly! } if @readonly - - records - end - - alias all to_a - - def first - @relation = @relation.take(1) - to_a.first - end - def select(selects) create_new_relation(@relation.project(selects)) end @@ -109,6 +78,48 @@ module ActiveRecord @relation.respond_to?(method) || Array.method_defined?(method) || super end + def to_a + return @records if loaded? + + @records = if @eager_load_associations.any? + catch :invalid_query do + return @klass.send(:find_with_associations, { + :select => @relation.send(:select_clauses).join(', '), + :joins => @relation.joins(relation), + :group => @relation.send(:group_clauses).join(', '), + :order => @relation.send(:order_clauses).join(', '), + :conditions => @relation.send(:where_clauses).join("\n\tAND "), + :limit => @relation.taken, + :offset => @relation.skipped + }, + ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, @eager_load_associations, nil)) + end + [] + else + @klass.find_by_sql(@relation.to_sql) + end + + @associations_to_preload.each {|associations| @klass.send(:preload_associations, @records, associations) } + @records.each { |record| record.readonly! } if @readonly + + @loaded = true + @records + end + + alias all to_a + + def first + if loaded? + @records.first + else + @first ||= limit(1).to_a[0] + end + end + + def loaded? + @loaded + end + private def method_missing(method, *args, &block) diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index d0a28c58e0..475090fbd8 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -24,6 +24,37 @@ class RelationTest < ActiveRecord::TestCase assert_no_queries { assert_equal 4, topics.size } end + def test_loaded_all + topics = Topic.scoped + + assert_queries(1) do + 2.times { assert_equal 4, topics.all.size } + end + + assert topics.loaded? + end + + def test_scoped_first + topics = Topic.scoped + + assert_queries(1) do + 2.times { assert_equal "The First Topic", topics.first.title } + end + + assert ! topics.loaded? + end + + def test_loaded_first + topics = Topic.scoped + + assert_queries(1) do + topics.all # force load + 2.times { assert_equal "The First Topic", topics.first.title } + end + + assert topics.loaded? + end + def test_finding_with_conditions assert_equal ["David"], Author.where(:name => 'David').map(&:name) assert_equal ['Mary'], Author.where(["name = ?", 'Mary']).map(&:name) @@ -141,7 +172,7 @@ class RelationTest < ActiveRecord::TestCase end end - def test_default_scope_with_conditions_string + def test_default_scope_with_conditions_string assert_equal Developer.find_all_by_name('David').map(&:id).sort, DeveloperCalledDavid.scoped.to_a.map(&:id).sort assert_equal nil, DeveloperCalledDavid.create!.name end -- cgit v1.2.3 From 9a9f97af2815469e6f28dee9b88577251ef1b832 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 15:28:23 +0530 Subject: Add relation.reload to force reloading the records --- activerecord/CHANGELOG | 8 ++++++++ activerecord/lib/active_record/relation.rb | 6 ++++++ activerecord/test/cases/relations_test.rb | 15 +++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index d13a9e61df..e584979251 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,13 @@ *Edge* +* Add relation.reload to force reloading the records. [Pratik Naik] + + topics = Topic.scoped + topics.to_a # force load + topics.first # returns a cached record + topics.reload + topics.first # Fetches a new record from the database + * Rename Model.conditions and relation.conditions to .where. [Pratik Naik] Before : diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index a030ba29fa..853103a606 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -120,6 +120,12 @@ module ActiveRecord @loaded end + def reload + @loaded = false + @records = @first = nil + self + end + private def method_missing(method, *args, &block) diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 475090fbd8..2b3c6eec1d 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -55,6 +55,21 @@ class RelationTest < ActiveRecord::TestCase assert topics.loaded? end + def test_reload + topics = Topic.scoped + + assert_queries(1) do + 2.times { topics.to_a } + end + + assert topics.loaded? + + topics.reload + assert ! topics.loaded? + + assert_queries(1) { topics.to_a } + end + def test_finding_with_conditions assert_equal ["David"], Author.where(:name => 'David').map(&:name) assert_equal ['Mary'], Author.where(["name = ?", 'Mary']).map(&:name) -- cgit v1.2.3 From c6258ee313653bc54e94e0008f1c098ed68aa3f4 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 19:15:05 +0530 Subject: Ensure all the finder methods respect scoping --- activerecord/lib/active_record/base.rb | 2 +- activerecord/test/cases/relations_test.rb | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 3c41d16f63..b6d73265a8 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -651,7 +651,7 @@ module ActiveRecord #:nodoc: end end - delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :to => :arel_table + delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :to => :scoped # A convenience wrapper for find(:first, *args). You can pass in all the # same arguments to this method as you can to find(:first). diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 2b3c6eec1d..e05da58ae6 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -188,7 +188,7 @@ class RelationTest < ActiveRecord::TestCase end def test_default_scope_with_conditions_string - assert_equal Developer.find_all_by_name('David').map(&:id).sort, DeveloperCalledDavid.scoped.to_a.map(&:id).sort + assert_equal Developer.find_all_by_name('David').map(&:id).sort, DeveloperCalledDavid.scoped.map(&:id).sort assert_equal nil, DeveloperCalledDavid.create!.name end @@ -197,6 +197,11 @@ class RelationTest < ActiveRecord::TestCase assert_equal 'Jamis', DeveloperCalledJamis.create!.name end + def test_default_scoping_finder_methods + developers = DeveloperCalledDavid.order('id').map(&:id).sort + assert_equal Developer.find_all_by_name('David').map(&:id).sort, developers + end + def test_loading_with_one_association posts = Post.preload(:comments) post = posts.find { |p| p.id == 1 } -- cgit v1.2.3 From f3741506981d8d4aafb28066f5a3a0509a4da846 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 00:11:31 +0530 Subject: Ensure Model.scoped adds type conditions for STI models --- activerecord/lib/active_record/named_scope.rb | 1 + activerecord/lib/active_record/relation.rb | 25 ++++++++++++++----------- activerecord/test/cases/finder_test.rb | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 38d54fa8ec..96d361093f 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -28,6 +28,7 @@ module ActiveRecord else if !scoped?(:find) relation = arel_table + relation = relation.where(type_condition) if finder_needs_type_condition? else relation = construct_finder_arel include_associations = scope(:find, :include) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 853103a606..b6800b07b7 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -49,19 +49,22 @@ module ActiveRecord end def joins(join, join_type = nil) - join = case join - when String - @relation.join(join) - when Hash, Array, Symbol - if @klass.send(:array_of_strings?, join) - @relation.join(join.join(' ')) - else - @relation.join(@klass.send(:build_association_joins, join)) - end + return self if join.blank? + + join_relation = case join + when String + @relation.join(join) + when Hash, Array, Symbol + if @klass.send(:array_of_strings?, join) + @relation.join(join.join(' ')) else - @relation.join(join, join_type) + @relation.join(@klass.send(:build_association_joins, join)) + end + else + @relation.join(join, join_type) end - create_new_relation(join) + + create_new_relation(join_relation) end def where(*args) diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 3de07797d4..57ce0eee61 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -291,7 +291,7 @@ class FinderTest < ActiveRecord::TestCase end def test_find_with_hash_conditions_on_joined_table - firms = Firm.all :joins => :account, :conditions => {:accounts => { :credit_limit => 50 }} + firms = Firm.joins(:account).where(:accounts => { :credit_limit => 50 }) assert_equal 1, firms.size assert_equal companies(:first_firm), firms.first end -- cgit v1.2.3 From 83f24afd44ecd6fb6e38d5e8a6830dfca4bf5ffe Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 00:21:24 +0530 Subject: Add new finder methods to association collection. --- activerecord/CHANGELOG | 10 ++++++++++ activerecord/lib/active_record/associations.rb | 4 ++-- .../associations/association_collection.rb | 19 +++++++++++++++++-- .../lib/active_record/autosave_association.rb | 4 ++-- .../has_many_through_associations_test.rb | 8 ++++---- activerecord/test/cases/batches_test.rb | 2 +- activerecord/test/cases/named_scope_test.rb | 4 ++-- 7 files changed, 38 insertions(+), 13 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index e584979251..df221a1ad0 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,15 @@ *Edge* +* Add new finder methods to association collection. [Pratik Naik] + + class User < ActiveRecord::Base + has_many :items + end + + user = User.first + user.items.where(:items => {:colour => 'red'}) + user.items.select('items.id') + * Add relation.reload to force reloading the records. [Pratik Naik] topics = Topic.scoped diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 0ed46046ff..2735bc5141 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1382,9 +1382,9 @@ module ActiveRecord if reflection.through_reflection && reflection.source_reflection.belongs_to? through = reflection.through_reflection primary_key = reflection.source_reflection.primary_key_name - send(through.name).all(:select => "DISTINCT #{through.quoted_table_name}.#{primary_key}").map!(&:"#{primary_key}") + send(through.name).select("DISTINCT #{through.quoted_table_name}.#{primary_key}").map!(&:"#{primary_key}") else - send(reflection.name).all(:select => "#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map!(&:id) + send(reflection.name).select("#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map!(&:id) end end end diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 25e329c0c1..b85d76e8d3 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -20,7 +20,22 @@ module ActiveRecord super construct_sql end - + + delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :to => :scoped + + def select(select = nil, &block) + if block_given? + load_target + @target.select(&block) + else + scoped.select(select) + end + end + + def scoped + with_scope(construct_scope) { @reflection.klass.scoped } + end + def find(*args) options = args.extract_options! @@ -383,7 +398,7 @@ module ActiveRecord loaded if target target end - + def method_missing(method, *args) if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method)) if block_given? diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 8f37fcd515..c0d8904bc8 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -221,9 +221,9 @@ module ActiveRecord if new_record association elsif association.loaded? - autosave ? association : association.select { |record| record.new_record? } + autosave ? association : association.find_all { |record| record.new_record? } else - autosave ? association.target : association.target.select { |record| record.new_record? } + autosave ? association.target : association.target.find_all { |record| record.new_record? } end end diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index fe68d03de2..608d5a3608 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -140,23 +140,23 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase def test_replace_order_is_preserved posts(:welcome).people.clear posts(:welcome).people = [people(:david), people(:michael)] - assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.all(:order => 'id').map(&:person_id) + assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.order('id').map(&:person_id) # Test the inverse order in case the first success was a coincidence posts(:welcome).people.clear posts(:welcome).people = [people(:michael), people(:david)] - assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.all(:order => 'id').map(&:person_id) + assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.order('id').map(&:person_id) end def test_replace_by_id_order_is_preserved posts(:welcome).people.clear posts(:welcome).person_ids = [people(:david).id, people(:michael).id] - assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.all(:order => 'id').map(&:person_id) + assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.order('id').map(&:person_id) # Test the inverse order in case the first success was a coincidence posts(:welcome).people.clear posts(:welcome).person_ids = [people(:michael).id, people(:david).id] - assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.all(:order => 'id').map(&:person_id) + assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.order('id').map(&:person_id) end def test_associate_with_create diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb index 5009a90846..e417d8a803 100644 --- a/activerecord/test/cases/batches_test.rb +++ b/activerecord/test/cases/batches_test.rb @@ -5,7 +5,7 @@ class EachTest < ActiveRecord::TestCase fixtures :posts def setup - @posts = Post.all(:order => "id asc") + @posts = Post.order("id asc") @total = Post.count end diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index 13427daf53..5d9232bc52 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -345,8 +345,8 @@ class NamedScopeTest < ActiveRecord::TestCase def test_chaining_should_use_latest_conditions_when_searching # Normal hash conditions - assert_equal Topic.all(:conditions => {:approved => true}).to_a, Topic.rejected.approved.all.to_a - assert_equal Topic.all(:conditions => {:approved => false}).to_a, Topic.approved.rejected.all.to_a + assert_equal Topic.where(:approved => true).to_a, Topic.rejected.approved.all.to_a + assert_equal Topic.where(:approved => false).to_a, Topic.approved.rejected.all.to_a # Nested hash conditions with same keys assert_equal [posts(:sti_comments)], Post.with_special_comments.with_very_special_comments.all.to_a -- cgit v1.2.3 From f53c36350d46a27f4859d82b07e48528fa4f2c74 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 26 Dec 2009 11:40:07 -0600 Subject: Expect Rack 1.1 --- actionpack/test/dispatch/test_request_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/test/dispatch/test_request_test.rb b/actionpack/test/dispatch/test_request_test.rb index 5da02b2ea6..e42ade73d1 100644 --- a/actionpack/test/dispatch/test_request_test.rb +++ b/actionpack/test/dispatch/test_request_test.rb @@ -18,7 +18,7 @@ class TestRequestTest < ActiveSupport::TestCase assert_equal "0.0.0.0", env.delete("REMOTE_ADDR") assert_equal "Rails Testing", env.delete("HTTP_USER_AGENT") - assert_equal [1, 0], env.delete("rack.version") + assert_equal [1, 1], env.delete("rack.version") assert_equal "", env.delete("rack.input").string assert_kind_of StringIO, env.delete("rack.errors") assert_equal true, env.delete("rack.multithread") -- cgit v1.2.3 From 673fa7f06608151aa4a5e6e754894978b4000b63 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 26 Dec 2009 12:43:50 -0600 Subject: rack-mount 0.4 --- actionpack/actionpack.gemspec | 2 +- actionpack/lib/action_dispatch/routing/mapper.rb | 38 +++++++++++----------- .../lib/action_dispatch/routing/route_set.rb | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index ab9c7f9c35..dc91fba64d 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -18,7 +18,7 @@ Gem::Specification.new do |s| s.add_dependency('activemodel', '= 3.0.pre') s.add_dependency('rack', '~> 1.1.0') s.add_dependency('rack-test', '~> 0.5.0') - s.add_dependency('rack-mount', '~> 0.3.3') + s.add_dependency('rack-mount', '~> 0.4.0') s.add_dependency('erubis', '~> 2.6.5') s.require_path = 'lib' diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index b48fc6edc5..e655d6a708 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -19,9 +19,9 @@ module ActionDispatch @constraints.each { |constraint| if constraint.respond_to?(:matches?) && !constraint.matches?(req) - return [ 417, {}, [] ] + return [ 404, {'X-Cascade' => 'pass'}, [] ] elsif constraint.respond_to?(:call) && !constraint.call(req) - return [ 417, {}, [] ] + return [ 404, {'X-Cascade' => 'pass'}, [] ] end } @@ -34,11 +34,11 @@ module ActionDispatch @set, @scope = set, scope @path, @options = extract_path_and_options(args) end - + def to_route [ app, conditions, requirements, defaults, @options[:as] ] end - + private def extract_path_and_options(args) options = args.extract_options! @@ -49,18 +49,18 @@ module ActionDispatch else path = args.first end - + [ normalize_path(path), options ] end def normalize_path(path) path = nil if path == "" path = "#{@scope[:path]}#{path}" if @scope[:path] - path = Rack::Mount::Utils.normalize_path(path) if path + path = Rack::Mount::Utils.normalize_path(path) if path raise ArgumentError, "path is required" unless path - - path + + path end @@ -74,7 +74,7 @@ module ActionDispatch def conditions { :path_info => @path }.merge(constraints).merge(request_method_condition) end - + def requirements @requirements ||= returning(@options[:constraints] || {}) do |requirements| requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints] @@ -96,30 +96,30 @@ module ActionDispatch else default_controller ? { :controller => default_controller } : {} end - + if defaults[:controller].blank? && segment_keys.exclude?("controller") raise ArgumentError, "missing :controller" end - + if defaults[:action].blank? && segment_keys.exclude?("action") raise ArgumentError, "missing :action" end - + defaults end end - + def blocks if @options[:constraints].present? && !@options[:constraints].is_a?(Hash) block = @options[:constraints] else block = nil end - - ((@scope[:blocks] || []) + [ block ]).compact + + ((@scope[:blocks] || []) + [ block ]).compact end - + def constraints @constraints ||= requirements.reject { |k, v| segment_keys.include?(k.to_s) || k == :controller } end @@ -132,7 +132,7 @@ module ActionDispatch { } end end - + def segment_keys @segment_keys ||= Rack::Mount::RegexpWithNamedGroups.new( Rack::Mount::Strexp.compile(@path, requirements, SEPARATORS) @@ -142,7 +142,7 @@ module ActionDispatch def to @options[:to] end - + def default_controller @scope[:controller].to_s if @scope[:controller] end @@ -520,4 +520,4 @@ module ActionDispatch include Resources end end -end \ No newline at end of file +end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 498ad3268c..bd397432ce 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -21,7 +21,7 @@ module ActionDispatch prepare_params!(params) unless controller = controller(params) - return [417, {}, []] + return [404, {'X-Cascade' => 'pass'}, []] end controller.action(params[:action]).call(env) -- cgit v1.2.3 From feb7382047bf5e354edd12aa89895ec4591a5614 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 26 Dec 2009 13:28:06 -0600 Subject: AD::Cascade that supports X-Cascade --- actionpack/lib/action_dispatch.rb | 1 + .../lib/action_dispatch/middleware/cascade.rb | 29 ++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 actionpack/lib/action_dispatch/middleware/cascade.rb diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index fafcf7dc4e..0696cb017c 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -41,6 +41,7 @@ module ActionDispatch autoload_under 'middleware' do autoload :Callbacks + autoload :Cascade autoload :ParamsParser autoload :Rescue autoload :ShowExceptions diff --git a/actionpack/lib/action_dispatch/middleware/cascade.rb b/actionpack/lib/action_dispatch/middleware/cascade.rb new file mode 100644 index 0000000000..9f5c9891f0 --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/cascade.rb @@ -0,0 +1,29 @@ +module ActionDispatch + class Cascade + def self.new(*apps) + apps = apps.flatten + + case apps.length + when 0 + raise ArgumentError, "app is required" + when 1 + apps.first + else + super(apps) + end + end + + def initialize(apps) + @apps = apps + end + + def call(env) + result = nil + @apps.each do |app| + result = app.call(env) + break unless result[1]["X-Cascade"] == "pass" + end + result + end + end +end -- cgit v1.2.3 From cc753eaf589b6dfae92f6fc0031c5710e616a26c Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 01:50:03 +0530 Subject: Replace Model.first(options) with new finder methods inside tests --- activerecord/test/cases/dirty_test.rb | 2 +- activerecord/test/cases/finder_test.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index f456d273fe..4961d12a44 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -301,7 +301,7 @@ class DirtyTest < ActiveRecord::TestCase def test_save_should_not_save_serialized_attribute_with_partial_updates_if_not_present with_partial_updates(Topic) do Topic.create!(:author_name => 'Bill', :content => {:a => "a"}) - topic = Topic.first(:select => 'id, author_name') + topic = Topic.select('id, author_name').first topic.update_attribute :author_name, 'John' topic = Topic.first assert_not_nil topic.content diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 57ce0eee61..efc850cffc 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -233,11 +233,11 @@ class FinderTest < ActiveRecord::TestCase end def test_first - assert_equal topics(:second).title, Topic.first(:conditions => "title = 'The Second Topic of the day'").title + assert_equal topics(:second).title, Topic.where("title = 'The Second Topic of the day'").first.title end def test_first_failing - assert_nil Topic.first(:conditions => "title = 'The Second Topic of the day!'") + assert_nil Topic.where("title = 'The Second Topic of the day!'").first end def test_unexisting_record_exception_handling -- cgit v1.2.3 From f6f416c58e805604390314e2e8e5ecf6a0a78b4f Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 03:25:29 +0530 Subject: Add find_by_* and find_all_by_* finders to ActiveRecord::Relation --- activerecord/lib/active_record/relation.rb | 14 ++++++++++++ activerecord/test/cases/relations_test.rb | 34 +++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index b6800b07b7..f9ca6cbbd2 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -136,6 +136,20 @@ module ActiveRecord @relation.send(method, *args, &block) elsif Array.method_defined?(method) to_a.send(method, *args, &block) + elsif match = DynamicFinderMatch.match(method) + attributes = match.attribute_names + super unless @klass.send(:all_attributes_exists?, attributes) + + if match.finder? + conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h} + result = where(conditions).send(match.finder) + + if match.bang? && result.blank? + raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}" + else + result + end + end else super end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index e05da58ae6..f94402f57e 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -1,4 +1,6 @@ require "cases/helper" +require 'models/tag' +require 'models/tagging' require 'models/post' require 'models/topic' require 'models/comment' @@ -10,7 +12,8 @@ require 'models/developer' require 'models/company' class RelationTest < ActiveRecord::TestCase - fixtures :authors, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :posts, :comments + fixtures :authors, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :posts, :comments, + :taggings def test_scoped topics = Topic.scoped @@ -222,5 +225,34 @@ class RelationTest < ActiveRecord::TestCase post = posts.find { |p| p.id == 1 } assert_equal Post.find(1).last_comment, post.last_comment end + + def test_dynamic_find_by_attributes + david = authors(:david) + author = Author.preload(:taggings).find_by_id(david.id) + expected_taggings = taggings(:welcome_general, :thinking_general) + + assert_no_queries do + assert_equal expected_taggings, author.taggings.uniq.sort_by { |t| t.id } + end + + authors = Author.scoped + assert_equal david, authors.find_by_id_and_name(david.id, david.name) + assert_equal david, authors.find_by_id_and_name!(david.id, david.name) + end + + def test_dynamic_find_by_attributes_bang + author = Author.scoped.find_by_id!(authors(:david).id) + assert_equal "David", author.name + + assert_raises(ActiveRecord::RecordNotFound) { Author.scoped.find_by_id_and_name!('invalid', 'wt') } + end + + def test_dynamic_find_all_by_attributes + authors = Author.scoped + + davids = authors.find_all_by_name('David') + assert_kind_of Array, davids + assert_equal [authors(:david)], davids + end end -- cgit v1.2.3 From bdf8ee44c54dec181827c02c2b74ea329bdab931 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 26 Dec 2009 18:21:36 -0600 Subject: script/server should init Rails by loading config.ru Fixes "Rails 3.0 doesn't fucking work" --- railties/lib/rails/commands/server.rb | 20 ++++---------------- .../generators/rails/app/templates/script/server.tt | 6 ++++-- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb index 57b7c6a49c..09d7207d51 100644 --- a/railties/lib/rails/commands/server.rb +++ b/railties/lib/rails/commands/server.rb @@ -37,15 +37,6 @@ module Rails Options.new end - def self.start(app) - new(app).start - end - - def initialize(app) - super() # Call Rack::Server#initialize without passing any options to use. - @app = app - end - def start puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}" puts "=> Rails #{Rails.version} application starting on http://#{options[:Host]}:#{options[:Port]}" @@ -69,20 +60,17 @@ module Rails end def log_path - "#{File.expand_path(@app.root)}/log/#{options[:environment]}.log" + "log/#{options[:environment]}.log" end def default_options - { + super.merge({ :Port => 3000, - :Host => "0.0.0.0", :environment => (ENV['RAILS_ENV'] || "development").dup, - :rack_file => "#{@app.root}/config.ru", :daemonize => false, :debugger => false, - :pid => "#{@app.root}/tmp/pids/server.pid", - :AccessLog => [] - } + :pid => "tmp/pids/server.pid" + }) end end end diff --git a/railties/lib/rails/generators/rails/app/templates/script/server.tt b/railties/lib/rails/generators/rails/app/templates/script/server.tt index 380dc42cb5..4fd0cc7832 100755 --- a/railties/lib/rails/generators/rails/app/templates/script/server.tt +++ b/railties/lib/rails/generators/rails/app/templates/script/server.tt @@ -1,3 +1,5 @@ -require File.expand_path('../../config/application', __FILE__) +require File.expand_path('../../config/boot', __FILE__) require 'rails/commands/server' -Rails::Server.start(<%= app_const %>.instance) + +Dir.chdir(File.expand_path('../..', __FILE__)) +Rails::Server.start -- cgit v1.2.3 From 51d84eff126392e3a19a7e1bf293d1f1eee21623 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 26 Dec 2009 22:22:48 -0800 Subject: Require bundled environment, if present, so Active Support can load i18n --- railties/Rakefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/railties/Rakefile b/railties/Rakefile index cb482c90bf..07c2ff84a0 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -1,3 +1,8 @@ +begin + require File.expand_path('../../vendor/gems/environment', __FILE__) +rescue LoadError +end + require 'rake' require 'rake/testtask' require 'rake/rdoctask' -- cgit v1.2.3 From 8829d6ecc6b3e1a36a41decaf8237f6024be2c06 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 13:15:00 +0530 Subject: Make Model.find_by_* and Model.find_all_by_* use relations and remove dynamic method caching --- activerecord/lib/active_record/base.rb | 76 ++++++++------------------- activerecord/lib/active_record/named_scope.rb | 18 ++----- activerecord/lib/active_record/relation.rb | 22 ++++---- activerecord/test/cases/finder_test.rb | 46 ---------------- 4 files changed, 38 insertions(+), 124 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index b6d73265a8..31737043eb 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -671,20 +671,10 @@ module ActiveRecord #:nodoc: options = args.extract_options! if options.empty? && !scoped?(:find) - relation = arel_table + arel_table else - relation = construct_finder_arel(options) - include_associations = merge_includes(scope(:find, :include), options[:include]) - - if include_associations.any? - if references_eager_loaded_tables?(options) - relation.eager_load(include_associations) - else - relation.preload(include_associations) - end - end + construct_finder_arel_with_includes(options) end - relation end # Executes a custom SQL query against your database and returns all the results. The results will @@ -1687,6 +1677,8 @@ module ActiveRecord #:nodoc: def construct_finder_arel(options = {}, scope = scope(:find)) # TODO add lock to Arel + validate_find_options(options) + relation = arel_table(options[:from]). joins(construct_join(options[:joins], scope)). where(construct_conditions(options[:conditions], scope)). @@ -1701,6 +1693,21 @@ module ActiveRecord #:nodoc: relation end + def construct_finder_arel_with_includes(options = {}) + relation = construct_finder_arel(options) + include_associations = merge_includes(scope(:find, :include), options[:include]) + + if include_associations.any? + if references_eager_loaded_tables?(options) + relation = relation.eager_load(include_associations) + else + relation = relation.preload(include_associations) + end + end + + relation + end + def construct_finder_sql(options, scope = scope(:find)) construct_finder_arel(options, scope).to_sql end @@ -1853,48 +1860,9 @@ module ActiveRecord #:nodoc: attribute_names = match.attribute_names super unless all_attributes_exists?(attribute_names) if match.finder? - finder = match.finder - bang = match.bang? - # def self.find_by_login_and_activated(*args) - # options = args.extract_options! - # attributes = construct_attributes_from_arguments( - # [:login,:activated], - # args - # ) - # finder_options = { :conditions => attributes } - # validate_find_options(options) - # set_readonly_option!(options) - # - # if options[:conditions] - # with_scope(:find => finder_options) do - # find(:first, options) - # end - # else - # find(:first, options.merge(finder_options)) - # end - # end - self.class_eval %{ - def self.#{method_id}(*args) - options = args.extract_options! - attributes = construct_attributes_from_arguments( - [:#{attribute_names.join(',:')}], - args - ) - finder_options = { :conditions => attributes } - validate_find_options(options) - set_readonly_option!(options) - - #{'result = ' if bang}if options[:conditions] - with_scope(:find => finder_options) do - find(:#{finder}, options) - end - else - find(:#{finder}, options.merge(finder_options)) - end - #{'result || raise(RecordNotFound, "Couldn\'t find #{name} with #{attributes.to_a.collect { |pair| pair.join(\' = \') }.join(\', \')}")' if bang} - end - }, __FILE__, __LINE__ - send(method_id, *arguments) + options = arguments.extract_options! + relation = options.any? ? construct_finder_arel_with_includes(options) : scoped + relation.send :find_by_attributes, match, attribute_names, *arguments elsif match.instantiator? instantiator = match.instantiator # def self.find_or_create_by_user_id(*args) diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 96d361093f..a6336e762a 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -26,23 +26,11 @@ module ActiveRecord if options.present? Scope.new(self, options, &block) else - if !scoped?(:find) - relation = arel_table - relation = relation.where(type_condition) if finder_needs_type_condition? + unless scoped?(:find) + finder_needs_type_condition? ? arel_table.where(type_condition) : arel_table else - relation = construct_finder_arel - include_associations = scope(:find, :include) - - if include_associations.present? - if references_eager_loaded_tables?(options) - relation.eager_load(include_associations) - else - relation.preload(include_associations) - end - end + construct_finder_arel_with_includes end - - relation end end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index f9ca6cbbd2..bcae352cc6 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -129,7 +129,7 @@ module ActiveRecord self end - private + protected def method_missing(method, *args, &block) if @relation.respond_to?(method) @@ -141,20 +141,24 @@ module ActiveRecord super unless @klass.send(:all_attributes_exists?, attributes) if match.finder? - conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h} - result = where(conditions).send(match.finder) - - if match.bang? && result.blank? - raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}" - else - result - end + find_by_attributes(match, attributes, *args) end else super end end + def find_by_attributes(match, attributes, *args) + conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h} + result = where(conditions).send(match.finder) + + if match.bang? && result.blank? + raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}" + else + result + end + end + def create_new_relation(relation, readonly = @readonly, preload = @associations_to_preload, eager_load = @eager_load_associations) Relation.new(@klass, relation, readonly, preload, eager_load) end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index efc850cffc..59c016d19c 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -571,21 +571,6 @@ class FinderTest < ActiveRecord::TestCase assert_equal(2, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 1])) end - def test_dynamic_finders_should_go_through_the_find_class_method - Topic.expects(:find).with(:first, :conditions => { :title => 'The First Topic!' }) - Topic.find_by_title("The First Topic!") - - Topic.expects(:find).with(:last, :conditions => { :title => 'The Last Topic!' }) - Topic.find_last_by_title("The Last Topic!") - - Topic.expects(:find).with(:all, :conditions => { :title => 'A Topic.' }) - Topic.find_all_by_title("A Topic.") - - Topic.expects(:find).with(:first, :conditions => { :title => 'Does not exist yet for sure!' }).times(2) - Topic.find_or_initialize_by_title('Does not exist yet for sure!') - Topic.find_or_create_by_title('Does not exist yet for sure!') - end - def test_find_by_one_attribute assert_equal topics(:first), Topic.find_by_title("The First Topic") assert_nil Topic.find_by_title("The First Topic!") @@ -596,21 +581,6 @@ class FinderTest < ActiveRecord::TestCase assert_raise(ActiveRecord::RecordNotFound) { Topic.find_by_title!("The First Topic!") } end - def test_find_by_one_attribute_caches_dynamic_finder - # ensure this test can run independently of order - class << Topic; self; end.send(:remove_method, :find_by_title) if Topic.public_methods.any? { |m| m.to_s == 'find_by_title' } - assert !Topic.public_methods.any? { |m| m.to_s == 'find_by_title' } - t = Topic.find_by_title("The First Topic") - assert Topic.public_methods.any? { |m| m.to_s == 'find_by_title' } - end - - def test_dynamic_finder_returns_same_results_after_caching - # ensure this test can run independently of order - class << Topic; self; end.send(:remove_method, :find_by_title) if Topic.public_method_defined?(:find_by_title) - t = Topic.find_by_title("The First Topic") - assert_equal t, Topic.find_by_title("The First Topic") # find_by_title has been cached - end - def test_find_by_one_attribute_with_order_option assert_equal accounts(:signals37), Account.find_by_credit_limit(50, :order => 'id') assert_equal accounts(:rails_core_account), Account.find_by_credit_limit(50, :order => 'id DESC') @@ -654,14 +624,6 @@ class FinderTest < ActiveRecord::TestCase assert_equal customers(:david), found_customer end - def test_dynamic_finder_on_one_attribute_with_conditions_caches_method - # ensure this test can run independently of order - class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.public_methods.any? { |m| m.to_s == 'find_by_credit_limit' } - assert !Account.public_methods.any? { |m| m.to_s == 'find_by_credit_limit' } - a = Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6]) - assert Account.public_methods.any? { |m| m.to_s == 'find_by_credit_limit' } - end - def test_dynamic_finder_on_one_attribute_with_conditions_returns_same_results_after_caching # ensure this test can run independently of order class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.public_methods.any? { |m| m.to_s == 'find_by_credit_limit' } @@ -694,14 +656,6 @@ class FinderTest < ActiveRecord::TestCase assert_nil Topic.find_last_by_title("A title with no matches") end - def test_find_last_by_one_attribute_caches_dynamic_finder - # ensure this test can run independently of order - class << Topic; self; end.send(:remove_method, :find_last_by_title) if Topic.public_methods.any? { |m| m.to_s == 'find_last_by_title' } - assert !Topic.public_methods.any? { |m| m.to_s == 'find_last_by_title' } - t = Topic.find_last_by_title(Topic.last.title) - assert Topic.public_methods.any? { |m| m.to_s == 'find_last_by_title' } - end - def test_find_last_by_invalid_method_syntax assert_raise(NoMethodError) { Topic.fail_to_find_last_by_title("The First Topic") } assert_raise(NoMethodError) { Topic.find_last_by_title?("The First Topic") } -- cgit v1.2.3 From d511de0261003aae4913e3c24f76df6e03a35916 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 14:28:19 +0530 Subject: Add find_or_create_by_* and find_or_initialize_by_* to relations --- activerecord/lib/active_record/relation.rb | 23 +++++++++++++++++++++++ activerecord/test/cases/relations_test.rb | 21 +++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index bcae352cc6..f17b889c53 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -142,6 +142,8 @@ module ActiveRecord if match.finder? find_by_attributes(match, attributes, *args) + elsif match.instantiator? + find_or_instantiator_by_attributes(match, attributes, *args, &block) end else super @@ -159,6 +161,27 @@ module ActiveRecord end end + def find_or_instantiator_by_attributes(match, attributes, *args) + guard_protected_attributes = false + + attributes_for_create = conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h} + + if args[0].is_a?(Hash) + guard_protected_attributes = true + conditions = args[0].with_indifferent_access.slice(*attributes).symbolize_keys + end + + record = where(conditions).first + + unless record + record = @klass.new { |r| r.send(:attributes=, attributes_for_create, guard_protected_attributes) } + yield(record) if block_given? + record.save if match.instantiator == :create + end + + record + end + def create_new_relation(relation, readonly = @readonly, preload = @associations_to_preload, eager_load = @eager_load_associations) Relation.new(@klass, relation, readonly, preload, eager_load) end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index f94402f57e..893ac9caf8 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -254,5 +254,26 @@ class RelationTest < ActiveRecord::TestCase assert_kind_of Array, davids assert_equal [authors(:david)], davids end + + def test_dynamic_find_or_initialize_by_attributes + authors = Author.scoped + + lifo = authors.find_or_initialize_by_name('Lifo') + assert_equal "Lifo", lifo.name + assert lifo.new_record? + + assert_equal authors(:david), authors.find_or_initialize_by_name(:name => 'David') + end + + def test_dynamic_find_or_create_by_attributes + authors = Author.scoped + + lifo = authors.find_or_create_by_name('Lifo') + assert_equal "Lifo", lifo.name + assert ! lifo.new_record? + + assert_equal authors(:david), authors.find_or_create_by_name(:name => 'David') + end + end -- cgit v1.2.3 From 85770ec7139fcba985310d239d4c57cfe6f6c60b Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 14:46:38 +0530 Subject: Make Model.find_or_create_by_* and find_or_initialize_by_* use relations and remove method caching --- activerecord/lib/active_record/base.rb | 55 +----------------------------- activerecord/lib/active_record/relation.rb | 7 ++-- activerecord/test/cases/finder_test.rb | 7 ---- 3 files changed, 5 insertions(+), 64 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 31737043eb..cba1e0ebe6 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1864,60 +1864,7 @@ module ActiveRecord #:nodoc: relation = options.any? ? construct_finder_arel_with_includes(options) : scoped relation.send :find_by_attributes, match, attribute_names, *arguments elsif match.instantiator? - instantiator = match.instantiator - # def self.find_or_create_by_user_id(*args) - # guard_protected_attributes = false - # - # if args[0].is_a?(Hash) - # guard_protected_attributes = true - # attributes = args[0].with_indifferent_access - # find_attributes = attributes.slice(*[:user_id]) - # else - # find_attributes = attributes = construct_attributes_from_arguments([:user_id], args) - # end - # - # options = { :conditions => find_attributes } - # set_readonly_option!(options) - # - # record = find(:first, options) - # - # if record.nil? - # record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) } - # yield(record) if block_given? - # record.save - # record - # else - # record - # end - # end - self.class_eval %{ - def self.#{method_id}(*args) - guard_protected_attributes = false - - if args[0].is_a?(Hash) - guard_protected_attributes = true - attributes = args[0].with_indifferent_access - find_attributes = attributes.slice(*[:#{attribute_names.join(',:')}]) - else - find_attributes = attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args) - end - - options = { :conditions => find_attributes } - set_readonly_option!(options) - - record = find(:first, options) - - if record.nil? - record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) } - #{'yield(record) if block_given?'} - #{'record.save' if instantiator == :create} - record - else - record - end - end - }, __FILE__, __LINE__ - send(method_id, *arguments, &block) + scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block end elsif match = DynamicScopeMatch.match(method_id) attribute_names = match.attribute_names diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index f17b889c53..89b9b9b4ff 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -164,11 +164,12 @@ module ActiveRecord def find_or_instantiator_by_attributes(match, attributes, *args) guard_protected_attributes = false - attributes_for_create = conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h} - if args[0].is_a?(Hash) guard_protected_attributes = true - conditions = args[0].with_indifferent_access.slice(*attributes).symbolize_keys + attributes_for_create = args[0].with_indifferent_access + conditions = attributes_for_create.slice(*attributes).symbolize_keys + else + attributes_for_create = conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h} end record = where(conditions).first diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 59c016d19c..c531a2dec1 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -880,13 +880,6 @@ class FinderTest < ActiveRecord::TestCase assert !c.new_record? end - def test_dynamic_find_or_initialize_from_one_attribute_caches_method - class << Company; self; end.send(:remove_method, :find_or_initialize_by_name) if Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' } - assert !Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' } - sig38 = Company.find_or_initialize_by_name("38signals") - assert Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' } - end - def test_find_or_initialize_from_two_attributes another = Topic.find_or_initialize_by_title_and_author_name("Another topic","John") assert_equal "Another topic", another.title -- cgit v1.2.3 From 1efc8edb5f8799c48ba1992e07a8566bb12b7224 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 14:50:33 +0530 Subject: Fix dynamic finder docs --- activerecord/lib/active_record/base.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index cba1e0ebe6..e619e41329 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1842,9 +1842,8 @@ module ActiveRecord #:nodoc: end # Enables dynamic finders like find_by_user_name(user_name) and find_by_user_name_and_password(user_name, password) - # that are turned into find(:first, :conditions => ["user_name = ?", user_name]) and - # find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password]) respectively. Also works for - # find(:all) by using find_all_by_amount(50) that is turned into find(:all, :conditions => ["amount = ?", 50]). + # that are turned into where(:user_name => user_name).first and where(:user_name => user_name, :password => :password).first + # respectively. Also works for all by using find_all_by_amount(50) that is turned into where(:amount => 50).all. # # It's even possible to use all the additional parameters to +find+. For example, the full interface for +find_all_by_amount+ # is actually find_all_by_amount(amount, options). -- cgit v1.2.3 From 81608cf8fa61973d65f24efbc4ae4931016888e7 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 15:06:45 +0530 Subject: Make Model.all return an array rather than a relation for consistency. Use Model.scoped to get a relation --- activerecord/lib/active_record/base.rb | 4 ++-- activerecord/test/cases/base_test.rb | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index e619e41329..2008dea5e9 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -671,9 +671,9 @@ module ActiveRecord #:nodoc: options = args.extract_options! if options.empty? && !scoped?(:find) - arel_table + arel_table.to_a else - construct_finder_arel_with_includes(options) + construct_finder_arel_with_includes(options).to_a end end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 4c16cb4804..b51c9f0cb3 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1902,8 +1902,14 @@ class BasicsTest < ActiveRecord::TestCase assert_equal Developer.find(:first, :order => 'id desc'), Developer.last end + def test_all + developers = Developer.all + assert_kind_of Array, developers + assert_equal Developer.find(:all), developers + end + def test_all_with_conditions - assert_equal Developer.find(:all, :order => 'id desc'), Developer.all.order('id desc').to_a + assert_equal Developer.find(:all, :order => 'id desc'), Developer.order('id desc').all end def test_find_ordered_last -- cgit v1.2.3 From d92c4a84023bc0c8dd75869c9b4d5e50277f4650 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 16:14:04 +0530 Subject: Add find(ids) to relations --- activerecord/CHANGELOG | 6 +++ activerecord/lib/active_record/relation.rb | 63 +++++++++++++++++++++++++++++- activerecord/test/cases/relations_test.rb | 24 +++++++++++- 3 files changed, 91 insertions(+), 2 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index df221a1ad0..a2c5528860 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,11 @@ *Edge* +* Add find(ids) to relations. [Pratik Naik] + + old_users = User.order("age DESC") + old_users.find(1) + old_users.find(1, 2, 3) + * Add new finder methods to association collection. [Pratik Naik] class User < ActiveRecord::Base diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 89b9b9b4ff..c927270eee 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -91,7 +91,7 @@ module ActiveRecord :joins => @relation.joins(relation), :group => @relation.send(:group_clauses).join(', '), :order => @relation.send(:order_clauses).join(', '), - :conditions => @relation.send(:where_clauses).join("\n\tAND "), + :conditions => where_clause, :limit => @relation.taken, :offset => @relation.skipped }, @@ -111,6 +111,25 @@ module ActiveRecord alias all to_a + def find(*ids, &block) + return to_a.find(&block) if block_given? + + expects_array = ids.first.kind_of?(Array) + return ids.first if expects_array && ids.first.empty? + + ids = ids.flatten.compact.uniq + + case ids.size + when 0 + raise RecordNotFound, "Couldn't find #{@klass.name} without an ID" + when 1 + result = find_one(ids.first) + expects_array ? [ result ] : result + else + find_some(ids) + end + end + def first if loaded? @records.first @@ -183,9 +202,51 @@ module ActiveRecord record end + def find_one(id) + record = where(@klass.primary_key => id).first + + unless record + conditions = where_clause(', ') + conditions = " [WHERE #{conditions}]" if conditions.present? + raise RecordNotFound, "Couldn't find #{@klass.name} with ID=#{id}#{conditions}" + end + + record + end + + def find_some(ids) + result = where(@klass.primary_key => ids).all + + expected_size = + if @relation.taken && ids.size > @relation.taken + @relation.taken + else + ids.size + end + + # 11 ids with limit 3, offset 9 should give 2 results. + if @relation.skipped && (ids.size - @relation.skipped < expected_size) + expected_size = ids.size - @relation.skipped + end + + if result.size == expected_size + result + else + conditions = where_clause(', ') + conditions = " [WHERE #{conditions}]" if conditions.present? + + error = "Couldn't find all #{@klass.name.pluralize} with IDs " + error << "(#{ids.join(", ")})#{conditions} (found #{result.size} results, but was looking for #{expected_size})" + raise RecordNotFound, error + end + end + def create_new_relation(relation, readonly = @readonly, preload = @associations_to_preload, eager_load = @eager_load_associations) Relation.new(@klass, relation, readonly, preload, eager_load) end + def where_clause(join_string = "\n\tAND ") + @relation.send(:where_clauses).join(join_string) + end end end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 893ac9caf8..9c5a38a399 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -275,5 +275,27 @@ class RelationTest < ActiveRecord::TestCase assert_equal authors(:david), authors.find_or_create_by_name(:name => 'David') end -end + def test_find_id + authors = Author.scoped + + david = authors.find(authors(:david).id) + assert_equal 'David', david.name + + assert_raises(ActiveRecord::RecordNotFound) { authors.where(:name => 'lifo').find('invalid') } + end + def test_find_ids + authors = Author.order('id ASC') + + results = authors.find(authors(:david).id, authors(:mary).id) + assert_kind_of Array, results + assert_equal 2, results.size + assert_equal 'David', results[0].name + assert_equal 'Mary', results[1].name + assert_equal results, authors.find([authors(:david).id, authors(:mary).id]) + + assert_raises(ActiveRecord::RecordNotFound) { authors.where(:name => 'lifo').find(authors(:david).id, 'invalid') } + assert_raises(ActiveRecord::RecordNotFound) { authors.find(['invalid', 'oops']) } + end + +end -- cgit v1.2.3