Remove --http.

This commit is contained in:
José Valim 2012-03-14 22:30:01 +01:00
parent 1efeb072bf
commit 6db930cb5b
30 changed files with 7 additions and 1020 deletions

View File

@ -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*

View File

@ -6,7 +6,6 @@ module ActionController
autoload :Base
autoload :Caching
autoload :HTTP
autoload :Metal
autoload :Middleware

View File

@ -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

View File

@ -293,10 +293,6 @@ module ActionController
end
end
class HTTP
include SharedTestRoutes.url_helpers
end
class TestCase
include ActionDispatch::TestProcess

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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
@ -58,10 +44,7 @@ module Rails
# 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 +82,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 = {}

View File

@ -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 ||= []

View File

@ -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

View File

@ -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

View File

@ -66,15 +66,6 @@ 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

View File

@ -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

View File

@ -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

View File

@ -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 -%>

View File

@ -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

View File

@ -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 -%>

View File

@ -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

View File

@ -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"

View File

@ -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!

View File

@ -132,26 +132,6 @@ module ApplicationTests
assert_no_match(/Errors running/, output)
end
def test_http_scaffold_tests_pass_by_default
add_to_config <<-RUBY
config.middleware.http_only!
config.generators.http_only!
RUBY
app_file "app/controllers/application_controller.rb", <<-RUBY
class ApplicationController < ActionController::HTTP
end
RUBY
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(/6 tests, 12 assertions, 0 failures, 0 errors/, output)
assert_no_match(/Errors running/, output)
end
def test_rake_dump_structure_should_respect_db_structure_env_variable
Dir.chdir(app_path) do
# ensure we have a schema_migrations table to dump

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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