Class: Typisch::Registry

Inherits:
Object
  • Object
show all
Defined in:
lib/typisch/registry.rb

Overview

A registry is a glorified hash lookup of types by name

  • provide a concise way of referring to more complex types

  • help with the wiring up of recursive types

-

Constant Summary collapse

GLOBALS =

While loading, we’ll register various types in this hash of types (boolean, string, …) which we want to be included in all registries

{}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(&block) ⇒ Registry

Returns a new instance of Registry.



11
12
13
14
15
16
17
# File 'lib/typisch/registry.rb', line 11

def initialize(&block)
  @types_by_name = GLOBALS.dup
  @pending_canonicalization = {}
  @types_by_class = {}
  @types_by_class_and_version = {}
  register(&block) if block
end

Instance Attribute Details

#types_by_classObject (readonly)

Returns the value of attribute types_by_class.



9
10
11
# File 'lib/typisch/registry.rb', line 9

def types_by_class
  @types_by_class
end

#types_by_class_and_versionObject (readonly)

Returns the value of attribute types_by_class_and_version.



9
10
11
# File 'lib/typisch/registry.rb', line 9

def types_by_class_and_version
  @types_by_class_and_version
end

#types_by_nameObject (readonly)

Returns the value of attribute types_by_name.



9
10
11
# File 'lib/typisch/registry.rb', line 9

def types_by_name
  @types_by_name
end

Class Method Details

.register_global_type(name, type) ⇒ Object



42
43
44
45
# File 'lib/typisch/registry.rb', line 42

def self.register_global_type(name, type)
  type.send(:name=, name) unless type.name
  GLOBALS[name] = type
end

Instance Method Details

#[](name, version = nil) ⇒ Object



19
20
21
22
23
# File 'lib/typisch/registry.rb', line 19

def [](name, version=nil)
  name = :"#{name}" if name.is_a?(::Module)
  name = :"#{name}__#{version}" if version
  @types_by_name[name] ||= Type::NamedPlaceholder.new(name, self)
end

#each_type_in_graph(*types) ⇒ Object



85
86
87
88
89
90
91
92
93
# File 'lib/typisch/registry.rb', line 85

def each_type_in_graph(*types)
  seen_so_far = {}
  while (type = types.pop)
    next if seen_so_far[type]
    seen_so_far[type] = true
    yield type
    types.push(*type.subexpression_types)
  end
end

#initialize_copy(other) ⇒ Object

Allow you to dup and merge registries



97
98
99
# File 'lib/typisch/registry.rb', line 97

def initialize_copy(other)
  @types_by_name = @types_by_name.dup
end

#merge(other) ⇒ Object



101
102
103
# File 'lib/typisch/registry.rb', line 101

def merge(other)
  dup.merge!(other)
end

#merge!(other) ⇒ Object



105
106
107
# File 'lib/typisch/registry.rb', line 105

def merge!(other)
  @types_by_name.merge!(other.types_by_name)
end

#register(&block) ⇒ Object

All registering of types in a registry needs to be done inside one of these blocks; it ensures that the any forward references or cyclic references are resolved (via canonicalize!-ing every type in the type graph) once you’ve finished registering types.

This also ensures that any uses of recursion are valid / well-founded, and does any other necessary validation of the type graph you’ve declared which isn’t possible to do upfront.

You can nest register blocks without ill-effect; it will only try to resolve forward references etc once the outermost block has exited.

Note, this is all very much non-threadsafe, wouldn’t be hard to make it so (probably just slap a big mutex around it) but not sure why exactly you’d want multi-threaded type registration anyway to anyway so leaving as-is for now.



62
63
64
65
66
67
68
69
70
# File 'lib/typisch/registry.rb', line 62

def register(&block)
  if @registering_types
    DSLContext.new(self).instance_eval(&block)
  else
    start_registering_types!
    DSLContext.new(self).instance_eval(&block)
    stop_registering_types!
  end
end

#register_type(name, type, &callback_on_canonicalization) ⇒ Object Also known as: []=



25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/typisch/registry.rb', line 25

def register_type(name, type, &callback_on_canonicalization)
  case @types_by_name[name]
  when Type::NamedPlaceholder
    @types_by_name[name].send(:target=, type)
  when NilClass
  else
    raise Error, "type already registered with name #{name.inspect}"
  end
  type.send(:name=, name) unless type.name
  @types_by_name[name] = type
  @pending_canonicalization[name] = [type, callback_on_canonicalization]
end

#register_type_for_class(klass, type) ⇒ Object



117
118
119
# File 'lib/typisch/registry.rb', line 117

def register_type_for_class(klass, type)
  @types_by_class[klass] = type
end

#register_version_type_for_class(klass, version, type) ⇒ Object



121
122
123
# File 'lib/typisch/registry.rb', line 121

def register_version_type_for_class(klass, version, type)
  @types_by_class_and_version[[klass, version]] = type
end

#start_registering_types!Object



72
73
74
# File 'lib/typisch/registry.rb', line 72

def start_registering_types!
  @registering_types = true
end

#stop_registering_types!Object



76
77
78
79
80
81
82
83
# File 'lib/typisch/registry.rb', line 76

def stop_registering_types!
  @registering_types = false

  types = @pending_canonicalization.values.map {|t,c| t}
  each_type_in_graph(*types) {|t| t.canonicalize!}
  @pending_canonicalization.each {|name,(type,callback)| callback.call if callback}
  @pending_canonicalization = {}
end

#to_sObject



109
110
111
112
113
114
115
# File 'lib/typisch/registry.rb', line 109

def to_s
  pairs = @types_by_name.map do |n,t|
    next if GLOBALS[n]
    "r.register #{n.inspect}, #{t.to_s(0, '  ')}"
  end.compact
  "Typisch::Registry.new do |r|\n  #{pairs.join("\n  ")}\nend"
end