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
@@ -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