Module: Typisch::DSL

Included in:
DSLContext
Defined in:
lib/typisch/dsl.rb

Overview

Apologies this is a bit messy. Should probably do a bit of a tidy-up once the dust has settled around the DSL syntax.

It’s a layer ontop of the core type model though - could be worse, it could be horribly intertwined with the model itself :)

Defined Under Namespace

Classes: DerivedObjectContext, ObjectContext

Instance Method Summary collapse

Instance Method Details

#_normalize_object_args(default_class, klass_or_properties = nil, properties = nil) ⇒ Object



93
94
95
96
97
98
99
# File 'lib/typisch/dsl.rb', line 93

def _normalize_object_args(default_class, klass_or_properties=nil, properties=nil)
  case klass_or_properties
  when ::Hash     then [default_class, klass_or_properties]
  when ::NilClass then [default_class, {}]
  when ::Module   then [klass_or_properties, properties || {}]
  end
end

#_object(klass, properties, derive_from = nil, &block) ⇒ Object

back-end for object, which takes args in a normalized format



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/typisch/dsl.rb', line 102

def _object(klass, properties, derive_from=nil, &block)
  if block
    object_context = if derive_from
      DerivedObjectContext.new(self, derive_from)
    else
      ObjectContext.new(self)
    end
    object_context.instance_eval(&block)
    properties.merge!(object_context.properties)
  end
  properties.keys.each do |k|
    type_args, type_block_arg = properties[k]
    properties[k] = type(*type_args, &type_block_arg)
  end
  type = Type::Object.new(klass.to_s, properties)
  if block && (prop_annot = object_context.property_annotations)
    type.annotations[:properties] = prop_annot
  end
  type
end

#annotate(description_or_options, options = nil) ⇒ Object

annotations apply to the next register’d type.

annotate “Some description”, :some_other => ‘annotations’ annotate :description => “Some description”, :some_other => ‘annotations’



41
42
43
44
45
46
47
48
49
# File 'lib/typisch/dsl.rb', line 41

def annotate(description_or_options, options=nil)
  if description_or_options.is_a?(::String)
    options ||= {}; options[:description] = description_or_options
  else
    options = description_or_options
  end
  @pending_annotations ||= {}
  @pending_annotations.merge!(options)
end

#derived_from(original_type, *args, &block_arg) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/typisch/dsl.rb', line 131

def derived_from(original_type, *args, &block_arg)
  if args.empty? && !block_arg
    original_type
  elsif args.last.is_a?(::Hash) && (version = args.last[:version]) && original_type.name
    # slightly messy; we rely on the convention that 'versions' of named types are registered
    # as :"#{name}__#{version}", this allows you to specify or override the version when
    # deriving from a type, even if it is still just a named placeholder, by manipulating the
    # name.
    original_name = original_type.name.to_s.sub(/__.*$/, '')
    :"#{original_name}__#{version}"
  else
    original_type = type(original_type).target
    case original_type
    when Type::Object
      klass, properties = _normalize_object_args(original_type.class_or_module, *args)
      _object(klass, properties, original_type, &block_arg)
    when Type::Sequence
      slice_overrides = (args.last.is_a?(::Hash) && args.last[:slice]) ? args.pop : {}
      Type::Sequence.new(derived_from(original_type.type, *args, &block_arg), slice_overrides)
    when Type::Union
      non_null = original_type.excluding_null
      raise "DSL doesn't support deriving from union types (except simple unions with null)" if Type::Union === non_null
      nullable(derived_from(non_null, *args, &block_arg))
    else
      raise "DSL doesn't support deriving from #{original_type.class} types" if args.length > 0 || block_arg
      original_type
    end
  end
end

#nullable(t) ⇒ Object



127
128
129
# File 'lib/typisch/dsl.rb', line 127

def nullable(t)
  union(type(t), :null)
end

#object(*args, &block) ⇒ Object



88
89
90
91
# File 'lib/typisch/dsl.rb', line 88

def object(*args, &block)
  klass, properties = _normalize_object_args(::Object, *args)
  _object(klass, properties, &block)
end

#register(name, *type_args, &type_block_arg) ⇒ Object



14
15
16
17
18
19
20
21
# File 'lib/typisch/dsl.rb', line 14

def register(name, *type_args, &type_block_arg)
  result = registry[name] = type(*type_args, &type_block_arg)
  if @pending_annotations
    result.annotations.merge!(@pending_annotations)
    @pending_annotations = nil
  end
  result
end

#register_type_for_class(klass, *object_type_args, &object_type_block_arg) ⇒ Object



23
24
25
26
27
28
# File 'lib/typisch/dsl.rb', line 23

def register_type_for_class(klass, *object_type_args, &object_type_block_arg)
  name = :"#{klass}"
  type = register(name, :object, klass, *object_type_args, &object_type_block_arg)
  registry.register_type_for_class(klass, type)
  type
end

#register_version_type_for_class(klass, version, *object_type_args, &object_type_block_arg) ⇒ Object



30
31
32
33
34
35
# File 'lib/typisch/dsl.rb', line 30

def register_version_type_for_class(klass, version, *object_type_args, &object_type_block_arg)
  name = :"#{klass}__#{version}"
  type = register(name, :object, klass, *object_type_args, &object_type_block_arg)
  registry.register_version_type_for_class(klass, version, type)
  type
end

#registryObject

Raises:

  • (NotImplementedError)


10
11
12
# File 'lib/typisch/dsl.rb', line 10

def registry
  raise NotImplementedError
end

#sequence(*args) ⇒ Object



74
75
76
77
78
79
80
81
82
# File 'lib/typisch/dsl.rb', line 74

def sequence(*args)
  seq_options = {}
  if (opts = args.last and opts.is_a?(::Hash))
    seq_options[:slice] = opts.delete(:slice) if opts.has_key?(:slice)
    seq_options[:total_length] = opts.delete(:total_length) if opts.has_key?(:total_length)
    args.pop if opts.empty?
  end
  Type::Sequence.new(type(*args), seq_options)
end

#string(refinements = nil) ⇒ Object



70
71
72
# File 'lib/typisch/dsl.rb', line 70

def string(refinements=nil)
  refinements ? Type::String.new(refinements) : registry[:string]
end

#tuple(*types) ⇒ Object



84
85
86
# File 'lib/typisch/dsl.rb', line 84

def tuple(*types)
  Type::Tuple.new(*types.map {|t| type(t)})
end

#type(arg, *more_args, &block_arg) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/typisch/dsl.rb', line 51

def type(arg, *more_args, &block_arg)
  case arg
  when Type
    arg
  when ::Symbol
    if more_args.empty? && !block_arg
      registry[arg]
    elsif more_args.last.is_a?(::Hash) && !block_arg && (version = more_args.last[:version])
      registry[:"#{arg}__#{version}"]
    else
      send(arg, *more_args, &block_arg)
    end
  when ::Module
    type(:"#{arg}", *more_args, &block_arg)
  else
    raise ArgumentError, "expected Type or type name or class, but was given #{arg.class}"
  end
end

#union(*types) ⇒ Object



123
124
125
# File 'lib/typisch/dsl.rb', line 123

def union(*types)
  Type::Union.new(*types.map {|t| type(t)})
end