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,41 @@
1
+ module Icss
2
+ module Meta
3
+
4
+ #
5
+ # Describes an Avro Union type.
6
+ #
7
+ # Unions are represented using JSON arrays. For example, ["string", "null"]
8
+ # declares a schema which may be either a string or null.
9
+ #
10
+ # Unions may not contain more than one schema with the same type, except for
11
+ # the named types record, fixed and enum. For example, unions containing two
12
+ # array types or two map types are not permitted, but two types with different
13
+ # names are permitted. (Names permit efficient resolution when reading and
14
+ # writing unions.)
15
+ #
16
+ # Unions may not immediately contain other unions.
17
+ #
18
+ class UnionSchema
19
+ # extend Icss::Meta::ContainerType
20
+ # #
21
+ # attr_accessor :embedded_types
22
+ # attr_accessor :declaration_flavors
23
+ # #
24
+ # def receive! type_list
25
+ # self.declaration_flavors = []
26
+ # self.embedded_types = type_list.map do |schema|
27
+ # type = TypeFactory.receive(schema)
28
+ # declaration_flavors << TypeFactory.classify_schema_declaration(schema)
29
+ # type
30
+ # end
31
+ # end
32
+ # def to_schema
33
+ # embedded_types.zip(declaration_flavors).map do |t,fl|
34
+ # [:structured_schema].include?(fl) ? t.name : t.to_schema
35
+ # end
36
+ # end
37
+ # end
38
+
39
+ end
40
+ end
41
+ end
@@ -1,28 +1,65 @@
1
1
  module Icss
2
- Message.class_eval do
2
+ module Meta
3
+ Message.class_eval do
4
+
5
+ def query_string
6
+ req_fields = request.first.type.fields rescue nil ; return unless req_fields
7
+ req_fields.map do |field|
8
+ "#{field.name}=#{first_sample_request_param[field.name.to_s]}"
9
+ end.join("&")
10
+ end
11
+
12
+ def api_url
13
+ # all calls accept xml or json
14
+ "http://api.infochimps.com/#{path}?#{query_string}"
15
+ end
16
+
17
+ def sample_field_value(field)
18
+ field_name = ((field.respond_to?(:name) && field.name) || field).to_s
19
+
20
+ value = first_sample_request_param[field_name] # sample value for field
21
+ end
3
22
 
4
- def query_string
5
- fields = request.first.type.fields rescue nil ; return unless fields
6
- fields.map do |field|
7
- "#{field.name}=#{first_sample_request_param[field.name.to_s]}"
8
- end.join("&")
9
23
  end
10
24
 
11
- def api_url
12
- # all calls accept xml or json
13
- "http://api.infochimps.com/#{path}?#{query_string}"
25
+ RecordField.class_eval do
26
+ def title
27
+ return "!!missing!!" if type.blank?
28
+ # case type
29
+ # when ArrayType then "array of #{type.title} #{type.to_hash.inspect}"
30
+ # else type.title
31
+ # end
32
+ type.title
33
+ end
14
34
  end
15
- end
16
35
 
17
- RecordField.class_eval do
18
- def title
19
- return "!!missing!!" if type.blank?
20
- # case type
21
- # when ArrayType then "array of #{type.title} #{type.to_hash.inspect}"
22
- # else type.title
23
- # end
24
- type.title
36
+ RecordType.class_eval do
37
+
38
+ # * when :display_fields are listed in the :_doc_hints, their field names
39
+ # are split on '.' and mapped to the right nested RecordField objects:
40
+ # content_location.geo.longitude will give the field for
41
+ # content_location in the current type, the geo field in that type, and
42
+ # the longitude field object in that type.
43
+ # * if no :display_fields is given, return the full (flat) set of fields.
44
+ #
45
+ def display_fields
46
+ df_names = self._schema._doc_hints[:display_fields]
47
+ return fields.map{|f| [f] } if df_names.blank?
48
+ df_names.map do |fn|
49
+ name_segs = fn.split('.')
50
+ type = self
51
+ sub_fields = []
52
+ name_segs.map do |name_seg|
53
+ sub_fields << (type.field_named(name_seg) || name_seg)
54
+ type = sub_fields.last.type if sub_fields.last.respond_to?(:type)
55
+ type = type.items if type.respond_to?(:items)
56
+ type = type.values if type.respond_to?(:values)
57
+ end
58
+ sub_fields
59
+ end
60
+ end
61
+
25
62
  end
26
- end
27
63
 
64
+ end
28
65
  end
@@ -0,0 +1,32 @@
1
+ ### NamedArray type
2
+
3
+ Defining
4
+
5
+ ```ruby
6
+ field :slices, NamedArray, :of => AggregateQuantity, :pivoting_on => :name, :receives => :remaining
7
+ ```
8
+
9
+ Lets it equivalently live as
10
+
11
+ ```yaml
12
+ - name: foo
13
+ average_value: 3
14
+ slices:
15
+ - name: subcat_1
16
+ average_value: 3
17
+ - name: subcat_2
18
+ average_value: 3
19
+ ```
20
+
21
+ or naturally pivot to be
22
+
23
+ ```yaml
24
+ foo:
25
+ average_value: 3
26
+ subcat_1:
27
+ average_value: 3
28
+ subcat_2:
29
+ average_value: 3
30
+ ```
31
+
32
+ All the rcvr_remaining (unclaimed) properties pivot on their :name field to look like the one or the other at your choice.
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+
5
+ # klass Foo [:module_self_foo, :module_meth_extif]
6
+ # sgtncl Foo []
7
+ # ---------------
8
+ # obj ClassIncludingFoo [:meth_in_class, :module_meth_foo, :module_meth_inclif]
9
+ # klass ClassIncludingFoo [:meth_in_sgtn_cl, :self_meth_in_class, -- -- ]
10
+ # sgtncl ClassIncludingFoo [:self_meth_sgtn_cl, -- -- -- ]
11
+ # ---------------
12
+ # obj ClassExtendingFoo [:meth_in_class, -- -- -- ]
13
+ # klass ClassExtendingFoo [:meth_in_sgtn_cl, :self_meth_in_class, :module_meth_foo, :module_meth_inclif]
14
+ # sgtncl ClassExtendingFoo [:self_meth_sgtn_cl, -- -- -- ]
15
+ # ---------------
16
+ # obj ClassInclFooInSgtnCl [:meth_in_class, -- -- -- ]
17
+ # klass ClassInclFooInSgtnCl [:meth_in_sgtn_cl, :self_meth_in_class, :module_meth_foo, :module_meth_inclif]
18
+ # sgtncl ClassInclFooInSgtnCl [:self_meth_sgtn_cl, -- -- -- ]
19
+ # ---------------
20
+ # obj ClassExtFooInSgtnCl [:meth_in_class, -- , -- -- ]
21
+ # klass ClassExtFooInSgtnCl [:meth_in_sgtn_cl, :self_meth_in_class, -- -- ]
22
+ # sgtncl ClassExtFooInSgtnCl [:self_meth_sgtn_cl, :module_meth_foo, :module_meth_inclif]
23
+ #
24
+ #
25
+ # Ancestor chain
26
+ #
27
+ # klass Foo IncludedInFoo
28
+ # sgtncl ExtendedInFoo Module Object Kernel BasicObject
29
+ # #
30
+ # klass ClassExtendingFoo SimpleClass Object Kernel BasicObject
31
+ # sgtncl Foo IncludedInFoo Class Module Object Kernel BasicObject
32
+ # #
33
+ # klass ClassIncludingFoo Foo IncludedInFoo SimpleClass Object Kernel BasicObject
34
+ # sgtncl Class Module Object Kernel BasicObject
35
+ # #
36
+ # klass ClassInclFooInSgtnCl SimpleClass Object Kernel BasicObject
37
+ # sgtncl Foo IncludedInFoo Class Module Object Kernel BasicObject
38
+ # #
39
+ # klass ClassExtFooInSgtnCl SimpleClass Object Kernel BasicObject
40
+ # sgtncl Class Module Object Kernel BasicObject
41
+
42
+
43
+ # --------------- Foo
44
+ # module_meth_extif class Foo Module
45
+ # module_self_foo class Foo Module
46
+ #
47
+ # --------------- ClassExtendingFoo
48
+ # meth_in_class obj #<ClassExtendingFoo:0x00000100e ClassExtendingFoo
49
+ # self_meth_in_class class ClassExtendingFoo Class #<Class:ClassExtendingFoo>
50
+ # meth_in_sgtn_cl class ClassExtendingFoo Class #<Class:ClassExtendingFoo>
51
+ # self_meth_sgtn_cl sgtn_cl #<Class:ClassExtendingFoo> Class
52
+ # module_meth_foo class ClassExtendingFoo Class #<Class:ClassExtendingFoo>
53
+ # module_meth_inclif class ClassExtendingFoo Class #<Class:ClassExtendingFoo>
54
+ #
55
+ # --------------- ClassIncludingFoo
56
+ # meth_in_class obj #<ClassIncludingFoo:0x000001020 ClassIncludingFoo
57
+ # self_meth_in_class class ClassIncludingFoo Class #<Class:ClassIncludingFoo>
58
+ # meth_in_sgtn_cl class ClassIncludingFoo Class #<Class:ClassIncludingFoo>
59
+ # self_meth_sgtn_cl sgtn_cl #<Class:ClassIncludingFoo> Class
60
+ # module_meth_foo obj #<ClassIncludingFoo:0x000001020 ClassIncludingFoo
61
+ # module_meth_inclif obj #<ClassIncludingFoo:0x000001020 ClassIncludingFoo
62
+ #
63
+ # --------------- ClassInclFooInSgtnCl
64
+ # meth_in_class obj #<ClassInclFooInSgtnCl:0x000001 ClassInclFooInSgtnCl
65
+ # self_meth_in_class class ClassInclFooInSgtnCl Class #<Class:ClassInclFooInSgtnCl>
66
+ # meth_in_sgtn_cl class ClassInclFooInSgtnCl Class #<Class:ClassInclFooInSgtnCl>
67
+ # self_meth_sgtn_cl sgtn_cl #<Class:ClassInclFooInSgtnCl> Class
68
+ # module_meth_foo class ClassInclFooInSgtnCl Class #<Class:ClassInclFooInSgtnCl>
69
+ # module_meth_inclif class ClassInclFooInSgtnCl Class #<Class:ClassInclFooInSgtnCl>
70
+ #
71
+ # --------------- ClassExtFooInSgtnCl
72
+ # meth_in_class obj #<ClassExtFooInSgtnCl:0x0000010 ClassExtFooInSgtnCl
73
+ # self_meth_in_class class ClassExtFooInSgtnCl Class #<Class:ClassExtFooInSgtnCl>
74
+ # meth_in_sgtn_cl class ClassExtFooInSgtnCl Class #<Class:ClassExtFooInSgtnCl>
75
+ # self_meth_sgtn_cl sgtn_cl #<Class:ClassExtFooInSgtnCl> Class
76
+ # module_meth_foo sgtn_cl #<Class:ClassExtFooInSgtnCl> Class
77
+ # module_meth_inclif sgtn_cl #<Class:ClassExtFooInSgtnCl> Class
78
+
79
+ def compare_methods(klass_a, klass_b)
80
+ puts( "%-31s\tobj \t%s" % [klass_a.to_s[0..30], (klass_a.new.public_methods - Object.new.public_methods).inspect]) if klass_a.respond_to?(:new)
81
+ puts( "%-31s\tklass \t%s" % [klass_a.to_s[0..30], (klass_a.public_methods - klass_b.public_methods).inspect])
82
+ puts( "%-31s\tsgtncl\t%s" % [klass_a.to_s[0..30], (klass_a.singleton_class.public_methods - klass_b.singleton_class.public_methods).inspect])
83
+ puts '---------------'
84
+ end
85
+
86
+ def show_anc_chain(klass_a)
87
+ puts ['klass ', klass_a.ancestors .reject{|kl| kl.name =~ /^(RSpec|PP::ObjectMixin)/ }].join("\t")
88
+ puts ['sgtncl', klass_a.singleton_class.ancestors.reject{|kl| kl.name =~ /^(RSpec|PP::ObjectMixin)/ }].join("\t")
89
+ end
90
+
91
+ def show_method_selfness(klass_a)
92
+ puts "\n", klass_a
93
+ on = (klass_a.respond_to?(:new) ? { 'obj' => klass_a.new } : {})
94
+ on['class'] = klass_a
95
+ on['sgtn_cl'] = klass_a.singleton_class
96
+ [
97
+ :meth_in_class,
98
+ :self_meth_in_class,
99
+ :meth_in_sgtn_cl,
100
+ :self_meth_sgtn_cl,
101
+
102
+ :explicit_method_extif,
103
+ :explicit_method_foo,
104
+ :module_meth_extif,
105
+ :module_meth_foo,
106
+ :explicit_method_inclif,
107
+ :module_meth_inclif,
108
+ :module_self_extif,
109
+ :module_self_foo,
110
+ :module_self_inclif,
111
+ ].each do |meth|
112
+ on.each do |kind, obj|
113
+ next unless obj.respond_to?(meth)
114
+ puts( "%-31s\t%-7s\t%-31s\t%-31s\t%s" % [meth, kind, obj.send(meth)].flatten.map{|x| x.to_s[0..30] } )
115
+ end
116
+ end
117
+ end
118
+
119
+
120
+ class SimpleClass
121
+ class << self
122
+ def self.self_meth_sgtn_cl() ; [self, self.class, ((self.is_a?(Class) && self.ancestors.first == self) ? self.singleton_class : nil)] ; end
123
+ def meth_in_sgtn_cl() ; [self, self.class, ((self.is_a?(Class) && self.ancestors.first == self) ? self.singleton_class : nil)] ; end
124
+ end
125
+ def self.self_meth_in_class() ; [self, self.class, ((self.is_a?(Class) && self.ancestors.first == self) ? self.singleton_class : nil)] ; end
126
+ def meth_in_class() ; [self, self.class, ((self.is_a?(Class) && self.ancestors.first == self) ? self.singleton_class : nil)] ; end
127
+ end
128
+
129
+ module IncludedInFoo
130
+ def self.module_self_inclif() ; [self, self.class, ((self.is_a?(Class) && self.ancestors.first == self) ? self.singleton_class : nil)] ; end
131
+ def module_meth_inclif() ; [self, self.class, ((self.is_a?(Class) && self.ancestors.first == self) ? self.singleton_class : nil)] ; end
132
+ end
133
+
134
+ module ExtendedInFoo
135
+ def self.module_self_extif() ; [self, self.class, ((self.is_a?(Class) && self.ancestors.first == self) ? self.singleton_class : nil)] ; end
136
+ def module_meth_extif() ; [self, self.class, ((self.is_a?(Class) && self.ancestors.first == self) ? self.singleton_class : nil)] ; end
137
+ end
138
+
139
+ module Foo
140
+ include IncludedInFoo
141
+ extend ExtendedInFoo
142
+ def self.module_self_foo() ; [self, self.class, ((self.is_a?(Class) && self.ancestors.first == self) ? self.singleton_class : nil)] ; end
143
+ def module_meth_foo() ; [self, self.class, ((self.is_a?(Class) && self.ancestors.first == self) ? self.singleton_class : nil)] ; end
144
+ end
145
+
146
+ class ClassExtendingFoo < SimpleClass
147
+ extend Foo
148
+ class << self
149
+ end
150
+ end
151
+
152
+ class ClassIncludingFoo < SimpleClass
153
+ include Foo
154
+ class << self
155
+ end
156
+ end
157
+
158
+ class ClassInclFooInSgtnCl < SimpleClass
159
+ class << self
160
+ include Foo
161
+ end
162
+ end
163
+
164
+ class ClassExtFooInSgtnCl < SimpleClass
165
+ class << self
166
+ extend Foo
167
+ end
168
+ end
169
+
170
+ [Foo,
171
+ ClassExtendingFoo, ClassIncludingFoo, ClassInclFooInSgtnCl,
172
+ ClassExtFooInSgtnCl,
173
+ ].each do |kl|
174
+ show_method_selfness(kl)
175
+ compare_methods(kl , Class)
176
+ end
@@ -0,0 +1,278 @@
1
+
2
+ #### Types, Properties and Individuals
3
+
4
+ There are things in the world that we know as "restaurants" -- for example, in
5
+ Austin you can't to better than "Torchy's Tacos". It, like all restaurants, have
6
+ some notion of a "name" and a "menu".
7
+
8
+ * "Restaurant" is a _*type*_: a group sharing common properties.
9
+ * "Torchy's Tacos" is an _*individual*_.
10
+ * "name" and "menu" are _*properties*_ of a type.
11
+ * An individual has _*values*_ for those properties: the restaurant "Torchy's
12
+ Taco's" has a menu which includes the "Dirty Sanchez" (no, really.)
13
+
14
+ In ruby, we might represent this as follows (greatly simplified):
15
+
16
+ ```ruby
17
+ class Restaurant
18
+ field :name, String
19
+ field :menu, Array, :items => String
20
+ field :geo, GeoCoordinates
21
+ end
22
+ yum = Restaurant.receive({
23
+ :name => "Torchy's Taco's",
24
+ :menu => ["Dirty Sanchez", "Fried Avocado"] ,
25
+ :geo => { :longitude => 30.295, :latitude => -97.745 },
26
+ })
27
+ yum.name # "Torchy's Taco's"
28
+ yum.geo.latitude # -97.745
29
+ ```
30
+
31
+ * The _type_ is implemented with the *class* `Restaurant`.
32
+ * Its _properties_ are described as `field`s, each with its own type.
33
+ * An _individual_ is represented by an *instance* of that class.
34
+
35
+ Probably most of this was boring and/or familiar. To clear the rest of the
36
+ straightforward stuff out of the way,
37
+
38
+ * A "Restaurant" is a specialization of a "Place": it has a "geo" property, just
39
+ as "Bowling Alley" and "Mountain" do. We'll represent that as straightforward
40
+ inheritance: `Restaurant` is a subclass of `Place`.
41
+ * A "Place" is itself a specialization of "Tangible Things", for which we will
42
+ use the ruby class `Thing`.
43
+ * A "Restaurant" is actually also a "Business", itself also a specialization of
44
+ "Thing". Using a trick you'll hear more about below, we can actually handle
45
+ this easily using module inclusion (`include`).
46
+
47
+ So this gives us (still simplified, but less so):
48
+
49
+ ```ruby
50
+ class Thing
51
+ field :name, String
52
+ end
53
+
54
+ class Place < Thing
55
+ field :geo, GeoCoordinates
56
+ end
57
+
58
+ class Business < Thing
59
+ field :opening_hours, Duration
60
+ end
61
+
62
+ class Restaurant < Place
63
+ include Meta::BusinessType
64
+ field :menu, Array, :items => String
65
+ end
66
+
67
+ class Mountain < Place
68
+ field :height_of_peak, Integer, :doc => "In meters"
69
+ end
70
+
71
+ yum = Restaurant.receive({
72
+ :name => "Torchy's Taco's",
73
+ :menu => ["Dirty Sanchez", "Fried Avocado"] ,
74
+ :geo => { :longitude => 30.295, :latitude => -97.745 },
75
+ })
76
+ yum.name # "Torchy's Taco's"
77
+ yum.geo.latitude # -97.745
78
+ ```
79
+
80
+ ### RecordModel
81
+
82
+ Including `Icss::Meta::RecordModel` gives you these superpowers:
83
+
84
+ * `.field` -- create accessors and receivers for a property
85
+ * `.fields` and `.field_names` -- describe the fields. (field names is guaranteed in order.)
86
+ * `.receive` -- create an instance given a (nested) hash of values; calls
87
+ in turn `.receive` on each field's type.
88
+
89
+ The class and instance behavior of `Place` (a direct child class of `Thing`) is
90
+ effectively (apart from one important detail) as follows:
91
+
92
+ Behavior Model Instance Methods Type Class Methods
93
+ -------------- ----------------- ----------------- --------------------- ------------------------
94
+ (superclasses) Object ...many... ...many...
95
+ structure type: Meta::BaseType .metamodel,.to_schema
96
+ Meta::RecordModel #receive! Meta::RecordType .receive, .field, .fields
97
+ parent models: Thing #name,#receive_name,&c
98
+ model klass: Place #geo, #receive_geo,&c
99
+
100
+ Note carefully the following relationships:
101
+
102
+ torchys = Place.new({...})
103
+
104
+ # true:
105
+ torchys.is_a? Thing
106
+ torchys.is_a? Meta::RecordModel
107
+ torchys.is_a? Object
108
+
109
+ # not true:
110
+ torchys.is_a? Meta::RecordType
111
+
112
+ # true:
113
+ Place < Thing
114
+ Place < Meta::RecordModel
115
+ Place.is_a? Meta::RecordType
116
+
117
+ ### metamodel
118
+
119
+ Here's the important detail promised earlier about inheritance. We actually slip
120
+ in a little overlay module, the `metamodel`:
121
+
122
+ Behavior Model Instance Methods Type Class Methods
123
+ -------------- ----------------- ----------------- --------------------- ------------------------
124
+ (superclasses) Object ...many... ...many...
125
+ structure type: Meta::BaseType .metamodel,.to_schema
126
+ Meta::RecordModel #receive! Meta::RecordType .receive, .field, .fields
127
+ parent models: Meta::ThingModel #name,#receive_name,&c
128
+ Thing
129
+ metamodel: Meta::PlaceModel #geo, #receive_geo,&c
130
+ model klass: Place <--- call super, override things, go crazy --->
131
+
132
+ This lets us (a) enable multiple inheritance, and (b) let a class declare a
133
+ field but only override *some* of its behavior (i.e. still letting you use
134
+ `super`).
135
+
136
+ We scribble all the instance methods for a place onto the `Meta::PlaceModel`
137
+ *module*. When the `Place` class calls `include Meta::PlaceModel`, it acquires
138
+ the prescribed behavior, but can override as required. Furthermore, any other
139
+ class can accomplish "Placehood" by *also* `include`ing the `Meta::PlaceModel`
140
+ metamodel.
141
+
142
+ The infochimps API uses this to good effect, where data sets often introduce
143
+ behavior across an entire universe of otherwise distinct types. Objects from
144
+ Twitter would generically be represented as `Social::Person` (an account),
145
+ `Event::UserTweets` (messages), and subclasses of `Geo::Place` (place checkins).
146
+ All have implied geolocations, and could naturally coexist on a map. Other data
147
+ sources imply other attributes: not just foursquare and yelp but also news
148
+ articles and stock filings. We can inject Twitter geolocation information
149
+ without subclassing everything in sight
150
+ (`RestaurantOnTwitterThatIsAlsoABranchOfAPubliclyTradedCompany`) or requiring
151
+ the prolix madness of an RDF graph
152
+ (`<http://ding.us/not/actually/a/url/torchys_tacos>
153
+ <http://some.rand.om/ontology#hasPropertyYouCantRememberSpellingOf>
154
+ 34^^<http://with.com/value/that/is/twelve/miles/long/even/though/its/a/goddamn/integer>`)
155
+
156
+ ### Model vs Type vs Schema
157
+
158
+ Now we want a way to programmatically describe and generate types. If we're not
159
+ careful, we'll end up going mad discussing how to type up the typical type of
160
+ type `Type` properly having property `schema` of type `RecordSchema`, or (even
161
+ worse) wind up in the omphaloskeptic realm of Semantic Web Ontologies. Eff that
162
+ -- this is called the Infochimps Stupid Schema, precisely to remind you it's
163
+ just a data model, we should really just relax.
164
+
165
+ However, if you must spend some time to understand how a small amount of clever
166
+ enables that greater and more productive recklessness, a certain measure of
167
+ careful terminology is required.
168
+
169
+ The regular kind of _type_ we've been discussing is a `Model` -- its individuals
170
+ correspond to things in the real world. A `Thing` is a model, and most things
171
+ are `Thing`s. `Integer` and `IsFriendOf` (a kind of `Relation`) are both models
172
+ but not `Thing`s. Recapping what you know,
173
+
174
+ - **instances** of a model represent individuals;
175
+ - **instance methods** of a model characterize a type's behavior, specifically
176
+ - **fields**, which represent properties and accept values describing the individual.
177
+
178
+ We also want to be able to discuss properties and impart shared behavior among
179
+ types themselves. For example,
180
+
181
+ - a `Restaurant` place, a `WeatherObservation` statistical summary, and a
182
+ `IsFriendOf` relation are quite different at the model level -- they
183
+ represent respectively a tangible Thing, an intangible Thing and a
184
+ non-Thing. But all of them are structured records, with fields an so on.
185
+
186
+ - A `FilePath`, a `RawJsonBlob` and a `Url` are all represented as simple
187
+ strings, but each has a distinct semantic meaning, and each implies rules
188
+ that would allow a computer to validate an instance's contents look right.
189
+
190
+ - We of course use `Array` to hold collections of items, and `Hash` to hold
191
+ labelled collections of items. Wherever possible, we'd like to specify
192
+ "Array of MusicAlbum" and so forth.
193
+
194
+ Let us now introduce the `Schema`, which characterizes commonalities among `Model` types.
195
+
196
+
197
+ <"Torchy's" instance> manufactured by Restaurant
198
+ <"Torchy's">.menu() instance methods from Restaurant
199
+
200
+ <Restaurant class> described by RecordSchema
201
+ <Restaurant>.field_names class methods from RecordSchema
202
+
203
+ A person who find this sort of thing elegant might point out that the
204
+ properties of a Schema describe specifics of a model; given values
205
+ characterizing those specifics, the Schema manufactures an individual
206
+ Model. That is: a Schema is a type for individual Types.
207
+
208
+ But if you're like me you're better off just remembering
209
+
210
+ > Schema == classes and class methods for a type;
211
+ > Model == instances and instance methods for a type.
212
+
213
+ In implementation land, a Schema is a class, with a distinct existence from its
214
+ type. If you're working with types on their own, you want schema objects;
215
+
216
+ (In fact, you may discover to your horror that a Schema is itself a RecordType:
217
+ it's turtles all the way down.)
218
+
219
+ ### Structured Schema (Array, Map, Enum, Fixed)
220
+
221
+ #### Meta::ArraySchema
222
+
223
+ The schema prescribes the *class* of items the `Array` should hold: for example,
224
+ an album's `tracks` property is an array of `MusicRecordings`.
225
+
226
+ Behavior Model Instance Methods Type Class Methods
227
+ -------------- ----------------- ----------------- --------------------- ------------------------
228
+ (superclasses) Object,Array ...many... ...many...
229
+ structure type: Meta::BaseType .metamodel,.to_schema
230
+ Meta::ArrayType .receive
231
+ metamodel: --
232
+ model klass: ArrayOfInt (directly on class) .items
233
+
234
+ Schema: class Meta::ArraySchema
235
+
236
+ For a record model, an individual schema typically doesn't have much to say
237
+ about the model class itself. Since models are usually real-world objects we'd
238
+ like to handle generically, we focus our interest on fields and instance
239
+ methods. An `Array` is something whose *individuals* should be completely
240
+ generic: we care about all the interesting things in a list, not the bag that
241
+ holds them. The schema for an array (as well as for Hash, Fixed and Enum)
242
+ prescribes information about the *type* not the *model*
243
+
244
+ Now after I just sold you on the virtues of a metamodel in the case of a record,
245
+ you might expect an analogous module to extend the class. There's no practical
246
+ benefit or need for this, though, so we currently just scribble the .items class
247
+ method directly onto the class.
248
+
249
+ #### Meta::HashSchema
250
+
251
+ Behavior Model Instance Methods Type Class Methods
252
+ -------------- ----------------- ----------------- --------------------- ------------------------
253
+ (superclasses) Object,Hash ...many... ...many...
254
+ structure type: Meta::BaseType .metamodel,.to_schema
255
+ Meta::HashType .receive
256
+ metamodel: --
257
+ model klass: HashOfYourMom (directly on class) .values
258
+
259
+ #### Meta::FixedSchema
260
+
261
+ Behavior Model Instance Methods Type Class Methods
262
+ -------------- ----------------- ----------------- --------------------- ------------------------
263
+ (superclasses) Object,String ...many... ...many...
264
+ structure type: Meta::BaseType .metamodel,.to_schema
265
+ Meta::FixedType .receive
266
+ metamodel: --
267
+ model klass: Fixed16 (directly on class) .bytesize
268
+
269
+ #### Meta::EnumSchema
270
+
271
+ Behavior Model Instance Methods Type Class Methods
272
+ -------------- ----------------- ----------------- --------------------- ------------------------
273
+ (superclasses) Object,String ...many... ...many...
274
+ structure type: Meta::BaseType .metamodel,.to_schema
275
+ Meta::EnumType .receive
276
+ metamodel: --
277
+ model klass: CountryCode (directly on class) .symbols
278
+