tp-blather 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. data/.autotest +13 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +19 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +8 -0
  6. data/CHANGELOG.md +249 -0
  7. data/Gemfile +4 -0
  8. data/Guardfile +5 -0
  9. data/LICENSE +22 -0
  10. data/README.md +413 -0
  11. data/Rakefile +20 -0
  12. data/TODO.md +2 -0
  13. data/blather.gemspec +51 -0
  14. data/examples/certs/README +20 -0
  15. data/examples/certs/ca-bundle.crt +3987 -0
  16. data/examples/echo.rb +19 -0
  17. data/examples/execute.rb +17 -0
  18. data/examples/ping_pong.rb +38 -0
  19. data/examples/print_hierarchy.rb +77 -0
  20. data/examples/rosterprint.rb +15 -0
  21. data/examples/stream_only.rb +28 -0
  22. data/examples/trusted_echo.rb +21 -0
  23. data/examples/xmpp4r/echo.rb +36 -0
  24. data/lib/blather.rb +112 -0
  25. data/lib/blather/cert_store.rb +53 -0
  26. data/lib/blather/client.rb +95 -0
  27. data/lib/blather/client/client.rb +345 -0
  28. data/lib/blather/client/dsl.rb +320 -0
  29. data/lib/blather/client/dsl/pubsub.rb +174 -0
  30. data/lib/blather/core_ext/eventmachine.rb +125 -0
  31. data/lib/blather/core_ext/ipaddr.rb +20 -0
  32. data/lib/blather/errors.rb +69 -0
  33. data/lib/blather/errors/sasl_error.rb +44 -0
  34. data/lib/blather/errors/stanza_error.rb +110 -0
  35. data/lib/blather/errors/stream_error.rb +84 -0
  36. data/lib/blather/file_transfer.rb +107 -0
  37. data/lib/blather/file_transfer/ibb.rb +68 -0
  38. data/lib/blather/file_transfer/s5b.rb +114 -0
  39. data/lib/blather/jid.rb +141 -0
  40. data/lib/blather/roster.rb +118 -0
  41. data/lib/blather/roster_item.rb +146 -0
  42. data/lib/blather/stanza.rb +167 -0
  43. data/lib/blather/stanza/disco.rb +32 -0
  44. data/lib/blather/stanza/disco/capabilities.rb +161 -0
  45. data/lib/blather/stanza/disco/disco_info.rb +205 -0
  46. data/lib/blather/stanza/disco/disco_items.rb +134 -0
  47. data/lib/blather/stanza/iq.rb +144 -0
  48. data/lib/blather/stanza/iq/command.rb +339 -0
  49. data/lib/blather/stanza/iq/ibb.rb +86 -0
  50. data/lib/blather/stanza/iq/ping.rb +50 -0
  51. data/lib/blather/stanza/iq/query.rb +53 -0
  52. data/lib/blather/stanza/iq/roster.rb +185 -0
  53. data/lib/blather/stanza/iq/s5b.rb +208 -0
  54. data/lib/blather/stanza/iq/si.rb +415 -0
  55. data/lib/blather/stanza/iq/vcard.rb +149 -0
  56. data/lib/blather/stanza/message.rb +428 -0
  57. data/lib/blather/stanza/message/muc_user.rb +119 -0
  58. data/lib/blather/stanza/muc/muc_user_base.rb +54 -0
  59. data/lib/blather/stanza/presence.rb +172 -0
  60. data/lib/blather/stanza/presence/c.rb +100 -0
  61. data/lib/blather/stanza/presence/muc.rb +35 -0
  62. data/lib/blather/stanza/presence/muc_user.rb +147 -0
  63. data/lib/blather/stanza/presence/status.rb +218 -0
  64. data/lib/blather/stanza/presence/subscription.rb +100 -0
  65. data/lib/blather/stanza/pubsub.rb +119 -0
  66. data/lib/blather/stanza/pubsub/affiliations.rb +79 -0
  67. data/lib/blather/stanza/pubsub/create.rb +65 -0
  68. data/lib/blather/stanza/pubsub/errors.rb +18 -0
  69. data/lib/blather/stanza/pubsub/event.rb +139 -0
  70. data/lib/blather/stanza/pubsub/items.rb +103 -0
  71. data/lib/blather/stanza/pubsub/publish.rb +103 -0
  72. data/lib/blather/stanza/pubsub/retract.rb +92 -0
  73. data/lib/blather/stanza/pubsub/subscribe.rb +68 -0
  74. data/lib/blather/stanza/pubsub/subscription.rb +135 -0
  75. data/lib/blather/stanza/pubsub/subscriptions.rb +83 -0
  76. data/lib/blather/stanza/pubsub/unsubscribe.rb +84 -0
  77. data/lib/blather/stanza/pubsub_owner.rb +51 -0
  78. data/lib/blather/stanza/pubsub_owner/delete.rb +52 -0
  79. data/lib/blather/stanza/pubsub_owner/purge.rb +52 -0
  80. data/lib/blather/stanza/x.rb +416 -0
  81. data/lib/blather/stream.rb +266 -0
  82. data/lib/blather/stream/client.rb +32 -0
  83. data/lib/blather/stream/component.rb +39 -0
  84. data/lib/blather/stream/features.rb +70 -0
  85. data/lib/blather/stream/features/register.rb +38 -0
  86. data/lib/blather/stream/features/resource.rb +63 -0
  87. data/lib/blather/stream/features/sasl.rb +190 -0
  88. data/lib/blather/stream/features/session.rb +45 -0
  89. data/lib/blather/stream/features/tls.rb +29 -0
  90. data/lib/blather/stream/parser.rb +102 -0
  91. data/lib/blather/version.rb +3 -0
  92. data/lib/blather/xmpp_node.rb +94 -0
  93. data/spec/blather/client/client_spec.rb +687 -0
  94. data/spec/blather/client/dsl/pubsub_spec.rb +492 -0
  95. data/spec/blather/client/dsl_spec.rb +266 -0
  96. data/spec/blather/errors/sasl_error_spec.rb +33 -0
  97. data/spec/blather/errors/stanza_error_spec.rb +129 -0
  98. data/spec/blather/errors/stream_error_spec.rb +108 -0
  99. data/spec/blather/errors_spec.rb +33 -0
  100. data/spec/blather/file_transfer_spec.rb +135 -0
  101. data/spec/blather/jid_spec.rb +87 -0
  102. data/spec/blather/roster_item_spec.rb +134 -0
  103. data/spec/blather/roster_spec.rb +107 -0
  104. data/spec/blather/stanza/discos/disco_info_spec.rb +247 -0
  105. data/spec/blather/stanza/discos/disco_items_spec.rb +154 -0
  106. data/spec/blather/stanza/iq/command_spec.rb +206 -0
  107. data/spec/blather/stanza/iq/ibb_spec.rb +124 -0
  108. data/spec/blather/stanza/iq/ping_spec.rb +45 -0
  109. data/spec/blather/stanza/iq/query_spec.rb +64 -0
  110. data/spec/blather/stanza/iq/roster_spec.rb +139 -0
  111. data/spec/blather/stanza/iq/s5b_spec.rb +57 -0
  112. data/spec/blather/stanza/iq/si_spec.rb +98 -0
  113. data/spec/blather/stanza/iq/vcard_spec.rb +93 -0
  114. data/spec/blather/stanza/iq_spec.rb +61 -0
  115. data/spec/blather/stanza/message/muc_user_spec.rb +152 -0
  116. data/spec/blather/stanza/message_spec.rb +282 -0
  117. data/spec/blather/stanza/presence/c_spec.rb +56 -0
  118. data/spec/blather/stanza/presence/muc_spec.rb +37 -0
  119. data/spec/blather/stanza/presence/muc_user_spec.rb +83 -0
  120. data/spec/blather/stanza/presence/status_spec.rb +144 -0
  121. data/spec/blather/stanza/presence/subscription_spec.rb +102 -0
  122. data/spec/blather/stanza/presence_spec.rb +125 -0
  123. data/spec/blather/stanza/pubsub/affiliations_spec.rb +57 -0
  124. data/spec/blather/stanza/pubsub/create_spec.rb +56 -0
  125. data/spec/blather/stanza/pubsub/event_spec.rb +98 -0
  126. data/spec/blather/stanza/pubsub/items_spec.rb +79 -0
  127. data/spec/blather/stanza/pubsub/publish_spec.rb +83 -0
  128. data/spec/blather/stanza/pubsub/retract_spec.rb +75 -0
  129. data/spec/blather/stanza/pubsub/subscribe_spec.rb +61 -0
  130. data/spec/blather/stanza/pubsub/subscription_spec.rb +97 -0
  131. data/spec/blather/stanza/pubsub/subscriptions_spec.rb +59 -0
  132. data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +74 -0
  133. data/spec/blather/stanza/pubsub_owner/delete_spec.rb +50 -0
  134. data/spec/blather/stanza/pubsub_owner/purge_spec.rb +50 -0
  135. data/spec/blather/stanza/pubsub_owner_spec.rb +27 -0
  136. data/spec/blather/stanza/pubsub_spec.rb +68 -0
  137. data/spec/blather/stanza/x_spec.rb +231 -0
  138. data/spec/blather/stanza_spec.rb +134 -0
  139. data/spec/blather/stream/client_spec.rb +1090 -0
  140. data/spec/blather/stream/component_spec.rb +108 -0
  141. data/spec/blather/stream/parser_spec.rb +152 -0
  142. data/spec/blather/stream/ssl_spec.rb +32 -0
  143. data/spec/blather/xmpp_node_spec.rb +47 -0
  144. data/spec/blather_spec.rb +34 -0
  145. data/spec/fixtures/pubsub.rb +311 -0
  146. data/spec/spec_helper.rb +17 -0
  147. data/yard/templates/default/class/html/handlers.erb +18 -0
  148. data/yard/templates/default/class/setup.rb +10 -0
  149. data/yard/templates/default/class/text/handlers.erb +1 -0
  150. metadata +459 -0
@@ -0,0 +1,144 @@
1
+ module Blather
2
+ class Stanza
3
+
4
+ # # Iq Stanza
5
+ #
6
+ # [RFC 3920 Section 9.2.3 - IQ Semantics](http://xmpp.org/rfcs/rfc3920.html#rfc.section.9.2.3)
7
+ #
8
+ # Info/Query, or IQ, is a request-response mechanism, similar in some ways
9
+ # to HTTP. The semantics of IQ enable an entity to make a request of, and
10
+ # receive a response from, another entity. The data content of the request
11
+ # and response is defined by the namespace declaration of a direct child
12
+ # element of the IQ element, and the interaction is tracked by the
13
+ # requesting entity through use of the 'id' attribute. Thus, IQ interactions
14
+ # follow a common pattern of structured data exchange such as get/result or
15
+ # set/result (although an error may be returned in reply to a request if
16
+ # appropriate).
17
+ #
18
+ # ## "ID" Attribute
19
+ #
20
+ # Iq Stanzas require the ID attribute be set. Blather will handle this
21
+ # automatically when a new Iq is created.
22
+ #
23
+ # ## "Type" Attribute
24
+ #
25
+ # * `:get` -- The stanza is a request for information or requirements.
26
+ #
27
+ # * `:set` -- The stanza provides required data, sets new values, or
28
+ # replaces existing values.
29
+ #
30
+ # * `:result` -- The stanza is a response to a successful get or set request.
31
+ #
32
+ # * `:error` -- An error has occurred regarding processing or delivery of a
33
+ # previously-sent get or set (see Stanza Errors).
34
+ #
35
+ # Blather provides a helper for each possible type:
36
+ #
37
+ # Iq#get?
38
+ # Iq#set?
39
+ # Iq#result?
40
+ # Iq#error?
41
+ #
42
+ # Blather treats the `type` attribute like a normal ruby object attribute
43
+ # providing a getter and setter. The default `type` is `get`.
44
+ #
45
+ # iq = Iq.new
46
+ # iq.type # => :get
47
+ # iq.get? # => true
48
+ # iq.type = :set
49
+ # iq.set? # => true
50
+ # iq.get? # => false
51
+ #
52
+ # iq.type = :invalid # => RuntimeError
53
+ #
54
+ # @handler :iq
55
+ class Iq < Stanza
56
+ # @private
57
+ VALID_TYPES = [:get, :set, :result, :error].freeze
58
+
59
+ register :iq
60
+
61
+ # @private
62
+ def self.import(node)
63
+ klass = nil
64
+ node.children.detect do |e|
65
+ ns = e.namespace ? e.namespace.href : nil
66
+ klass = class_from_registration(e.element_name, ns)
67
+ end
68
+
69
+ if klass && klass != self
70
+ klass.import(node)
71
+ else
72
+ new(node[:type]).inherit(node)
73
+ end
74
+ end
75
+
76
+ # Create a new Iq
77
+ #
78
+ # @param [Symbol, nil] type the type of stanza (:get, :set, :result, :error)
79
+ # @param [Blather::JID, String, nil] jid the JID of the inteded recipient
80
+ # @param [#to_s] id the stanza's ID. Leaving this nil will set the ID to
81
+ # the next unique number
82
+ def self.new(type = nil, to = nil, id = nil)
83
+ node = super :iq
84
+ node.type = type || :get
85
+ node.to = to
86
+ node.id = id || self.next_id
87
+ node
88
+ end
89
+
90
+ # Check if the IQ is of type :get
91
+ #
92
+ # @return [true, false]
93
+ def get?
94
+ self.type == :get
95
+ end
96
+
97
+ # Check if the IQ is of type :set
98
+ #
99
+ # @return [true, false]
100
+ def set?
101
+ self.type == :set
102
+ end
103
+
104
+ # Check if the IQ is of type :result
105
+ #
106
+ # @return [true, false]
107
+ def result?
108
+ self.type == :result
109
+ end
110
+
111
+ # Check if the IQ is of type :error
112
+ #
113
+ # @return [true, false]
114
+ def error?
115
+ self.type == :error
116
+ end
117
+
118
+ # Ensures type is :get, :set, :result or :error
119
+ #
120
+ # @param [#to_sym] type the Iq type. Must be one of VALID_TYPES
121
+ def type=(type)
122
+ if type && !VALID_TYPES.include?(type.to_sym)
123
+ raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}"
124
+ end
125
+ super
126
+ end
127
+
128
+ # Overrides the parent method to ensure the reply is of type :result and that
129
+ # all children are removed.
130
+ #
131
+ # @param [Hash] opts options to pass to reply!
132
+ # @option opts [Boolean] :remove_children Wether or not to remove child nodes when replying
133
+ #
134
+ # @return [self]
135
+ def reply!(opts = {})
136
+ opts = {:remove_children => true}.merge opts
137
+ super
138
+ self.type = :result
139
+ self
140
+ end
141
+ end
142
+
143
+ end
144
+ end
@@ -0,0 +1,339 @@
1
+ module Blather
2
+ class Stanza
3
+ class Iq
4
+
5
+ # # Command Stanza
6
+ #
7
+ # [XEP-0050 Ad-Hoc Commands](http://xmpp.org/extensions/xep-0050.html)
8
+ #
9
+ # This is a base class for any command based Iq stanzas. It provides a base set
10
+ # of methods for working with command stanzas
11
+ #
12
+ # @handler :command
13
+ class Command < Iq
14
+ # @private
15
+ VALID_ACTIONS = [:cancel, :execute, :complete, :next, :prev].freeze
16
+ # @private
17
+ VALID_STATUS = [:executing, :completed, :canceled].freeze
18
+ # @private
19
+ VALID_NOTE_TYPES = [:info, :warn, :error].freeze
20
+
21
+ register :command, :command, 'http://jabber.org/protocol/commands'
22
+
23
+ # Overrides the parent method to ensure a command node is created
24
+ #
25
+ # @param [:get, :set, :result, :error, nil] type the IQ type
26
+ # @param [String] node the name of the node
27
+ # @param [:cancel, :execute, :complete, :next, :prev, nil] action the command's action
28
+ # @return [Command] a new Command stanza
29
+ def self.new(type = :set, node = nil, action = :execute)
30
+ new_node = super type
31
+ new_node.command
32
+ new_node.node = node
33
+ new_node.action = action
34
+ new_node
35
+ end
36
+
37
+ # Overrides the parent method to ensure the current command node is destroyed
38
+ # and the action is set to execute if no action provided
39
+ #
40
+ # @see Blather::Stanza::Iq#inherit
41
+ def inherit(node)
42
+ command.remove
43
+ super
44
+ self.action = :execute unless self.action
45
+ self
46
+ end
47
+
48
+ # Overrides the parent method to ensure the reply has no action
49
+ #
50
+ # @param [Hash] opts options to pass to reply!
51
+ # @option opts [Boolean] :remove_children Wether or not to remove child nodes when replying
52
+ #
53
+ # @return [self]
54
+ def reply!(opts = {})
55
+ super
56
+ self.action = nil
57
+ self
58
+ end
59
+
60
+ # Command node accessor
61
+ # If a command node exists it will be returned.
62
+ # Otherwise a new node will be created and returned
63
+ #
64
+ # @return [Blather::XMPPNode]
65
+ def command
66
+ c = if self.class.registered_ns
67
+ find_first('ns:command', :ns => self.class.registered_ns)
68
+ else
69
+ find_first('command')
70
+ end
71
+
72
+ unless c
73
+ (self << (c = XMPPNode.new('command', self.document)))
74
+ c.namespace = self.class.registered_ns
75
+ end
76
+ c
77
+ end
78
+
79
+ # Get the name of the node
80
+ #
81
+ # @return [String, nil]
82
+ def node
83
+ command[:node]
84
+ end
85
+
86
+ # Set the name of the node
87
+ #
88
+ # @param [String, nil] node the new node name
89
+ def node=(node)
90
+ command[:node] = node
91
+ end
92
+
93
+ # Get the sessionid of the command
94
+ #
95
+ # @return [String, nil]
96
+ def sessionid
97
+ command[:sessionid]
98
+ end
99
+
100
+ # Check if there is a sessionid set
101
+ #
102
+ # @return [true, false]
103
+ def sessionid?
104
+ !sessionid.nil?
105
+ end
106
+
107
+ # Set the sessionid of the command
108
+ #
109
+ # @param [String, nil] sessionid the new sessionid
110
+ def sessionid=(sessionid)
111
+ command[:sessionid] = Digest::SHA1.hexdigest(sessionid)
112
+ end
113
+
114
+ # Generate a new session ID (SHA-1 hash)
115
+ def new_sessionid!
116
+ self.sessionid = "commandsession-#{id}"
117
+ end
118
+
119
+ # Get the action of the command
120
+ #
121
+ # @return [Symbol, nil]
122
+ def action
123
+ (val = command[:action]) && val.to_sym
124
+ end
125
+
126
+ # Check if the command action is :cancel
127
+ #
128
+ # @return [true, false]
129
+ def cancel?
130
+ self.action == :cancel
131
+ end
132
+
133
+ # Check if the command action is :execute
134
+ #
135
+ # @return [true, false]
136
+ def execute?
137
+ self.action == :execute
138
+ end
139
+
140
+ # Check if the command action is :complete
141
+ #
142
+ # @return [true, false]
143
+ def complete?
144
+ self.action == :complete
145
+ end
146
+
147
+ # Check if the command action is :next
148
+ #
149
+ # @return [true, false]
150
+ def next?
151
+ self.action == :next
152
+ end
153
+
154
+ # Check if the command action is :prev
155
+ #
156
+ # @return [true, false]
157
+ def prev?
158
+ self.action == :prev
159
+ end
160
+
161
+ # Set the action of the command
162
+ #
163
+ # @param [:cancel, :execute, :complete, :next, :prev] action the new action
164
+ def action=(action)
165
+ if action && !VALID_ACTIONS.include?(action.to_sym)
166
+ raise ArgumentError, "Invalid Action (#{action}), use: #{VALID_ACTIONS*' '}"
167
+ end
168
+ command[:action] = action
169
+ end
170
+
171
+ # Get the status of the command
172
+ #
173
+ # @return [Symbol, nil]
174
+ def status
175
+ ((val = command[:status]) && val.to_sym) || :executing
176
+ end
177
+
178
+ # Check if the command status is :executing
179
+ #
180
+ # @return [true, false]
181
+ def executing?
182
+ self.status == :executing
183
+ end
184
+
185
+ # Check if the command status is :completed
186
+ #
187
+ # @return [true, false]
188
+ def completed?
189
+ self.status == :completed
190
+ end
191
+
192
+ # Check if the command status is :canceled
193
+ #
194
+ # @return [true, false]
195
+ def canceled?
196
+ self.status == :canceled
197
+ end
198
+
199
+ # Set the status of the command
200
+ #
201
+ # @param [:executing, :completed, :canceled] status the new status
202
+ def status=(status)
203
+ if status && !VALID_STATUS.include?(status.to_sym)
204
+ raise ArgumentError, "Invalid Action (#{status}), use: #{VALID_STATUS*' '}"
205
+ end
206
+ command[:status] = status
207
+ end
208
+
209
+ # Command actions accessor
210
+ # If a command actions element exists it will be returned.
211
+ # Otherwise a new actions element will be created and returned
212
+ #
213
+ # @return [Blather::XMPPNode]
214
+ def actions
215
+ unless a = self.command.find_first('ns:actions', :ns => self.class.registered_ns)
216
+ (self.command << (a = XMPPNode.new('actions', self.document)))
217
+ a.namespace = self.command.namespace
218
+ end
219
+ a
220
+ end
221
+
222
+ # Get the command's allowed actions
223
+ #
224
+ # @return [Array<Symbol>]
225
+ def allowed_actions
226
+ ([:execute] + actions.children.map { |action| action.name.to_sym }).uniq
227
+ end
228
+
229
+ # Get the primary allowed action
230
+ #
231
+ # @return [Symbol]
232
+ def primary_allowed_action
233
+ (actions[:execute] || :execute).to_sym
234
+ end
235
+
236
+ # Set the primary allowed action
237
+ #
238
+ # This must be one of :prev, :next, :complete or :execute
239
+ #
240
+ # @param [#to_sym] a the primary allowed action
241
+ def primary_allowed_action=(a)
242
+ a = a.to_sym
243
+ if a && ![:prev, :next, :complete, :execute].include?(a)
244
+ raise ArgumentError, "Invalid Action (#{a}), use: #{[:prev, :next, :complete, :execute]*' '}"
245
+ end
246
+ actions[:execute] = a
247
+ end
248
+
249
+ # Add allowed actions to the command
250
+ #
251
+ # @param [[:prev, :next, :complete]] allowed_actions the new allowed actions
252
+ def allowed_actions=(allowed_actions)
253
+ allowed_actions = ([allowed_actions].flatten.map(&:to_sym) + [:execute]).uniq
254
+ if (invalid_actions = allowed_actions - VALID_ACTIONS).size > 0
255
+ raise ArgumentError, "Invalid Action(s) (#{invalid_actions*' '}), use: #{VALID_ACTIONS*' '}"
256
+ end
257
+ actions.children.map(&:remove)
258
+ allowed_actions.each { |action| actions << XMPPNode.new(action.to_s) }
259
+ end
260
+
261
+ # Remove allowed actions from the command
262
+ #
263
+ # @param [[:prev, :next, :complete]] disallowed_actions the allowed actions to remove
264
+ def remove_allowed_actions!
265
+ actions.remove
266
+ end
267
+
268
+ # Command note accessor
269
+ # If a command note exists it will be returned.
270
+ # Otherwise a new note will be created and returned
271
+ #
272
+ # @return [Blather::XMPPNode]
273
+ def note
274
+ unless n = self.command.find_first('ns:note', :ns => self.class.registered_ns)
275
+ (self.command << (n = XMPPNode.new('note', self.document)))
276
+ n.namespace = self.command.namespace
277
+ end
278
+ n
279
+ end
280
+
281
+ # Get the note_type of the command
282
+ #
283
+ # @return [Symbol, nil]
284
+ def note_type
285
+ (val = note[:type]) && val.to_sym
286
+ end
287
+
288
+ # Check if the command status is :info
289
+ #
290
+ # @return [true, false]
291
+ def info?
292
+ self.note_type == :info
293
+ end
294
+
295
+ # Check if the command status is :warn
296
+ #
297
+ # @return [true, false]
298
+ def warn?
299
+ self.status == :warn
300
+ end
301
+
302
+ # Check if the command status is :error
303
+ #
304
+ # @return [true, false]
305
+ def error?
306
+ self.status == :error
307
+ end
308
+
309
+ # Set the note_type of the command
310
+ #
311
+ # @param [:executing, :completed, :canceled] note_type the new note_type
312
+ def note_type=(note_type)
313
+ if note_type && !VALID_NOTE_TYPES.include?(note_type.to_sym)
314
+ raise ArgumentError, "Invalid Action (#{note_type}), use: #{VALID_NOTE_TYPES*' '}"
315
+ end
316
+ note[:type] = note_type
317
+ end
318
+
319
+ # Get the text of the command's note
320
+ def note_text
321
+ content_from :note
322
+ end
323
+
324
+ # Set the command's note text
325
+ #
326
+ # @param [String] note_text the command's new note text
327
+ def note_text=(note_text)
328
+ set_content_for :note, note_text
329
+ end
330
+
331
+ # Returns the command's x:data form child
332
+ def form
333
+ X.find_or_create command
334
+ end
335
+ end #Command
336
+
337
+ end #Iq
338
+ end #Stanza
339
+ end #Blather