aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/type.rb7
-rw-r--r--activerecord/lib/active_record/type/hash_lookup_type_map.rb23
-rw-r--r--activerecord/lib/active_record/type/type_map.rb64
3 files changed, 90 insertions, 4 deletions
diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb
index 165043021b..5b089d66a0 100644
--- a/activerecord/lib/active_record/type.rb
+++ b/activerecord/lib/active_record/type.rb
@@ -16,14 +16,15 @@ require 'active_model/type/time'
require 'active_model/type/unsigned_integer'
require 'active_model/type/registry'
-require 'active_model/type/type_map'
-require 'active_model/type/hash_lookup_type_map'
require 'active_record/type/internal/abstract_json'
require 'active_record/type/internal/timezone'
require 'active_record/type/serialized'
require 'active_record/type/adapter_specific_registry'
+require 'active_record/type/type_map'
+require 'active_record/type/hash_lookup_type_map'
+
module ActiveRecord
module Type
@registry = AdapterSpecificRegistry.new
@@ -78,8 +79,6 @@ module ActiveRecord
Text = ActiveModel::Type::Text
UnsignedInteger = ActiveModel::Type::UnsignedInteger
Value = ActiveModel::Type::Value
- TypeMap = ActiveModel::Type::TypeMap
- HashLookupTypeMap = ActiveModel::Type::HashLookupTypeMap
register(:big_integer, Type::BigInteger, override: false)
register(:binary, Type::Binary, override: false)
diff --git a/activerecord/lib/active_record/type/hash_lookup_type_map.rb b/activerecord/lib/active_record/type/hash_lookup_type_map.rb
new file mode 100644
index 0000000000..3b01e3f8ca
--- /dev/null
+++ b/activerecord/lib/active_record/type/hash_lookup_type_map.rb
@@ -0,0 +1,23 @@
+module ActiveRecord
+ module Type
+ class HashLookupTypeMap < TypeMap # :nodoc:
+ def alias_type(type, alias_type)
+ register_type(type) { |_, *args| lookup(alias_type, *args) }
+ end
+
+ def key?(key)
+ @mapping.key?(key)
+ end
+
+ def keys
+ @mapping.keys
+ end
+
+ private
+
+ def perform_fetch(type, *args, &block)
+ @mapping.fetch(type, block).call(type, *args)
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/type/type_map.rb b/activerecord/lib/active_record/type/type_map.rb
new file mode 100644
index 0000000000..8ce36cc9af
--- /dev/null
+++ b/activerecord/lib/active_record/type/type_map.rb
@@ -0,0 +1,64 @@
+require 'concurrent'
+
+module ActiveRecord
+ module Type
+ class TypeMap # :nodoc:
+ def initialize
+ @mapping = {}
+ @cache = Concurrent::Map.new do |h, key|
+ h.fetch_or_store(key, Concurrent::Map.new)
+ end
+ end
+
+ def lookup(lookup_key, *args)
+ fetch(lookup_key, *args) { default_value }
+ end
+
+ def fetch(lookup_key, *args, &block)
+ @cache[lookup_key].fetch_or_store(args) do
+ perform_fetch(lookup_key, *args, &block)
+ end
+ end
+
+ def register_type(key, value = nil, &block)
+ raise ::ArgumentError unless value || block
+ @cache.clear
+
+ if block
+ @mapping[key] = block
+ else
+ @mapping[key] = proc { value }
+ end
+ end
+
+ def alias_type(key, target_key)
+ register_type(key) do |sql_type, *args|
+ metadata = sql_type[/\(.*\)/, 0]
+ lookup("#{target_key}#{metadata}", *args)
+ end
+ end
+
+ def clear
+ @mapping.clear
+ end
+
+ private
+
+ def perform_fetch(lookup_key, *args)
+ matching_pair = @mapping.reverse_each.detect do |key, _|
+ key === lookup_key
+ end
+
+ if matching_pair
+ matching_pair.last.call(lookup_key, *args)
+ else
+ yield lookup_key, *args
+ end
+ end
+
+ def default_value
+ @default_value ||= Value.new
+ end
+ end
+ end
+end