icss 0.1.3 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.watchr +35 -3
- data/CHANGELOG.md +38 -0
- data/Gemfile +19 -14
- data/README.md +296 -0
- data/Rakefile +2 -6
- data/TODO.md +13 -0
- data/VERSION +1 -1
- data/examples/avro_examples/complicated.icss.yaml +14 -13
- data/examples/bnc.icss.yaml +70 -0
- data/examples/chronic.icss.yaml +3 -3
- data/examples/license.icss.yaml +7 -0
- data/examples/source1.icss.yaml +4 -0
- data/examples/source2.icss.yaml +4 -0
- data/examples/test_icss.yaml +67 -0
- data/icss.gemspec +103 -43
- data/lib/icss.rb +37 -15
- data/lib/icss/core_types.rb +19 -0
- data/lib/icss/error.rb +4 -0
- data/{init.rb → lib/icss/init.rb} +0 -0
- data/lib/icss/message.rb +124 -66
- data/lib/icss/message/message_sample.rb +144 -0
- data/lib/icss/protocol.rb +184 -131
- data/lib/icss/protocol/code_asset.rb +18 -0
- data/lib/icss/protocol/data_asset.rb +23 -0
- data/lib/icss/protocol/license.rb +41 -0
- data/lib/icss/protocol/source.rb +37 -0
- data/lib/icss/protocol/target.rb +68 -0
- data/lib/icss/receiver_model.rb +24 -0
- data/lib/icss/receiver_model/active_model_shim.rb +36 -0
- data/lib/icss/receiver_model/acts_as_catalog.rb +170 -0
- data/lib/icss/receiver_model/acts_as_hash.rb +177 -0
- data/lib/icss/receiver_model/acts_as_loadable.rb +47 -0
- data/lib/icss/receiver_model/acts_as_tuple.rb +100 -0
- data/lib/icss/receiver_model/locale/en.yml +27 -0
- data/lib/icss/receiver_model/to_geo_json.rb +19 -0
- data/lib/icss/receiver_model/tree_merge.rb +34 -0
- data/lib/icss/receiver_model/validations.rb +31 -0
- data/lib/icss/serialization.rb +51 -0
- data/lib/icss/serialization/zaml.rb +443 -0
- data/lib/icss/type.rb +148 -501
- data/lib/icss/type/base_type.rb +0 -0
- data/lib/icss/type/named_type.rb +184 -0
- data/lib/icss/type/record_field.rb +77 -0
- data/lib/icss/type/record_model.rb +49 -0
- data/lib/icss/type/record_schema.rb +54 -0
- data/lib/icss/type/record_type.rb +325 -0
- data/lib/icss/type/simple_types.rb +72 -0
- data/lib/icss/type/structured_schema.rb +288 -0
- data/lib/icss/type/type_factory.rb +144 -0
- data/lib/icss/type/union_schema.rb +41 -0
- data/lib/icss/view_helper.rb +56 -19
- data/notes/named_array.md +32 -0
- data/notes/on_include_vs_extend_etc.rb +176 -0
- data/notes/technical_details.md +278 -0
- data/spec/core_types_spec.rb +119 -0
- data/spec/fixtures/zaml_complex_hash.yaml +35 -0
- data/spec/icss_spec.rb +86 -23
- data/spec/message/message_sample_spec.rb +4 -0
- data/spec/message_spec.rb +139 -0
- data/spec/protocol/license_spec.rb +67 -0
- data/spec/protocol/protocol_catalog_spec.rb +48 -0
- data/spec/protocol/protocol_validations_spec.rb +176 -0
- data/spec/protocol/source_spec.rb +65 -0
- data/spec/protocol_spec.rb +91 -37
- data/spec/receiver_model_spec.rb +111 -0
- data/spec/serialization/zaml_spec.rb +81 -0
- data/spec/serialization/zaml_test.rb +473 -0
- data/spec/serialization_spec.rb +63 -0
- data/spec/spec_helper.rb +24 -7
- data/spec/support/icss_test_helper.rb +67 -0
- data/spec/support/load_example_protocols.rb +17 -0
- data/spec/type/base_type_spec.rb +0 -0
- data/spec/type/named_type_spec.rb +75 -0
- data/spec/type/record_field_spec.rb +44 -0
- data/spec/type/record_model_spec.rb +206 -0
- data/spec/type/record_schema_spec.rb +161 -0
- data/spec/type/record_type_spec.rb +155 -0
- data/spec/type/simple_types_spec.rb +121 -0
- data/spec/type/structured_schema_spec.rb +300 -0
- data/spec/type/type_catalog_spec.rb +44 -0
- data/spec/type/type_factory_spec.rb +93 -0
- data/spec/type/union_schema_spec.rb +0 -0
- data/spec/type_spec.rb +63 -0
- metadata +205 -144
- data/CHANGELOG.textile +0 -9
- data/Gemfile.lock +0 -40
- data/README.textile +0 -29
- data/lib/icss/brevity.rb +0 -136
- data/lib/icss/code_asset.rb +0 -16
- data/lib/icss/core_ext.rb +0 -9
- data/lib/icss/data_asset.rb +0 -22
- data/lib/icss/old.rb +0 -96
- data/lib/icss/protocol_set.rb +0 -48
- data/lib/icss/sample_message_call.rb +0 -142
- data/lib/icss/target.rb +0 -72
- data/lib/icss/type/factory.rb +0 -196
- data/lib/icss/validations.rb +0 -16
- data/spec/validations_spec.rb +0 -171
@@ -0,0 +1,19 @@
|
|
1
|
+
# p [__FILE__, "core types from ", Settings.catalog_root]
|
2
|
+
|
3
|
+
module Icss
|
4
|
+
module Encyclopedic ; module Wikipedia ; module Dbpedia ; end ; end ; end
|
5
|
+
module Web ; module An ; end ; end
|
6
|
+
module Engineering ; module Chemical; module Msds ; end ; end ; end
|
7
|
+
module Government ; module Public ; module Acs ; end ; end ; end
|
8
|
+
module Language ; module Corpora ; module WordFreq ; end ; end ; end
|
9
|
+
module Sports ; module Stats ; module Baseball ; end ; module Vargatron ; end ; end ; end
|
10
|
+
module Social ; module Network ; module Tw ; end ; module Qwerly ; end ; end ; end
|
11
|
+
module Soc ; module Net ; module Tw ; end ; end ; end
|
12
|
+
module Meta ; module Req ; class Geolocator ; end ; end ; end
|
13
|
+
module St ; class Url < String ; end ; end
|
14
|
+
module Geo ; module Location ; end ; end
|
15
|
+
end
|
16
|
+
|
17
|
+
unless defined?(Icss::Geo::Place)
|
18
|
+
warn "Could not load core type 'place'. Make sure the catalog (#{Settings.catalog_root}) is where you expect it to be."
|
19
|
+
end
|
data/lib/icss/error.rb
ADDED
File without changes
|
data/lib/icss/message.rb
CHANGED
@@ -1,75 +1,133 @@
|
|
1
1
|
module Icss
|
2
|
+
module Meta
|
3
|
+
#
|
4
|
+
# Describes an Avro Message
|
5
|
+
#
|
6
|
+
# A message has attributes:
|
7
|
+
#
|
8
|
+
# * doc: an optional description of the message,
|
9
|
+
# * request: a list of named, typed parameter schemas (this has the same form as the fields of a record declaration);
|
10
|
+
# * response: a valid schema for the response
|
11
|
+
# * errors: an optional union of error schemas.
|
12
|
+
#
|
13
|
+
# A request parameter list is processed equivalently to an anonymous
|
14
|
+
# record. Since record field lists may vary between reader and writer, request
|
15
|
+
# parameters may also differ between the caller and responder, and such
|
16
|
+
# differences are resolved in the same manner as record field differences.
|
17
|
+
#
|
18
|
+
class Message
|
19
|
+
include ::Icss::ReceiverModel
|
2
20
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
#
|
8
|
-
# * doc: an optional description of the message,
|
9
|
-
# * request: a list of named, typed parameter schemas (this has the same form as the fields of a record declaration);
|
10
|
-
# * response: a valid schema for the response
|
11
|
-
# * errors: an optional union of error schemas.
|
12
|
-
#
|
13
|
-
# A request parameter list is processed equivalently to an anonymous
|
14
|
-
# record. Since record field lists may vary between reader and writer, request
|
15
|
-
# parameters may also differ between the caller and responder, and such
|
16
|
-
# differences are resolved in the same manner as record field differences.
|
17
|
-
#
|
18
|
-
class Message
|
19
|
-
include Receiver
|
20
|
-
include Receiver::ActsAsHash
|
21
|
-
rcvr_accessor :name, String
|
22
|
-
rcvr_accessor :doc, String
|
23
|
-
|
24
|
-
#we're starting to attach a lot of pork to this lib...
|
25
|
-
rcvr_accessor :initial_free_qty, Integer
|
26
|
-
rcvr_accessor :price_per_k_in_cents, Integer
|
27
|
-
|
28
|
-
rcvr_accessor :request, Array, :of => Icss::RecordField, :default => []
|
29
|
-
rcvr_accessor :response, Icss::TypeFactory
|
30
|
-
rcvr_accessor :errors, Icss::UnionType, :default => []
|
31
|
-
attr_accessor :protocol
|
32
|
-
# this is defined in sample_message_call.rb -- since we don't do referenced types yet
|
33
|
-
# rcvr_accessor :samples, Array, :of => Icss::SampleMessageCall, :default => []
|
34
|
-
|
35
|
-
after_receive do |hsh|
|
36
|
-
# track recursion of type references
|
37
|
-
@response_is_reference = true if hsh['response'].is_a?(String) || hsh['response'].is_a?(Symbol)
|
38
|
-
# tie each sample back to this, its parent message
|
39
|
-
(self.samples ||= []).each{|sample| sample.message = self }
|
40
|
-
end
|
21
|
+
field :name, String
|
22
|
+
alias_method :basename, :name
|
23
|
+
alias_method :basename=, :name=
|
24
|
+
field :doc, String
|
41
25
|
|
42
|
-
|
43
|
-
File.join(protocol.path, name)
|
44
|
-
end
|
26
|
+
field :request_decorators, Hash, :default => {:anchors => []}
|
45
27
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
28
|
+
field :request, Array, :items => Icss::Meta::RecordField, :default => []
|
29
|
+
field :response, Icss::Meta::TypeFactory
|
30
|
+
field :errors, Object # FIXME: Icss::Meta::UnionType, :default => []
|
31
|
+
# this is defined in sample_message_call.rb -- since we don't do referenced types yet
|
50
32
|
|
51
|
-
|
52
|
-
# Conversion
|
53
|
-
#
|
54
|
-
def to_hash()
|
55
|
-
{
|
56
|
-
:doc => doc,
|
57
|
-
:initial_free_qty => initial_free_qty,
|
58
|
-
:price_per_k_in_cents => price_per_k_in_cents,
|
59
|
-
:request => summary_of_request_attr,
|
60
|
-
:response => summary_of_response_attr,
|
61
|
-
:samples => samples.map(&:to_hash).map(&:compact_blank),
|
62
|
-
:errors => (errors.blank? ? nil : errors),
|
63
|
-
}.reject{|k,v| v.nil? }
|
64
|
-
end
|
65
|
-
def to_json(*args) to_hash.to_json(*args) ; end
|
33
|
+
attr_accessor :protocol
|
66
34
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
35
|
+
#we're starting to attach a lot of pork to this lib...
|
36
|
+
field :initial_free_qty, Integer
|
37
|
+
field :price_per_k_in_cents, Integer
|
38
|
+
|
39
|
+
after_receive(:are_my_types_references) do |hsh|
|
40
|
+
# track recursion of type references
|
41
|
+
@response_referenceness = ! hsh[:response].respond_to?(:each_pair)
|
42
|
+
end
|
43
|
+
|
44
|
+
after_receive(:parent_my_samples) do |hsh|
|
45
|
+
# # tie each sample back to this, its parent message
|
46
|
+
(self.samples||=[]).each{|samp| samp.message = self }
|
47
|
+
end
|
48
|
+
|
49
|
+
def fullname
|
50
|
+
"#{protocol.fullname}.#{basename}"
|
51
|
+
end
|
52
|
+
def path
|
53
|
+
fullname.gsub(%r{\.},'/')
|
54
|
+
end
|
55
|
+
|
56
|
+
# the type of the message's params (by convention, its first request field)
|
57
|
+
def params_type
|
58
|
+
request.first ? request.first.type : {}
|
59
|
+
end
|
60
|
+
|
61
|
+
def first_sample_request_param
|
62
|
+
req = samples.first.request.first rescue nil
|
63
|
+
req || {}
|
64
|
+
end
|
65
|
+
|
66
|
+
# ----------------------------------------
|
67
|
+
# GEO
|
68
|
+
#
|
69
|
+
|
70
|
+
def is_a_geo?
|
71
|
+
geolocators.present?
|
72
|
+
end
|
73
|
+
|
74
|
+
rcvr_alias(:is_geo, :is_geo)
|
75
|
+
def receive_is_geo(val)
|
76
|
+
return unless val
|
77
|
+
unless defined?(Icss::Meta::Req::Geolocator) then
|
78
|
+
warn "View helpers can\'t help with geolocators: Icss::Meta::Req::Geolocator type is missing. Is the catalog loaded properly?"
|
79
|
+
return
|
80
|
+
end
|
81
|
+
self.request_decorators = {
|
82
|
+
:anchors => [
|
83
|
+
Icss::Meta::Req::PointWithRadiusGeolocator,
|
84
|
+
Icss::Meta::Req::AddressTextGeolocator,
|
85
|
+
Icss::Meta::Req::TileXYZoomGeolocator,
|
86
|
+
Icss::Meta::Req::BoundingBoxGeolocator,
|
87
|
+
Icss::Meta::Req::IpAddressGeolocator
|
88
|
+
],
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
def geolocators
|
93
|
+
request_decorators[:anchors]
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# Conversion
|
98
|
+
#
|
99
|
+
def to_hash()
|
100
|
+
{
|
101
|
+
:request => summary_of_request_attr,
|
102
|
+
:response => summary_of_response_attr,
|
103
|
+
:doc => doc,
|
104
|
+
:errors => (errors.blank? ? nil : errors),
|
105
|
+
:samples => samples.map(&:to_hash).map(&:compact_blank),
|
106
|
+
:initial_free_qty => initial_free_qty,
|
107
|
+
:price_per_k_in_cents => price_per_k_in_cents,
|
108
|
+
}.compact
|
109
|
+
end
|
110
|
+
def to_json(*args) to_hash.to_json(*args) ; end
|
111
|
+
|
112
|
+
private
|
113
|
+
def summary_of_response_attr
|
114
|
+
case
|
115
|
+
when response.blank? then response
|
116
|
+
when @response_referenceness then response.fullname
|
117
|
+
else response.to_schema.compact_blank
|
118
|
+
end
|
119
|
+
end
|
120
|
+
def summary_of_request_attr
|
121
|
+
request.map do |req|
|
122
|
+
case
|
123
|
+
when req.blank? then req
|
124
|
+
# Is there a case where this needs to be a string and not a hash?
|
125
|
+
# when req.is_reference? then req.type.fullname
|
126
|
+
else req.to_schema.compact_blank
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
73
130
|
end
|
74
131
|
end
|
132
|
+
|
75
133
|
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module Icss
|
2
|
+
module Meta
|
3
|
+
#
|
4
|
+
# Holds a sample call for a message and its expected response
|
5
|
+
#
|
6
|
+
# You may define the request parameters using an array of parameters
|
7
|
+
# or with the corresponding URL it would render to.
|
8
|
+
#
|
9
|
+
# This file also decorates Icss::Meta::Message and Icss::Meta::Protocol with helper methods for sample calls.
|
10
|
+
#
|
11
|
+
class MessageSample
|
12
|
+
include ReceiverModel
|
13
|
+
field :name, String
|
14
|
+
field :doc, String
|
15
|
+
field :request, Array, :default => []
|
16
|
+
field :response_hsh, Hash # a hash suitable for populating the message's @response@ type
|
17
|
+
field :response, Hash
|
18
|
+
attr_accessor :raw_response # the raw http response from fetching
|
19
|
+
field :error, String
|
20
|
+
field :url, String
|
21
|
+
attr_accessor :message
|
22
|
+
|
23
|
+
# Whips up the class implied by the ICSS type of this message's response,
|
24
|
+
# and populates it using the response hash.
|
25
|
+
def response_obj
|
26
|
+
return if response.blank?
|
27
|
+
message.response.receive(response)
|
28
|
+
end
|
29
|
+
|
30
|
+
# The URL implied by the given hostname and the sample request parameters.
|
31
|
+
#
|
32
|
+
# @param [String] hostname The hostname or hostname:port to include in the URL
|
33
|
+
# @param [Hash] extra_query_params A hash of extra params to in
|
34
|
+
#
|
35
|
+
# The URI expects string values in the hash used to build the query -- if
|
36
|
+
# calling #to_s on a field won't do what you want, clobber the value beforehand.
|
37
|
+
#
|
38
|
+
def full_url hostname, extra_query_params={}
|
39
|
+
host, port = hostname.split(':', 2)
|
40
|
+
u = Addressable::URI.new(:host => host, :port => port, :path => self.path, :scheme => 'http')
|
41
|
+
u.query_values = query_hash(extra_query_params)
|
42
|
+
u
|
43
|
+
end
|
44
|
+
|
45
|
+
def query_hash extra_query_params={}
|
46
|
+
hsh = (@url.present? ? @url.query_values : request.first.to_hash) rescue {}
|
47
|
+
hsh = hsh.merge extra_query_params
|
48
|
+
hsh.each{|k,v| hsh[k] = v.to_s }
|
49
|
+
hsh
|
50
|
+
end
|
51
|
+
|
52
|
+
def path
|
53
|
+
((@url && @url.path).present? ? @url.path : "/#{message.path}" )
|
54
|
+
end
|
55
|
+
|
56
|
+
# @param [String, Addressable::URI]
|
57
|
+
# the URL can be fully-qualified (htttp://api.infochimps.com/this/that?the=other) or relative (this/that?the=other)
|
58
|
+
# and the path must match that of the message.
|
59
|
+
#
|
60
|
+
def url= new_url
|
61
|
+
if new_url.is_a?(String)
|
62
|
+
unless new_url.include?('?') then warn "sample request url should have a '?' introducing its query parameters: {#{new_url}}" ; end
|
63
|
+
new_url = Addressable::URI.parse(new_url)
|
64
|
+
end
|
65
|
+
@url = new_url
|
66
|
+
end
|
67
|
+
|
68
|
+
# retrieve the response from the given host, storing it in response. this
|
69
|
+
# catches all server errors and constructs a dummy response hash if the call
|
70
|
+
# fails.
|
71
|
+
def fetch_response! hostname="", extra_query_params={}
|
72
|
+
self.raw_response = fetch_raw_response( full_url(hostname, extra_query_params) )
|
73
|
+
begin
|
74
|
+
resp_hsh = JSON.load(raw_response.body)
|
75
|
+
rescue StandardError => e
|
76
|
+
warn [" error parsing response: #{e}"].join("\n")
|
77
|
+
self.response = nil
|
78
|
+
self.error = "JsonParseError"
|
79
|
+
return
|
80
|
+
end
|
81
|
+
if raw_response.code == 200
|
82
|
+
self.response = resp_hsh
|
83
|
+
self.error = nil
|
84
|
+
else
|
85
|
+
self.response = nil
|
86
|
+
self.error = resp_hsh["error"]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
protected
|
91
|
+
|
92
|
+
def fetch_raw_response full_url
|
93
|
+
RestClient.get(full_url.to_s) do |response, request, result|
|
94
|
+
response
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
class Icss::Meta::Message
|
103
|
+
field :samples, Array, :items => Icss::Meta::MessageSample, :default => []
|
104
|
+
end
|
105
|
+
|
106
|
+
class Icss::Meta::Protocol
|
107
|
+
|
108
|
+
#
|
109
|
+
# a hash for dumping to file:
|
110
|
+
# @example: from the whole thing, would dump only this:
|
111
|
+
#
|
112
|
+
# namespace: util.time
|
113
|
+
# protocol: chronic
|
114
|
+
# messages:
|
115
|
+
# parse:
|
116
|
+
# samples:
|
117
|
+
# - url: "?now=5%3A06%3A07%202010-08-08&time_str=Yesterday"
|
118
|
+
# response: { "time": "2010-08-07 05:06:07 UTC", "epoch_seconds": 1281225967 }
|
119
|
+
#
|
120
|
+
def message_samples_hash
|
121
|
+
hsh = { :namespace => namespace, :protocol => protocol, :messages => {} }
|
122
|
+
messages.each do |msg_name, msg|
|
123
|
+
hsh[:messages][msg_name] = { :samples => [] }
|
124
|
+
msg.samples.each do |sample_req|
|
125
|
+
sample_hsh = {
|
126
|
+
:name => sample_req.name,
|
127
|
+
:doc => sample_req.doc,
|
128
|
+
}
|
129
|
+
if sample_req.response.present?
|
130
|
+
then sample_hsh[:response] = sample_req.response
|
131
|
+
else sample_hsh[:error] = sample_req.error
|
132
|
+
end
|
133
|
+
if sample_req.url.present?
|
134
|
+
then sample_hsh[:url] = sample_req.url.to_s
|
135
|
+
else sample_hsh[:request] = sample_req.request
|
136
|
+
end
|
137
|
+
sample_hsh.compact_blank!
|
138
|
+
hsh[:messages][msg_name][:samples] << sample_hsh
|
139
|
+
end
|
140
|
+
end
|
141
|
+
return hsh
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
data/lib/icss/protocol.rb
CHANGED
@@ -1,146 +1,199 @@
|
|
1
1
|
module Icss
|
2
|
+
module Meta
|
3
|
+
|
4
|
+
# predefine so we can use below
|
5
|
+
|
6
|
+
class Message ; end
|
2
7
|
|
3
|
-
#
|
4
|
-
# Describes an Avro Protocol Declaration
|
5
|
-
#
|
6
|
-
# Avro protocols describe RPC interfaces. The Protocol class will receive an
|
7
|
-
# Avro JSON
|
8
|
-
#
|
9
|
-
# A Protocol has the following attributes:
|
10
|
-
#
|
11
|
-
# * protocol, a string, the name of the protocol (required). +name+ is
|
12
|
-
# provided as an alias for +protocol+.
|
13
|
-
#
|
14
|
-
# * namespace, a string that qualifies the name (optional).
|
15
|
-
#
|
16
|
-
# * doc, a string describing this protocol (optional).
|
17
|
-
#
|
18
|
-
# * types, an optional list of definitions of named types (records, enums,
|
19
|
-
# fixed and errors). An error definition is just like a record definition
|
20
|
-
# except it uses "error" instead of "record". Note that forward references
|
21
|
-
# to named types are not permitted.
|
22
|
-
#
|
23
|
-
# * messages, an optional JSON object whose keys are message names and whose
|
24
|
-
# values are objects whose attributes are described below. No two messages
|
25
|
-
# may have the same name.
|
26
|
-
#
|
27
|
-
# The name and namespace qualification rules defined for schema objects apply
|
28
|
-
# to protocols as well: see the documentation for Icss::Type.
|
29
|
-
#
|
30
|
-
# For example, one may define a simple HelloWorld protocol with:
|
31
|
-
#
|
32
|
-
# {
|
33
|
-
# "namespace": "com.acme",
|
34
|
-
# "protocol": "HelloWorld",
|
35
|
-
# "doc": "Protocol Greetings",
|
36
|
-
# "types": [
|
37
|
-
# { "name": "Greeting",
|
38
|
-
# "type": "record",
|
39
|
-
# "fields": [ {"name": "message", "type": "string"} ]},
|
40
|
-
# { "name": "Curse",
|
41
|
-
# "type": "error",
|
42
|
-
# "fields": [ {"name": "message", "type": "string"} ]}
|
43
|
-
# ],
|
44
|
-
# "messages": {
|
45
|
-
# "hello": {
|
46
|
-
# "doc": "Say hello.",
|
47
|
-
# "request": [{"name": "greeting", "type": "Greeting" }],
|
48
|
-
# "response": "Greeting",
|
49
|
-
# "errors": ["Curse"]
|
50
|
-
# }
|
51
|
-
# }
|
52
|
-
# }
|
53
|
-
#
|
54
|
-
class Protocol
|
55
|
-
include Receiver
|
56
|
-
include Receiver::ActsAsHash
|
57
|
-
include Receiver::ActsAsLoadable
|
58
|
-
include Gorillib::Hashlike::TreeMerge
|
59
|
-
include Receiver::ActiveModelShim
|
60
|
-
|
61
|
-
rcvr_accessor :protocol, String, :required => true
|
62
|
-
alias_method :name, :protocol
|
63
|
-
rcvr_accessor :namespace, String
|
64
|
-
rcvr_accessor :doc, String
|
65
8
|
#
|
66
|
-
|
67
|
-
|
68
|
-
#
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
9
|
+
# Describes an Avro Protocol Declaration
|
10
|
+
#
|
11
|
+
# Avro protocols describe RPC interfaces. The Protocol class will receive an
|
12
|
+
# Avro JSON
|
13
|
+
#
|
14
|
+
# A Protocol has the following attributes:
|
15
|
+
#
|
16
|
+
# * protocol, a string, the name of the protocol (required). +name+ is
|
17
|
+
# provided as an alias for +protocol+.
|
18
|
+
#
|
19
|
+
# * namespace, a string that qualifies the name (optional).
|
20
|
+
#
|
21
|
+
# * doc, a string describing this protocol (optional).
|
22
|
+
#
|
23
|
+
# * types, an optional list of definitions of named types (records, enums,
|
24
|
+
# fixed and errors). An error definition is just like a record definition
|
25
|
+
# except it uses "error" instead of "record". Note that forward references
|
26
|
+
# to named types are not permitted.
|
27
|
+
#
|
28
|
+
# * messages, an optional JSON object whose keys are message names and whose
|
29
|
+
# values are objects whose attributes are described below. No two messages
|
30
|
+
# may have the same name.
|
31
|
+
#
|
32
|
+
# The name and namespace qualification rules defined for schema objects apply
|
33
|
+
# to protocols as well: see the documentation for Icss::Meta::Type.
|
34
|
+
#
|
35
|
+
# For example, one may define a simple HelloWorld protocol with:
|
36
|
+
#
|
37
|
+
# {
|
38
|
+
# "namespace": "com.acme",
|
39
|
+
# "protocol": "HelloWorld",
|
40
|
+
# "doc": "Protocol Greetings",
|
41
|
+
# "types": [
|
42
|
+
# { "name": "Greeting",
|
43
|
+
# "type": "record",
|
44
|
+
# "fields": [ {"name": "message", "type": "string"} ]},
|
45
|
+
# { "name": "Curse",
|
46
|
+
# "type": "error",
|
47
|
+
# "fields": [ {"name": "message", "type": "string"} ]}
|
48
|
+
# ],
|
49
|
+
# "messages": {
|
50
|
+
# "hello": {
|
51
|
+
# "doc": "Say hello.",
|
52
|
+
# "request": [{"name": "greeting", "type": "Greeting" }],
|
53
|
+
# "response": "Greeting",
|
54
|
+
# "errors": ["Curse"]
|
55
|
+
# }
|
56
|
+
# }
|
57
|
+
# }
|
58
|
+
#
|
59
|
+
class Protocol
|
60
|
+
include Icss::ReceiverModel
|
61
|
+
include Icss::ReceiverModel::ActsAsCatalog
|
89
62
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
63
|
+
field :protocol, String, :required => true, :validates => { :format => { :with => /\A[A-Za-z_]\w*\Z/, :message => "must start with [A-Za-z_] and contain only [A-Za-z0-9_]." } }
|
64
|
+
alias_method :basename, :protocol
|
65
|
+
field :namespace, String, :required => true, :validates => { :format => { :with => /\A([A-Za-z_]\w*\.?)+\Z/, :message => "Segments that start with [A-Za-z_] and contain only [A-Za-z0-9_], joined by '.'dots" } }
|
66
|
+
field :title, String
|
67
|
+
field :doc, String
|
68
|
+
#
|
69
|
+
field :types, Array, :items => Icss::Meta::TypeFactory, :default => []
|
70
|
+
field :_doc_hints, Hash, :default => {}
|
94
71
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
72
|
+
field :messages, Hash, :values => Icss::Meta::Message, :default => {}
|
73
|
+
field :data_assets, Array, :items => Icss::Meta::DataAsset, :default => []
|
74
|
+
field :code_assets, Array, :items => Icss::Meta::CodeAsset, :default => []
|
75
|
+
field :targets, Hash, :values => Icss::TargetListFactory, :default => {}, :merge_as => :hash_of_arrays
|
76
|
+
|
77
|
+
field :tags, Array, :items => String, :default => []
|
78
|
+
field :categories, Array, :items => String, :default => []
|
79
|
+
field :license_id, String
|
80
|
+
field :credits, Hash, :values=> String, :default => {} # hash of source_ids
|
81
|
+
|
82
|
+
def license
|
83
|
+
Icss::Meta::License.find(license_id) unless license_id.blank?
|
84
|
+
end
|
85
|
+
|
86
|
+
def sources
|
87
|
+
@sources ||= credits.inject(Hash.new){|hash, credit| hash[credit[0].to_sym] = Icss::Meta::Source.find(credit[1]); hash }
|
88
|
+
end
|
99
89
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
messages[nm]
|
104
|
-
end
|
90
|
+
field :under_consideration, Boolean
|
91
|
+
field :update_frequency, String, :validates => { :format => { :with => /daily|weekly|monthly|quarterly|never/ }, :allow_blank => true }
|
92
|
+
rcvr_remaining :_extra_params
|
105
93
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
94
|
+
after_receive(:parent_my_messages) do |hsh|
|
95
|
+
# Set each message's protocol to self, and if the basename wasn't given, set
|
96
|
+
# it using the message's hash key.
|
97
|
+
self.messages.each{|msg_name, msg| msg.protocol = self; msg.basename ||= msg_name }
|
98
|
+
end
|
99
|
+
after_receive(:warn_if_invalid) do |hsh|
|
100
|
+
warn errors.inspect unless valid?
|
101
|
+
warn "Extra params #{_extra_params.keys.inspect} given to #{self.fullname}" if _extra_params.present?
|
102
|
+
end
|
103
|
+
after_receive(:declare_core_types) do |hsh|
|
104
|
+
self.types.each{|type| type.respond_to?(:_schema) && (type._schema.is_core = (self.fullname == "icss.core.typedefs")) }
|
105
|
+
end
|
106
|
+
after_receive(:fix_legacy_catalog_info) do
|
107
|
+
if targets[:catalog].present?
|
108
|
+
catalog = targets[:catalog].first
|
109
|
+
if self.title.blank? then self.title = catalog.title ; end
|
110
|
+
if self.tags.blank? then self.tags = catalog.tags ; end
|
111
|
+
if self.doc.blank? then self.doc = catalog.description; end
|
112
|
+
end
|
113
|
+
end
|
111
114
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
tgts.each do |target_name, target_info_list|
|
116
|
-
targets[target_name] = TargetListFactory.receive(target_info_list, target_name) # array of targets
|
115
|
+
# String: namespace.basename
|
116
|
+
def fullname
|
117
|
+
[namespace, basename].compact.join(".")
|
117
118
|
end
|
118
|
-
targets
|
119
|
-
end
|
120
119
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
:doc => doc,
|
126
|
-
:under_consideration => under_consideration,
|
127
|
-
:update_frequency => update_frequency,
|
128
|
-
:types => (types && types.map(&:to_hash)),
|
129
|
-
:messages => messages.inject({}){|h,(k,v)| h[k] = v.to_hash; h },
|
130
|
-
:data_assets => data_assets.map(&:to_hash).map(&:compact_blank),
|
131
|
-
:code_assets => code_assets.map(&:to_hash).map(&:compact_blank),
|
132
|
-
:targets => targets_to_hash,
|
133
|
-
}.reject{|k,v| v.nil? }
|
134
|
-
end
|
120
|
+
# a / separated version of the fullname, with no / at start
|
121
|
+
def path
|
122
|
+
fullname.gsub('.', '/')
|
123
|
+
end
|
135
124
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
125
|
+
def find_message nm
|
126
|
+
return if messages.blank?
|
127
|
+
nm = nm.to_s.gsub("/", ".").split(".").last
|
128
|
+
messages[nm]
|
140
129
|
end
|
141
|
-
end
|
142
130
|
|
143
|
-
|
144
|
-
|
131
|
+
def receive_types(types)
|
132
|
+
# this is a horrible, horrible kludge so that types with simple names ('bob') can become
|
133
|
+
# properly namespaced ('foo.bar.bob') even when they haven't met their parents (and even
|
134
|
+
# where the calls to receive types are nested/recursive)
|
135
|
+
Icss::Meta::TypeFactory.with_namespace(namespace) do
|
136
|
+
super(types)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def receive_messages(types)
|
141
|
+
# this is a horrible, horrible kludge so that messages with simple names ('do_bob') can become
|
142
|
+
# properly namespaced ('foo.bar.do_bob') even when they haven't met their parents (and even
|
143
|
+
# where the calls to receive_messages are nested/recursive)
|
144
|
+
Icss::Meta::TypeFactory.with_namespace(namespace) do
|
145
|
+
super(types)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def receive_protocol(nm)
|
150
|
+
name_segs = nm.to_s.gsub("/", ".").split(".")
|
151
|
+
self.protocol = name_segs.pop
|
152
|
+
self.namespace = name_segs.join('.') if name_segs.present?
|
153
|
+
end
|
154
|
+
|
155
|
+
def receive_targets(tgts)
|
156
|
+
return unless tgts.present?
|
157
|
+
self.targets ||= {}
|
158
|
+
tgts.symbolize_keys!.each do |target_name, target_info_list|
|
159
|
+
targets[target_name] = TargetListFactory.receive(target_info_list, target_name) # array of targets
|
160
|
+
end
|
161
|
+
targets
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.catalog_sections
|
165
|
+
['core', 'datasets', 'old']
|
166
|
+
end
|
167
|
+
|
168
|
+
def to_hash()
|
169
|
+
{
|
170
|
+
:namespace => @namespace, # use accessor so unset namespace isn't given
|
171
|
+
:protocol => protocol,
|
172
|
+
:license_id => license_id,
|
173
|
+
:credits => credits,
|
174
|
+
:tags => tags,
|
175
|
+
:categories => categories,
|
176
|
+
:doc => doc,
|
177
|
+
:types => (types && types.map(&:to_schema)),
|
178
|
+
:messages => messages.inject({}){|h,(k,v)| h[k.to_sym] = v.to_hash; h },
|
179
|
+
:data_assets => data_assets.map(&:to_hash).map(&:compact_blank),
|
180
|
+
:code_assets => code_assets.map(&:to_hash).map(&:compact_blank),
|
181
|
+
:update_frequency => update_frequency,
|
182
|
+
:under_consideration => under_consideration,
|
183
|
+
:targets => targets_to_hash,
|
184
|
+
}.reject{|k,v| v.nil? }
|
185
|
+
end
|
186
|
+
|
187
|
+
def targets_to_hash
|
188
|
+
return unless targets
|
189
|
+
targets.inject({}) do |hsh,(k,targs)|
|
190
|
+
hsh[k] = targs.map(&:to_hash).map(&:compact_blank) ; hsh
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# This will cause funny errors when it is an element of something that's to_json'ed
|
195
|
+
def to_json(*args) to_hash.to_json(*args) ; end
|
196
|
+
end
|
145
197
|
end
|
198
|
+
|
146
199
|
end
|