icss 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +3 -0
- data/.watchr +20 -0
- data/CHANGELOG.textile +8 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +34 -0
- data/LICENSE.textile +20 -0
- data/README.textile +19 -0
- data/Rakefile +43 -0
- data/VERSION +1 -0
- data/examples/BulkData.avpr +21 -0
- data/examples/complicated.icss.yaml +158 -0
- data/examples/interop.avsc +32 -0
- data/examples/mail.avpr +20 -0
- data/examples/namespace.avpr +28 -0
- data/examples/org/apache/avro/ipc/HandshakeRequest.avsc +11 -0
- data/examples/org/apache/avro/ipc/HandshakeResponse.avsc +15 -0
- data/examples/org/apache/avro/ipc/trace/avroTrace.avdl +64 -0
- data/examples/org/apache/avro/ipc/trace/avroTrace.avpr +82 -0
- data/examples/org/apache/avro/mapred/tether/InputProtocol.avpr +59 -0
- data/examples/org/apache/avro/mapred/tether/OutputProtocol.avpr +75 -0
- data/examples/simple.avpr +70 -0
- data/examples/weather.avsc +9 -0
- data/icss.gemspec +104 -0
- data/icss_specification.textile +370 -0
- data/init.rb +3 -0
- data/lib/icss.rb +19 -0
- data/lib/icss/brevity.rb +136 -0
- data/lib/icss/code_asset.rb +16 -0
- data/lib/icss/core_ext.rb +4 -0
- data/lib/icss/data_asset.rb +22 -0
- data/lib/icss/message.rb +72 -0
- data/lib/icss/old.rb +96 -0
- data/lib/icss/protocol.rb +138 -0
- data/lib/icss/protocol_set.rb +48 -0
- data/lib/icss/sample_message_call.rb +140 -0
- data/lib/icss/target.rb +71 -0
- data/lib/icss/type.rb +517 -0
- data/lib/icss/type/factory.rb +196 -0
- data/lib/icss/validations.rb +16 -0
- data/lib/icss/view_helper.rb +28 -0
- data/spec/icss_spec.rb +7 -0
- data/spec/spec_helper.rb +31 -0
- metadata +218 -0
data/lib/icss/target.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module Icss
|
2
|
+
|
3
|
+
#
|
4
|
+
# Instantiates an array of target objects
|
5
|
+
#
|
6
|
+
class TargetListFactory
|
7
|
+
def self.receive target_name, target_info_list
|
8
|
+
klass = ("Icss::"+target_name.camelize+"Target").constantize
|
9
|
+
target_info_list.map{|target_info| klass.receive(target_info)}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Target
|
14
|
+
include Receiver
|
15
|
+
include Receiver::ActsAsHash
|
16
|
+
#
|
17
|
+
# Name should not be something like 'default', it should be something
|
18
|
+
# that 'appeals' to the message name.
|
19
|
+
#
|
20
|
+
rcvr_accessor :name, String
|
21
|
+
end
|
22
|
+
|
23
|
+
class MysqlTarget < Target
|
24
|
+
rcvr_accessor :data_assets, Array, :of => String
|
25
|
+
rcvr_accessor :database, String
|
26
|
+
rcvr_accessor :table_name, String
|
27
|
+
end
|
28
|
+
|
29
|
+
class ApeyeyeTarget < Target
|
30
|
+
rcvr_accessor :code_assets, Array, :of => String
|
31
|
+
end
|
32
|
+
|
33
|
+
class HbaseTarget < Target
|
34
|
+
rcvr_accessor :data_assets, Array, :of => String
|
35
|
+
rcvr_accessor :table_name, String
|
36
|
+
rcvr_accessor :column_families, Array, :of => String
|
37
|
+
rcvr_accessor :column_family, String
|
38
|
+
rcvr_accessor :loader, String
|
39
|
+
rcvr_accessor :id_field, String
|
40
|
+
end
|
41
|
+
|
42
|
+
class ElasticSearchTarget < Target
|
43
|
+
rcvr_accessor :data_assets, Array, :of => String
|
44
|
+
rcvr_accessor :index_name, String
|
45
|
+
rcvr_accessor :id_field, String
|
46
|
+
rcvr_accessor :object_type, String
|
47
|
+
rcvr_accessor :loader, String
|
48
|
+
end
|
49
|
+
|
50
|
+
class GeoIndexTarget < Target
|
51
|
+
rcvr_accessor :data_assets, Array, :of => String
|
52
|
+
rcvr_accessor :table_name, String
|
53
|
+
rcvr_accessor :min_zoom, Integer
|
54
|
+
rcvr_accessor :max_zoom, Integer
|
55
|
+
rcvr_accessor :chars_per_page, Integer
|
56
|
+
rcvr_accessor :sort_field, String
|
57
|
+
end
|
58
|
+
|
59
|
+
class CatalogTarget < Target
|
60
|
+
rcvr_accessor :name, String
|
61
|
+
rcvr_accessor :license, String
|
62
|
+
rcvr_accessor :title, String
|
63
|
+
rcvr_accessor :link, String
|
64
|
+
rcvr_accessor :description, String
|
65
|
+
rcvr_accessor :owner, String
|
66
|
+
rcvr_accessor :price, Float
|
67
|
+
rcvr_accessor :tags, Array, :of => String
|
68
|
+
rcvr_accessor :messages, Array, :of => String
|
69
|
+
rcvr_accessor :packages, Array, :of => Hash
|
70
|
+
end
|
71
|
+
end
|
data/lib/icss/type.rb
ADDED
@@ -0,0 +1,517 @@
|
|
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(/(.*)\./, "")
|
39
|
+
end
|
40
|
+
VALID_TYPES[type_name.to_sym] || 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
|
+
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
|
+
end
|
70
|
+
|
71
|
+
# ---------------------------------------------------------------------------
|
72
|
+
#
|
73
|
+
# Primitive Types
|
74
|
+
#
|
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')
|
96
|
+
#
|
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
|
101
|
+
end
|
102
|
+
|
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
|
189
|
+
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]
|
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)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
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
|
316
|
+
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
|
+
def to_hash
|
346
|
+
super.merge( :fields => (fields||[]).map{|field| field.to_hash} )
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
# An error definition is just like a record definition except it uses "error" instead of "record".
|
351
|
+
class ErrorType < RecordType
|
352
|
+
self.type = :error
|
353
|
+
end
|
354
|
+
|
355
|
+
#
|
356
|
+
# Describes an Avro Enum type.
|
357
|
+
#
|
358
|
+
# Enums use the type name "enum" and support the following attributes:
|
359
|
+
#
|
360
|
+
# name: a string providing the name of the enum (required).
|
361
|
+
# namespace: a string that qualifies the name;
|
362
|
+
# doc: a string providing documentation to the user of this schema (optional).
|
363
|
+
# symbols: an array, listing symbols, as strings or ruby symbols (required). All
|
364
|
+
# symbols in an enum must be unique; duplicates are prohibited.
|
365
|
+
#
|
366
|
+
# For example, playing card suits might be defined with:
|
367
|
+
#
|
368
|
+
# { "type": "enum",
|
369
|
+
# "name": "Suit",
|
370
|
+
# "symbols" : ["SPADES", "HEARTS", "DIAMONDS", "CLUBS"]
|
371
|
+
# }
|
372
|
+
#
|
373
|
+
class EnumType < NamedType
|
374
|
+
rcvr_accessor :symbols, Array, :of => String, :required => true
|
375
|
+
self.type = :enum
|
376
|
+
|
377
|
+
def to_hash
|
378
|
+
super.merge( :symbols => symbols )
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
#
|
383
|
+
# base class for Avro enumerable types (array, map and union)
|
384
|
+
#
|
385
|
+
# (do not confuse with EnumType, which is not an EnumerableType. sigh).
|
386
|
+
#
|
387
|
+
class EnumerableType < Type
|
388
|
+
class_attribute :type
|
389
|
+
class_attribute :ruby_klass
|
390
|
+
def to_hash
|
391
|
+
super.merge( :type => type.to_s )
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
#
|
396
|
+
# ArrayType describes an Avro Array type.
|
397
|
+
#
|
398
|
+
# Arrays use the type name "array" and support a single attribute:
|
399
|
+
#
|
400
|
+
# * items: the schema of the array's items.
|
401
|
+
#
|
402
|
+
# @example, an array of strings is declared with:
|
403
|
+
#
|
404
|
+
# {"type": "array", "items": "string"}
|
405
|
+
#
|
406
|
+
class ArrayType < EnumerableType
|
407
|
+
# FIXME: is items required? The schema doesn't say so.
|
408
|
+
rcvr_accessor :items, TypeFactory, :required => true
|
409
|
+
self.type = :array
|
410
|
+
self.ruby_klass = Array
|
411
|
+
|
412
|
+
def title
|
413
|
+
"array of #{items.title}"
|
414
|
+
end
|
415
|
+
|
416
|
+
def to_hash
|
417
|
+
super.merge( :items => (items && items.name) )
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
#
|
422
|
+
# MapType describes an Avro Map type (which corresponds to a Ruby
|
423
|
+
# Hash). HashType is a synonym for MapType.
|
424
|
+
#
|
425
|
+
# Maps use the type name "map" and support one attribute:
|
426
|
+
#
|
427
|
+
# * values: the schema of the map's values. Avro Map keys are assumed to be strings.
|
428
|
+
#
|
429
|
+
# @example, a map from string to long is declared with:
|
430
|
+
#
|
431
|
+
# {"type": "map", "values": "long"}
|
432
|
+
#
|
433
|
+
class MapType < EnumerableType
|
434
|
+
# FIXME: is items required? The schema doesn't say so.
|
435
|
+
rcvr_accessor :values, TypeFactory, :required => true
|
436
|
+
self.type = :map
|
437
|
+
self.ruby_klass = Hash
|
438
|
+
|
439
|
+
def to_hash
|
440
|
+
super.merge( :values => values.to_hash )
|
441
|
+
end
|
442
|
+
end
|
443
|
+
HashType = MapType unless defined?(HashType)
|
444
|
+
|
445
|
+
#
|
446
|
+
# Describes an Avro Union type.
|
447
|
+
#
|
448
|
+
# Unions are represented using JSON arrays. For example, ["string", "null"]
|
449
|
+
# declares a schema which may be either a string or null.
|
450
|
+
#
|
451
|
+
# Unions may not contain more than one schema with the same type, except for
|
452
|
+
# the named types record, fixed and enum. For example, unions containing two
|
453
|
+
# array types or two map types are not permitted, but two types with different
|
454
|
+
# names are permitted. (Names permit efficient resolution when reading and
|
455
|
+
# writing unions.)
|
456
|
+
#
|
457
|
+
# Unions may not immediately contain other unions.
|
458
|
+
#
|
459
|
+
class UnionType < EnumerableType
|
460
|
+
attr_accessor :available_types
|
461
|
+
attr_accessor :referenced_types
|
462
|
+
self.type = :union
|
463
|
+
|
464
|
+
def receive! type_list
|
465
|
+
self.available_types = type_list.map do |type_info|
|
466
|
+
type = TypeFactory.receive(type_info)
|
467
|
+
(referenced_types||=[]) << type if (type_info.is_a?(String) || type_info.is_a?(Symbol))
|
468
|
+
type
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
def to_hash
|
473
|
+
available_types.map{|t| t.name } # (referenced_types||=[]).include?(t) ? t.name : t.to_hash }
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
#
|
478
|
+
# Describes an Avro Fixed type.
|
479
|
+
#
|
480
|
+
# Fixed uses the type name "fixed" and supports the attributes:
|
481
|
+
#
|
482
|
+
# * name: a string naming this fixed (required).
|
483
|
+
# * namespace, a string that qualifies the name;
|
484
|
+
# * size: an integer, specifying the number of bytes per value (required).
|
485
|
+
#
|
486
|
+
# For example, 16-byte quantity may be declared with:
|
487
|
+
#
|
488
|
+
# {"type": "fixed", "size": 16, "name": "md5"}
|
489
|
+
#
|
490
|
+
class FixedType < NamedType
|
491
|
+
rcvr_accessor :size, Integer, :required => true
|
492
|
+
class_attribute :ruby_klass
|
493
|
+
self.type = :fixed
|
494
|
+
self.ruby_klass = String
|
495
|
+
|
496
|
+
def to_hash
|
497
|
+
super.merge( :size => size )
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
Type.class_eval do
|
502
|
+
Icss::Type::NAMED_TYPES = {
|
503
|
+
:fixed => FixedType,
|
504
|
+
:enum => EnumType,
|
505
|
+
:record => RecordType,
|
506
|
+
:error => ErrorType
|
507
|
+
}.freeze unless defined?(Icss::Type::NAMED_TYPES)
|
508
|
+
Icss::Type::ENUMERABLE_TYPES = {
|
509
|
+
:array => ArrayType,
|
510
|
+
:map => MapType,
|
511
|
+
:union => UnionType,
|
512
|
+
# :request => RequestType,
|
513
|
+
}.freeze unless defined?(Icss::Type::ENUMERABLE_TYPES)
|
514
|
+
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)
|
515
|
+
Icss::Type::VALID_TYPES.each{|n, t| t.name = n if t.is_a?(Icss::Type) }
|
516
|
+
end
|
517
|
+
end
|