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,72 +0,0 @@
1
- module Icss
2
-
3
- #
4
- # Instantiates an array of target objects
5
- #
6
- class TargetListFactory
7
- def self.receive target_info_list, target_name
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
- include Gorillib::Hashlike
17
- #
18
- # Name should not be something like 'default', it should be something
19
- # that 'appeals' to the message name.
20
- #
21
- rcvr_accessor :name, String
22
- end
23
-
24
- class MysqlTarget < Target
25
- rcvr_accessor :data_assets, Array, :of => String
26
- rcvr_accessor :database, String
27
- rcvr_accessor :table_name, String
28
- end
29
-
30
- class ApeyeyeTarget < Target
31
- rcvr_accessor :code_assets, Array, :of => String
32
- end
33
-
34
- class HbaseTarget < Target
35
- rcvr_accessor :data_assets, Array, :of => String
36
- rcvr_accessor :table_name, String
37
- rcvr_accessor :column_families, Array, :of => String
38
- rcvr_accessor :column_family, String
39
- rcvr_accessor :loader, String
40
- rcvr_accessor :id_field, String
41
- end
42
-
43
- class ElasticSearchTarget < Target
44
- rcvr_accessor :data_assets, Array, :of => String
45
- rcvr_accessor :index_name, String
46
- rcvr_accessor :id_field, String
47
- rcvr_accessor :object_type, String
48
- rcvr_accessor :loader, String
49
- end
50
-
51
- class GeoIndexTarget < Target
52
- rcvr_accessor :data_assets, Array, :of => String
53
- rcvr_accessor :table_name, String
54
- rcvr_accessor :min_zoom, Integer
55
- rcvr_accessor :max_zoom, Integer
56
- rcvr_accessor :chars_per_page, Integer
57
- rcvr_accessor :sort_field, String
58
- end
59
-
60
- class CatalogTarget < Target
61
- rcvr_accessor :name, String
62
- rcvr_accessor :license, String
63
- rcvr_accessor :title, String
64
- rcvr_accessor :link, String
65
- rcvr_accessor :description, String
66
- rcvr_accessor :owner, String
67
- rcvr_accessor :price, Float
68
- rcvr_accessor :tags, Array, :of => String
69
- rcvr_accessor :messages, Array, :of => String
70
- rcvr_accessor :packages, Array, :of => Hash
71
- end
72
- end
@@ -1,196 +0,0 @@
1
- module Icss
2
- class MetaType
3
- include Receiver
4
-
5
- def initialize *args
6
- receive! *args unless args.empty?
7
- end
8
-
9
- # Returns a string containing an XML representation of its receiver:
10
- #
11
- # {"foo" => 1, "bar" => 2}.to_xml
12
- # # =>
13
- # # <?xml version="1.0" encoding="UTF-8"?>
14
- # # <hash>
15
- # # <foo type="integer">1</foo>
16
- # # <bar type="integer">2</bar>
17
- # # </hash>
18
- #
19
- # To do so, the method loops over the pairs and builds nodes that depend on
20
- # the _values_. Given a pair +key+, +value+:
21
- #
22
- # * If +value+ is a hash there's a recursive call with +key+ as <tt>:root</tt>.
23
- #
24
- # * If +value+ is an array there's a recursive call with +key+ as <tt>:root</tt>,
25
- # and +key+ singularized as <tt>:children</tt>.
26
- #
27
- # * If +value+ is a callable object it must expect one or two arguments. Depending
28
- # on the arity, the callable is invoked with the +options+ hash as first argument
29
- # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. Its
30
- # return value becomes a new node.
31
- #
32
- # * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>.
33
- #
34
- # * Otherwise, a node with +key+ as tag is created with a string representation of
35
- # +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added.
36
- # Unless the option <tt>:skip_types</tt> exists and is true, an attribute "type" is
37
- # added as well according to the following mapping:
38
- #
39
- # XML_TYPE_NAMES = {
40
- # "Symbol" => "symbol",
41
- # "Fixnum" => "integer",
42
- # "Bignum" => "integer",
43
- # "BigDecimal" => "decimal",
44
- # "Float" => "float",
45
- # "TrueClass" => "boolean",
46
- # "FalseClass" => "boolean",
47
- # "Date" => "date",
48
- # "DateTime" => "datetime",
49
- # "Time" => "datetime"
50
- # }
51
- #
52
- # By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
53
- #
54
- # The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can
55
- # configure your own builder with the <tt>:builder</tt> option. The method also accepts
56
- # options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
57
- # Returns a string containing an XML representation of its receiver:
58
- #
59
- # {"foo" => 1, "bar" => 2}.to_xml
60
- # # =>
61
- # # <?xml version="1.0" encoding="UTF-8"?>
62
- # # <hash>
63
- # # <foo type="integer">1</foo>
64
- # # <bar type="integer">2</bar>
65
- # # </hash>
66
- #
67
- # To do so, the method loops over the pairs and builds nodes that depend on
68
- # the _values_. Given a pair +key+, +value+:
69
- #
70
- # * If +value+ is a hash there's a recursive call with +key+ as <tt>:root</tt>.
71
- #
72
- # * If +value+ is an array there's a recursive call with +key+ as <tt>:root</tt>,
73
- # and +key+ singularized as <tt>:children</tt>.
74
- #
75
- # * If +value+ is a callable object it must expect one or two arguments. Depending
76
- # on the arity, the callable is invoked with the +options+ hash as first argument
77
- # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. Its
78
- # return value becomes a new node.
79
- #
80
- # * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>.
81
- #
82
- # * Otherwise, a node with +key+ as tag is created with a string representation of
83
- # +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added.
84
- # Unless the option <tt>:skip_types</tt> exists and is true, an attribute "type" is
85
- # added as well according to the following mapping:
86
- #
87
- # XML_TYPE_NAMES = {
88
- # "Symbol" => "symbol",
89
- # "Fixnum" => "integer",
90
- # "Bignum" => "integer",
91
- # "BigDecimal" => "decimal",
92
- # "Float" => "float",
93
- # "TrueClass" => "boolean",
94
- # "FalseClass" => "boolean",
95
- # "Date" => "date",
96
- # "DateTime" => "datetime",
97
- # "Time" => "datetime"
98
- # }
99
- #
100
- # By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
101
- #
102
- # The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can
103
- # configure your own builder with the <tt>:builder</tt> option. The method also accepts
104
- # options like <tt>:dasherize</tt> and friends, they are forwarded to the
105
- #builder.
106
- #
107
- def to_xml options={}, &block
108
- options = options.reverse_merge(:root => self.class.xml_type_name)
109
- xml_hsh = self.to_hash
110
- # # remove once microsoft has signed off on this
111
- # xml_hsh.merge!(:_note => "XML support is experimental, structure may change in future") unless options[:skip_instruct]
112
- xml_hsh.to_xml(options, &block)
113
- end
114
-
115
- def self.xml_type_name
116
- self.to_s.demodulize.underscore.gsub(/_/, '-')
117
- end
118
-
119
- def to_json *args
120
- to_hash.to_json(*args)
121
- end
122
- end
123
-
124
- class RecordType < NamedType
125
-
126
- def ruby_klass_scope_names
127
- fullname.split('.').map(&:camelize)
128
- end
129
- def ruby_klass_name
130
- "::" + ruby_klass_scope_names.join('::')
131
- end
132
- def ruby_klass_parent
133
- ("::" + ruby_klass_scope_names[0..-2].join('::')).constantize
134
- end
135
-
136
- def ensure_parent_modules!
137
- ruby_klass_scope_names[0..-2].inject(Object) do |parent_module, module_name|
138
- new_parent = "::#{parent_module}::#{module_name}".constantize rescue nil
139
- new_parent ||= parent_module.const_set(module_name.to_sym, Module.new)
140
- # p [parent_module, new_parent, module_name]
141
- new_parent
142
- end
143
- end
144
-
145
- def define_klass
146
- ensure_parent_modules!
147
- parent_module = ruby_klass_parent
148
- klass_basename = ruby_klass_scope_names.last.to_sym
149
- klass = parent_module.const_get(klass_basename) rescue nil
150
- klass ||= parent_module.const_set(klass_basename, Class.new(::Icss::MetaType))
151
- klass
152
- end
153
-
154
- def ruby_klass
155
- return @klass if @klass
156
- klass = define_klass
157
- decorate_with_receivers(klass)
158
- decorate_with_conveniences(klass)
159
- @klass = klass
160
- end
161
-
162
- def decorate_with_receivers klass
163
- fields.each do |field|
164
- field.define_receiver_on(klass)
165
- end
166
- end
167
-
168
- def decorate_with_conveniences klass
169
- klass.send :include, Receiver::ActsAsHash
170
- end
171
-
172
- end
173
-
174
- Icss::RecordField.class_eval do
175
- def define_receiver_on(klass)
176
- if union? || enum?
177
- warn "Can't handle union or enum types yet: #{self.inspect}"
178
- return
179
- end
180
- p self if type.nil?
181
- klass.rcvr_accessor name.to_sym, type.ruby_klass, field_receiver_attrs
182
- end
183
-
184
- def field_receiver_attrs
185
- attrs = {}
186
- (self.class.receiver_attr_names - [:name, :type]).each do |attr|
187
- val = attr_set?(attr) && self.send(attr) or next
188
- attrs[attr] = val
189
- end
190
- case self.type
191
- when Icss::ArrayType then attrs[:of] = self.type.items.ruby_klass
192
- end
193
- attrs
194
- end
195
- end
196
- end
@@ -1,16 +0,0 @@
1
- module Icss
2
- module Validations
3
-
4
- # An avro name must
5
- # * start with [A-Za-z_]
6
- # * subsequently contain only [A-Za-z0-9_]
7
- def validate_name
8
- (name =~ /\A[A-Za-z_]\w*\z/) or raise "An avro name must start with [A-Za-z_] and contain only [A-Za-z0-9_]; have #{name}. A namespace is the dot-separated sequence of such names."
9
- end
10
-
11
- def validate_namespace
12
- (name =~ /\A([A-Za-z_]\w*\.?)+\z/) or raise "An avro name must start with [A-Za-z_] and contain only [A-Za-z0-9_]; have #{name}. A namespace is the dot-separated sequence of such names."
13
- end
14
-
15
- end
16
- end
@@ -1,171 +0,0 @@
1
- require 'rubygems'
2
- require 'rspec'
3
- require 'icss'
4
-
5
- def template_icss
6
- return <<EOS
7
- ---
8
- namespace: foo.bar
9
- protocol: baz
10
-
11
- data_assets:
12
- - name: test_data_asset
13
- location: data/test_data.tsv
14
- type: test_data_record
15
-
16
- messages:
17
- search:
18
- doc: A testable template message
19
- request:
20
- - name: test_request
21
- type: test_request_record
22
- response: test_data_record
23
- samples:
24
- - request:
25
- - first_param: foo
26
- second_param: bar
27
-
28
- targets:
29
- catalog:
30
- - name: test_catalog_entry
31
- title: Test Icss
32
- description: This is a template Icss to test the error handling of the Icss library.
33
- tags:
34
- - test
35
- - icss
36
- - template
37
- messages:
38
- - search
39
- packages:
40
- - data_assets:
41
- - test_data_asset
42
-
43
- types:
44
- - name: test_request_record
45
- doc: A template request record
46
- type: record
47
- fields:
48
- - name: first_param
49
- doc: The first test parameter
50
- type: string
51
- - name: second param
52
- doc: The second test parameter
53
- type: string
54
-
55
- - name: test_complex_data
56
- doc: A template complex data type
57
- type: record
58
- fields:
59
- - name: field_one
60
- doc: A simple field for a complex data type
61
- type: string
62
- - name: field_two
63
- doc: Another simple field for a complex data type
64
- type: string
65
-
66
- - name: test_data_record
67
- doc: A template data record
68
- type: record
69
- fields:
70
- - name: simple_data
71
- doc: A simple piece of data
72
- type: int
73
- - name: complex_data
74
- doc: A complex piece of data
75
- type: test_complex_data
76
-
77
- EOS
78
- end
79
-
80
- describe "Icss::Protocol validations" do
81
- before :each do
82
- @template = YAML.load(template_icss)
83
- end
84
-
85
- it "should be able to receive a correctly formatted template Icss" do
86
- lambda { @icss =Icss::Protocol.receive @template }.should_not raise_error
87
- @icss.errors.should be_empty
88
- end
89
-
90
- it "should contain only the keys that were included in the Icss file" do
91
- @icss = Icss::Protocol.receive @template
92
- @icss.keys.map { |k| k.to_s }.should == @template.keys
93
- end
94
-
95
- it "should generate an error when the namespace if formatted incorrectly" do
96
- @template['namespace'] = '$bad_namespace'
97
- Icss::Protocol.receive(@template).errors.keys.should == [:namespace]
98
- end
99
-
100
- it "should generate an error when the protocol if formatted incorrectly" do
101
- @template['protocol'] = '$bad_protocol'
102
- Icss::Protocol.receive(@template).errors.keys.should == [:protocol]
103
- end
104
-
105
- context "Catalog Target" do
106
-
107
- it "should generate an error when an undefined data_asset is specified" do
108
- @template['targets']['catalog'].first['packages'].first['data_assets'] = ['fake_data_asset']
109
- Icss::Protocol.receive(@template).errors.keys.should == [:catalog]
110
- end
111
-
112
- it "should generate an error when an undefined message name is specified" do
113
- @template['targets']['catalog'].first['messages'] = ['fake_message']
114
- Icss::Protocol.receive(@template).errors.keys.should == [:catalog]
115
- end
116
-
117
- end
118
-
119
- context "Data Assets" do
120
-
121
- it "should generate an error when an asset's type is undefined" do
122
- @template['data_assets'].first['type'] = 'fake_data_asset'
123
- Icss::Protocol.receive(@template).errors.keys.should == [:data_assets]
124
- end
125
-
126
- end
127
-
128
- context "Messages" do
129
-
130
- it "should generate an error when a message's request type is undefined" do
131
- @template['messages']['search']['request'].first['type'] = 'fake_request_type'
132
- Icss::Protocol.receive(@template).errors.keys.should == [:messages]
133
- end
134
-
135
- it "should generate an error when a message's response type is undefined" do
136
- @template['messages']['search']['response'] = 'fake_response_type'
137
- Icss::Protocol.receive(@template).errors.keys.should == [:messages]
138
- end
139
-
140
- it "should generate an error when a message's sample request types do not match the request record" do
141
- @template['messages']['search']['samples'].first['request'] = [{ 'foo' => 'bar' }]
142
- Icss::Protocol.receive(@template).errors.keys.should == [:messages]
143
- end
144
-
145
- end
146
-
147
- context "Types" do
148
-
149
- it "should generate an error when an bad type definition is given for a specific type" do
150
- @template['types'].push({
151
- 'name' => 'fake_type_record',
152
- 'type' => 'fake'
153
- })
154
- Icss::Protocol.receive(@template).errors.keys.should == [:types]
155
- end
156
-
157
- it "should generate an error when an undefined type definition is given for a specific field" do
158
- @template['types'].push({
159
- 'name' => 'fake_type_record',
160
- 'type' => 'record',
161
- 'fields' => [{
162
- 'name' => 'fake_field',
163
- 'type' => 'fake_type'
164
- }]
165
- })
166
- Icss::Protocol.receive(@template).errors.keys.should == [:types]
167
- end
168
-
169
- end
170
-
171
- end