icss 0.1.3 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. data/.watchr +35 -3
  2. data/CHANGELOG.md +38 -0
  3. data/Gemfile +19 -14
  4. data/README.md +296 -0
  5. data/Rakefile +2 -6
  6. data/TODO.md +13 -0
  7. data/VERSION +1 -1
  8. data/examples/avro_examples/complicated.icss.yaml +14 -13
  9. data/examples/bnc.icss.yaml +70 -0
  10. data/examples/chronic.icss.yaml +3 -3
  11. data/examples/license.icss.yaml +7 -0
  12. data/examples/source1.icss.yaml +4 -0
  13. data/examples/source2.icss.yaml +4 -0
  14. data/examples/test_icss.yaml +67 -0
  15. data/icss.gemspec +103 -43
  16. data/lib/icss.rb +37 -15
  17. data/lib/icss/core_types.rb +19 -0
  18. data/lib/icss/error.rb +4 -0
  19. data/{init.rb → lib/icss/init.rb} +0 -0
  20. data/lib/icss/message.rb +124 -66
  21. data/lib/icss/message/message_sample.rb +144 -0
  22. data/lib/icss/protocol.rb +184 -131
  23. data/lib/icss/protocol/code_asset.rb +18 -0
  24. data/lib/icss/protocol/data_asset.rb +23 -0
  25. data/lib/icss/protocol/license.rb +41 -0
  26. data/lib/icss/protocol/source.rb +37 -0
  27. data/lib/icss/protocol/target.rb +68 -0
  28. data/lib/icss/receiver_model.rb +24 -0
  29. data/lib/icss/receiver_model/active_model_shim.rb +36 -0
  30. data/lib/icss/receiver_model/acts_as_catalog.rb +170 -0
  31. data/lib/icss/receiver_model/acts_as_hash.rb +177 -0
  32. data/lib/icss/receiver_model/acts_as_loadable.rb +47 -0
  33. data/lib/icss/receiver_model/acts_as_tuple.rb +100 -0
  34. data/lib/icss/receiver_model/locale/en.yml +27 -0
  35. data/lib/icss/receiver_model/to_geo_json.rb +19 -0
  36. data/lib/icss/receiver_model/tree_merge.rb +34 -0
  37. data/lib/icss/receiver_model/validations.rb +31 -0
  38. data/lib/icss/serialization.rb +51 -0
  39. data/lib/icss/serialization/zaml.rb +443 -0
  40. data/lib/icss/type.rb +148 -501
  41. data/lib/icss/type/base_type.rb +0 -0
  42. data/lib/icss/type/named_type.rb +184 -0
  43. data/lib/icss/type/record_field.rb +77 -0
  44. data/lib/icss/type/record_model.rb +49 -0
  45. data/lib/icss/type/record_schema.rb +54 -0
  46. data/lib/icss/type/record_type.rb +325 -0
  47. data/lib/icss/type/simple_types.rb +72 -0
  48. data/lib/icss/type/structured_schema.rb +288 -0
  49. data/lib/icss/type/type_factory.rb +144 -0
  50. data/lib/icss/type/union_schema.rb +41 -0
  51. data/lib/icss/view_helper.rb +56 -19
  52. data/notes/named_array.md +32 -0
  53. data/notes/on_include_vs_extend_etc.rb +176 -0
  54. data/notes/technical_details.md +278 -0
  55. data/spec/core_types_spec.rb +119 -0
  56. data/spec/fixtures/zaml_complex_hash.yaml +35 -0
  57. data/spec/icss_spec.rb +86 -23
  58. data/spec/message/message_sample_spec.rb +4 -0
  59. data/spec/message_spec.rb +139 -0
  60. data/spec/protocol/license_spec.rb +67 -0
  61. data/spec/protocol/protocol_catalog_spec.rb +48 -0
  62. data/spec/protocol/protocol_validations_spec.rb +176 -0
  63. data/spec/protocol/source_spec.rb +65 -0
  64. data/spec/protocol_spec.rb +91 -37
  65. data/spec/receiver_model_spec.rb +111 -0
  66. data/spec/serialization/zaml_spec.rb +81 -0
  67. data/spec/serialization/zaml_test.rb +473 -0
  68. data/spec/serialization_spec.rb +63 -0
  69. data/spec/spec_helper.rb +24 -7
  70. data/spec/support/icss_test_helper.rb +67 -0
  71. data/spec/support/load_example_protocols.rb +17 -0
  72. data/spec/type/base_type_spec.rb +0 -0
  73. data/spec/type/named_type_spec.rb +75 -0
  74. data/spec/type/record_field_spec.rb +44 -0
  75. data/spec/type/record_model_spec.rb +206 -0
  76. data/spec/type/record_schema_spec.rb +161 -0
  77. data/spec/type/record_type_spec.rb +155 -0
  78. data/spec/type/simple_types_spec.rb +121 -0
  79. data/spec/type/structured_schema_spec.rb +300 -0
  80. data/spec/type/type_catalog_spec.rb +44 -0
  81. data/spec/type/type_factory_spec.rb +93 -0
  82. data/spec/type/union_schema_spec.rb +0 -0
  83. data/spec/type_spec.rb +63 -0
  84. metadata +205 -144
  85. data/CHANGELOG.textile +0 -9
  86. data/Gemfile.lock +0 -40
  87. data/README.textile +0 -29
  88. data/lib/icss/brevity.rb +0 -136
  89. data/lib/icss/code_asset.rb +0 -16
  90. data/lib/icss/core_ext.rb +0 -9
  91. data/lib/icss/data_asset.rb +0 -22
  92. data/lib/icss/old.rb +0 -96
  93. data/lib/icss/protocol_set.rb +0 -48
  94. data/lib/icss/sample_message_call.rb +0 -142
  95. data/lib/icss/target.rb +0 -72
  96. data/lib/icss/type/factory.rb +0 -196
  97. data/lib/icss/validations.rb +0 -16
  98. data/spec/validations_spec.rb +0 -171
@@ -1,521 +1,168 @@
1
- module Icss
2
- #
3
- # Describes an avro type
4
- #
5
- # A Schema is represented in JSON by one of:
6
- #
7
- # * A JSON string, naming a defined type.
8
- # * A JSON object, of the form:
9
- # {"type": "typeName" ...attributes...}
10
- # where typeName is either a primitive or derived type name, as defined
11
- # in the Icss::Type class
12
- # * A JSON array, representing a union of embedded types.
13
- #
14
- #
15
- class Type
16
- include Receiver
17
- rcvr_accessor :name, String
18
- rcvr_accessor :doc, String
19
- # Schema factory
20
- rcvr_accessor :ruby_klass, Object
21
- rcvr_accessor :pig_name, String
22
- rcvr_accessor :mysql_name, String
23
-
24
- #
25
- # Factory methods
26
- #
27
-
28
- # Registry for synthesized types (eg the result of a type record definition)
29
- Icss::Type::DERIVED_TYPES = {} unless defined?(Icss::Type::DERIVED_TYPES)
30
-
31
- # VALID_TYPES, PRIMITIVE_TYPES, etc are way down below (the klasses need to
32
- # be defined first)
33
-
34
- #
35
- def self.find type_name
36
- if type_name.to_s.include?('.')
37
- warn "crap. can't properly do namespaced types yet."
38
- type_name = type_name.to_s.gsub(/(.*)\./, "")
1
+ module Icss
2
+ module Meta
3
+ module Type
4
+ include Icss::ReceiverModel::ActsAsCatalog
5
+ def self.catalog_sections
6
+ ::Icss::Meta::Protocol.catalog_sections
7
+ end
8
+ def self.receive(hsh)
9
+ ::Icss::Meta::Protocol.receive(hsh)
39
10
  end
40
- Icss::Type::VALID_TYPES[type_name.to_sym] || Icss::Type::DERIVED_TYPES[type_name.to_sym]
41
- end
42
-
43
- def self.primitive? name
44
- PRIMITIVE_TYPES.include?(name.to_sym)
45
- end
46
- def primitive?
47
- false
48
- end
49
-
50
- def title
51
- self.name
52
- end
53
-
54
- #
55
- # Schema Translation
56
- #
57
- def self.pig_name
58
- "undefined_#{self.to_s.underscore}"
59
- end
60
- #
61
- # Conversion
62
- #
63
-
64
- def to_hash()
65
- {:name => name, :doc => doc }.reject{|k,v| v.nil? }
66
11
  end
67
- # This will cause funny errors when it is an element of something that's to_json'ed
68
- def to_json() to_hash.to_json ; end
69
12
  end
70
13
 
71
- # ---------------------------------------------------------------------------
72
- #
73
- # Primitive Types
74
14
  #
75
-
76
- class PrimitiveType < Type
77
- def to_hash
78
- name.to_s
79
- end
80
- def primitive?
81
- true
82
- end
83
- end
84
-
85
- # Registry for primitive types
86
- unless defined?(::Icss::Type::PRIMITIVE_TYPES)
87
- ::Icss::Type::PRIMITIVE_TYPES = {}
88
- ::Icss::Type::PRIMITIVE_TYPES[:null] = PrimitiveType.receive(:ruby_klass => NilClass, :pig_name => 'FIXME WHAT GOES HERE' )
89
- ::Icss::Type::PRIMITIVE_TYPES[:boolean] = PrimitiveType.receive(:ruby_klass => Boolean, :pig_name => 'FIXME WHAT GOES HERE')
90
- ::Icss::Type::PRIMITIVE_TYPES[:string] = PrimitiveType.receive(:ruby_klass => String, :pig_name => 'chararray',:mysql_name => 'VARCHAR')
91
- ::Icss::Type::PRIMITIVE_TYPES[:bytes] = PrimitiveType.receive(:ruby_klass => String, :pig_name => 'bytearray',:mysql_name => 'VARCHAR')
92
- ::Icss::Type::PRIMITIVE_TYPES[:int] = PrimitiveType.receive(:ruby_klass => Integer, :pig_name => 'int',:mysql_name => 'INT')
93
- ::Icss::Type::PRIMITIVE_TYPES[:long] = PrimitiveType.receive(:ruby_klass => Integer, :pig_name => 'long',:mysql_name => 'BIGINT')
94
- ::Icss::Type::PRIMITIVE_TYPES[:float] = PrimitiveType.receive(:ruby_klass => Float, :pig_name => 'float',:mysql_name => 'FLOAT')
95
- ::Icss::Type::PRIMITIVE_TYPES[:double] = PrimitiveType.receive(:ruby_klass => Float, :pig_name => 'double',:mysql_name => 'DOUBLE')
15
+ # Predefining the namespaces here makes inclusion-order less brittle.
16
+ #
17
+
18
+ # full definitions in type/simple_types.rb
19
+ class ::Boolean < ::BasicObject ; end
20
+ class ::Long < ::Integer ; end
21
+ class ::Double < ::Float ; end
22
+ class ::Binary < ::String ; end
23
+
24
+ # patron saint of Simple Types (Structured Text)
25
+ module St ; end
26
+ # pasture wherein graze MeasurementUnits
27
+ module Mu ; end
28
+ # stand in the place where you are
29
+ module Business ; end
30
+ #
31
+ module Culture ; end
32
+ # we gots the whhole worl innour hans
33
+ module Encyclopedic ; end
34
+ # Eventfully, Tom phoned the caterer.
35
+ module Ev ; end
36
+ #
37
+ module Geo ; end
38
+ # Relatively speaking, this is where links and relations go
39
+ module Rel ; end
40
+ # I don't want to sell anything, buy anything, or process anything as a career.
41
+ # I don't want to sell anything bought or processed, or buy anything sold or processed, or process
42
+ # anything sold, bought, or processed, or repair anything sold, bought, or processed.
43
+ # You know, as a career, I don't want to do that.
44
+ module Prod ; end
45
+ #
46
+ module Social ; end
47
+ # Oh what a tangled web we weave when first we practice to receive
48
+ module Web ; end
49
+ # Raw records, for use by data mungers
50
+ module Raw ; end
51
+
52
+ # Buffalo Buffalo buffalo Buffalo Buffalo Buffalo buffalow.
53
+ module Meta
54
+ # full definitions in type/structured_schema.rb and type/union_schema.rb
55
+ class NamedSchema ; end
56
+ class SimpleSchema < NamedSchema ; end
57
+ class UnionSchema < NamedSchema ; end
58
+ class RecordSchema < SimpleSchema ; end
59
+ class ErrorSchema < RecordSchema ; end
96
60
  #
97
- ::Icss::Type::PRIMITIVE_TYPES[:symbol] = PrimitiveType.receive(:ruby_klass => Symbol, :pig_name => 'chararray')
98
- ::Icss::Type::PRIMITIVE_TYPES[:time] = PrimitiveType.receive(:ruby_klass => Time, :pig_name => 'chararray')
99
- ::Icss::Type::PRIMITIVE_TYPES[:date] = PrimitiveType.receive(:ruby_klass => Date, :pig_name => 'chararray')
100
- ::Icss::Type::PRIMITIVE_TYPES.freeze
61
+ class StructuredSchema < NamedSchema ; end
62
+ class HashSchema < StructuredSchema ; end
63
+ class ArraySchema < StructuredSchema ; end
64
+ class FixedSchema < StructuredSchema ; end
65
+ class EnumSchema < StructuredSchema ; end
101
66
  end
102
67
 
103
- # ---------------------------------------------------------------------------
104
- #
105
- # Complex Types
106
- #
107
-
108
- #
109
- # Record, enums and fixed are named types. Each has a fullname that is
110
- # composed of two parts; a name and a namespace. Equality of names is defined
111
- # on the fullname.
112
- #
113
- # The name portion of a fullname, and record field names must:
114
- # * start with [A-Za-z_]
115
- # * subsequently contain only [A-Za-z0-9_]
116
- # * A namespace is a dot-separated sequence of such names.
117
- #
118
- # References to previously defined names are as in the latter two cases above:
119
- # if they contain a dot they are a fullname, if they do not contain a dot, the
120
- # namespace is the namespace of the enclosing definition.
121
- #
122
- # Primitive type names have no namespace and their names may not be defined in
123
- # any namespace. A schema may only contain multiple definitions of a fullname
124
- # if the definitions are equivalent.
125
- #
126
- #
127
- class NamedType < Type
128
- rcvr_accessor :namespace, String
129
- attr_accessor :parent
130
- # the avro base type name
131
- class_attribute :type
132
- include Icss::Validations
133
-
134
- # In named types, the namespace and name are determined in one of the following ways:
135
- #
136
- # * A name and namespace are both specified. For example, one might use
137
- # "name": "X", "namespace": "org.foo" to indicate the fullname org.foo.X.
138
- #
139
- # * A fullname is specified. If the name specified contains a dot, then it is
140
- # assumed to be a fullname, and any namespace also specified is ignored. For
141
- # example, use "name": "org.foo.X" to indicate the fullname org.foo.X.
142
- #
143
- # * A name only is specified, i.e., a name that contains no dots. In this case
144
- # the namespace is taken from the most tightly enclosing schema or
145
- # protocol. For example, if "name": "X" is specified, and this occurs within
146
- # a field of the record definition of org.foo.Y, then the fullname is
147
- # org.foo.X.
148
- #
149
- def name= nm
150
- if nm.include?('.')
151
- split_name = nm.split('.')
152
- @use_fullname = true
153
- @name = split_name.pop
154
- @namespace = split_name.join('.')
155
- else
156
- @name = nm
157
- end
158
- ::Icss::Type::DERIVED_TYPES[@name.to_sym] = self
159
- @name
160
- end
161
-
162
- def receive_namespace nmsp
163
- # If the namespace is given in the name (using a dotted name string) then
164
- # any namespace also specified is ignored.
165
- if @namespace then warn "Warning: namespace already set, ignoring" and return ; end
166
- @namespace = nmsp
167
- end
168
-
169
- def namespace
170
- @namespace || (parent ? parent.namespace : "")
171
- end
172
-
173
- #
174
- # If no explicit namespace is specified, the namespace is taken from the
175
- # most tightly enclosing schema or protocol. For example, if "name": "X" is
176
- # specified, and this occurs within a field of the record definition of
177
- # org.foo.Y, then the fullname is org.foo.X.
178
- #
179
- def fullname
180
- [namespace, name].reject(&:blank?).join('.')
181
- end
182
-
183
- def to_hash
184
- hsh = super
185
- if @use_fullname then hsh[:name] = fullname
186
- elsif @namespace then hsh.merge!( :namespace => @namespace ) ; end
187
- hsh.merge( :type => self.class.type )
188
- end
68
+ ::Icss::SIMPLE_TYPES = {} unless defined?( ::Icss::SIMPLE_TYPES )
69
+ ::Icss::FACTORY_TYPES = {} unless defined?( ::Icss::FACTORY_TYPES )
70
+ ::Icss::STRUCTURED_SCHEMAS = {} unless defined?( ::Icss::STRUCTURED_SCHEMAS )
71
+ ::Icss::UNION_SCHEMAS = {} unless defined?( ::Icss::UNION_SCHEMAS )
72
+
73
+ unless defined?(::Icss::AVRO_TYPES)
74
+ ::Icss::AVRO_TYPES = {
75
+ :null => ::NilClass,
76
+ :boolean => ::Boolean,
77
+ :int => ::Integer,
78
+ :long => ::Long,
79
+ :float => ::Float,
80
+ :double => ::Double,
81
+ :string => ::String,
82
+ :bytes => ::Binary,
83
+ }.freeze
189
84
  end
190
-
191
- #
192
- # Avro Schema Declaration
193
- #
194
- # A Schema is represented in JSON by one of:
195
- #
196
- # * A JSON string, naming a defined type.
197
- # * A JSON object, of the form:
198
- # {"type": "typeName" ...attributes...}
199
- # where typeName is either a primitive or derived type name, as defined
200
- # in the Icss::Type class
201
- # * A JSON array, representing a union of embedded types.
202
- #
203
- #
204
- class TypeFactory
205
- def self.receive type_info
206
- # p ['----------', self, 'receive', type_info, Icss::Type::DERIVED_TYPES] if type_info.is_a?(String) && type_info =~ /abstr/
207
- case
208
- when type_info.is_a?(Icss::Type)
209
- type_info
210
- when type_info.is_a?(String) || type_info.is_a?(Symbol)
211
- Icss::Type.find(type_info)
212
- when type_info.is_a?(Array)
213
- UnionType.receive(type_info)
214
- else
215
- type_info = type_info.symbolize_keys
216
- raise "No type was given in #{type_info.inspect}" if type_info[:type].blank?
217
- type_name = type_info[:type].to_sym
218
- type = Icss::Type.find(type_name)
219
- obj = type.receive(type_info)
85
+ ::Icss::SIMPLE_TYPES.merge!(::Icss::AVRO_TYPES)
86
+
87
+ ::Icss::SIMPLE_TYPES.merge!({
88
+ :binary => ::Binary,
89
+ :symbol => ::Symbol,
90
+ :time => ::Time,
91
+ :integer => ::Integer,
92
+ :numeric => ::Numeric,
93
+ :regexp => ::Regexp,
94
+ })
95
+
96
+ ::Icss::STRUCTURED_SCHEMAS.merge!({
97
+ :simple => Icss::Meta::SimpleSchema,
98
+ :record => Icss::Meta::RecordSchema,
99
+ :error => Icss::Meta::ErrorSchema,
100
+ :map => Icss::Meta::HashSchema,
101
+ :hash => Icss::Meta::HashSchema,
102
+ Hash => Icss::Meta::HashSchema,
103
+ :array => Icss::Meta::ArraySchema,
104
+ Array => Icss::Meta::ArraySchema,
105
+ :fixed => Icss::Meta::FixedSchema,
106
+ :enum => Icss::Meta::EnumSchema,
107
+ })
108
+ ::Icss::UNION_SCHEMAS.merge!({
109
+ :union => Icss::Meta::UnionSchema,
110
+ })
111
+
112
+ module Meta
113
+ module Type
114
+ #:nodoc:
115
+ NORMAL_NAMED_CONSTANT_RE = /\A[\w\:\.]+\z/ unless defined?(NORMAL_NAMED_CONSTANT_RE)
116
+
117
+ # Turns a type name into its dotted (avro-style) name, regardless of its
118
+ # current form.
119
+ #
120
+ # @example
121
+ # Icss::Meta::Type.fullname_for(Icss::This::That::TheOther) # 'this.that.the_other'
122
+ # Icss::Meta::Type.fullname_for("Icss::This::That::TheOther") # 'this.that.the_other'
123
+ # Icss::Meta::Type.fullname_for('this.that.the_other') # 'this.that.the_other'
124
+ #
125
+ def self.fullname_for(klass_name)
126
+ return nil unless klass_name.present? && (klass_name.to_s =~ NORMAL_NAMED_CONSTANT_RE)
127
+ klass_name.to_s.gsub(/^:*Icss::/, '').underscore.gsub(%r{/},".")
220
128
  end
221
- end
222
- end
223
129
 
224
- #
225
- # Describes a field in an Avro Record object.
226
- #
227
- # Each field has the following attributes:
228
- # * name: a string providing the name of the field (required), and
229
- # * doc: a string describing this field for users (optional).
230
- # * type: a schema, or a string or symbol naming a record definition (required).
231
- #
232
- # avro type json type ruby type example
233
- # null null NilClass nil
234
- # boolean boolean Boolean true
235
- # int,long integer Integer 1
236
- # float,double number Float 1.1
237
- # bytes string String "\u00FF"
238
- # string string String "foo"
239
- # record object RecordType {"a": 1}
240
- # enum string Enum "FOO"
241
- # array array Array [1]
242
- # map object Hash { "a": 1 }
243
- # fixed string String "\u00ff"
244
- #
245
- # * default: a default value for this field, used when reading instances that
246
- # lack this field (optional). Permitted values depend on the
247
- # field's schema type, according to the table below. Default
248
- # values for union fields correspond to the first schema in the
249
- # union. Default values for bytes and fixed fields are JSON
250
- # strings, where Unicode code points 0-255 are mapped to unsigned
251
- # 8-bit byte values 0-255.
252
- #
253
- # * order: specifies how this field impacts sort ordering of this record
254
- # (optional). Valid values are "ascending" (the default),
255
- # "descending", or "ignore". For more details on how this is used,
256
- # see the the sort order section below.
257
- #
258
- # * required: raises an error if the field is unset when validate! is called
259
- #
260
- # See RecordType for examples.
261
- #
262
- class RecordField
263
- include Receiver
264
- include Receiver::ActsAsHash
265
- rcvr_accessor :name, String, :required => true
266
- rcvr_accessor :doc, String
267
- attr_accessor :type # work around a bug in ruby 1.8, which has defined (and deprecated) type
268
- rcvr_accessor :type, Icss::Type, :required => true
269
- rcvr_accessor :default, Object
270
- rcvr :order, String
271
- rcvr_accessor :required, Boolean
272
-
273
- # is_reference is true if the type is a named reference to a defined type;
274
- # false if the type was defined right here in the schema.
275
- attr_accessor :is_reference
276
- def is_reference?() is_reference ; end
277
-
278
- def receive_type type_info
279
- self.is_reference = type_info.is_a?(String) || type_info.is_a?(Symbol)
280
- self.type = TypeFactory.receive(type_info)
281
- end
282
-
283
- ALLOWED_ORDERS = %w[ascending descending ignore].freeze unless defined?(ALLOWED_ORDERS)
284
- def order
285
- @order || 'ascending'
286
- end
287
- def order_direction
288
- case order when 'ascending' then 1 when 'descending' then -1 else 0 ; end
289
- end
290
- # QUESTION: should this be an override of order=, or of receive_order?
291
- def order= v
292
- raise "'order' may only take the values ascending (the default), descending, or ignore." unless v.nil? || ALLOWED_ORDERS.include?(v)
293
- @order = v
294
- end
295
-
296
- def record?() type.is_a? Icss::RecordType end
297
- def union?() type.is_a? Array end
298
- def enum?() type.is_a? Icss::EnumType end
299
-
300
- def to_hash()
301
- { :name => name,
302
- :type => expand_type,
303
- :default => default,
304
- :order => @order,
305
- :doc => doc,
306
- }.reject{|k,v| v.nil? }
307
- end
308
-
309
- def expand_type
310
- case
311
- when is_reference? && type.respond_to?(:name) then type.name
312
- when is_reference? then '(_unspecified_)'
313
- when type.is_a?(Array) then type.map{|t| t.to_hash }
314
- when type.respond_to?(:to_hash) then type.to_hash
315
- else type.to_s
130
+ # Converts a type name to its ruby (camel-cased) form. Works on class,
131
+ # name of class, or dotted (avro-style) namespace.name. Names will have
132
+ # an 'Icss::' prefix.
133
+ #
134
+ # @example
135
+ # Icss::Meta::Type.fullname_for('this.that.the_other') # "Icss::This::That::TheOther"
136
+ # Icss::Meta::Type.fullname_for(Icss::This::That::TheOther) # "Icss::This::That::TheOther"
137
+ # Icss::Meta::Type.fullname_for("Icss::This::That::TheOther") # "Icss::This::That::TheOther"
138
+ #
139
+ def self.klassname_for(obj)
140
+ return nil unless obj.present? && (obj.to_s =~ NORMAL_NAMED_CONSTANT_RE)
141
+ nm = obj.to_s.gsub(/^:*Icss:+/, '').
142
+ gsub(%r{::},'.').
143
+ split('.').map(&:camelize).join('::')
144
+ "::Icss::#{nm}"
316
145
  end
317
- end
318
- end
319
-
320
- #
321
- # Icss/Avro Record type
322
- #
323
- # Records use the type name "record" and support these attributes:
324
- #
325
- # * name: a string providing the name of the record (required).
326
- # * namespace: a string that qualifies the name;
327
- # * doc: a string providing documentation to the user of this schema (optional).
328
- # * fields: an array of RecordField's (required).
329
- #
330
- # For example, a linked-list of 64-bit values may be defined with:
331
- #
332
- # {
333
- # "type": "record",
334
- # "name": "LongList",
335
- # "fields" : [
336
- # {"name": "value", "type": "long"}, // each element has a long
337
- # {"name": "next", "type": ["LongList", "null"]} // optional next element
338
- # ]
339
- # }
340
- #
341
- class RecordType < NamedType
342
- rcvr_accessor :fields, Array, :of => Icss::RecordField, :required => true
343
- self.type = :record
344
-
345
- after_receive do |hsh|
346
- Icss::Type::DERIVED_TYPES[name.to_sym] = self
347
- end
348
-
349
- def to_hash
350
- super.merge( :fields => (fields||[]).map{|field| field.to_hash} )
351
- end
352
- end
353
-
354
- # An error definition is just like a record definition except it uses "error" instead of "record".
355
- class ErrorType < RecordType
356
- self.type = :error
357
- end
358
-
359
- #
360
- # Describes an Avro Enum type.
361
- #
362
- # Enums use the type name "enum" and support the following attributes:
363
- #
364
- # name: a string providing the name of the enum (required).
365
- # namespace: a string that qualifies the name;
366
- # doc: a string providing documentation to the user of this schema (optional).
367
- # symbols: an array, listing symbols, as strings or ruby symbols (required). All
368
- # symbols in an enum must be unique; duplicates are prohibited.
369
- #
370
- # For example, playing card suits might be defined with:
371
- #
372
- # { "type": "enum",
373
- # "name": "Suit",
374
- # "symbols" : ["SPADES", "HEARTS", "DIAMONDS", "CLUBS"]
375
- # }
376
- #
377
- class EnumType < NamedType
378
- rcvr_accessor :symbols, Array, :of => String, :required => true
379
- self.type = :enum
380
-
381
- def to_hash
382
- super.merge( :symbols => symbols )
383
- end
384
- end
385
-
386
- #
387
- # base class for Avro enumerable types (array, map and union)
388
- #
389
- # (do not confuse with EnumType, which is not an EnumerableType. sigh).
390
- #
391
- class EnumerableType < Type
392
- class_attribute :type
393
- class_attribute :ruby_klass
394
- def to_hash
395
- super.merge( :type => type.to_s )
396
- end
397
- end
398
-
399
- #
400
- # ArrayType describes an Avro Array type.
401
- #
402
- # Arrays use the type name "array" and support a single attribute:
403
- #
404
- # * items: the schema of the array's items.
405
- #
406
- # @example, an array of strings is declared with:
407
- #
408
- # {"type": "array", "items": "string"}
409
- #
410
- class ArrayType < EnumerableType
411
- # FIXME: is items required? The schema doesn't say so.
412
- rcvr_accessor :items, TypeFactory, :required => true
413
- self.type = :array
414
- self.ruby_klass = Array
415
-
416
- def title
417
- "array of #{items.title}"
418
- end
419
-
420
- def to_hash
421
- super.merge( :items => (items && items.name) )
422
- end
423
- end
424
-
425
- #
426
- # MapType describes an Avro Map type (which corresponds to a Ruby
427
- # Hash). HashType is a synonym for MapType.
428
- #
429
- # Maps use the type name "map" and support one attribute:
430
- #
431
- # * values: the schema of the map's values. Avro Map keys are assumed to be strings.
432
- #
433
- # @example, a map from string to long is declared with:
434
- #
435
- # {"type": "map", "values": "long"}
436
- #
437
- class MapType < EnumerableType
438
- # FIXME: is items required? The schema doesn't say so.
439
- rcvr_accessor :values, TypeFactory, :required => true
440
- self.type = :map
441
- self.ruby_klass = Hash
442
146
 
443
- def to_hash
444
- super.merge( :values => values.to_hash )
445
- end
446
- end
447
- HashType = MapType unless defined?(HashType)
448
-
449
- #
450
- # Describes an Avro Union type.
451
- #
452
- # Unions are represented using JSON arrays. For example, ["string", "null"]
453
- # declares a schema which may be either a string or null.
454
- #
455
- # Unions may not contain more than one schema with the same type, except for
456
- # the named types record, fixed and enum. For example, unions containing two
457
- # array types or two map types are not permitted, but two types with different
458
- # names are permitted. (Names permit efficient resolution when reading and
459
- # writing unions.)
460
- #
461
- # Unions may not immediately contain other unions.
462
- #
463
- class UnionType < EnumerableType
464
- attr_accessor :available_types
465
- attr_accessor :referenced_types
466
- self.type = :union
467
-
468
- def receive! type_list
469
- self.available_types = type_list.map do |type_info|
470
- type = TypeFactory.receive(type_info)
471
- (referenced_types||=[]) << type if (type_info.is_a?(String) || type_info.is_a?(Symbol))
472
- type
147
+ def self.schema_for(obj)
148
+ case
149
+ when obj.respond_to?(:to_schema) then obj.to_schema
150
+ when str = fullname_for(obj) then str.to_sym
151
+ else nil ; end
473
152
  end
474
- end
475
153
 
476
- def to_hash
477
- available_types.map{|t| t.name } # (referenced_types||=[]).include?(t) ? t.name : t.to_hash }
478
- end
479
- end
154
+ # true if class is among the defined primitive types:
155
+ # null boolean integer long float double string binary
156
+ # and so forth
157
+ #
158
+ # note this takes no account of inheritance -- only the types specifically
159
+ # listed in Icss::SIMPLE_TYPES are simple
160
+ def self.simple?(tt) ::Icss::SIMPLE_TYPES.has_value?(tt) ; end
480
161
 
481
- #
482
- # Describes an Avro Fixed type.
483
- #
484
- # Fixed uses the type name "fixed" and supports the attributes:
485
- #
486
- # * name: a string naming this fixed (required).
487
- # * namespace, a string that qualifies the name;
488
- # * size: an integer, specifying the number of bytes per value (required).
489
- #
490
- # For example, 16-byte quantity may be declared with:
491
- #
492
- # {"type": "fixed", "size": 16, "name": "md5"}
493
- #
494
- class FixedType < NamedType
495
- rcvr_accessor :size, Integer, :required => true
496
- class_attribute :ruby_klass
497
- self.type = :fixed
498
- self.ruby_klass = String
162
+ def self.union?(tt) false ; end
499
163
 
500
- def to_hash
501
- super.merge( :size => size )
502
- end
503
- end
164
+ def self.record?(tt) false ; end
504
165
 
505
- Type.class_eval do
506
- Icss::Type::NAMED_TYPES = {
507
- :fixed => FixedType,
508
- :enum => EnumType,
509
- :record => RecordType,
510
- :error => ErrorType
511
- }.freeze unless defined?(Icss::Type::NAMED_TYPES)
512
- Icss::Type::ENUMERABLE_TYPES = {
513
- :array => ArrayType,
514
- :map => MapType,
515
- :union => UnionType,
516
- # :request => RequestType,
517
- }.freeze unless defined?(Icss::Type::ENUMERABLE_TYPES)
518
- Icss::Type::VALID_TYPES = (Icss::Type::PRIMITIVE_TYPES.merge(Icss::Type::NAMED_TYPES.merge(Icss::Type::ENUMERABLE_TYPES))).freeze unless defined?(Icss::Type::VALID_TYPES)
519
- Icss::Type::VALID_TYPES.each{|n, t| t.name = n if t.is_a?(Icss::Type) }
166
+ end
520
167
  end
521
168
  end