diff options
Diffstat (limited to 'activerecord/lib')
4 files changed, 110 insertions, 73 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index a7856539b7..6aa4a2c5b3 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -80,9 +80,10 @@ module ActiveRecord when :decimal then klass.value_to_decimal(value) when :datetime, :timestamp then klass.string_to_time(value) when :time then klass.string_to_dummy_time(value) - when :date then klass.string_to_date(value) + when :date then klass.value_to_date(value) when :binary then klass.binary_to_string(value) when :boolean then klass.value_to_boolean(value) + when :hstore then klass.cast_hstore(value) else value end end @@ -97,9 +98,10 @@ module ActiveRecord when :decimal then "#{klass}.value_to_decimal(#{var_name})" when :datetime, :timestamp then "#{klass}.string_to_time(#{var_name})" when :time then "#{klass}.string_to_dummy_time(#{var_name})" - when :date then "#{klass}.string_to_date(#{var_name})" + when :date then "#{klass}.value_to_date(#{var_name})" when :binary then "#{klass}.binary_to_string(#{var_name})" when :boolean then "#{klass}.value_to_boolean(#{var_name})" + when :hstore then "#{klass}.cast_hstore(#{var_name})" else var_name end end @@ -132,11 +134,15 @@ module ActiveRecord value end - def string_to_date(string) - return string unless string.is_a?(String) - return nil if string.empty? - - fast_string_to_date(string) || fallback_string_to_date(string) + def value_to_date(value) + if value.is_a?(String) + return nil if value.empty? + fast_string_to_date(value) || fallback_string_to_date(value) + elsif value.respond_to?(:to_date) + value.to_date + else + value + end end def string_to_time(string) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 6b742ed858..d7adcdc5d4 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -49,6 +49,42 @@ module ActiveRecord super end end + + def cast_hstore(object) + if Hash === object + object.map { |k,v| + "#{escape_hstore(k)}=>#{escape_hstore(v)}" + }.join ', ' + else + kvs = object.scan(/(?<!\\)".*?(?<!\\)"/).map { |o| + unescape_hstore(o[1...-1]) + } + Hash[kvs.each_slice(2).to_a] + end + end + + private + HSTORE_ESCAPE = { + ' ' => '\\ ', + '\\' => '\\\\', + '"' => '\\"', + '=' => '\\=', + } + HSTORE_ESCAPE_RE = Regexp.union(HSTORE_ESCAPE.keys) + HSTORE_UNESCAPE = HSTORE_ESCAPE.invert + HSTORE_UNESCAPE_RE = Regexp.union(HSTORE_UNESCAPE.keys) + + def unescape_hstore(value) + value.gsub(HSTORE_UNESCAPE_RE) do |match| + HSTORE_UNESCAPE[match] + end + end + + def escape_hstore(value) + value.gsub(HSTORE_ESCAPE_RE) do |match| + HSTORE_ESCAPE[match] + end + end end # :startdoc: @@ -79,53 +115,55 @@ module ActiveRecord # Maps PostgreSQL-specific data types to logical Rails types. def simplified_type(field_type) case field_type - # Numeric and monetary types - when /^(?:real|double precision)$/ - :float - # Monetary types - when 'money' - :decimal - # Character types - when /^(?:character varying|bpchar)(?:\(\d+\))?$/ - :string - # Binary data types - when 'bytea' - :binary - # Date/time types - when /^timestamp with(?:out)? time zone$/ - :datetime - when 'interval' - :string - # Geometric types - when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/ - :string - # Network address types - when /^(?:cidr|inet|macaddr)$/ - :string - # Bit strings - when /^bit(?: varying)?(?:\(\d+\))?$/ - :string - # XML type - when 'xml' - :xml - # tsvector type - when 'tsvector' - :tsvector - # Arrays - when /^\D+\[\]$/ - :string - # Object identifier types - when 'oid' - :integer - # UUID type - when 'uuid' - :string - # Small and big integer types - when /^(?:small|big)int$/ - :integer - # Pass through all types that are not specific to PostgreSQL. - else - super + # Numeric and monetary types + when /^(?:real|double precision)$/ + :float + # Monetary types + when 'money' + :decimal + when 'hstore' + :hstore + # Character types + when /^(?:character varying|bpchar)(?:\(\d+\))?$/ + :string + # Binary data types + when 'bytea' + :binary + # Date/time types + when /^timestamp with(?:out)? time zone$/ + :datetime + when 'interval' + :string + # Geometric types + when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/ + :string + # Network address types + when /^(?:cidr|inet|macaddr)$/ + :string + # Bit strings + when /^bit(?: varying)?(?:\(\d+\))?$/ + :string + # XML type + when 'xml' + :xml + # tsvector type + when 'tsvector' + :tsvector + # Arrays + when /^\D+\[\]$/ + :string + # Object identifier types + when 'oid' + :integer + # UUID type + when 'uuid' + :string + # Small and big integer types + when /^(?:small|big)int$/ + :integer + # Pass through all types that are not specific to PostgreSQL. + else + super end end @@ -215,6 +253,10 @@ module ActiveRecord options = args.extract_options! column(args[0], 'tsvector', options) end + + def hstore(name, options = {}) + column(name, 'hstore', options) + end end ADAPTER_NAME = 'PostgreSQL' diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb index 9840cbccae..6b5070808a 100644 --- a/activerecord/lib/active_record/scoping/default.rb +++ b/activerecord/lib/active_record/scoping/default.rb @@ -12,7 +12,7 @@ module ActiveRecord end module ClassMethods - # Returns a scope for this class without taking into account the default_scope. + # Returns a scope for the model without the default_scope. # # class Post < ActiveRecord::Base # def self.default_scope @@ -23,18 +23,20 @@ module ActiveRecord # Post.all # Fires "SELECT * FROM posts WHERE published = true" # Post.unscoped.all # Fires "SELECT * FROM posts" # - # This method also accepts a block meaning that all queries inside the block will + # This method also accepts a block. All queries inside the block will # not use the default_scope: # # Post.unscoped { # Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10" # } # - # It is recommended to use block form of unscoped because chaining unscoped with <tt>scope</tt> - # does not work. Assuming that <tt>published</tt> is a <tt>scope</tt> following two statements are same. + # It is recommended to use the block form of unscoped because chaining + # unscoped with <tt>scope</tt> does not work. Assuming that + # <tt>published</tt> is a <tt>scope</tt>, the following two statements + # are equal: the default_scope is applied on both. # - # Post.unscoped.published - # Post.published + # Post.unscoped.published + # Post.published def unscoped #:nodoc: block_given? ? relation.scoping { yield } : relation end diff --git a/activerecord/lib/active_record/test_case.rb b/activerecord/lib/active_record/test_case.rb index ffe9b08dce..21aff475a8 100644 --- a/activerecord/lib/active_record/test_case.rb +++ b/activerecord/lib/active_record/test_case.rb @@ -56,18 +56,5 @@ module ActiveRecord ensure ActiveRecord::SQLCounter.ignored_sql = prev_ignored_sql end - - def with_kcode(kcode) - if RUBY_VERSION < '1.9' - orig_kcode, $KCODE = $KCODE, kcode - begin - yield - ensure - $KCODE = orig_kcode - end - else - yield - end - end end end |