aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record/associations.rb10
-rw-r--r--activerecord/lib/active_record/associations/builder/association.rb5
-rw-r--r--activerecord/lib/active_record/associations/builder/singular_association.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/cast.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/column.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb52
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb50
8 files changed, 70 insertions, 86 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 6222bfe903..d9b339a1f6 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1309,6 +1309,10 @@ module ActiveRecord
# that is the inverse of this <tt>has_one</tt> association. Does not work in combination
# with <tt>:through</tt> or <tt>:as</tt> options.
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
+ # [:required]
+ # When set to +true+, the association will also have its presence validated.
+ # This will validate the association itself, not the id. You can use
+ # +:inverse_of+ to avoid an extra query during validation.
#
# Option examples:
# has_one :credit_card, dependent: :destroy # destroys the associated credit card
@@ -1320,6 +1324,7 @@ module ActiveRecord
# has_one :boss, readonly: :true
# has_one :club, through: :membership
# has_one :primary_address, -> { where primary: true }, through: :addressables, source: :addressable
+ # has_one :credit_card, required: true
def has_one(name, scope = nil, options = {})
reflection = Builder::HasOne.build(self, name, scope, options)
Reflection.add_reflection self, name, reflection
@@ -1421,6 +1426,10 @@ module ActiveRecord
# object that is the inverse of this <tt>belongs_to</tt> association. Does not work in
# combination with the <tt>:polymorphic</tt> options.
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
+ # [:required]
+ # When set to +true+, the association will also have its presence validated.
+ # This will validate the association itself, not the id. You can use
+ # +:inverse_of+ to avoid an extra query during validation.
#
# Option examples:
# belongs_to :firm, foreign_key: "client_of"
@@ -1433,6 +1442,7 @@ module ActiveRecord
# belongs_to :post, counter_cache: true
# belongs_to :company, touch: true
# belongs_to :company, touch: :employees_last_updated_at
+ # belongs_to :company, required: true
def belongs_to(name, scope = nil, options = {})
reflection = Builder::BelongsTo.build(self, name, scope, options)
Reflection.add_reflection self, name, reflection
diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb
index e474236939..947d61ee7b 100644
--- a/activerecord/lib/active_record/associations/builder/association.rb
+++ b/activerecord/lib/active_record/associations/builder/association.rb
@@ -36,6 +36,7 @@ module ActiveRecord::Associations::Builder
reflection = builder.build(model)
define_accessors model, reflection
define_callbacks model, reflection
+ define_validations model, reflection
builder.define_extensions model
reflection
end
@@ -124,6 +125,10 @@ module ActiveRecord::Associations::Builder
CODE
end
+ def self.define_validations(model, reflection)
+ # noop
+ end
+
def self.valid_dependent_options
raise NotImplementedError
end
diff --git a/activerecord/lib/active_record/associations/builder/singular_association.rb b/activerecord/lib/active_record/associations/builder/singular_association.rb
index e655c389a6..17f36acf40 100644
--- a/activerecord/lib/active_record/associations/builder/singular_association.rb
+++ b/activerecord/lib/active_record/associations/builder/singular_association.rb
@@ -3,7 +3,7 @@
module ActiveRecord::Associations::Builder
class SingularAssociation < Association #:nodoc:
def valid_options
- super + [:remote, :dependent, :primary_key, :inverse_of]
+ super + [:remote, :dependent, :primary_key, :inverse_of, :required]
end
def self.define_accessors(model, reflection)
@@ -27,5 +27,12 @@ module ActiveRecord::Associations::Builder
end
CODE
end
+
+ def self.define_validations(model, reflection)
+ super
+ if reflection.options[:required]
+ model.validates_presence_of reflection.name
+ end
+ 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
deleted file mode 100644
index 5b3e88fb08..0000000000
--- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-module ActiveRecord
- module ConnectionAdapters
- module PostgreSQL
- module Cast # :nodoc:
- def range_to_string(object) # :nodoc:
- from = object.begin.respond_to?(:infinite?) && object.begin.infinite? ? '' : object.begin
- to = object.end.respond_to?(:infinite?) && object.end.infinite? ? '' : object.end
- "[#{from},#{to}#{object.exclude_end? ? ')' : ']'}"
- end
- end
- end
- end
-end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
index 847fd4dded..37e5c3859c 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
@@ -1,11 +1,7 @@
-require 'active_record/connection_adapters/postgresql/cast'
-
module ActiveRecord
module ConnectionAdapters
# PostgreSQL-specific extensions to column definitions in a table.
class PostgreSQLColumn < Column #:nodoc:
- extend PostgreSQL::Cast
-
attr_accessor :array
def initialize(name, default, cast_type, sql_type = nil, null = true, default_function = nil)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
index 991cdd0913..ae967d5167 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
@@ -10,28 +10,10 @@ module ActiveRecord
@type = type
end
- def extract_bounds(value)
- from, to = value[1..-2].split(',')
- {
- from: (value[1] == ',' || from == '-infinity') ? @subtype.infinity(negative: true) : from,
- to: (value[-2] == ',' || to == 'infinity') ? @subtype.infinity : to,
- exclude_start: (value[0] == '('),
- exclude_end: (value[-1] == ')')
- }
- end
-
- def infinity?(value)
- value.respond_to?(:infinite?) && value.infinite?
- end
-
def type_cast_for_schema(value)
value.inspect.gsub('Infinity', '::Float::INFINITY')
end
- def type_cast_single(value)
- infinity?(value) ? value : @subtype.type_cast_from_database(value)
- end
-
def cast_value(value)
return if value == 'empty'
return value if value.is_a?(::Range)
@@ -53,6 +35,40 @@ This is not reliable and will be removed in the future.
end
::Range.new(from, to, extracted[:exclude_end])
end
+
+ def type_cast_for_database(value)
+ if value.is_a?(::Range)
+ from = type_cast_single_for_database(value.begin)
+ to = type_cast_single_for_database(value.end)
+ "[#{from},#{to}#{value.exclude_end? ? ')' : ']'}"
+ else
+ super
+ end
+ end
+
+ private
+
+ def type_cast_single(value)
+ infinity?(value) ? value : @subtype.type_cast_from_database(value)
+ end
+
+ def type_cast_single_for_database(value)
+ infinity?(value) ? '' : @subtype.type_cast_for_database(value)
+ end
+
+ def extract_bounds(value)
+ from, to = value[1..-2].split(',')
+ {
+ from: (value[1] == ',' || from == '-infinity') ? @subtype.infinity(negative: true) : from,
+ to: (value[-2] == ',' || to == 'infinity') ? @subtype.infinity : to,
+ exclude_start: (value[0] == '('),
+ exclude_end: (value[-1] == ')')
+ }
+ end
+
+ def infinity?(value)
+ value.respond_to?(:infinite?) && value.infinite?
+ 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 39b36731cd..be598f997f 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -21,13 +21,6 @@ module ActiveRecord
sql_type = type_to_sql(column.type, column.limit, column.precision, column.scale)
case value
- when Range
- if /range$/ =~ sql_type
- escaped = quote_string(PostgreSQLColumn.range_to_string(value))
- "'#{escaped}'::#{sql_type}"
- else
- super
- end
when Float
if value.infinite? || value.nan?
"'#{value.to_s}'"
@@ -54,12 +47,6 @@ module ActiveRecord
return super unless column
case value
- when Range
- if /range$/ =~ column.sql_type
- PostgreSQLColumn.range_to_string(value)
- else
- super
- end
when NilClass
if column.array
value
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 360922dfaa..e6163771e8 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -50,34 +50,6 @@ module ActiveRecord
end
end
- class SQLite3String < Type::String # :nodoc:
- def initialize(logger, *args)
- @logger = logger
- super(*args)
- end
-
- def type_cast_for_database(value)
- return unless value
-
- if value.encoding == Encoding::ASCII_8BIT
- @logger.error "Binary data inserted for `string` type" if @logger
- value.encode Encoding::UTF_8
- else
- value
- end
- end
-
- class Factory
- def initialize(logger)
- @logger = logger
- end
-
- def new(*args)
- SQLite3String.new(@logger, *args)
- end
- end
- end
-
# The SQLite3 adapter works SQLite 3.6.16 or newer
# with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
#
@@ -255,14 +227,6 @@ module ActiveRecord
end
end
- def _type_cast(value) # :nodoc:
- if value.is_a?(BigDecimal)
- value.to_f
- else
- super
- end
- end
-
def quote_string(s) #:nodoc:
@connection.class.quote(s)
end
@@ -285,6 +249,19 @@ module ActiveRecord
end
end
+ def type_cast(value, column) # :nodoc:
+ return value.to_f if BigDecimal === value
+ return super unless String === value
+ return super unless column && value
+
+ value = super
+ if column.type == :string && value.encoding == Encoding::ASCII_8BIT
+ logger.error "Binary data inserted for `string` type on column `#{column.name}`" if logger
+ value = value.encode Encoding::UTF_8
+ end
+ value
+ end
+
# DATABASE STATEMENTS ======================================
def explain(arel, binds = [])
@@ -526,7 +503,6 @@ module ActiveRecord
def initialize_type_map(m)
super
m.register_type(/binary/i, SQLite3Binary.new)
- register_class_with_limit m, %r(char)i, SQLite3String::Factory.new(logger)
end
def select(sql, name = nil, binds = []) #:nodoc: