Merge branch 'master' of github.com:rails/rails

This commit is contained in:
David Heinemeier Hansson 2014-08-07 16:10:20 -07:00
commit 089661b69a
37 changed files with 400 additions and 207 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,72 +25,47 @@ 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
get "/url_to_application", :to => "inside_engine_generating#url_to_application"
get "/polymorphic_path_for_engine", :to => "inside_engine_generating#polymorphic_path_for_engine"
get "/conflicting_url", :to => "inside_engine_generating#conflicting"
get "/foo", :to => "never#invoked", :as => :named_helper_that_should_be_invoked_only_in_respond_to_test
routes.draw do
get "/posts/:id", :to => "inside_engine_generating#show", :as => :post
get "/posts", :to => "inside_engine_generating#index", :as => :posts
get "/url_to_application", :to => "inside_engine_generating#url_to_application"
get "/polymorphic_path_for_engine", :to => "inside_engine_generating#polymorphic_path_for_engine"
get "/conflicting_url", :to => "inside_engine_generating#conflicting"
get "/foo", :to => "never#invoked", :as => :named_helper_that_should_be_invoked_only_in_respond_to_test
get "/relative_path_root", :to => redirect("")
get "/relative_path_redirect", :to => redirect("foo")
get "/relative_option_root", :to => redirect(:path => "")
get "/relative_option_redirect", :to => redirect(:path => "foo")
get "/relative_custom_root", :to => redirect { |params, request| "" }
get "/relative_custom_redirect", :to => redirect { |params, request| "foo" }
get "/relative_path_root", :to => redirect("")
get "/relative_path_redirect", :to => redirect("foo")
get "/relative_option_root", :to => redirect(:path => "")
get "/relative_option_redirect", :to => redirect(:path => "foo")
get "/relative_custom_root", :to => redirect { |params, request| "" }
get "/relative_custom_redirect", :to => redirect { |params, request| "foo" }
get "/absolute_path_root", :to => redirect("/")
get "/absolute_path_redirect", :to => redirect("/foo")
get "/absolute_option_root", :to => redirect(:path => "/")
get "/absolute_option_redirect", :to => redirect(:path => "/foo")
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)
get "/absolute_path_root", :to => redirect("/")
get "/absolute_path_redirect", :to => redirect("/foo")
get "/absolute_option_root", :to => redirect(:path => "/")
get "/absolute_option_redirect", :to => redirect(:path => "/foo")
get "/absolute_custom_root", :to => redirect { |params, request| "/" }
get "/absolute_custom_redirect", :to => redirect { |params, request| "/foo" }
end
end
class RailsApplication
def self.routes
@routes ||= begin
routes = ActionDispatch::Routing::RouteSet.new
routes.draw do
scope "/:omg", :omg => "awesome" do
mount BlogEngine => "/blog", :as => "blog_engine"
end
get "/posts/:id", :to => "outside_engine_generating#post", :as => :post
get "/generate", :to => "outside_engine_generating#index"
get "/polymorphic_path_for_app", :to => "outside_engine_generating#polymorphic_path_for_app"
get "/polymorphic_path_for_engine", :to => "outside_engine_generating#polymorphic_path_for_engine"
get "/polymorphic_with_url_for", :to => "outside_engine_generating#polymorphic_with_url_for"
get "/conflicting_url", :to => "outside_engine_generating#conflicting"
get "/ivar_usage", :to => "outside_engine_generating#ivar_usage"
root :to => "outside_engine_generating#index"
end
routes
class RailsApplication < Rails::Engine
routes.draw do
scope "/:omg", :omg => "awesome" do
mount BlogEngine => "/blog", :as => "blog_engine"
end
end
def self.call(env)
env['action_dispatch.routes'] = routes
routes.call(env)
get "/posts/:id", :to => "outside_engine_generating#post", :as => :post
get "/generate", :to => "outside_engine_generating#index"
get "/polymorphic_path_for_app", :to => "outside_engine_generating#polymorphic_path_for_app"
get "/polymorphic_path_for_engine", :to => "outside_engine_generating#polymorphic_path_for_engine"
get "/polymorphic_with_url_for", :to => "outside_engine_generating#polymorphic_with_url_for"
get "/conflicting_url", :to => "outside_engine_generating#conflicting"
get "/ivar_usage", :to => "outside_engine_generating#ivar_usage"
root :to => "outside_engine_generating#index"
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,27 +367,12 @@ module TestGenerationPrefix
end
end
class RailsApplication
def self.routes
@routes ||= begin
routes = ActionDispatch::Routing::RouteSet.new
routes.draw do
mount BlogEngine => "/"
end
routes
end
end
def self.call(env)
env['action_dispatch.routes'] = routes
routes.call(env)
class RailsApplication < Rails::Engine
routes.draw do
mount BlogEngine => "/"
end
end
# force draw
RailsApplication.routes
class ::PostsController < ActionController::Base
include BlogEngine.routes.url_helpers
include RailsApplication.routes.mounted_helpers
@ -423,7 +383,7 @@ module TestGenerationPrefix
end
def app
RailsApplication
RailsApplication.instance
end
test "generating path inside engine" do

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -38,9 +38,11 @@ generate(:scaffold, "person name:string")
route "root to: 'people#index'"
rake("db:migrate")
git :init
git add: "."
git commit: %Q{ -m 'Initial commit' }
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
--------------

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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