diff options
49 files changed, 171 insertions, 1070 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 97215b90d6..6c9f3e0e67 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,7 +1,5 @@ ## Rails 4.0.0 (unreleased) ## -* Support API apps http://edgeguides.rubyonrails.org/api_app.html *Santiago Pastorino and Carlos Antonio da Silva* - * Add `include_hidden` option to select tag. With `:include_hidden => false` select with `multiple` attribute doesn't generate hidden input with blank value. *Vasiliy Ermolovich* * Removed default `size` option from the `text_field`, `search_field`, `telephone_field`, `url_field`, `email_field` helpers. *Philip Arndt* @@ -91,6 +89,9 @@ * check_box with `:form` html5 attribute will now replicate the `:form` attribute to the hidden field as well. *Carlos Antonio da Silva* +* Turn off verbose mode of rack-cache, we still have X-Rack-Cache to + check that info. Closes #5245. *Santiago Pastorino* + * `label` form helper accepts :for => nil to not generate the attribute. *Carlos Antonio da Silva* * Add `:format` option to number_to_percentage *Rodrigo Flores* @@ -125,6 +126,8 @@ ## Rails 3.2.3 (unreleased) ## +* Do not include the authenticity token in forms where remote: true as ajax forms use the meta-tag value *DHH* + * Upgrade rack-cache to 1.2. *José Valim* * ActionController::SessionManagement is removed. *Santiago Pastorino* diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index b8624fd1ba..7c10fcbb8a 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -6,7 +6,6 @@ module ActionController autoload :Base autoload :Caching - autoload :HTTP autoload :Metal autoload :Middleware diff --git a/actionpack/lib/action_controller/caching/sweeping.rb b/actionpack/lib/action_controller/caching/sweeping.rb index 49cf70ec21..808a6fe5f3 100644 --- a/actionpack/lib/action_controller/caching/sweeping.rb +++ b/actionpack/lib/action_controller/caching/sweeping.rb @@ -88,7 +88,7 @@ module ActionController #:nodoc: end def method_missing(method, *arguments, &block) - return unless @controller + super unless @controller @controller.__send__(method, *arguments, &block) end end diff --git a/actionpack/lib/action_controller/http.rb b/actionpack/lib/action_controller/http.rb deleted file mode 100644 index 252a652cd9..0000000000 --- a/actionpack/lib/action_controller/http.rb +++ /dev/null @@ -1,134 +0,0 @@ -require "action_controller/log_subscriber" - -module ActionController - # HTTP Controller is a lightweight version of <tt>ActionController::Base</tt>, - # created for applications that don't require all functionality that a complete - # \Rails controller provides, allowing you to create faster controllers. The - # main scenario where HTTP Controllers could be used is API only applications. - # - # An HTTP Controller is different from a normal controller in the sense that - # by default it doesn't include a number of features that are usually required - # by browser access only: layouts and templates rendering, cookies, sessions, - # flash, assets, and so on. This makes the entire controller stack thinner and - # faster, suitable for API applications. It doesn't mean you won't have such - # features if you need them: they're all available for you to include in - # your application, they're just not part of the default HTTP Controller stack. - # - # By default, only the ApplicationController in a \Rails application inherits - # from <tt>ActionController::HTTP</tt>. All other controllers in turn inherit - # from ApplicationController. - # - # A sample controller could look like this: - # - # class PostsController < ApplicationController - # def index - # @posts = Post.all - # render json: @posts - # end - # end - # - # Request, response and parameters objects all work the exact same way as - # <tt>ActionController::Base</tt>. - # - # == Renders - # - # The default HTTP Controller stack includes all renderers, which means you - # can use <tt>render :json</tt> and brothers freely in your controllers. Keep - # in mind that templates are not going to be rendered, so you need to ensure - # your controller is calling either <tt>render</tt> or <tt>redirect</tt> in - # all actions. - # - # def show - # @post = Post.find(params[:id]) - # render json: @post - # end - # - # == Redirects - # - # Redirects are used to move from one action to another. You can use the - # <tt>redirect</tt> method in your controllers in the same way as - # <tt>ActionController::Base</tt>. For example: - # - # def create - # redirect_to root_url and return if not_authorized? - # # do stuff here - # end - # - # == Adding new behavior - # - # In some scenarios you may want to add back some functionality provided by - # <tt>ActionController::Base</tt> that is not present by default in - # <tt>ActionController::HTTP</tt>, for instance <tt>MimeResponds</tt>. This - # module gives you the <tt>respond_to</tt> and <tt>respond_with</tt> methods. - # Adding it is quite simple, you just need to include the module in a specific - # controller or in <tt>ApplicationController</tt> in case you want it - # available to your entire app: - # - # class ApplicationController < ActionController::HTTP - # include ActionController::MimeResponds - # end - # - # class PostsController < ApplicationController - # respond_to :json, :xml - # - # def index - # @posts = Post.all - # respond_with @posts - # end - # end - # - # Quite straightforward. Make sure to check <tt>ActionController::Base</tt> - # available modules if you want to include any other functionality that is - # not provided by <tt>ActionController::HTTP</tt> out of the box. - class HTTP < Metal - abstract! - - # Shortcut helper that returns all the ActionController::HTTP modules except the ones passed in the argument: - # - # class MetalController - # ActionController::HTTP.without_modules(:ParamsWrapper, :Streaming).each do |left| - # include left - # end - # end - # - # This gives better control over what you want to exclude and makes it easier - # to create a bare controller class, instead of listing the modules required manually. - def self.without_modules(*modules) - modules = modules.map do |m| - m.is_a?(Symbol) ? ActionController.const_get(m) : m - end - - MODULES - modules - end - - MODULES = [ - HideActions, - UrlFor, - Redirecting, - Rendering, - Renderers::All, - ConditionalGet, - RackDelegation, - - ForceSSL, - DataStreaming, - - # Before callbacks should also be executed the earliest as possible, so - # also include them at the bottom. - AbstractController::Callbacks, - - # Append rescue at the bottom to wrap as much as possible. - Rescue, - - # Add instrumentations hooks at the bottom, to ensure they instrument - # all the methods properly. - Instrumentation - ] - - MODULES.each do |mod| - include mod - end - - ActiveSupport.run_load_hooks(:action_controller, self) - end -end diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index d070eaae5d..1a4bca12d2 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -52,6 +52,7 @@ module ActionController module Helpers extend ActiveSupport::Concern + class << self; attr_accessor :helpers_path; end include AbstractController::Helpers included do diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb index 5e837ca6e1..851a2c4aee 100644 --- a/actionpack/lib/action_controller/railtie.rb +++ b/actionpack/lib/action_controller/railtie.rb @@ -3,33 +3,32 @@ require "action_controller" require "action_dispatch/railtie" require "action_view/railtie" require "abstract_controller/railties/routes_helpers" -require "action_controller/railties/paths" +require "action_controller/railties/helpers" module ActionController class Railtie < Rails::Railtie #:nodoc: config.action_controller = ActiveSupport::OrderedOptions.new - initializer "action_controller.logger" do - ActiveSupport.on_load(:action_controller) { self.logger ||= Rails.logger } - end - - initializer "action_controller.initialize_framework_caches" do - ActiveSupport.on_load(:action_controller) { self.cache_store ||= Rails.cache if respond_to?(:cache_store) } - end - initializer "action_controller.assets_config", :group => :all do |app| app.config.action_controller.assets_dir ||= app.config.paths["public"].first end + initializer "action_controller.set_helpers_path" do |app| + ActionController::Helpers.helpers_path = app.helpers_paths + end + initializer "action_controller.set_configs" do |app| paths = app.config.paths options = app.config.action_controller + options.logger ||= Rails.logger + options.cache_store ||= Rails.cache + options.javascripts_dir ||= paths["public/javascripts"].first options.stylesheets_dir ||= paths["public/stylesheets"].first options.page_cache_directory ||= paths["public"].first - # make sure readers methods get compiled + # Ensure readers methods get compiled options.asset_path ||= app.config.asset_path options.asset_host ||= app.config.asset_host options.relative_url_root ||= app.config.relative_url_root @@ -37,7 +36,8 @@ module ActionController ActiveSupport.on_load(:action_controller) do include app.routes.mounted_helpers extend ::AbstractController::Railties::RoutesHelpers.with(app.routes) - extend ::ActionController::Railties::Paths.with(app) if respond_to?(:helpers_path) + extend ::ActionController::Railties::Helpers + options.each do |k,v| k = "#{k}=" if respond_to?(k) diff --git a/actionpack/lib/action_controller/railties/helpers.rb b/actionpack/lib/action_controller/railties/helpers.rb new file mode 100644 index 0000000000..3985c6b273 --- /dev/null +++ b/actionpack/lib/action_controller/railties/helpers.rb @@ -0,0 +1,22 @@ +module ActionController + module Railties + module Helpers + def inherited(klass) + super + return unless klass.respond_to?(:helpers_path=) + + if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_helpers_paths) } + paths = namespace.railtie_helpers_paths + else + paths = ActionController::Helpers.helpers_path + end + + klass.helpers_path = paths + + if klass.superclass == ActionController::Base && ActionController::Base.include_all_helpers + klass.helper :all + end + end + end + end +end diff --git a/actionpack/lib/action_controller/railties/paths.rb b/actionpack/lib/action_controller/railties/paths.rb deleted file mode 100644 index 7e79b036ed..0000000000 --- a/actionpack/lib/action_controller/railties/paths.rb +++ /dev/null @@ -1,24 +0,0 @@ -module ActionController - module Railties - module Paths - def self.with(app) - Module.new do - define_method(:inherited) do |klass| - super(klass) - - if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_helpers_paths) } - paths = namespace.railtie_helpers_paths - else - paths = app.helpers_paths - end - klass.helpers_path = paths - - if klass.superclass == ActionController::Base && ActionController::Base.include_all_helpers - klass.helper :all - end - end - end - end - end - end -end diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 7af30ed690..7ba8319e4c 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -505,11 +505,6 @@ module ActionController end end - # Cause the action to be rescued according to the regular rules for rescue_action when the visitor is not local - def rescue_action_in_public! - @request.remote_addr = '208.77.188.166' # example.com - end - included do include ActionController::TemplateAssertions include ActionDispatch::Assertions diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb index 63b7422287..9073e6582d 100644 --- a/actionpack/lib/action_dispatch/middleware/static.rb +++ b/actionpack/lib/action_dispatch/middleware/static.rb @@ -39,6 +39,7 @@ module ActionDispatch end def escape_glob_chars(path) + path.force_encoding('binary') if path.respond_to? :force_encoding path.gsub(/[*?{}\[\]]/, "\\\\\\&") end end diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 9fad30a48f..41d895c15e 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -27,7 +27,9 @@ module ActionView # is added to simulate the verb over post. # * <tt>:authenticity_token</tt> - Authenticity token to use in the form. Use only if you need to # pass custom authenticity token string, or to not add authenticity_token field at all - # (by passing <tt>false</tt>). + # (by passing <tt>false</tt>). If this is a remote form, the authenticity_token will by default + # not be included as the ajax handler will get it from the meta-tag (but you can force it to be + # rendered anyway in that case by passing <tt>true</tt>). # * A list of parameters to feed to the URL the form will be posted to. # * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the # submit behavior. By default this behavior is an ajax submit. @@ -616,8 +618,17 @@ module ActionView # responsibility of the caller to escape all the values. html_options["action"] = url_for(url_for_options) html_options["accept-charset"] = "UTF-8" + html_options["data-remote"] = true if html_options.delete("remote") - html_options["authenticity_token"] = html_options.delete("authenticity_token") if html_options.has_key?("authenticity_token") + + if html_options["data-remote"] && html_options["authenticity_token"] == true + # Include the default authenticity_token, which is only generated when its set to nil, + # but we needed the true value to override the default of no authenticity_token on data-remote. + html_options["authenticity_token"] = nil + elsif html_options["data-remote"] + # The authenticity token is taken from the meta tag in this case + html_options["authenticity_token"] = false + end end end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index a05a816b71..b1a5356ddd 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -293,10 +293,6 @@ module ActionController end end - class HTTP - include SharedTestRoutes.url_helpers - end - class TestCase include ActionDispatch::TestProcess diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb index 03d9873fc7..2d4083252e 100644 --- a/actionpack/test/controller/base_test.rb +++ b/actionpack/test/controller/base_test.rb @@ -130,8 +130,6 @@ class PerformActionTest < ActionController::TestCase @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new @request.host = "www.nextangle.com" - - rescue_action_in_public! end def test_process_should_be_precise @@ -155,7 +153,6 @@ class UrlOptionsTest < ActionController::TestCase def setup super @request.host = 'www.example.com' - rescue_action_in_public! end def test_url_for_query_params_included @@ -206,7 +203,6 @@ class DefaultUrlOptionsTest < ActionController::TestCase def setup super @request.host = 'www.example.com' - rescue_action_in_public! end def test_default_url_options_override @@ -257,7 +253,6 @@ class EmptyUrlOptionsTest < ActionController::TestCase def setup super @request.host = 'www.example.com' - rescue_action_in_public! end def test_ensure_url_for_works_as_expected_when_called_with_no_options_if_default_url_options_is_not_set diff --git a/actionpack/test/controller/http/action_methods_test.rb b/actionpack/test/controller/http/action_methods_test.rb deleted file mode 100644 index 20bb53aca2..0000000000 --- a/actionpack/test/controller/http/action_methods_test.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'abstract_unit' - -class ActionMethodsHTTPController < ActionController::HTTP - def one; end - def two; end - hide_action :two -end - -class ActionMethodsHTTPTest < ActiveSupport::TestCase - def setup - @controller = ActionMethodsHTTPController.new - end - - def test_action_methods - assert_equal Set.new(%w(one)), - @controller.class.action_methods, - "#{@controller.controller_path} should not be empty!" - end -end diff --git a/actionpack/test/controller/http/conditional_get_test.rb b/actionpack/test/controller/http/conditional_get_test.rb deleted file mode 100644 index 70d5ef296f..0000000000 --- a/actionpack/test/controller/http/conditional_get_test.rb +++ /dev/null @@ -1,55 +0,0 @@ -require 'abstract_unit' - -class ConditionalGetHTTPController < ActionController::HTTP - before_filter :handle_last_modified_and_etags, :only => :two - - def one - if stale?(:last_modified => Time.now.utc.beginning_of_day, :etag => [:foo, 123]) - render :text => "Hi!" - end - end - - def two - render :text => "Hi!" - end - - private - - def handle_last_modified_and_etags - fresh_when(:last_modified => Time.now.utc.beginning_of_day, :etag => [ :foo, 123 ]) - end -end - -class ConditionalGetHTTPTest < ActionController::TestCase - tests ConditionalGetHTTPController - - def setup - @last_modified = Time.now.utc.beginning_of_day.httpdate - end - - def test_request_with_bang_gets_last_modified - get :two - assert_equal @last_modified, @response.headers['Last-Modified'] - assert_response :success - end - - def test_request_with_bang_obeys_last_modified - @request.if_modified_since = @last_modified - get :two - assert_response :not_modified - end - - def test_last_modified_works_with_less_than_too - @request.if_modified_since = 5.years.ago.httpdate - get :two - assert_response :success - end - - def test_request_not_modified - @request.if_modified_since = @last_modified - get :one - assert_equal 304, @response.status.to_i - assert_blank @response.body - assert_equal @last_modified, @response.headers['Last-Modified'] - end -end diff --git a/actionpack/test/controller/http/data_streaming_test.rb b/actionpack/test/controller/http/data_streaming_test.rb deleted file mode 100644 index 67457b25b0..0000000000 --- a/actionpack/test/controller/http/data_streaming_test.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'abstract_unit' - -module TestHTTPFileUtils - def file_name() File.basename(__FILE__) end - def file_path() File.expand_path(__FILE__) end - def file_data() @data ||= File.open(file_path, 'rb') { |f| f.read } end -end - -class DataStreamingHTTPController < ActionController::HTTP - include TestHTTPFileUtils - - def one; end - def two - send_data(file_data, {}) - end -end - -class DataStreamingHTTPTest < ActionController::TestCase - include TestHTTPFileUtils - tests DataStreamingHTTPController - - def test_data - response = process('two') - assert_kind_of String, response.body - assert_equal file_data, response.body - end -end diff --git a/actionpack/test/controller/http/force_ssl_test.rb b/actionpack/test/controller/http/force_ssl_test.rb deleted file mode 100644 index 479ede6b78..0000000000 --- a/actionpack/test/controller/http/force_ssl_test.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'abstract_unit' - -class ForceSSLHTTPController < ActionController::HTTP - force_ssl - - def one; end - def two - head :ok - end -end - -class ForceSSLHTTPTest < ActionController::TestCase - tests ForceSSLHTTPController - - def test_banana_redirects_to_https - get :two - assert_response 301 - assert_equal "https://test.host/force_sslhttp/two", redirect_to_url - end -end diff --git a/actionpack/test/controller/http/redirect_to_test.rb b/actionpack/test/controller/http/redirect_to_test.rb deleted file mode 100644 index c410910bae..0000000000 --- a/actionpack/test/controller/http/redirect_to_test.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'abstract_unit' - -class RedirectToHTTPController < ActionController::HTTP - def one - redirect_to :action => "two" - end - - def two; end -end - -class RedirectToHTTPTest < ActionController::TestCase - tests RedirectToHTTPController - - def test_redirect_to - get :one - assert_response :redirect - assert_equal "http://test.host/redirect_to_http/two", redirect_to_url - end -end diff --git a/actionpack/test/controller/http/renderers_test.rb b/actionpack/test/controller/http/renderers_test.rb deleted file mode 100644 index a28f226a94..0000000000 --- a/actionpack/test/controller/http/renderers_test.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'abstract_unit' - -class Model - def to_json(options = {}) - { :a => 'b' }.to_json(options) - end - - def to_xml(options = {}) - { :a => 'b' }.to_xml(options) - end -end - -class RenderersHTTPController < ActionController::HTTP - def one - render :json => Model.new - end - - def two - render :xml => Model.new - end -end - -class RenderersHTTPTest < ActionController::TestCase - tests RenderersHTTPController - - def test_render_json - get :one - assert_response :success - assert_equal({ :a => 'b' }.to_json, @response.body) - end - - def test_render_xml - get :two - assert_response :success - assert_equal({ :a => 'b' }.to_xml, @response.body) - end -end diff --git a/actionpack/test/controller/http/url_for_test.rb b/actionpack/test/controller/http/url_for_test.rb deleted file mode 100644 index fba24011a2..0000000000 --- a/actionpack/test/controller/http/url_for_test.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'abstract_unit' - -class UrlForHTTPController < ActionController::HTTP - def one; end - def two; end -end - -class UrlForHTTPTest < ActionController::TestCase - tests UrlForHTTPController - - def setup - super - @request.host = 'www.example.com' - end - - def test_url_for - get :one - assert_equal "http://www.example.com/url_for_http/one", @controller.url_for - end -end diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index ef795dad89..8d4b76849f 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -35,6 +35,16 @@ module RequestForgeryProtectionActions def form_for_without_protection render :inline => "<%= form_for(:some_resource, :authenticity_token => false ) {} %>" end + + def form_for_remote + render :inline => "<%= form_for(:some_resource, :remote => true ) {} %>" + end + + def form_for_remote_with_token + render :inline => "<%= form_for(:some_resource, :remote => true, :authenticity_token => true ) {} %>" + end + + def rescue_action(e) raise e end end # sample controllers @@ -98,6 +108,20 @@ module RequestForgeryProtectionTests assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', @token end + def test_should_render_form_without_token_tag_if_remote + assert_not_blocked do + get :form_for_remote + end + assert_no_match /authenticity_token/, response.body + end + + def test_should_render_form_with_token_tag_if_remote_and_authenticity_token_requested + assert_not_blocked do + get :form_for_remote_with_token + end + assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', @token + end + def test_should_allow_get assert_not_blocked { get :index } end diff --git a/actionpack/test/controller/sweeper_test.rb b/actionpack/test/controller/sweeper_test.rb new file mode 100644 index 0000000000..0561efc62f --- /dev/null +++ b/actionpack/test/controller/sweeper_test.rb @@ -0,0 +1,16 @@ +require 'abstract_unit' + + +class SweeperTest < ActionController::TestCase + + class ::AppSweeper < ActionController::Caching::Sweeper; end + + def test_sweeper_should_not_ignore_unknown_method_calls + sweeper = ActionController::Caching::Sweeper.send(:new) + assert_raise NameError do + sweeper.instance_eval do + some_method_that_doesnt_exist + end + end + end +end diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb index 092ca3e20a..112f470786 100644 --- a/actionpack/test/dispatch/static_test.rb +++ b/actionpack/test/dispatch/static_test.rb @@ -7,6 +7,10 @@ module StaticTests assert_equal "Hello, World!", get("/nofile").body end + def test_handles_urls_with_bad_encoding + assert_equal "Hello, World!", get("/doorkeeper%E3E4").body + end + def test_sets_cache_control response = get("/index.html") assert_html "/index.html", response diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb index 2ede084e95..3ff249536e 100644 --- a/activesupport/lib/active_support/file_update_checker.rb +++ b/activesupport/lib/active_support/file_update_checker.rb @@ -106,11 +106,15 @@ module ActiveSupport globs = [] hash.each do |key, value| - globs << "#{key}/**/*#{compile_ext(value)}" + globs << "#{escape(key)}/**/*#{compile_ext(value)}" end "{#{globs.join(",")}}" end + def escape(key) + key.gsub(',','\,') + end + def compile_ext(array) #:nodoc: array = Array(array) return if array.empty? diff --git a/activesupport/test/file_update_checker_test.rb b/activesupport/test/file_update_checker_test.rb index dd2483287b..c884068c59 100644 --- a/activesupport/test/file_update_checker_test.rb +++ b/activesupport/test/file_update_checker_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'fileutils' +require 'thread' MTIME_FIXTURES_PATH = File.expand_path("../fixtures", __FILE__) @@ -79,4 +80,18 @@ class FileUpdateCheckerWithEnumerableTest < ActiveSupport::TestCase assert !checker.execute_if_updated assert_equal 0, i end + + def test_should_not_block_if_a_strange_filename_used + FileUtils.mkdir_p("tmp_watcher/valid,yetstrange,path,") + FileUtils.touch(FILES.map { |file_name| "tmp_watcher/valid,yetstrange,path,/#{file_name}" } ) + + test = Thread.new do + checker = ActiveSupport::FileUpdateChecker.new([],"tmp_watcher/valid,yetstrange,path," => :txt){ i += 1 } + Thread.exit + end + test.priority = -1 + test.join(5) + + assert !test.alive? + end end diff --git a/railties/guides/source/api_app.textile b/railties/guides/source/api_app.textile deleted file mode 100644 index 6c12b2a6dd..0000000000 --- a/railties/guides/source/api_app.textile +++ /dev/null @@ -1,271 +0,0 @@ -h2. Using Rails for API-only Apps - -In this guide you will learn: - -* What Rails provides for API-only applications -* How to configure Rails to start without any browser features -* How to decide which middlewares you will want to include -* How to decide which modules to use in your controller - -endprologue. - -h3. What is an API app? - -Traditionally, when people said that they used Rails as an "API", they meant providing a programmatically accessible API alongside their web application. -For example, GitHub provides "an API":http://developer.github.com that you can use from your own custom clients. - -With the advent of client-side frameworks, more developers are using Rails to build a backend that is shared between their web application and other native applications. - -For example, Twitter uses its "public API":https://dev.twitter.com in its web application, which is built as a static site that consumes JSON resources. - -Instead of using Rails to generate dynamic HTML that will communicate with the server through forms and links, many developers are treating their web application as just another client, delivered as static HTML, CSS and JavaScript, and consuming a simple JSON API - -This guide covers building a Rails application that serves JSON resources to an API client *or* client-side framework. - -h3. Why use Rails for JSON APIs? - -The first question a lot of people have when thinking about building a JSON API using Rails is: "isn't using Rails to spit out some JSON overkill? Shouldn't I just use something like Sinatra?" - -For very simple APIs, this may be true. However, even in very HTML-heavy applications, most of an application's logic is actually outside of the view layer. - -The reason most people use Rails is that it provides a set of defaults that allows us to get up and running quickly without having to make a lot of trivial decisions. - -Let's take a look at some of the things that Rails provides out of the box that are still applicable to API applications. - -Handled at the middleware layer: - -* Reloading: Rails applications support transparent reloading. This works even if your application gets big and restarting the server for every request becomes non-viable. -* Development Mode: Rails application come with smart defaults for development, making development pleasant without compromising production-time performance. -* Test Mode: Ditto test mode. -* Logging: Rails applications log every request, with a level of verbosity appropriate for the current mode. Rails logs in development include information about the request environment, database queries, and basic performance information. -* Security: Rails detects and thwarts "IP spoofing attacks":http://en.wikipedia.org/wiki/IP_address_spoofing and handles cryptographic signatures in a "timing attack":http://en.wikipedia.org/wiki/Timing_attack aware way. Don't know what an IP spoofing attack or a timing attack is? Exactly. -* Parameter Parsing: Want to specify your parameters as JSON instead of as a URL-encoded String? No problem. Rails will decode the JSON for you and make it available in +params+. Want to use nested URL-encoded params? That works too. -* Conditional GETs: Rails handles conditional +GET+, (+ETag+ and +Last-Modified+), processing request headers and returning the correct response headers and status code. All you need to do is use the "stale?":http://api.rubyonrails.org/classes/ActionController/ConditionalGet.html#method-i-stale-3F check in your controller, and Rails will handle all of the HTTP details for you. -* Caching: If you use +dirty?+ with public cache control, Rails will automatically cache your responses. You can easily configure the cache store. -* HEAD requests: Rails will transparently convert +HEAD+ requests into +GET+ requests, and return just the headers on the way out. This makes +HEAD+ work reliably in all Rails APIs. - -While you could obviously build these up in terms of existing Rack middlewares, I think this list demonstrates that the default Rails middleware stack provides a lot of value, even if you're "just generating JSON". - -Handled at the ActionPack layer: - -* Resourceful Routing: If you're building a RESTful JSON API, you want to be using the Rails router. Clean and conventional mapping from HTTP to controllers means not having to spend time thinking about how to model your API in terms of HTTP. -* URL Generation: The flip side of routing is URL generation. A good API based on HTTP includes URLs (see "the GitHub gist API":http://developer.github.com/v3/gists/ for an example). -* Header and Redirection Responses: +head :no_content+ and +redirect_to user_url(current_user)+ come in handy. Sure, you could manually add the response headers, but why? -* Caching: Rails provides page, action and fragment caching. Fragment caching is especially helpful when building up a nested JSON object. -* Basic, Digest and Token Authentication: Rails comes with out-of-the-box support for three kinds of HTTP authentication. -* Instrumentation: Rails 3.0 added an instrumentation API that will trigger registered handlers for a variety of events, such as action processing, sending a file or data, redirection, and database queries. The payload of each event comes with relevant information (for the action processing event, the payload includes the controller, action, params, request format, request method and the request's full path). -* Generators: This may be passé for advanced Rails users, but it can be nice to generate a resource and get your model, controller, test stubs, and routes created for you in a single command. -* Plugins: Many third-party libraries come with support for Rails that reduces or eliminates the cost of setting up and gluing together the library and the web framework. This includes things like overriding default generators, adding rake tasks, and honoring Rails choices (like the logger and cache backend). - -Of course, the Rails boot process also glues together all registered components. For example, the Rails boot process is what uses your +config/database.yml+ file when configuring ActiveRecord. - -**The short version is**: you may not have thought about which parts of Rails are still applicable even if you remove the view layer, but the answer turns out to be "most of it". - -h3. The Basic Configuration - -If you're building a Rails application that will be an API server first and foremost, you can start with a more limited subset of Rails and add in features as needed. - -You can generate a new bare Rails app: - -<shell> -$ rails new my_api --http -</shell> - -This will do three main things for you: - -* Configure your application to start with a more limited set of middleware than normal. Specifically, it will not include any middleware primarily useful for browser applications (like cookie support) by default. -* Make +ApplicationController+ inherit from +ActionController::HTTP+ instead of +ActionController::Base+. As with middleware, this will leave out any +ActionController+ modules that provide functionality primarily used by browser applications. -* Configure the generators to skip generating views, helpers and assets when you generate a new resource. - -If you want to take an existing app and make it an API app, follow the following steps. - -In +config/application.rb+ add the following lines at the top of the +Application+ class: - -<ruby> -config.middleware.http_only! -config.generators.http_only! -</ruby> - -Change +app/controllers/application_controller.rb+: - -<ruby> -# instead of -class ApplicationController < ActionController::Base -end - -# do -class ApplicationController < ActionController::HTTP -end -</ruby> - -h3. Choosing Middlewares - -An API application comes with the following middlewares by default. - -* +Rack::Cache+: Caches responses with public +Cache-Control+ headers using HTTP caching semantics. See below for more information. -* +Rack::Sendfile+: Uses a front-end server's file serving support from your Rails application. -* +Rack::Lock+: If your application is not marked as threadsafe (+config.threadsafe!+), this middleware will add a mutex around your requests. -* +ActionDispatch::RequestId+: -* +Rails::Rack::Logger+: -* +Rack::Runtime+: Adds a header to the response listing the total runtime of the request. -* +ActionDispatch::ShowExceptions+: Rescue exceptions and re-dispatch them to an exception handling application -* +ActionDispatch::DebugExceptions+: Log exceptions -* +ActionDispatch::RemoteIp+: Protect against IP spoofing attacks -* +ActionDispatch::Reloader+: In development mode, support code reloading. -* +ActionDispatch::ParamsParser+: Parse XML, YAML and JSON parameters when the request's +Content-Type+ is one of those. -* +ActionDispatch::Head+: Dispatch +HEAD+ requests as +GET+ requests, and return only the status code and headers. -* +Rack::ConditionalGet+: Supports the +stale?+ feature in Rails controllers. -* +Rack::ETag+: Automatically set an +ETag+ on all string responses. This means that if the same response is returned from a controller for the same URL, the server will return a +304 Not Modified+, even if no additional caching steps are taken. This is primarily a client-side optimization; it reduces bandwidth costs but not server processing time. - -Other plugins, including +ActiveRecord+, may add additional middlewares. In general, these middlewares are agnostic to the type of app you are building, and make sense in an API-only Rails application. - -You can get a list of all middlewares in your application via: - -<shell> -$ rake middleware -</shell> - -h4. Using Rack::Cache - -When used with Rails, +Rack::Cache+ uses the Rails cache store for its entity and meta stores. This means that if you use memcache, for your Rails app, for instance, the built-in HTTP cache will use memcache. - -To make use of +Rack::Cache+, you will want to use +stale?+ in your controller. Here's an example of +stale?+ in use. - -<ruby> -def show - @post = Post.find(params[:id]) - - if stale?(:last_modified => @post.updated_at) - render json: @post - end -end -</ruby> - -The call to +stale?+ will compare the +If-Modified-Since+ header in the request with +@post.updated_at+. If the header is newer than the last modified, this action will return a +304 Not Modified+ response. Otherwise, it will render the response and include a +Last-Modified+ header with the response. - -Normally, this mechanism is used on a per-client basis. +Rack::Cache+ allows us to share this caching mechanism across clients. We can enable cross-client caching in the call to +stale?+ - -<ruby> -def show - @post = Post.find(params[:id]) - - if stale?(:last_modified => @post.updated_at, :public => true) - render json: @post - end -end -</ruby> - -This means that +Rack::Cache+ will store off +Last-Modified+ value for a URL in the Rails cache, and add an +If-Modified-Since+ header to any subsequent inbound requests for the same URL. - -Think of it as page caching using HTTP semantics. - -NOTE: The +Rack::Cache+ middleware is always outside of the +Rack::Lock+ mutex, even in single-threaded apps. - -h4. Using Rack::Sendfile - -When you use the +send_file+ method in a Rails controller, it sets the +X-Sendfile+ header. +Rack::Sendfile+ is responsible for actually sending the file. - -If your front-end server supports accelerated file sending, +Rack::Sendfile+ will offload the actual file sending work to the front-end server. - -You can configure the name of the header that your front-end server uses for this purposes using +config.action_dispatch.x_sendfile_header+ in the appropriate environment config file. - -You can learn more about how to use +Rack::Sendfile+ with popular front-ends in "the Rack::Sendfile documentation":http://rubydoc.info/github/rack/rack/master/Rack/Sendfile - -The values for popular servers once they are configured to support accelerated file sending: - -<ruby> -# Apache and lighttpd -config.action_dispatch.x_sendfile_header = "X-Sendfile" - -# nginx -config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" -</ruby> - -Make sure to configure your server to support these options following the instructions in the +Rack::Sendfile+ documentation. - -NOTE: The +Rack::Sendfile+ middleware is always outside of the +Rack::Lock+ mutex, even in single-threaded apps. - -h4. Using ActionDispatch::ParamsParser - -+ActionDispatch::ParamsParser+ will take parameters from the client in JSON and make them available in your controller as +params+. - -To use this, your client will need to make a request with JSON-encoded parameters and specify the +Content-Type+ as +application/json+. - -Here's an example in jQuery: - -<plain> -jQuery.ajax({ - type: 'POST', - url: '/people' - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify({ person: { firstName: "Yehuda", lastName: "Katz" } }), - - success: function(json) { } -}); -</plain> - -+ActionDispatch::ParamsParser+ will see the +Content-Type+ and your params will be +{ :person => { :firstName => "Yehuda", :lastName => "Katz" } }+. - -h4. Other Middlewares - -Rails ships with a number of other middlewares that you might want to use in an API app, especially if one of your API clients is the browser: - -* +Rack::MethodOverride+: Allows the use of the +_method+ hack to route POST requests to other verbs. -* +ActionDispatch::Cookies+: Supports the +cookie+ method in +ActionController+, including support for signed and encrypted cookies. -* +ActionDispatch::Flash+: Supports the +flash+ mechanism in +ActionController+. -* +ActionDispatch::BestStandards+: Tells Internet Explorer to use the most standards-compliant available renderer. In production mode, if ChromeFrame is available, use ChromeFrame. -* Session Management: If a +config.session_store+ is supplied, this middleware makes the session available as the +session+ method in +ActionController+. - -Any of these middlewares can be adding via: - -<ruby> -config.middleware.use Rack::MethodOverride -</ruby> - -h4. Removing Middlewares - -If you don't want to use a middleware that is included by default in the API-only middleware set, you can remove it using +config.middleware.delete+: - -<ruby> -config.middleware.delete ::Rack::Sendfile -</ruby> - -Keep in mind that removing these features may remove support for certain features in +ActionController+. - -h3. Choosing Controller Modules - -An API application (using +ActionController::HTTP+) comes with the following controller modules by default: - -* +ActionController::UrlFor+: Makes +url_for+ and friends available -* +ActionController::Redirecting+: Support for +redirect_to+ -* +ActionController::Rendering+: Basic support for rendering -* +ActionController::Renderers::All+: Support for +render :json+ and friends -* +ActionController::ConditionalGet+: Support for +stale?+ -* +ActionController::ForceSSL+: Support for +force_ssl+ -* +ActionController::RackDelegation+: Support for the +request+ and +response+ methods returning +ActionDispatch::Request+ and +ActionDispatch::Response+ objects. -* +ActionController::DataStreaming+: Support for +send_file+ and +send_data+ -* +AbstractController::Callbacks+: Support for +before_filter+ and friends -* +ActionController::Instrumentation+: Support for the instrumentation hooks defined by +ActionController+ (see "the source":https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/instrumentation.rb for more). -* +ActionController::Rescue+: Support for +rescue_from+. - -Other plugins may add additional modules. You can get a list of all modules included into +ActionController::HTTP+ in the rails console: - -<shell> -$ irb ->> ActionController::HTTP.ancestors - ActionController::Metal.ancestors -</shell> - -h4. Adding Other Modules - -All ActionController modules know about their dependent modules, so you can feel free to include any modules into your controllers, and all dependencies will be included and set up as well. - -Some common modules you might want to add: - -* +AbstractController::Translation+: Support for the +l+ and +t+ localization and translation methods. These delegate to +I18n.translate+ and +I18n.localize+. -* +ActionController::HTTPAuthentication::Basic+ (or +Digest+ or +Token): Support for basic, digest or token HTTP authentication. -* +AbstractController::Layouts+: Support for layouts when rendering. -* +ActionController::MimeResponds+: Support for content negotiation (+respond_to+, +respond_with+). -* +ActionController::Cookies+: Support for +cookies+, which includes support for signed and encrypted cookies. This requires the cookie middleware. - -The best place to add a module is in your +ApplicationController+. You can also add modules to individual controllers. diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 3191fe68a7..8d64aff430 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -239,7 +239,7 @@ module Rails middleware.use ::Rack::Lock unless config.allow_concurrency middleware.use ::Rack::Runtime - middleware.use ::Rack::MethodOverride unless config.middleware.http_only? + middleware.use ::Rack::MethodOverride middleware.use ::ActionDispatch::RequestId middleware.use ::Rails::Rack::Logger, config.log_tags # must come after Rack::MethodOverride to properly log overridden methods middleware.use ::ActionDispatch::ShowExceptions, config.exceptions_app || ActionDispatch::PublicExceptions.new(Rails.public_path) @@ -252,9 +252,9 @@ module Rails end middleware.use ::ActionDispatch::Callbacks - middleware.use ::ActionDispatch::Cookies unless config.middleware.http_only? + middleware.use ::ActionDispatch::Cookies - if !config.middleware.http_only? && config.session_store + if config.session_store if config.force_ssl && !config.session_options.key?(:secure) config.session_options[:secure] = true end @@ -267,7 +267,7 @@ module Rails middleware.use ::Rack::ConditionalGet middleware.use ::Rack::ETag, "no-cache" - if !config.middleware.http_only? && config.action_dispatch.best_standards_support + if config.action_dispatch.best_standards_support middleware.use ::ActionDispatch::BestStandardsSupport, config.action_dispatch.best_standards_support end end diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb index 0fb463c44d..d8ca6cbd21 100644 --- a/railties/lib/rails/configuration.rb +++ b/railties/lib/rails/configuration.rb @@ -6,20 +6,6 @@ require 'rails/rack' module Rails module Configuration - module HttpOnly #:nodoc: - def initialize - @http_only = false - end - - def http_only! - @http_only = true - end - - def http_only? - @http_only - end - end - # MiddlewareStackProxy is a proxy for the Rails middleware stack that allows # you to configure middlewares in your application. It works basically as a # command recorder, saving each command to be applied after initialization @@ -47,21 +33,8 @@ module Rails # # config.middleware.delete ActionDispatch::BestStandardsSupport # - # In addition to these methods to handle the stack, if your application is - # going to be used as an API endpoint only, the middleware stack can be - # configured like this: - # - # config.middleware.http_only! - # - # By doing this, Rails will create a smaller middleware stack, by not adding - # some middlewares that are usually useful for browser access only, such as - # Cookies, Session and Flash, BestStandardsSupport, and MethodOverride. You - # can always add any of them later manually if you want. class MiddlewareStackProxy - include HttpOnly - def initialize - super @operations = [] end @@ -99,10 +72,7 @@ module Rails attr_accessor :aliases, :options, :templates, :fallbacks, :colorize_logging attr_reader :hidden_namespaces - include HttpOnly - def initialize - super @aliases = Hash.new { |h,k| h[k] = {} } @options = Hash.new { |h,k| h[k] = {} } @fallbacks = {} diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index 9629ac55c2..131d6e5711 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -561,7 +561,7 @@ module Rails initializer :add_view_paths do views = paths["app/views"].existent unless views.empty? - ActiveSupport.on_load(:action_controller){ prepend_view_path(views) } + ActiveSupport.on_load(:action_controller){ prepend_view_path(views) if respond_to?(:prepend_view_path) } ActiveSupport.on_load(:action_mailer){ prepend_view_path(views) } end end diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index 3965e05823..b9c1b01f54 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -46,7 +46,6 @@ module Rails :assets => true, :force_plural => false, :helper => true, - :http => false, :integration_tool => nil, :javascripts => true, :javascript_engine => :js, @@ -62,7 +61,6 @@ module Rails } def self.configure!(config) #:nodoc: - http_only! if config.http_only? no_color! unless config.colorize_logging aliases.deep_merge! config.aliases options.deep_merge! config.options @@ -72,7 +70,7 @@ module Rails hide_namespaces(*config.hidden_namespaces) end - def self.templates_path + def self.templates_path #:nodoc: @templates_path ||= [] end @@ -106,25 +104,6 @@ module Rails Thor::Base.shell = Thor::Shell::Basic end - # Configure generators for http only applications. It basically hides - # everything that is usually browser related, such as assets and session - # migration generators, and completely disable views, helpers and assets - # so generators such as scaffold won't create them. - def self.http_only! - hide_namespaces "assets", "css", "js", "session_migration" - - options[:rails].merge!( - :assets => false, - :helper => false, - :http => true, - :javascripts => false, - :javascript_engine => nil, - :stylesheets => false, - :stylesheet_engine => nil, - :template_engine => nil - ) - end - # Track all generators subclasses. def self.subclasses @subclasses ||= [] diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index ffdfb32aba..f0745df667 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -144,9 +144,6 @@ module Rails class AppGenerator < AppBase add_shared_options_for "application" - class_option :http, :type => :boolean, :default => false, - :desc => "Preconfigure smaller stack for HTTP only apps" - # Add bin/rails options class_option :version, :type => :boolean, :aliases => "-v", :group => :rails, :desc => "Show Rails version number and quit" @@ -159,10 +156,6 @@ module Rails if !options[:skip_active_record] && !DATABASES.include?(options[:database]) raise Error, "Invalid value for --database option. Supported for preconfiguration are: #{DATABASES.join(", ")}." end - - # Force sprockets to be skipped when generating http only app. - # Can't modify options hash as it's frozen by default. - self.options = options.merge(:skip_sprockets => true).freeze if options.http? end public_task :create_root @@ -177,7 +170,6 @@ module Rails def create_app_files build(:app) - remove_file("app/views") if options.http? end def create_config_files diff --git a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt index 4dc85ec156..3ddc86ae0a 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt @@ -1,5 +1,5 @@ -class ApplicationController < ActionController::<%= options.http? ? "HTTP" : "Base" %> +class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :reset_session instead. - <%= comment_if :http %>protect_from_forgery :with => :exception -end + protect_from_forgery :with => :exception +end
\ No newline at end of file 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 ba3785be35..c8a3c13b95 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb @@ -67,14 +67,5 @@ module <%= app_const_base %> # Version of your assets, change this if you want to expire all your assets. config.assets.version = '1.0' <% end -%> -<% if options.http? -%> - - # Only loads a smaller set of middleware suitable for HTTP only apps. - # Middleware like session, flash, cookies can be added back manually. - config.middleware.http_only! - - # Skip views, helpers and assets when generating a new resource. - config.generators.http_only! -<% end -%> end end diff --git a/railties/lib/rails/generators/rails/resource/resource_generator.rb b/railties/lib/rails/generators/rails/resource/resource_generator.rb index 11326388b4..7c7b289d19 100644 --- a/railties/lib/rails/generators/rails/resource/resource_generator.rb +++ b/railties/lib/rails/generators/rails/resource/resource_generator.rb @@ -21,7 +21,6 @@ module Rails return if options[:actions].present? route_config = regular_class_path.collect{ |namespace| "namespace :#{namespace} do " }.join(" ") route_config << "resources :#{file_name.pluralize}" - route_config << ", except: :edit" if options.http? route_config << " end" * regular_class_path.size route route_config end diff --git a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb index 17d462fa40..083eb49d65 100644 --- a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb +++ b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb @@ -14,8 +14,7 @@ module Rails :desc => "Generate controller with HTTP actions only" def create_controller_files - template_file = options.http? ? "http_controller.rb" : "controller.rb" - template template_file, File.join('app/controllers', class_path, "#{controller_file_name}_controller.rb") + template "controller.rb", File.join('app/controllers', class_path, "#{controller_file_name}_controller.rb") end hook_for :template_engine, :test_framework, :as => :scaffold 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 fd73835e1d..b95aea5f19 100644 --- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb +++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb @@ -46,10 +46,10 @@ class <%= controller_class_name %>Controller < ApplicationController respond_to do |format| if @<%= orm_instance.save %> format.html { redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully created.'" %> } - format.json { render json: <%= "@#{singular_table_name}" %>, :status: :created, location: <%= "@#{singular_table_name}" %> } + format.json { render json: <%= "@#{singular_table_name}" %>, status: :created, location: <%= "@#{singular_table_name}" %> } else format.html { render action: "new" } - format.json { render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity %> } + format.json { render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity } end end end diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/http_controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/http_controller.rb deleted file mode 100644 index 3f44ac18a4..0000000000 --- a/railties/lib/rails/generators/rails/scaffold_controller/templates/http_controller.rb +++ /dev/null @@ -1,60 +0,0 @@ -<% module_namespacing do -%> -class <%= controller_class_name %>Controller < ApplicationController - # GET <%= route_url %> - # GET <%= route_url %>.json - def index - @<%= plural_table_name %> = <%= orm_class.all(class_name) %> - - render json: @<%= plural_table_name %> - end - - # GET <%= route_url %>/1 - # GET <%= route_url %>/1.json - def show - @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> - - render json: @<%= singular_table_name %> - end - - # GET <%= route_url %>/new - # GET <%= route_url %>/new.json - def new - @<%= singular_table_name %> = <%= orm_class.build(class_name) %> - - render json: @<%= singular_table_name %> - end - - # POST <%= route_url %> - # POST <%= route_url %>.json - def create - @<%= singular_table_name %> = <%= orm_class.build(class_name, "params[:#{singular_table_name}]") %> - - if @<%= orm_instance.save %> - render json: @<%= singular_table_name %>, status: :created, location: @<%= singular_table_name %> - else - render json: @<%= orm_instance.errors %>, status: :unprocessable_entity - end - end - - # PATCH/PUT <%= route_url %>/1 - # PATCH/PUT <%= route_url %>/1.json - def update - @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> - - if @<%= orm_instance.update_attributes("params[:#{singular_table_name}]") %> - head :no_content - else - render json: @<%= orm_instance.errors %>, status: :unprocessable_entity - end - end - - # DELETE <%= route_url %>/1 - # DELETE <%= route_url %>/1.json - def destroy - @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> - @<%= orm_instance.destroy %> - - head :no_content - end -end -<% end -%> diff --git a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb index e875c81340..9e76587a0d 100644 --- a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb @@ -14,10 +14,8 @@ module TestUnit :desc => "Generate functional test with HTTP actions only" def create_test_files - template_file = options.http? ? "http_functional_test.rb" : "functional_test.rb" - - template template_file, - File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb") + template "functional_test.rb", + File.join("test/functional", controller_class_path, "#{controller_file_name}_controller_test.rb") end private diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/http_functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/http_functional_test.rb deleted file mode 100644 index 5bb61cb263..0000000000 --- a/railties/lib/rails/generators/test_unit/scaffold/templates/http_functional_test.rb +++ /dev/null @@ -1,50 +0,0 @@ -require 'test_helper' - -<% module_namespacing do -%> -class <%= controller_class_name %>ControllerTest < ActionController::TestCase - setup do - @<%= singular_table_name %> = <%= table_name %>(:one) - @request.accept = "application/json" - end - - test "should get index" do - get :index - assert_response :success - assert_not_nil assigns(:<%= table_name %>) - end - - test "should get new" do - get :new - assert_response :success - end - - test "should create <%= singular_table_name %>" do - assert_difference('<%= class_name %>.count') do - post :create, <%= "#{singular_table_name}: { #{attributes_hash} }" %> - end - - assert_response 201 - assert_not_nil assigns(:<%= singular_table_name %>) - end - - test "should show <%= singular_table_name %>" do - get :show, id: @<%= singular_table_name %> - assert_response :success - end - - test "should update <%= singular_table_name %>" do - put :update, id: @<%= singular_table_name %>, <%= "#{singular_table_name}: { #{attributes_hash} }" %> - assert_response 204 - assert_not_nil assigns(:<%= singular_table_name %>) - end - - test "should destroy <%= singular_table_name %>" do - assert_difference('<%= class_name %>.count', -1) do - delete :destroy, id: @<%= singular_table_name %> - end - - assert_response 204 - assert_not_nil assigns(:<%= singular_table_name %>) - end -end -<% end -%> diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb index 15a71260c1..bf58bb3f74 100644 --- a/railties/test/application/generators_test.rb +++ b/railties/test/application/generators_test.rb @@ -125,33 +125,5 @@ module ApplicationTests assert_equal expected, c.generators.options end end - - test "http only disables options from generators" do - add_to_config <<-RUBY - config.generators.http_only! - RUBY - - # Initialize the application - require "#{app_path}/config/environment" - Rails.application.load_generators - - assert !Rails::Generators.options[:rails][:template_engine], - "http only should have disabled generator options" - end - - test "http only allow overriding generators options" do - add_to_config <<-RUBY - config.generators.helper = true - config.generators.http_only! - config.generators.template_engine = :my_template - RUBY - - # Initialize the application - require "#{app_path}/config/environment" - Rails.application.load_generators - - assert_equal :my_template, Rails::Generators.options[:rails][:template_engine] - assert_equal true, Rails::Generators.options[:rails][:helper] - end end end diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index cb321f0dd4..a08e5b2374 100644 --- a/railties/test/application/initializers/frameworks_test.rb +++ b/railties/test/application/initializers/frameworks_test.rb @@ -130,33 +130,6 @@ module ApplicationTests assert_equal "false", last_response.body end - test "action_controller http initializes successfully" do - app_file "app/controllers/application_controller.rb", <<-RUBY - class ApplicationController < ActionController::HTTP - end - RUBY - - app_file "app/controllers/omg_controller.rb", <<-RUBY - class OmgController < ApplicationController - def show - render :json => { :omg => 'omg' } - end - end - RUBY - - app_file "config/routes.rb", <<-RUBY - AppTemplate::Application.routes.draw do - match "/:controller(/:action)" - end - RUBY - - require 'rack/test' - extend Rack::Test::Methods - - get '/omg/show' - assert_equal '{"omg":"omg"}', last_response.body - end - # AD test "action_dispatch extensions are applied to ActionDispatch" do add_to_config "config.action_dispatch.tld_length = 2" diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb index 0354c08067..9302443c98 100644 --- a/railties/test/application/loading_test.rb +++ b/railties/test/application/loading_test.rb @@ -95,7 +95,7 @@ class LoadingTest < ActiveSupport::TestCase assert_equal [ActiveRecord::SchemaMigration], ActiveRecord::Base.descendants end - test "initialize_cant_be_called_twice" do + test "initialize cant be called twice" do require "#{app_path}/config/environment" assert_raise(RuntimeError) { ::AppTemplate::Application.initialize! } end @@ -256,6 +256,35 @@ class LoadingTest < ActiveSupport::TestCase assert_equal "BODY", last_response.body end + test "AC load hooks can be used with metal" do + app_file "app/controllers/omg_controller.rb", <<-RUBY + begin + class OmgController < ActionController::Metal + ActiveSupport.run_load_hooks(:action_controller, self) + def show + self.response_body = ["OK"] + end + end + rescue => e + puts "Error loading metal: \#{e.class} \#{e.message}" + end + RUBY + + app_file "config/routes.rb", <<-RUBY + AppTemplate::Application.routes.draw do + match "/:controller(/:action)" + end + RUBY + + require "#{rails_root}/config/environment" + + require 'rack/test' + extend Rack::Test::Methods + + get '/omg/show' + assert_equal 'OK', last_response.body + end + protected def setup_ar! diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb index 43f86f40e4..2f62d978e3 100644 --- a/railties/test/application/middleware_test.rb +++ b/railties/test/application/middleware_test.rb @@ -52,36 +52,6 @@ module ApplicationTests ], middleware end - test "http only middleware stack" do - add_to_config "config.middleware.http_only!" - add_to_config "config.force_ssl = true" - add_to_config "config.action_dispatch.x_sendfile_header = 'X-Sendfile'" - - boot! - - assert_equal [ - "Rack::SSL", - "Rack::Sendfile", - "ActionDispatch::Static", - "Rack::Lock", - "ActiveSupport::Cache::Strategy::LocalCache", - "Rack::Runtime", - "ActionDispatch::RequestId", - "Rails::Rack::Logger", - "ActionDispatch::ShowExceptions", - "ActionDispatch::DebugExceptions", - "ActionDispatch::RemoteIp", - "ActionDispatch::Reloader", - "ActionDispatch::Callbacks", - "ActiveRecord::ConnectionAdapters::ConnectionManagement", - "ActiveRecord::QueryCache", - "ActionDispatch::ParamsParser", - "ActionDispatch::Head", - "Rack::ConditionalGet", - "Rack::ETag" - ], middleware - end - test "Rack::Sendfile is not included by default" do boot! diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb index fed9dce8e1..b6cbb10141 100644 --- a/railties/test/application/rake_test.rb +++ b/railties/test/application/rake_test.rb @@ -123,12 +123,13 @@ module ApplicationTests end def test_scaffold_tests_pass_by_default - content = Dir.chdir(app_path) do + output = Dir.chdir(app_path) do `rails generate scaffold user username:string password:string; bundle exec rake db:migrate db:test:clone test` end - assert_match(/\d+ tests, \d+ assertions, 0 failures, 0 errors/, content) + assert_match(/7 tests, 13 assertions, 0 failures, 0 errors/, output) + assert_no_match(/Errors running/, output) end def test_rake_dump_structure_should_respect_db_structure_env_variable diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 363155bc55..27960c63e9 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -361,39 +361,6 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_file "config/application.rb", /config\.active_record\.dependent_restrict_raises = false/ end - def test_http_generates_config_middleware_and_generator_http_setup - run_generator [destination_root, "--http"] - assert_file "config/application.rb", /config\.middleware\.http_only!/, - /config\.generators\.http_only!/ - end - - def test_http_generates_application_controller_with_action_controller_http - run_generator [destination_root, "--http"] - assert_file "app/controllers/application_controller.rb", - /class ApplicationController < ActionController::HTTP/ - end - - def test_http_generates_application_controller_with_protect_from_forgery_commented_out_setup - run_generator [destination_root, "--http"] - assert_file "app/controllers/application_controller.rb", /^ # protect_from_forgery/ - end - - def test_http_does_not_generate_app_views_dir - run_generator [destination_root, "--http"] - assert_no_directory "app/views" - end - - def test_http_skip_sprockets_entries_in_gemfile_and_application - run_generator [destination_root, "--http"] - assert_file "Gemfile" do |content| - assert_no_match(/group :assets/, content) - end - assert_file "config/application.rb" do |content| - assert_match(/^# require "sprockets/, content) - assert_no_match(/config\.assets/, content) - end - end - def test_pretend_option output = run_generator [File.join(destination_root, "myapp"), "--pretend"] assert_no_match(/run bundle install/, output) diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb index 4ba3ad8c41..73804dae45 100644 --- a/railties/test/generators/resource_generator_test.rb +++ b/railties/test/generators/resource_generator_test.rb @@ -86,10 +86,4 @@ class ResourceGeneratorTest < Rails::Generators::TestCase assert_no_match(/resources :accounts$/, route) end end - - def test_http_only_does_not_generate_edit_route - run_generator ["Account", "--http"] - - assert_file "config/routes.rb", /resources :accounts, except: :edit$/ - end end diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb index ed54ce43da..1eea50b0d9 100644 --- a/railties/test/generators/scaffold_controller_generator_test.rb +++ b/railties/test/generators/scaffold_controller_generator_test.rb @@ -142,41 +142,4 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase assert_match(/\{ render action: "new" \}/, content) end end - - def test_http_only_generates_controller_without_respond_to_block_and_html_format - run_generator ["User", "--http"] - - assert_file "app/controllers/users_controller.rb" do |content| - assert_match(/render json: @user/, content) - assert_no_match(/respond_to/, content) - assert_no_match(/format\.html/, content) - end - end - - def test_http_only_generates_functional_tests_with_json_format_and_http_status_assertions - run_generator ["User", "--http"] - - assert_file "test/functional/users_controller_test.rb" do |content| - assert_match(/class UsersControllerTest < ActionController::TestCase/, content) - assert_match(/@request\.accept = "application\/json"/, content) - assert_match(/test "should get index"/, content) - - assert_match(/assert_response 201/, content) - assert_no_match(/assert_redirected_to/, content) - end - end - - def test_http_only_does_not_generate_edit_action - run_generator ["User", "--http"] - - assert_file "app/controllers/users_controller.rb" do |content| - assert_match(/def index/, content) - assert_no_match(/def edit/, content) - end - - assert_file "test/functional/users_controller_test.rb" do |content| - assert_match(/test "should get index"/, content) - assert_no_match(/test "should get edit"/, content) - end - end end diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb index 9e7ec86fdf..60e7e57a91 100644 --- a/railties/test/generators_test.rb +++ b/railties/test/generators_test.rb @@ -213,55 +213,4 @@ class GeneratorsTest < Rails::Generators::TestCase Rails::Generators.hide_namespace("special:namespace") assert Rails::Generators.hidden_namespaces.include?("special:namespace") end - - def test_http_only_hides_generators - generators = %w(assets js css session_migration) - - generators.each do |generator| - assert !Rails::Generators.hidden_namespaces.include?(generator) - end - - with_http_only! do - generators.each do |generator| - assert Rails::Generators.hidden_namespaces.include?(generator), - "http only should hide #{generator} generator" - end - end - end - - def test_http_only_enables_http_option - options = Rails::Generators.options[:rails] - - assert !options[:http], "http option should be disabled by default" - - with_http_only! do - assert options[:http], "http only should enable generator http option" - end - end - - def test_http_only_disables_template_and_helper_and_assets_options - options = Rails::Generators.options[:rails] - disable_options = [:assets, :helper, :javascripts, :javascript_engine, - :stylesheets, :stylesheet_engine, :template_engine] - - disable_options.each do |disable_option| - assert options[disable_option], "without http only should have generator option #{disable_option} enabled" - end - - with_http_only! do - disable_options.each do |disable_option| - assert !options[disable_option], "http only should have generator option #{disable_option} disabled" - end - end - end - - private - - def with_http_only! - Rails::Generators.http_only! - yield - ensure - Rails::Generators.instance_variable_set(:@hidden_namespaces, nil) - Rails::Generators.instance_variable_set(:@options, nil) - end end diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index dc52c79fb5..ac4c2abfc8 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -116,7 +116,12 @@ module TestHelpers end end - add_to_config 'config.secret_token = "3b7cd727ee24e8444053437c36cc66c4"; config.session_store :cookie_store, :key => "_myapp_session"; config.active_support.deprecation = :log' + add_to_config <<-RUBY + config.secret_token = "3b7cd727ee24e8444053437c36cc66c4" + config.session_store :cookie_store, :key => "_myapp_session" + config.active_support.deprecation = :log + config.action_controller.allow_forgery_protection = false + RUBY end def teardown_app |