diff options
41 files changed, 265 insertions, 213 deletions
diff --git a/actionmailer/README.rdoc b/actionmailer/README.rdoc index 67b64fe469..ceca912ada 100644 --- a/actionmailer/README.rdoc +++ b/actionmailer/README.rdoc @@ -156,7 +156,11 @@ API documentation is at * http://api.rubyonrails.org -Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here: +Bug reports can be filed for the Ruby on Rails project here: * https://github.com/rails/rails/issues +Feature requests should be discussed on the rails-core mailing list here: + +* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core + diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 7da58ca4e7..6a50565de5 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -115,7 +115,7 @@ *Boris Kuznetsov* * Swapped the parameters of assert_equal in `assert_select` so that the - proper values were printed correctly + proper values were printed correctly. Fixes #14422. diff --git a/actionpack/README.rdoc b/actionpack/README.rdoc index 2f6575c3b5..02a24a7412 100644 --- a/actionpack/README.rdoc +++ b/actionpack/README.rdoc @@ -48,6 +48,11 @@ API documentation is at * http://api.rubyonrails.org -Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here: +Bug reports can be filed for the Ruby on Rails project here: * https://github.com/rails/rails/issues + +Feature requests should be discussed on the rails-core mailing list here: + +* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core + diff --git a/actionview/README.rdoc b/actionview/README.rdoc index 35f805346c..5bb62c7562 100644 --- a/actionview/README.rdoc +++ b/actionview/README.rdoc @@ -29,6 +29,11 @@ API documentation is at * http://api.rubyonrails.org -Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here: +Bug reports can be filed for the Ruby on Rails project here: * https://github.com/rails/rails/issues + +Feature requests should be discussed on the rails-core mailing list here: + +* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core + diff --git a/actionview/lib/action_view/helpers/debug_helper.rb b/actionview/lib/action_view/helpers/debug_helper.rb index c29c1b1eea..16cddec339 100644 --- a/actionview/lib/action_view/helpers/debug_helper.rb +++ b/actionview/lib/action_view/helpers/debug_helper.rb @@ -11,20 +11,16 @@ module ActionView # If the object cannot be converted to YAML using +to_yaml+, +inspect+ will be called instead. # Useful for inspecting an object at the time of rendering. # - # @user = User.new({ username: 'testing', password: 'xyz', age: 42}) %> + # @user = User.new({ username: 'testing', password: 'xyz', age: 42}) # debug(@user) # # => # <pre class='debug_dump'>--- !ruby/object:User # attributes: # updated_at: # username: testing - # # age: 42 # password: xyz # created_at: - # attributes_cache: {} - # - # new_record: true # </pre> def debug(object) Marshal::dump(object) diff --git a/activemodel/README.rdoc b/activemodel/README.rdoc index c6500f47e2..f6beff14e1 100644 --- a/activemodel/README.rdoc +++ b/activemodel/README.rdoc @@ -262,6 +262,11 @@ API documentation is at * http://api.rubyonrails.org -Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here: +Bug reports can be filed for the Ruby on Rails project here: * https://github.com/rails/rails/issues + +Feature requests should be discussed on the rails-core mailing list here: + +* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core + diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 078d63f8f2..9da1304953 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* New records remain new after YAML serialization. + + *Sean Griffin* + * PostgreSQL support default values for enum types. Fixes #7814. *Yves Senn* diff --git a/activerecord/README.rdoc b/activerecord/README.rdoc index c813b22f3b..f4777919d3 100644 --- a/activerecord/README.rdoc +++ b/activerecord/README.rdoc @@ -19,9 +19,9 @@ A short rundown of some of the major features: class Product < ActiveRecord::Base end - + {Learn more}[link:classes/ActiveRecord/Base.html] - + The Product class is automatically mapped to the table named "products", which might look like this: @@ -33,7 +33,7 @@ which might look like this: This would also define the following accessors: `Product#name` and `Product#name=(new_name)`. - + * Associations between objects defined by simple class methods. @@ -208,6 +208,11 @@ API documentation is at: * http://api.rubyonrails.org -Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here: +Bug reports can be filed for the Ruby on Rails project here: * https://github.com/rails/rails/issues + +Feature requests should be discussed on the rails-core mailing list here: + +* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core + diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb index 816fb51942..c4cf084a04 100644 --- a/activerecord/lib/active_record/attribute_assignment.rb +++ b/activerecord/lib/active_record/attribute_assignment.rb @@ -13,9 +13,9 @@ module ActiveRecord # exception is raised. # # cat = Cat.new(name: "Gorby", status: "yawning") - # cat.attributes # => { "name" => "Gorby", "status" => "yawning" } + # cat.attributes # => { "name" => "Gorby", "status" => "yawning", "created_at" => nil, "updated_at" => nil} # cat.assign_attributes(status: "sleeping") - # cat.attributes # => { "name" => "Gorby", "status" => "sleeping" } + # cat.attributes # => { "name" => "Gorby", "status" => "sleeping", "created_at" => nil, "updated_at" => nil } # # New attributes will be persisted in the database when the object is saved. # diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index fb6473b7c7..b6520b9b3d 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -287,11 +287,6 @@ module ActiveRecord } end - # Placeholder so it can be overriden when needed by serialization - def attributes_for_coder # :nodoc: - attributes - end - # Returns an <tt>#inspect</tt>-like string for the value of the # attribute +attr_name+. String attributes are truncated upto 50 # characters, Date and Time attributes are returned in the diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index 99b95b1fc4..ae3785638a 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -94,7 +94,7 @@ module ActiveRecord def cacheable_column?(column) if attribute_types_cached_by_default == ATTRIBUTE_TYPES_CACHED_BY_DEFAULT - ! serialized_attributes.include? column.name + true else attribute_types_cached_by_default.include?(column.type) end diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index b247bf8a45..425c33f2c6 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -76,21 +76,6 @@ module ActiveRecord module Behavior # :nodoc: extend ActiveSupport::Concern - module ClassMethods # :nodoc: - def initialize_attributes(attributes, options = {}) - serialized = (options.delete(:serialized) { true }) ? :serialized : :unserialized - super(attributes, options) - - serialized_attributes.each do |key, coder| - if attributes.key?(key) - attributes[key] = Type::Serialized::Attribute.new(coder, attributes[key], serialized) - end - end - - attributes - end - end - def should_record_timestamps? super || (self.record_timestamps && (attributes.keys & self.class.serialized_attributes.keys).present?) end @@ -106,42 +91,6 @@ module ActiveRecord super end end - - def read_attribute_before_type_cast(attr_name) - if self.class.serialized_attributes.include?(attr_name) - super.unserialized_value - else - super - end - end - - def attributes_before_type_cast - super.dup.tap do |attributes| - self.class.serialized_attributes.each_key do |key| - if attributes.key?(key) - attributes[key] = attributes[key].unserialized_value - end - end - end - end - - def typecasted_attribute_value(name) - if self.class.serialized_attributes.include?(name) - @raw_attributes[name].serialized_value - else - super - end - end - - def attributes_for_coder - attribute_names.each_with_object({}) do |name, attrs| - attrs[name] = if self.class.serialized_attributes.include?(name) - @raw_attributes[name].serialized_value - else - read_attribute(name) - end - end - end end end end diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index 3b9b9c81e8..c3e601a208 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -53,11 +53,11 @@ module ActiveRecord # specified +value+. Empty strings for fixnum and float columns are # turned into +nil+. def write_attribute(attr_name, value) - write_attribute_with_type_cast(attr_name, value, :type_cast_for_write) + write_attribute_with_type_cast(attr_name, value, true) end def raw_write_attribute(attr_name, value) - write_attribute_with_type_cast(attr_name, value, :raw_type_cast_for_write) + write_attribute_with_type_cast(attr_name, value, false) end private @@ -66,7 +66,7 @@ module ActiveRecord write_attribute(attribute_name, value) end - def write_attribute_with_type_cast(attr_name, value, type_cast_method) + def write_attribute_with_type_cast(attr_name, value, should_type_cast) attr_name = attr_name.to_s attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key @attributes.delete(attr_name) @@ -78,9 +78,9 @@ module ActiveRecord @attributes[attr_name] = value end - if column - @raw_attributes[attr_name] = column.public_send(type_cast_method, value) - elsif @raw_attributes.has_key?(attr_name) + if column && should_type_cast + @raw_attributes[attr_name] = column.type_cast_for_write(value) + elsif !should_type_cast || @raw_attributes.has_key?(attr_name) @raw_attributes[attr_name] = value else raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'" diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index 4fbc55a07e..60da541e3d 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -17,7 +17,7 @@ module ActiveRecord delegate :type, :precision, :scale, :limit, :klass, :accessor, :text?, :number?, :binary?, :serialized?, - :type_cast, :type_cast_for_write, :raw_type_cast_for_write, :type_cast_for_database, + :type_cast, :type_cast_for_write, :type_cast_for_database, :type_cast_for_schema, to: :cast_type @@ -52,7 +52,7 @@ module ActiveRecord end def extract_default(default) - type_cast(default) + type_cast_for_write(type_cast(default)) end end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb index f7bad20f00..971f5eed7e 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb @@ -6,15 +6,6 @@ module ActiveRecord "(#{point[0]},#{point[1]})" end - def string_to_bit(value) # :nodoc: - case value - when /^0x/i - value[2..-1].hex.to_s(2) # Hexadecimal notation - else - value # Bit-string notation - end - end - def hstore_to_string(object, array_member = false) # :nodoc: if Hash === object string = object.map { |k, v| "#{escape_hstore(k)}=>#{escape_hstore(v)}" }.join(',') @@ -76,28 +67,6 @@ module ActiveRecord end end - def string_to_cidr(string) # :nodoc: - if string.nil? - nil - elsif String === string - begin - IPAddr.new(string) - rescue ArgumentError - nil - end - else - string - end - end - - def cidr_to_string(object) # :nodoc: - if IPAddr === object - "#{object.to_s}/#{object.instance_variable_get(:@mask_addr).to_s(2).count('1')}" - else - object - end - end - def string_to_array(string, oid) # :nodoc: parse_pg_array(string).map {|val| type_cast_array(oid, val)} end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb index 9b2d887d07..dc077993c5 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb @@ -5,7 +5,12 @@ module ActiveRecord class Bit < Type::String def type_cast(value) if ::String === value - ConnectionAdapters::PostgreSQLColumn.string_to_bit value + case value + when /^0x/i + value[2..-1].hex.to_s(2) # Hexadecimal notation + else + value # Bit-string notation + end else value end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb index 158468fe5b..534961a414 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb @@ -18,8 +18,26 @@ module ActiveRecord end end + def type_cast_for_database(value) + if IPAddr === value + "#{value.to_s}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}" + else + value + end + end + def cast_value(value) - ConnectionAdapters::PostgreSQLColumn.string_to_cidr value + if value.nil? + nil + elsif String === value + begin + IPAddr.new(value) + rescue ArgumentError + nil + end + else + value + end end end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb index 7b7144a3a8..c875bc5162 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb @@ -44,11 +44,6 @@ module ActiveRecord when 'json' then super(PostgreSQLColumn.json_to_string(value), column) else super end - when IPAddr - case sql_type - when 'inet', 'cidr' then super(PostgreSQLColumn.cidr_to_string(value), column) - else super - end when Float if value.infinite? && column.type == :datetime "'#{value.to_s.downcase}'" @@ -125,12 +120,6 @@ module ActiveRecord when 'json' then PostgreSQLColumn.json_to_string(value) else super(value, column) end - when IPAddr - if %w(inet cidr).include? column.sql_type - PostgreSQLColumn.cidr_to_string(value) - else - super(value, column) - end else super(value, column) end diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index e99e235fe8..88c1fc7e4c 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -284,7 +284,7 @@ module ActiveRecord init_internals - @new_record = false + @new_record = coder['new_record'] self.class.define_attribute_methods @@ -353,7 +353,8 @@ module ActiveRecord # Post.new.encode_with(coder) # coder # => {"attributes" => {"id" => nil, ... }} def encode_with(coder) - coder['attributes'] = attributes_for_coder + coder['attributes'] = @raw_attributes + coder['new_record'] = new_record? end # Returns true if +comparison_object+ is the same exact object, or +comparison_object+ diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 78ae05073a..2e3bcc0956 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -49,7 +49,11 @@ module ActiveRecord def instantiate(attributes, column_types = {}) klass = discriminate_class_for_record(attributes) column_types = klass.decorate_columns(column_types.dup) - klass.allocate.init_with('attributes' => attributes, 'column_types' => column_types) + klass.allocate.init_with( + 'attributes' => attributes, + 'column_types' => column_types, + 'new_record' => false, + ) end private diff --git a/activerecord/lib/active_record/type/serialized.rb b/activerecord/lib/active_record/type/serialized.rb index 4052ac0fa0..eac31f6cc3 100644 --- a/activerecord/lib/active_record/type/serialized.rb +++ b/activerecord/lib/active_record/type/serialized.rb @@ -10,20 +10,21 @@ module ActiveRecord end def type_cast(value) - if value.respond_to?(:unserialized_value) - value.unserialized_value(super(value.value)) + if is_default_value?(value) + value else - super + coder.load(super) end end def type_cast_for_write(value) - Attribute.new(coder, value, :unserialized) + return if value.nil? + unless is_default_value?(value) + coder.dump(value) + end end - def raw_type_cast_for_write(value) - Attribute.new(coder, value, :serialized) - end + alias type_cast_for_database type_cast_for_write def serialized? true @@ -33,24 +34,10 @@ module ActiveRecord ActiveRecord::Store::IndifferentHashAccessor end - class Attribute < Struct.new(:coder, :value, :state) # :nodoc: - def unserialized_value(v = value) - state == :serialized ? unserialize(v) : value - end - - def serialized_value - state == :unserialized ? serialize : value - end - - def unserialize(v) - self.state = :unserialized - self.value = coder.load(v) - end + private - def serialize - self.state = :serialized - self.value = coder.dump(value) - end + def is_default_value?(value) + value == coder.load(nil) end end end diff --git a/activerecord/lib/active_record/type/value.rb b/activerecord/lib/active_record/type/value.rb index 9c1e9dc01e..1f7d4e20b2 100644 --- a/activerecord/lib/active_record/type/value.rb +++ b/activerecord/lib/active_record/type/value.rb @@ -12,8 +12,8 @@ module ActiveRecord @limit = options[:limit] end - # The simplified that this object represents. Subclasses - # should override this method. + # The simplified type that this object represents. Subclasses + # must override this method. def type; end # Takes an input from the database, or from attribute setters, @@ -54,7 +54,6 @@ module ActiveRecord def type_cast_for_write(value) # :nodoc: value end - alias_method :raw_type_cast_for_write, :type_cast_for_write # :internal: private diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index b6fccc9b94..2e7b1d7206 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -14,7 +14,6 @@ module ActiveRecord finder_class = find_finder_class_for(record) table = finder_class.arel_table value = map_enum_attribute(finder_class, attribute, value) - value = deserialize_attribute(record, attribute, value) relation = build_relation(finder_class, table, attribute, value) relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.id)) if record.persisted? @@ -86,12 +85,6 @@ module ActiveRecord relation end - def deserialize_attribute(record, attribute, value) - coder = record.class.serialized_attributes[attribute.to_s] - value = coder.dump value if value && coder - value - end - def map_enum_attribute(klass, attribute, value) mapping = klass.defined_enums[attribute.to_s] value = mapping[value] if value && mapping diff --git a/activerecord/test/cases/adapters/postgresql/composite_test.rb b/activerecord/test/cases/adapters/postgresql/composite_test.rb index ecccbf10e6..d804d1fa97 100644 --- a/activerecord/test/cases/adapters/postgresql/composite_test.rb +++ b/activerecord/test/cases/adapters/postgresql/composite_test.rb @@ -93,6 +93,7 @@ class PostgresqlCompositeWithCustomOIDTest < ActiveRecord::TestCase end def type_cast_for_write(value) + return if value.nil? "(#{value.city},#{value.street})" end end diff --git a/activerecord/test/cases/adapters/postgresql/geometric_test.rb b/activerecord/test/cases/adapters/postgresql/geometric_test.rb new file mode 100644 index 0000000000..775b1d2d69 --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/geometric_test.rb @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +require "cases/helper" +require 'support/connection_helper' +require 'active_record/base' +require 'active_record/connection_adapters/postgresql_adapter' + +class PostgresqlPointTest < ActiveRecord::TestCase + include ConnectionHelper + + class PostgresqlPoint < ActiveRecord::Base; end + + def setup + @connection = ActiveRecord::Base.connection + @connection.transaction do + @connection.create_table('postgresql_points') do |t| + t.column :x, :point + end + end + end + + teardown do + @connection.execute 'DROP TABLE IF EXISTS postgresql_points' + end + + def test_column + column = PostgresqlPoint.columns_hash["x"] + assert_equal :string, column.type + assert_equal "point", column.sql_type + assert column.text? + assert_not column.number? + assert_not column.binary? + assert_not column.array + end + + def test_roundtrip + PostgresqlPoint.create! x: [10, 25.2] + record = PostgresqlPoint.first + assert_equal [10, 25.2], record.x + + record.x = [1.1, 2.2] + record.save! + assert record.reload + assert_equal [1.1, 2.2], record.x + end +end diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index da2876170e..139fe9c04b 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -870,7 +870,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase end def cached_columns - Topic.columns.map(&:name) - Topic.serialized_attributes.keys + Topic.columns.map(&:name) end def time_related_columns_on_topic diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb index eaa28148f4..5ea62c9f59 100644 --- a/activerecord/test/cases/serialized_attribute_test.rb +++ b/activerecord/test/cases/serialized_attribute_test.rb @@ -59,8 +59,9 @@ class SerializedAttributeTest < ActiveRecord::TestCase def test_serialized_attribute_calling_dup_method Topic.serialize :content, JSON - t = Topic.new(:content => { :foo => :bar }).dup - assert_equal({ :foo => :bar }, t.content_before_type_cast) + orig = Topic.new(content: { foo: :bar }) + clone = orig.dup + assert_equal(orig.content, clone.content) end def test_serialized_attribute_declared_in_subclass @@ -103,8 +104,10 @@ class SerializedAttributeTest < ActiveRecord::TestCase def test_serialized_attribute_should_raise_exception_on_save_with_wrong_type Topic.serialize(:content, Hash) - topic = Topic.new(:content => "string") - assert_raise(ActiveRecord::SerializationTypeMismatch) { topic.save } + assert_raise(ActiveRecord::SerializationTypeMismatch) do + topic = Topic.new(content: 'string') + topic.save + end end def test_should_raise_exception_on_serialized_attribute_with_type_mismatch diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb index 6a34c55011..f841b1c983 100644 --- a/activerecord/test/cases/store_test.rb +++ b/activerecord/test/cases/store_test.rb @@ -183,20 +183,6 @@ class StoreTest < ActiveRecord::TestCase assert_equal({}, @john.params) end - test "attributes_for_coder should return stored fields already serialized" do - attributes = { - "id" => @john.id, - "name"=> @john.name, - "settings" => "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\ncolor: black\n", - "preferences" => "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nremember_login: true\n", - "json_data" => "{\"height\":\"tall\"}", "json_data_empty"=>"{\"is_a_good_guy\":true}", - "params" => "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess {}\n", - "account_id"=> @john.account_id - } - - assert_equal attributes, @john.attributes_for_coder - end - test "dump, load and dump again a model" do dumped = YAML.dump(@john) loaded = YAML.load(dumped) diff --git a/activerecord/test/cases/yaml_serialization_test.rb b/activerecord/test/cases/yaml_serialization_test.rb index 15815d56e4..d4f8ef5b4d 100644 --- a/activerecord/test/cases/yaml_serialization_test.rb +++ b/activerecord/test/cases/yaml_serialization_test.rb @@ -23,13 +23,6 @@ class YamlSerializationTest < ActiveRecord::TestCase assert_equal({:omg=>:lol}, YAML.load(YAML.dump(topic)).content) end - def test_encode_with_coder - topic = Topic.first - coder = {} - topic.encode_with coder - assert_equal({'attributes' => topic.attributes}, coder) - end - def test_psych_roundtrip topic = Topic.first assert topic @@ -47,4 +40,33 @@ class YamlSerializationTest < ActiveRecord::TestCase def test_active_record_relation_serialization [Topic.all].to_yaml end + + def test_raw_types_are_not_changed_on_round_trip + topic = Topic.new(parent_id: "123") + assert_equal "123", topic.parent_id_before_type_cast + assert_equal "123", YAML.load(YAML.dump(topic)).parent_id_before_type_cast + end + + def test_cast_types_are_not_changed_on_round_trip + topic = Topic.new(parent_id: "123") + assert_equal 123, topic.parent_id + assert_equal 123, YAML.load(YAML.dump(topic)).parent_id + end + + def test_new_records_remain_new_after_round_trip + topic = Topic.new + + assert topic.new_record?, "Sanity check that new records are new" + assert YAML.load(YAML.dump(topic)).new_record?, "Record should be new after deserialization" + + topic.save! + + assert_not topic.new_record?, "Saved records are not new" + assert_not YAML.load(YAML.dump(topic)).new_record?, "Saved record should not be new after deserialization" + + topic = Topic.select('title').last + + assert_not topic.new_record?, "Loaded records without ID are not new" + assert_not YAML.load(YAML.dump(topic)).new_record?, "Record should not be new after deserialization" + end end diff --git a/activesupport/README.rdoc b/activesupport/README.rdoc index f3582767c0..a6424a353a 100644 --- a/activesupport/README.rdoc +++ b/activesupport/README.rdoc @@ -30,6 +30,11 @@ API documentation is at: * http://api.rubyonrails.org -Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here: +Bug reports can be filed for the Ruby on Rails project here: * https://github.com/rails/rails/issues + +Feature requests should be discussed on the rails-core mailing list here: + +* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core + diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb index 28536e32a4..5934c578ea 100644 --- a/activesupport/lib/active_support/core_ext/hash/keys.rb +++ b/activesupport/lib/active_support/core_ext/hash/keys.rb @@ -27,7 +27,7 @@ class Hash # hash = { name: 'Rob', age: '28' } # # hash.stringify_keys - # # => { "name" => "Rob", "age" => "28" } + # # => {"name"=>"Rob", "age"=>"28"} def stringify_keys transform_keys{ |key| key.to_s } end @@ -44,7 +44,7 @@ class Hash # hash = { 'name' => 'Rob', 'age' => '28' } # # hash.symbolize_keys - # # => { name: "Rob", age: "28" } + # # => {:name=>"Rob", :age=>"28"} def symbolize_keys transform_keys{ |key| key.to_sym rescue key } end diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb index 7a96c66626..325a3d75dc 100644 --- a/activesupport/lib/active_support/notifications.rb +++ b/activesupport/lib/active_support/notifications.rb @@ -141,6 +141,11 @@ module ActiveSupport # # ActiveSupport::Notifications.unsubscribe(subscriber) # + # You can also unsubscribe by passing the name of the subscriber object. Note + # that this will unsubscribe all subscriptions with the given name: + # + # ActiveSupport::Notifications.unsubscribe("render") + # # == Default Queue # # Notifications ships with a queue implementation that consumes and publishes events @@ -173,8 +178,8 @@ module ActiveSupport unsubscribe(subscriber) end - def unsubscribe(args) - notifier.unsubscribe(args) + def unsubscribe(subscriber_or_name) + notifier.unsubscribe(subscriber_or_name) end def instrumenter diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index 8f5fa646e8..6bf8c7d5de 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -25,9 +25,15 @@ module ActiveSupport subscriber end - def unsubscribe(subscriber) + def unsubscribe(subscriber_or_name) synchronize do - @subscribers.reject! { |s| s.matches?(subscriber) } + case subscriber_or_name + when String + @subscribers.reject! { |s| s.matches?(subscriber_or_name) } + else + @subscribers.delete(subscriber_or_name) + end + @listeners_for.clear end end @@ -97,12 +103,11 @@ module ActiveSupport end def subscribed_to?(name) - @pattern === name.to_s + @pattern === name end - def matches?(subscriber_or_name) - self === subscriber_or_name || - @pattern && @pattern === subscriber_or_name + def matches?(name) + @pattern && @pattern === name end end diff --git a/guides/source/3_2_release_notes.md b/guides/source/3_2_release_notes.md index cdcde67869..2416e1a228 100644 --- a/guides/source/3_2_release_notes.md +++ b/guides/source/3_2_release_notes.md @@ -562,4 +562,4 @@ Credits See the [full list of contributors to Rails](http://contributors.rubyonrails.org/) for the many people who spent many hours making Rails, the stable and robust framework it is. Kudos to all of them. -Rails 3.2 Release Notes were compiled by [Vijay Dev](https://github.com/vijaydev.) +Rails 3.2 Release Notes were compiled by [Vijay Dev](https://github.com/vijaydev). diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index ee8cf4ade6..673dcfc1d3 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -659,6 +659,23 @@ FROM orders GROUP BY date(created_at) ``` +### Total of grouped items + +To get the total of grouped items on a single query call `count` after the `group`. + +```ruby +Order.group(:status).count +# => { 'awaiting_approval' => 7, 'paid' => 12 } +``` + +The SQL that would be executed would be something like this: + +```sql +SELECT COUNT (*) AS count_all, status AS status +FROM "orders" +GROUP BY status +``` + Having ------ diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index 984480c70f..559d3f5e7d 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -580,7 +580,7 @@ config.assets.raise_runtime_errors = false When this option is true, the asset pipeline will check if all the assets loaded in your application are included in the `config.assets.precompile` list. -If `config.assets.digests` is also true, the asset pipeline will require that +If `config.assets.digest` is also true, the asset pipeline will require that all requests for assets include digests. ### Turning Digests Off @@ -589,7 +589,7 @@ You can turn off digests by updating `config/environments/development.rb` to include: ```ruby -config.assets.digests = false +config.assets.digest = false ``` When this option is true, digests will be generated for asset URLs. diff --git a/guides/source/documents.yaml b/guides/source/documents.yaml index e365435b50..5138412312 100644 --- a/guides/source/documents.yaml +++ b/guides/source/documents.yaml @@ -96,11 +96,6 @@ url: command_line.html description: This guide covers the command line tools and rake tasks provided by Rails. - - name: Caching with Rails - work_in_progress: true - url: caching_with_rails.html - description: Various caching techniques provided by Rails. - - name: Asset Pipeline url: asset_pipeline.html description: This guide documents the asset pipeline. diff --git a/guides/source/migrations.md b/guides/source/migrations.md index 6742c05946..31e314c69b 100644 --- a/guides/source/migrations.md +++ b/guides/source/migrations.md @@ -415,6 +415,29 @@ end removes the `description` and `name` columns, creates a `part_number` string column and adds an index on it. Finally it renames the `upccode` column. +### Changing Columns + +Like the `remove_column` and `add_column` Rails provides the `change_column` +migration method. + +```ruby +change_column :products, :part_number, :text +``` + +This changes the column `part_number` on products table to be a `:text` field. + +Besides `change_column`, the `change_column_null` and `change_column_default` +methods are used specifically to change the null and default values of a +column. + +```ruby +change_column_null :products, :name, false +change_column_default :products, :approved, false +``` + +This sets `:name` field on products to a `NOT NULL` column and the default +value of the `:approved` field to false. + ### When Helpers aren't Enough If the helpers provided by Active Record aren't enough you can use the `execute` diff --git a/railties/README.rdoc b/railties/README.rdoc index 6248b5feed..a25658668c 100644 --- a/railties/README.rdoc +++ b/railties/README.rdoc @@ -31,7 +31,11 @@ API documentation is at * http://api.rubyonrails.org -Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here: +Bug reports can be filed for the Ruby on Rails project here: * https://github.com/rails/rails/issues +Feature requests should be discussed on the rails-core mailing list here: + +* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core + diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index 04ce38f841..2a0148fe9d 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -161,6 +161,11 @@ module Rails end end + # Returns an array of generator namespaces that are hidden. + # Generator namespaces may be hidden for a variety of reasons. + # Some are aliased such as "rails:migration" and can be + # invoked with the shorter "migration", others are private to other generators + # such as "css:scaffold". def self.hidden_namespaces @hidden_namespaces ||= begin orm = options[:rails][:orm] diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb index 8e198d5fe1..b998fef42e 100644 --- a/railties/test/generators/shared_generator_tests.rb +++ b/railties/test/generators/shared_generator_tests.rb @@ -78,9 +78,12 @@ module SharedGeneratorTests end def test_template_raises_an_error_with_invalid_path - content = capture(:stderr){ run_generator([destination_root, "-m", "non/existent/path"]) } - assert_match(/The template \[.*\] could not be loaded/, content) - assert_match(/non\/existent\/path/, content) + quietly do + content = capture(:stderr){ run_generator([destination_root, "-m", "non/existent/path"]) } + + assert_match(/The template \[.*\] could not be loaded/, content) + assert_match(/non\/existent\/path/, content) + end end def test_template_is_executed_when_supplied @@ -89,7 +92,7 @@ module SharedGeneratorTests 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) - assert_match(/It works!/, capture(:stdout) { generator.invoke_all }) + quietly { assert_match(/It works!/, capture(:stdout) { generator.invoke_all }) } end def test_template_is_executed_when_supplied_an_https_path @@ -98,7 +101,7 @@ module SharedGeneratorTests 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) - assert_match(/It works!/, capture(:stdout) { generator.invoke_all }) + quietly { assert_match(/It works!/, capture(:stdout) { generator.invoke_all }) } end def test_dev_option |