icss 0.1.3 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,161 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'yaml'
3
+ require 'gorillib/object/try_dup'
4
+ require 'icss/receiver_model/acts_as_hash'
5
+ require 'icss/receiver_model/active_model_shim'
6
+ require 'icss/type' #
7
+ require 'icss/type/simple_types' # Boolean, Integer, ...
8
+ require 'icss/type/named_type' # class methods for a named type: .metamodel .doc, .fullname, &c
9
+ require 'icss/type/record_type' # class methods for a record model: .field, .receive,
10
+ require 'icss/type/record_model' # instance methods for a record model
11
+ require 'icss/type/type_factory' # factory for instances based on type
12
+ require 'icss/type/structured_schema' # generate type from array, hash, &c schema
13
+ require 'icss/type/record_schema'
14
+ require 'icss/type/record_field'
15
+
16
+ require ENV.root_path('spec/support/icss_test_helper')
17
+ include IcssTestHelper
18
+
19
+ module Icss
20
+ module This
21
+ module That
22
+ class TheOther
23
+ def bob
24
+ 'hi bob'
25
+ end
26
+ end
27
+ end
28
+ module Right
29
+ class Here
30
+ include Icss::Meta::RecordModel
31
+ end
32
+ class OverThere < Here
33
+ end
34
+ end
35
+ end
36
+ class ::Icss::Numeric < ::Numeric ; end
37
+ end
38
+
39
+ include IcssTestHelper
40
+
41
+ describe Icss::Meta::RecordSchema do
42
+
43
+ BASIC_RECORD_SCHEMA = {:type => :record, :name => 'business.restaurant',
44
+ :doc => "y'know, for food and stuff",
45
+ :fields => [ { :name => 'menu', :type => 'string' } ] }
46
+ before(:each) do
47
+ remove_icss_constants('Business::Restaurant')
48
+ remove_icss_constants(:LabExperiment, :DayOfWeek, :GeoCoordinates)
49
+ end
50
+
51
+ describe "With basic schema" do
52
+ before(:each) do
53
+ @model_klass = Icss::Meta::RecordSchema.receive(BASIC_RECORD_SCHEMA)
54
+ @schema_writer = @model_klass._schema
55
+ end
56
+
57
+ it 'has schema_writer' do
58
+ @schema_writer.type.should == :record
59
+ @schema_writer.fullname.should == 'business.restaurant'
60
+ @schema_writer.fields.length.should == 1
61
+ end
62
+
63
+ it 'creates a named class' do
64
+ @model_klass.name.to_s.should == 'Icss::Business::Restaurant'
65
+ Icss::Business::Restaurant.fullname.should == "business.restaurant"
66
+ Icss::Business::Restaurant.doc.should == "y'know, for food and stuff"
67
+ end
68
+
69
+ it 'has accessors and receivers for the fields' do
70
+ blank_model = Class.new{ include Icss::Meta::RecordModel }
71
+ (@model_klass.public_methods - blank_model.public_methods).sort.should == [
72
+ :_domain_id_field, :_schema, :is_a, :_doc_hints,
73
+ ].sort.uniq
74
+ (@model_klass.public_instance_methods - Object.public_instance_methods).sort.should == [
75
+ :attr_set?, :menu, :menu=, :receive!, :receive_menu
76
+ ].sort.uniq
77
+ end
78
+ end
79
+
80
+ describe '#is_a' do
81
+ [
82
+ [ [Icss::This::That::TheOther], ['Icss::This::That::TheOther']],
83
+ [ ['this.that.the_other'], ['Icss::This::That::TheOther'] ],
84
+ [ ['this.right.here'], ['Icss::This::Right::Here', 'Icss::Meta::This::Right::HereModel'] ],
85
+ [ ['this.that.the_other', 'this.right.here'], ['Icss::This::That::TheOther', 'Icss::Meta::This::Right::HereModel'] ],
86
+ ].each do |given_is_a, expected_superklasses|
87
+ it "sets a superclass from #{given_is_a}" do
88
+ schema_hsh = BASIC_RECORD_SCHEMA.merge(:is_a => given_is_a)
89
+ @model_klass = Icss::Meta::RecordSchema.receive(schema_hsh)
90
+ expected_superklasses.each do |superklass|
91
+ @model_klass.should < superklass.constantize
92
+ end
93
+ end
94
+ end
95
+
96
+ it 'does NOT follow superclasses of multiple inheritance parents' do
97
+ schema_hsh = BASIC_RECORD_SCHEMA.merge(:is_a => ['this.that.the_other', 'this.right.over_there'])
98
+ @model_klass = Icss::Meta::RecordSchema.receive(schema_hsh)
99
+ @model_klass.should < Icss::This::That::TheOther
100
+ @model_klass.should_not < Icss::This::Right::OverThere
101
+ @model_klass.should < Icss::This::Right::OverThere.metamodel
102
+ @model_klass.should_not < Icss::This::Right::Here
103
+ @model_klass.should_not < Icss::This::Right::Here.metamodel
104
+ end
105
+
106
+ it 'does allow explicitly-listed superclasses of multiple inheritance parents' do
107
+ schema_hsh = BASIC_RECORD_SCHEMA.merge(:is_a => ['this.that.the_other', 'this.right.over_there', 'this.right.here'])
108
+ @model_klass = Icss::Meta::RecordSchema.receive(schema_hsh)
109
+ @model_klass.should < Icss::This::That::TheOther
110
+ @model_klass.should_not < Icss::This::Right::OverThere
111
+ @model_klass.should < Icss::This::Right::OverThere.metamodel
112
+ @model_klass.should_not < Icss::This::Right::Here
113
+ @model_klass.should < Icss::This::Right::Here.metamodel
114
+ end
115
+ end
116
+ end
117
+
118
+ describe Icss::Meta::RecordModel do
119
+ context 'record schema' do
120
+ before do
121
+ @klass = Icss::Meta::TypeFactory.receive({
122
+ 'type' => 'record',
123
+ 'name' => 'lab_experiment',
124
+ 'is_a' => ['this.that.the_other', 'this.right.here'],
125
+ 'fields' => [
126
+ { 'name' => 'temperature', 'type' => 'float' },
127
+ { 'name' => 'day', 'type' => {
128
+ 'name' => 'day_of_week', :type => 'enum',
129
+ 'symbols' => [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday] }},
130
+ { 'name' => 'geo', 'type' => {
131
+ :type => 'record', 'name' => 'geo_coordinates',
132
+ 'fields' => [
133
+ { 'name' => 'latitude', 'type' => 'float' },
134
+ { 'name' => 'longitude', 'type' => 'float' },
135
+ { 'name' => 'spatial_extent', 'type' =>
136
+ { 'type' => 'array', 'items' => {
137
+ 'type' => 'array', 'items' => 'float' }}},
138
+ ]}},
139
+ ] })
140
+ @obj = @klass.receive({ :temperature => 97.4, :day => 'tuesday',
141
+ :geo => {
142
+ 'longitude' => '-97.75', :latitude => "30.03",
143
+ 'spatial_extent' => [ ['-97.75', '30.03'], ['-97.70', '30.1'], ['-97.90', '30.1'] ]
144
+ } })
145
+ end
146
+ it 'handles array of record of ...' do
147
+ @klass.to_s.should == 'Icss::LabExperiment'
148
+ @obj.should be_a(@klass)
149
+ @obj.should be_a(Icss::This::That::TheOther)
150
+ @obj.should be_a(Icss::This::Right::Here.metamodel)
151
+ end
152
+ it 'receives data' do
153
+ @obj.temperature.should == 97.4
154
+ @obj.day.should == :tuesday
155
+ @obj.geo.latitude.should == 30.03
156
+ @obj.geo.longitude.should == -97.75
157
+ @obj.geo.should be_a(Icss::GeoCoordinates)
158
+ @obj.geo.spatial_extent.should == [ [-97.75, 30.03], [-97.70, 30.1], [-97.90, 30.1] ]
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,155 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'gorillib/object/try_dup'
3
+ require 'icss/receiver_model/acts_as_hash'
4
+ require 'icss/receiver_model/acts_as_loadable'
5
+ require 'icss/receiver_model/acts_as_catalog'
6
+ require 'icss/type'
7
+ require 'icss/type/simple_types'
8
+ require 'icss/type/named_type'
9
+ require 'icss/type/record_type'
10
+ require 'icss/type/record_model' # instance methods for a record model
11
+ require 'icss/type/type_factory' #
12
+ require 'icss/type/structured_schema'
13
+ #
14
+ require 'icss'
15
+ #
16
+ require ENV.root_path('spec/support/icss_test_helper')
17
+ include IcssTestHelper
18
+
19
+ describe Icss::Meta::RecordType do
20
+
21
+ before(:each) do
22
+ IcssTestHelper.remove_icss_constants('Smurf::Poppa', 'Smurf::Brainy', 'Smurf::Smurfette')
23
+ module Icss
24
+ module Smurf
25
+ class Poppa < Icss::SmurfRecord
26
+ field :smurfiness, :int
27
+ end
28
+ module Brainy
29
+ include Icss::Meta::RecordModel
30
+ field :has_glasses, Boolean
31
+ end
32
+ class Smurfette < Poppa
33
+ include Brainy
34
+ field :blondness, Integer
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ let(:new_smurf_klass){ k = Class.new(Icss::Smurf::Poppa) }
41
+ let(:module_smurf ){ m = Module.new; m.send(:extend, Icss::Meta::RecordType) ; m }
42
+ let(:poppa ){ Icss::Smurf::Poppa.new() }
43
+ let(:smurfette ){ Icss::Smurf::Smurfette.new() }
44
+
45
+ it 'adds few class methods' do
46
+ (Icss::Smurf::Smurfette.public_methods - Class.public_methods).sort.should == [
47
+ :after_receive, :after_receivers,
48
+ :field, :field_names, :fields, :has_field?, :field_named,
49
+ :metamodel, :to_schema, :is_core?,
50
+ :fullname, :namespace, :basename, :doc, :doc=,
51
+ :rcvr, :rcvr_alias, :rcvr_remaining, :receive, :pathname
52
+ ].sort
53
+ end
54
+ it 'adds few methods' do
55
+ (Icss::Smurf::Smurfette.new.public_methods -
56
+ Object.public_methods -
57
+ [ :attr_set?, :receive!, ] # maybe added later by record_model, they're OK
58
+ ).sort.should == [
59
+ :blondness, :blondness=, :has_glasses, :has_glasses=,
60
+ :receive_blondness,:receive_has_glasses,
61
+ :receive_smurfiness, :smurfiness, :smurfiness=
62
+ ].sort
63
+ end
64
+
65
+ context '.field' do
66
+ it 'adds field names' do
67
+ Icss::Smurf::Poppa.field_names.should == [:smurfiness]
68
+ end
69
+ it 'adds field info' do
70
+ Icss::Smurf::Poppa.field_named(:smurfiness).to_hash.should == {:name => :smurfiness, :type => :int}
71
+ end
72
+ it 'inherits parent fields' do
73
+ new_smurf_klass.field(:birthday, :time)
74
+ Icss::Smurf::Poppa.field_names.should == [:smurfiness]
75
+ Icss::Smurf::Poppa.field_named(:smurfiness).to_hash.should == {:name => :smurfiness, :type => :int}
76
+ new_smurf_klass.field_names.should == [:smurfiness, :birthday]
77
+ new_smurf_klass.field_named(:smurfiness).to_hash.should == {:name => :smurfiness, :type => :int}
78
+ new_smurf_klass.field_named(:birthday ).to_hash.should == {:name => :birthday, :type => :time}
79
+ end
80
+ it 'sets accessor visibility' do
81
+ new_smurf_klass.field(:field_1, Integer, :accessor => :none)
82
+ new_smurf_klass.field(:field_2, Integer, :accessor => :private)
83
+ new_smurf_klass.field(:field_3, Integer, :accessor => :protected)
84
+ new_smurf = new_smurf_klass.new
85
+ new_smurf.should_not respond_to('field_1=')
86
+ new_smurf.should_not respond_to('field_1')
87
+ lambda{ new_smurf.field_2 = :hi }.should raise_error(NoMethodError, /private method \`field_2=/)
88
+ lambda{ new_smurf.field_2 }.should raise_error(NoMethodError, /private method \`field_2/)
89
+ lambda{ new_smurf.field_3 = :yo }.should raise_error(NoMethodError, /protected method \`field_3=/)
90
+ lambda{ new_smurf.field_3 }.should raise_error(NoMethodError, /protected method \`field_3/)
91
+ end
92
+ end
93
+
94
+ context '.fields' do
95
+ it 'inheritance works without weirdness' do
96
+ Icss::Smurf::Poppa.field_names.should == [:smurfiness]
97
+ Icss::Smurf::Smurfette.field_names.should == [:smurfiness, :has_glasses, :blondness]
98
+ Icss::Smurf::Brainy.field_names.should == [:has_glasses]
99
+ end
100
+ #
101
+ it 'fields dynamically added to superclasses appear, in order of ancestry' do
102
+ new_smurf_klass.send(:include, module_smurf)
103
+ module_smurf.field_names.should == []
104
+ new_smurf_klass.field_names.should == [:smurfiness]
105
+ #
106
+ module_smurf.field(:smurfberries, :integer)
107
+ new_smurf_klass.field_names.should == [:smurfiness, :smurfberries]
108
+ #
109
+ new_smurf_klass.field(:singing, :boolean)
110
+ module_smurf.field(:smurfberry_crunch, :integer)
111
+ new_smurf_klass.field_names.should == [:smurfiness, :smurfberries, :smurfberry_crunch, :singing]
112
+ end
113
+ #
114
+ it 're-announcing a field modifies its info hash downstream without rearranging' do
115
+ uncle_smurf = Class.new(Icss::Smurf::Poppa)
116
+ baby_smurf = Class.new(uncle_smurf)
117
+ baby_smurf.field_names.should == [:smurfiness]
118
+ uncle_smurf.field_named(:smurfiness).to_hash.should == {:name => :smurfiness, :type => :int}
119
+ baby_smurf.field_named( :smurfiness).to_hash.should == {:name => :smurfiness, :type => :int}
120
+ #
121
+ uncle_smurf.field(:smurfiness, :float, :validates => { :numericality => true })
122
+ Icss::Smurf::Poppa.field_named(:smurfiness).to_hash.should == {:name => :smurfiness, :type => :int}
123
+ uncle_smurf.field_named(:smurfiness).to_hash.should == {:name => :smurfiness, :type => :float, :validates => { :numericality => true }}
124
+ baby_smurf.field_named(:smurfiness ).to_hash.should == {:name => :smurfiness, :type => :float, :validates => { :numericality => true }}
125
+ end
126
+ it 'does not override an existing method' do
127
+ new_smurf_klass.class_eval{ def foo() "hello!" end }
128
+ new_smurf_klass.field :foo, String
129
+ new_smurf_klass.new.foo.should == "hello!"
130
+ end
131
+ end
132
+
133
+ context 'class schema' do
134
+ it "has .fullname, .namespace, .basename, and .doc" do
135
+ [:fullname, :namespace, :basename, :doc].each do |meth|
136
+ Icss::Smurf::Smurfette.should respond_to(meth)
137
+ Icss::Smurf::Smurfette.new.should_not respond_to(meth)
138
+ end
139
+ end
140
+ it "name corresponds to its class & module scope" do
141
+ Icss::Smurf::Smurfette.basename.should == 'smurfette'
142
+ Icss::Smurf::Smurfette.namespace.should == 'smurf'
143
+ Icss::Smurf::Smurfette.fullname.should == 'smurf.smurfette'
144
+ end
145
+ it "has a settable doc string" do
146
+ Icss::Smurf::Poppa.doc = "Poppa Doc: be cool with them Haitians"
147
+ Icss::Smurf::Poppa.doc.should == "Poppa Doc: be cool with them Haitians"
148
+ Icss::Smurf::Smurfette.doc.should == "Poppa Doc: be cool with them Haitians"
149
+ Icss::Smurf::Smurfette.doc = "Gentlesmurfs prefer blondes"
150
+ Icss::Smurf::Smurfette.doc.should == "Gentlesmurfs prefer blondes"
151
+ Icss::Smurf::Poppa.doc.should == "Poppa Doc: be cool with them Haitians"
152
+ end
153
+ end
154
+
155
+ end
@@ -0,0 +1,121 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'time'
3
+ require 'icss/type'
4
+ require 'icss/type/simple_types'
5
+ require ENV.root_path('spec/support/icss_test_helper')
6
+ include IcssTestHelper
7
+
8
+ describe 'Icss::SIMPLE_TYPES' do
9
+ it('tests all of them'){ SIMPLE_TYPES_TO_TEST.values.map(&:first).map(&:to_s).sort.should == Icss::SIMPLE_TYPES.values.map(&:to_s).sort }
10
+ it('tests all of them'){ SIMPLE_TYPES_TO_TEST.keys.map(&:to_s).sort.should == Icss::SIMPLE_TYPES.keys.map(&:to_s).sort }
11
+
12
+ SIMPLE_TYPES_TO_TEST.each do |basename, (type_klass, parent_base_class, parent_instance)|
13
+ context type_klass do
14
+ it 'is named in Icss::SIMPLE_TYPES' do
15
+ basename = Icss::SIMPLE_TYPES.key(type_klass)
16
+ type_klass.to_schema == basename
17
+ end
18
+ it 'is named in Icss::SIMPLE_TYPES' do
19
+ basename = Icss::SIMPLE_TYPES.key(type_klass)
20
+ type_klass.to_schema == basename
21
+ end
22
+ it 'returns its type name as its schema' do
23
+ schema = type_klass.to_schema
24
+ schema.should be_a(Symbol)
25
+ Icss::SIMPLE_TYPES[schema].should == type_klass
26
+ end
27
+ it 'is simple? and simple?, but not record? or union?' do
28
+ Icss::Meta::Type.simple?( type_klass).should be_true
29
+ Icss::Meta::Type.simple?( type_klass).should be_true
30
+ Icss::Meta::Type.union?( type_klass).should be_false
31
+ Icss::Meta::Type.record?( type_klass).should be_false
32
+ end
33
+
34
+ it 'descends from a base type' do
35
+ type_klass.should be <= parent_base_class unless type_klass == Boolean
36
+ type_klass.receive(parent_instance).should be_a(parent_base_class)
37
+ end
38
+
39
+ it 'has .receive' do
40
+ obj = type_klass.receive(parent_instance)
41
+ if FALSE_PARENTS.include?(type_klass.to_s)
42
+ obj.should be_a(parent_base_class)
43
+ else
44
+ obj.should be_a(type_klass)
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ describe '.receive' do
51
+ def self.it_correctly_converts(type, orig, desired)
52
+ it "for #{type} converts #{orig.inspect} to #{desired.inspect}" do
53
+ type.receive(orig).should == desired
54
+ end
55
+ end
56
+
57
+ describe 'type coercion' do
58
+ [
59
+ [Symbol, 'foo', :foo], [Symbol, :foo, :foo], [Symbol, nil, nil], [Symbol, '', nil],
60
+ [Integer, '5', 5], [Integer, 5, 5], [Integer, nil, nil], [Integer, '', nil],
61
+ [Integer, '5', 5], [Integer, 5, 5], [Integer, nil, nil], [Integer, '', nil],
62
+ [Long, '5', 5], [Long, 5, 5], [Long, nil, nil], [Long, '', nil],
63
+ [Float, '5.2', 5.2], [Float, 5.2, 5.2], [Float, nil, nil], [Float, '', nil],
64
+ [Double, '5.2', 5.2], [Double, 5.2, 5.2], [Double,nil, nil], [Double,'', nil],
65
+ [String, 'foo', 'foo'], [String, :foo, 'foo'], [String, nil, ""], [String, '', ""],
66
+ [String, 5.2, "5.2"], [String, [1], "[1]"], [String, 1, "1"],
67
+ [Binary, 'foo', 'foo'], [Binary, :foo, 'foo'], [Binary, nil, ""], [Binary, '', ""],
68
+ [Binary, 5.2, "5.2"], [Binary, [1], "[1]"], [Binary, 1, "1"],
69
+ [Time, '1985-11-05T04:03:02Z', Time.parse('1985-11-05T04:03:02Z')],
70
+ [Time, '1985-11-05T04:03:02+06:00', Time.parse('1985-11-04T22:03:02Z')],
71
+ [Time, Time.parse('1985-11-05T04:03:02Z'), Time.parse('1985-11-05T04:03:02Z')],
72
+ [Time, nil, nil], [Time, '', nil], [Time, 'blah', nil],
73
+ [Boolean, '0', true], [Boolean, 0, true], [Boolean, '', false], [Boolean, [], true], [Boolean, nil, nil],
74
+ [Boolean, '1', true], [Boolean, 1, true], [Boolean, '5', true], [Boolean, 'true', true],
75
+ [NilClass, nil, nil],
76
+ ].each do |type, orig, desired|
77
+ it_correctly_converts type, orig, desired
78
+ end
79
+
80
+ describe 'NilClass' do
81
+ it 'only accepts nil' do
82
+ lambda{ NilClass.receive('hello') }.should raise_error(ArgumentError, /must be initialized with nil, but \[hello\] was given/)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+
90
+ describe '::Boolean' do
91
+ let(:true_bool ){ ::Boolean.new(true) }
92
+ let(:false_bool){ ::Boolean.new(false) }
93
+ it("has #class Boolean" ){ (true_bool.class).should == ::Boolean ; (false_bool.class).should == ::Boolean }
94
+ describe 'mimicking true/false' do
95
+ it(":!" ){ (! true_bool).should == (! true) ; (! false_bool).should == (! false) }
96
+ it(":nil?" ){ (true_bool.nil?).should == (true.nil?) ; (false_bool.nil?).should == (false.nil?) }
97
+ it(":to_s" ){ (true_bool.to_s).should == (true.to_s) ; (false_bool.to_s).should == (false.to_s) }
98
+ { :!= => [true, false, nil],
99
+ :!~ => [true, false, nil],
100
+ :& => [true, false, nil],
101
+ :<=> => [true, false, nil],
102
+ :== => [true, false, nil],
103
+ :=== => [true, false, nil],
104
+ :=~ => [true, false, nil],
105
+ :^ => [true, false, nil],
106
+ :eql? => [true, false, nil],
107
+ :| => [true, false, nil],
108
+ :instance_of? => [::TrueClass, ::FalseClass, ::Object],
109
+ :is_a? => [::TrueClass, ::FalseClass, ::Object],
110
+ :kind_of? => [::TrueClass, ::FalseClass, ::Object],
111
+ }.each do |meth, meth_args|
112
+ it meth do
113
+ meth_args.each do |meth_arg|
114
+ true_bool.send( meth, meth_arg).should equal(true.send( meth, meth_arg))
115
+ false_bool.send(meth, meth_arg).should equal(false.send(meth, meth_arg))
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+
@@ -0,0 +1,300 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'gorillib/object/try_dup'
3
+ require 'icss/receiver_model/acts_as_hash'
4
+ require 'icss/type' #
5
+ require 'icss/type/simple_types' # Boolean, Integer, ...
6
+ require 'icss/type/named_type' # class methods for a named type: .metamodel .doc, .fullname, &c
7
+ require 'icss/type/record_type' # class methods for a record model: .field, .receive,
8
+ require 'icss/type/record_model' # instance methods for a record model
9
+ require 'icss/type/type_factory' # turn schema into types
10
+ require 'icss/type/structured_schema' # loads array, hash, enum, fixed and simple schema
11
+ #
12
+ require ENV.root_path('spec/support/icss_test_helper')
13
+ include IcssTestHelper
14
+
15
+
16
+ module Icss
17
+ module Test
18
+ module Foo ; class Bar ; end ; end
19
+ end
20
+ class Top ; end
21
+ end
22
+
23
+ describe 'complex types' do
24
+ before(:each) do
25
+ IcssTestHelper.remove_icss_constants('Test::Foo::Bar', 'Test::Foo', 'St::Bob')
26
+ module Icss
27
+ module Test
28
+ module Foo ; class Bar ; end ; end
29
+ end
30
+ end
31
+ end
32
+
33
+ describe Icss::Meta::SimpleSchema do
34
+ let(:bob){ Icss::Meta::SimpleSchema.receive({ :name => 'st.bob', :type => :simple, :is_a => [Binary], :doc => "hi, bob" }) }
35
+ it 'loads types that inherit from simple base types' do
36
+ bob.should < Binary
37
+ bob.should < String
38
+ bob.should < Icss::Meta::St::BobModel
39
+ bob.metamodel.should == Icss::Meta::St::BobModel
40
+ end
41
+ it 'has doc and all that' do
42
+ bob.fullname.should == 'st.bob'
43
+ bob.basename.should == :'bob'
44
+ bob.doc.should == 'hi, bob'
45
+ bob.is_a.should == [Binary]
46
+ end
47
+ end
48
+
49
+ describe Icss::Meta::ArraySchema do
50
+ [
51
+ [{:type => :array, :items => :'test.foo.bar'}, 'Icss::Test::Foo::Bar', ],
52
+ [{:type => :array, :items => :'int' }, 'Integer', ],
53
+ [{:type => :array, :items => :'top'}, 'Icss::Top', ],
54
+ ].each do |schema, expected_item_factory|
55
+ describe "With #{schema}" do
56
+ before do
57
+ @model_klass = Icss::Meta::ArraySchema.receive(schema)
58
+ @schema_writer = @model_klass._schema
59
+ end
60
+ it 'is a descendent of Array and its metatype' do
61
+ @model_klass.should < Array
62
+ @model_klass.should be_a Icss::Meta::ArrayType
63
+ @model_klass.should be_a Icss::Meta::NamedType
64
+ end
65
+ it 'has items and an item_factory' do
66
+ @model_klass.should respond_to(:items)
67
+ @model_klass.items.should == expected_item_factory.constantize
68
+ end
69
+ it 'has schema_writer' do
70
+ @schema_writer.type.should == :array
71
+ @schema_writer.to_hash.should == schema
72
+ # @schema_writer.should be_valid
73
+ @schema_writer.items = nil
74
+ # @schema_writer.should_not be_valid
75
+ end
76
+ end
77
+ end
78
+
79
+ context "klass name" do
80
+ [
81
+ [ 'ArrayOfSymbol', :symbol ],
82
+ [ 'ArrayOfArrayOfInteger', { :type => :array, :items => :int } ],
83
+ [ 'ArrayOfHashOfArrayOfString', { :type => :map, :values => { :type => :array, :items => :string } } ],
84
+ [ 'ArrayOfTestDotFooDotBar', 'test.foo.bar' ],
85
+ [ 'ArrayOfHashOfArrayOfTestDotFooDotBar', { :type => :map, :values => { :type => :array, :items => 'test.foo.bar' } } ],
86
+ ].each do |expected_name, items_schema|
87
+ it "is #{expected_name} for #{items_schema}" do
88
+ schema = {:type => :array, :items => items_schema }
89
+ kl = Icss::Meta::ArraySchema.receive(schema)
90
+ kl.fullname.should == expected_name
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ describe Icss::Meta::ArrayType do
97
+ context '.receive' do
98
+ it 'generates an instance of the type' do
99
+ Icss::Meta::ArraySchema.receive({:type => :array, :items => :'int' })
100
+ inst = Icss::ArrayOfInteger.receive([1, 2.0, nil, "4.5", "8", "fnord"])
101
+ inst.should be_a(Array)
102
+ inst.should be_a(Icss::ArrayOfInteger)
103
+ end
104
+ it 'with nil or "" gives nil; with [] gives []' do
105
+ Icss::Meta::ArraySchema.receive({:type => :array, :items => :'int' })
106
+ inst = Icss::ArrayOfInteger.receive(nil)
107
+ inst.should be_nil
108
+ inst = Icss::ArrayOfInteger.receive('')
109
+ inst.should be_nil
110
+ inst = Icss::ArrayOfInteger.receive([])
111
+ inst.should == []
112
+ inst = Icss::ArrayOfInteger.receive({})
113
+ inst.should == []
114
+ inst.should be_a(Icss::ArrayOfInteger)
115
+ end
116
+ it 'applies the item_factory' do
117
+ Icss::Meta::ArraySchema.receive({:type => :array, :items => :'int' })
118
+ inst = Icss::ArrayOfInteger.receive([1, 2.0, nil, "4.5", "8", "fnord"])
119
+ inst.should eql([1, 2, nil, 4, 8, 0]) # (1 == 1.0) is true but 1.eql?(1.0) is false
120
+ end
121
+ it 'passes items through if they already is_a? the item factory type'
122
+ end
123
+ end
124
+
125
+ describe Icss::Meta::HashSchema do
126
+ [
127
+ [{:type => :map, :values => :'test.foo.bar'}, 'Icss::Test::Foo::Bar', ],
128
+ [{:type => :map, :values => :'int' }, 'Integer', ],
129
+ [{:type => :map, :values => :'top'}, 'Icss::Top', ],
130
+ ].each do |schema, expected_value_factory|
131
+ describe "With #{schema}" do
132
+ before do
133
+ @model_klass = Icss::Meta::HashSchema.receive(schema)
134
+ @schema_writer = @model_klass._schema
135
+ end
136
+ it 'is a descendent of Hash and of its metatype' do
137
+ @model_klass.should < Hash
138
+ @model_klass.should be_a Icss::Meta::HashType
139
+ @model_klass.should be_a Icss::Meta::NamedType
140
+ end
141
+ it 'has values and an value_factory' do
142
+ @model_klass.should respond_to(:values)
143
+ @model_klass.values.should == expected_value_factory.constantize
144
+ end
145
+ it 'has schema_writer' do
146
+ @schema_writer.type.should == :map
147
+ @schema_writer.to_hash.should == schema
148
+ # @schema_writer.should be_valid
149
+ @schema_writer.values = nil
150
+ # @schema_writer.should_not be_valid
151
+ end
152
+ end
153
+ end
154
+ context "klass name" do
155
+ [
156
+ [ 'HashOfSymbol', :symbol ],
157
+ [ 'HashOfHashOfInteger', { :type => :map, :values => :int } ],
158
+ [ 'HashOfHashOfArrayOfString', { :type => :map, :values => { :type => :array, :items => :string } } ],
159
+ [ 'HashOfTestDotFooDotBar', 'test.foo.bar' ],
160
+ [ 'HashOfHashOfArrayOfTestDotFooDotBar', { :type => :map, :values => { :type => :array, :items => 'test.foo.bar' } } ],
161
+ ].each do |expected_name, values_schema|
162
+ it "is #{expected_name} for #{values_schema}" do
163
+ schema = {:type => :map, :values => values_schema }
164
+ kl = Icss::Meta::HashSchema.receive(schema)
165
+ kl.fullname.should == expected_name
166
+ end
167
+ end
168
+ end
169
+ end
170
+
171
+ describe Icss::Meta::HashType do
172
+ context '.receive' do
173
+ it 'generates an instance of the type' do
174
+ Icss::Meta::HashSchema.receive({:type => :map, :values => :'int' })
175
+ inst = Icss::HashOfInteger.receive([1, 2.0, nil, "4.5", "8", "fnord"])
176
+ inst.should be_a(Hash)
177
+ inst.should be_a(Icss::HashOfInteger)
178
+ end
179
+ it 'with nil or "" gives nil; with [] gives []' do
180
+ Icss::Meta::HashSchema.receive({:type => :map, :values => :'int' })
181
+ inst = Icss::HashOfInteger.receive(nil)
182
+ inst.should be_nil
183
+ inst = Icss::HashOfInteger.receive('')
184
+ inst.should be_nil
185
+ inst = Icss::HashOfInteger.receive([])
186
+ inst.should == {}
187
+ inst = Icss::HashOfInteger.receive({})
188
+ inst.should == {}
189
+ inst.should be_a(Icss::HashOfInteger)
190
+ end
191
+ it 'applies the value_factory' do
192
+ Icss::Meta::HashSchema.receive({:type => :map, :values => :'int' })
193
+ inst = Icss::HashOfInteger.receive({ :a => 1, 'b' => 2.0, :c => nil, 'd' => "4.5", :e => "8", 99 => "fnord"})
194
+ inst.should eql({ :a => 1, 'b' => 2, :c => nil, 'd' => 4, :e => 8, 99 => 0})
195
+ end
196
+ it 'passes items through if they already is_a? the values factory type'
197
+ it 'warns when I supply "items" not "values"' do
198
+ lambda{
199
+ Icss::Meta::HashSchema.receive({:type => :map, :items => :int })
200
+ }.should raise_error(ArgumentError)
201
+ end
202
+ end
203
+ end
204
+
205
+ describe Icss::Meta::EnumSchema do
206
+ [
207
+ {:type => :enum, :name => 'games.coin_outcomes', :symbols => %w[heads tails sideways]},
208
+ {:type => :enum, :name => 'always_awesome', :symbols => %w[AWESOME]},
209
+ {:type => :enum, :name => 'jackson.five', :symbols => [:a, :b, :c]},
210
+ ].each do |schema|
211
+ describe "With #{schema}" do
212
+ before do
213
+ @model_klass = Icss::Meta::EnumSchema.receive(schema)
214
+ @schema_writer = @model_klass._schema
215
+ end
216
+ it 'is a descendent of Enum and of its metatype' do
217
+ @model_klass.should < Symbol
218
+ @model_klass.should be_a Icss::Meta::EnumType
219
+ @model_klass.should be_a Icss::Meta::NamedType
220
+ end
221
+ it 'has symbols' do
222
+ @model_klass.should respond_to(:symbols)
223
+ @schema_writer.symbols.should == schema[:symbols].map(&:to_sym)
224
+ end
225
+ it 'has schema_writer' do
226
+ @schema_writer.type.should == :enum
227
+ # @schema_writer.should be_valid
228
+ @schema_writer.symbols = []
229
+ # @schema_writer.should_not be_valid
230
+ end
231
+ end
232
+ end
233
+
234
+ context '.receive' do
235
+ it 'applies the value_factory' do
236
+ Icss::Meta::EnumSchema.receive({:type => :enum, :name => 'games.coin_outcomes', :symbols => %w[heads tails sideways]})
237
+ inst = Icss::Games::CoinOutcomes.receive('heads')
238
+ inst.should == :heads ; inst.should be_a(Symbol)
239
+ inst = Icss::Games::CoinOutcomes.receive(:heads)
240
+ inst.should == :heads ; inst.should be_a(Symbol)
241
+ inst = Icss::Games::CoinOutcomes.receive('sideways')
242
+ inst.should == :sideways ; inst.should be_a(Symbol)
243
+ end
244
+ it 'raises an error on non-included value' do
245
+ Icss::Meta::EnumSchema.receive({:type => :enum, :name => 'games.coin_outcomes', :symbols => %w[heads tails sideways]})
246
+ lambda{ Icss::Games::CoinOutcomes.receive('ace_of_spades') }.should raise_error(ArgumentError, /Cannot receive ace_of_spades: must be one of heads,tails,sideways/)
247
+ Icss::Meta::EnumSchema.receive({:type => :enum, :name => 'herb.caen', :symbols => %w[parseley sage rosemary thyme]})
248
+ lambda{ Icss::Herb::Caen.receive('weed') }.should raise_error(ArgumentError, /Cannot receive weed: must be one of parseley,sage,rosemary,.../)
249
+ end
250
+ it 'raises an error on non-symbolizable value' do
251
+ Icss::Meta::EnumSchema.receive({:type => :enum, :name => 'games.coin_outcomes', :symbols => %w[heads tails sideways]})
252
+ lambda{ Icss::Games::CoinOutcomes.receive(77) }.should raise_error(NoMethodError, /undefined method .to_sym/)
253
+ end
254
+ it 'returns nil on nil/empty string value' do
255
+ Icss::Meta::EnumSchema.receive({:type => :enum, :name => 'games.coin_outcomes', :symbols => %w[heads tails sideways]})
256
+ Icss::Games::CoinOutcomes.receive(nil).should be_nil
257
+ Icss::Games::CoinOutcomes.receive('').should be_nil
258
+ Icss::Games::CoinOutcomes.receive(:"").should be_nil
259
+ end
260
+ end
261
+ end
262
+
263
+ describe Icss::Meta::FixedSchema do
264
+ [
265
+ {:type => :fixed, :name => 'geo_feature_category', :size => 1 },
266
+ {:type => :fixed, :name => 'sixteen_bytes_long', :size => 16 },
267
+ ].each do |schema|
268
+ describe "With #{schema}" do
269
+ before do
270
+ @model_klass = Icss::Meta::FixedSchema.receive(schema)
271
+ @schema_writer = @model_klass._schema
272
+ end
273
+ it 'has schema_writer' do
274
+ @schema_writer.type.should == :fixed
275
+ # @schema_writer.should be_valid
276
+ @schema_writer.size = nil
277
+ # @schema_writer.should_not be_valid
278
+ end
279
+ end
280
+ end
281
+
282
+ context '.receive' do
283
+ it 'applies the value_factory' do
284
+ Icss::Meta::FixedSchema.receive({:type => :fixed, :name => 'sixteen_bytes_long', :size => 16})
285
+ Icss::SixteenBytesLong.receive('123456789_123456').should == '123456789_123456'
286
+ end
287
+ it 'raises an error on too-long value' do
288
+ Icss::Meta::FixedSchema.receive({:type => :fixed, :name => 'sixteen_bytes_long', :size => 16})
289
+ lambda{ Icss::SixteenBytesLong.receive('123456789_1234567') }.should raise_error(ArgumentError, /Wrong size for a fixed-length type sixteen_bytes_long: got 17, not 16/)
290
+ end
291
+ it 'returns nil on nil/empty string value' do
292
+ Icss::Meta::FixedSchema.receive({:type => :fixed, :name => 'sixteen_bytes_long', :size => 16})
293
+ Icss::SixteenBytesLong.receive(nil).should be_nil
294
+ Icss::SixteenBytesLong.receive('').should be_nil
295
+ Icss::SixteenBytesLong.receive(:"").should be_nil
296
+ end
297
+ end
298
+ end
299
+
300
+ end