aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib/active_model/type/type_map.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel/lib/active_model/type/type_map.rb')
-rw-r--r--activemodel/lib/active_model/type/type_map.rb64
1 files changed, 64 insertions, 0 deletions
diff --git a/activemodel/lib/active_model/type/type_map.rb b/activemodel/lib/active_model/type/type_map.rb
new file mode 100644
index 0000000000..402b660a71
--- /dev/null
+++ b/activemodel/lib/active_model/type/type_map.rb
@@ -0,0 +1,64 @@
+require 'concurrent'
+
+module ActiveModel
+ 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