diff options
61 files changed, 480 insertions, 319 deletions
@@ -4,7 +4,7 @@ gemspec gem 'arel', github: 'rails/arel', branch: 'master' -gem 'mocha', '>= 0.11.2', require: false +gem 'mocha', github: 'freerange/mocha', require: false gem 'rack-test', github: 'brynary/rack-test' gem 'rack-cache', '~> 1.2' gem 'bcrypt-ruby', '~> 3.0.0' diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec index 0c669e2e91..0177a13e50 100644 --- a/actionmailer/actionmailer.gemspec +++ b/actionmailer/actionmailer.gemspec @@ -1,4 +1,4 @@ -version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip +version = File.read(File.expand_path('../../RAILS_VERSION', __FILE__)).strip Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY @@ -6,17 +6,20 @@ Gem::Specification.new do |s| s.version = version s.summary = 'Email composition, delivery, and receiving framework (part of Rails).' s.description = 'Email on Rails. Compose, deliver, receive, and test emails using the familiar controller/view pattern. First-class support for multipart email and attachments.' + s.required_ruby_version = '>= 1.9.3' - s.license = 'MIT' - s.author = 'David Heinemeier Hansson' - s.email = 'david@loudthinking.com' - s.homepage = 'http://www.rubyonrails.org' + s.license = 'MIT' + + s.author = 'David Heinemeier Hansson' + s.email = 'david@loudthinking.com' + s.homepage = 'http://www.rubyonrails.org' s.files = Dir['CHANGELOG.md', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*'] s.require_path = 'lib' s.requirements << 'none' - s.add_dependency('actionpack', version) - s.add_dependency('mail', '~> 2.4.4') + s.add_dependency 'actionpack', version + + s.add_dependency 'mail', '~> 2.4.4' end diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 72121668ac..e04eac739d 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,5 +1,21 @@ ## Rails 4.0.0 (unreleased) ## +* Fix input name when `:multiple => true` and `:index` are set. + + Before: + + check_box("post", "comment_ids", { :multiple => true, :index => "foo" }, 1) + #=> <input name=\"post[foo][comment_ids]\" type=\"hidden\" value=\"0\" /><input id=\"post_foo_comment_ids_1\" name=\"post[foo][comment_ids]\" type=\"checkbox\" value=\"1\" /> + + After: + + check_box("post", "comment_ids", { :multiple => true, :index => "foo" }, 1) + #=> <input name=\"post[foo][comment_ids][]\" type=\"hidden\" value=\"0\" /><input id=\"post_foo_comment_ids_1\" name=\"post[foo][comment_ids][]\" type=\"checkbox\" value=\"1\" /> + + Fix #8108 + + *Daniel Fox, Grant Hutchins & Trace Wax* + * Clear url helpers when reloading routes. *Santiago Pastorino* diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index 7d292ac17c..89fdd528c2 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -1,4 +1,4 @@ -version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip +version = File.read(File.expand_path('../../RAILS_VERSION', __FILE__)).strip Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY @@ -6,24 +6,26 @@ Gem::Specification.new do |s| s.version = version s.summary = 'Web-flow and rendering framework putting the VC in MVC (part of Rails).' s.description = 'Web apps on Rails. Simple, battle-tested conventions for building and testing MVC web applications. Works with any Rack-compatible server.' + s.required_ruby_version = '>= 1.9.3' - s.license = 'MIT' - s.author = 'David Heinemeier Hansson' - s.email = 'david@loudthinking.com' - s.homepage = 'http://www.rubyonrails.org' + s.license = 'MIT' + + s.author = 'David Heinemeier Hansson' + s.email = 'david@loudthinking.com' + s.homepage = 'http://www.rubyonrails.org' s.files = Dir['CHANGELOG.md', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*'] s.require_path = 'lib' s.requirements << 'none' - s.add_dependency('activesupport', version) - s.add_dependency('builder', '~> 3.1.0') - s.add_dependency('rack', '~> 1.4.1') - s.add_dependency('rack-test', '~> 0.6.1') - s.add_dependency('journey', '~> 2.0.0') - s.add_dependency('erubis', '~> 2.7.0') + s.add_dependency 'activesupport', version + s.add_dependency 'builder', '~> 3.1.0' + s.add_dependency 'rack', '~> 1.4.1' + s.add_dependency 'rack-test', '~> 0.6.1' + s.add_dependency 'journey', '~> 2.0.0' + s.add_dependency 'erubis', '~> 2.7.0' - s.add_development_dependency('activemodel', version) - s.add_development_dependency('tzinfo', '~> 0.3.33') + s.add_development_dependency 'activemodel', version + s.add_development_dependency 'tzinfo', '~> 0.3.33' end diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index bd0bcd23ff..da640502a2 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -259,7 +259,9 @@ module ActionController # params.slice(:a, :b) # => {"a"=>1, "b"=>2} # params.slice(:d) # => {} def slice(*keys) - self.class.new(super) + self.class.new(super).tap do |new_instance| + new_instance.instance_variable_set :@permitted, @permitted + end end # Returns an exact copy of the <tt>ActionController::Parameters</tt> diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index 2b5d3d85bf..f56f09c5b3 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -288,18 +288,23 @@ module Mime @@html_types.include?(to_sym) || @string =~ /html/ end + private - def method_missing(method, *args) - if method.to_s.ends_with? '?' - method[0..-2].downcase.to_sym == to_sym - else - super - end - end - def respond_to_missing?(method, include_private = false) #:nodoc: - method.to_s.ends_with? '?' + def to_ary; end + def to_a; end + + def method_missing(method, *args) + if method.to_s.ends_with? '?' + method[0..-2].downcase.to_sym == to_sym + else + super end + end + + def respond_to_missing?(method, include_private = false) #:nodoc: + method.to_s.ends_with? '?' + end end end diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 0de10695e0..2b37a8d026 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -28,7 +28,7 @@ module ActionDispatch def call(env) begin - response = @app.call(env) + response = @app.call(env) rescue Exception => exception raise exception if env['action_dispatch.show_exceptions'] == false end diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 045299281c..2311afc6c1 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -491,9 +491,7 @@ module ActionDispatch prefix_options = options.slice(*_route.segment_keys) # we must actually delete prefix segment keys to avoid passing them to next url_for _route.segment_keys.each { |k| options.delete(k) } - prefix = _routes.url_helpers.send("#{name}_path", prefix_options) - prefix = '' if prefix == '/' - prefix + _routes.url_helpers.send("#{name}_path", prefix_options) end end end diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index b7b3db959e..46ebe60ec2 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -152,7 +152,7 @@ module ActionView # form, and parameters extraction gets the last occurrence of any repeated # key in the query string, that works for ordinary forms. # - # In case if you don't want the helper to generate this hidden field you can specify <tt>include_blank: false</tt> option. + # In case if you don't want the helper to generate this hidden field you can specify <tt>include_hidden: false</tt> option. # def select(object, method, choices, options = {}, html_options = {}) Tags::Select.new(object, method, self, choices, options, html_options).render diff --git a/actionpack/lib/action_view/path_set.rb b/actionpack/lib/action_view/path_set.rb index bbb1af8154..d9c76366f8 100644 --- a/actionpack/lib/action_view/path_set.rb +++ b/actionpack/lib/action_view/path_set.rb @@ -5,6 +5,8 @@ module ActionView #:nodoc: attr_reader :paths + delegate :[], :include?, :pop, :size, :each, to: :paths + def initialize(paths = []) @paths = typecast paths end @@ -14,30 +16,10 @@ module ActionView #:nodoc: self end - def [](i) - paths[i] - end - def to_ary paths.dup end - def include?(item) - paths.include? item - end - - def pop - paths.pop - end - - def size - paths.size - end - - def each(&block) - paths.each(&block) - end - def compact PathSet.new paths.compact end diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb index fc63470174..7cc71fe6dc 100644 --- a/actionpack/test/controller/parameters/parameters_permit_test.rb +++ b/actionpack/test/controller/parameters/parameters_permit_test.rb @@ -20,26 +20,51 @@ class ParametersPermitTest < ActiveSupport::TestCase assert_equal "monkey", @params.fetch(:foo) { "monkey" } end - test "permitted is sticky on accessors" do + test "not permitted is sticky on accessors" do assert !@params.slice(:person).permitted? assert !@params[:person][:name].permitted? + assert !@params[:person].except(:name).permitted? - @params.each { |key, value| assert(value.permitted?) if key == :person } + @params.each { |key, value| assert(!value.permitted?) if key == "person" } assert !@params.fetch(:person).permitted? assert !@params.values_at(:person).first.permitted? end + test "permitted is sticky on accessors" do + @params.permit! + assert @params.slice(:person).permitted? + assert @params[:person][:name].permitted? + assert @params[:person].except(:name).permitted? + + @params.each { |key, value| assert(value.permitted?) if key == "person" } + + assert @params.fetch(:person).permitted? + + assert @params.values_at(:person).first.permitted? + end + + test "not permitted is sticky on mutators" do + assert !@params.delete_if { |k| k == "person" }.permitted? + assert !@params.keep_if { |k,v| k == "person" }.permitted? + end + test "permitted is sticky on mutators" do - assert !@params.delete_if { |k| k == :person }.permitted? - assert !@params.keep_if { |k,v| k == :person }.permitted? + @params.permit! + assert @params.delete_if { |k| k == "person" }.permitted? + assert @params.keep_if { |k,v| k == "person" }.permitted? end - test "permitted is sticky beyond merges" do + test "not permitted is sticky beyond merges" do assert !@params.merge(a: "b").permitted? end + test "permitted is sticky beyond merges" do + @params.permit! + assert @params.merge(a: "b").permitted? + end + test "modifying the parameters" do @params[:person][:hometown] = "Chicago" @params[:person][:family] = { brother: "Jonas" } @@ -77,7 +102,7 @@ class ParametersPermitTest < ActiveSupport::TestCase ActionController::Parameters.permit_all_parameters = false end end - + test "permitting parameters as an array" do assert_equal "32", @params[:person].permit([ :age ])[:age] end diff --git a/actionpack/test/controller/show_exceptions_test.rb b/actionpack/test/controller/show_exceptions_test.rb index ab1bd0e3b6..718d06ef38 100644 --- a/actionpack/test/controller/show_exceptions_test.rb +++ b/actionpack/test/controller/show_exceptions_test.rb @@ -104,7 +104,7 @@ module ShowExceptions get '/', {}, 'HTTP_ACCEPT' => 'text/json' assert_response :internal_server_error assert_equal 'text/plain', response.content_type.to_s - + ensure @app.instance_variable_set(:@exceptions_app, @exceptions_app) $stderr = STDERR end diff --git a/actionpack/test/dispatch/prefix_generation_test.rb b/actionpack/test/dispatch/prefix_generation_test.rb index cfbf970a37..113608ecf4 100644 --- a/actionpack/test/dispatch/prefix_generation_test.rb +++ b/actionpack/test/dispatch/prefix_generation_test.rb @@ -241,6 +241,11 @@ module TestGenerationPrefix assert_equal "/something/", app_object.root_path end + test "[OBJECT] generating application's route includes default_url_options[:trailing_slash]" do + RailsApplication.routes.default_url_options[:trailing_slash] = true + assert_equal "/awesome/blog/posts", engine_object.posts_path + end + test "[OBJECT] generating engine's route with url_for" do path = engine_object.url_for(:controller => "inside_engine_generating", :action => "show", diff --git a/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec index be5d5d3ca8..51655fe3da 100644 --- a/activemodel/activemodel.gemspec +++ b/activemodel/activemodel.gemspec @@ -8,15 +8,17 @@ Gem::Specification.new do |s| s.description = 'A toolkit for building modeling frameworks like Active Record. Rich support for attributes, callbacks, validations, observers, serialization, internationalization, and testing.' s.required_ruby_version = '>= 1.9.3' - s.license = 'MIT' - s.author = 'David Heinemeier Hansson' - s.email = 'david@loudthinking.com' - s.homepage = 'http://www.rubyonrails.org' + s.license = 'MIT' + + s.author = 'David Heinemeier Hansson' + s.email = 'david@loudthinking.com' + s.homepage = 'http://www.rubyonrails.org' s.files = Dir['CHANGELOG.md', 'MIT-LICENSE', 'README.rdoc', 'lib/**/*'] s.require_path = 'lib' - s.add_dependency('activesupport', version) - s.add_dependency('builder', '~> 3.1.0') + s.add_dependency 'activesupport', version + + s.add_dependency 'builder', '~> 3.1.0' end diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 6882b59e26..c82d4f012c 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -308,7 +308,7 @@ module ActiveModel # person.errors.messages # # => {:name=>["can't be empty"]} def add_on_empty(attributes, options = {}) - [attributes].flatten.each do |attribute| + Array(attributes).each do |attribute| value = @base.send(:read_attribute_for_validation, attribute) is_empty = value.respond_to?(:empty?) ? value.empty? : false add(attribute, :empty, options) if value.nil? || is_empty @@ -322,7 +322,7 @@ module ActiveModel # person.errors.messages # # => {:name=>["can't be blank"]} def add_on_blank(attributes, options = {}) - [attributes].flatten.each do |attribute| + Array(attributes).each do |attribute| value = @base.send(:read_attribute_for_validation, attribute) add(attribute, :blank, options) if value.blank? end diff --git a/activemodel/lib/active_model/forbidden_attributes_protection.rb b/activemodel/lib/active_model/forbidden_attributes_protection.rb index 4c05b19cba..7468f95548 100644 --- a/activemodel/lib/active_model/forbidden_attributes_protection.rb +++ b/activemodel/lib/active_model/forbidden_attributes_protection.rb @@ -16,7 +16,7 @@ module ActiveModel module ForbiddenAttributesProtection # :nodoc: protected - def sanitize_for_mass_assignment(attributes, options = {}) + def sanitize_for_mass_assignment(attributes) if attributes.respond_to?(:permitted?) && !attributes.permitted? raise ActiveModel::ForbiddenAttributesError else diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb index 4651154934..1eb0716891 100644 --- a/activemodel/lib/active_model/validations/validates.rb +++ b/activemodel/lib/active_model/validations/validates.rb @@ -104,7 +104,7 @@ module ActiveModel raise ArgumentError, "You need to supply at least one attribute" if attributes.empty? raise ArgumentError, "You need to supply at least one validation" if validations.empty? - defaults.merge!(:attributes => attributes) + defaults[:attributes] = attributes validations.each do |key, options| next unless options diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb index c795dc9dcd..629b157fed 100644 --- a/activemodel/lib/active_model/validator.rb +++ b/activemodel/lib/active_model/validator.rb @@ -135,7 +135,7 @@ module ActiveModel # and instead be made available through the +attributes+ reader. def initialize(options) @attributes = Array(options.delete(:attributes)) - raise ":attributes cannot be blank" if @attributes.empty? + raise ArgumentError, ":attributes cannot be blank" if @attributes.empty? super check_validity! end diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb index 3bc0d58351..293ce07f4e 100644 --- a/activemodel/test/cases/errors_test.rb +++ b/activemodel/test/cases/errors_test.rb @@ -7,7 +7,7 @@ class ErrorsTest < ActiveModel::TestCase @errors = ActiveModel::Errors.new(self) end - attr_accessor :name + attr_accessor :name, :age attr_reader :errors def validate! @@ -201,5 +201,43 @@ class ErrorsTest < ActiveModel::TestCase person.errors.generate_message(:name, :blank) } end + + test "add_on_empty generates message" do + person = Person.new + person.errors.expects(:generate_message).with(:name, :empty, {}) + person.errors.add_on_empty :name + end + + test "add_on_empty generates message for multiple attributes" do + person = Person.new + person.errors.expects(:generate_message).with(:name, :empty, {}) + person.errors.expects(:generate_message).with(:age, :empty, {}) + person.errors.add_on_empty [:name, :age] + end + + test "add_on_empty generates message with custom default message" do + person = Person.new + person.errors.expects(:generate_message).with(:name, :empty, {:message => 'custom'}) + person.errors.add_on_empty :name, :message => 'custom' + end + + test "add_on_blank generates message" do + person = Person.new + person.errors.expects(:generate_message).with(:name, :blank, {}) + person.errors.add_on_blank :name + end + + test "add_on_blank generates message for multiple attributes" do + person = Person.new + person.errors.expects(:generate_message).with(:name, :blank, {}) + person.errors.expects(:generate_message).with(:age, :blank, {}) + person.errors.add_on_blank [:name, :age] + end + + test "add_on_blank generates message with custom default message" do + person = Person.new + person.errors.expects(:generate_message).with(:name, :blank, {:message => 'custom'}) + person.errors.add_on_blank :name, :message => 'custom' + end end diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb index 4f8b7327c0..4c01b47608 100644 --- a/activemodel/test/cases/validations/i18n_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_validation_test.rb @@ -21,26 +21,6 @@ class I18nValidationTest < ActiveModel::TestCase I18n.backend = @old_backend end - def test_errors_add_on_empty_generates_message - @person.errors.expects(:generate_message).with(:title, :empty, {}) - @person.errors.add_on_empty :title - end - - def test_errors_add_on_empty_generates_message_with_custom_default_message - @person.errors.expects(:generate_message).with(:title, :empty, {:message => 'custom'}) - @person.errors.add_on_empty :title, :message => 'custom' - end - - def test_errors_add_on_blank_generates_message - @person.errors.expects(:generate_message).with(:title, :blank, {}) - @person.errors.add_on_blank :title - end - - def test_errors_add_on_blank_generates_message_with_custom_default_message - @person.errors.expects(:generate_message).with(:title, :blank, {:message => 'custom'}) - @person.errors.add_on_blank :title, :message => 'custom' - end - def test_full_message_encoding I18n.backend.store_translations('en', :errors => { :messages => { :too_short => '猫舌' }}) diff --git a/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb index 07c1bd0533..457f553661 100644 --- a/activemodel/test/cases/validations/with_validation_test.rb +++ b/activemodel/test/cases/validations/with_validation_test.rb @@ -151,7 +151,7 @@ class ValidatesWithTest < ActiveModel::TestCase end test "each validator expects attributes to be given" do - assert_raise RuntimeError do + assert_raise ArgumentError do Topic.validates_with(ValidatorPerEachAttribute) end end diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index 53791d96ef..31ddb01123 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -1,4 +1,4 @@ -version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip +version = File.read(File.expand_path('../../RAILS_VERSION', __FILE__)).strip Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY @@ -8,21 +8,22 @@ Gem::Specification.new do |s| s.description = 'Databases on Rails. Build a persistent domain model by mapping database tables to Ruby classes. Strong conventions for associations, validations, aggregations, migrations, and testing come baked-in.' s.required_ruby_version = '>= 1.9.3' - s.license = 'MIT' - s.author = 'David Heinemeier Hansson' - s.email = 'david@loudthinking.com' - s.homepage = 'http://www.rubyonrails.org' + s.license = 'MIT' + + s.author = 'David Heinemeier Hansson' + s.email = 'david@loudthinking.com' + s.homepage = 'http://www.rubyonrails.org' s.files = Dir['CHANGELOG.md', 'MIT-LICENSE', 'README.rdoc', 'examples/**/*', 'lib/**/*'] s.require_path = 'lib' - s.extra_rdoc_files = %w( README.rdoc ) + s.extra_rdoc_files = %w(README.rdoc) s.rdoc_options.concat ['--main', 'README.rdoc'] - s.add_dependency('activesupport', version) - s.add_dependency('activemodel', version) - s.add_dependency('arel', '~> 3.0.2') + s.add_dependency 'activesupport', version + s.add_dependency 'activemodel', version - s.add_dependency('activerecord-deprecated_finders', '0.0.1') + s.add_dependency 'arel', '~> 3.0.2' + s.add_dependency 'activerecord-deprecated_finders', '0.0.1' end diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 5dd833e5a6..5949269f2a 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1,6 +1,9 @@ require 'active_support/core_ext/enumerable' require 'active_support/core_ext/string/conversions' require 'active_support/core_ext/module/remove_method' +require 'active_support/dependencies/autoload' +require 'active_support/concern' +require 'active_record/errors' module ActiveRecord class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc: diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb index 1b382f7285..fcdfc1e150 100644 --- a/activerecord/lib/active_record/associations/builder/collection_association.rb +++ b/activerecord/lib/active_record/associations/builder/collection_association.rb @@ -1,5 +1,8 @@ +require 'active_record/associations' + module ActiveRecord::Associations::Builder class CollectionAssociation < Association #:nodoc: + CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove] def valid_options diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 54215cf88d..862ff201de 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -174,8 +174,6 @@ module ActiveRecord # association, it will be used for the query. Otherwise, construct options and pass them with # scope to the target class's +count+. def count(column_name = nil, count_options = {}) - return 0 if owner.new_record? - column_name, count_options = nil, column_name if column_name.is_a?(Hash) if options[:counter_sql] || options[:finder_sql] @@ -366,6 +364,16 @@ module ActiveRecord record end + def scope(opts = {}) + scope = super() + scope.none! if opts.fetch(:nullify, true) && null_scope? + scope + end + + def null_scope? + owner.new_record? && !foreign_key_present? + end + private def custom_counter_sql diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index e73f940334..1b746b46b7 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -28,10 +28,12 @@ module ActiveRecord # is computed directly through SQL and does not trigger by itself the # instantiation of the actual post records. class CollectionProxy < Relation + delegate *ActiveRecord::Calculations.public_instance_methods, to: :scope + def initialize(association) #:nodoc: @association = association super association.klass, association.klass.arel_table - merge! association.scope + merge! association.scope(nullify: false) end def target diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index eabbd80f66..a694a292fe 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -8,7 +8,6 @@ require 'active_support/core_ext/class/attribute_accessors' require 'active_support/core_ext/class/delegating_attributes' require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/hash/deep_merge' -require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/string/behavior' require 'active_support/core_ext/kernel/singleton_class' diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index c3b9a0f9b7..22347fcaef 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -642,7 +642,11 @@ module ActiveRecord def proper_table_name(name) # Use the Active Record objects own table_name, or pre/suffix from ActiveRecord::Base if name is a symbol/string - name.table_name rescue "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}" + if name.respond_to? :table_name + name.table_name + else + "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}" + end end def migrations_paths diff --git a/activerecord/lib/active_record/null_relation.rb b/activerecord/lib/active_record/null_relation.rb index 4c1c91e3df..711fc8b883 100644 --- a/activerecord/lib/active_record/null_relation.rb +++ b/activerecord/lib/active_record/null_relation.rb @@ -46,7 +46,11 @@ module ActiveRecord {} end - def count + def count(*) + 0 + end + + def sum(*) 0 end diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 99c2f45bc8..af67b2ba6c 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -1,5 +1,3 @@ -require 'active_support/core_ext/hash/indifferent_access' - module ActiveRecord module FinderMethods # Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]). @@ -225,7 +223,7 @@ module ActiveRecord def construct_limited_ids_condition(relation) orders = relation.order_values.map { |val| val.presence }.compact - values = @klass.connection.distinct("#{@klass.connection.quote_table_name table_name}.#{primary_key}", orders) + values = @klass.connection.distinct("#{quoted_table_name}.#{primary_key}", orders) relation = relation.dup @@ -234,8 +232,6 @@ module ActiveRecord end def find_with_ids(*ids) - return to_a.find { |*block_args| yield(*block_args) } if block_given? - expects_array = ids.first.kind_of?(Array) return ids.first if expects_array && ids.first.empty? diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 4fdc296c7e..0817bb6d81 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -218,7 +218,6 @@ module ActiveRecord # Like #order, but modifies relation in place. def order!(*args) args.flatten! - validate_order_args args references = args.reject { |arg| Arel::Node === arg } @@ -245,7 +244,6 @@ module ActiveRecord # Like #reorder, but modifies relation in place. def reorder!(*args) args.flatten! - validate_order_args args self.reordering_value = true @@ -796,7 +794,7 @@ module ActiveRecord def reverse_sql_order(order_query) order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty? - order_query.map do |o| + order_query.flat_map do |o| case o when Arel::Nodes::Ordering o.reverse @@ -814,7 +812,7 @@ module ActiveRecord else o end - end.flatten + end end def array_of_strings?(o) @@ -825,7 +823,7 @@ module ActiveRecord orders = order_values orders = reverse_sql_order(orders) if reverse_order_value - orders = orders.uniq.reject(&:blank?).map do |order| + orders = orders.uniq.reject(&:blank?).flat_map do |order| case order when Symbol table[order].asc @@ -834,7 +832,7 @@ module ActiveRecord else order end - end.flatten + end arel.order(*orders) unless orders.empty? end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 8077222e30..45b0a76f5a 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -1656,4 +1656,15 @@ class HasManyAssociationsTest < ActiveRecord::TestCase klass = Class.new(ActiveRecord::Base) assert_deprecated { klass.has_many :foo, :counter_sql => 'lol' } end + + test "has many associations on new records use null relations" do + post = Post.new + + assert_no_queries do + assert_equal [], post.comments + assert_equal [], post.comments.where(body: 'omg') + assert_equal [], post.comments.pluck(:body) + assert_equal 0, post.comments.sum(:id) + end + end end diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index 9674f2ce94..3f08f9ea4d 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -185,6 +185,17 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase assert_equal "James", mean_pirate.parrot.name assert_equal "blue", mean_pirate.parrot.color end + + def test_accepts_nested_attributes_for_can_be_overridden_in_subclasses + Pirate.accepts_nested_attributes_for(:parrot) + + mean_pirate_class = Class.new(Pirate) do + accepts_nested_attributes_for :parrot + end + mean_pirate = mean_pirate_class.new + mean_pirate.parrot_attributes = { :name => "James" } + assert_equal "James", mean_pirate.parrot.name + end end class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 0781abb6ed..61d85543d1 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,4 +1,5 @@ ## Rails 4.0.0 (unreleased) ## +* Kernel#capture can catch output from subprocesses *Dmitry Vorotilin* * `to_xml` conversions now use builder's `tag!` method instead of explicit invocation of `method_missing`. diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec index 30f4ded005..a4216d2cb4 100644 --- a/activesupport/activesupport.gemspec +++ b/activesupport/activesupport.gemspec @@ -1,4 +1,4 @@ -version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip +version = File.read(File.expand_path('../../RAILS_VERSION', __FILE__)).strip Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY @@ -8,19 +8,20 @@ Gem::Specification.new do |s| s.description = 'A toolkit of support libraries and Ruby core extensions extracted from the Rails framework. Rich support for multibyte strings, internationalization, time zones, and testing.' s.required_ruby_version = '>= 1.9.3' - s.license = 'MIT' - s.author = 'David Heinemeier Hansson' - s.email = 'david@loudthinking.com' - s.homepage = 'http://www.rubyonrails.org' + s.license = 'MIT' + + s.author = 'David Heinemeier Hansson' + s.email = 'david@loudthinking.com' + s.homepage = 'http://www.rubyonrails.org' s.files = Dir['CHANGELOG.md', 'MIT-LICENSE', 'README.rdoc', 'lib/**/*'] s.require_path = 'lib' s.rdoc_options.concat ['--encoding', 'UTF-8'] - s.add_dependency('i18n', '~> 0.6') - s.add_dependency('multi_json', '~> 1.3') - s.add_dependency('tzinfo', '~> 0.3.33') - s.add_dependency('minitest', '~> 4.1') + s.add_dependency 'i18n', '~> 0.6' + s.add_dependency 'multi_json', '~> 1.3' + s.add_dependency 'tzinfo', '~> 0.3.33' + s.add_dependency 'minitest', '~> 4.1' end diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index 02ae57b4a6..439d380af7 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -8,6 +8,8 @@ require 'active_support/core_ext/date_and_time/calculations' class Date include DateAndTime::Calculations + @beginning_of_week_default = nil + class << self attr_accessor :beginning_of_week_default diff --git a/activesupport/lib/active_support/core_ext/kernel/reporting.rb b/activesupport/lib/active_support/core_ext/kernel/reporting.rb index bc97da6ef2..7b518821c8 100644 --- a/activesupport/lib/active_support/core_ext/kernel/reporting.rb +++ b/activesupport/lib/active_support/core_ext/kernel/reporting.rb @@ -1,4 +1,5 @@ require 'rbconfig' +require 'tempfile' module Kernel # Sets $VERBOSE to nil for the duration of the block and back to its original @@ -66,19 +67,33 @@ module Kernel # Captures the given stream and returns it: # - # stream = capture(:stdout) { puts 'Cool' } - # stream # => "Cool\n" + # stream = capture(:stdout) { puts 'notice' } + # stream # => "notice\n" + # + # stream = capture(:stderr) { warn 'error' } + # stream # => "error\n" + # + # even for subprocesses: + # + # stream = capture(:stdout) { system('echo notice') } + # stream # => "notice\n" + # + # stream = capture(:stderr) { system('echo error 1>&2') } + # stream # => "error\n" def capture(stream) - begin - stream = stream.to_s - eval "$#{stream} = StringIO.new" - yield - result = eval("$#{stream}").string - ensure - eval("$#{stream} = #{stream.upcase}") - end + stream = stream.to_s + captured_stream = Tempfile.new(stream) + stream_io = eval("$#{stream}") + origin_stream = stream_io.dup + stream_io.reopen(captured_stream) + + yield - result + stream_io.rewind + return captured_stream.read + ensure + captured_stream.unlink + stream_io.reopen(origin_stream) end alias :silence :capture diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb index 139d48f59c..796c5f9805 100644 --- a/activesupport/lib/active_support/core_ext/time/zones.rb +++ b/activesupport/lib/active_support/core_ext/time/zones.rb @@ -1,6 +1,8 @@ require 'active_support/time_with_zone' class Time + @zone_default = nil + class << self attr_accessor :zone_default diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index 3910a2dc42..1eb2b4212b 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -286,10 +286,12 @@ module ActiveSupport # ordinal(-11) # => "th" # ordinal(-1021) # => "st" def ordinal(number) - if (11..13).include?(number.to_i.abs % 100) + abs_number = number.to_i.abs + + if (11..13).include?(abs_number % 100) "th" else - case number.to_i.abs % 10 + case abs_number % 10 when 1; "st" when 2; "nd" when 3; "rd" diff --git a/activesupport/lib/active_support/testing/mocha_module.rb b/activesupport/lib/active_support/testing/mocha_module.rb index ed2942d23a..833dc867f0 100644 --- a/activesupport/lib/active_support/testing/mocha_module.rb +++ b/activesupport/lib/active_support/testing/mocha_module.rb @@ -2,7 +2,7 @@ module ActiveSupport module Testing module MochaModule begin - require 'mocha_standalone' + require 'mocha/api' include Mocha::API def before_setup diff --git a/activesupport/lib/active_support/testing/tagged_logging.rb b/activesupport/lib/active_support/testing/tagged_logging.rb index 899467c45f..8ea2605733 100644 --- a/activesupport/lib/active_support/testing/tagged_logging.rb +++ b/activesupport/lib/active_support/testing/tagged_logging.rb @@ -1,10 +1,6 @@ -require 'active_support/concern' - module ActiveSupport module Testing module TaggedLogging - extend ActiveSupport::Concern - attr_writer :tagged_logger def before_setup diff --git a/activesupport/test/constantize_test_cases.rb b/activesupport/test/constantize_test_cases.rb index ec05213409..9b62295c96 100644 --- a/activesupport/test/constantize_test_cases.rb +++ b/activesupport/test/constantize_test_cases.rb @@ -24,52 +24,52 @@ end module ConstantizeTestCases def run_constantize_tests_on - assert_nothing_raised { assert_equal Ace::Base::Case, yield("Ace::Base::Case") } - assert_nothing_raised { assert_equal Ace::Base::Case, yield("::Ace::Base::Case") } - assert_nothing_raised { assert_equal Ace::Base::Case::Dice, yield("Ace::Base::Case::Dice") } - assert_nothing_raised { assert_equal Ace::Base::Fase::Dice, yield("Ace::Base::Fase::Dice") } - assert_nothing_raised { assert_equal Ace::Gas::Case, yield("Ace::Gas::Case") } - assert_nothing_raised { assert_equal Ace::Gas::Case::Dice, yield("Ace::Gas::Case::Dice") } - assert_nothing_raised { assert_equal Case::Dice, yield("Case::Dice") } - assert_nothing_raised { assert_equal Case::Dice, yield("Object::Case::Dice") } - assert_nothing_raised { assert_equal ConstantizeTestCases, yield("ConstantizeTestCases") } - assert_nothing_raised { assert_equal ConstantizeTestCases, yield("::ConstantizeTestCases") } - assert_nothing_raised { assert_equal Object, yield("") } - assert_nothing_raised { assert_equal Object, yield("::") } - assert_raise(NameError) { yield("UnknownClass") } - assert_raise(NameError) { yield("UnknownClass::Ace") } - assert_raise(NameError) { yield("UnknownClass::Ace::Base") } - assert_raise(NameError) { yield("An invalid string") } - assert_raise(NameError) { yield("InvalidClass\n") } - assert_raise(NameError) { yield("Ace::ConstantizeTestCases") } - assert_raise(NameError) { yield("Ace::Base::ConstantizeTestCases") } - assert_raise(NameError) { yield("Ace::Gas::Base") } - assert_raise(NameError) { yield("Ace::Gas::ConstantizeTestCases") } + assert_equal Ace::Base::Case, yield("Ace::Base::Case") + assert_equal Ace::Base::Case, yield("::Ace::Base::Case") + assert_equal Ace::Base::Case::Dice, yield("Ace::Base::Case::Dice") + assert_equal Ace::Base::Fase::Dice, yield("Ace::Base::Fase::Dice") + assert_equal Ace::Gas::Case, yield("Ace::Gas::Case") + assert_equal Ace::Gas::Case::Dice, yield("Ace::Gas::Case::Dice") + assert_equal Case::Dice, yield("Case::Dice") + assert_equal Case::Dice, yield("Object::Case::Dice") + assert_equal ConstantizeTestCases, yield("ConstantizeTestCases") + assert_equal ConstantizeTestCases, yield("::ConstantizeTestCases") + assert_equal Object, yield("") + assert_equal Object, yield("::") + assert_raises(NameError) { yield("UnknownClass") } + assert_raises(NameError) { yield("UnknownClass::Ace") } + assert_raises(NameError) { yield("UnknownClass::Ace::Base") } + assert_raises(NameError) { yield("An invalid string") } + assert_raises(NameError) { yield("InvalidClass\n") } + assert_raises(NameError) { yield("Ace::ConstantizeTestCases") } + assert_raises(NameError) { yield("Ace::Base::ConstantizeTestCases") } + assert_raises(NameError) { yield("Ace::Gas::Base") } + assert_raises(NameError) { yield("Ace::Gas::ConstantizeTestCases") } end def run_safe_constantize_tests_on - assert_nothing_raised { assert_equal Ace::Base::Case, yield("Ace::Base::Case") } - assert_nothing_raised { assert_equal Ace::Base::Case, yield("::Ace::Base::Case") } - assert_nothing_raised { assert_equal Ace::Base::Case::Dice, yield("Ace::Base::Case::Dice") } - assert_nothing_raised { assert_equal Ace::Base::Fase::Dice, yield("Ace::Base::Fase::Dice") } - assert_nothing_raised { assert_equal Ace::Gas::Case, yield("Ace::Gas::Case") } - assert_nothing_raised { assert_equal Ace::Gas::Case::Dice, yield("Ace::Gas::Case::Dice") } - assert_nothing_raised { assert_equal Case::Dice, yield("Case::Dice") } - assert_nothing_raised { assert_equal Case::Dice, yield("Object::Case::Dice") } - assert_nothing_raised { assert_equal ConstantizeTestCases, yield("ConstantizeTestCases") } - assert_nothing_raised { assert_equal ConstantizeTestCases, yield("::ConstantizeTestCases") } - assert_nothing_raised { assert_equal Object, yield("") } - assert_nothing_raised { assert_equal Object, yield("::") } - assert_nothing_raised { assert_equal nil, yield("UnknownClass") } - assert_nothing_raised { assert_equal nil, yield("UnknownClass::Ace") } - assert_nothing_raised { assert_equal nil, yield("UnknownClass::Ace::Base") } - assert_nothing_raised { assert_equal nil, yield("An invalid string") } - assert_nothing_raised { assert_equal nil, yield("InvalidClass\n") } - assert_nothing_raised { assert_equal nil, yield("blargle") } - assert_nothing_raised { assert_equal nil, yield("Ace::ConstantizeTestCases") } - assert_nothing_raised { assert_equal nil, yield("Ace::Base::ConstantizeTestCases") } - assert_nothing_raised { assert_equal nil, yield("Ace::Gas::Base") } - assert_nothing_raised { assert_equal nil, yield("Ace::Gas::ConstantizeTestCases") } - assert_nothing_raised { assert_equal nil, yield("#<Class:0x7b8b718b>::Nested_1") } + assert_equal Ace::Base::Case, yield("Ace::Base::Case") + assert_equal Ace::Base::Case, yield("::Ace::Base::Case") + assert_equal Ace::Base::Case::Dice, yield("Ace::Base::Case::Dice") + assert_equal Ace::Base::Fase::Dice, yield("Ace::Base::Fase::Dice") + assert_equal Ace::Gas::Case, yield("Ace::Gas::Case") + assert_equal Ace::Gas::Case::Dice, yield("Ace::Gas::Case::Dice") + assert_equal Case::Dice, yield("Case::Dice") + assert_equal Case::Dice, yield("Object::Case::Dice") + assert_equal ConstantizeTestCases, yield("ConstantizeTestCases") + assert_equal ConstantizeTestCases, yield("::ConstantizeTestCases") + assert_equal Object, yield("") + assert_equal Object, yield("::") + assert_nil yield("UnknownClass") + assert_nil yield("UnknownClass::Ace") + assert_nil yield("UnknownClass::Ace::Base") + assert_nil yield("An invalid string") + assert_nil yield("InvalidClass\n") + assert_nil yield("blargle") + assert_nil yield("Ace::ConstantizeTestCases") + assert_nil yield("Ace::Base::ConstantizeTestCases") + assert_nil yield("Ace::Gas::Base") + assert_nil yield("Ace::Gas::ConstantizeTestCases") + assert_nil yield("#<Class:0x7b8b718b>::Nested_1") end end diff --git a/activesupport/test/core_ext/kernel_test.rb b/activesupport/test/core_ext/kernel_test.rb index 439bc87323..1583c1fa32 100644 --- a/activesupport/test/core_ext/kernel_test.rb +++ b/activesupport/test/core_ext/kernel_test.rb @@ -51,6 +51,8 @@ class KernelTest < ActiveSupport::TestCase def test_capture assert_equal 'STDERR', capture(:stderr) { $stderr.print 'STDERR' } assert_equal 'STDOUT', capture(:stdout) { print 'STDOUT' } + assert_equal "STDERR\n", capture(:stderr) { system('echo STDERR 1>&2') } + assert_equal "STDOUT\n", capture(:stdout) { system('echo STDOUT') } end end diff --git a/activesupport/test/spec_type_test.rb b/activesupport/test/spec_type_test.rb index 95a982d8fd..9a6cb4ded2 100644 --- a/activesupport/test/spec_type_test.rb +++ b/activesupport/test/spec_type_test.rb @@ -4,7 +4,6 @@ require "active_record" class SomeRandomModel < ActiveRecord::Base; end class SpecTypeTest < ActiveSupport::TestCase - def assert_support actual assert_equal ActiveSupport::TestCase, actual end @@ -13,7 +12,7 @@ class SpecTypeTest < ActiveSupport::TestCase assert_equal MiniTest::Spec, actual end - def test_spec_type_resolves_for_actitive_record_constants + def test_spec_type_resolves_for_active_record_constants assert_support MiniTest::Spec.spec_type(SomeRandomModel) end diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb index c02bfa8497..64426d02e9 100644 --- a/activesupport/test/test_case_test.rb +++ b/activesupport/test/test_case_test.rb @@ -16,6 +16,9 @@ module ActiveSupport def options nil end + + def record(*args) + end end def test_standard_error_raised_within_setup_callback_is_puked diff --git a/activesupport/test/testing/constant_lookup_test.rb b/activesupport/test/testing/constant_lookup_test.rb index c56c032cde..19280ba74a 100644 --- a/activesupport/test/testing/constant_lookup_test.rb +++ b/activesupport/test/testing/constant_lookup_test.rb @@ -1,7 +1,7 @@ require 'abstract_unit' class Foo; end -class Bar < Foo; +class Bar < Foo def index; end def self.index; end end diff --git a/guides/assets/stylesheets/main.css b/guides/assets/stylesheets/main.css index 9f5e101d1c..589c96e0e9 100644 --- a/guides/assets/stylesheets/main.css +++ b/guides/assets/stylesheets/main.css @@ -26,11 +26,13 @@ dl { margin: 0 0 1.5em 0; } dl dt { font-weight: bold; } dd { margin-left: 1.5em;} -pre,code { margin: 1.5em 0; overflow: auto; color: #222;} -pre,code { - font-size: 1em; - font-family: "Anonymous Pro", "Inconsolata", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; - line-height: 1.5; +pre, code { + font-size: 1em; + font-family: "Anonymous Pro", "Inconsolata", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + line-height: 1.5; + margin: 1.5em 0; + overflow: auto; + color: #222; } pre,tt,code,.note>p { white-space: pre-wrap; /* css-3 */ @@ -92,14 +94,14 @@ body { line-height: 1.5em; background: #fff; color: #999; - } +} .wrapper { text-align: left; margin: 0 auto; max-width: 960px; padding: 0 1em; - } +} .red-button { display: inline-block; @@ -164,7 +166,6 @@ body { .more-info:last-child:after { content: ""; } - } @media screen and (max-width: 1024px) { @@ -218,7 +219,7 @@ body { color: #FFF; padding: 1.5em 0; z-index: 99; - } +} #feature { background: #d5e9f6 url(../images/feature_tile.gif) repeat-x; @@ -229,12 +230,12 @@ body { #container { color: #333; padding: 0.5em 0 1.5em 0; - } +} #mainCol { max-width: 630px; margin-left: 2em; - } +} #subCol { position: absolute; @@ -247,7 +248,7 @@ body { font-size: 0.9285em; line-height: 1.3846em; margin-right: 1em; - } +} @media screen and (max-width: 800px) { @@ -265,7 +266,7 @@ body { #footer { padding: 2em 0; background: #222 url(../images/footer_tile.gif) repeat-x; - } +} #footer .wrapper { padding-left: 1em; max-width: 960px; @@ -284,12 +285,11 @@ body { a, a:link, a:visited { color: #ee3f3f; text-decoration: underline; - } +} #mainCol a, #subCol a, #feature a {color: #980905;} #mainCol a code, #subCol a code, #feature a code {color: #980905;} - /* Navigation --------------------------------------- */ @@ -313,7 +313,6 @@ a, a:link, a:visited { background: #980905; position: relative; color: white; - cursor: pointer; } .guides-index .guides-index-item { @@ -345,7 +344,6 @@ a, a:link, a:visited { } @media screen and (max-width: 480px) { - .nav { float: none; width: 100%; @@ -408,7 +406,8 @@ a, a:link, a:visited { padding: 0; } #guides dt {padding:0; margin: 0.5em 0 0;} -#guides a {color: #FFF; background: none !important;} +#guides a {color: #FFF; background: none !important; text-decoration: none;} +#guides a:hover {text-decoration: underline;} #guides .L, #guides .R {float: left; width: 50%; margin: 0; padding: 0;} #guides .R {float: right;} #guides hr { @@ -427,14 +426,14 @@ h1 { line-height: 1em; margin: 0.6em 0 .2em; font-weight: bold; - } +} h2 { font-size: 2.1428em; line-height: 1em; margin: 0.7em 0 .2333em; font-weight: bold; - } +} @media screen and (max-width: 480px) { h2 { @@ -447,7 +446,7 @@ h3 { line-height: 1.286em; margin: 0.875em 0 0.2916em; font-weight: bold; - } +} @media screen and (max-width: 480px) { h3 { @@ -460,7 +459,7 @@ h4 { line-height: 1.2em; margin: 1.6667em 0 .3887em; font-weight: bold; - } +} h5 { font-size: 1em; @@ -474,7 +473,7 @@ h6 { line-height: 1.5em; margin: 1em 0 .5em; font-weight: normal; - } +} .section { padding-bottom: 0.25em; @@ -542,13 +541,19 @@ h6 { } #mainCol dt, #subCol dt { - font-size: 1em; + font-size: 1.2857em; padding: 0.125em 0 0.25em 0; margin-bottom: 0; /*background: url(../images/book_icon.gif) no-repeat left top; padding: 0.125em 0 0.25em 28px;*/ } +@media screen and (max-width: 480px) { + #mainCol dt, #subCol dt { + font-size: 1em; + } +} + #mainCol dd.work-in-progress, #subCol dd.work-in-progress { background: #fff9d8 url(../images/tab_yellow.gif) no-repeat left top; border: none; @@ -609,10 +614,10 @@ div.code_container { } #mainCol div.todo { - background: #fff9d8 url(../images/tab_yellow.gif) no-repeat left top; - border: none; - padding: 1em 1em 0.25em 48px; - margin: 0.25em 0 1.5em 0; + background: #fff9d8 url(../images/tab_yellow.gif) no-repeat left top; + border: none; + padding: 1em 1em 0.25em 48px; + margin: 0.25em 0 1.5em 0; } .note code, .info code, .todo code {border:none; background: none; padding: 0;} @@ -640,11 +645,11 @@ div.code_container { --------------------------------------- */ .clearfix:after { - content: "."; - display: block; - height: 0; - clear: both; - visibility: hidden; + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; } .clearfix {display: inline-block;} @@ -655,13 +660,13 @@ div.code_container { /* Same bottom margin for special boxes than for regular paragraphs, this way intermediate whitespace looks uniform. */ div.code_container, div.important, div.caution, div.warning, div.note, div.info { - margin-bottom: 1.5em; + margin-bottom: 1.5em; } /* Remove bottom margin of paragraphs in special boxes, otherwise they get a spurious blank area below with the box background. */ div.important p, div.caution p, div.warning p, div.note p, div.info p { - margin-bottom: 1em; + margin-bottom: 1em; } /* Edge Badge @@ -683,19 +688,18 @@ table td, table th { padding: 9px 10px; text-align: left; } /* Mobile */ @media only screen and (max-width: 767px) { - - table.responsive { margin-bottom: 0; } - - .pinned { position: absolute; left: 0; top: 0; background: #fff; width: 35%; overflow: hidden; overflow-x: scroll; border-right: 1px solid #ccc; border-left: 1px solid #ccc; } - .pinned table { border-right: none; border-left: none; width: 100%; } - .pinned table th, .pinned table td { white-space: nowrap; } - .pinned td:last-child { border-bottom: 0; } - - div.table-wrapper { position: relative; margin-bottom: 20px; overflow: hidden; border-right: 1px solid #ccc; } - div.table-wrapper div.scrollable table { margin-left: 35%; } - div.table-wrapper div.scrollable { overflow: scroll; overflow-y: hidden; } - - table.responsive td, table.responsive th { position: relative; white-space: nowrap; overflow: hidden; } - table.responsive th:first-child, table.responsive td:first-child, table.responsive td:first-child, table.responsive.pinned td { display: none; } - + table.responsive { margin-bottom: 0; } + + .pinned { position: absolute; left: 0; top: 0; background: #fff; width: 35%; overflow: hidden; overflow-x: scroll; border-right: 1px solid #ccc; border-left: 1px solid #ccc; } + .pinned table { border-right: none; border-left: none; width: 100%; } + .pinned table th, .pinned table td { white-space: nowrap; } + .pinned td:last-child { border-bottom: 0; } + + div.table-wrapper { position: relative; margin-bottom: 20px; overflow: hidden; border-right: 1px solid #ccc; } + div.table-wrapper div.scrollable table { margin-left: 35%; } + div.table-wrapper div.scrollable { overflow: scroll; overflow-y: hidden; } + + table.responsive td, table.responsive th { position: relative; white-space: nowrap; overflow: hidden; } + table.responsive th:first-child, table.responsive td:first-child, table.responsive td:first-child, table.responsive.pinned td { display: none; } + } diff --git a/guides/rails_guides/markdown/renderer.rb b/guides/rails_guides/markdown/renderer.rb index 2f36af1fb3..c3fe5b8799 100644 --- a/guides/rails_guides/markdown/renderer.rb +++ b/guides/rails_guides/markdown/renderer.rb @@ -9,7 +9,7 @@ module RailsGuides <<-HTML <div class="code_container"> <pre class="brush: #{brush_for(language)}; gutter: false; toolbar: false"> -#{ERB::Util.h(code).strip} +#{ERB::Util.h(code)} </pre> </div> HTML diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md index 3791467584..01bc26c4d5 100644 --- a/guides/source/contributing_to_ruby_on_rails.md +++ b/guides/source/contributing_to_ruby_on_rails.md @@ -376,6 +376,44 @@ Now you need to get other people to look at your patch, just as you've looked at It’s entirely possible that the feedback you get will suggest changes. Don’t get discouraged: the whole point of contributing to an active open source project is to tap into community knowledge. If people are encouraging you to tweak your code, then it’s worth making the tweaks and resubmitting. If the feedback is that your code doesn’t belong in the core, you might still think about releasing it as a gem. +#### Squashing commits + +One of the things that we may ask you to do is "squash your commits," which +will combine all of your commits into a single commit. We prefer pull requests +that are a single commit. This makes it easier to backport changes to stable +branches, squashing makes it easier to revert bad commits, and the git history +can be a bit easier to follow. Rails is a large project, and a bunch of +extraneous commits can add a lot of noise. + +In order to do this, you'll need to have a git remote that points at the main +Rails repository. This is useful anyway, but just in case you don't have it set +up, make sure that you do this first: + +```bash +$ git remote add upstream https://github.com/rails/rails.git +``` + +You can call this remote whatever you'd like, but if you don't use `upstream`, +then change the name to your own in the instructions below. + +Given that your remote branch is called `my_pull_request`, then you can do the +following: + +```bash +$ git fetch upstream +$ git checkout my_pull_request +$ git rebase upstream/master +$ git rebase -i + +< Choose 'squash' for all of your commits except the first one. > +< Edit the commit message to make sense, and describe all your changes. > + +$ git push origin my_pull_request -f +``` + +You should be able to refresh the pull request on GitHub and see that it has +been updated. + ### Backporting Changes that are merged into master are intended for the next major release of Rails. Sometimes, it might be beneficial for your changes to propagate back to the maintenance releases for older stable branches. Generally, security fixes and bug fixes are good candidates for a backport, while new features and patches that introduce a change in behavior will not be accepted. When in doubt, it is best to consult a Rails team member before backporting your changes to avoid wasted effort. diff --git a/rails.gemspec b/rails.gemspec index 97f6dfeac8..c8d5e6bc65 100644 --- a/rails.gemspec +++ b/rails.gemspec @@ -1,4 +1,4 @@ -version = File.read(File.expand_path("../RAILS_VERSION",__FILE__)).strip +version = File.read(File.expand_path('../RAILS_VERSION', __FILE__)).strip Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY @@ -8,8 +8,9 @@ Gem::Specification.new do |s| s.description = 'Ruby on Rails is a full-stack web framework optimized for programmer happiness and sustainable productivity. It encourages beautiful code by favoring convention over configuration.' s.required_ruby_version = '>= 1.9.3' - s.required_rubygems_version = ">= 1.8.11" - s.license = 'MIT' + s.required_rubygems_version = '>= 1.8.11' + + s.license = 'MIT' s.author = 'David Heinemeier Hansson' s.email = 'david@loudthinking.com' @@ -19,11 +20,12 @@ Gem::Specification.new do |s| s.executables = [] s.files = Dir['guides/**/*'] - s.add_dependency('activesupport', version) - s.add_dependency('actionpack', version) - s.add_dependency('activerecord', version) - s.add_dependency('actionmailer', version) - s.add_dependency('railties', version) - s.add_dependency('bundler', '~> 1.2') - s.add_dependency('sprockets-rails', '~> 2.0.0.rc1') + s.add_dependency 'activesupport', version + s.add_dependency 'actionpack', version + s.add_dependency 'activerecord', version + s.add_dependency 'actionmailer', version + s.add_dependency 'railties', version + + s.add_dependency 'bundler', '~> 1.2' + s.add_dependency 'sprockets-rails', '~> 2.0.0.rc1' end diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index cc77d08684..8538d5fbfa 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,5 +1,10 @@ ## Rails 4.0.0 (unreleased) ## +* Add dummy app Rake tasks when --skip-test-unit and --dummy-path is passed to the plugin generator. + Fix #8121 + + *Yves Senn* + * Ensure that RAILS_ENV is set when accessing Rails.env *Steve Klabnik* * Don't eager-load app/assets and app/views *Elia Schito* @@ -9,7 +14,7 @@ * New test locations `test/models`, `test/helpers`, `test/controllers`, and `test/mailers`. Corresponding rake tasks added as well. *Mike Moore* -* Set a different cache per environment for assets pipeline +* Set a different cache per environment for assets pipeline through `config.assets.cache`. *Guillermo Iguaran* diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb index 80fdc06cd2..0b897d736d 100644 --- a/railties/lib/rails/commands/server.rb +++ b/railties/lib/rails/commands/server.rb @@ -72,6 +72,7 @@ module Rails console = ActiveSupport::Logger.new($stdout) console.formatter = Rails.logger.formatter + console.level = Rails.logger.level Rails.logger.extend(ActiveSupport::Logger.broadcast(console)) end diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb index 4a0bcc35a4..48ce3e86a1 100644 --- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb +++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb @@ -225,7 +225,7 @@ task default: :test end def create_test_dummy_files - return if options[:skip_test_unit] && options[:dummy_path] == 'test/dummy' + return unless with_dummy_app? create_dummy_app end @@ -279,6 +279,10 @@ task default: :test options[:mountable] end + def with_dummy_app? + options[:skip_test_unit].blank? || options[:dummy_path] != 'test/dummy' + end + def self.banner "rails plugin new #{self.arguments.map(&:usage).join(' ')} [options]" end diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile b/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile index 1369140537..65a5bae712 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile +++ b/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile @@ -14,7 +14,7 @@ RDoc::Task.new(:rdoc) do |rdoc| rdoc.rdoc_files.include('lib/**/*.rb') end -<% if full? && !options[:skip_active_record] && !options[:skip_test_unit] -%> +<% if full? && !options[:skip_active_record] && with_dummy_app? -%> APP_RAKEFILE = File.expand_path("../<%= dummy_path -%>/Rakefile", __FILE__) load 'rails/tasks/engine.rake' <% end %> 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 121205b254..a0e5553e44 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 @@ -32,7 +32,7 @@ module Rails # route prepends two spaces onto the front of the string that is passed, this corrects that route route_string[2..-1] end - + private def route_string @route_string ||= "" diff --git a/railties/lib/rails/rack/logger.rb b/railties/lib/rails/rack/logger.rb index 3f59bb8733..7be2333981 100644 --- a/railties/lib/rails/rack/logger.rb +++ b/railties/lib/rails/rack/logger.rb @@ -24,8 +24,8 @@ module Rails def call_app(request, env) # Put some space between requests in development logs. if Rails.env.development? - Rails.logger.info '' - Rails.logger.info '' + Rails.logger.debug '' + Rails.logger.debug '' end Rails.logger.info started_request_message(request) diff --git a/railties/railties.gemspec b/railties/railties.gemspec index 6d28947e83..e39430560f 100644 --- a/railties/railties.gemspec +++ b/railties/railties.gemspec @@ -1,4 +1,4 @@ -version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip +version = File.read(File.expand_path('../../RAILS_VERSION', __FILE__)).strip Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY @@ -6,24 +6,27 @@ Gem::Specification.new do |s| s.version = version s.summary = 'Tools for creating, working with, and running Rails applications.' s.description = 'Rails internals: application bootup, plugins, generators, and rake tasks.' + s.required_ruby_version = '>= 1.9.3' - s.license = 'MIT' - s.author = 'David Heinemeier Hansson' - s.email = 'david@loudthinking.com' - s.homepage = 'http://www.rubyonrails.org' + s.license = 'MIT' + + s.author = 'David Heinemeier Hansson' + s.email = 'david@loudthinking.com' + s.homepage = 'http://www.rubyonrails.org' - s.files = Dir['CHANGELOG.md', 'README.rdoc', 'bin/**/*', 'lib/**/{*,.[a-z]*}'] - s.require_path = 'lib' + s.files = Dir['CHANGELOG.md', 'README.rdoc', 'bin/**/*', 'lib/**/{*,.[a-z]*}'] + s.require_path = 'lib' - s.bindir = 'bin' - s.executables = ['rails'] + s.bindir = 'bin' + s.executables = ['rails'] s.rdoc_options << '--exclude' << '.' - s.add_dependency('rake', '>= 0.8.7') - s.add_dependency('thor', '>= 0.15.4', '< 2.0') - s.add_dependency('rdoc', '~> 3.4') - s.add_dependency('activesupport', version) - s.add_dependency('actionpack', version) + s.add_dependency 'activesupport', version + s.add_dependency 'actionpack', version + + s.add_dependency 'rake', '>= 0.8.7' + s.add_dependency 'thor', '>= 0.15.4', '< 2.0' + s.add_dependency 'rdoc', '~> 3.4' end diff --git a/railties/test/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb index 4b168ae110..9e7626647e 100644 --- a/railties/test/generators/namespaced_generators_test.rb +++ b/railties/test/generators/namespaced_generators_test.rb @@ -244,13 +244,9 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase /module TestApp\n class ProductLinesControllerTest < ActionController::TestCase/ # Views - %w( - index - edit - new - show - _form - ).each { |view| assert_file "app/views/test_app/product_lines/#{view}.html.erb" } + %w(index edit new show _form).each do |view| + assert_file "app/views/test_app/product_lines/#{view}.html.erb" + end assert_no_file "app/views/layouts/test_app/product_lines.html.erb" # Helpers @@ -316,13 +312,9 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase /module TestApp\n class Admin::RolesControllerTest < ActionController::TestCase/ # Views - %w( - index - edit - new - show - _form - ).each { |view| assert_file "app/views/test_app/admin/roles/#{view}.html.erb" } + %w(index edit new show _form).each do |view| + assert_file "app/views/test_app/admin/roles/#{view}.html.erb" + end assert_no_file "app/views/layouts/admin/roles.html.erb" # Helpers @@ -389,13 +381,9 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase /module TestApp\n class Admin::User::Special::RolesControllerTest < ActionController::TestCase/ # Views - %w( - index - edit - new - show - _form - ).each { |view| assert_file "app/views/test_app/admin/user/special/roles/#{view}.html.erb" } + %w(index edit new show _form).each do |view| + assert_file "app/views/test_app/admin/user/special/roles/#{view}.html.erb" + end assert_no_file "app/views/layouts/admin/user/special/roles.html.erb" # Helpers diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb index 6974db5751..ab78800a4e 100644 --- a/railties/test/generators/plugin_new_generator_test.rb +++ b/railties/test/generators/plugin_new_generator_test.rb @@ -66,6 +66,12 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase assert_no_match(/APP_RAKEFILE/, File.read(File.join(destination_root, "Rakefile"))) end + def test_generating_adds_dummy_app_rake_tasks_without_unit_test_files + run_generator [destination_root, "-T", "--mountable", '--dummy-path', 'my_dummy_app'] + + assert_match(/APP_RAKEFILE/, File.read(File.join(destination_root, "Rakefile"))) + end + def test_ensure_that_plugin_options_are_not_passed_to_app_generator FileUtils.cd(Rails.root) assert_no_match(/It works from file!.*It works_from_file/, run_generator([destination_root, "-m", "lib/template.rb"])) diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb index 38454dfb8b..8cacca668f 100644 --- a/railties/test/generators/scaffold_controller_generator_test.rb +++ b/railties/test/generators/scaffold_controller_generator_test.rb @@ -63,12 +63,9 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase def test_views_are_generated run_generator - %w( - index - edit - new - show - ).each { |view| assert_file "app/views/users/#{view}.html.erb" } + %w(index edit new show).each do |view| + assert_file "app/views/users/#{view}.html.erb" + end assert_no_file "app/views/layouts/users.html.erb" end diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb index efe47cdfcb..54d5a9db6f 100644 --- a/railties/test/generators/scaffold_generator_test.rb +++ b/railties/test/generators/scaffold_generator_test.rb @@ -67,13 +67,9 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase end # Views - %w( - index - edit - new - show - _form - ).each { |view| assert_file "app/views/product_lines/#{view}.html.erb" } + %w(index edit new show _form).each do |view| + assert_file "app/views/product_lines/#{view}.html.erb" + end assert_no_file "app/views/layouts/product_lines.html.erb" # Helpers @@ -187,13 +183,9 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase /class Admin::RolesControllerTest < ActionController::TestCase/ # Views - %w( - index - edit - new - show - _form - ).each { |view| assert_file "app/views/admin/roles/#{view}.html.erb" } + %w(index edit new show _form).each do |view| + assert_file "app/views/admin/roles/#{view}.html.erb" + end assert_no_file "app/views/layouts/admin/roles.html.erb" # Helpers |