adaptation 0.0.6 → 0.0.7

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.
data/CHANGELOG CHANGED
@@ -6,3 +6,5 @@
6
6
  - migrated to ActiveRrecord validations
7
7
  * 0.0.6
8
8
  - forced to use ROXML version 1.2
9
+ * 0.0.7
10
+ - Documentation update + Message class clean up
data/README CHANGED
@@ -36,14 +36,15 @@ adapting a database-backed application.
36
36
  By default this will try to subscribe to a mom listening on localhost:8080, using port
37
37
  8081 to subscribe. These values can be changed editing <tt>config/mom.yml</tt>. In mom.yml
38
38
  you can also specify wich topics your adaptor is interested in.
39
- 4. Start developing your adaptor, probably generating __Adaptors__, __Messages__ and __Models__.
39
+ 4. Start developing your adaptor, probably generating __Adaptors__[link:../rdoc/classes/Adaptation/Adaptor.html], __Messages__[link:../rdoc/classes/Adaptation/Message.html]
40
+ and __Models__[http://api.rubyonrails.org/classes/ActiveRecord/Base.html].
40
41
 
41
42
 
42
43
  == Moms
43
44
 
44
45
  By default, Adaptation will try to use druby to execute the built-in Ruby mom. This
45
46
  mom is suitable for development, but not for production. For a production environment
46
- a more stable solution like Xmlblaster should be chosen.
47
+ a more stable solution like Xmlblaster[http://www.xmlblaster.org] should be chosen.
47
48
 
48
49
 
49
50
  == Description of contents
@@ -3,7 +3,7 @@ module Adaptation
3
3
  #
4
4
  #Adaptation::Adaptor is the base class for those classes containing the logic to be executed when a message is read through the mom.
5
5
  #
6
- #Each class extending Adaptation::Adaptor must implement the _process_ function, using it as the main entry point for the logic to be executed when a message arrives. The name of the class extending Adaptation::Message associates the class with the one to be executed when a message arrives. Ie. if a message is received with a root element named _<hello>_, adaptation will search for a class extending Adaptation::Adaptor named _HelloAdaptor_.
6
+ #Each class extending Adaptation::Adaptor must implement the _process_ function, using it as the main entry point for the logic to be executed when a message arrives. The name of the class extending Adaptation::Message associates the class with the one to be executed when a message arrives. Ie. if a message is received with a root element named <hello>, adaptation will search for a class extending Adaptation::Adaptor named _HelloAdaptor_.
7
7
  #
8
8
  #<i>Adaptation::Adaptors</i> (classes extending Adaptation::Adaptor) must be stored under <i>app/adaptors_name</i> in the adaptation file tree. This is done automatically when an adaptor is generated using adaptation built-in generator:
9
9
  # script/generate adaptor hello
@@ -15,76 +15,83 @@ module Adaptation
15
15
  #
16
16
  #* Automated mapping between classes and xml attributes and elements.
17
17
  #
18
- # class Contact < Adaptation::Message
19
- # has_one :text, :name
20
- # end
18
+ # class Contact < Adaptation::Message
19
+ # has_one :text, :name
20
+ # end
21
21
  #
22
- # ...is automatically mapped to a xml structure with root element named "contact", such as:
22
+ # ...is automatically mapped to a xml structure with root element named "contact", such as:
23
23
  #
24
- # <contact>
25
- # <name>Name</name>
26
- # </contact>
24
+ # <contact>
25
+ # <name>Name</name>
26
+ # </contact>
27
27
  #
28
- # ...which gives Contact#name
28
+ # ...which gives Contact#name
29
29
  #
30
30
  #
31
31
  #* Associations between objects controlled by simple meta-programming macros.
32
32
  #
33
- # class Agenda < Adaptation::Message
34
- # has_one :attribute, :type
35
- # has_one :text, :owner
36
- # has_many :contacts
37
- # end
33
+ # class Agenda < Adaptation::Message
34
+ # has_one :attribute, :type
35
+ # has_one :text, :owner
36
+ # has_many :contacts
37
+ # end
38
38
  #
39
- # class Contact < Adaptation::Message
40
- # has_one :text, :name
41
- # end
39
+ # class Contact < Adaptation::Message
40
+ # has_one :text, :name
41
+ # end
42
42
  #
43
- # Agenda class would be mapping the following xml structure:
43
+ # Agenda class would be mapping the following xml structure:
44
44
  #
45
- # <agenda type="...">
46
- # <owner>...</owner>
47
- # <contact>...</contact>
48
- # <contact>...</contact>
49
- # ...
50
- # </agenda>
45
+ # <agenda type="...">
46
+ # <owner>...</owner>
47
+ # <contact>...</contact>
48
+ # <contact>...</contact>
49
+ # ...
50
+ # </agenda>
51
51
  #
52
- # while the _Contact_ class would map:
52
+ # while the _Contact_ class would map:
53
53
  #
54
- # <contact>
55
- # <name>...</name>
56
- # </contact>
54
+ # <contact>
55
+ # <name>...</name>
56
+ # </contact>
57
57
  #
58
- # The Contact class is a partial, a structure included in a bigger structure, so its
59
- # file name starts with an underscore: _contact.rb
58
+ # The Contact class is a partial, a structure included in a bigger structure, so its
59
+ # file name starts with an underscore: _contact.rb
60
60
  #
61
- # We could have wrote Agenda like this, to change the contacts container name:
61
+ # We could have wrote Agenda like this, to change the contacts container name:
62
62
  #
63
- # class Agenda < Adaptation::Message
64
- # has_one :attribute, :type
65
- # has_one :text, :owner
66
- # has_many :contacts, :in => :contact_list
67
- # end
63
+ # class Agenda < Adaptation::Message
64
+ # has_one :attribute, :type
65
+ # has_one :text, :owner
66
+ # has_many :contacts, :in => :contact_list
67
+ # end
68
68
  #
69
- # and Contact like:
69
+ # and Contact like:
70
70
  #
71
- # class Contact < Adaptation::Message
72
- # belongs_to :contact_list
73
- # has_one :text, :name
74
- # end
71
+ # class Contact < Adaptation::Message
72
+ # has_one :text, :name
73
+ # end
75
74
  #
76
- # Then the mapping in Agenda would be:
75
+ # Then the mapping in Agenda would be:
77
76
  #
78
- # <agenda type="...">
79
- # <owner>...</owner>
80
- # <contact_list>
81
- # <contact>...</contact>
82
- # </contact_list>
83
- # </agenda>
77
+ # <agenda type="...">
78
+ # <owner>...</owner>
79
+ # <contact_list>
80
+ # <contact>...</contact>
81
+ # </contact_list>
82
+ # </agenda>
84
83
  #
85
84
  #
86
85
  #* Validation rules.
87
86
  #
87
+ # Adaptation::Message uses {ActiveRecord::Validations}[http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html].
88
+ # This means that {ActiveRecord validations}[http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html]
89
+ # can be used in an Adaptation::Message object and that custom validations can be easily implemented.
90
+ #
91
+ # Adaptation::Message also defines some {custom validation methods}[link:../rdoc/classes/ActiveRecord/Validations/ClassMethods.html].
92
+ #
93
+ # Validations usage example:
94
+ #
88
95
  # class Agenda < Adaptation::Message
89
96
  # has_one :attribute, :type
90
97
  # has_one :text, :owner
@@ -97,107 +104,46 @@ module Adaptation
97
104
 
98
105
  @@classes_with_brothers = []
99
106
  cattr_reader :classes_with_brothers
100
- cattr_reader :validations
101
107
  cattr_reader :objects
102
108
 
103
109
  include Validateable
104
110
  include ROXML
105
111
 
106
- def to_hash
107
- s = self.to_yaml
108
- h = YAML::load s[4..s.length].downcase.gsub(/\!ruby\/object:.* /,"")
109
- end
110
-
111
- def has_son?(son)
112
- search_symbol_in_hash self.to_hash, son
113
- end
114
-
115
- def search_symbol_in_hash hash, symbol
116
- found = false
117
- hash.each_pair do |k,v|
118
- if (k.to_sym == symbol)
119
- found = true
120
- break
121
- end
122
- end
123
-
124
- if !found
125
- hash.each_pair do |k,v|
126
- found = search_symbol_in_hash(v, symbol) if v.is_a?(Hash)
127
- break if found
128
- end
129
- end
130
-
131
- found
132
- end
133
-
134
- def search_paths(symbol)
135
- get_paths_from_hash self.to_hash, symbol
136
- end
137
-
138
- def search_objects(symbol)
139
- objects = []
140
- paths = search_paths(symbol)
141
- paths.each do |path|
142
- objects << get_object(path)
143
- end
144
- objects.flatten
145
- end
146
-
147
- def get_paths_from_hash hash, symbol, root_path = ""
148
- paths = []
149
- hash.each_pair do |k,v|
150
- if (k.to_sym == symbol)
151
- paths << "#{root_path}.#{k}" unless paths.include?("#{root_path}.#{k}")
152
- end
153
- end
154
-
155
- hash.each_pair do |k,v|
156
- if v.is_a?(Hash)
157
- son_paths = get_paths_from_hash(v, symbol, k)
158
- son_paths.each do |sp|
159
- paths << "#{root_path}.#{sp}" unless paths.include?("#{root_path}.#{sp}")
160
- end
161
- elsif v.is_a?(Array)
162
- v.each do |h|
163
- son_paths = get_paths_from_hash(h, symbol, k)
164
- son_paths.each do |sp|
165
- paths << "#{root_path}.#{sp}" unless paths.include?("#{root_path}.#{sp}")
166
- end
167
- end
168
- end
169
- end
170
-
171
- paths.map!{|p|
172
- if p.first == "."
173
- p[1..p.length]
174
- else
175
- p
176
- end
177
- }
178
-
179
- paths
180
- end
181
-
182
- def get_object path
183
- subpaths = path.split(".")
184
- objects = [self]
185
- subpaths.each do |s|
186
- array = []
187
- objects.each do |o|
188
- if o.is_a?(Array)
189
- o.each do |os|
190
- array << os.send(s.to_sym)
191
- end
192
- else
193
- array << o.send(s.to_sym)
194
- end
195
- end
196
- objects = array
197
- end
198
- objects
199
- end
200
112
 
113
+ # Maps an xml attribute or element.
114
+ #
115
+ # Example 1: Mapping an attribute:
116
+ # <xml attr="something"/>
117
+ #
118
+ # class Xml < Adaptation::Message
119
+ # has_one :attribute, :attr
120
+ # end
121
+ #
122
+ # Example 2: Mapping an xml element that contains text.
123
+ # <xml>
124
+ # <element>something</element>
125
+ # </xml>
126
+ #
127
+ # class Xml < Adaptation::Message
128
+ # has_one :text, :element
129
+ # end
130
+ #
131
+ # Example 3: Mapping an xml element that contains xml data.
132
+ # <xml>
133
+ # <element>
134
+ # <subelement>something</subelement>
135
+ # </element>
136
+ # </xml>
137
+ #
138
+ # class Xml < Adaptation::Message
139
+ # has_one :object, :element
140
+ # end
141
+ #
142
+ # The element xml structure would be described in a separate partial file <em>_element.rb</em>:
143
+ # class Element < Adaptation::Message
144
+ # has_one :text, :subelement
145
+ # end
146
+ #
201
147
  def self.has_one *symbols
202
148
  xml_tag = symbols[0]
203
149
  case xml_tag
@@ -233,6 +179,30 @@ module Adaptation
233
179
  end
234
180
  end
235
181
 
182
+ # Maps an xml element text.
183
+ #
184
+ # If an element contains plain text data and has not been declared in its parent element with
185
+ # <em>has_one :text</em>, it must declare itself with <em>has_text</em> to be able to contain text.
186
+ #
187
+ # Example:
188
+ #
189
+ # <xml>
190
+ # <element attr="something">something more</element>
191
+ # </xml>
192
+ #
193
+ # class Xml < Adaptation::Message
194
+ # has_one :object, :element
195
+ # end
196
+ #
197
+ # In this case element cannot be declared just like <em>has_one :text</em>, because it also has
198
+ # an attribute. It must be declared like <em>has_one :object</em>. Then the element declares itself
199
+ # in a file called <em>_element.rb</em> like this:
200
+ #
201
+ # class Element < Adaptation::Message
202
+ # has_one :attribute, :attr
203
+ # has_text
204
+ # end
205
+ #
236
206
  def self.has_text
237
207
  if @has_text.nil?
238
208
  @has_text = true
@@ -240,20 +210,34 @@ module Adaptation
240
210
  end
241
211
  end
242
212
 
243
- def self.has_brothers?
244
- brothers_containers = []
245
- @@classes_with_brothers.each do |cwb|
246
- if cwb =~ /^#{self.to_s}:/
247
- brothers_containers << cwb
248
- end
249
- end
250
- if brothers_containers.empty?
251
- return nil
252
- else
253
- return brothers_containers
254
- end
255
- end
256
-
213
+ # Maps many xml subelements.
214
+ #
215
+ # Example 1:
216
+ #
217
+ # <xml>
218
+ # <subelement>...</subelement>
219
+ # <subelement>...</subelement>
220
+ # <subelement>...</subelement>
221
+ # </xml>
222
+ #
223
+ # class Xml < Adaptation::Message
224
+ # has_many :subelements
225
+ # end
226
+ #
227
+ # Example 2:
228
+ #
229
+ # <xml>
230
+ # <subelement_list>
231
+ # <subelement>...</subelement>
232
+ # <subelement>...</subelement>
233
+ # <subelement>...</subelement>
234
+ # </subelement_list>
235
+ # </xml>
236
+ #
237
+ # class Xml < Adaptation::Message
238
+ # has_many :subelements, :in => :subelement_list
239
+ # end
240
+ #
257
241
  def self.has_many *options
258
242
  configuration = {}
259
243
  configuration.update(options.pop) if options.last.is_a?(Hash)
@@ -285,14 +269,22 @@ module Adaptation
285
269
  end
286
270
  end
287
271
 
288
- def self.get_class_object(searched_class)
272
+ def self.get_class_object(searched_class) #:nodoc:
289
273
  Object.const_get searched_class
290
274
  end
291
-
275
+
276
+ # This is the constructor. Instead of <em>Adaptation::Message#new</em>, an <em>Adaptation::Message</em>
277
+ # instance is created like this:
278
+ #
279
+ # m = Adaptation::Message.to_object("<xml>valid xml</xml>")
280
+ #
292
281
  def self.to_object xml_message
293
282
  parse xml_message
294
283
  end
295
284
 
285
+ # Checks if an Adaptation::Message object passes all validations.
286
+ #
287
+ # It is an alias for <em>Adaptation::Message#valid?</em>
296
288
  def check
297
289
  valid?
298
290
  end
@@ -19,6 +19,27 @@ module ActiveRecord
19
19
  module Validations
20
20
  module ClassMethods
21
21
 
22
+ # Validates whether the value of the specified xml attribute/element is the expected one.
23
+ #
24
+ # Example 1:
25
+ #
26
+ # <leftwing side="left"/>
27
+ #
28
+ # class Leftwing < Adaptation::Message
29
+ # has_one :attribute, :side
30
+ #
31
+ # validates_value_of :side, "left"
32
+ # end
33
+ #
34
+ # Example 2:
35
+ # <bird><wings><wing side="left"/><wing side="right"/></wings></bird>
36
+ #
37
+ # class Bird < Adaptation::Message
38
+ # has_many :wings
39
+ #
40
+ # validates_value_of :side, "left", :in => :wings
41
+ # end
42
+ #
22
43
  def validates_value_of(*attr_names)
23
44
  configuration = {
24
45
  :message => 'value doesn\'t exist'
@@ -44,7 +65,18 @@ module ActiveRecord
44
65
  end
45
66
  end
46
67
  end
47
-
68
+
69
+ # Validates whether the value of the specified xml attribute/element has a valid email format.
70
+ #
71
+ # Example:
72
+ # <contact email="mail@xample.com">...</contact>
73
+ #
74
+ # class Contact < Adaptation::Message
75
+ # has_one :attribute, :email
76
+ #
77
+ # validates_as_email :email
78
+ # end
79
+ #
48
80
  def validates_as_email(*attr_names)
49
81
  configuration = {
50
82
  :message => 'is an invalid email',
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: adaptation
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.6
7
- date: 2008-07-24 00:00:00 +02:00
6
+ version: 0.0.7
7
+ date: 2008-11-24 00:00:00 +01:00
8
8
  summary: Framework to facilitate web-application interaction.
9
9
  require_paths:
10
10
  - lib
@@ -15,7 +15,7 @@ description: Adaptation is a framework for building "adaptors" for web-applicati
15
15
  autorequire: adaptation
16
16
  default_executable: adaptation
17
17
  bindir: bin
18
- has_rdoc: false
18
+ has_rdoc: true
19
19
  required_ruby_version: !ruby/object:Gem::Version::Requirement
20
20
  requirements:
21
21
  - - ">"