mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge branch 'master' of github.com:rails/rails
This commit is contained in:
commit
089661b69a
37 changed files with 400 additions and 207 deletions
2
Gemfile
2
Gemfile
|
@ -10,7 +10,7 @@ gem 'mocha', '~> 0.14', require: false
|
|||
gem 'rack', github: 'rack/rack'
|
||||
gem 'rack-cache', '~> 1.2'
|
||||
gem 'jquery-rails', '~> 3.1.0'
|
||||
gem 'turbolinks'
|
||||
gem 'turbolinks', github: 'rails/turbolinks', branch: 'master'
|
||||
gem 'coffee-rails', '~> 4.0.0'
|
||||
gem 'arel', github: 'rails/arel', branch: 'master'
|
||||
gem 'sprockets-rails', github: 'rails/sprockets-rails', branch: 'master'
|
||||
|
|
|
@ -72,11 +72,11 @@ module ActionController
|
|||
raise AbstractController::DoubleRenderError if response_body
|
||||
|
||||
self.status = _extract_redirect_to_status(options, response_status)
|
||||
self.location = _compute_redirect_to_location(options)
|
||||
self.location = _compute_redirect_to_location(request, options)
|
||||
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"
|
||||
end
|
||||
|
||||
def _compute_redirect_to_location(options) #:nodoc:
|
||||
def _compute_redirect_to_location(request, options) #:nodoc:
|
||||
case options
|
||||
# The scheme name consist of a letter followed by any combination of
|
||||
# letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
|
||||
|
@ -90,11 +90,13 @@ module ActionController
|
|||
when :back
|
||||
request.headers["Referer"] or raise RedirectBackError
|
||||
when Proc
|
||||
_compute_redirect_to_location options.call
|
||||
_compute_redirect_to_location request, options.call
|
||||
else
|
||||
url_for(options)
|
||||
end.delete("\0\r\n")
|
||||
end
|
||||
module_function :_compute_redirect_to_location
|
||||
public :_compute_redirect_to_location
|
||||
|
||||
private
|
||||
def _extract_redirect_to_status(options, response_status)
|
||||
|
|
|
@ -456,7 +456,6 @@ module ActionController
|
|||
end
|
||||
|
||||
def controller_class=(new_class)
|
||||
prepare_controller_class(new_class) if new_class
|
||||
self._controller_class = new_class
|
||||
end
|
||||
|
||||
|
@ -473,11 +472,6 @@ module ActionController
|
|||
Class === constant && constant < ActionController::Metal
|
||||
end
|
||||
end
|
||||
|
||||
def prepare_controller_class(new_class)
|
||||
new_class.send :include, ActionController::TestCase::RaiseActionExceptions
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Simulate a GET request with the given parameters.
|
||||
|
@ -713,34 +707,6 @@ module ActionController
|
|||
end
|
||||
end
|
||||
|
||||
# When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
|
||||
# (skipping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular
|
||||
# rescue_action process takes place. This means you can test your rescue_action code by setting remote_addr to something else
|
||||
# than 0.0.0.0.
|
||||
#
|
||||
# The exception is stored in the exception accessor for further inspection.
|
||||
module RaiseActionExceptions
|
||||
def self.included(base) #:nodoc:
|
||||
unless base.method_defined?(:exception) && base.method_defined?(:exception=)
|
||||
base.class_eval do
|
||||
attr_accessor :exception
|
||||
protected :exception, :exception=
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def rescue_action_without_handler(e)
|
||||
self.exception = e
|
||||
|
||||
if request.remote_addr == "0.0.0.0"
|
||||
raise(e)
|
||||
else
|
||||
super(e)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
include Behavior
|
||||
end
|
||||
end
|
||||
|
|
|
@ -73,13 +73,8 @@ module ActionDispatch
|
|||
if Regexp === fragment
|
||||
fragment
|
||||
else
|
||||
handle = @controller || Class.new(ActionController::Metal) do
|
||||
include ActionController::Redirecting
|
||||
def initialize(request)
|
||||
@_request = request
|
||||
end
|
||||
end.new(@request)
|
||||
handle._compute_redirect_to_location(fragment)
|
||||
handle = @controller || ActionController::Redirecting
|
||||
handle._compute_redirect_to_location(@request, fragment)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -221,7 +221,7 @@ XML
|
|||
assert_equal 200, @response.status
|
||||
end
|
||||
|
||||
def test_head_params_as_sting
|
||||
def test_head_params_as_string
|
||||
assert_raise(NoMethodError) { head :test_params, "document body", :id => 10 }
|
||||
end
|
||||
|
||||
|
|
|
@ -25,9 +25,6 @@ module TestGenerationPrefix
|
|||
include Rack::Test::Methods
|
||||
|
||||
class BlogEngine < Rails::Engine
|
||||
def self.routes
|
||||
@routes ||= begin
|
||||
routes = ActionDispatch::Routing::RouteSet.new
|
||||
routes.draw do
|
||||
get "/posts/:id", :to => "inside_engine_generating#show", :as => :post
|
||||
get "/posts", :to => "inside_engine_generating#index", :as => :posts
|
||||
|
@ -50,21 +47,9 @@ module TestGenerationPrefix
|
|||
get "/absolute_custom_root", :to => redirect { |params, request| "/" }
|
||||
get "/absolute_custom_redirect", :to => redirect { |params, request| "/foo" }
|
||||
end
|
||||
|
||||
routes
|
||||
end
|
||||
end
|
||||
|
||||
def self.call(env)
|
||||
env['action_dispatch.routes'] = routes
|
||||
routes.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
class RailsApplication
|
||||
def self.routes
|
||||
@routes ||= begin
|
||||
routes = ActionDispatch::Routing::RouteSet.new
|
||||
class RailsApplication < Rails::Engine
|
||||
routes.draw do
|
||||
scope "/:omg", :omg => "awesome" do
|
||||
mount BlogEngine => "/blog", :as => "blog_engine"
|
||||
|
@ -78,19 +63,9 @@ module TestGenerationPrefix
|
|||
get "/ivar_usage", :to => "outside_engine_generating#ivar_usage"
|
||||
root :to => "outside_engine_generating#index"
|
||||
end
|
||||
|
||||
routes
|
||||
end
|
||||
end
|
||||
|
||||
def self.call(env)
|
||||
env['action_dispatch.routes'] = routes
|
||||
routes.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
# force draw
|
||||
RailsApplication.routes
|
||||
RailsApplication.routes.define_mounted_helper(:main_app)
|
||||
|
||||
class ::InsideEngineGeneratingController < ActionController::Base
|
||||
|
@ -162,7 +137,7 @@ module TestGenerationPrefix
|
|||
end
|
||||
|
||||
def app
|
||||
RailsApplication
|
||||
RailsApplication.instance
|
||||
end
|
||||
|
||||
attr_reader :engine_object, :app_object
|
||||
|
@ -392,26 +367,11 @@ module TestGenerationPrefix
|
|||
end
|
||||
end
|
||||
|
||||
class RailsApplication
|
||||
def self.routes
|
||||
@routes ||= begin
|
||||
routes = ActionDispatch::Routing::RouteSet.new
|
||||
class RailsApplication < Rails::Engine
|
||||
routes.draw do
|
||||
mount BlogEngine => "/"
|
||||
end
|
||||
|
||||
routes
|
||||
end
|
||||
end
|
||||
|
||||
def self.call(env)
|
||||
env['action_dispatch.routes'] = routes
|
||||
routes.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
# force draw
|
||||
RailsApplication.routes
|
||||
|
||||
class ::PostsController < ActionController::Base
|
||||
include BlogEngine.routes.url_helpers
|
||||
|
@ -423,7 +383,7 @@ module TestGenerationPrefix
|
|||
end
|
||||
|
||||
def app
|
||||
RailsApplication
|
||||
RailsApplication.instance
|
||||
end
|
||||
|
||||
test "generating path inside engine" do
|
||||
|
|
|
@ -66,15 +66,6 @@ module ActionView #:nodoc:
|
|||
# Headline: <%= headline %>
|
||||
# First name: <%= person.first_name %>
|
||||
#
|
||||
# If you need to find out whether a certain local variable has been assigned a value in a particular render call,
|
||||
# you need to use the following pattern:
|
||||
#
|
||||
# <% if local_assigns.has_key? :headline %>
|
||||
# Headline: <%= headline %>
|
||||
# <% end %>
|
||||
#
|
||||
# Testing using <tt>defined? headline</tt> will not work. This is an implementation restriction.
|
||||
#
|
||||
# === Template caching
|
||||
#
|
||||
# By default, Rails will compile each template to a method in order to render it. When you alter a template,
|
||||
|
|
|
@ -3,7 +3,7 @@ module ActionView
|
|||
module Tags # :nodoc:
|
||||
class Select < Base # :nodoc:
|
||||
def initialize(object_name, method_name, template_object, choices, options, html_options)
|
||||
@choices = block_given? ? template_object.capture { yield } : choices
|
||||
@choices = block_given? ? template_object.capture { yield || "" } : choices
|
||||
@choices = @choices.to_a if @choices.is_a?(Range)
|
||||
|
||||
@html_options = html_options
|
||||
|
|
|
@ -591,6 +591,19 @@ class FormOptionsHelperTest < ActionView::TestCase
|
|||
)
|
||||
end
|
||||
|
||||
def test_select_under_fields_for_with_block_without_options
|
||||
@post = Post.new
|
||||
|
||||
output_buffer = fields_for :post, @post do |f|
|
||||
concat(f.select(:category) {})
|
||||
end
|
||||
|
||||
assert_dom_equal(
|
||||
"<select id=\"post_category\" name=\"post[category]\"></select>",
|
||||
output_buffer
|
||||
)
|
||||
end
|
||||
|
||||
def test_select_with_multiple_to_add_hidden_input
|
||||
output_buffer = select(:post, :category, "", {}, :multiple => true)
|
||||
assert_dom_equal(
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
* Passwords with spaces only allowed in `ActiveModel::SecurePassword`.
|
||||
|
||||
Presence validation can be used to restore old behavior.
|
||||
|
||||
*Yevhene Shemet*
|
||||
|
||||
* Validate options passed to `ActiveModel::Validations.validate`.
|
||||
|
||||
Preventing, in many cases, the simple mistake of using `validate` instead of `validates`.
|
||||
|
|
|
@ -105,7 +105,7 @@ module ActiveModel
|
|||
attr_reader :password
|
||||
|
||||
# Encrypts the password into the +password_digest+ attribute, only if the
|
||||
# new password is not blank.
|
||||
# new password is not empty.
|
||||
#
|
||||
# class User < ActiveRecord::Base
|
||||
# has_secure_password validations: false
|
||||
|
@ -119,7 +119,7 @@ module ActiveModel
|
|||
def password=(unencrypted_password)
|
||||
if unencrypted_password.nil?
|
||||
self.password_digest = nil
|
||||
elsif unencrypted_password.present?
|
||||
elsif !unencrypted_password.empty?
|
||||
@password = unencrypted_password
|
||||
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
|
||||
self.password_digest = BCrypt::Password.create(unencrypted_password, cost: cost)
|
||||
|
|
|
@ -40,6 +40,11 @@ class SecurePasswordTest < ActiveModel::TestCase
|
|||
assert @user.valid?(:create), 'user should be valid'
|
||||
end
|
||||
|
||||
test "create a new user with validation and a spaces only password" do
|
||||
@user.password = ' ' * 72
|
||||
assert @user.valid?(:create), 'user should be valid'
|
||||
end
|
||||
|
||||
test "create a new user with validation and a blank password" do
|
||||
@user.password = ''
|
||||
assert !@user.valid?(:create), 'user should be invalid'
|
||||
|
@ -105,6 +110,11 @@ class SecurePasswordTest < ActiveModel::TestCase
|
|||
assert @existing_user.valid?(:update), 'user should be valid'
|
||||
end
|
||||
|
||||
test "updating an existing user with validation and a spaces only password" do
|
||||
@user.password = ' ' * 72
|
||||
assert @user.valid?(:update), 'user should be valid'
|
||||
end
|
||||
|
||||
test "updating an existing user with validation and a blank password and password_confirmation" do
|
||||
@existing_user.password = ''
|
||||
@existing_user.password_confirmation = ''
|
||||
|
|
|
@ -1,3 +1,27 @@
|
|||
* Define `id_was` to get the previous value of the primary key.
|
||||
|
||||
Currently when we call id_was and we have a custom primary key name
|
||||
Active Record will return the current value of the primary key. This
|
||||
make impossible to correctly do an update operation if you change the
|
||||
id.
|
||||
|
||||
Fixes #16413.
|
||||
|
||||
*Rafael Mendonça França*
|
||||
|
||||
* Deprecate `DatabaseTasks.load_schema` to act on the current connection.
|
||||
Use `.load_schema_current` instead. In the future `load_schema` will
|
||||
require the `configuration` to act on as an argument.
|
||||
|
||||
*Yves Senn*
|
||||
|
||||
* Fixed automatic maintaining test schema to properly handle sql structure
|
||||
schema format.
|
||||
|
||||
Fixes #15394.
|
||||
|
||||
*Wojciech Wnętrzak*
|
||||
|
||||
* Fix type casting to Decimal from Float with large precision.
|
||||
|
||||
*Tomohiro Hashidate*
|
||||
|
|
|
@ -39,6 +39,12 @@ module ActiveRecord
|
|||
read_attribute_before_type_cast(self.class.primary_key)
|
||||
end
|
||||
|
||||
# Returns the primary key previous value.
|
||||
def id_was
|
||||
sync_with_transaction_state
|
||||
attribute_was(self.class.primary_key)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def attribute_method?(attr_name)
|
||||
|
@ -54,7 +60,7 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast).to_set
|
||||
ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was).to_set
|
||||
|
||||
def dangerous_attribute_method?(method_name)
|
||||
super && !ID_ATTRIBUTE_METHODS.include?(method_name)
|
||||
|
|
|
@ -32,7 +32,7 @@ module ActiveRecord
|
|||
# }
|
||||
def initialize(url)
|
||||
raise "Database URL cannot be empty" if url.blank?
|
||||
@uri = URI.parse(url)
|
||||
@uri = uri_parser.parse(url)
|
||||
@adapter = @uri.scheme.gsub('-', '_')
|
||||
@adapter = "postgresql" if @adapter == "postgres"
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ module ActiveRecord
|
|||
|
||||
# A cached lookup for table existence.
|
||||
def table_exists?(name)
|
||||
prepare_tables if @tables.empty?
|
||||
return @tables[name] if @tables.key? name
|
||||
|
||||
@tables[name] = connection.table_exists?(name)
|
||||
|
@ -82,6 +83,12 @@ module ActiveRecord
|
|||
def marshal_load(array)
|
||||
@version, @columns, @columns_hash, @primary_keys, @tables = array
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def prepare_tables
|
||||
connection.tables.each { |table| @tables[table] = true }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -399,7 +399,7 @@ module ActiveRecord
|
|||
|
||||
def load_schema_if_pending!
|
||||
if ActiveRecord::Migrator.needs_migration?
|
||||
ActiveRecord::Tasks::DatabaseTasks.load_schema
|
||||
ActiveRecord::Tasks::DatabaseTasks.load_schema_current
|
||||
check_pending!
|
||||
end
|
||||
end
|
||||
|
|
|
@ -240,7 +240,7 @@ db_namespace = namespace :db do
|
|||
|
||||
desc 'Load a schema.rb file into the database'
|
||||
task :load => [:environment, :load_config] do
|
||||
ActiveRecord::Tasks::DatabaseTasks.load_schema(:ruby, ENV['SCHEMA'])
|
||||
ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:ruby, ENV['SCHEMA'])
|
||||
end
|
||||
|
||||
task :load_if_ruby => ['db:create', :environment] do
|
||||
|
@ -286,7 +286,7 @@ db_namespace = namespace :db do
|
|||
|
||||
desc "Recreate the databases from the structure.sql file"
|
||||
task :load => [:environment, :load_config] do
|
||||
ActiveRecord::Tasks::DatabaseTasks.load_schema(:sql, ENV['DB_STRUCTURE'])
|
||||
ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:sql, ENV['DB_STRUCTURE'])
|
||||
end
|
||||
|
||||
task :load_if_sql => ['db:create', :environment] do
|
||||
|
@ -317,9 +317,8 @@ db_namespace = namespace :db do
|
|||
task :load_schema => %w(db:test:deprecated db:test:purge) do
|
||||
begin
|
||||
should_reconnect = ActiveRecord::Base.connection_pool.active_connection?
|
||||
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
|
||||
ActiveRecord::Schema.verbose = false
|
||||
db_namespace["schema:load"].invoke
|
||||
ActiveRecord::Tasks::DatabaseTasks.load_schema_for ActiveRecord::Base.configurations['test'], :ruby, ENV['SCHEMA']
|
||||
ensure
|
||||
if should_reconnect
|
||||
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[ActiveRecord::Tasks::DatabaseTasks.env])
|
||||
|
@ -329,12 +328,7 @@ db_namespace = namespace :db do
|
|||
|
||||
# desc "Recreate the test database from an existent structure.sql file"
|
||||
task :load_structure => %w(db:test:deprecated db:test:purge) do
|
||||
begin
|
||||
ActiveRecord::Tasks::DatabaseTasks.current_config(:config => ActiveRecord::Base.configurations['test'])
|
||||
db_namespace["structure:load"].invoke
|
||||
ensure
|
||||
ActiveRecord::Tasks::DatabaseTasks.current_config(:config => nil)
|
||||
end
|
||||
ActiveRecord::Tasks::DatabaseTasks.load_schema_for ActiveRecord::Base.configurations['test'], :sql, ENV['SCHEMA']
|
||||
end
|
||||
|
||||
# desc "Recreate the test database from a fresh schema"
|
||||
|
|
|
@ -184,20 +184,39 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def load_schema(format = ActiveRecord::Base.schema_format, file = nil)
|
||||
ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
|
||||
This method will act on a specific connection in the future.
|
||||
To act on the current connection, use `load_schema_current` instead.
|
||||
MESSAGE
|
||||
load_schema_current(format, file)
|
||||
end
|
||||
|
||||
# This method is the successor of +load_schema+. We should rename it
|
||||
# after +load_schema+ went through a deprecation cycle. (Rails > 4.2)
|
||||
def load_schema_for(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc:
|
||||
case format
|
||||
when :ruby
|
||||
file ||= File.join(db_dir, "schema.rb")
|
||||
check_schema_file(file)
|
||||
purge(configuration)
|
||||
ActiveRecord::Base.establish_connection(configuration)
|
||||
load(file)
|
||||
when :sql
|
||||
file ||= File.join(db_dir, "structure.sql")
|
||||
check_schema_file(file)
|
||||
structure_load(current_config, file)
|
||||
purge(configuration)
|
||||
structure_load(configuration, file)
|
||||
else
|
||||
raise ArgumentError, "unknown format #{format.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
|
||||
each_current_configuration(environment) { |configuration|
|
||||
load_schema_for configuration, format, file
|
||||
}
|
||||
end
|
||||
|
||||
def check_schema_file(filename)
|
||||
unless File.exist?(filename)
|
||||
message = %{#{filename} doesn't exist yet. Run `rake db:migrate` to create it, then try again.}
|
||||
|
|
|
@ -21,7 +21,11 @@ module ActiveRecord
|
|||
|
||||
FileUtils.rm(file) if File.exist?(file)
|
||||
end
|
||||
alias :purge :drop
|
||||
|
||||
def purge
|
||||
drop
|
||||
create
|
||||
end
|
||||
|
||||
def charset
|
||||
connection.encoding
|
||||
|
|
|
@ -5,6 +5,7 @@ require 'models/subscriber'
|
|||
require 'models/movie'
|
||||
require 'models/keyboard'
|
||||
require 'models/mixed_case_monkey'
|
||||
require 'models/dashboard'
|
||||
|
||||
class PrimaryKeysTest < ActiveRecord::TestCase
|
||||
fixtures :topics, :subscribers, :movies, :mixed_case_monkeys
|
||||
|
@ -164,6 +165,15 @@ class PrimaryKeysTest < ActiveRecord::TestCase
|
|||
MixedCaseMonkey.reset_primary_key
|
||||
assert_equal "monkeyID", MixedCaseMonkey.primary_key
|
||||
end
|
||||
|
||||
def test_primary_key_update_with_custom_key_name
|
||||
dashboard = Dashboard.create!(dashboard_id: '1')
|
||||
dashboard.id = '2'
|
||||
dashboard.save!
|
||||
|
||||
dashboard = Dashboard.first
|
||||
assert_equal '2', dashboard.id
|
||||
end
|
||||
end
|
||||
|
||||
class PrimaryKeyWithNoConnectionTest < ActiveRecord::TestCase
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
* Fix DateTime comparison with DateTime::Infinity object.
|
||||
|
||||
*Rafael Mendonça França*
|
||||
|
||||
* Added Object#itself which returns the object itself. Useful when dealing with a chaining scenario, like Active Record scopes:
|
||||
|
||||
Event.public_send(state.presence_in([ :trashed, :drafted ]) || :itself).order(:created_at)
|
||||
|
|
|
@ -161,7 +161,9 @@ class DateTime
|
|||
# Layers additional behavior on DateTime#<=> so that Time and
|
||||
# ActiveSupport::TimeWithZone instances can be compared with a DateTime.
|
||||
def <=>(other)
|
||||
if other.respond_to? :to_datetime
|
||||
if other.kind_of?(Infinity)
|
||||
super
|
||||
elsif other.respond_to? :to_datetime
|
||||
super other.to_datetime
|
||||
else
|
||||
nil
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
module ActiveSupport
|
||||
class FileWatcher
|
||||
class Backend
|
||||
def initialize(path, watcher)
|
||||
@watcher = watcher
|
||||
@path = path
|
||||
end
|
||||
|
||||
def trigger(files)
|
||||
@watcher.trigger(files)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize
|
||||
@regex_matchers = {}
|
||||
end
|
||||
|
||||
def watch(pattern, &block)
|
||||
@regex_matchers[pattern] = block
|
||||
end
|
||||
|
||||
def trigger(files)
|
||||
trigger_files = Hash.new { |h,k| h[k] = Hash.new { |h2,k2| h2[k2] = [] } }
|
||||
|
||||
files.each do |file, state|
|
||||
@regex_matchers.each do |pattern, block|
|
||||
trigger_files[block][state] << file if pattern === file
|
||||
end
|
||||
end
|
||||
|
||||
trigger_files.each do |block, payload|
|
||||
block.call payload
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -16,6 +16,7 @@ class RangeTest < ActiveSupport::TestCase
|
|||
def test_date_range
|
||||
assert_instance_of Range, DateTime.new..DateTime.new
|
||||
assert_instance_of Range, DateTime::Infinity.new..DateTime::Infinity.new
|
||||
assert_instance_of Range, DateTime.new..DateTime::Infinity.new
|
||||
end
|
||||
|
||||
def test_overlaps_last_inclusive
|
||||
|
|
|
@ -85,6 +85,9 @@ Please refer to the [Changelog][railties] for detailed changes.
|
|||
* Introduced `Rails.gem_version` as a convenience method to return `Gem::Version.new(Rails.version)`.
|
||||
([Pull Request](https://github.com/rails/rails/pull/14101))
|
||||
|
||||
* Introduced an `after_bundle` callback in the Rails templates.
|
||||
([Pull Request](https://github.com/rails/rails/pull/16359))
|
||||
|
||||
|
||||
Action Pack
|
||||
-----------
|
||||
|
|
|
@ -38,9 +38,11 @@ generate(:scaffold, "person name:string")
|
|||
route "root to: 'people#index'"
|
||||
rake("db:migrate")
|
||||
|
||||
after_bundle do
|
||||
git :init
|
||||
git add: "."
|
||||
git commit: %Q{ -m 'Initial commit' }
|
||||
end
|
||||
```
|
||||
|
||||
The following sections outline the primary methods provided by the API:
|
||||
|
@ -228,6 +230,22 @@ git add: "."
|
|||
git commit: "-a -m 'Initial commit'"
|
||||
```
|
||||
|
||||
### after_bundle(&block)
|
||||
|
||||
Registers a callback to be executed after the gems are bundled and binstubs
|
||||
are generated. Useful for all generated files to version control:
|
||||
|
||||
```ruby
|
||||
after_bundle do
|
||||
git :init
|
||||
git add: '.'
|
||||
git commit: "-a -m 'Initial commit'"
|
||||
end
|
||||
```
|
||||
|
||||
The callbacks gets executed even if `--skip-bundle` and/or `--skip-spring` has
|
||||
been passed.
|
||||
|
||||
Advanced Usage
|
||||
--------------
|
||||
|
||||
|
|
|
@ -58,6 +58,38 @@ When assigning `nil` to a serialized attribute, it will be saved to the database
|
|||
as `NULL` instead of passing the `nil` value through the coder (e.g. `"null"`
|
||||
when using the `JSON` coder).
|
||||
|
||||
### `after_bundle` in Rails templates
|
||||
|
||||
If you have a Rails template that adds all the files in version control, it
|
||||
fails to add the generated binstubs because it gets executed before Bundler:
|
||||
|
||||
```ruby
|
||||
# template.rb
|
||||
generate(:scaffold, "person name:string")
|
||||
route "root to: 'people#index'"
|
||||
rake("db:migrate")
|
||||
|
||||
git :init
|
||||
git add: "."
|
||||
git commit: %Q{ -m 'Initial commit' }
|
||||
```
|
||||
|
||||
You can now wrap the `git` calls in an `after_bundle` block. It will be run
|
||||
after the binstubs have been generated.
|
||||
|
||||
```ruby
|
||||
# template.rb
|
||||
generate(:scaffold, "person name:string")
|
||||
route "root to: 'people#index'"
|
||||
rake("db:migrate")
|
||||
|
||||
after_bundle do
|
||||
git :init
|
||||
git add: "."
|
||||
git commit: %Q{ -m 'Initial commit' }
|
||||
end
|
||||
```
|
||||
|
||||
Upgrading from Rails 4.0 to Rails 4.1
|
||||
-------------------------------------
|
||||
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
* Add `after_bundle` callbacks in Rails templates. Useful for allowing the
|
||||
generated binstubs to be added to version control.
|
||||
|
||||
Fixes #16292.
|
||||
|
||||
*Stefan Kanev*
|
||||
|
||||
* Pull in the custom configuration concept from dhh/custom_configuration, which allows you to
|
||||
configure your own code through the Rails configuration object with custom configuration:
|
||||
|
||||
|
|
|
@ -29,7 +29,13 @@ module Rails
|
|||
autoload :WelcomeController
|
||||
|
||||
class << self
|
||||
attr_accessor :application, :cache, :logger
|
||||
@application = @app_class = nil
|
||||
|
||||
attr_writer :application
|
||||
attr_accessor :app_class, :cache, :logger
|
||||
def application
|
||||
@application ||= (app_class.instance if app_class)
|
||||
end
|
||||
|
||||
delegate :initialize!, :initialized?, to: :application
|
||||
|
||||
|
|
|
@ -87,7 +87,15 @@ module Rails
|
|||
class << self
|
||||
def inherited(base)
|
||||
super
|
||||
base.instance
|
||||
Rails.app_class = base
|
||||
end
|
||||
|
||||
def instance
|
||||
super.run_load_hooks!
|
||||
end
|
||||
|
||||
def create(initial_variable_values = {}, &block)
|
||||
new(initial_variable_values, &block).run_load_hooks!
|
||||
end
|
||||
|
||||
# Makes the +new+ method public.
|
||||
|
@ -116,19 +124,13 @@ module Rails
|
|||
@ordered_railties = nil
|
||||
@railties = nil
|
||||
@message_verifiers = {}
|
||||
@ran_load_hooks = false
|
||||
|
||||
Rails.application ||= self
|
||||
# are these actually used?
|
||||
@initial_variable_values = initial_variable_values
|
||||
@block = block
|
||||
|
||||
add_lib_to_load_path!
|
||||
ActiveSupport.run_load_hooks(:before_configuration, self)
|
||||
|
||||
initial_variable_values.each do |variable_name, value|
|
||||
if INITIAL_VARIABLES.include?(variable_name)
|
||||
instance_variable_set("@#{variable_name}", value)
|
||||
end
|
||||
end
|
||||
|
||||
instance_eval(&block) if block_given?
|
||||
end
|
||||
|
||||
# Returns true if the application is initialized.
|
||||
|
@ -136,6 +138,21 @@ module Rails
|
|||
@initialized
|
||||
end
|
||||
|
||||
def run_load_hooks! # :nodoc:
|
||||
return self if @ran_load_hooks
|
||||
@ran_load_hooks = true
|
||||
ActiveSupport.run_load_hooks(:before_configuration, self)
|
||||
|
||||
@initial_variable_values.each do |variable_name, value|
|
||||
if INITIAL_VARIABLES.include?(variable_name)
|
||||
instance_variable_set("@#{variable_name}", value)
|
||||
end
|
||||
end
|
||||
|
||||
instance_eval(&@block) if @block
|
||||
self
|
||||
end
|
||||
|
||||
# Implements call according to the Rack API. It simply
|
||||
# dispatches the request to the underlying middleware stack.
|
||||
def call(env)
|
||||
|
|
|
@ -7,6 +7,7 @@ module Rails
|
|||
def initialize(*) # :nodoc:
|
||||
super
|
||||
@in_group = nil
|
||||
@after_bundle_callbacks = []
|
||||
end
|
||||
|
||||
# Adds an entry into +Gemfile+ for the supplied gem.
|
||||
|
@ -232,6 +233,16 @@ module Rails
|
|||
log File.read(find_in_source_paths(path))
|
||||
end
|
||||
|
||||
# Registers a callback to be executed after bundle and spring binstubs
|
||||
# have run.
|
||||
#
|
||||
# after_bundle do
|
||||
# git add: '.'
|
||||
# end
|
||||
def after_bundle(&block)
|
||||
@after_bundle_callbacks << block
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Define log for backwards compatibility. If just one argument is sent,
|
||||
|
|
|
@ -259,6 +259,12 @@ module Rails
|
|||
public_task :apply_rails_template, :run_bundle
|
||||
public_task :generate_spring_binstubs
|
||||
|
||||
def run_after_bundle_callbacks
|
||||
@after_bundle_callbacks.each do |callback|
|
||||
callback.call
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def self.banner
|
||||
|
|
|
@ -36,23 +36,23 @@ module ApplicationTests
|
|||
end
|
||||
|
||||
def test_initialization_of_application_with_previous_config
|
||||
application1 = AppTemplate::Application.new(config: Rails.application.config)
|
||||
application2 = AppTemplate::Application.new
|
||||
application1 = AppTemplate::Application.create(config: Rails.application.config)
|
||||
application2 = AppTemplate::Application.create
|
||||
|
||||
assert_equal Rails.application.config, application1.config, "Creating a new application while setting an initial config should result in the same config"
|
||||
assert_not_equal Rails.application.config, application2.config, "New applications without setting an initial config should not have the same config"
|
||||
end
|
||||
|
||||
def test_initialization_of_application_with_previous_railties
|
||||
application1 = AppTemplate::Application.new(railties: Rails.application.railties)
|
||||
application2 = AppTemplate::Application.new
|
||||
application1 = AppTemplate::Application.create(railties: Rails.application.railties)
|
||||
application2 = AppTemplate::Application.create
|
||||
|
||||
assert_equal Rails.application.railties, application1.railties
|
||||
assert_not_equal Rails.application.railties, application2.railties
|
||||
end
|
||||
|
||||
def test_initialize_new_application_with_all_previous_initialization_variables
|
||||
application1 = AppTemplate::Application.new(
|
||||
application1 = AppTemplate::Application.create(
|
||||
config: Rails.application.config,
|
||||
railties: Rails.application.railties,
|
||||
routes_reloader: Rails.application.routes_reloader,
|
||||
|
|
|
@ -67,7 +67,7 @@ module ApplicationTests
|
|||
assert_match %r{/app/test/unit/failing_test\.rb}, output
|
||||
end
|
||||
|
||||
test "migrations" do
|
||||
test "ruby schema migrations" do
|
||||
output = script('generate model user name:string')
|
||||
version = output.match(/(\d+)_create_users\.rb/)[1]
|
||||
|
||||
|
@ -104,6 +104,95 @@ module ApplicationTests
|
|||
assert !result.include?("create_table(:users)")
|
||||
end
|
||||
|
||||
test "sql structure migrations" do
|
||||
output = script('generate model user name:string')
|
||||
version = output.match(/(\d+)_create_users\.rb/)[1]
|
||||
|
||||
app_file 'test/models/user_test.rb', <<-RUBY
|
||||
require 'test_helper'
|
||||
|
||||
class UserTest < ActiveSupport::TestCase
|
||||
test "user" do
|
||||
User.create! name: "Jon"
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
app_file 'db/structure.sql', ''
|
||||
app_file 'config/initializers/enable_sql_schema_format.rb', <<-RUBY
|
||||
Rails.application.config.active_record.schema_format = :sql
|
||||
RUBY
|
||||
|
||||
assert_unsuccessful_run "models/user_test.rb", "Migrations are pending"
|
||||
|
||||
app_file 'db/structure.sql', <<-SQL
|
||||
CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL);
|
||||
CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version");
|
||||
CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255));
|
||||
INSERT INTO schema_migrations (version) VALUES ('#{version}');
|
||||
SQL
|
||||
|
||||
app_file 'config/initializers/disable_maintain_test_schema.rb', <<-RUBY
|
||||
Rails.application.config.active_record.maintain_test_schema = false
|
||||
RUBY
|
||||
|
||||
assert_unsuccessful_run "models/user_test.rb", "Could not find table 'users'"
|
||||
|
||||
File.delete "#{app_path}/config/initializers/disable_maintain_test_schema.rb"
|
||||
|
||||
assert_successful_test_run('models/user_test.rb')
|
||||
end
|
||||
|
||||
test "sql structure migrations when adding column to existing table" do
|
||||
output_1 = script('generate model user name:string')
|
||||
version_1 = output_1.match(/(\d+)_create_users\.rb/)[1]
|
||||
|
||||
app_file 'test/models/user_test.rb', <<-RUBY
|
||||
require 'test_helper'
|
||||
class UserTest < ActiveSupport::TestCase
|
||||
test "user" do
|
||||
User.create! name: "Jon"
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
app_file 'config/initializers/enable_sql_schema_format.rb', <<-RUBY
|
||||
Rails.application.config.active_record.schema_format = :sql
|
||||
RUBY
|
||||
|
||||
app_file 'db/structure.sql', <<-SQL
|
||||
CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL);
|
||||
CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version");
|
||||
CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255));
|
||||
INSERT INTO schema_migrations (version) VALUES ('#{version_1}');
|
||||
SQL
|
||||
|
||||
assert_successful_test_run('models/user_test.rb')
|
||||
|
||||
output_2 = script('generate migration add_email_to_users')
|
||||
version_2 = output_2.match(/(\d+)_add_email_to_users\.rb/)[1]
|
||||
|
||||
app_file 'test/models/user_test.rb', <<-RUBY
|
||||
require 'test_helper'
|
||||
|
||||
class UserTest < ActiveSupport::TestCase
|
||||
test "user" do
|
||||
User.create! name: "Jon", email: "jon@doe.com"
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
app_file 'db/structure.sql', <<-SQL
|
||||
CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL);
|
||||
CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version");
|
||||
CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "email" varchar(255));
|
||||
INSERT INTO schema_migrations (version) VALUES ('#{version_1}');
|
||||
INSERT INTO schema_migrations (version) VALUES ('#{version_2}');
|
||||
SQL
|
||||
|
||||
assert_successful_test_run('models/user_test.rb')
|
||||
end
|
||||
|
||||
private
|
||||
def assert_unsuccessful_run(name, message)
|
||||
result = run_test_file(name)
|
||||
|
|
|
@ -11,4 +11,15 @@ class EngineTest < ActiveSupport::TestCase
|
|||
|
||||
assert !engine.routes?
|
||||
end
|
||||
|
||||
def test_application_can_be_subclassed
|
||||
klass = Class.new(Rails::Application) do
|
||||
attr_reader :hello
|
||||
def initialize
|
||||
@hello = "world"
|
||||
super
|
||||
end
|
||||
end
|
||||
assert_equal "world", klass.instance.hello
|
||||
end
|
||||
end
|
||||
|
|
|
@ -501,6 +501,21 @@ class AppGeneratorTest < Rails::Generators::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_after_bundle_callback
|
||||
path = 'http://example.org/rails_template'
|
||||
template = %{ after_bundle { run 'echo ran after_bundle' } }
|
||||
template.instance_eval "def read; self; end" # Make the string respond to read
|
||||
|
||||
generator([destination_root], template: path).expects(:open).with(path, 'Accept' => 'application/x-thor-template').returns(template)
|
||||
|
||||
bundler_first = sequence('bundle, binstubs, after_bundle')
|
||||
generator.expects(:bundle_command).with('install').once.in_sequence(bundler_first)
|
||||
generator.expects(:bundle_command).with('exec spring binstub --all').in_sequence(bundler_first)
|
||||
generator.expects(:run).with('echo ran after_bundle').in_sequence(bundler_first)
|
||||
|
||||
quietly { generator.invoke_all }
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def action(*args, &block)
|
||||
|
|
Loading…
Reference in a new issue