diff options
51 files changed, 841 insertions, 230 deletions
@@ -12,7 +12,7 @@ gem 'jquery-rails', '~> 3.1.0' gem 'turbolinks' gem 'coffee-rails', '~> 4.0.0' gem 'arel', github: 'rails/arel', branch: 'master' -gem 'sprockets-rails', github: 'rails/sprockets-rails', branch: '2-1-stable' +gem 'sprockets-rails', github: 'rails/sprockets-rails', branch: 'master' gem 'i18n', github: 'svenfuchs/i18n', branch: 'master' # require: false so bcrypt is loaded only when has_secure_password is used. diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb index 0443b73953..29ce5abd55 100644 --- a/actionpack/lib/action_controller/metal/renderers.rb +++ b/actionpack/lib/action_controller/metal/renderers.rb @@ -6,6 +6,11 @@ module ActionController Renderers.add(key, &block) end + # See <tt>Renderers.remove</tt> + def self.remove_renderer(key) + Renderers.remove(key) + end + class MissingRenderer < LoadError def initialize(format) super "No renderer defined for format: #{format}" @@ -83,6 +88,17 @@ module ActionController RENDERERS << key.to_sym end + # This method is the opposite of add method. + # + # Usage: + # + # ActionController::Renderers.remove(:csv) + def self.remove(key) + RENDERERS.delete(key.to_sym) + method = "_render_option_#{key}" + remove_method(method) if method_defined?(method) + end + module All extend ActiveSupport::Concern include Renderers diff --git a/actionpack/lib/action_dispatch/journey/parser.rb b/actionpack/lib/action_dispatch/journey/parser.rb index 430812fafe..d129ba7e16 100644 --- a/actionpack/lib/action_dispatch/journey/parser.rb +++ b/actionpack/lib/action_dispatch/journey/parser.rb @@ -1,7 +1,7 @@ # # DO NOT MODIFY!!!! -# This file is automatically generated by Racc 1.4.9 -# from Racc grammar file "". +# This file is automatically generated by Racc 1.4.11 +# from Racc grammer file "". # require 'racc/parser.rb' @@ -9,42 +9,38 @@ require 'racc/parser.rb' require 'action_dispatch/journey/parser_extras' module ActionDispatch - module Journey # :nodoc: - class Parser < Racc::Parser # :nodoc: + module Journey + class Parser < Racc::Parser ##### State transition tables begin ### racc_action_table = [ - 17, 21, 13, 15, 14, 7, nil, 16, 8, 19, - 13, 15, 14, 7, 23, 16, 8, 19, 13, 15, - 14, 7, nil, 16, 8, 13, 15, 14, 7, nil, - 16, 8, 13, 15, 14, 7, nil, 16, 8 ] + 13, 15, 14, 7, 21, 16, 8, 19, 13, 15, + 14, 7, 17, 16, 8, 13, 15, 14, 7, 24, + 16, 8, 13, 15, 14, 7, 19, 16, 8 ] racc_action_check = [ - 1, 17, 1, 1, 1, 1, nil, 1, 1, 1, - 20, 20, 20, 20, 20, 20, 20, 20, 7, 7, - 7, 7, nil, 7, 7, 19, 19, 19, 19, nil, - 19, 19, 0, 0, 0, 0, nil, 0, 0 ] + 2, 2, 2, 2, 17, 2, 2, 2, 0, 0, + 0, 0, 1, 0, 0, 19, 19, 19, 19, 20, + 19, 19, 7, 7, 7, 7, 22, 7, 7 ] racc_action_pointer = [ - 30, 0, nil, nil, nil, nil, nil, 16, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 1, nil, 23, - 8, nil, nil, nil ] + 6, 12, -2, nil, nil, nil, nil, 20, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 4, nil, 13, + 13, nil, 17, nil, nil ] racc_action_default = [ - -18, -18, -2, -3, -4, -5, -6, -18, -9, -10, - -11, -12, -13, -14, -15, -16, -17, -18, -1, -18, - -18, 24, -8, -7 ] + -19, -19, -2, -3, -4, -5, -6, -19, -10, -11, + -12, -13, -14, -15, -16, -17, -18, -19, -1, -19, + -19, 25, -8, -9, -7 ] racc_goto_table = [ - 18, 1, nil, nil, nil, nil, nil, nil, 20, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 22, 18 ] + 1, 22, 18, 23, nil, nil, nil, 20 ] racc_goto_check = [ - 2, 1, nil, nil, nil, nil, nil, nil, 1, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 2, 2 ] + 1, 2, 1, 3, nil, nil, nil, 1 ] racc_goto_pointer = [ - nil, 1, -1, nil, nil, nil, nil, nil, nil, nil, + nil, 0, -18, -16, nil, nil, nil, nil, nil, nil, nil ] racc_goto_default = [ @@ -61,19 +57,20 @@ racc_reduce_table = [ 1, 12, :_reduce_none, 3, 15, :_reduce_7, 3, 13, :_reduce_8, - 1, 16, :_reduce_9, + 3, 13, :_reduce_9, + 1, 16, :_reduce_10, 1, 14, :_reduce_none, 1, 14, :_reduce_none, 1, 14, :_reduce_none, 1, 14, :_reduce_none, - 1, 19, :_reduce_14, - 1, 17, :_reduce_15, - 1, 18, :_reduce_16, - 1, 20, :_reduce_17 ] + 1, 19, :_reduce_15, + 1, 17, :_reduce_16, + 1, 18, :_reduce_17, + 1, 20, :_reduce_18 ] -racc_reduce_n = 18 +racc_reduce_n = 19 -racc_shift_n = 24 +racc_shift_n = 25 racc_token_table = { false => 0, @@ -137,12 +134,12 @@ Racc_debug_parser = false # reduce 0 omitted def _reduce_1(val, _values, result) - result = Cat.new(val.first, val.last) + result = Cat.new(val.first, val.last) result end def _reduce_2(val, _values, result) - result = val.first + result = val.first result end @@ -155,21 +152,24 @@ end # reduce 6 omitted def _reduce_7(val, _values, result) - result = Group.new(val[1]) + result = Group.new(val[1]) result end def _reduce_8(val, _values, result) - result = Or.new([val.first, val.last]) + result = Or.new([val.first, val.last]) result end def _reduce_9(val, _values, result) - result = Star.new(Symbol.new(val.last)) + result = Or.new([val.first, val.last]) result end -# reduce 10 omitted +def _reduce_10(val, _values, result) + result = Star.new(Symbol.new(val.last)) + result +end # reduce 11 omitted @@ -177,23 +177,25 @@ end # reduce 13 omitted -def _reduce_14(val, _values, result) - result = Slash.new('/') - result -end +# reduce 14 omitted def _reduce_15(val, _values, result) - result = Symbol.new(val.first) + result = Slash.new('/') result end def _reduce_16(val, _values, result) - result = Literal.new(val.first) + result = Symbol.new(val.first) result end def _reduce_17(val, _values, result) - result = Dot.new(val.first) + result = Literal.new(val.first) + result +end + +def _reduce_18(val, _values, result) + result = Dot.new(val.first) result end diff --git a/actionpack/lib/action_dispatch/journey/parser.y b/actionpack/lib/action_dispatch/journey/parser.y index 040f8d5922..0ead222551 100644 --- a/actionpack/lib/action_dispatch/journey/parser.y +++ b/actionpack/lib/action_dispatch/journey/parser.y @@ -4,7 +4,7 @@ token SLASH LITERAL SYMBOL LPAREN RPAREN DOT STAR OR rule expressions - : expressions expression { result = Cat.new(val.first, val.last) } + : expression expressions { result = Cat.new(val.first, val.last) } | expression { result = val.first } | or ; @@ -17,7 +17,8 @@ rule : LPAREN expressions RPAREN { result = Group.new(val[1]) } ; or - : expressions OR expression { result = Or.new([val.first, val.last]) } + : expression OR expression { result = Or.new([val.first, val.last]) } + | expression OR or { result = Or.new([val.first, val.last]) } ; star : STAR { result = Star.new(Symbol.new(val.last)) } diff --git a/actionpack/lib/action_dispatch/journey/visitors.rb b/actionpack/lib/action_dispatch/journey/visitors.rb index d9f634623d..616c2d26d4 100644 --- a/actionpack/lib/action_dispatch/journey/visitors.rb +++ b/actionpack/lib/action_dispatch/journey/visitors.rb @@ -107,10 +107,11 @@ module ActionDispatch # Used for formatting urls (url_for) class Formatter < Visitor # :nodoc: - attr_reader :options + attr_reader :options, :consumed def initialize(options) @options = options + @consumed = {} end private @@ -122,41 +123,41 @@ module ActionDispatch Router::Utils.escape_segment(value) end - def visit(node, optional = false) - case node.type - when :LITERAL, :SLASH, :DOT - node.left - when :STAR - visit_STAR(node.left) - when :GROUP - visit(node.left, true) - when :CAT - visit_CAT(node, optional) - when :SYMBOL - visit_SYMBOL(node, node.to_sym) + def visit_GROUP(node) + if consumed == options + nil + else + route = visit(node.left) + route.include?("\0") ? nil : route end end - def visit_CAT(node, optional) - left = visit(node.left, optional) - right = visit(node.right, optional) + def terminal(node) + node.left + end - if optional && !(right && left) - "" - else - [left, right].join - end + def binary(node) + [visit(node.left), visit(node.right)].join + end + + def nary(node) + node.children.map { |c| visit(c) }.join end def visit_STAR(node) - if value = options[node.to_sym] + if value = options[node.left.to_sym] escape_path(value) end end - def visit_SYMBOL(node, name) - if value = options[name] - name == :controller ? escape_path(value) : escape_segment(value) + def visit_SYMBOL(node) + key = node.to_sym + + if value = options[key] + consumed[key] = value + key == :controller ? escape_path(value) : escape_segment(value) + else + "\0" end end end diff --git a/actionpack/test/controller/mime/respond_with_test.rb b/actionpack/test/controller/mime/respond_with_test.rb index 235ff86c24..115f3b2f41 100644 --- a/actionpack/test/controller/mime/respond_with_test.rb +++ b/actionpack/test/controller/mime/respond_with_test.rb @@ -649,6 +649,8 @@ class RespondWithControllerTest < ActionController::TestCase get :index, format: 'csv' assert_equal Mime::CSV, @response.content_type assert_equal "c,s,v", @response.body + ensure + ActionController::Renderers.remove :csv end def test_raises_missing_renderer_if_an_api_behavior_with_no_renderer @@ -658,6 +660,23 @@ class RespondWithControllerTest < ActionController::TestCase end end + def test_removing_renderers + ActionController::Renderers.add :csv do |obj, options| + send_data obj.to_csv, type: Mime::CSV + end + @controller = CsvRespondWithController.new + @request.accept = "text/csv" + get :index, format: 'csv' + assert_equal Mime::CSV, @response.content_type + + ActionController::Renderers.remove :csv + assert_raise ActionController::MissingRenderer do + get :index, format: 'csv' + end + ensure + ActionController::Renderers.remove :csv + end + def test_error_is_raised_if_no_respond_to_is_declared_and_respond_with_is_called @controller = EmptyRespondWithController.new @request.accept = "*/*" diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb index 0c6df16325..f52f8be101 100644 --- a/actionpack/test/controller/url_for_test.rb +++ b/actionpack/test/controller/url_for_test.rb @@ -11,6 +11,26 @@ module AbstractController W.default_url_options.clear end + def test_nested_optional + klass = Class.new { + include ActionDispatch::Routing::RouteSet.new.tap { |r| + r.draw { + get "/foo/(:bar/(:baz))/:zot", :as => 'fun', + :controller => :articles, + :action => :index + } + }.url_helpers + self.default_url_options[:host] = 'example.com' + } + + path = klass.new.fun_path({:controller => :articles, + :baz => "baz", + :zot => "zot", + :only_path => true }) + # :bar key isn't provided + assert_equal '/foo/zot', path + end + def add_host! W.default_url_options[:host] = 'www.basecamphq.com' end diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 6145aff539..d186fd2c10 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,14 @@ +* Remove unused `:timestamp` type. Transparently alias it to `:datetime` + in all cases. Fixes inconsistencies when column types are sent outside of + `ActiveRecord`, such as for XML Serialization. + + *Sean Griffin* + +* Fix bug that added `table_name_prefix` and `table_name_suffix` to + extension names in PostgreSQL when migrating. + + *Joao Carlos* + * The `:index` option in migrations, which previously was only available for `references`, now works with any column types. diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb index 87ecbe54f1..816fb51942 100644 --- a/activerecord/lib/active_record/attribute_assignment.rb +++ b/activerecord/lib/active_record/attribute_assignment.rb @@ -149,7 +149,7 @@ module ActiveRecord end def read_time - # If column is a :time (and not :date or :timestamp) there is no need to validate if + # If column is a :time (and not :date or :datetime) there is no need to validate if # there are year/month/day fields if column.type == :time # if the column is a time set the values to their defaults as January 1, 1970, but only if they're nil diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index d01e9aea59..979dfb207e 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -35,7 +35,7 @@ module ActiveRecord extend ActiveSupport::Concern - ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date] + ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :time, :date] included do class_attribute :attribute_types_cached_by_default, instance_writer: false @@ -52,7 +52,7 @@ module ActiveRecord end # Returns the attributes which are cached. By default time related columns - # with datatype <tt>:datetime, :timestamp, :time, :date</tt> are cached. + # with datatype <tt>:datetime, :time, :date</tt> are cached. def cached_attributes @cached_attributes ||= columns.select { |c| cacheable_column?(c) }.map { |col| col.name }.to_set end diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb index f168282ea3..dfebb2cf56 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -28,7 +28,7 @@ module ActiveRecord module ClassMethods protected - # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled. + # Defined for all +datetime+ attributes when +time_zone_aware_attributes+ are enabled. # This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone. def define_method_attribute=(attr_name) if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name]) @@ -51,7 +51,7 @@ module ActiveRecord def create_time_zone_conversion_attribute?(name, column) time_zone_aware_attributes && !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) && - (:datetime == column.type || :timestamp == column.type) + (:datetime == column.type) end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index f54fcc4040..117c0f0969 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -262,6 +262,7 @@ module ActiveRecord alias :belongs_to :references def new_column_definition(name, type, options) # :nodoc: + type = aliased_types[type] || type column = create_column_definition name, type limit = options.fetch(:limit) do native[type][:limit] if native[type].is_a?(Hash) @@ -292,6 +293,12 @@ module ActiveRecord def native @native end + + def aliased_types + HashWithIndifferentAccess.new( + timestamp: :datetime, + ) + end end class AlterTable # :nodoc: diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index d9c939689f..0c55dbb037 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -361,10 +361,46 @@ module ActiveRecord pool.checkin self end + def type_map # :nodoc: + @type_map ||= Type::TypeMap.new.tap do |mapping| + initialize_type_map(mapping) + end + end + protected def lookup_cast_type(sql_type) # :nodoc: - Type::Value.new + type_map.lookup(sql_type) + end + + def initialize_type_map(m) # :nodoc: + m.register_type %r(boolean)i, Type::Boolean.new + m.register_type %r(char)i, Type::String.new + m.register_type %r(binary)i, Type::Binary.new + m.alias_type %r(blob)i, 'binary' + m.register_type %r(text)i, Type::Text.new + m.alias_type %r(clob)i, 'text' + m.register_type %r(date)i, Type::Date.new + m.register_type %r(time)i, Type::Time.new + m.alias_type %r(timestamp)i, 'datetime' + m.register_type %r(datetime)i, Type::DateTime.new + m.alias_type %r(numeric)i, 'decimal' + m.alias_type %r(number)i, 'decimal' + m.register_type %r(float)i, Type::Float.new + m.alias_type %r(double)i, 'float' + m.register_type %r(int)i, Type::Integer.new + m.register_type(%r(decimal)i) do |sql_type| + if Type.extract_scale(sql_type) == 0 + Type::Integer.new + else + Type::Decimal.new + end + end + end + + def reload_type_map # :nodoc: + type_map.clear + initialize_type_map(type_map) end def translate_exception_class(e, sql) diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index 7074f69583..852b7105d3 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -97,18 +97,6 @@ module ActiveRecord private - def simplified_type(field_type) - return :boolean if adapter.emulate_booleans && field_type.downcase.index("tinyint(1)") - - case field_type - when /enum/i, /set/i then :string - when /year/i then :integer - when /bit/i then :binary - else - super - end - end - def extract_limit(sql_type) case sql_type when /^enum\((.+)\)/i @@ -175,7 +163,6 @@ module ActiveRecord :float => { :name => "float" }, :decimal => { :name => "decimal" }, :datetime => { :name => "datetime" }, - :timestamp => { :name => "datetime" }, :time => { :name => "time" }, :date => { :name => "date" }, :binary => { :name => "blob" }, @@ -318,6 +305,11 @@ module ActiveRecord # DATABASE STATEMENTS ====================================== + def clear_cache! + super + reload_type_map + end + # Executes the SQL statement in the context of this connection. def execute(sql, name = nil) log(sql, name) { @connection.query(sql) } @@ -645,6 +637,15 @@ module ActiveRecord protected + def initialize_type_map(m) + super + m.alias_type %r(tinyint\(1\))i, 'boolean' if emulate_booleans + m.alias_type %r(enum)i, 'varchar' + m.alias_type %r(set)i, 'varchar' + m.alias_type %r(year)i, 'integer' + m.alias_type %r(bit)i, 'binary' + end + # MySQL is too stupid to create a temporary table for use subquery, so we have # to give it some prompting in the form of a subsubquery. Ugh! def subquery_for(key, select) diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index 3bab325e42..25a9cdafcf 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -13,11 +13,13 @@ module ActiveRecord ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/ end - attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale, :default_function + attr_reader :name, :default, :cast_type, :limit, :null, :sql_type, :precision, :scale, :default_function attr_accessor :primary, :coder alias :encoded? :coder + delegate :type, to: :cast_type + # Instantiates a new column in the table. # # +name+ is the column's name, such as <tt>supplier_id</tt> in <tt>supplier_id int(11)</tt>. @@ -35,7 +37,6 @@ module ActiveRecord @limit = extract_limit(sql_type) @precision = extract_precision(sql_type) @scale = extract_scale(sql_type) - @type = simplified_type(sql_type) @default = extract_default(default) @default_function = nil @primary = nil @@ -62,7 +63,7 @@ module ActiveRecord when :integer then Fixnum when :float then Float when :decimal then BigDecimal - when :datetime, :timestamp, :time then Time + when :datetime, :time then Time when :date then Date when :text, :string, :binary then String when :boolean then Object @@ -109,7 +110,7 @@ module ActiveRecord when :integer then klass.value_to_integer(value) when :float then value.to_f when :decimal then klass.value_to_decimal(value) - when :datetime, :timestamp then klass.string_to_time(value) + when :datetime then klass.string_to_time(value) when :time then klass.string_to_dummy_time(value) when :date then klass.value_to_date(value) when :binary then klass.binary_to_string(value) @@ -256,6 +257,8 @@ module ActiveRecord end private + delegate :extract_scale, to: Type + def extract_limit(sql_type) $1.to_i if sql_type =~ /\((.*)\)/ end @@ -263,40 +266,6 @@ module ActiveRecord def extract_precision(sql_type) $2.to_i if sql_type =~ /^(numeric|decimal|number)\((\d+)(,\d+)?\)/i end - - def extract_scale(sql_type) - case sql_type - when /^(numeric|decimal|number)\((\d+)\)/i then 0 - when /^(numeric|decimal|number)\((\d+)(,(\d+))\)/i then $4.to_i - end - end - - def simplified_type(field_type) - case field_type - when /int/i - :integer - when /float|double/i - :float - when /decimal|numeric|number/i - extract_scale(field_type) == 0 ? :integer : :decimal - when /datetime/i - :datetime - when /timestamp/i - :timestamp - when /time/i - :time - when /date/i - :date - when /clob/i, /text/i - :text - when /blob/i, /binary/i - :binary - when /char/i - :string - when /boolean/i - :boolean - end - end end end # :startdoc: diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 69e2b0ab2b..bf09bfe217 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -223,6 +223,7 @@ module ActiveRecord # Clears the prepared statements cache. def clear_cache! + super @statements.clear end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb index 77fdebbbc9..1dd8acc257 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb @@ -165,11 +165,6 @@ module ActiveRecord super end end - - # Maps PostgreSQL-specific data types to logical Rails types. - def simplified_type(field_type) - @oid_type.simplified_type(field_type) || super - end end end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb index a97c33ae6f..90bf6c6d1a 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb @@ -2,10 +2,7 @@ module ActiveRecord module ConnectionAdapters module PostgreSQL module OID # :nodoc: - class Type - def type; end - def simplified_type(sql_type); type end - + class Type < Type::Value def infinity(options = {}) ::Float::INFINITY * (options[:negative] ? -1 : 1) end @@ -136,11 +133,11 @@ module ActiveRecord end class Range < Type - attr_reader :subtype - def simplified_type(sql_type); sql_type.to_sym end + attr_reader :subtype, :type - def initialize(subtype) + def initialize(subtype, type) @subtype = subtype + @type = type end def extract_bounds(value) @@ -412,7 +409,7 @@ This is not reliable and will be removed in the future. def register_range_type(row) if subtype = @store[row['rngsubtype'].to_i] - register row['oid'], OID::Range.new(subtype) + register row['oid'], OID::Range.new(subtype, row['typname'].to_sym) end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 183d0c4ec6..39fa75518b 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -216,7 +216,6 @@ module ActiveRecord float: { name: "float" }, decimal: { name: "decimal" }, datetime: { name: "timestamp" }, - timestamp: { name: "timestamp" }, time: { name: "time" }, date: { name: "date" }, daterange: { name: "daterange" }, @@ -539,10 +538,6 @@ module ActiveRecord private - def type_map - @type_map - end - def get_oid_type(oid, fmod, column_name) if !type_map.key?(oid) initialize_type_map(type_map, [oid]) @@ -554,11 +549,6 @@ module ActiveRecord } end - def reload_type_map - type_map.clear - initialize_type_map(type_map) - end - def initialize_type_map(type_map, oids = nil) if supports_ranges? query = <<-SQL diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 0330fa762c..03ff0d4ead 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -69,7 +69,6 @@ module ActiveRecord float: { name: "float" }, decimal: { name: "decimal" }, datetime: { name: "datetime" }, - timestamp: { name: "datetime" }, time: { name: "time" }, date: { name: "date" }, binary: { name: "blob" }, diff --git a/activerecord/lib/active_record/connection_adapters/type.rb b/activerecord/lib/active_record/connection_adapters/type.rb index 1b27377cde..0268e0d569 100644 --- a/activerecord/lib/active_record/connection_adapters/type.rb +++ b/activerecord/lib/active_record/connection_adapters/type.rb @@ -1,8 +1,27 @@ require 'active_record/connection_adapters/type/value' +require 'active_record/connection_adapters/type/binary' +require 'active_record/connection_adapters/type/boolean' +require 'active_record/connection_adapters/type/date' +require 'active_record/connection_adapters/type/date_time' +require 'active_record/connection_adapters/type/decimal' +require 'active_record/connection_adapters/type/float' +require 'active_record/connection_adapters/type/integer' +require 'active_record/connection_adapters/type/string' +require 'active_record/connection_adapters/type/text' +require 'active_record/connection_adapters/type/time' +require 'active_record/connection_adapters/type/type_map' module ActiveRecord module ConnectionAdapters module Type # :nodoc: + class << self + def extract_scale(sql_type) + case sql_type + when /^(numeric|decimal|number)\((\d+)\)/i then 0 + when /^(numeric|decimal|number)\((\d+)(,(\d+))\)/i then $4.to_i + end + end + end end end end diff --git a/activerecord/lib/active_record/connection_adapters/type/binary.rb b/activerecord/lib/active_record/connection_adapters/type/binary.rb new file mode 100644 index 0000000000..168d824d3d --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/binary.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class Binary < Value # :nodoc: + def type + :binary + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/boolean.rb b/activerecord/lib/active_record/connection_adapters/type/boolean.rb new file mode 100644 index 0000000000..938d227632 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/boolean.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class Boolean < Value # :nodoc: + def type + :boolean + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/date.rb b/activerecord/lib/active_record/connection_adapters/type/date.rb new file mode 100644 index 0000000000..1632f3c8f4 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/date.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class Date < Value # :nodoc: + def type + :date + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/date_time.rb b/activerecord/lib/active_record/connection_adapters/type/date_time.rb new file mode 100644 index 0000000000..1485fd3d80 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/date_time.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class DateTime < Value # :nodoc: + def type + :datetime + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/decimal.rb b/activerecord/lib/active_record/connection_adapters/type/decimal.rb new file mode 100644 index 0000000000..5b39ea9e2f --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/decimal.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class Decimal < Value # :nodoc: + def type + :decimal + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/float.rb b/activerecord/lib/active_record/connection_adapters/type/float.rb new file mode 100644 index 0000000000..089169e7c9 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/float.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class Float < Value # :nodoc: + def type + :float + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/integer.rb b/activerecord/lib/active_record/connection_adapters/type/integer.rb new file mode 100644 index 0000000000..5510a11bd4 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/integer.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class Integer < Value # :nodoc: + def type + :integer + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/string.rb b/activerecord/lib/active_record/connection_adapters/type/string.rb new file mode 100644 index 0000000000..0feb4299f5 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/string.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class String < Value # :nodoc: + def type + :string + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/text.rb b/activerecord/lib/active_record/connection_adapters/type/text.rb new file mode 100644 index 0000000000..ee5842a3fc --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/text.rb @@ -0,0 +1,13 @@ +require 'active_record/connection_adapters/type/string' + +module ActiveRecord + module ConnectionAdapters + module Type + class Text < String # :nodoc: + def type + :text + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/time.rb b/activerecord/lib/active_record/connection_adapters/type/time.rb new file mode 100644 index 0000000000..a3a687a8ad --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/time.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class Time < Value # :nodoc: + def type + :time + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/type_map.rb b/activerecord/lib/active_record/connection_adapters/type/type_map.rb new file mode 100644 index 0000000000..d89171a820 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/type/type_map.rb @@ -0,0 +1,50 @@ +module ActiveRecord + module ConnectionAdapters + module Type + class TypeMap # :nodoc: + def initialize + @mapping = {} + end + + def lookup(lookup_key) + matching_pair = @mapping.reverse_each.detect do |key, _| + key === lookup_key + end + + if matching_pair + matching_pair.last.call(lookup_key) + else + default_value + end + end + + def register_type(key, value = nil, &block) + raise ::ArgumentError unless value || block + + if block + @mapping[key] = block + else + @mapping[key] = proc { value } + end + end + + def alias_type(key, target_key) + register_type(key) do |sql_type| + metadata = sql_type[/\(.*\)/, 0] + lookup("#{target_key}#{metadata}") + end + end + + def clear + @mapping.clear + end + + private + + def default_value + @default_value ||= Value.new + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/type/value.rb b/activerecord/lib/active_record/connection_adapters/type/value.rb index 36f680050f..f7d7b9351b 100644 --- a/activerecord/lib/active_record/connection_adapters/type/value.rb +++ b/activerecord/lib/active_record/connection_adapters/type/value.rb @@ -2,6 +2,7 @@ module ActiveRecord module ConnectionAdapters module Type class Value # :nodoc: + def type; end end end end diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index fc579e5c0f..8fe32bcb6c 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -640,7 +640,7 @@ module ActiveRecord say_with_time "#{method}(#{arg_list})" do unless @connection.respond_to? :revert - unless arguments.empty? || method == :execute + unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method) arguments[0] = proper_table_name(arguments.first, table_name_options) arguments[1] = proper_table_name(arguments.second, table_name_options) if method == :rename_table end diff --git a/activerecord/test/cases/adapters/mysql/quoting_test.rb b/activerecord/test/cases/adapters/mysql/quoting_test.rb index f650e3fde8..d8a954efa8 100644 --- a/activerecord/test/cases/adapters/mysql/quoting_test.rb +++ b/activerecord/test/cases/adapters/mysql/quoting_test.rb @@ -9,13 +9,13 @@ module ActiveRecord end def test_type_cast_true - c = Column.new(nil, 1, Type::Value.new, 'boolean') + c = Column.new(nil, 1, Type::Boolean.new) assert_equal 1, @conn.type_cast(true, nil) assert_equal 1, @conn.type_cast(true, c) end def test_type_cast_false - c = Column.new(nil, 1, Type::Value.new, 'boolean') + c = Column.new(nil, 1, Type::Boolean.new) assert_equal 0, @conn.type_cast(false, nil) assert_equal 0, @conn.type_cast(false, c) end diff --git a/activerecord/test/cases/adapters/postgresql/composite_test.rb b/activerecord/test/cases/adapters/postgresql/composite_test.rb index 68b9e6daf7..1e7071c136 100644 --- a/activerecord/test/cases/adapters/postgresql/composite_test.rb +++ b/activerecord/test/cases/adapters/postgresql/composite_test.rb @@ -85,7 +85,6 @@ class PostgresqlCompositeWithCustomOIDTest < ActiveRecord::TestCase class FullAddressType def type; :full_address end - def simplified_type(sql_type); type end def type_cast(value) if value =~ /\("?([^",]*)"?,"?([^",]*)"?\)/ diff --git a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb new file mode 100644 index 0000000000..91058f8681 --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb @@ -0,0 +1,65 @@ +require "cases/helper" +require "active_record/base" +require "active_record/connection_adapters/postgresql_adapter" + +class PostgresqlExtensionMigrationTest < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + class EnableHstore < ActiveRecord::Migration + def change + enable_extension "hstore" + end + end + + class DisableHstore < ActiveRecord::Migration + def change + disable_extension "hstore" + end + end + + def setup + super + + @connection = ActiveRecord::Base.connection + + unless @connection.supports_extensions? + return skip("no extension support") + end + + @old_schema_migration_tabel_name = ActiveRecord::SchemaMigration.table_name + @old_tabel_name_prefix = ActiveRecord::Base.table_name_prefix + @old_tabel_name_suffix = ActiveRecord::Base.table_name_suffix + + ActiveRecord::Base.table_name_prefix = "p_" + ActiveRecord::Base.table_name_suffix = "_s" + ActiveRecord::SchemaMigration.delete_all rescue nil + ActiveRecord::SchemaMigration.table_name = "p_schema_migrations_s" + ActiveRecord::Migration.verbose = false + end + + def teardown + ActiveRecord::Base.table_name_prefix = @old_tabel_name_prefix + ActiveRecord::Base.table_name_suffix = @old_tabel_name_suffix + ActiveRecord::SchemaMigration.delete_all rescue nil + ActiveRecord::Migration.verbose = true + ActiveRecord::SchemaMigration.table_name = @old_schema_migration_tabel_name + + super + end + + def test_enable_extension_migration_ignores_prefix_and_suffix + @connection.disable_extension("hstore") + + migrations = [EnableHstore.new(nil, 1)] + ActiveRecord::Migrator.new(:up, migrations).migrate + assert @connection.extension_enabled?("hstore"), "extension hstore should be enabled" + end + + def test_disable_extension_migration_ignores_prefix_and_suffix + @connection.enable_extension("hstore") + + migrations = [DisableHstore.new(nil, 1)] + ActiveRecord::Migrator.new(:up, migrations).migrate + assert_not @connection.extension_enabled?("hstore"), "extension hstore should not be enabled" + end +end diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb index 63170e710e..0c4f06d6a9 100644 --- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb @@ -47,13 +47,13 @@ module ActiveRecord end def test_type_cast_true - c = Column.new(nil, 1, Type::Value.new, 'int') + c = Column.new(nil, 1, Type::Integer.new) assert_equal 't', @conn.type_cast(true, nil) assert_equal 1, @conn.type_cast(true, c) end def test_type_cast_false - c = Column.new(nil, 1, Type::Value.new, 'int') + c = Column.new(nil, 1, Type::Integer.new) assert_equal 'f', @conn.type_cast(false, nil) assert_equal 0, @conn.type_cast(false, c) end @@ -61,16 +61,16 @@ module ActiveRecord def test_type_cast_string assert_equal '10', @conn.type_cast('10', nil) - c = Column.new(nil, 1, Type::Value.new, 'int') + c = Column.new(nil, 1, Type::Integer.new) assert_equal 10, @conn.type_cast('10', c) - c = Column.new(nil, 1, Type::Value.new, 'float') + c = Column.new(nil, 1, Type::Float.new) assert_equal 10.1, @conn.type_cast('10.1', c) - c = Column.new(nil, 1, Type::Value.new, 'binary') + c = Column.new(nil, 1, Type::Binary.new) assert_equal '10.1', @conn.type_cast('10.1', c) - c = Column.new(nil, 1, Type::Value.new, 'date') + c = Column.new(nil, 1, Type::Date.new) assert_equal '10.1', @conn.type_cast('10.1', c) end diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 38e93288e4..495b43c9e5 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -516,7 +516,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase end def test_only_time_related_columns_are_meant_to_be_cached_by_default - expected = %w(datetime timestamp time date).sort + expected = %w(datetime time date).sort assert_equal expected, ActiveRecord::Base.attribute_types_cached_by_default.map(&:to_s).sort end diff --git a/activerecord/test/cases/column_definition_test.rb b/activerecord/test/cases/column_definition_test.rb index 33aba6421e..23e6254577 100644 --- a/activerecord/test/cases/column_definition_test.rb +++ b/activerecord/test/cases/column_definition_test.rb @@ -12,13 +12,13 @@ module ActiveRecord end def test_can_set_coder - column = Column.new("title", nil, Type::Value.new, "varchar(20)") + column = Column.new("title", nil, Type::String.new, "varchar(20)") column.coder = YAML assert_equal YAML, column.coder end def test_encoded? - column = Column.new("title", nil, Type::Value.new, "varchar(20)") + column = Column.new("title", nil, Type::String.new, "varchar(20)") assert !column.encoded? column.coder = YAML @@ -26,7 +26,7 @@ module ActiveRecord end def test_type_case_coded_column - column = Column.new("title", nil, Type::Value.new, "varchar(20)") + column = Column.new("title", nil, Type::String.new, "varchar(20)") column.coder = YAML assert_equal "hello", column.type_cast("--- hello") end @@ -34,7 +34,7 @@ module ActiveRecord # Avoid column definitions in create table statements like: # `title` varchar(255) DEFAULT NULL def test_should_not_include_default_clause_when_default_is_null - column = Column.new("title", nil, Type::Value.new, "varchar(20)") + column = Column.new("title", nil, Type::String.new, "varchar(20)") column_def = ColumnDefinition.new( column.name, "string", column.limit, column.precision, column.scale, column.default, column.null) @@ -42,7 +42,7 @@ module ActiveRecord end def test_should_include_default_clause_when_default_is_present - column = Column.new("title", "Hello", Type::Value.new, "varchar(20)") + column = Column.new("title", "Hello", Type::String.new, "varchar(20)") column_def = ColumnDefinition.new( column.name, "string", column.limit, column.precision, column.scale, column.default, column.null) @@ -50,7 +50,7 @@ module ActiveRecord end def test_should_specify_not_null_if_null_option_is_false - column = Column.new("title", "Hello", Type::Value.new, "varchar(20)", false) + column = Column.new("title", "Hello", Type::String.new, "varchar(20)", false) column_def = ColumnDefinition.new( column.name, "string", column.limit, column.precision, column.scale, column.default, column.null) @@ -59,68 +59,68 @@ module ActiveRecord if current_adapter?(:MysqlAdapter) def test_should_set_default_for_mysql_binary_data_types - binary_column = MysqlAdapter::Column.new("title", "a", Type::Value.new, "binary(1)") + binary_column = MysqlAdapter::Column.new("title", "a", Type::Binary.new, "binary(1)") assert_equal "a", binary_column.default - varbinary_column = MysqlAdapter::Column.new("title", "a", Type::Value.new, "varbinary(1)") + varbinary_column = MysqlAdapter::Column.new("title", "a", Type::Binary.new, "varbinary(1)") assert_equal "a", varbinary_column.default end def test_should_not_set_default_for_blob_and_text_data_types assert_raise ArgumentError do - MysqlAdapter::Column.new("title", "a", Type::Value.new, "blob") + MysqlAdapter::Column.new("title", "a", Type::Binary.new, "blob") end assert_raise ArgumentError do - MysqlAdapter::Column.new("title", "Hello", Type::Value.new, "text") + MysqlAdapter::Column.new("title", "Hello", Type::Text.new) end - text_column = MysqlAdapter::Column.new("title", nil, Type::Value.new, "text") + text_column = MysqlAdapter::Column.new("title", nil, Type::Text.new) assert_equal nil, text_column.default - not_null_text_column = MysqlAdapter::Column.new("title", nil, Type::Value.new, "text", false) + not_null_text_column = MysqlAdapter::Column.new("title", nil, Type::Text.new, "text", false) assert_equal "", not_null_text_column.default end def test_has_default_should_return_false_for_blob_and_text_data_types - blob_column = MysqlAdapter::Column.new("title", nil, Type::Value.new, "blob") + blob_column = MysqlAdapter::Column.new("title", nil, Type::Binary.new, "blob") assert !blob_column.has_default? - text_column = MysqlAdapter::Column.new("title", nil, Type::Value.new, "text") + text_column = MysqlAdapter::Column.new("title", nil, Type::Text.new) assert !text_column.has_default? end end if current_adapter?(:Mysql2Adapter) def test_should_set_default_for_mysql_binary_data_types - binary_column = Mysql2Adapter::Column.new("title", "a", Type::Value.new, "binary(1)") + binary_column = Mysql2Adapter::Column.new("title", "a", Type::Binary.new, "binary(1)") assert_equal "a", binary_column.default - varbinary_column = Mysql2Adapter::Column.new("title", "a", Type::Value.new, "varbinary(1)") + varbinary_column = Mysql2Adapter::Column.new("title", "a", Type::Binary.new, "varbinary(1)") assert_equal "a", varbinary_column.default end def test_should_not_set_default_for_blob_and_text_data_types assert_raise ArgumentError do - Mysql2Adapter::Column.new("title", "a", Type::Value.new, "blob") + Mysql2Adapter::Column.new("title", "a", Type::Binary.new, "blob") end assert_raise ArgumentError do - Mysql2Adapter::Column.new("title", "Hello", Type::Value.new, "text") + Mysql2Adapter::Column.new("title", "Hello", Type::Text.new) end - text_column = Mysql2Adapter::Column.new("title", nil, Type::Value.new, "text") + text_column = Mysql2Adapter::Column.new("title", nil, Type::Text.new) assert_equal nil, text_column.default - not_null_text_column = Mysql2Adapter::Column.new("title", nil, Type::Value.new, "text", false) + not_null_text_column = Mysql2Adapter::Column.new("title", nil, Type::Text.new, "text", false) assert_equal "", not_null_text_column.default end def test_has_default_should_return_false_for_blob_and_text_data_types - blob_column = Mysql2Adapter::Column.new("title", nil, Type::Value.new, "blob") + blob_column = Mysql2Adapter::Column.new("title", nil, Type::Binary.new, "blob") assert !blob_column.has_default? - text_column = Mysql2Adapter::Column.new("title", nil, Type::Value.new, "text") + text_column = Mysql2Adapter::Column.new("title", nil, Type::Text.new) assert !text_column.has_default? end end diff --git a/activerecord/test/cases/column_test.rb b/activerecord/test/cases/column_test.rb index c2135d75da..fffcb19e56 100644 --- a/activerecord/test/cases/column_test.rb +++ b/activerecord/test/cases/column_test.rb @@ -5,7 +5,7 @@ module ActiveRecord module ConnectionAdapters class ColumnTest < ActiveRecord::TestCase def test_type_cast_boolean - column = Column.new("field", nil, Type::Value.new, "boolean") + column = Column.new("field", nil, Type::Boolean.new) assert column.type_cast('').nil? assert column.type_cast(nil).nil? @@ -36,14 +36,14 @@ module ActiveRecord end def test_type_cast_string - column = Column.new("field", nil, Type::Value.new, "varchar") + column = Column.new("field", nil, Type::String.new) assert_equal "1", column.type_cast(true) assert_equal "0", column.type_cast(false) assert_equal "123", column.type_cast(123) end def test_type_cast_integer - column = Column.new("field", nil, Type::Value.new, "integer") + column = Column.new("field", nil, Type::Integer.new) assert_equal 1, column.type_cast(1) assert_equal 1, column.type_cast('1') assert_equal 1, column.type_cast('1ignore') @@ -56,50 +56,50 @@ module ActiveRecord end def test_type_cast_non_integer_to_integer - column = Column.new("field", nil, Type::Value.new, "integer") + column = Column.new("field", nil, Type::Integer.new) assert_nil column.type_cast([1,2]) assert_nil column.type_cast({1 => 2}) assert_nil column.type_cast((1..2)) end def test_type_cast_activerecord_to_integer - column = Column.new("field", nil, Type::Value.new, "integer") + column = Column.new("field", nil, Type::Integer.new) firm = Firm.create(:name => 'Apple') assert_nil column.type_cast(firm) end def test_type_cast_object_without_to_i_to_integer - column = Column.new("field", nil, Type::Value.new, "integer") + column = Column.new("field", nil, Type::Integer.new) assert_nil column.type_cast(Object.new) end def test_type_cast_nan_and_infinity_to_integer - column = Column.new("field", nil, Type::Value.new, "integer") + column = Column.new("field", nil, Type::Integer.new) assert_nil column.type_cast(Float::NAN) assert_nil column.type_cast(1.0/0.0) end def test_type_cast_float - column = Column.new("field", nil, Type::Value.new, "float") + column = Column.new("field", nil, Type::Float.new) assert_equal 1.0, column.type_cast("1") end def test_type_cast_decimal - column = Column.new("field", nil, Type::Value.new, "decimal") + column = Column.new("field", nil, Type::Decimal.new) assert_equal BigDecimal.new("0"), column.type_cast(BigDecimal.new("0")) assert_equal BigDecimal.new("123"), column.type_cast(123.0) assert_equal BigDecimal.new("1"), column.type_cast(:"1") end def test_type_cast_binary - column = Column.new("field", nil, Type::Value.new, "binary") + column = Column.new("field", nil, Type::Binary.new) assert_equal nil, column.type_cast(nil) assert_equal "1", column.type_cast("1") assert_equal 1, column.type_cast(1) end def test_type_cast_time - column = Column.new("field", nil, Type::Value.new, "time") + column = Column.new("field", nil, Type::Time.new) assert_equal nil, column.type_cast(nil) assert_equal nil, column.type_cast('') assert_equal nil, column.type_cast('ABC') @@ -109,19 +109,18 @@ module ActiveRecord end def test_type_cast_datetime_and_timestamp - [Column.new("field", nil, Type::Value.new, "datetime"), Column.new("field", nil, Type::Value.new, "timestamp")].each do |column| - assert_equal nil, column.type_cast(nil) - assert_equal nil, column.type_cast('') - assert_equal nil, column.type_cast(' ') - assert_equal nil, column.type_cast('ABC') - - datetime_string = Time.now.utc.strftime("%FT%T") - assert_equal datetime_string, column.type_cast(datetime_string).strftime("%FT%T") - end + column = Column.new("field", nil, Type::DateTime.new) + assert_equal nil, column.type_cast(nil) + assert_equal nil, column.type_cast('') + assert_equal nil, column.type_cast(' ') + assert_equal nil, column.type_cast('ABC') + + datetime_string = Time.now.utc.strftime("%FT%T") + assert_equal datetime_string, column.type_cast(datetime_string).strftime("%FT%T") end def test_type_cast_date - column = Column.new("field", nil, Type::Value.new, "date") + column = Column.new("field", nil, Type::Date.new) assert_equal nil, column.type_cast(nil) assert_equal nil, column.type_cast('') assert_equal nil, column.type_cast(' ') @@ -132,7 +131,7 @@ module ActiveRecord end def test_type_cast_duration_to_integer - column = Column.new("field", nil, Type::Value.new, "integer") + column = Column.new("field", nil, Type::Integer.new) assert_equal 1800, column.type_cast(30.minutes) assert_equal 7200, column.type_cast(2.hours) end @@ -147,7 +146,7 @@ module ActiveRecord if current_adapter?(:SQLite3Adapter) def test_binary_encoding - column = SQLite3Column.new("field", nil, Type::Value.new, "binary") + column = SQLite3Column.new("field", nil, Type::Binary.new) utf8_string = "a string".encode(Encoding::UTF_8) type_cast = column.type_cast(utf8_string) diff --git a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb new file mode 100644 index 0000000000..d4d67487db --- /dev/null +++ b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb @@ -0,0 +1,61 @@ +require "cases/helper" + +if current_adapter?(:MysqlAdapter, :Mysql2Adapter) +module ActiveRecord + module ConnectionAdapters + class MysqlTypeLookupTest < ActiveRecord::TestCase + setup do + @connection = ActiveRecord::Base.connection + end + + def test_boolean_types + emulate_booleans(true) do + assert_lookup_type :boolean, 'tinyint(1)' + assert_lookup_type :boolean, 'TINYINT(1)' + end + end + + def test_string_types + assert_lookup_type :string, "enum('one', 'two', 'three')" + assert_lookup_type :string, "ENUM('one', 'two', 'three')" + assert_lookup_type :string, "set('one', 'two', 'three')" + assert_lookup_type :string, "SET('one', 'two', 'three')" + end + + def test_binary_types + assert_lookup_type :binary, 'bit' + assert_lookup_type :binary, 'BIT' + end + + def test_integer_types + emulate_booleans(false) do + assert_lookup_type :integer, 'tinyint(1)' + assert_lookup_type :integer, 'TINYINT(1)' + assert_lookup_type :integer, 'year' + assert_lookup_type :integer, 'YEAR' + end + end + + private + + def assert_lookup_type(type, lookup) + cast_type = @connection.type_map.lookup(lookup) + assert_equal type, cast_type.type + end + + def emulate_booleans(value) + old_emulate_booleans = @connection.emulate_booleans + change_emulate_booleans(value) + yield + ensure + change_emulate_booleans(old_emulate_booleans) + end + + def change_emulate_booleans(value) + @connection.emulate_booleans = value + @connection.clear_cache! + end + end + end +end +end diff --git a/activerecord/test/cases/connection_adapters/type/type_map_test.rb b/activerecord/test/cases/connection_adapters/type/type_map_test.rb new file mode 100644 index 0000000000..300c2ed225 --- /dev/null +++ b/activerecord/test/cases/connection_adapters/type/type_map_test.rb @@ -0,0 +1,102 @@ +require "cases/helper" + +module ActiveRecord + module ConnectionAdapters + module Type + class TypeMapTest < ActiveRecord::TestCase + def test_default_type + mapping = TypeMap.new + + assert_kind_of Value, mapping.lookup(:undefined) + end + + def test_registering_types + boolean = Boolean.new + mapping = TypeMap.new + + mapping.register_type(/boolean/i, boolean) + + assert_equal mapping.lookup('boolean'), boolean + end + + def test_overriding_registered_types + time = Time.new + timestamp = DateTime.new + mapping = TypeMap.new + + mapping.register_type(/time/i, time) + mapping.register_type(/time/i, timestamp) + + assert_equal mapping.lookup('time'), timestamp + end + + def test_fuzzy_lookup + string = String.new + mapping = TypeMap.new + + mapping.register_type(/varchar/i, string) + + assert_equal mapping.lookup('varchar(20)'), string + end + + def test_aliasing_types + string = String.new + mapping = TypeMap.new + + mapping.register_type(/string/i, string) + mapping.alias_type(/varchar/i, 'string') + + assert_equal mapping.lookup('varchar'), string + end + + def test_changing_type_changes_aliases + time = Time.new + timestamp = DateTime.new + mapping = TypeMap.new + + mapping.register_type(/timestamp/i, time) + mapping.alias_type(/datetime/i, 'timestamp') + mapping.register_type(/timestamp/i, timestamp) + + assert_equal mapping.lookup('datetime'), timestamp + end + + def test_aliases_keep_metadata + mapping = TypeMap.new + + mapping.register_type(/decimal/i) { |sql_type| sql_type } + mapping.alias_type(/number/i, 'decimal') + + assert_equal mapping.lookup('number(20)'), 'decimal(20)' + assert_equal mapping.lookup('number'), 'decimal' + end + + def test_register_proc + string = String.new + binary = Binary.new + mapping = TypeMap.new + + mapping.register_type(/varchar/i) do |type| + if type.include?('(') + string + else + binary + end + end + + assert_equal mapping.lookup('varchar(20)'), string + assert_equal mapping.lookup('varchar'), binary + end + + def test_requires_value_or_block + mapping = TypeMap.new + + assert_raises(ArgumentError) do + mapping.register_type(/only key/i) + end + end + end + end + end +end + diff --git a/activerecord/test/cases/connection_adapters/type_lookup_test.rb b/activerecord/test/cases/connection_adapters/type_lookup_test.rb new file mode 100644 index 0000000000..18df30faf5 --- /dev/null +++ b/activerecord/test/cases/connection_adapters/type_lookup_test.rb @@ -0,0 +1,97 @@ +require "cases/helper" + +unless current_adapter?(:PostgreSQLAdapter) # PostgreSQL does not use type strigns for lookup +module ActiveRecord + module ConnectionAdapters + class TypeLookupTest < ActiveRecord::TestCase + setup do + @connection = ActiveRecord::Base.connection + end + + def test_boolean_types + assert_lookup_type :boolean, 'boolean' + assert_lookup_type :boolean, 'BOOLEAN' + end + + def test_string_types + assert_lookup_type :string, 'char' + assert_lookup_type :string, 'varchar' + assert_lookup_type :string, 'VARCHAR' + assert_lookup_type :string, 'varchar(255)' + assert_lookup_type :string, 'character varying' + end + + def test_binary_types + assert_lookup_type :binary, 'binary' + assert_lookup_type :binary, 'BINARY' + assert_lookup_type :binary, 'blob' + assert_lookup_type :binary, 'BLOB' + end + + def test_text_types + assert_lookup_type :text, 'text' + assert_lookup_type :text, 'TEXT' + assert_lookup_type :text, 'clob' + assert_lookup_type :text, 'CLOB' + end + + def test_date_types + assert_lookup_type :date, 'date' + assert_lookup_type :date, 'DATE' + end + + def test_time_types + assert_lookup_type :time, 'time' + assert_lookup_type :time, 'TIME' + end + + def test_datetime_types + assert_lookup_type :datetime, 'datetime' + assert_lookup_type :datetime, 'DATETIME' + assert_lookup_type :datetime, 'timestamp' + assert_lookup_type :datetime, 'TIMESTAMP' + end + + def test_decimal_types + assert_lookup_type :decimal, 'decimal' + assert_lookup_type :decimal, 'decimal(2,8)' + assert_lookup_type :decimal, 'DECIMAL' + assert_lookup_type :decimal, 'numeric' + assert_lookup_type :decimal, 'numeric(2,8)' + assert_lookup_type :decimal, 'NUMERIC' + assert_lookup_type :decimal, 'number' + assert_lookup_type :decimal, 'number(2,8)' + assert_lookup_type :decimal, 'NUMBER' + end + + def test_float_types + assert_lookup_type :float, 'float' + assert_lookup_type :float, 'FLOAT' + assert_lookup_type :float, 'double' + assert_lookup_type :float, 'DOUBLE' + end + + def test_integer_types + assert_lookup_type :integer, 'integer' + assert_lookup_type :integer, 'INTEGER' + assert_lookup_type :integer, 'tinyint' + assert_lookup_type :integer, 'smallint' + assert_lookup_type :integer, 'bigint' + assert_lookup_type :integer, 'decimal(2)' + assert_lookup_type :integer, 'decimal(2,0)' + assert_lookup_type :integer, 'numeric(2)' + assert_lookup_type :integer, 'numeric(2,0)' + assert_lookup_type :integer, 'number(2)' + assert_lookup_type :integer, 'number(2,0)' + end + + private + + def assert_lookup_type(type, lookup) + cast_type = @connection.type_map.lookup(lookup) + assert_equal type, cast_type.type + end + end + end +end +end diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb index cbad718b43..b63dcf4459 100644 --- a/activerecord/test/cases/migration/change_schema_test.rb +++ b/activerecord/test/cases/migration/change_schema_test.rb @@ -233,6 +233,23 @@ module ActiveRecord end end + def test_add_column_with_timestamp_type + connection.create_table :testings do |t| + t.column :foo, :timestamp + end + + klass = Class.new(ActiveRecord::Base) + klass.table_name = 'testings' + + assert_equal :datetime, klass.columns_hash['foo'].type + + if current_adapter?(:PostgreSQLAdapter) + assert_equal 'timestamp without time zone', klass.columns_hash['foo'].sql_type + else + assert_equal klass.connection.type_to_sql('datetime'), klass.columns_hash['foo'].sql_type + end + end + def test_change_column_quotes_column_names connection.create_table :testings do |t| t.column :select, :string diff --git a/guides/code/getting_started/config/environments/development.rb b/guides/code/getting_started/config/environments/development.rb index ae9ffe209a..5c1c600feb 100644 --- a/guides/code/getting_started/config/environments/development.rb +++ b/guides/code/getting_started/config/environments/development.rb @@ -27,4 +27,12 @@ Rails.application.configure do # Debug mode disables concatenation and preprocessing of assets. config.assets.debug = true + + # Generate digests for assets URLs. + config.assets.digest = true + + # Adds additional error checking when serving assets at runtime. + # Checks for improperly declared sprockets dependencies. + # Raises helpful error messages. + config.assets.raise_runtime_errors = true end diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index 950cfdca29..4d69d5168e 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -198,12 +198,9 @@ will result in your assets being included more than once. WARNING: When using asset precompilation, you will need to ensure that your controller assets will be precompiled when loading them on a per page basis. By -default .coffee and .scss files will not be precompiled on their own. This will -result in false positives during development as these files will work just fine -since assets are compiled on the fly in development mode. When running in -production, however, you will see 500 errors since live compilation is turned -off by default. See [Precompiling Assets](#precompiling-assets) for more -information on how precompiling works. +default .coffee and .scss files will not be precompiled on their own. See +[Precompiling Assets](#precompiling-assets) for more information on how +precompiling works. NOTE: You must have an ExecJS supported runtime in order to use CoffeeScript. If you are using Mac OS X or Windows, you have a JavaScript runtime installed in @@ -581,8 +578,21 @@ runtime. To disable this behavior you can set: config.assets.raise_runtime_errors = false ``` -When this option is true asset pipeline will check if all the assets loaded in your application -are included in the `config.assets.precompile` list. +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 +all requests for assets include digests. + +### Turning Digests Off + +You can turn off digests by updating `config/environments/development.rb` to +include: + +```ruby +config.assets.digests = false +``` + +When this option is true, digests will be generated for asset URLs. ### Turning Debugging Off diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 577bc86fa9..ba6a0feeef 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,7 @@ +* Default `config.assets.digest` to `true` in development. + + *Dan Kang* + * Load database configuration from the first database.yml available in paths. diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt index de12565a73..bbb409616d 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt @@ -30,6 +30,9 @@ Rails.application.configure do # number of complex assets. config.assets.debug = true + # Generate digests for assets URLs. + config.assets.digest = true + # Adds additional error checking when serving assets at runtime. # Checks for improperly declared sprockets dependencies. # Raises helpful error messages. diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb index 410b0f7d70..8f091cfdbf 100644 --- a/railties/test/application/assets_test.rb +++ b/railties/test/application/assets_test.rb @@ -50,6 +50,8 @@ module ApplicationTests end RUBY + add_to_env_config "development", "config.assets.digest = false" + require "#{app_path}/config/environment" get "/assets/demo.js" @@ -189,7 +191,6 @@ module ApplicationTests end test "asset pipeline should use a Sprockets::Index when config.assets.digest is true" do - add_to_config "config.assets.digest = true" add_to_config "config.action_controller.perform_caching = false" ENV["RAILS_ENV"] = "production" @@ -202,8 +203,6 @@ module ApplicationTests app_file "app/assets/images/rails.png", "notactuallyapng" app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>" app_file "app/assets/javascripts/application.js", "alert();" - # digest is default in false, we must enable it for test environment - add_to_config "config.assets.digest = true" precompile! manifest = Dir["#{app_path}/public/assets/manifest-*.json"].first @@ -215,8 +214,6 @@ module ApplicationTests test "the manifest file should be saved by default in the same assets folder" do app_file "app/assets/javascripts/application.js", "alert();" - # digest is default in false, we must enable it for test environment - add_to_config "config.assets.digest = true" add_to_config "config.assets.prefix = '/x'" precompile! @@ -249,7 +246,6 @@ module ApplicationTests test "precompile properly refers files referenced with asset_path and runs in the provided RAILS_ENV" do app_file "app/assets/images/rails.png", "notactuallyapng" app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>" - # digest is default in false, we must enable it for test environment add_to_env_config "test", "config.assets.digest = true" precompile!('RAILS_ENV=test') @@ -281,12 +277,9 @@ module ApplicationTests test "precompile appends the md5 hash to files referenced with asset_path and run in production with digest true" do app_file "app/assets/images/rails.png", "notactuallyapng" app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>" - add_to_config "config.assets.compile = true" - add_to_config "config.assets.digest = true" - ENV["RAILS_ENV"] = nil - - precompile!('RAILS_GROUPS=assets') + ENV["RAILS_ENV"] = "production" + precompile! file = Dir["#{app_path}/public/assets/application-*.css"].first assert_match(/\/assets\/rails-([0-z]+)\.png/, File.read(file)) @@ -342,6 +335,8 @@ module ApplicationTests end RUBY + add_to_env_config "development", "config.assets.digest = false" + require "#{app_path}/config/environment" class ::OmgController < ActionController::Base @@ -366,6 +361,8 @@ module ApplicationTests app_file "app/assets/javascripts/demo.js", "alert();" + add_to_env_config "development", "config.assets.digest = false" + require "#{app_path}/config/environment" get "/assets/demo.js" @@ -395,7 +392,6 @@ module ApplicationTests app_file "app/assets/javascripts/application.js", "//= require_tree ." app_file "app/assets/javascripts/xmlhr.js.erb", "<%= Post.name %>" - add_to_config "config.assets.digest = false" precompile! assert_equal "Post;\n", File.read(Dir["#{app_path}/public/assets/application-*.js"].first) end @@ -415,7 +411,6 @@ module ApplicationTests test "digested assets are not mistakenly removed" do app_file "app/assets/application.js", "alert();" add_to_config "config.assets.compile = true" - add_to_config "config.assets.digest = true" precompile! @@ -438,6 +433,7 @@ module ApplicationTests test "asset urls should use the request's protocol by default" do app_with_assets_in_view add_to_config "config.asset_host = 'example.com'" + add_to_env_config "development", "config.assets.digest = false" require "#{app_path}/config/environment" class ::PostsController < ActionController::Base; end @@ -452,6 +448,7 @@ module ApplicationTests app_file "app/assets/javascripts/image_loader.js.erb", "var src='<%= image_path('rails.png') %>';" add_to_config "config.assets.precompile = %w{rails.png image_loader.js}" add_to_config "config.asset_host = 'example.com'" + add_to_env_config "development", "config.assets.digest = false" precompile! assert_match "src='//example.com/assets/rails.png'", File.read(Dir["#{app_path}/public/assets/image_loader-*.js"].first) @@ -460,9 +457,9 @@ module ApplicationTests test "asset paths should use RAILS_RELATIVE_URL_ROOT by default" do ENV["RAILS_RELATIVE_URL_ROOT"] = "/sub/uri" app_file "app/assets/images/rails.png", "notreallyapng" - app_file "app/assets/javascripts/app.js.erb", "var src='<%= image_path('rails.png') %>';" add_to_config "config.assets.precompile = %w{rails.png app.js}" + add_to_env_config "development", "config.assets.digest = false" precompile! assert_match "src='/sub/uri/assets/rails.png'", File.read(Dir["#{app_path}/public/assets/app-*.js"].first) diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index 28e5b2ff1e..6240dc04ec 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -34,6 +34,7 @@ module RailtiesTest test "serving sprocket's assets" do @plugin.write "app/assets/javascripts/engine.js.erb", "<%= :alert %>();" + add_to_env_config "development", "config.assets.digest = false" boot_rails require 'rack/test' @@ -1080,6 +1081,7 @@ YAML RUBY add_to_config("config.railties_order = [:all, :main_app, Blog::Engine]") + add_to_env_config "development", "config.assets.digest = false" boot_rails |