icss 0.0.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.
- 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
|