tp-blather 0.8.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 (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