aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xactionmailer/bin/test4
-rw-r--r--actionpack/CHANGELOG.md6
-rwxr-xr-xactionpack/bin/test4
-rw-r--r--actionpack/lib/action_controller.rb3
-rw-r--r--actionpack/lib/action_controller/api.rb145
-rw-r--r--actionpack/lib/action_controller/base.rb6
-rw-r--r--actionpack/lib/action_controller/metal/basic_implicit_render.rb11
-rw-r--r--actionpack/lib/action_controller/metal/implicit_render.rb9
-rw-r--r--actionpack/lib/action_controller/test_case.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/static.rb7
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb25
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb22
-rw-r--r--actionpack/test/abstract_unit.rb4
-rw-r--r--actionpack/test/controller/api/conditional_get_test.rb57
-rw-r--r--actionpack/test/controller/api/data_streaming_test.rb26
-rw-r--r--actionpack/test/controller/api/force_ssl_test.rb20
-rw-r--r--actionpack/test/controller/api/implicit_render_test.rb15
-rw-r--r--actionpack/test/controller/api/params_wrapper_test.rb26
-rw-r--r--actionpack/test/controller/api/redirect_to_test.rb19
-rw-r--r--actionpack/test/controller/api/renderers_test.rb38
-rw-r--r--actionpack/test/controller/api/url_for_test.rb20
-rw-r--r--actionpack/test/controller/render_test.rb14
-rw-r--r--actionpack/test/dispatch/routing_test.rb75
-rw-r--r--actionpack/test/dispatch/show_exceptions_test.rb2
-rw-r--r--actionpack/test/dispatch/static_test.rb2
-rwxr-xr-xactionview/bin/test4
-rw-r--r--actionview/lib/action_view/digestor.rb3
-rw-r--r--actionview/lib/action_view/railtie.rb6
-rw-r--r--actionview/test/template/digestor_test.rb20
-rwxr-xr-xactivemodel/bin/test4
-rw-r--r--activerecord/CHANGELOG.md17
-rwxr-xr-xactiverecord/bin/test19
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb5
-rw-r--r--activerecord/lib/active_record/enum.rb42
-rw-r--r--activerecord/lib/active_record/fixtures.rb2
-rw-r--r--activerecord/lib/active_record/migration/command_recorder.rb10
-rw-r--r--activerecord/lib/active_record/persistence.rb3
-rw-r--r--activerecord/lib/active_record/railtie.rb2
-rw-r--r--activerecord/lib/active_record/sanitization.rb4
-rw-r--r--activerecord/test/cases/adapters/mysql/active_schema_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/charset_collation_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/connection_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/consistency_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/enum_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/quoting_test.rb24
-rw-r--r--activerecord/test/cases/adapters/mysql/reserved_word_test.rb40
-rw-r--r--activerecord/test/cases/adapters/mysql/schema_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/sp_test.rb4
-rw-r--r--activerecord/test/cases/adapters/mysql/sql_types_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/statement_pool_test.rb28
-rw-r--r--activerecord/test/cases/adapters/mysql/table_options_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/unsigned_type_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/active_schema_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/boolean_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/charset_collation_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/enum_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/explain_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/reserved_word_test.rb40
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb81
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/sql_types_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/table_options_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/active_schema_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/array_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/bit_string_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/bytea_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/change_schema_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/cidr_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/citext_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/collation_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/composite_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/datatype_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/domain_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/enum_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/explain_test.rb30
-rw-r--r--activerecord/test/cases/adapters/postgresql/extension_migration_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/full_text_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/geometric_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/hstore_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/infinity_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/integer_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/json_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/ltree_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/money_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/network_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/numbers_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/quoting_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/range_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/rename_table_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/serial_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/statement_pool_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/timestamp_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/type_lookup_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/utils_test.rb5
-rw-r--r--activerecord/test/cases/adapters/postgresql/uuid_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/view_test.rb7
-rw-r--r--activerecord/test/cases/adapters/postgresql/xml_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/collation_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/copy_table_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/explain_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/quoting_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb3
-rw-r--r--activerecord/test/cases/enum_test.rb50
-rw-r--r--activerecord/test/cases/fixtures_test.rb10
-rw-r--r--activerecord/test/cases/migration/command_recorder_test.rb31
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb5
-rw-r--r--activerecord/test/cases/serialized_attribute_test.rb13
-rw-r--r--activerecord/test/cases/test_case.rb24
-rw-r--r--activerecord/test/fixtures/books.yml4
-rw-r--r--activerecord/test/fixtures/doubloons.yml3
-rw-r--r--activerecord/test/models/book.rb4
-rw-r--r--activerecord/test/models/doubloon.rb12
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb1
-rw-r--r--activerecord/test/schema/schema.rb9
-rwxr-xr-xactivesupport/bin/test4
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb2
-rw-r--r--activesupport/lib/active_support/testing/autorun.rb9
-rw-r--r--guides/source/active_record_migrations.md3
-rw-r--r--guides/source/api_app.md435
-rw-r--r--guides/source/command_line.md4
-rw-r--r--guides/source/working_with_javascript_in_rails.md28
-rw-r--r--railties/CHANGELOG.md7
-rw-r--r--railties/lib/rails/application/configuration.rb8
-rw-r--r--railties/lib/rails/application/default_middleware_stack.rb8
-rw-r--r--railties/lib/rails/configuration.rb3
-rw-r--r--railties/lib/rails/generators.rb17
-rw-r--r--railties/lib/rails/generators/app_base.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb36
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile8
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb7
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb14
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb1
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb5
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb61
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb6
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb40
-rw-r--r--railties/lib/rails/test_help.rb5
-rw-r--r--railties/test/application/generators_test.rb35
-rw-r--r--railties/test/application/initializers/frameworks_test.rb29
-rw-r--r--railties/test/application/middleware_test.rb27
-rw-r--r--railties/test/application/rake_test.rb19
-rw-r--r--railties/test/generators/api_app_generator_test.rb96
-rw-r--r--railties/test/generators/scaffold_controller_generator_test.rb47
-rw-r--r--railties/test/generators/scaffold_generator_test.rb70
-rw-r--r--tools/test.rb10
161 files changed, 2036 insertions, 317 deletions
diff --git a/actionmailer/bin/test b/actionmailer/bin/test
new file mode 100755
index 0000000000..404cabba51
--- /dev/null
+++ b/actionmailer/bin/test
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+COMPONENT_ROOT = File.expand_path("../../", __FILE__)
+require File.expand_path("../tools/test", COMPONENT_ROOT)
+exit Minitest.run(ARGV)
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 66f1d8c6a6..7b2f150c67 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,9 @@
+* Add support for API only apps.
+ ActionController::API is added as a replacement of
+ ActionController::Base for this kind of applications.
+
+ *Santiago Pastorino & Jorge Bejar*
+
* Remove `assigns` and `assert_template`. Both methods have been extracted
into a gem at https://github.com/rails/rails-controller-testing.
diff --git a/actionpack/bin/test b/actionpack/bin/test
new file mode 100755
index 0000000000..404cabba51
--- /dev/null
+++ b/actionpack/bin/test
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+COMPONENT_ROOT = File.expand_path("../../", __FILE__)
+require File.expand_path("../tools/test", COMPONENT_ROOT)
+exit Minitest.run(ARGV)
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index a1893ce920..89fc4520d3 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -7,6 +7,7 @@ require 'action_controller/metal/strong_parameters'
module ActionController
extend ActiveSupport::Autoload
+ autoload :API
autoload :Base
autoload :Caching
autoload :Metal
@@ -15,7 +16,6 @@ module ActionController
autoload :FormBuilder
autoload_under "metal" do
- autoload :Compatibility
autoload :ConditionalGet
autoload :Cookies
autoload :DataStreaming
@@ -25,6 +25,7 @@ module ActionController
autoload :Head
autoload :Helpers
autoload :HttpAuthentication
+ autoload :BasicImplicitRender
autoload :ImplicitRender
autoload :Instrumentation
autoload :MimeResponds
diff --git a/actionpack/lib/action_controller/api.rb b/actionpack/lib/action_controller/api.rb
new file mode 100644
index 0000000000..d8149e0232
--- /dev/null
+++ b/actionpack/lib/action_controller/api.rb
@@ -0,0 +1,145 @@
+require 'action_view'
+require 'action_controller'
+require 'action_controller/log_subscriber'
+
+module ActionController
+ # API 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 controllers with just the
+ # features that you need for API only applications.
+ #
+ # An API 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,
+ # 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 API Controller stack.
+ #
+ # By default, only the ApplicationController in a \Rails application inherits
+ # from <tt>ActionController::API</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 API 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, otherwise it will return 204 No Content response.
+ #
+ # 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::API</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::API
+ # include ActionController::MimeResponds
+ # end
+ #
+ # class PostsController < ApplicationController
+ # def index
+ # @posts = Post.all
+ #
+ # respond_to do |format|
+ # format.json { render json: @posts }
+ # format.xml { render xml: @posts }
+ # end
+ # 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::API</tt> out of the box.
+ class API < Metal
+ abstract!
+
+ # Shortcut helper that returns all the ActionController::API modules except the ones passed in the argument:
+ #
+ # class MetalController
+ # ActionController::API.without_modules(:Redirecting, :DataStreaming).each do |left|
+ # include left
+ # end
+ # end
+ #
+ # This gives better control over what you want to exclude and makes it easier
+ # to create an api 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 = [
+ AbstractController::Rendering,
+
+ UrlFor,
+ Redirecting,
+ Rendering,
+ Renderers::All,
+ ConditionalGet,
+ RackDelegation,
+ BasicImplicitRender,
+ StrongParameters,
+
+ 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,
+
+ # Params wrapper should come before instrumentation so they are
+ # properly showed in logs
+ ParamsWrapper
+ ]
+
+ MODULES.each do |mod|
+ include mod
+ end
+
+ ActiveSupport.run_load_hooks(:action_controller, self)
+ end
+end
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index bfae372f53..2c3b3f4e05 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -50,9 +50,9 @@ module ActionController
#
# == Parameters
#
- # All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method
- # which returns a hash. For example, an action that was performed through <tt>/posts?category=All&limit=5</tt> will include
- # <tt>{ "category" => "All", "limit" => "5" }</tt> in params.
+ # All request parameters, whether they come from a query string in the URL or form data submitted through a POST request are
+ # available through the params method which returns a hash. For example, an action that was performed through
+ # <tt>/posts?category=All&limit=5</tt> will include <tt>{ "category" => "All", "limit" => "5" }</tt> in params.
#
# It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
#
diff --git a/actionpack/lib/action_controller/metal/basic_implicit_render.rb b/actionpack/lib/action_controller/metal/basic_implicit_render.rb
new file mode 100644
index 0000000000..6c6f8381ff
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/basic_implicit_render.rb
@@ -0,0 +1,11 @@
+module ActionController
+ module BasicImplicitRender
+ def send_action(method, *args)
+ super.tap { default_render unless performed? }
+ end
+
+ def default_render(*args)
+ head :no_content
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/metal/implicit_render.rb b/actionpack/lib/action_controller/metal/implicit_render.rb
index 1573ea7099..d66b2214ce 100644
--- a/actionpack/lib/action_controller/metal/implicit_render.rb
+++ b/actionpack/lib/action_controller/metal/implicit_render.rb
@@ -1,17 +1,14 @@
module ActionController
module ImplicitRender
- def send_action(method, *args)
- ret = super
- default_render unless performed?
- ret
- end
+
+ include BasicImplicitRender
def default_render(*args)
if template_exists?(action_name.to_s, _prefixes, variants: request.variant)
render(*args)
else
logger.info "No template found for #{self.class.name}\##{action_name}, rendering head :no_content" if logger
- head :no_content
+ super
end
end
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index d34149143f..96f161fb09 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -165,7 +165,7 @@ module ActionController
# class BooksControllerTest < ActionController::TestCase
# def test_create
# # Simulate a POST response with the given HTTP parameters.
- # post(:create, book: { title: "Love Hina" })
+ # post(:create, params: { book: { title: "Love Hina" }})
#
# # Assert that the controller tried to redirect us to
# # the created book's URI.
diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb
index 3aa0eef4ad..b098ea389f 100644
--- a/actionpack/lib/action_dispatch/middleware/static.rb
+++ b/actionpack/lib/action_dispatch/middleware/static.rb
@@ -13,7 +13,7 @@ module ActionDispatch
# located at `public/assets/application.js` if the file exists. If the file
# does not exist, a 404 "File not Found" response will be returned.
class FileHandler
- def initialize(root, cache_control, index = 'index')
+ def initialize(root, cache_control, index: 'index')
@root = root.chomp('/')
@compiled_root = /^#{Regexp.escape(root)}/
headers = cache_control && { 'Cache-Control' => cache_control }
@@ -21,7 +21,6 @@ module ActionDispatch
@index = index
end
-
# Takes a path to a file. If the file is found, has valid encoding, and has
# correct read permissions, the return value is a URI-escaped string
# representing the filename. Otherwise, false is returned.
@@ -105,9 +104,9 @@ module ActionDispatch
# produce a directory traversal using this middleware. Only 'GET' and 'HEAD'
# requests will result in a file being returned.
class Static
- def initialize(app, path, cache_control = nil, index = 'index')
+ def initialize(app, path, cache_control = nil, index: 'index')
@app = app
- @file_handler = FileHandler.new(path, cache_control, index)
+ @file_handler = FileHandler.new(path, cache_control, index: index)
end
def call(env)
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 15980db39b..7cfe4693c1 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1042,7 +1042,7 @@ module ActionDispatch
class Resource #:nodoc:
attr_reader :controller, :path, :options, :param
- def initialize(entities, options = {})
+ def initialize(entities, api_only = false, options = {})
@name = entities.to_s
@path = (options[:path] || @name).to_s
@controller = (options[:controller] || @name).to_s
@@ -1050,10 +1050,15 @@ module ActionDispatch
@param = (options[:param] || :id).to_sym
@options = options
@shallow = false
+ @api_only = api_only
end
def default_actions
- [:index, :create, :new, :show, :update, :destroy, :edit]
+ if @api_only
+ [:index, :create, :show, :update, :destroy]
+ else
+ [:index, :create, :new, :show, :update, :destroy, :edit]
+ end
end
def actions
@@ -1120,7 +1125,7 @@ module ActionDispatch
end
class SingletonResource < Resource #:nodoc:
- def initialize(entities, options)
+ def initialize(entities, api_only, options)
super
@as = nil
@controller = (options[:controller] || plural).to_s
@@ -1128,7 +1133,11 @@ module ActionDispatch
end
def default_actions
- [:show, :create, :update, :destroy, :new, :edit]
+ if @api_only
+ [:show, :create, :update, :destroy]
+ else
+ [:show, :create, :update, :destroy, :new, :edit]
+ end
end
def plural
@@ -1178,7 +1187,7 @@ module ActionDispatch
return self
end
- resource_scope(:resource, SingletonResource.new(resources.pop, options)) do
+ resource_scope(:resource, SingletonResource.new(resources.pop, api_only?, options)) do
yield if block_given?
concerns(options[:concerns]) if options[:concerns]
@@ -1336,7 +1345,7 @@ module ActionDispatch
return self
end
- resource_scope(:resources, Resource.new(resources.pop, options)) do
+ resource_scope(:resources, Resource.new(resources.pop, api_only?, options)) do
yield if block_given?
concerns(options[:concerns]) if options[:concerns]
@@ -1768,6 +1777,10 @@ module ActionDispatch
delete :destroy if parent_resource.actions.include?(:destroy)
end
end
+
+ def api_only?
+ @set.api_only?
+ end
end
# Routing Concerns allow you to declare common routes that can be reused
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index cf35e85b40..454593b59f 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -319,17 +319,23 @@ module ActionDispatch
end
def self.new_with_config(config)
+ route_set_config = DEFAULT_CONFIG
+
+ # engines apparently don't have this set
if config.respond_to? :relative_url_root
- new Config.new config.relative_url_root
- else
- # engines apparently don't have this set
- new
+ route_set_config.relative_url_root = config.relative_url_root
+ end
+
+ if config.respond_to? :api_only
+ route_set_config.api_only = config.api_only
end
+
+ new route_set_config
end
- Config = Struct.new :relative_url_root
+ Config = Struct.new :relative_url_root, :api_only
- DEFAULT_CONFIG = Config.new(nil)
+ DEFAULT_CONFIG = Config.new(nil, false)
def initialize(config = DEFAULT_CONFIG)
self.named_routes = NamedRouteCollection.new
@@ -352,6 +358,10 @@ module ActionDispatch
@config.relative_url_root
end
+ def api_only?
+ @config.api_only
+ end
+
def request_class
ActionDispatch::Request
end
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 1690bdd542..cc610b6d75 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -232,6 +232,10 @@ class Rack::TestCase < ActionDispatch::IntegrationTest
end
module ActionController
+ class API
+ extend AbstractController::Railties::RoutesHelpers.with(SharedTestRoutes)
+ end
+
class Base
# This stub emulates the Railtie including the URL helpers from a Rails application
extend AbstractController::Railties::RoutesHelpers.with(SharedTestRoutes)
diff --git a/actionpack/test/controller/api/conditional_get_test.rb b/actionpack/test/controller/api/conditional_get_test.rb
new file mode 100644
index 0000000000..d1eb27bf24
--- /dev/null
+++ b/actionpack/test/controller/api/conditional_get_test.rb
@@ -0,0 +1,57 @@
+require 'abstract_unit'
+require 'active_support/core_ext/integer/time'
+require 'active_support/core_ext/numeric/time'
+
+class ConditionalGetApiController < ActionController::API
+ before_action :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 ConditionalGetApiTest < ActionController::TestCase
+ tests ConditionalGetApiController
+
+ def setup
+ @last_modified = Time.now.utc.beginning_of_day.httpdate
+ end
+
+ def test_request_gets_last_modified
+ get :two
+ assert_equal @last_modified, @response.headers['Last-Modified']
+ assert_response :success
+ end
+
+ def test_request_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 @response.body.blank?
+ assert_equal @last_modified, @response.headers['Last-Modified']
+ end
+end
diff --git a/actionpack/test/controller/api/data_streaming_test.rb b/actionpack/test/controller/api/data_streaming_test.rb
new file mode 100644
index 0000000000..0e7d97d1f4
--- /dev/null
+++ b/actionpack/test/controller/api/data_streaming_test.rb
@@ -0,0 +1,26 @@
+require 'abstract_unit'
+
+module TestApiFileUtils
+ def file_path() File.expand_path(__FILE__) end
+ def file_data() @data ||= File.open(file_path, 'rb') { |f| f.read } end
+end
+
+class DataStreamingApiController < ActionController::API
+ include TestApiFileUtils
+
+ def one; end
+ def two
+ send_data(file_data, {})
+ end
+end
+
+class DataStreamingApiTest < ActionController::TestCase
+ include TestApiFileUtils
+ tests DataStreamingApiController
+
+ 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/api/force_ssl_test.rb b/actionpack/test/controller/api/force_ssl_test.rb
new file mode 100644
index 0000000000..8578340d82
--- /dev/null
+++ b/actionpack/test/controller/api/force_ssl_test.rb
@@ -0,0 +1,20 @@
+require 'abstract_unit'
+
+class ForceSSLApiController < ActionController::API
+ force_ssl
+
+ def one; end
+ def two
+ head :ok
+ end
+end
+
+class ForceSSLApiTest < ActionController::TestCase
+ tests ForceSSLApiController
+
+ def test_redirects_to_https
+ get :two
+ assert_response 301
+ assert_equal "https://test.host/force_ssl_api/two", redirect_to_url
+ end
+end
diff --git a/actionpack/test/controller/api/implicit_render_test.rb b/actionpack/test/controller/api/implicit_render_test.rb
new file mode 100644
index 0000000000..26f9cd8f78
--- /dev/null
+++ b/actionpack/test/controller/api/implicit_render_test.rb
@@ -0,0 +1,15 @@
+require 'abstract_unit'
+
+class ImplicitRenderAPITestController < ActionController::API
+ def empty_action
+ end
+end
+
+class ImplicitRenderAPITest < ActionController::TestCase
+ tests ImplicitRenderAPITestController
+
+ def test_implicit_no_content_response
+ get :empty_action
+ assert_response :no_content
+ end
+end
diff --git a/actionpack/test/controller/api/params_wrapper_test.rb b/actionpack/test/controller/api/params_wrapper_test.rb
new file mode 100644
index 0000000000..e40a39d829
--- /dev/null
+++ b/actionpack/test/controller/api/params_wrapper_test.rb
@@ -0,0 +1,26 @@
+require 'abstract_unit'
+
+class ParamsWrapperForApiTest < ActionController::TestCase
+ class UsersController < ActionController::API
+ attr_accessor :last_parameters
+
+ wrap_parameters :person, format: [:json]
+
+ def test
+ self.last_parameters = params.except(:controller, :action)
+ head :ok
+ end
+ end
+
+ class Person; end
+
+ tests UsersController
+
+ def test_specify_wrapper_name
+ @request.env['CONTENT_TYPE'] = 'application/json'
+ post :test, params: { 'username' => 'sikachu' }
+
+ expected = { 'username' => 'sikachu', 'person' => { 'username' => 'sikachu' }}
+ assert_equal expected, @controller.last_parameters
+ end
+end
diff --git a/actionpack/test/controller/api/redirect_to_test.rb b/actionpack/test/controller/api/redirect_to_test.rb
new file mode 100644
index 0000000000..18877c4b3a
--- /dev/null
+++ b/actionpack/test/controller/api/redirect_to_test.rb
@@ -0,0 +1,19 @@
+require 'abstract_unit'
+
+class RedirectToApiController < ActionController::API
+ def one
+ redirect_to action: "two"
+ end
+
+ def two; end
+end
+
+class RedirectToApiTest < ActionController::TestCase
+ tests RedirectToApiController
+
+ def test_redirect_to
+ get :one
+ assert_response :redirect
+ assert_equal "http://test.host/redirect_to_api/two", redirect_to_url
+ end
+end
diff --git a/actionpack/test/controller/api/renderers_test.rb b/actionpack/test/controller/api/renderers_test.rb
new file mode 100644
index 0000000000..9405538833
--- /dev/null
+++ b/actionpack/test/controller/api/renderers_test.rb
@@ -0,0 +1,38 @@
+require 'abstract_unit'
+require 'active_support/core_ext/hash/conversions'
+
+class RenderersApiController < ActionController::API
+ class Model
+ def to_json(options = {})
+ { a: 'b' }.to_json(options)
+ end
+
+ def to_xml(options = {})
+ { a: 'b' }.to_xml(options)
+ end
+ end
+
+ def one
+ render json: Model.new
+ end
+
+ def two
+ render xml: Model.new
+ end
+end
+
+class RenderersApiTest < ActionController::TestCase
+ tests RenderersApiController
+
+ 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/api/url_for_test.rb b/actionpack/test/controller/api/url_for_test.rb
new file mode 100644
index 0000000000..0d8691a091
--- /dev/null
+++ b/actionpack/test/controller/api/url_for_test.rb
@@ -0,0 +1,20 @@
+require 'abstract_unit'
+
+class UrlForApiController < ActionController::API
+ def one; end
+ def two; end
+end
+
+class UrlForApiTest < ActionController::TestCase
+ tests UrlForApiController
+
+ def setup
+ super
+ @request.host = 'www.example.com'
+ end
+
+ def test_url_for
+ get :one
+ assert_equal "http://www.example.com/url_for_api/one", @controller.url_for
+ end
+end
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index cabacad940..c9c43de37d 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -24,6 +24,11 @@ class TestControllerWithExtraEtags < ActionController::Base
end
end
+class ImplicitRenderTestController < ActionController::Base
+ def empty_action
+ end
+end
+
class TestController < ActionController::Base
protect_from_forgery
@@ -463,6 +468,15 @@ class MetalRenderTest < ActionController::TestCase
end
end
+class ImplicitRenderTest < ActionController::TestCase
+ tests ImplicitRenderTestController
+
+ def test_implicit_no_content_response
+ get :empty_action
+ assert_response :no_content
+ end
+end
+
class HeadRenderTest < ActionController::TestCase
tests TestController
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 62c99a2edc..77f86b7a62 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -167,6 +167,46 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal '/session/reset', reset_session_path
end
+ def test_session_singleton_resource_for_api_app
+ self.class.stub_controllers do |_|
+ config = ActionDispatch::Routing::RouteSet::Config.new
+ config.api_only = true
+
+ routes = ActionDispatch::Routing::RouteSet.new(config)
+
+ routes.draw do
+ resource :session do
+ get :create
+ post :reset
+ end
+ end
+ @app = RoutedRackApp.new routes
+ end
+
+ get '/session'
+ assert_equal 'sessions#create', @response.body
+ assert_equal '/session', session_path
+
+ post '/session'
+ assert_equal 'sessions#create', @response.body
+
+ put '/session'
+ assert_equal 'sessions#update', @response.body
+
+ delete '/session'
+ assert_equal 'sessions#destroy', @response.body
+
+ post '/session/reset'
+ assert_equal 'sessions#reset', @response.body
+ assert_equal '/session/reset', reset_session_path
+
+ get '/session/new'
+ assert_equal 'Not Found', @response.body
+
+ get '/session/edit'
+ assert_equal 'Not Found', @response.body
+ end
+
def test_session_info_nested_singleton_resource
draw do
resource :session do
@@ -509,6 +549,41 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal '/projects/1/edit', edit_project_path(:id => '1')
end
+ def test_projects_for_api_app
+ self.class.stub_controllers do |_|
+ config = ActionDispatch::Routing::RouteSet::Config.new
+ config.api_only = true
+
+ routes = ActionDispatch::Routing::RouteSet.new(config)
+ routes.draw do
+ resources :projects, controller: :project
+ end
+ @app = RoutedRackApp.new routes
+ end
+
+ get '/projects'
+ assert_equal 'project#index', @response.body
+ assert_equal '/projects', projects_path
+
+ post '/projects'
+ assert_equal 'project#create', @response.body
+
+ get '/projects.xml'
+ assert_equal 'project#index', @response.body
+ assert_equal '/projects.xml', projects_path(format: 'xml')
+
+ get '/projects/1'
+ assert_equal 'project#show', @response.body
+ assert_equal '/projects/1', project_path(id: '1')
+
+ get '/projects/1.xml'
+ assert_equal 'project#show', @response.body
+ assert_equal '/projects/1.xml', project_path(id: '1', format: 'xml')
+
+ get '/projects/1/edit'
+ assert_equal 'Not Found', @response.body
+ end
+
def test_projects_with_post_action_and_new_path_on_collection
draw do
resources :projects, :controller => :project do
diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb
index 72eaa916bc..cbb12a2209 100644
--- a/actionpack/test/dispatch/show_exceptions_test.rb
+++ b/actionpack/test/dispatch/show_exceptions_test.rb
@@ -11,7 +11,7 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest
when "/bad_params"
raise ActionDispatch::ParamsParser::ParseError.new("", StandardError.new)
when "/method_not_allowed"
- raise ActionController::MethodNotAllowed
+ raise ActionController::MethodNotAllowed, 'PUT'
when "/unknown_http_method"
raise ActionController::UnknownHttpMethod
when "/not_found_original_exception"
diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb
index e729cc44f9..95971b3a0e 100644
--- a/actionpack/test/dispatch/static_test.rb
+++ b/actionpack/test/dispatch/static_test.rb
@@ -263,7 +263,7 @@ class StaticTest < ActiveSupport::TestCase
end
def test_non_default_static_index
- @app = ActionDispatch::Static.new(DummyApp, @root, "public, max-age=60", "other-index")
+ @app = ActionDispatch::Static.new(DummyApp, @root, "public, max-age=60", index: "other-index")
assert_html "/other-index.html", get("/other-index.html")
assert_html "/other-index.html", get("/other-index")
assert_html "/other-index.html", get("/")
diff --git a/actionview/bin/test b/actionview/bin/test
new file mode 100755
index 0000000000..404cabba51
--- /dev/null
+++ b/actionview/bin/test
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+COMPONENT_ROOT = File.expand_path("../../", __FILE__)
+require File.expand_path("../tools/test", COMPONENT_ROOT)
+exit Minitest.run(ARGV)
diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb
index 1f103786cb..b29eb48425 100644
--- a/actionview/lib/action_view/digestor.rb
+++ b/actionview/lib/action_view/digestor.rb
@@ -70,7 +70,8 @@ module ActionView
def dependencies
DependencyTracker.find_dependencies(name, template)
rescue ActionView::MissingTemplate
- [] # File doesn't exist, so no dependencies
+ logger.try :error, " '#{name}' file doesn't exist, so no dependencies"
+ []
end
def nested_dependencies
diff --git a/actionview/lib/action_view/railtie.rb b/actionview/lib/action_view/railtie.rb
index 9a26cba574..5dc7950d6b 100644
--- a/actionview/lib/action_view/railtie.rb
+++ b/actionview/lib/action_view/railtie.rb
@@ -48,8 +48,10 @@ module ActionView
end
end
- rake_tasks do
- load "action_view/tasks/dependencies.rake"
+ rake_tasks do |app|
+ unless app.config.api_only
+ load "action_view/tasks/dependencies.rake"
+ end
end
end
end
diff --git a/actionview/test/template/digestor_test.rb b/actionview/test/template/digestor_test.rb
index c2b8439df3..f0afcdb5ae 100644
--- a/actionview/test/template/digestor_test.rb
+++ b/actionview/test/template/digestor_test.rb
@@ -111,6 +111,18 @@ class TemplateDigestorTest < ActionView::TestCase
end
end
+ def test_logging_of_missing_template_for_dependencies
+ assert_logged "'messages/something_missing' file doesn't exist, so no dependencies" do
+ dependencies("messages/something_missing")
+ end
+ end
+
+ def test_logging_of_missing_template_for_nested_dependencies
+ assert_logged "'messages/something_missing' file doesn't exist, so no dependencies" do
+ nested_dependencies("messages/something_missing")
+ end
+ end
+
def test_nested_template_directory
assert_digest_difference("messages/show") do
change_template("messages/actions/_move")
@@ -298,6 +310,14 @@ class TemplateDigestorTest < ActionView::TestCase
ActionView::Digestor.digest({ name: template_name, finder: finder }.merge(options))
end
+ def dependencies(template_name)
+ ActionView::Digestor.new({ name: template_name, finder: finder }).dependencies
+ end
+
+ def nested_dependencies(template_name)
+ ActionView::Digestor.new({ name: template_name, finder: finder }).nested_dependencies
+ end
+
def finder
@finder ||= FixtureFinder.new
end
diff --git a/activemodel/bin/test b/activemodel/bin/test
new file mode 100755
index 0000000000..404cabba51
--- /dev/null
+++ b/activemodel/bin/test
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+COMPONENT_ROOT = File.expand_path("../../", __FILE__)
+require File.expand_path("../tools/test", COMPONENT_ROOT)
+exit Minitest.run(ARGV)
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 02b2096ef6..14906e441b 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,20 @@
+* Make `remove_foreign_key` reversible. Any foreign key options must be
+ specified, similar to `remove_column`.
+
+ *Aster Ryan*
+
+* Add `:enum_prefix`/`:enum_suffix` option to `enum` definition.
+
+ Fixes #17511 and #17415
+
+ *Igor Kapkov*
+
+* Correctly handle decimal arrays with defaults in the schema dumper.
+
+ Fixes #20515.
+
+ *Sean Griffin & jmondo*
+
* Deprecate the PG `:point` type in favor of a new one which will return
`Point` objects instead of an `Array`
diff --git a/activerecord/bin/test b/activerecord/bin/test
new file mode 100755
index 0000000000..f8adf2aabc
--- /dev/null
+++ b/activerecord/bin/test
@@ -0,0 +1,19 @@
+#!/usr/bin/env ruby
+COMPONENT_ROOT = File.expand_path("../../", __FILE__)
+require File.expand_path("../tools/test", COMPONENT_ROOT)
+module Minitest
+ def self.plugin_active_record_options(opts, options)
+ opts.separator ""
+ opts.separator "Active Record options:"
+ opts.on("-a", "--adapter [ADAPTER]",
+ "Run tests using a specific adapter (sqlite3, sqlite3_mem, mysql, mysql2, postgresql)") do |adapter|
+ ENV["ARCONN"] = adapter.strip
+ end
+
+ opts
+ end
+end
+
+Minitest.extensions.unshift 'active_record'
+
+exit Minitest.run(ARGV)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index dd7dcb9cdd..c8be038d76 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -777,7 +777,10 @@ module ActiveRecord
execute schema_creation.accept(at)
end
- # Removes the given foreign key from the table.
+ # Removes the given foreign key from the table. Any option parameters provided
+ # will be used to re-add the foreign key in case of a migration rollback.
+ # It is recommended that you provide any options used when creating the foreign
+ # key so that the migration can be reverted properly.
#
# Removes the foreign key on +accounts.branch_id+.
#
@@ -791,6 +794,7 @@ module ActiveRecord
#
# remove_foreign_key :accounts, name: :special_fk_name
#
+ # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
def remove_foreign_key(from_table, options_or_to_table = {})
return unless supports_foreign_keys?
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
index 3de794f797..25961a9869 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
@@ -45,6 +45,11 @@ module ActiveRecord
delimiter == other.delimiter
end
+ def type_cast_for_schema(value)
+ return super unless value.is_a?(::Array)
+ "[" + value.map { |v| subtype.type_cast_for_schema(v) }.join(", ") + "]"
+ end
+
private
def type_cast_array(value, method)
diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb
index 2b99899e42..ad33c84dbc 100644
--- a/activerecord/lib/active_record/enum.rb
+++ b/activerecord/lib/active_record/enum.rb
@@ -75,6 +75,22 @@ module ActiveRecord
#
# Conversation.where("status <> ?", Conversation.statuses[:archived])
#
+ # You can use <tt>:enum_prefix</tt>/<tt>:enum_suffix</tt> option then you need
+ # to define multiple enums with same values. If option value is <tt>true</tt>,
+ # the methods are prefixed/suffixed with the name of the enum.
+ #
+ # class Invoice < ActiveRecord::Base
+ # enum verification: [:done, :fail], enum_prefix: true
+ # end
+ #
+ # It is also possible to supply a custom prefix.
+ #
+ # class Invoice < ActiveRecord::Base
+ # enum verification: [:done, :fail], enum_prefix: :verification_status
+ # end
+ #
+ # Note that <tt>:enum_prefix</tt>/<tt>:enum_postfix</tt> are reserved keywords
+ # and can not be used as an enum name.
module Enum
def self.extended(base) # :nodoc:
@@ -121,6 +137,8 @@ module ActiveRecord
def enum(definitions)
klass = self
+ enum_prefix = definitions.delete(:enum_prefix)
+ enum_suffix = definitions.delete(:enum_suffix)
definitions.each do |name, values|
# statuses = { }
enum_values = ActiveSupport::HashWithIndifferentAccess.new
@@ -138,19 +156,31 @@ module ActiveRecord
_enum_methods_module.module_eval do
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
pairs.each do |value, i|
+ if enum_prefix == true
+ prefix = "#{name}_"
+ elsif enum_prefix
+ prefix = "#{enum_prefix}_"
+ end
+ if enum_suffix == true
+ suffix = "_#{name}"
+ elsif enum_suffix
+ suffix = "_#{enum_suffix}"
+ end
+
+ value_method_name = "#{prefix}#{value}#{suffix}"
enum_values[value] = i
# def active?() status == 0 end
- klass.send(:detect_enum_conflict!, name, "#{value}?")
- define_method("#{value}?") { self[name] == value.to_s }
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
+ define_method("#{value_method_name}?") { self[name] == value.to_s }
# def active!() update! status: :active end
- klass.send(:detect_enum_conflict!, name, "#{value}!")
- define_method("#{value}!") { update! name => value }
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
+ define_method("#{value_method_name}!") { update! name => value }
# scope :active, -> { where status: 0 }
- klass.send(:detect_enum_conflict!, name, value, true)
- klass.scope value, -> { klass.where name => value }
+ klass.send(:detect_enum_conflict!, name, value_method_name, true)
+ klass.scope value_method_name, -> { klass.where name => value }
end
end
defined_enums[name.to_s] = enum_values
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 7760fd33b7..738fab2bd9 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -668,7 +668,7 @@ module ActiveRecord
row[association.foreign_type] = $1
end
- fk_type = association.active_record.type_for_attribute(fk_name).type
+ fk_type = reflection_class.type_for_attribute(fk_name).type
row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
end
when :has_many
diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb
index 36256415df..ee4545ed71 100644
--- a/activerecord/lib/active_record/migration/command_recorder.rb
+++ b/activerecord/lib/active_record/migration/command_recorder.rb
@@ -184,6 +184,16 @@ module ActiveRecord
[:remove_foreign_key, [from_table, options]]
end
+ def invert_remove_foreign_key(args)
+ from_table, to_table, remove_options = args
+ raise ActiveRecord::IrreversibleMigration, "remove_foreign_key is only reversible if given a second table" if to_table.nil? || to_table.is_a?(Hash)
+
+ reversed_args = [from_table, to_table]
+ reversed_args << remove_options if remove_options
+
+ [:add_foreign_key, reversed_args]
+ end
+
# Forwards any missing method call to the \target.
def method_missing(method, *args, &block)
if @delegate.respond_to?(method)
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 437ba31711..0a6e4ac0bd 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -211,8 +211,7 @@ module ActiveRecord
def becomes(klass)
became = klass.new
became.instance_variable_set("@attributes", @attributes)
- changed_attributes = @changed_attributes if defined?(@changed_attributes)
- became.instance_variable_set("@changed_attributes", changed_attributes || {})
+ became.instance_variable_set("@changed_attributes", attributes_changed_by_setter)
became.instance_variable_set("@new_record", new_record?)
became.instance_variable_set("@destroyed", destroyed?)
became.instance_variable_set("@errors", errors)
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 5af64b717a..da6b8447d3 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -156,8 +156,8 @@ end_warning
ActiveSupport.on_load(:active_record) do
ActionDispatch::Reloader.send(hook) do
if ActiveRecord::Base.connected?
- ActiveRecord::Base.clear_reloadable_connections!
ActiveRecord::Base.clear_cache!
+ ActiveRecord::Base.clear_reloadable_connections!
end
end
end
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index c2567311bd..ba75ffa5a1 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -119,9 +119,9 @@ module ActiveRecord
end
def replace_named_bind_variables(statement, bind_vars) #:nodoc:
- statement.gsub(/(:?):([a-zA-Z]\w*)/) do
+ statement.gsub(/(:?):([a-zA-Z]\w*)/) do |match|
if $1 == ':' # skip postgresql casts
- $& # return the whole match
+ match # return the whole match
elsif bind_vars.include?(match = $2.to_sym)
replace_bind_variable(bind_vars[match])
else
diff --git a/activerecord/test/cases/adapters/mysql/active_schema_test.rb b/activerecord/test/cases/adapters/mysql/active_schema_test.rb
index 57eb5d0e18..f0fd95ac16 100644
--- a/activerecord/test/cases/adapters/mysql/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql/active_schema_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/connection_helper'
-class ActiveSchemaTest < ActiveRecord::TestCase
+class MysqlActiveSchemaTest < ActiveRecord::MysqlTestCase
include ConnectionHelper
def setup
diff --git a/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb b/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb
index 345122b1ad..98d44315dd 100644
--- a/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb
+++ b/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class MysqlCaseSensitivityTest < ActiveRecord::TestCase
+class MysqlCaseSensitivityTest < ActiveRecord::MysqlTestCase
class CollationTest < ActiveRecord::Base
end
diff --git a/activerecord/test/cases/adapters/mysql/charset_collation_test.rb b/activerecord/test/cases/adapters/mysql/charset_collation_test.rb
index c8dd49d00a..f2117a97e6 100644
--- a/activerecord/test/cases/adapters/mysql/charset_collation_test.rb
+++ b/activerecord/test/cases/adapters/mysql/charset_collation_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class CharsetCollationTest < ActiveRecord::TestCase
+class MysqlCharsetCollationTest < ActiveRecord::MysqlTestCase
include SchemaDumpingHelper
self.use_transactional_tests = false
diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb
index 9903cd3c0b..ddbc007b87 100644
--- a/activerecord/test/cases/adapters/mysql/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql/connection_test.rb
@@ -2,7 +2,7 @@ require "cases/helper"
require 'support/connection_helper'
require 'support/ddl_helper'
-class MysqlConnectionTest < ActiveRecord::TestCase
+class MysqlConnectionTest < ActiveRecord::MysqlTestCase
include ConnectionHelper
include DdlHelper
diff --git a/activerecord/test/cases/adapters/mysql/consistency_test.rb b/activerecord/test/cases/adapters/mysql/consistency_test.rb
index ae190b728d..743f6436e4 100644
--- a/activerecord/test/cases/adapters/mysql/consistency_test.rb
+++ b/activerecord/test/cases/adapters/mysql/consistency_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class MysqlConsistencyTest < ActiveRecord::TestCase
+class MysqlConsistencyTest < ActiveRecord::MysqlTestCase
self.use_transactional_tests = false
class Consistency < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/mysql/enum_test.rb b/activerecord/test/cases/adapters/mysql/enum_test.rb
index f4e7a3ef0a..ef8ee0a6e3 100644
--- a/activerecord/test/cases/adapters/mysql/enum_test.rb
+++ b/activerecord/test/cases/adapters/mysql/enum_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class MysqlEnumTest < ActiveRecord::TestCase
+class MysqlEnumTest < ActiveRecord::MysqlTestCase
class EnumTest < ActiveRecord::Base
end
diff --git a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
index 48ceef365e..b804cb45b9 100644
--- a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
@@ -4,7 +4,7 @@ require 'support/ddl_helper'
module ActiveRecord
module ConnectionAdapters
- class MysqlAdapterTest < ActiveRecord::TestCase
+ class MysqlAdapterTest < ActiveRecord::MysqlTestCase
include DdlHelper
def setup
diff --git a/activerecord/test/cases/adapters/mysql/quoting_test.rb b/activerecord/test/cases/adapters/mysql/quoting_test.rb
index a2206153e9..a296cf9d31 100644
--- a/activerecord/test/cases/adapters/mysql/quoting_test.rb
+++ b/activerecord/test/cases/adapters/mysql/quoting_test.rb
@@ -1,21 +1,15 @@
require "cases/helper"
-module ActiveRecord
- module ConnectionAdapters
- class MysqlAdapter
- class QuotingTest < ActiveRecord::TestCase
- def setup
- @conn = ActiveRecord::Base.connection
- end
+class MysqlQuotingTest < ActiveRecord::MysqlTestCase
+ def setup
+ @conn = ActiveRecord::Base.connection
+ end
- def test_type_cast_true
- assert_equal 1, @conn.type_cast(true)
- end
+ def test_type_cast_true
+ assert_equal 1, @conn.type_cast(true)
+ end
- def test_type_cast_false
- assert_equal 0, @conn.type_cast(false)
- end
- end
- end
+ def test_type_cast_false
+ assert_equal 0, @conn.type_cast(false)
end
end
diff --git a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb
index ec1c394f40..4ea1d9ad36 100644
--- a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb
+++ b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb
@@ -1,29 +1,29 @@
require "cases/helper"
-class Group < ActiveRecord::Base
- Group.table_name = 'group'
- belongs_to :select
- has_one :values
-end
+# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with
+# reserved word names (ie: group, order, values, etc...)
+class MysqlReservedWordTest < ActiveRecord::MysqlTestCase
+ class Group < ActiveRecord::Base
+ Group.table_name = 'group'
+ belongs_to :select
+ has_one :values
+ end
-class Select < ActiveRecord::Base
- Select.table_name = 'select'
- has_many :groups
-end
+ class Select < ActiveRecord::Base
+ Select.table_name = 'select'
+ has_many :groups
+ end
-class Values < ActiveRecord::Base
- Values.table_name = 'values'
-end
+ class Values < ActiveRecord::Base
+ Values.table_name = 'values'
+ end
-class Distinct < ActiveRecord::Base
- Distinct.table_name = 'distinct'
- has_and_belongs_to_many :selects
- has_many :values, :through => :groups
-end
+ class Distinct < ActiveRecord::Base
+ Distinct.table_name = 'distinct'
+ has_and_belongs_to_many :selects
+ has_many :values, :through => :groups
+ end
-# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with
-# reserved word names (ie: group, order, values, etc...)
-class MysqlReservedWordTest < ActiveRecord::TestCase
def setup
@connection = ActiveRecord::Base.connection
diff --git a/activerecord/test/cases/adapters/mysql/schema_test.rb b/activerecord/test/cases/adapters/mysql/schema_test.rb
index b7f9c2ce84..2e18f609fd 100644
--- a/activerecord/test/cases/adapters/mysql/schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql/schema_test.rb
@@ -4,7 +4,7 @@ require 'models/comment'
module ActiveRecord
module ConnectionAdapters
- class MysqlSchemaTest < ActiveRecord::TestCase
+ class MysqlSchemaTest < ActiveRecord::MysqlTestCase
fixtures :posts
def setup
diff --git a/activerecord/test/cases/adapters/mysql/sp_test.rb b/activerecord/test/cases/adapters/mysql/sp_test.rb
index 3ca2917ca4..a3d5110032 100644
--- a/activerecord/test/cases/adapters/mysql/sp_test.rb
+++ b/activerecord/test/cases/adapters/mysql/sp_test.rb
@@ -1,11 +1,11 @@
require "cases/helper"
require 'models/topic'
-class StoredProcedureTest < ActiveRecord::TestCase
+class StoredProcedureTest < ActiveRecord::MysqlTestCase
fixtures :topics
# Test that MySQL allows multiple results for stored procedures
- if Mysql.const_defined?(:CLIENT_MULTI_RESULTS)
+ if defined?(Mysql) && Mysql.const_defined?(:CLIENT_MULTI_RESULTS)
def test_multi_results_from_find_by_sql
topics = Topic.find_by_sql 'CALL topics();'
assert_equal 1, topics.size
diff --git a/activerecord/test/cases/adapters/mysql/sql_types_test.rb b/activerecord/test/cases/adapters/mysql/sql_types_test.rb
index 1ddb1b91c9..25b28de7f0 100644
--- a/activerecord/test/cases/adapters/mysql/sql_types_test.rb
+++ b/activerecord/test/cases/adapters/mysql/sql_types_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class SqlTypesTest < ActiveRecord::TestCase
+class MysqlSqlTypesTest < ActiveRecord::MysqlTestCase
def test_binary_types
assert_equal 'varbinary(64)', type_to_sql(:binary, 64)
assert_equal 'varbinary(4095)', type_to_sql(:binary, 4095)
diff --git a/activerecord/test/cases/adapters/mysql/statement_pool_test.rb b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb
index 209a0cf464..6be36566de 100644
--- a/activerecord/test/cases/adapters/mysql/statement_pool_test.rb
+++ b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb
@@ -1,23 +1,19 @@
require 'cases/helper'
-module ActiveRecord::ConnectionAdapters
- class MysqlAdapter
- class StatementPoolTest < ActiveRecord::TestCase
- if Process.respond_to?(:fork)
- def test_cache_is_per_pid
- cache = StatementPool.new nil, 10
- cache['foo'] = 'bar'
- assert_equal 'bar', cache['foo']
+class MysqlStatementPoolTest < ActiveRecord::MysqlTestCase
+ if Process.respond_to?(:fork)
+ def test_cache_is_per_pid
+ cache = ActiveRecord::ConnectionAdapters::MysqlAdapter::StatementPool.new nil, 10
+ cache['foo'] = 'bar'
+ assert_equal 'bar', cache['foo']
- pid = fork {
- lookup = cache['foo'];
- exit!(!lookup)
- }
+ pid = fork {
+ lookup = cache['foo'];
+ exit!(!lookup)
+ }
- Process.waitpid pid
- assert $?.success?, 'process should exit successfully'
- end
- end
+ Process.waitpid pid
+ assert $?.success?, 'process should exit successfully'
end
end
end
diff --git a/activerecord/test/cases/adapters/mysql/table_options_test.rb b/activerecord/test/cases/adapters/mysql/table_options_test.rb
index 0e5b0e8aec..99df6d6cba 100644
--- a/activerecord/test/cases/adapters/mysql/table_options_test.rb
+++ b/activerecord/test/cases/adapters/mysql/table_options_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class MysqlTableOptionsTest < ActiveRecord::TestCase
+class MysqlTableOptionsTest < ActiveRecord::MysqlTestCase
include SchemaDumpingHelper
def setup
diff --git a/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb
index e9edc53f93..ed9398a918 100644
--- a/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb
+++ b/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class UnsignedTypeTest < ActiveRecord::TestCase
+class MysqlUnsignedTypeTest < ActiveRecord::MysqlTestCase
self.use_transactional_tests = false
class UnsignedType < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
index 0ea556d4fa..6558d60aa1 100644
--- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/connection_helper'
-class ActiveSchemaTest < ActiveRecord::TestCase
+class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
include ConnectionHelper
def setup
diff --git a/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb b/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb
index 5e8065d80d..abdf3dbf5b 100644
--- a/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb
@@ -4,7 +4,7 @@ require 'models/topic'
module ActiveRecord
module ConnectionAdapters
class Mysql2Adapter
- class BindParameterTest < ActiveRecord::TestCase
+ class BindParameterTest < ActiveRecord::Mysql2TestCase
fixtures :topics
def test_update_question_marks
diff --git a/activerecord/test/cases/adapters/mysql2/boolean_test.rb b/activerecord/test/cases/adapters/mysql2/boolean_test.rb
index 0d81dd6eee..8575df9e43 100644
--- a/activerecord/test/cases/adapters/mysql2/boolean_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/boolean_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class Mysql2BooleanTest < ActiveRecord::TestCase
+class Mysql2BooleanTest < ActiveRecord::Mysql2TestCase
self.use_transactional_tests = false
class BooleanType < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
index ccf3d84a44..963116f08a 100644
--- a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class Mysql2CaseSensitivityTest < ActiveRecord::TestCase
+class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase
class CollationTest < ActiveRecord::Base
end
diff --git a/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb b/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb
index c8dd49d00a..4fd34def15 100644
--- a/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class CharsetCollationTest < ActiveRecord::TestCase
+class Mysql2CharsetCollationTest < ActiveRecord::Mysql2TestCase
include SchemaDumpingHelper
self.use_transactional_tests = false
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index c4e0278c89..000bcadebe 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/connection_helper'
-class MysqlConnectionTest < ActiveRecord::TestCase
+class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
include ConnectionHelper
fixtures :comments
diff --git a/activerecord/test/cases/adapters/mysql2/enum_test.rb b/activerecord/test/cases/adapters/mysql2/enum_test.rb
index 6dd9a5ec87..bd732b5eca 100644
--- a/activerecord/test/cases/adapters/mysql2/enum_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/enum_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class Mysql2EnumTest < ActiveRecord::TestCase
+class Mysql2EnumTest < ActiveRecord::Mysql2TestCase
class EnumTest < ActiveRecord::Base
end
diff --git a/activerecord/test/cases/adapters/mysql2/explain_test.rb b/activerecord/test/cases/adapters/mysql2/explain_test.rb
index 2b01d941b8..4fc7414b18 100644
--- a/activerecord/test/cases/adapters/mysql2/explain_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/explain_test.rb
@@ -5,7 +5,7 @@ require 'models/computer'
module ActiveRecord
module ConnectionAdapters
class Mysql2Adapter
- class ExplainTest < ActiveRecord::TestCase
+ class ExplainTest < ActiveRecord::Mysql2TestCase
fixtures :developers
def test_explain_for_one_query
diff --git a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
index 799e60a683..ffb4e2c5cf 100644
--- a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
@@ -1,29 +1,29 @@
require "cases/helper"
-class Group < ActiveRecord::Base
- Group.table_name = 'group'
- belongs_to :select
- has_one :values
-end
+# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with
+# reserved word names (ie: group, order, values, etc...)
+class Mysql2ReservedWordTest < ActiveRecord::Mysql2TestCase
+ class Group < ActiveRecord::Base
+ Group.table_name = 'group'
+ belongs_to :select
+ has_one :values
+ end
-class Select < ActiveRecord::Base
- Select.table_name = 'select'
- has_many :groups
-end
+ class Select < ActiveRecord::Base
+ Select.table_name = 'select'
+ has_many :groups
+ end
-class Values < ActiveRecord::Base
- Values.table_name = 'values'
-end
+ class Values < ActiveRecord::Base
+ Values.table_name = 'values'
+ end
-class Distinct < ActiveRecord::Base
- Distinct.table_name = 'distinct'
- has_and_belongs_to_many :selects
- has_many :values, :through => :groups
-end
+ class Distinct < ActiveRecord::Base
+ Distinct.table_name = 'distinct'
+ has_and_belongs_to_many :selects
+ has_many :values, :through => :groups
+ end
-# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with
-# reserved word names (ie: group, order, values, etc...)
-class MysqlReservedWordTest < ActiveRecord::TestCase
def setup
@connection = ActiveRecord::Base.connection
diff --git a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
index 417ccf6d11..396f235e77 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
@@ -1,47 +1,42 @@
require "cases/helper"
-module ActiveRecord
- module ConnectionAdapters
- class AbstractMysqlAdapter
- class SchemaMigrationsTest < ActiveRecord::TestCase
- def test_renaming_index_on_foreign_key
- connection.add_index "engines", "car_id"
- connection.add_foreign_key :engines, :cars, name: "fk_engines_cars"
-
- connection.rename_index("engines", "index_engines_on_car_id", "idx_renamed")
- assert_equal ["idx_renamed"], connection.indexes("engines").map(&:name)
- ensure
- connection.remove_foreign_key :engines, name: "fk_engines_cars"
- end
-
- def test_initializes_schema_migrations_for_encoding_utf8mb4
- smtn = ActiveRecord::Migrator.schema_migrations_table_name
- connection.drop_table smtn, if_exists: true
-
- database_name = connection.current_database
- database_info = connection.select_one("SELECT * FROM information_schema.schemata WHERE schema_name = '#{database_name}'")
-
- original_charset = database_info["DEFAULT_CHARACTER_SET_NAME"]
- original_collation = database_info["DEFAULT_COLLATION_NAME"]
-
- execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET utf8mb4")
-
- connection.initialize_schema_migrations_table
-
- assert connection.column_exists?(smtn, :version, :string, limit: AbstractMysqlAdapter::MAX_INDEX_LENGTH_FOR_CHARSETS_OF_4BYTES_MAXLEN)
- ensure
- execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET #{original_charset} COLLATE #{original_collation}")
- end
-
- private
- def connection
- @connection ||= ActiveRecord::Base.connection
- end
-
- def execute(sql)
- connection.execute(sql)
- end
- end
- end
+class SchemaMigrationsTest < ActiveRecord::Mysql2TestCase
+ def test_renaming_index_on_foreign_key
+ connection.add_index "engines", "car_id"
+ connection.add_foreign_key :engines, :cars, name: "fk_engines_cars"
+
+ connection.rename_index("engines", "index_engines_on_car_id", "idx_renamed")
+ assert_equal ["idx_renamed"], connection.indexes("engines").map(&:name)
+ ensure
+ connection.remove_foreign_key :engines, name: "fk_engines_cars"
+ end
+
+ def test_initializes_schema_migrations_for_encoding_utf8mb4
+ smtn = ActiveRecord::Migrator.schema_migrations_table_name
+ connection.drop_table smtn, if_exists: true
+
+ database_name = connection.current_database
+ database_info = connection.select_one("SELECT * FROM information_schema.schemata WHERE schema_name = '#{database_name}'")
+
+ original_charset = database_info["DEFAULT_CHARACTER_SET_NAME"]
+ original_collation = database_info["DEFAULT_COLLATION_NAME"]
+
+ execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET utf8mb4")
+
+ connection.initialize_schema_migrations_table
+
+ limit = ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MAX_INDEX_LENGTH_FOR_CHARSETS_OF_4BYTES_MAXLEN
+ assert connection.column_exists?(smtn, :version, :string, limit: limit)
+ ensure
+ execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET #{original_charset} COLLATE #{original_collation}")
+ end
+
+ private
+ def connection
+ @connection ||= ActiveRecord::Base.connection
+ end
+
+ def execute(sql)
+ connection.execute(sql)
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb
index 47707b7d4f..880a2123d2 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb
@@ -4,7 +4,7 @@ require 'models/comment'
module ActiveRecord
module ConnectionAdapters
- class Mysql2SchemaTest < ActiveRecord::TestCase
+ class Mysql2SchemaTest < ActiveRecord::Mysql2TestCase
fixtures :posts
def setup
diff --git a/activerecord/test/cases/adapters/mysql2/sql_types_test.rb b/activerecord/test/cases/adapters/mysql2/sql_types_test.rb
index 1ddb1b91c9..ae505d29c9 100644
--- a/activerecord/test/cases/adapters/mysql2/sql_types_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/sql_types_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class SqlTypesTest < ActiveRecord::TestCase
+class Mysql2SqlTypesTest < ActiveRecord::Mysql2TestCase
def test_binary_types
assert_equal 'varbinary(64)', type_to_sql(:binary, 64)
assert_equal 'varbinary(4095)', type_to_sql(:binary, 4095)
diff --git a/activerecord/test/cases/adapters/mysql2/table_options_test.rb b/activerecord/test/cases/adapters/mysql2/table_options_test.rb
index 0e5b0e8aec..af121ee7d9 100644
--- a/activerecord/test/cases/adapters/mysql2/table_options_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/table_options_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class MysqlTableOptionsTest < ActiveRecord::TestCase
+class Mysql2TableOptionsTest < ActiveRecord::Mysql2TestCase
include SchemaDumpingHelper
def setup
diff --git a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
index e9edc53f93..9e06db2519 100644
--- a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class UnsignedTypeTest < ActiveRecord::TestCase
+class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase
self.use_transactional_tests = false
class UnsignedType < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
index 3808db5141..dc7ba314c6 100644
--- a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
@@ -1,6 +1,6 @@
require 'cases/helper'
-class PostgresqlActiveSchemaTest < ActiveRecord::TestCase
+class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase
def setup
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
def execute(sql, name = nil) sql end
diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb
index 6edbd9c3a6..380a90d765 100644
--- a/activerecord/test/cases/adapters/postgresql/array_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/array_test.rb
@@ -1,10 +1,9 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class PostgresqlArrayTest < ActiveRecord::TestCase
+class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
include InTimeZone
- OID = ActiveRecord::ConnectionAdapters::PostgreSQL::OID
class PgArray < ActiveRecord::Base
self.table_name = 'pg_arrays'
@@ -212,8 +211,9 @@ class PostgresqlArrayTest < ActiveRecord::TestCase
def test_quoting_non_standard_delimiters
strings = ["hello,", "world;"]
- comma_delim = OID::Array.new(ActiveRecord::Type::String.new, ',')
- semicolon_delim = OID::Array.new(ActiveRecord::Type::String.new, ';')
+ oid = ActiveRecord::ConnectionAdapters::PostgreSQL::OID
+ comma_delim = oid::Array.new(ActiveRecord::Type::String.new, ',')
+ semicolon_delim = oid::Array.new(ActiveRecord::Type::String.new, ';')
assert_equal %({"hello,",world;}), comma_delim.serialize(strings)
assert_equal %({hello,;"world;"}), semicolon_delim.serialize(strings)
diff --git a/activerecord/test/cases/adapters/postgresql/bit_string_test.rb b/activerecord/test/cases/adapters/postgresql/bit_string_test.rb
index 1a5ff4316c..6f72fa6e0f 100644
--- a/activerecord/test/cases/adapters/postgresql/bit_string_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/bit_string_test.rb
@@ -2,7 +2,7 @@ require "cases/helper"
require 'support/connection_helper'
require 'support/schema_dumping_helper'
-class PostgresqlBitStringTest < ActiveRecord::TestCase
+class PostgresqlBitStringTest < ActiveRecord::PostgreSQLTestCase
include ConnectionHelper
include SchemaDumpingHelper
diff --git a/activerecord/test/cases/adapters/postgresql/bytea_test.rb b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
index 16db5ab83d..b6bb1929e6 100644
--- a/activerecord/test/cases/adapters/postgresql/bytea_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class PostgresqlByteaTest < ActiveRecord::TestCase
+class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase
class ByteaDataType < ActiveRecord::Base
self.table_name = 'bytea_data_type'
end
diff --git a/activerecord/test/cases/adapters/postgresql/change_schema_test.rb b/activerecord/test/cases/adapters/postgresql/change_schema_test.rb
index 5a9796887c..bc12df668d 100644
--- a/activerecord/test/cases/adapters/postgresql/change_schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/change_schema_test.rb
@@ -2,7 +2,7 @@ require 'cases/helper'
module ActiveRecord
class Migration
- class PGChangeSchemaTest < ActiveRecord::TestCase
+ class PGChangeSchemaTest < ActiveRecord::PostgreSQLTestCase
attr_reader :connection
def setup
diff --git a/activerecord/test/cases/adapters/postgresql/cidr_test.rb b/activerecord/test/cases/adapters/postgresql/cidr_test.rb
index 6cb11d17b4..52f2a0096c 100644
--- a/activerecord/test/cases/adapters/postgresql/cidr_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/cidr_test.rb
@@ -3,8 +3,8 @@ require "ipaddr"
module ActiveRecord
module ConnectionAdapters
- class PostgreSQLAdapter
- class CidrTest < ActiveRecord::TestCase
+ class PostgreSQLAdapter < AbstractAdapter
+ class CidrTest < ActiveRecord::PostgreSQLTestCase
test "type casting IPAddr for database" do
type = OID::Cidr.new
ip = IPAddr.new("255.0.0.0/8")
diff --git a/activerecord/test/cases/adapters/postgresql/citext_test.rb b/activerecord/test/cases/adapters/postgresql/citext_test.rb
index f706847890..bd62041e79 100644
--- a/activerecord/test/cases/adapters/postgresql/citext_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/citext_test.rb
@@ -2,7 +2,7 @@ require 'cases/helper'
require 'support/schema_dumping_helper'
if ActiveRecord::Base.connection.supports_extensions?
- class PostgresqlCitextTest < ActiveRecord::TestCase
+ class PostgresqlCitextTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class Citext < ActiveRecord::Base
self.table_name = 'citexts'
diff --git a/activerecord/test/cases/adapters/postgresql/collation_test.rb b/activerecord/test/cases/adapters/postgresql/collation_test.rb
index 17ef5f304c..8470329c35 100644
--- a/activerecord/test/cases/adapters/postgresql/collation_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/collation_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class PostgresqlCollationTest < ActiveRecord::TestCase
+class PostgresqlCollationTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
def setup
diff --git a/activerecord/test/cases/adapters/postgresql/composite_test.rb b/activerecord/test/cases/adapters/postgresql/composite_test.rb
index 16e3f90a47..1de87e5f01 100644
--- a/activerecord/test/cases/adapters/postgresql/composite_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/composite_test.rb
@@ -40,7 +40,7 @@ end
# "unknown OID 5653508: failed to recognize type of 'address'. It will be treated as String."
# To take full advantage of composite types, we suggest you register your own +OID::Type+.
# See PostgresqlCompositeWithCustomOIDTest
-class PostgresqlCompositeTest < ActiveRecord::TestCase
+class PostgresqlCompositeTest < ActiveRecord::PostgreSQLTestCase
include PostgresqlCompositeBehavior
def test_column
@@ -77,7 +77,7 @@ class PostgresqlCompositeTest < ActiveRecord::TestCase
end
end
-class PostgresqlCompositeWithCustomOIDTest < ActiveRecord::TestCase
+class PostgresqlCompositeWithCustomOIDTest < ActiveRecord::PostgreSQLTestCase
include PostgresqlCompositeBehavior
class FullAddressType < ActiveRecord::Type::Value
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index 55ad76c8c0..820d41e13b 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -2,7 +2,7 @@ require "cases/helper"
require 'support/connection_helper'
module ActiveRecord
- class PostgresqlConnectionTest < ActiveRecord::TestCase
+ class PostgresqlConnectionTest < ActiveRecord::PostgreSQLTestCase
include ConnectionHelper
class NonExistentTable < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
index 2c14252ae4..232c25cb3b 100644
--- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
@@ -11,7 +11,7 @@ end
class PostgresqlLtree < ActiveRecord::Base
end
-class PostgresqlDataTypeTest < ActiveRecord::TestCase
+class PostgresqlDataTypeTest < ActiveRecord::PostgreSQLTestCase
self.use_transactional_tests = false
def setup
@@ -69,7 +69,7 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase
end
end
-class PostgresqlInternalDataTypeTest < ActiveRecord::TestCase
+class PostgresqlInternalDataTypeTest < ActiveRecord::PostgreSQLTestCase
include DdlHelper
setup do
diff --git a/activerecord/test/cases/adapters/postgresql/domain_test.rb b/activerecord/test/cases/adapters/postgresql/domain_test.rb
index 26e064c937..6102ddacd1 100644
--- a/activerecord/test/cases/adapters/postgresql/domain_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/domain_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/connection_helper'
-class PostgresqlDomainTest < ActiveRecord::TestCase
+class PostgresqlDomainTest < ActiveRecord::PostgreSQLTestCase
include ConnectionHelper
class PostgresqlDomain < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/postgresql/enum_test.rb b/activerecord/test/cases/adapters/postgresql/enum_test.rb
index ed084483bc..6816a6514b 100644
--- a/activerecord/test/cases/adapters/postgresql/enum_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/enum_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/connection_helper'
-class PostgresqlEnumTest < ActiveRecord::TestCase
+class PostgresqlEnumTest < ActiveRecord::PostgreSQLTestCase
include ConnectionHelper
class PostgresqlEnum < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/postgresql/explain_test.rb b/activerecord/test/cases/adapters/postgresql/explain_test.rb
index 6ffb4c9f33..4d0fd640aa 100644
--- a/activerecord/test/cases/adapters/postgresql/explain_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/explain_test.rb
@@ -2,25 +2,19 @@ require "cases/helper"
require 'models/developer'
require 'models/computer'
-module ActiveRecord
- module ConnectionAdapters
- class PostgreSQLAdapter
- class ExplainTest < ActiveRecord::TestCase
- fixtures :developers
+class PostgreSQLExplainTest < ActiveRecord::PostgreSQLTestCase
+ fixtures :developers
- def test_explain_for_one_query
- explain = Developer.where(:id => 1).explain
- assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = $1), explain
- assert_match %(QUERY PLAN), explain
- end
+ def test_explain_for_one_query
+ explain = Developer.where(:id => 1).explain
+ assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = $1), explain
+ assert_match %(QUERY PLAN), explain
+ end
- def test_explain_with_eager_loading
- explain = Developer.where(:id => 1).includes(:audit_logs).explain
- assert_match %(QUERY PLAN), explain
- assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = $1), explain
- assert_match %(EXPLAIN for: SELECT "audit_logs".* FROM "audit_logs" WHERE "audit_logs"."developer_id" = 1), explain
- end
- end
- end
+ def test_explain_with_eager_loading
+ explain = Developer.where(:id => 1).includes(:audit_logs).explain
+ assert_match %(QUERY PLAN), explain
+ assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = $1), explain
+ assert_match %(EXPLAIN for: SELECT "audit_logs".* FROM "audit_logs" WHERE "audit_logs"."developer_id" = 1), explain
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
index 06d427f464..9cfc133308 100644
--- a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class PostgresqlExtensionMigrationTest < ActiveRecord::TestCase
+class PostgresqlExtensionMigrationTest < ActiveRecord::PostgreSQLTestCase
self.use_transactional_tests = false
class EnableHstore < ActiveRecord::Migration
diff --git a/activerecord/test/cases/adapters/postgresql/full_text_test.rb b/activerecord/test/cases/adapters/postgresql/full_text_test.rb
index b83063c94e..bde7513339 100644
--- a/activerecord/test/cases/adapters/postgresql/full_text_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/full_text_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class PostgresqlFullTextTest < ActiveRecord::TestCase
+class PostgresqlFullTextTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class Tsvector < ActiveRecord::Base; end
diff --git a/activerecord/test/cases/adapters/postgresql/geometric_test.rb b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
index 7057848eb0..0baf985654 100644
--- a/activerecord/test/cases/adapters/postgresql/geometric_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
@@ -2,7 +2,7 @@ require "cases/helper"
require 'support/connection_helper'
require 'support/schema_dumping_helper'
-class PostgresqlPointTest < ActiveRecord::TestCase
+class PostgresqlPointTest < ActiveRecord::PostgreSQLTestCase
include ConnectionHelper
include SchemaDumpingHelper
@@ -166,7 +166,7 @@ class PostgresqlPointTest < ActiveRecord::TestCase
end
end
-class PostgresqlGeometricTest < ActiveRecord::TestCase
+class PostgresqlGeometricTest < ActiveRecord::PostgreSQLTestCase
class PostgresqlGeometric < ActiveRecord::Base; end
setup do
diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
index ad9dd311a6..6a2d501646 100644
--- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
@@ -2,7 +2,7 @@ require "cases/helper"
require 'support/schema_dumping_helper'
if ActiveRecord::Base.connection.supports_extensions?
- class PostgresqlHstoreTest < ActiveRecord::TestCase
+ class PostgresqlHstoreTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class Hstore < ActiveRecord::Base
self.table_name = 'hstores'
diff --git a/activerecord/test/cases/adapters/postgresql/infinity_test.rb b/activerecord/test/cases/adapters/postgresql/infinity_test.rb
index d9d7832094..bfda933fa4 100644
--- a/activerecord/test/cases/adapters/postgresql/infinity_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/infinity_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class PostgresqlInfinityTest < ActiveRecord::TestCase
+class PostgresqlInfinityTest < ActiveRecord::PostgreSQLTestCase
include InTimeZone
class PostgresqlInfinity < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/postgresql/integer_test.rb b/activerecord/test/cases/adapters/postgresql/integer_test.rb
index 679a0fc7b3..b4e55964b9 100644
--- a/activerecord/test/cases/adapters/postgresql/integer_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/integer_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require "active_support/core_ext/numeric/bytes"
-class PostgresqlIntegerTest < ActiveRecord::TestCase
+class PostgresqlIntegerTest < ActiveRecord::PostgreSQLTestCase
class PgInteger < ActiveRecord::Base
end
diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb
index 6878516aeb..f242f32496 100644
--- a/activerecord/test/cases/adapters/postgresql/json_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/json_test.rb
@@ -188,7 +188,7 @@ module PostgresqlJSONSharedTestCases
end
end
-class PostgresqlJSONTest < ActiveRecord::TestCase
+class PostgresqlJSONTest < ActiveRecord::PostgreSQLTestCase
include PostgresqlJSONSharedTestCases
def column_type
@@ -196,7 +196,7 @@ class PostgresqlJSONTest < ActiveRecord::TestCase
end
end
-class PostgresqlJSONBTest < ActiveRecord::TestCase
+class PostgresqlJSONBTest < ActiveRecord::PostgreSQLTestCase
include PostgresqlJSONSharedTestCases
def column_type
diff --git a/activerecord/test/cases/adapters/postgresql/ltree_test.rb b/activerecord/test/cases/adapters/postgresql/ltree_test.rb
index ce0ad16557..56516c82b4 100644
--- a/activerecord/test/cases/adapters/postgresql/ltree_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/ltree_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class PostgresqlLtreeTest < ActiveRecord::TestCase
+class PostgresqlLtreeTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class Ltree < ActiveRecord::Base
self.table_name = 'ltrees'
diff --git a/activerecord/test/cases/adapters/postgresql/money_test.rb b/activerecord/test/cases/adapters/postgresql/money_test.rb
index cedd399380..e3670c203d 100644
--- a/activerecord/test/cases/adapters/postgresql/money_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/money_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class PostgresqlMoneyTest < ActiveRecord::TestCase
+class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class PostgresqlMoney < ActiveRecord::Base; end
diff --git a/activerecord/test/cases/adapters/postgresql/network_test.rb b/activerecord/test/cases/adapters/postgresql/network_test.rb
index 033695518e..fe6ee4e2d9 100644
--- a/activerecord/test/cases/adapters/postgresql/network_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/network_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class PostgresqlNetworkTest < ActiveRecord::TestCase
+class PostgresqlNetworkTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class PostgresqlNetworkAddress < ActiveRecord::Base; end
diff --git a/activerecord/test/cases/adapters/postgresql/numbers_test.rb b/activerecord/test/cases/adapters/postgresql/numbers_test.rb
index d8e01e3b89..ba7e7dc9a3 100644
--- a/activerecord/test/cases/adapters/postgresql/numbers_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/numbers_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class PostgresqlNumberTest < ActiveRecord::TestCase
+class PostgresqlNumberTest < ActiveRecord::PostgreSQLTestCase
class PostgresqlNumber < ActiveRecord::Base; end
setup do
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index 9a1b889d4d..6e6850c4a9 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -4,7 +4,7 @@ require 'support/connection_helper'
module ActiveRecord
module ConnectionAdapters
- class PostgreSQLAdapterTest < ActiveRecord::TestCase
+ class PostgreSQLAdapterTest < ActiveRecord::PostgreSQLTestCase
include DdlHelper
include ConnectionHelper
diff --git a/activerecord/test/cases/adapters/postgresql/quoting_test.rb b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
index e4420d9d13..5e6f4dbbb8 100644
--- a/activerecord/test/cases/adapters/postgresql/quoting_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
@@ -4,7 +4,7 @@ require 'ipaddr'
module ActiveRecord
module ConnectionAdapters
class PostgreSQLAdapter
- class QuotingTest < ActiveRecord::TestCase
+ class QuotingTest < ActiveRecord::PostgreSQLTestCase
def setup
@conn = ActiveRecord::Base.connection
end
diff --git a/activerecord/test/cases/adapters/postgresql/range_test.rb b/activerecord/test/cases/adapters/postgresql/range_test.rb
index bbf96278b0..02b1083430 100644
--- a/activerecord/test/cases/adapters/postgresql/range_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/range_test.rb
@@ -1,12 +1,12 @@
require "cases/helper"
require 'support/connection_helper'
-if ActiveRecord::Base.connection.supports_ranges?
+if ActiveRecord::Base.connection.respond_to?(:supports_ranges?) && ActiveRecord::Base.connection.supports_ranges?
class PostgresqlRange < ActiveRecord::Base
self.table_name = "postgresql_ranges"
end
- class PostgresqlRangeTest < ActiveRecord::TestCase
+ class PostgresqlRangeTest < ActiveRecord::PostgreSQLTestCase
self.use_transactional_tests = false
include ConnectionHelper
diff --git a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
index 7200ed2771..c895ab9db5 100644
--- a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
@@ -1,7 +1,7 @@
require 'cases/helper'
require 'support/connection_helper'
-class PostgreSQLReferentialIntegrityTest < ActiveRecord::TestCase
+class PostgreSQLReferentialIntegrityTest < ActiveRecord::PostgreSQLTestCase
self.use_transactional_tests = false
include ConnectionHelper
diff --git a/activerecord/test/cases/adapters/postgresql/rename_table_test.rb b/activerecord/test/cases/adapters/postgresql/rename_table_test.rb
index f507328868..bd64bae308 100644
--- a/activerecord/test/cases/adapters/postgresql/rename_table_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/rename_table_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class PostgresqlRenameTableTest < ActiveRecord::TestCase
+class PostgresqlRenameTableTest < ActiveRecord::PostgreSQLTestCase
def setup
@connection = ActiveRecord::Base.connection
@connection.create_table :before_rename, force: true
diff --git a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
index 359a45bbd1..fa6584eae5 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
@@ -3,7 +3,7 @@ require "cases/helper"
class SchemaThing < ActiveRecord::Base
end
-class SchemaAuthorizationTest < ActiveRecord::TestCase
+class SchemaAuthorizationTest < ActiveRecord::PostgreSQLTestCase
self.use_transactional_tests = false
TABLE_NAME = 'schema_things'
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index f925dcad97..9aba2b5976 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -2,7 +2,7 @@ require "cases/helper"
require 'models/default'
require 'support/schema_dumping_helper'
-class SchemaTest < ActiveRecord::TestCase
+class SchemaTest < ActiveRecord::PostgreSQLTestCase
self.use_transactional_tests = false
SCHEMA_NAME = 'test_schema'
@@ -441,7 +441,7 @@ class SchemaTest < ActiveRecord::TestCase
end
end
-class SchemaForeignKeyTest < ActiveRecord::TestCase
+class SchemaForeignKeyTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
setup do
@@ -466,7 +466,7 @@ class SchemaForeignKeyTest < ActiveRecord::TestCase
end
end
-class DefaultsUsingMultipleSchemasAndDomainTest < ActiveSupport::TestCase
+class DefaultsUsingMultipleSchemasAndDomainTest < ActiveRecord::PostgreSQLTestCase
setup do
@connection = ActiveRecord::Base.connection
@connection.execute "DROP SCHEMA IF EXISTS schema_1 CASCADE"
diff --git a/activerecord/test/cases/adapters/postgresql/serial_test.rb b/activerecord/test/cases/adapters/postgresql/serial_test.rb
index 458a8dae6c..7d30db247b 100644
--- a/activerecord/test/cases/adapters/postgresql/serial_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/serial_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class PostgresqlSerialTest < ActiveRecord::TestCase
+class PostgresqlSerialTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class PostgresqlSerial < ActiveRecord::Base; end
@@ -30,7 +30,7 @@ class PostgresqlSerialTest < ActiveRecord::TestCase
end
end
-class PostgresqlBigSerialTest < ActiveRecord::TestCase
+class PostgresqlBigSerialTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class PostgresqlBigSerial < ActiveRecord::Base; end
diff --git a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
index 1497b0abc7..5aab246c99 100644
--- a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
@@ -13,7 +13,7 @@ module ActiveRecord
end
end
- class StatementPoolTest < ActiveRecord::TestCase
+ class StatementPoolTest < ActiveRecord::PostgreSQLTestCase
if Process.respond_to?(:fork)
def test_cache_is_per_pid
cache = StatementPool.new nil, 10
diff --git a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
index a639f98272..4c4866b46b 100644
--- a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
@@ -2,7 +2,7 @@ require 'cases/helper'
require 'models/developer'
require 'models/topic'
-class PostgresqlTimestampTest < ActiveRecord::TestCase
+class PostgresqlTimestampTest < ActiveRecord::PostgreSQLTestCase
class PostgresqlTimestampWithZone < ActiveRecord::Base; end
self.use_transactional_tests = false
@@ -43,7 +43,7 @@ class PostgresqlTimestampTest < ActiveRecord::TestCase
end
end
-class TimestampTest < ActiveRecord::TestCase
+class PostgresqlTimestampFixtureTest < ActiveRecord::PostgreSQLTestCase
fixtures :topics
def test_group_by_date
diff --git a/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb b/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb
index c0907b8f21..77a99ca778 100644
--- a/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb
@@ -1,6 +1,6 @@
require 'cases/helper'
-class PostgresqlTypeLookupTest < ActiveRecord::TestCase
+class PostgresqlTypeLookupTest < ActiveRecord::PostgreSQLTestCase
setup do
@connection = ActiveRecord::Base.connection
end
diff --git a/activerecord/test/cases/adapters/postgresql/utils_test.rb b/activerecord/test/cases/adapters/postgresql/utils_test.rb
index 3fdb6888d9..095c1826e5 100644
--- a/activerecord/test/cases/adapters/postgresql/utils_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/utils_test.rb
@@ -1,6 +1,7 @@
require 'cases/helper'
+require 'active_record/connection_adapters/postgresql/utils'
-class PostgreSQLUtilsTest < ActiveSupport::TestCase
+class PostgreSQLUtilsTest < ActiveRecord::PostgreSQLTestCase
Name = ActiveRecord::ConnectionAdapters::PostgreSQL::Name
include ActiveRecord::ConnectionAdapters::PostgreSQL::Utils
@@ -20,7 +21,7 @@ class PostgreSQLUtilsTest < ActiveSupport::TestCase
end
end
-class PostgreSQLNameTest < ActiveSupport::TestCase
+class PostgreSQLNameTest < ActiveRecord::PostgreSQLTestCase
Name = ActiveRecord::ConnectionAdapters::PostgreSQL::Name
test "represents itself as schema.name" do
diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
index e9379a1019..7127d69e9e 100644
--- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
@@ -11,7 +11,7 @@ module PostgresqlUUIDHelper
end
end
-class PostgresqlUUIDTest < ActiveRecord::TestCase
+class PostgresqlUUIDTest < ActiveRecord::PostgreSQLTestCase
include PostgresqlUUIDHelper
include SchemaDumpingHelper
@@ -135,7 +135,7 @@ class PostgresqlUUIDTest < ActiveRecord::TestCase
end
end
-class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase
+class PostgresqlUUIDGenerationTest < ActiveRecord::PostgreSQLTestCase
include PostgresqlUUIDHelper
include SchemaDumpingHelper
@@ -210,7 +210,7 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase
end
end
-class PostgresqlUUIDTestNilDefault < ActiveRecord::TestCase
+class PostgresqlUUIDTestNilDefault < ActiveRecord::PostgreSQLTestCase
include PostgresqlUUIDHelper
include SchemaDumpingHelper
@@ -244,7 +244,7 @@ class PostgresqlUUIDTestNilDefault < ActiveRecord::TestCase
end
end
-class PostgresqlUUIDTestInverseOf < ActiveRecord::TestCase
+class PostgresqlUUIDTestInverseOf < ActiveRecord::PostgreSQLTestCase
include PostgresqlUUIDHelper
class UuidPost < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/postgresql/view_test.rb b/activerecord/test/cases/adapters/postgresql/view_test.rb
index 8a8e1d3b17..2dd6ec5fe6 100644
--- a/activerecord/test/cases/adapters/postgresql/view_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/view_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require "cases/view_test"
-class UpdateableViewTest < ActiveRecord::TestCase
+class UpdateableViewTest < ActiveRecord::PostgreSQLTestCase
fixtures :books
class PrintedBook < ActiveRecord::Base
@@ -46,8 +46,9 @@ class UpdateableViewTest < ActiveRecord::TestCase
end
end
-if ActiveRecord::Base.connection.supports_materialized_views?
-class MaterializedViewTest < ActiveRecord::TestCase
+if ActiveRecord::Base.connection.respond_to?(:supports_materialized_views?) &&
+ ActiveRecord::Base.connection.supports_materialized_views?
+class MaterializedViewTest < ActiveRecord::PostgreSQLTestCase
include ViewBehavior
private
diff --git a/activerecord/test/cases/adapters/postgresql/xml_test.rb b/activerecord/test/cases/adapters/postgresql/xml_test.rb
index b097deb2f4..add32699fa 100644
--- a/activerecord/test/cases/adapters/postgresql/xml_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/xml_test.rb
@@ -1,7 +1,7 @@
require 'cases/helper'
require 'support/schema_dumping_helper'
-class PostgresqlXMLTest < ActiveRecord::TestCase
+class PostgresqlXMLTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class XmlDataType < ActiveRecord::Base
self.table_name = 'xml_data_type'
diff --git a/activerecord/test/cases/adapters/sqlite3/collation_test.rb b/activerecord/test/cases/adapters/sqlite3/collation_test.rb
index 39e4620bc9..58a9469ce5 100644
--- a/activerecord/test/cases/adapters/sqlite3/collation_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/collation_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class SQLite3CollationTest < ActiveRecord::TestCase
+class SQLite3CollationTest < ActiveRecord::SQLite3TestCase
include SchemaDumpingHelper
def setup
diff --git a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
index 13b754d226..34e3b2e023 100644
--- a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class CopyTableTest < ActiveRecord::TestCase
+class CopyTableTest < ActiveRecord::SQLite3TestCase
fixtures :customers
def setup
diff --git a/activerecord/test/cases/adapters/sqlite3/explain_test.rb b/activerecord/test/cases/adapters/sqlite3/explain_test.rb
index 7d66c44798..2aec322582 100644
--- a/activerecord/test/cases/adapters/sqlite3/explain_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/explain_test.rb
@@ -5,7 +5,7 @@ require 'models/computer'
module ActiveRecord
module ConnectionAdapters
class SQLite3Adapter
- class ExplainTest < ActiveRecord::TestCase
+ class ExplainTest < ActiveRecord::SQLite3TestCase
fixtures :developers
def test_explain_for_one_query
diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
index 243f65df98..87a892db37 100644
--- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
@@ -6,7 +6,7 @@ require 'securerandom'
module ActiveRecord
module ConnectionAdapters
class SQLite3Adapter
- class QuotingTest < ActiveRecord::TestCase
+ class QuotingTest < ActiveRecord::SQLite3TestCase
def setup
@conn = Base.sqlite3_connection :database => ':memory:',
:adapter => 'sqlite3',
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 6be9795603..7996e7ad50 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -5,7 +5,7 @@ require 'support/ddl_helper'
module ActiveRecord
module ConnectionAdapters
- class SQLite3AdapterTest < ActiveRecord::TestCase
+ class SQLite3AdapterTest < ActiveRecord::SQLite3TestCase
include DdlHelper
self.use_transactional_tests = false
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb
index deedf67c8e..887dcfc96c 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb
@@ -3,7 +3,7 @@ require 'models/owner'
module ActiveRecord
module ConnectionAdapters
- class SQLite3CreateFolder < ActiveRecord::TestCase
+ class SQLite3CreateFolder < ActiveRecord::SQLite3TestCase
def test_sqlite_creates_directory
Dir.mktmpdir do |dir|
dir = Pathname.new(dir)
diff --git a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
index fd0044ac05..ef324183a7 100644
--- a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
@@ -2,7 +2,7 @@ require 'cases/helper'
module ActiveRecord::ConnectionAdapters
class SQLite3Adapter
- class StatementPoolTest < ActiveRecord::TestCase
+ class StatementPoolTest < ActiveRecord::SQLite3TestCase
if Process.respond_to?(:fork)
def test_cache_is_per_pid
@@ -22,4 +22,3 @@ module ActiveRecord::ConnectionAdapters
end
end
end
-
diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb
index 3641826daa..769b171717 100644
--- a/activerecord/test/cases/enum_test.rb
+++ b/activerecord/test/cases/enum_test.rb
@@ -14,16 +14,26 @@ class EnumTest < ActiveRecord::TestCase
assert_not @book.proposed?
assert @book.read?
+ assert @book.in_english?
+ assert @book.author_visibility_visible?
+ assert @book.illustrator_visibility_visible?
+ assert @book.with_medium_font_size?
end
test "query state with strings" do
assert_equal "published", @book.status
assert_equal "read", @book.read_status
+ assert_equal "english", @book.language
+ assert_equal "visible", @book.author_visibility
+ assert_equal "visible", @book.illustrator_visibility
end
test "find via scope" do
assert_equal @book, Book.published.first
assert_equal @book, Book.read.first
+ assert_equal @book, Book.in_english.first
+ assert_equal @book, Book.author_visibility_visible.first
+ assert_equal @book, Book.illustrator_visibility_visible.first
end
test "find via where with values" do
@@ -72,6 +82,10 @@ class EnumTest < ActiveRecord::TestCase
test "update by declaration" do
@book.written!
assert @book.written?
+ @book.in_english!
+ assert @book.in_english?
+ @book.author_visibility_visible!
+ assert @book.author_visibility_visible?
end
test "update by setter" do
@@ -96,42 +110,61 @@ class EnumTest < ActiveRecord::TestCase
test "enum changed attributes" do
old_status = @book.status
+ old_language = @book.language
@book.status = :proposed
+ @book.language = :spanish
assert_equal old_status, @book.changed_attributes[:status]
+ assert_equal old_language, @book.changed_attributes[:language]
end
test "enum changes" do
old_status = @book.status
+ old_language = @book.language
@book.status = :proposed
+ @book.language = :spanish
assert_equal [old_status, 'proposed'], @book.changes[:status]
+ assert_equal [old_language, 'spanish'], @book.changes[:language]
end
test "enum attribute was" do
old_status = @book.status
+ old_language = @book.language
@book.status = :published
+ @book.language = :spanish
assert_equal old_status, @book.attribute_was(:status)
+ assert_equal old_language, @book.attribute_was(:language)
end
test "enum attribute changed" do
@book.status = :proposed
+ @book.language = :french
assert @book.attribute_changed?(:status)
+ assert @book.attribute_changed?(:language)
end
test "enum attribute changed to" do
@book.status = :proposed
+ @book.language = :french
assert @book.attribute_changed?(:status, to: 'proposed')
+ assert @book.attribute_changed?(:language, to: 'french')
end
test "enum attribute changed from" do
old_status = @book.status
+ old_language = @book.language
@book.status = :proposed
+ @book.language = :french
assert @book.attribute_changed?(:status, from: old_status)
+ assert @book.attribute_changed?(:language, from: old_language)
end
test "enum attribute changed from old status to new status" do
old_status = @book.status
+ old_language = @book.language
@book.status = :proposed
+ @book.language = :french
assert @book.attribute_changed?(:status, from: old_status, to: 'proposed')
+ assert @book.attribute_changed?(:language, from: old_language, to: 'french')
end
test "enum didn't change" do
@@ -201,11 +234,15 @@ class EnumTest < ActiveRecord::TestCase
test "building new objects with enum scopes" do
assert Book.written.build.written?
assert Book.read.build.read?
+ assert Book.in_spanish.build.in_spanish?
+ assert Book.illustrator_visibility_invisible.build.illustrator_visibility_invisible?
end
test "creating new objects with enum scopes" do
assert Book.written.create.written?
assert Book.read.create.read?
+ assert Book.in_spanish.create.in_spanish?
+ assert Book.illustrator_visibility_invisible.create.illustrator_visibility_invisible?
end
test "_before_type_cast returns the enum label (required for form fields)" do
@@ -355,4 +392,17 @@ class EnumTest < ActiveRecord::TestCase
book2 = klass.single.create!
assert book2.single?
end
+
+ test "query state by predicate with prefix" do
+ assert @book.author_visibility_visible?
+ assert_not @book.author_visibility_invisible?
+ assert @book.illustrator_visibility_visible?
+ assert_not @book.illustrator_visibility_invisible?
+ end
+
+ test "query state by predicate with custom prefix" do
+ assert @book.in_english?
+ assert_not @book.in_spanish?
+ assert_not @book.in_french?
+ end
end
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 47532c84e8..03a187ae92 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -16,6 +16,7 @@ require 'models/joke'
require 'models/matey'
require 'models/parrot'
require 'models/pirate'
+require 'models/doubloon'
require 'models/post'
require 'models/randomly_named_c1'
require 'models/reply'
@@ -903,3 +904,12 @@ class FixturesWithDefaultScopeTest < ActiveRecord::TestCase
assert_equal "special", bulbs(:special).name
end
end
+
+class FixturesWithAbstractBelongsTo < ActiveRecord::TestCase
+ fixtures :pirates, :doubloons
+
+ test "creates fixtures with belongs_to associations defined in abstract base classes" do
+ assert_not_nil doubloons(:blackbeards_doubloon)
+ assert_equal pirates(:blackbeard), doubloons(:blackbeards_doubloon).pirate
+ end
+end
diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb
index 3844b1a92e..24d3c085a7 100644
--- a/activerecord/test/cases/migration/command_recorder_test.rb
+++ b/activerecord/test/cases/migration/command_recorder_test.rb
@@ -281,17 +281,42 @@ module ActiveRecord
assert_equal [:remove_foreign_key, [:dogs, :people]], enable
end
+ def test_invert_remove_foreign_key
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people]
+ assert_equal [:add_foreign_key, [:dogs, :people]], enable
+ end
+
def test_invert_add_foreign_key_with_column
enable = @recorder.inverse_of :add_foreign_key, [:dogs, :people, column: "owner_id"]
assert_equal [:remove_foreign_key, [:dogs, column: "owner_id"]], enable
end
+ def test_invert_remove_foreign_key_with_column
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people, column: "owner_id"]
+ assert_equal [:add_foreign_key, [:dogs, :people, column: "owner_id"]], enable
+ end
+
def test_invert_add_foreign_key_with_column_and_name
enable = @recorder.inverse_of :add_foreign_key, [:dogs, :people, column: "owner_id", name: "fk"]
assert_equal [:remove_foreign_key, [:dogs, name: "fk"]], enable
end
- def test_remove_foreign_key_is_irreversible
+ def test_invert_remove_foreign_key_with_column_and_name
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people, column: "owner_id", name: "fk"]
+ assert_equal [:add_foreign_key, [:dogs, :people, column: "owner_id", name: "fk"]], enable
+ end
+
+ def test_invert_remove_foreign_key_with_primary_key
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people, primary_key: "person_id"]
+ assert_equal [:add_foreign_key, [:dogs, :people, primary_key: "person_id"]], enable
+ end
+
+ def test_invert_remove_foreign_key_with_on_delete_on_update
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people, on_delete: :nullify, on_update: :cascade]
+ assert_equal [:add_foreign_key, [:dogs, :people, on_delete: :nullify, on_update: :cascade]], enable
+ end
+
+ def test_invert_remove_foreign_key_is_irreversible_without_to_table
assert_raises ActiveRecord::IrreversibleMigration do
@recorder.inverse_of :remove_foreign_key, [:dogs, column: "owner_id"]
end
@@ -299,6 +324,10 @@ module ActiveRecord
assert_raises ActiveRecord::IrreversibleMigration do
@recorder.inverse_of :remove_foreign_key, [:dogs, name: "fk"]
end
+
+ assert_raises ActiveRecord::IrreversibleMigration do
+ @recorder.inverse_of :remove_foreign_key, [:dogs]
+ end
end
end
end
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index e6f0fe6f75..51170c533b 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -253,6 +253,11 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r{t\.integer\s+"big_int_data_points\",\s+limit: 8,\s+array: true}, output
end
+ def test_schema_dump_allows_array_of_decimal_defaults
+ output = standard_dump
+ assert_match %r{t\.decimal\s+"decimal_array_default",\s+default: \[1.23, 3.45\],\s+array: true}, output
+ end
+
if ActiveRecord::Base.connection.supports_extensions?
def test_schema_dump_includes_extensions
connection = ActiveRecord::Base.connection
diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb
index 7c92453ee3..a31c3c828c 100644
--- a/activerecord/test/cases/serialized_attribute_test.rb
+++ b/activerecord/test/cases/serialized_attribute_test.rb
@@ -274,4 +274,17 @@ class SerializedAttributeTest < ActiveRecord::TestCase
assert_equal({}, topic.content)
end
+
+ def test_values_cast_from_nil_are_persisted_as_nil
+ # This is required to fulfil the following contract, which must be universally
+ # true in Active Record:
+ #
+ # model.attribute = value
+ # assert_equal model.attribute, model.tap(&:save).reload.attribute
+ Topic.serialize(:content, Hash)
+ topic = Topic.create!(content: {})
+ topic2 = Topic.create!(content: nil)
+
+ assert_equal [topic, topic2], Topic.where(content: nil)
+ end
end
diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb
index a5e6738f14..7761ea5612 100644
--- a/activerecord/test/cases/test_case.rb
+++ b/activerecord/test/cases/test_case.rb
@@ -65,6 +65,30 @@ module ActiveRecord
end
end
+ class PostgreSQLTestCase < TestCase
+ def self.run(*args)
+ super if current_adapter?(:PostgreSQLAdapter)
+ end
+ end
+
+ class Mysql2TestCase < TestCase
+ def self.run(*args)
+ super if current_adapter?(:Mysql2Adapter)
+ end
+ end
+
+ class MysqlTestCase < TestCase
+ def self.run(*args)
+ super if current_adapter?(:MysqlAdapter)
+ end
+ end
+
+ class SQLite3TestCase < TestCase
+ def self.run(*args)
+ super if current_adapter?(:SQLite3Adapter)
+ end
+ end
+
class SQLCounter
class << self
attr_accessor :ignored_sql, :log, :log_all
diff --git a/activerecord/test/fixtures/books.yml b/activerecord/test/fixtures/books.yml
index 380dd3dfca..93cfabd61c 100644
--- a/activerecord/test/fixtures/books.yml
+++ b/activerecord/test/fixtures/books.yml
@@ -5,6 +5,10 @@ awdr:
format: "paperback"
status: :published
read_status: :read
+ language: :english
+ author_visibility: :visible
+ illustrator_visibility: :visible
+ font_size: :medium
rfr:
author_id: 1
diff --git a/activerecord/test/fixtures/doubloons.yml b/activerecord/test/fixtures/doubloons.yml
new file mode 100644
index 0000000000..efd1643971
--- /dev/null
+++ b/activerecord/test/fixtures/doubloons.yml
@@ -0,0 +1,3 @@
+blackbeards_doubloon:
+ pirate: blackbeard
+ weight: 2
diff --git a/activerecord/test/models/book.rb b/activerecord/test/models/book.rb
index 2170018068..24bfe47bbf 100644
--- a/activerecord/test/models/book.rb
+++ b/activerecord/test/models/book.rb
@@ -10,6 +10,10 @@ class Book < ActiveRecord::Base
enum status: [:proposed, :written, :published]
enum read_status: {unread: 0, reading: 2, read: 3}
enum nullable_status: [:single, :married]
+ enum language: [:english, :spanish, :french], enum_prefix: :in
+ enum author_visibility: [:visible, :invisible], enum_prefix: true
+ enum illustrator_visibility: [:visible, :invisible], enum_prefix: true
+ enum font_size: [:small, :medium, :large], enum_prefix: :with, enum_suffix: true
def published!
super
diff --git a/activerecord/test/models/doubloon.rb b/activerecord/test/models/doubloon.rb
new file mode 100644
index 0000000000..2b11d128e2
--- /dev/null
+++ b/activerecord/test/models/doubloon.rb
@@ -0,0 +1,12 @@
+class AbstractDoubloon < ActiveRecord::Base
+ # This has functionality that might be shared by multiple classes.
+
+ self.abstract_class = true
+ belongs_to :pirate
+end
+
+class Doubloon < AbstractDoubloon
+ # This uses an abstract class that defines attributes and associations.
+
+ self.table_name = 'doubloons'
+end
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index 872fa595b4..df0362573b 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -107,5 +107,6 @@ _SQL
create_table :bigint_array, force: true do |t|
t.integer :big_int_data_points, limit: 8, array: true
+ t.decimal :decimal_array_default, array: true, default: [1.23, 3.45]
end
end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 66f8f1611d..dffccc9326 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -100,6 +100,10 @@ ActiveRecord::Schema.define do
t.column :status, :integer, default: 0
t.column :read_status, :integer, default: 0
t.column :nullable_status, :integer
+ t.column :language, :integer, default: 0
+ t.column :author_visibility, :integer, default: 0
+ t.column :illustrator_visibility, :integer, default: 0
+ t.column :font_size, :integer, default: 0
end
create_table :booleans, force: true do |t|
@@ -266,6 +270,11 @@ ActiveRecord::Schema.define do
t.string :alias
end
+ create_table :doubloons, force: true do |t|
+ t.integer :pirate_id
+ t.integer :weight
+ end
+
create_table :edges, force: true, id: false do |t|
t.column :source_id, :integer, null: false
t.column :sink_id, :integer, null: false
diff --git a/activesupport/bin/test b/activesupport/bin/test
new file mode 100755
index 0000000000..404cabba51
--- /dev/null
+++ b/activesupport/bin/test
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+COMPONENT_ROOT = File.expand_path("../../", __FILE__)
+require File.expand_path("../tools/test", COMPONENT_ROOT)
+exit Minitest.run(ARGV)
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index 49fd1723e2..be369d21c6 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -153,7 +153,7 @@ module ActiveSupport
# titleize('TheManWithoutAPast') # => "The Man Without A Past"
# titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
def titleize(word)
- humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { $&.capitalize }
+ humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { |match| match.capitalize }
end
# Creates the name of a table like Rails does for models to table names.
diff --git a/activesupport/lib/active_support/testing/autorun.rb b/activesupport/lib/active_support/testing/autorun.rb
index 5aa5f46310..84c6b89340 100644
--- a/activesupport/lib/active_support/testing/autorun.rb
+++ b/activesupport/lib/active_support/testing/autorun.rb
@@ -2,4 +2,11 @@ gem 'minitest'
require 'minitest'
-Minitest.autorun
+if Minitest.respond_to?(:run_with_rails_extension)
+ unless Minitest.run_with_rails_extension
+ Minitest.run_with_autorun = true
+ Minitest.autorun
+ end
+else
+ Minitest.autorun
+end
diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md
index 80b1bde1c7..ad069a112e 100644
--- a/guides/source/active_record_migrations.md
+++ b/guides/source/active_record_migrations.md
@@ -475,7 +475,8 @@ column names can not be derived from the table names, you can use the
`:column` and `:primary_key` options.
Rails will generate a name for every foreign key starting with
-`fk_rails_` followed by 10 random characters.
+`fk_rails_` followed by 10 character which is deterministically
+generated from the `from_table` and `column`.
There is a `:name` option to specify a different name if needed.
NOTE: Active Record only supports single column foreign keys. `execute` and
diff --git a/guides/source/api_app.md b/guides/source/api_app.md
new file mode 100644
index 0000000000..0a6335ed88
--- /dev/null
+++ b/guides/source/api_app.md
@@ -0,0 +1,435 @@
+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.
+
+### 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.
+
+### 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”.
+
+### 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 api Rails app:
+
+<shell>\
+\$ rails new my\_api --api\
+</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::API*
+ 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 line at the top of the
+*Application* class:
+
+<ruby>\
+config.api\_only!\
+</ruby>
+
+Change *app/controllers/application\_controller.rb*:
+
+<ruby>
+
+1. instead of\
+ class ApplicationController \< ActionController::Base\
+ end
+
+<!-- -->
+
+1. do\
+ class ApplicationController \< ActionController::API\
+ end\
+ </ruby>
+
+### 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>
+
+#### 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.
+
+#### 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>
+
+1. Apache and lighttpd\
+ config.action\_dispatch.x\_sendfile\_header = “X-Sendfile”
+
+<!-- -->
+
+1. 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.
+
+#### 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” } }*.
+
+#### 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>
+
+#### 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*.
+
+### Choosing Controller Modules
+
+An API application (using *ActionController::API*) 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::API* in the rails console:
+
+<shell>\
+\$ irb\
+\>\> ActionController::API.ancestors -
+ActionController::Metal.ancestors\
+</shell>
+
+#### 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/guides/source/command_line.md b/guides/source/command_line.md
index 315d8c14b6..0f5a9e4e39 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -64,10 +64,10 @@ $ cd commandsapp
$ bin/rails server
=> Booting WEBrick
=> Rails 5.0.0 application starting in development on http://localhost:3000
-=> Call with -d to detach
+=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
[2013-08-07 02:00:01] INFO WEBrick 1.3.1
-[2013-08-07 02:00:01] INFO ruby 2.0.0 (2013-06-27) [x86_64-darwin11.2.0]
+[2013-08-07 02:00:01] INFO ruby 2.2.2 (2015-06-27) [x86_64-darwin11.2.0]
[2013-08-07 02:00:01] INFO WEBrick::HTTPServer#start: pid=69680 port=3000
```
diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md
index e3856a285a..f3d3a83afc 100644
--- a/guides/source/working_with_javascript_in_rails.md
+++ b/guides/source/working_with_javascript_in_rails.md
@@ -191,34 +191,6 @@ $(document).ready ->
Obviously, you'll want to be a bit more sophisticated than that, but it's a
start. You can see more about the events [in the jquery-ujs wiki](https://github.com/rails/jquery-ujs/wiki/ajax).
-Another possibility is returning javascript directly from the server side on
-remote calls:
-
-```ruby
-# articles_controller
-def create
- respond_to do |format|
- if @article.save
- format.html { ... }
- format.js do
- render js: <<-endjs
- alert('Article saved successfully!');
- window.location = '#{article_path(@article)}';
- endjs
- end
- else
- format.html { ... }
- format.js do
- render js: "alert('There are empty fields in the form!');"
- end
- end
- end
-end
-```
-
-NOTE: If javascript is disabled in the user browser, `format.html { ... }`
-block should be executed as fallback.
-
### form_tag
[`form_tag`](http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-form_tag)
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index a0fb612cba..3536929d00 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,10 @@
+* Add support for API only apps.
+ Middleware stack was slimmed down and it has only the needed
+ middleware for API apps & generators generates the right files,
+ folders and configurations.
+
+ *Santiago Pastorino & Jorge Bejar*
+
* Make generated scaffold functional tests work inside engines.
*Yuji Yaginuma*
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index 78a47fcda9..4fc7a1db62 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -16,7 +16,7 @@ module Rails
:beginning_of_week, :filter_redirect, :x
attr_writer :log_level
- attr_reader :encoding
+ attr_reader :encoding, :api_only
def initialize(*)
super
@@ -49,6 +49,7 @@ module Rails
@eager_load = nil
@secret_token = nil
@secret_key_base = nil
+ @api_only = false
@x = Custom.new
end
@@ -60,6 +61,11 @@ module Rails
end
end
+ def api_only=(value)
+ @api_only = value
+ generators.api_only = value
+ end
+
def paths
@paths ||= begin
paths = super
diff --git a/railties/lib/rails/application/default_middleware_stack.rb b/railties/lib/rails/application/default_middleware_stack.rb
index 503977b395..6f9ccec137 100644
--- a/railties/lib/rails/application/default_middleware_stack.rb
+++ b/railties/lib/rails/application/default_middleware_stack.rb
@@ -18,7 +18,7 @@ module Rails
middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header
if config.serve_static_files
- middleware.use ::ActionDispatch::Static, paths["public"].first, config.static_cache_control, config.static_index
+ middleware.use ::ActionDispatch::Static, paths["public"].first, config.static_cache_control, index: config.static_index
end
if rack_cache = load_rack_cache
@@ -28,7 +28,7 @@ module Rails
middleware.use ::Rack::Lock unless allow_concurrency?
middleware.use ::Rack::Runtime
- middleware.use ::Rack::MethodOverride
+ middleware.use ::Rack::MethodOverride unless config.api_only
middleware.use ::ActionDispatch::RequestId
# Must come after Rack::MethodOverride to properly log overridden methods
@@ -42,9 +42,9 @@ module Rails
end
middleware.use ::ActionDispatch::Callbacks
- middleware.use ::ActionDispatch::Cookies
+ middleware.use ::ActionDispatch::Cookies unless config.api_only
- if config.session_store
+ if !config.api_only && config.session_store
if config.force_ssl && !config.session_options.key?(:secure)
config.session_options[:secure] = true
end
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index 76364cea8f..d99d27a756 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -74,7 +74,7 @@ module Rails
end
class Generators #:nodoc:
- attr_accessor :aliases, :options, :templates, :fallbacks, :colorize_logging
+ attr_accessor :aliases, :options, :templates, :fallbacks, :colorize_logging, :api_only
attr_reader :hidden_namespaces
def initialize
@@ -83,6 +83,7 @@ module Rails
@fallbacks = {}
@templates = []
@colorize_logging = true
+ @api_only = false
@hidden_namespaces = []
end
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index 79088bbe3c..b430cf1909 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -45,6 +45,7 @@ module Rails
DEFAULT_OPTIONS = {
rails: {
+ api: false,
assets: true,
force_plural: false,
helper: true,
@@ -64,6 +65,7 @@ module Rails
}
def self.configure!(config) #:nodoc:
+ api_only! if config.api_only
no_color! unless config.colorize_logging
aliases.deep_merge! config.aliases
options.deep_merge! config.options
@@ -101,6 +103,21 @@ module Rails
@fallbacks ||= {}
end
+ # Configure generators for API 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.api_only!
+ hide_namespaces "assets", "helper", "css", "js"
+
+ options[:rails].merge!(
+ api: true,
+ assets: false,
+ helper: false,
+ template_engine: nil
+ )
+ end
+
# Remove the color from output.
def self.no_color!
Thor::Base.shell = Thor::Shell::Basic
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index c02b39d203..249fe96772 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -266,6 +266,8 @@ module Rails
end
def jbuilder_gemfile_entry
+ return [] if options[:api]
+
comment = 'Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder'
GemfileEntry.version('jbuilder', '~> 2.0', comment)
end
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 152c26860e..4b73313388 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -175,6 +175,9 @@ module Rails
class_option :version, type: :boolean, aliases: "-v", group: :rails,
desc: "Show Rails version number and quit"
+ class_option :api, type: :boolean,
+ desc: "Preconfigure smaller stack for API only apps"
+
def initialize(*args)
super
@@ -185,6 +188,10 @@ 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 API only apps.
+ # Can't modify options hash as it's frozen by default.
+ self.options = options.merge(skip_sprockets: true, skip_javascript: true).freeze if options[:api]
end
public_task :set_default_accessors!
@@ -252,6 +259,28 @@ module Rails
build(:vendor)
end
+ def delete_app_assets_if_api_option
+ if options[:api]
+ remove_dir 'app/assets'
+ remove_dir 'lib/assets'
+ remove_dir 'tmp/cache/assets'
+ remove_dir 'vendor/assets'
+ end
+ end
+
+ def delete_app_helpers_if_api_option
+ if options[:api]
+ remove_dir 'app/helpers'
+ remove_dir 'test/helpers'
+ end
+ end
+
+ def delete_app_views_if_api_option
+ if options[:api]
+ remove_dir 'app/views'
+ end
+ end
+
def delete_js_folder_skipping_javascript
if options[:skip_javascript]
remove_dir 'app/assets/javascripts'
@@ -270,6 +299,13 @@ module Rails
end
end
+ def delete_non_api_initializers_if_api_option
+ if options[:api]
+ remove_file 'config/initializers/session_store.rb'
+ remove_file 'config/initializers/cookies_serializer.rb'
+ end
+ end
+
def finish_template
build(:leftovers)
end
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index 29203b9c37..606f1d4f96 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -21,6 +21,14 @@ source 'https://rubygems.org'
# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development
+<%- if options.api? -%>
+# Use ActiveModelSerializers to serialize JSON responses
+gem 'active_model_serializers', '~> 0.10.0.rc1'
+
+# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
+# gem 'rack-cors'
+
+<%- end -%>
<% if RUBY_ENGINE == 'ruby' -%>
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
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 d83690e1b9..f726fd6305 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,7 @@
-class ApplicationController < ActionController::Base
+class ApplicationController < ActionController::<%= options[:api] ? "API" : "Base" %>
+<%- unless options[:api] -%>
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
+<%- end -%>
end
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 a2661bfb51..6b7d7abd0b 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -32,5 +32,12 @@ module <%= app_const_base %>
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
+<%- if options[:api] -%>
+
+ # Only loads a smaller set of middleware suitable for API only apps.
+ # Middleware like session, flash, cookies can be added back manually.
+ # Skip views, helpers and assets when generating a new resource.
+ config.api_only = true
+<%- end -%>
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb
new file mode 100644
index 0000000000..45c44d24f8
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb
@@ -0,0 +1,14 @@
+# Avoid CORS issues when API is called from the frontend app
+# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests
+
+# Read more: https://github.com/cyu/rack-cors
+
+# Rails.application.config.middleware.insert_before 0, "Rack::Cors" do
+# allow do
+# origins 'example.com'
+#
+# resource '*',
+# headers: :any,
+# methods: [:get, :post, :put, :patch, :delete, :options, :head]
+# end
+# end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt
index 94f612c3dd..cadc85cfac 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt
@@ -5,7 +5,7 @@
# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
ActiveSupport.on_load(:action_controller) do
- wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
+ wrap_parameters format: [:json]
end
<%- unless options.skip_active_record? -%>
diff --git a/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb b/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
index c986f95e67..42705107ae 100644
--- a/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
+++ b/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
@@ -1,7 +1,6 @@
module Rails
module Generators
class ResourceRouteGenerator < NamedBase # :nodoc:
-
# Properly nests namespaces passed into a generator
#
# $ rails generate resource admin/users/products
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 c01b82884d..d0b8cad896 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
@@ -10,11 +10,14 @@ module Rails
class_option :helper, type: :boolean
class_option :orm, banner: "NAME", type: :string, required: true,
desc: "ORM to generate the controller for"
+ class_option :api, type: :boolean,
+ desc: "Generates API controller"
argument :attributes, type: :array, default: [], banner: "field:type field:type"
def create_controller_files
- template "controller.rb", File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")
+ template_file = options.api? ? "api_controller.rb" : "controller.rb"
+ template template_file, File.join('app/controllers', controller_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/api_controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb
new file mode 100644
index 0000000000..bc3c9b3f6b
--- /dev/null
+++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb
@@ -0,0 +1,61 @@
+<% if namespaced? -%>
+require_dependency "<%= namespaced_file_path %>/application_controller"
+
+<% end -%>
+<% module_namespacing do -%>
+class <%= controller_class_name %>Controller < ApplicationController
+ before_action :set_<%= singular_table_name %>, only: [:show, :update, :destroy]
+
+ # GET <%= route_url %>
+ def index
+ @<%= plural_table_name %> = <%= orm_class.all(class_name) %>
+
+ render json: <%= "@#{plural_table_name}" %>
+ end
+
+ # GET <%= route_url %>/1
+ def show
+ render json: <%= "@#{singular_table_name}" %>
+ end
+
+ # POST <%= route_url %>
+ def create
+ @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %>
+
+ 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
+ def update
+ if @<%= orm_instance.update("#{singular_table_name}_params") %>
+ render json: <%= "@#{singular_table_name}" %>
+ else
+ render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity
+ end
+ end
+
+ # DELETE <%= route_url %>/1
+ def destroy
+ @<%= orm_instance.destroy %>
+ end
+
+ private
+ # Use callbacks to share common setup or constraints between actions.
+ def set_<%= singular_table_name %>
+ @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
+ end
+
+ # Only allow a trusted parameter "white list" through.
+ def <%= "#{singular_table_name}_params" %>
+ <%- if attributes_names.empty? -%>
+ params[:<%= singular_table_name %>]
+ <%- else -%>
+ params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>)
+ <%- end -%>
+ 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 c36a64db31..d634584beb 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
@@ -8,10 +8,14 @@ module TestUnit # :nodoc:
check_class_collision suffix: "ControllerTest"
+ class_option :api, type: :boolean,
+ desc: "Generates API functional tests"
+
argument :attributes, type: :array, default: [], banner: "field:type field:type"
def create_test_files
- template "functional_test.rb",
+ template_file = options.api? ? "api_functional_test.rb" : "functional_test.rb"
+ template template_file,
File.join("test/controllers", controller_class_path, "#{controller_file_name}_controller_test.rb")
end
diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb
new file mode 100644
index 0000000000..896b38bc8f
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb
@@ -0,0 +1,40 @@
+require 'test_helper'
+
+<% module_namespacing do -%>
+class <%= controller_class_name %>ControllerTest < ActionController::TestCase
+ setup do
+ @<%= singular_table_name %> = <%= table_name %>(:one)
+ end
+
+ test "should get index" do
+ get :index
+ assert_response :success
+ end
+
+ test "should create <%= singular_table_name %>" do
+ assert_difference('<%= class_name %>.count') do
+ post :create, params: { <%= "#{singular_table_name}: { #{attributes_hash} }" %> }
+ end
+
+ assert_response 201
+ end
+
+ test "should show <%= singular_table_name %>" do
+ get :show, params: { id: <%= "@#{singular_table_name}" %> }
+ assert_response :success
+ end
+
+ test "should update <%= singular_table_name %>" do
+ patch :update, params: { id: <%= "@#{singular_table_name}" %>, <%= "#{singular_table_name}: { #{attributes_hash} }" %> }
+ assert_response 200
+ end
+
+ test "should destroy <%= singular_table_name %>" do
+ assert_difference('<%= class_name %>.count', -1) do
+ delete :destroy, params: { id: <%= "@#{singular_table_name}" %> }
+ end
+
+ assert_response 204
+ end
+end
+<% end -%>
diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb
index 3b444b0932..828039dc43 100644
--- a/railties/lib/rails/test_help.rb
+++ b/railties/lib/rails/test_help.rb
@@ -9,10 +9,7 @@ require 'action_controller/test_case'
require 'action_dispatch/testing/integration'
require 'rails/generators/test_case'
-unless Minitest.run_with_rails_extension
- Minitest.run_with_autorun = true
- require 'active_support/testing/autorun'
-end
+require 'active_support/testing/autorun'
if defined?(ActiveRecord::Base)
ActiveRecord::Migration.maintain_test_schema!
diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb
index 78ada58ec8..84cc6e120b 100644
--- a/railties/test/application/generators_test.rb
+++ b/railties/test/application/generators_test.rb
@@ -125,5 +125,40 @@ module ApplicationTests
assert_equal expected, c.generators.options
end
end
+
+ test "api only generators hide assets, helper, js and css namespaces and set api option" do
+ add_to_config <<-RUBY
+ config.api_only = true
+ RUBY
+
+ # Initialize the application
+ require "#{app_path}/config/environment"
+ Rails.application.load_generators
+
+ assert Rails::Generators.hidden_namespaces.include?("assets")
+ assert Rails::Generators.hidden_namespaces.include?("helper")
+ assert Rails::Generators.hidden_namespaces.include?("js")
+ assert Rails::Generators.hidden_namespaces.include?("css")
+ assert Rails::Generators.options[:rails][:api]
+ assert_equal false, Rails::Generators.options[:rails][:assets]
+ assert_equal false, Rails::Generators.options[:rails][:helper]
+ assert_nil Rails::Generators.options[:rails][:template_engine]
+ end
+
+ test "api only generators allow overriding generator options" do
+ add_to_config <<-RUBY
+ config.generators.helper = true
+ config.api_only = true
+ config.generators.template_engine = :my_template
+ RUBY
+
+ # Initialize the application
+ require "#{app_path}/config/environment"
+ Rails.application.load_generators
+
+ assert Rails::Generators.options[:rails][:api]
+ assert Rails::Generators.options[:rails][:helper]
+ assert_equal :my_template, Rails::Generators.options[:rails][:template_engine]
+ end
end
end
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index 97b51911d9..af98e08d0e 100644
--- a/railties/test/application/initializers/frameworks_test.rb
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -129,6 +129,35 @@ module ApplicationTests
assert_equal "false", last_response.body
end
+ test "action_controller api executes using all the middleware stack" do
+ add_to_config "config.api_only = true"
+
+ app_file "app/controllers/application_controller.rb", <<-RUBY
+ class ApplicationController < ActionController::API
+ 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
+ Rails.application.routes.draw do
+ get "/: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/middleware_test.rb b/railties/test/application/middleware_test.rb
index 04bd19784a..ce92ebbf66 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -50,6 +50,33 @@ module ApplicationTests
], middleware
end
+ test "api middleware stack" do
+ add_to_config "config.api_only = true"
+
+ boot!
+
+ assert_equal [
+ "Rack::Sendfile",
+ "ActionDispatch::Static",
+ "Rack::Lock",
+ "ActiveSupport::Cache::Strategy::LocalCache",
+ "Rack::Runtime",
+ "ActionDispatch::RequestId",
+ "Rails::Rack::Logger", # must come after Rack::MethodOverride to properly log overridden methods
+ "ActionDispatch::ShowExceptions",
+ "ActionDispatch::DebugExceptions",
+ "ActionDispatch::RemoteIp",
+ "ActionDispatch::Reloader",
+ "ActionDispatch::Callbacks",
+ "ActiveRecord::ConnectionAdapters::ConnectionManagement",
+ "ActiveRecord::QueryCache",
+ "ActionDispatch::ParamsParser",
+ "Rack::Head",
+ "Rack::ConditionalGet",
+ "Rack::ETag"
+ ], middleware
+ end
+
test "Rack::Cache is not included by default" do
boot!
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index dd26ec867d..4c1913f0cc 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -194,6 +194,25 @@ module ApplicationTests
assert_no_match(/Errors running/, output)
end
+ def test_api_scaffold_tests_pass_by_default
+ add_to_config <<-RUBY
+ config.api_only = true
+ RUBY
+
+ app_file "app/controllers/application_controller.rb", <<-RUBY
+ class ApplicationController < ActionController::API
+ end
+ RUBY
+
+ output = Dir.chdir(app_path) do
+ `rails generate scaffold user username:string password:string;
+ bundle exec rake db:migrate test`
+ end
+
+ assert_match(/5 runs, 7 assertions, 0 failures, 0 errors/, output)
+ assert_no_match(/Errors running/, output)
+ end
+
def test_scaffold_with_references_columns_tests_pass_when_belongs_to_is_optional
app_file "config/initializers/active_record_belongs_to_required_by_default.rb",
"Rails.application.config.active_record.belongs_to_required_by_default = false"
diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb
new file mode 100644
index 0000000000..9978ad0da1
--- /dev/null
+++ b/railties/test/generators/api_app_generator_test.rb
@@ -0,0 +1,96 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/app/app_generator'
+
+class ApiAppGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ tests Rails::Generators::AppGenerator
+
+ arguments [destination_root, '--api']
+
+ def setup
+ Rails.application = TestApp::Application
+ super
+
+ Kernel::silence_warnings do
+ Thor::Base.shell.send(:attr_accessor, :always_force)
+ @shell = Thor::Base.shell.new
+ @shell.send(:always_force=, true)
+ end
+ end
+
+ def teardown
+ super
+ Rails.application = TestApp::Application.instance
+ end
+
+ def test_skeleton_is_created
+ run_generator
+
+ default_files.each { |path| assert_file path }
+ skipped_files.each { |path| assert_no_file path }
+ end
+
+ def test_api_modified_files
+ run_generator
+
+ assert_file "Gemfile" do |content|
+ assert_no_match(/gem 'coffee-rails'/, content)
+ assert_no_match(/gem 'jquery-rails'/, content)
+ assert_no_match(/gem 'sass-rails'/, content)
+ assert_no_match(/gem 'jbuilder'/, content)
+ assert_match(/gem 'active_model_serializers'/, content)
+ end
+
+ assert_file "config/application.rb" do |content|
+ assert_match(/config.api_only = true/, content)
+ end
+
+ assert_file "config/initializers/cors.rb"
+
+ assert_file "config/initializers/wrap_parameters.rb"
+
+ assert_file "app/controllers/application_controller.rb", /ActionController::API/
+ end
+
+ private
+
+ def default_files
+ files = %W(
+ .gitignore
+ Gemfile
+ Rakefile
+ config.ru
+ app/controllers
+ app/mailers
+ app/models
+ config/environments
+ config/initializers
+ config/locales
+ db
+ lib
+ lib/tasks
+ log
+ test/fixtures
+ test/controllers
+ test/integration
+ test/models
+ tmp
+ vendor
+ )
+ files.concat %w(bin/bundle bin/rails bin/rake)
+ files
+ end
+
+ def skipped_files
+ %w(app/assets
+ app/helpers
+ app/views
+ config/initializers/assets.rb
+ config/initializers/cookies_serializer.rb
+ config/initializers/session_store.rb
+ lib/assets
+ vendor/assets
+ test/helpers
+ tmp/cache/assets)
+ end
+end
diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
index 7c282377d7..5dae36b65e 100644
--- a/railties/test/generators/scaffold_controller_generator_test.rb
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -185,4 +185,51 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
assert_match(/2 runs, 2 assertions, 0 failures, 0 errors/, `bundle exec rake test 2>&1`)
end
end
+
+ def test_api_only_generates_a_proper_api_controller
+ run_generator ["User", "--api"]
+
+ assert_file "app/controllers/users_controller.rb" do |content|
+ assert_match(/class UsersController < ApplicationController/, content)
+ assert_no_match(/respond_to/, content)
+
+ assert_match(/before_action :set_user, only: \[:show, :update, :destroy\]/, content)
+
+ assert_instance_method :index, content do |m|
+ assert_match(/@users = User\.all/, m)
+ assert_match(/render json: @users/, m)
+ end
+
+ assert_instance_method :show, content do |m|
+ assert_match(/render json: @user/, m)
+ end
+
+ assert_instance_method :create, content do |m|
+ assert_match(/@user = User\.new\(user_params\)/, m)
+ assert_match(/@user\.save/, m)
+ assert_match(/@user\.errors/, m)
+ end
+
+ assert_instance_method :update, content do |m|
+ assert_match(/@user\.update\(user_params\)/, m)
+ assert_match(/@user\.errors/, m)
+ end
+
+ assert_instance_method :destroy, content do |m|
+ assert_match(/@user\.destroy/, m)
+ end
+ end
+ end
+
+ def test_api_controller_tests
+ run_generator ["User", "name:string", "age:integer", "organization:references{polymorphic}", "--api"]
+
+ assert_file "test/controllers/users_controller_test.rb" do |content|
+ assert_match(/class UsersControllerTest < ActionController::TestCase/, content)
+ assert_match(/test "should get index"/, content)
+ assert_match(/post :create, params: \{ user: \{ age: @user\.age, name: @user\.name, organization_id: @user\.organization_id, organization_type: @user\.organization_type \} \}/, content)
+ assert_match(/patch :update, params: \{ id: @user, user: \{ age: @user\.age, name: @user\.name, organization_id: @user\.organization_id, organization_type: @user\.organization_type \} \}/, content)
+ assert_no_match(/assert_redirected_to/, content)
+ end
+ end
end
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index 8d7fea8741..3401b96d7d 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -87,6 +87,76 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_file "app/assets/stylesheets/product_lines.css"
end
+ def test_api_scaffold_on_invoke
+ run_generator %w(product_line title:string product:belongs_to user:references --api --no-template-engine --no-helper --no-assets)
+
+ # Model
+ assert_file "app/models/product_line.rb", /class ProductLine < ActiveRecord::Base/
+ assert_file "test/models/product_line_test.rb", /class ProductLineTest < ActiveSupport::TestCase/
+ assert_file "test/fixtures/product_lines.yml"
+ assert_migration "db/migrate/create_product_lines.rb", /belongs_to :product, index: true/
+ assert_migration "db/migrate/create_product_lines.rb", /references :user, index: true/
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_match(/resources :product_lines$/, route)
+ end
+
+ # Controller
+ assert_file "app/controllers/product_lines_controller.rb" do |content|
+ assert_match(/class ProductLinesController < ApplicationController/, content)
+ assert_no_match(/respond_to/, content)
+
+ assert_match(/before_action :set_product_line, only: \[:show, :update, :destroy\]/, content)
+
+ assert_instance_method :index, content do |m|
+ assert_match(/@product_lines = ProductLine\.all/, m)
+ assert_match(/render json: @product_lines/, m)
+ end
+
+ assert_instance_method :show, content do |m|
+ assert_match(/render json: @product_line/, m)
+ end
+
+ assert_instance_method :create, content do |m|
+ assert_match(/@product_line = ProductLine\.new\(product_line_params\)/, m)
+ assert_match(/@product_line\.save/, m)
+ assert_match(/@product_line\.errors/, m)
+ end
+
+ assert_instance_method :update, content do |m|
+ assert_match(/@product_line\.update\(product_line_params\)/, m)
+ assert_match(/@product_line\.errors/, m)
+ end
+
+ assert_instance_method :destroy, content do |m|
+ assert_match(/@product_line\.destroy/, m)
+ end
+ end
+
+ assert_file "test/controllers/product_lines_controller_test.rb" do |test|
+ assert_match(/class ProductLinesControllerTest < ActionController::TestCase/, test)
+ assert_match(/post :create, params: \{ product_line: \{ product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test)
+ assert_match(/patch :update, params: \{ id: @product_line, product_line: \{ product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test)
+ assert_no_match(/assert_redirected_to/, test)
+ end
+
+ # Views
+ assert_no_file "app/views/layouts/product_lines.html.erb"
+
+ %w(index show new edit _form).each do |view|
+ assert_no_file "app/views/product_lines/#{view}.html.erb"
+ end
+
+ # Helpers
+ assert_no_file "app/helpers/product_lines_helper.rb"
+
+ # Assets
+ assert_no_file "app/assets/stylesheets/scaffold.css"
+ assert_no_file "app/assets/javascripts/product_lines.js"
+ assert_no_file "app/assets/stylesheets/product_lines.css"
+ end
+
def test_functional_tests_without_attributes
run_generator ["product_line"]
diff --git a/tools/test.rb b/tools/test.rb
new file mode 100644
index 0000000000..50cce2081a
--- /dev/null
+++ b/tools/test.rb
@@ -0,0 +1,10 @@
+$: << File.expand_path("test", COMPONENT_ROOT)
+require File.expand_path("../../load_paths", __FILE__)
+require "rails/test_unit/minitest_plugin"
+
+module Rails
+ # Necessary to get rerun-snippts working.
+ def self.root
+ @root ||= Pathname.new(COMPONENT_ROOT)
+ end
+end