icss 0.1.3 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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