xmpp4r-simple-19 1.0.0

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 (6) hide show
  1. data/CHANGELOG +73 -0
  2. data/COPYING +281 -0
  3. data/README +63 -0
  4. data/lib/xmpp4r-simple.rb +492 -0
  5. data/test/test_xmpp4r_simple.rb +242 -0
  6. metadata +102 -0
@@ -0,0 +1,73 @@
1
+ xmpp4r-simple (0.8.8)
2
+
3
+ [ Blaine Cook]
4
+ * Add xmpp4r-simple.gemspec, remove gem-related code from Rakefile
5
+
6
+ -- Blaine Cook <romeda@gmail.com> Wed, 30 Jul 2008 16:03:00 -0800
7
+
8
+ xmpp4r-simple (0.8.7)
9
+
10
+ [ Blaine Cook ]
11
+ * If using Jabber::Simple in a DRb environment, the Jabber::Simple object
12
+ will execute in the DRb server's environment to improve efficiency.
13
+ * Jabber::Simple will now recover gracefully from server-side disconnects.
14
+ * Updated Rakefile to pass tests on firebrigade (http://firebrigade.seattlerb.org)
15
+
16
+ -- Blaine Cook <romeda@gmail.com> Wed, 31 Jan 2007 17:15:56 -0800
17
+
18
+ xmpp4r-simple (0.8.6)
19
+
20
+ [ Blaine Cook ]
21
+ * Catch a rare exception that's thrown when we are disconnecting.
22
+
23
+ -- Blaine Cook <romeda@gmail.com> Fri, 26 Jan 2007 11:45:23 -0800
24
+
25
+
26
+ xmpp4r-simple (0.8.5)
27
+
28
+ [ Blaine Cook ]
29
+ * Accept Jabber::Message messages as arguments to Jabber::Simple#deliver
30
+
31
+ -- Blaine Cook <romeda@gmail.com> Mon, 22 Jan 2007 07:23:09 -0800
32
+
33
+
34
+ xmpp4r-simple (0.8.4)
35
+
36
+ [ Blaine Cook ]
37
+ * Catch broken connections and attempt to reconnect 3 times.
38
+
39
+ -- Blaine Cook <romeda@gmail.com> Fri, 23 Dec 2006 00:12:09 -0800
40
+
41
+
42
+ xmpp4r-simple (0.8.3)
43
+
44
+ [ Blaine Cook ]
45
+ * Update presence_updates to only store one presence_update per user.
46
+ Changes methods, will break code that uses presence_updates if not updated
47
+ correspondingly, check the documenation for the new semantics.
48
+
49
+ -- Blaine Cook <romeda@gmail.com> Thu, 07 Dec 2006 12:45:52 -0800
50
+
51
+
52
+ xmpp4r-simple (0.8.2)
53
+
54
+ [ Blaine Cook ]
55
+ * Add presence_updates?, received_messages?, and new_subscriptions? methods.
56
+
57
+ -- Blaine Cook <romeda@gmail.com> Wed, 06 Dec 2006 09:40:28 -0800
58
+
59
+
60
+ xmpp4r-simple (0.8.1)
61
+
62
+ [ Blaine Cook ]
63
+ * Make the deferred_delivery method public
64
+
65
+ -- Blaine Cook <romeda@gmail.com> Tue, 05 Dec 2006 17:39:14 -0800
66
+
67
+
68
+ xmpp4r-simple (0.8.0)
69
+
70
+ [ Blaine Cook ]
71
+ * initial import
72
+
73
+ -- Blaine Cook <romeda@gmail.com> Wed, 08 Nov 2006 20:42:42 -0800
data/COPYING ADDED
@@ -0,0 +1,281 @@
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ Preamble
10
+
11
+ The licenses for most software are designed to take away your
12
+ freedom to share and change it. By contrast, the GNU General Public
13
+ License is intended to guarantee your freedom to share and change free
14
+ software--to make sure the software is free for all its users. This
15
+ General Public License applies to most of the Free Software
16
+ Foundation's software and to any other program whose authors commit to
17
+ using it. (Some other Free Software Foundation software is covered by
18
+ the GNU Lesser General Public License instead.) You can apply it to
19
+ your programs, too.
20
+
21
+ When we speak of free software, we are referring to freedom, not
22
+ price. Our General Public Licenses are designed to make sure that you
23
+ have the freedom to distribute copies of free software (and charge for
24
+ this service if you wish), that you receive source code or can get it
25
+ if you want it, that you can change the software or use pieces of it
26
+ in new free programs; and that you know you can do these things.
27
+
28
+ To protect your rights, we need to make restrictions that forbid
29
+ anyone to deny you these rights or to ask you to surrender the rights.
30
+ These restrictions translate to certain responsibilities for you if you
31
+ distribute copies of the software, or if you modify it.
32
+
33
+ For example, if you distribute copies of such a program, whether
34
+ gratis or for a fee, you must give the recipients all the rights that
35
+ you have. You must make sure that they, too, receive or can get the
36
+ source code. And you must show them these terms so they know their
37
+ rights.
38
+
39
+ We protect your rights with two steps: (1) copyright the software, and
40
+ (2) offer you this license which gives you legal permission to copy,
41
+ distribute and/or modify the software.
42
+
43
+ Also, for each author's protection and ours, we want to make certain
44
+ that everyone understands that there is no warranty for this free
45
+ software. If the software is modified by someone else and passed on, we
46
+ want its recipients to know that what they have is not the original, so
47
+ that any problems introduced by others will not reflect on the original
48
+ authors' reputations.
49
+
50
+ Finally, any free program is threatened constantly by software
51
+ patents. We wish to avoid the danger that redistributors of a free
52
+ program will individually obtain patent licenses, in effect making the
53
+ program proprietary. To prevent this, we have made it clear that any
54
+ patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ GNU GENERAL PUBLIC LICENSE
60
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+ 0. This License applies to any program or other work which contains
63
+ a notice placed by the copyright holder saying it may be distributed
64
+ under the terms of this General Public License. The "Program", below,
65
+ refers to any such program or work, and a "work based on the Program"
66
+ means either the Program or any derivative work under copyright law:
67
+ that is to say, a work containing the Program or a portion of it,
68
+ either verbatim or with modifications and/or translated into another
69
+ language. (Hereinafter, translation is included without limitation in
70
+ the term "modification".) Each licensee is addressed as "you".
71
+
72
+ Activities other than copying, distribution and modification are not
73
+ covered by this License; they are outside its scope. The act of
74
+ running the Program is not restricted, and the output from the Program
75
+ is covered only if its contents constitute a work based on the
76
+ Program (independent of having been made by running the Program).
77
+ Whether that is true depends on what the Program does.
78
+
79
+ 1. You may copy and distribute verbatim copies of the Program's
80
+ source code as you receive it, in any medium, provided that you
81
+ conspicuously and appropriately publish on each copy an appropriate
82
+ copyright notice and disclaimer of warranty; keep intact all the
83
+ notices that refer to this License and to the absence of any warranty;
84
+ and give any other recipients of the Program a copy of this License
85
+ along with the Program.
86
+
87
+ You may charge a fee for the physical act of transferring a copy, and
88
+ you may at your option offer warranty protection in exchange for a fee.
89
+
90
+ 2. You may modify your copy or copies of the Program or any portion
91
+ of it, thus forming a work based on the Program, and copy and
92
+ distribute such modifications or work under the terms of Section 1
93
+ above, provided that you also meet all of these conditions:
94
+
95
+ a) You must cause the modified files to carry prominent notices
96
+ stating that you changed the files and the date of any change.
97
+
98
+ b) You must cause any work that you distribute or publish, that in
99
+ whole or in part contains or is derived from the Program or any
100
+ part thereof, to be licensed as a whole at no charge to all third
101
+ parties under the terms of this License.
102
+
103
+ c) If the modified program normally reads commands interactively
104
+ when run, you must cause it, when started running for such
105
+ interactive use in the most ordinary way, to print or display an
106
+ announcement including an appropriate copyright notice and a
107
+ notice that there is no warranty (or else, saying that you provide
108
+ a warranty) and that users may redistribute the program under
109
+ these conditions, and telling the user how to view a copy of this
110
+ License. (Exception: if the Program itself is interactive but
111
+ does not normally print such an announcement, your work based on
112
+ the Program is not required to print an announcement.)
113
+
114
+ These requirements apply to the modified work as a whole. If
115
+ identifiable sections of that work are not derived from the Program,
116
+ and can be reasonably considered independent and separate works in
117
+ themselves, then this License, and its terms, do not apply to those
118
+ sections when you distribute them as separate works. But when you
119
+ distribute the same sections as part of a whole which is a work based
120
+ on the Program, the distribution of the whole must be on the terms of
121
+ this License, whose permissions for other licensees extend to the
122
+ entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.
279
+
280
+ END OF TERMS AND CONDITIONS
281
+
data/README ADDED
@@ -0,0 +1,63 @@
1
+ = Name
2
+
3
+ Jabber::Simple - An extremely easy-to-use Jabber client library.
4
+
5
+ = THIS IS RUBY 1.9 COMPATIBLE VERSION of xmpp4r-simple
6
+
7
+ This is simply updated to work with ruby 1.9.
8
+
9
+ = Synopsis
10
+
11
+ # Send a message to a friend, asking for authorization if necessary:
12
+ im = Jabber::Simple.new("user@example.com", "password")
13
+ im.deliver("friend@example.com", "Hey there friend!")
14
+
15
+ # Get received messages and print them out to the console:
16
+ im.received_messages { |msg| puts msg.body if msg.type == :chat }
17
+
18
+ # Send an authorization request to a user:
19
+ im.add("friend@example.com")
20
+
21
+ # Get presence updates from your friends, and print them out to the console:
22
+ # (admittedly, this one needs some work)
23
+ im.presence_updates { |update|
24
+ from = update[0].jid.strip.to_s
25
+ status = update[2].status
26
+ presence = update[2].show
27
+ puts "#{from} went #{presence}: #{status}"
28
+ end
29
+
30
+ # Remove a user from your contact list:
31
+ im.remove("unfriendly@example.com")
32
+
33
+ # See the Jabber::Simple documentation for more information.
34
+
35
+ = Description
36
+
37
+ Jabber::Simple is intended to make Jabber client programming dead simple. XMPP,
38
+ the Jabber protocol, is extremely powerful but also carries a steep learning
39
+ curve. This library exposes only the most common tasks, and does so in a way
40
+ that is familiar to users of traditional instant messenger clients.
41
+
42
+ = Known Issues
43
+
44
+ * None. If you'd like additional functionality, please contact the developer!
45
+
46
+ = Copyright
47
+
48
+ Jabber::Simple - An extremely easy-to-use Jabber client library.
49
+ Copyright 2006-2008 Blaine Cook <romeda@gmail.com>.
50
+
51
+ Jabber::Simple is free software; you can redistribute it and/or modify
52
+ it under the terms of the GNU General Public License as published by
53
+ the Free Software Foundation; either version 2 of the License, or
54
+ (at your option) any later version.
55
+
56
+ Jabber::Simple is distributed in the hope that it will be useful,
57
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
58
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
59
+ GNU General Public License for more details.
60
+
61
+ You should have received a copy of the GNU General Public License
62
+ along with Jabber::Simple; if not, write to the Free Software
63
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
@@ -0,0 +1,492 @@
1
+ # Jabber::Simple - An extremely easy-to-use Jabber client library.
2
+ # Copyright 2006 Blaine Cook <blaine@obvious.com>, Obvious Corp.
3
+ #
4
+ # Jabber::Simple is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # Jabber::Simple is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with Jabber::Simple; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ require 'rubygems'
19
+ require 'xmpp4r'
20
+ require 'xmpp4r/roster'
21
+ require 'xmpp4r/vcard'
22
+
23
+ module Jabber
24
+
25
+ class ConnectionError < StandardError #:nodoc:
26
+ end
27
+
28
+ class Contact #:nodoc:
29
+
30
+ include DRb::DRbUndumped if defined?(DRb::DRbUndumped)
31
+
32
+ def initialize(client, jid)
33
+ @jid = jid.respond_to?(:resource) ? jid : JID.new(jid)
34
+ @client = client
35
+ end
36
+
37
+ def inspect
38
+ "Jabber::Contact #{jid.to_s}"
39
+ end
40
+
41
+ def subscribed?
42
+ [:to, :both].include?(subscription)
43
+ end
44
+
45
+ def subscription
46
+ roster_item && roster_item.subscription
47
+ end
48
+
49
+ def ask_for_authorization!
50
+ subscription_request = Presence.new.set_type(:subscribe)
51
+ subscription_request.to = jid
52
+ client.send!(subscription_request)
53
+ end
54
+
55
+ def unsubscribe!
56
+ unsubscription_request = Presence.new.set_type(:unsubscribe)
57
+ unsubscription_request.to = jid
58
+ client.send!(unsubscription_request)
59
+ client.send!(unsubscription_request.set_type(:unsubscribed))
60
+ end
61
+
62
+ def jid(bare=true)
63
+ bare ? @jid.strip : @jid
64
+ end
65
+
66
+ private
67
+
68
+ def roster_item
69
+ client.roster.items[jid]
70
+ end
71
+
72
+ def client
73
+ @client
74
+ end
75
+ end
76
+
77
+ class Simple
78
+
79
+ include DRb::DRbUndumped if defined?(DRb::DRbUndumped)
80
+
81
+ # Create a new Jabber::Simple client. You will be automatically connected
82
+ # to the Jabber server and your status message will be set to the string
83
+ # passed in as the status_message argument.
84
+ #
85
+ # jabber = Jabber::Simple.new("me@example.com", "password", "Chat with me - Please!")
86
+ def initialize(jid, password, status = nil, status_message = "Available")
87
+ @jid = jid
88
+ @password = password
89
+ @disconnected = false
90
+ status(status, status_message)
91
+ start_deferred_delivery_thread
92
+ end
93
+
94
+ def inspect #:nodoc:
95
+ "Jabber::Simple #{@jid}"
96
+ end
97
+
98
+ # Send a message to jabber user jid.
99
+ #
100
+ # Valid message types are:
101
+ #
102
+ # * :normal (default): a normal message.
103
+ # * :chat: a one-to-one chat message.
104
+ # * :groupchat: a group-chat message.
105
+ # * :headline: a "headline" message.
106
+ # * :error: an error message.
107
+ #
108
+ # If the recipient is not in your contacts list, the message will be queued
109
+ # for later delivery, and the Contact will be automatically asked for
110
+ # authorization (see Jabber::Simple#add).
111
+ #
112
+ # message should be a string or a valid Jabber::Message object. In either case,
113
+ # the message recipient will be set to jid.
114
+ def deliver(jid, message, type=:chat)
115
+ contacts(jid) do |friend|
116
+ unless subscribed_to? friend
117
+ add(friend.jid)
118
+ return deliver_deferred(friend.jid, message, type)
119
+ end
120
+ if message.kind_of?(Jabber::Message)
121
+ msg = message
122
+ msg.to = friend.jid
123
+ else
124
+ msg = Message.new(friend.jid)
125
+ msg.type = type
126
+ msg.body = message
127
+ end
128
+ send!(msg)
129
+ end
130
+ end
131
+
132
+ # Set your presence, with a message.
133
+ #
134
+ # Available values for presence are:
135
+ #
136
+ # * nil: online.
137
+ # * :chat: free for chat.
138
+ # * :away: away from the computer.
139
+ # * :dnd: do not disturb.
140
+ # * :xa: extended away.
141
+ #
142
+ # It's not possible to set an offline status - to do that, disconnect! :-)
143
+ def status(presence, message)
144
+ @presence = presence
145
+ @status_message = message
146
+ stat_msg = Presence.new(@presence, @status_message)
147
+ send!(stat_msg)
148
+ end
149
+
150
+ # Ask the users specified by jids for authorization (i.e., ask them to add
151
+ # you to their contact list). If you are already in the user's contact list,
152
+ # add() will not attempt to re-request authorization. In order to force
153
+ # re-authorization, first remove() the user, then re-add them.
154
+ #
155
+ # Example usage:
156
+ #
157
+ # jabber_simple.add("friend@friendosaurus.com")
158
+ #
159
+ # Because the authorization process might take a few seconds, or might
160
+ # never happen depending on when (and if) the user accepts your
161
+ # request, results are placed in the Jabber::Simple#new_subscriptions queue.
162
+ def add(*jids)
163
+ contacts(*jids) do |friend|
164
+ next if subscribed_to? friend
165
+ friend.ask_for_authorization!
166
+ end
167
+ end
168
+
169
+ # Remove the jabber users specified by jids from the contact list.
170
+ def remove(*jids)
171
+ contacts(*jids) do |unfriend|
172
+ unfriend.unsubscribe!
173
+ end
174
+ end
175
+
176
+ # Returns true if this Jabber account is subscribed to status updates for
177
+ # the jabber user jid, false otherwise.
178
+ def subscribed_to?(jid)
179
+ contacts(jid) do |contact|
180
+ return contact.subscribed?
181
+ end
182
+ end
183
+
184
+ # If contacts is a single contact, returns a Jabber::Contact object
185
+ # representing that user; if contacts is an array, returns an array of
186
+ # Jabber::Contact objects.
187
+ #
188
+ # When called with a block, contacts will yield each Jabber::Contact object
189
+ # in turn. This is mainly used internally, but exposed as an utility
190
+ # function.
191
+ def contacts(*contacts, &block)
192
+ @contacts ||= {}
193
+ contakts = []
194
+ contacts.each do |contact|
195
+ jid = contact.to_s
196
+ unless @contacts[jid]
197
+ @contacts[jid] = contact.respond_to?(:ask_for_authorization!) ? contact : Contact.new(self, contact)
198
+ end
199
+ yield @contacts[jid] if block_given?
200
+ contakts << @contacts[jid]
201
+ end
202
+ contakts.size > 1 ? contakts : contakts.first
203
+ end
204
+
205
+ # Returns true if the Jabber client is connected to the Jabber server,
206
+ # false otherwise.
207
+ def connected?
208
+ @client ||= nil
209
+ connected = @client.respond_to?(:is_connected?) && @client.is_connected?
210
+ return connected
211
+ end
212
+
213
+ # Returns an array of messages received since the last time
214
+ # received_messages was called. Passing a block will yield each message in
215
+ # turn, allowing you to break part-way through processing (especially
216
+ # useful when your message handling code is not thread-safe (e.g.,
217
+ # ActiveRecord).
218
+ #
219
+ # e.g.:
220
+ #
221
+ # jabber.received_messages do |message|
222
+ # puts "Received message from #{message.from}: #{message.body}"
223
+ # end
224
+ def received_messages(&block)
225
+ dequeue(:received_messages, &block)
226
+ end
227
+
228
+ # Returns true if there are unprocessed received messages waiting in the
229
+ # queue, false otherwise.
230
+ def received_messages?
231
+ !queue(:received_messages).empty?
232
+ end
233
+
234
+ # Returns an array of presence updates received since the last time
235
+ # presence_updates was called. Passing a block will yield each update in
236
+ # turn, allowing you to break part-way through processing (especially
237
+ # useful when your presence handling code is not thread-safe (e.g.,
238
+ # ActiveRecord).
239
+ #
240
+ # e.g.:
241
+ #
242
+ # jabber.presence_updates do |friend, new_presence|
243
+ # puts "Received presence update from #{friend}: #{new_presence}"
244
+ # end
245
+ def presence_updates(&block)
246
+ updates = []
247
+ @presence_mutex.synchronize do
248
+ dequeue(:presence_updates) do |friend|
249
+ presence = @presence_updates[friend]
250
+ next unless presence
251
+ new_update = [friend, presence[0], presence[1]]
252
+ yield new_update if block_given?
253
+ updates << new_update
254
+ @presence_updates.delete(friend)
255
+ end
256
+ end
257
+ return updates
258
+ end
259
+
260
+ # Returns true if there are unprocessed presence updates waiting in the
261
+ # queue, false otherwise.
262
+ def presence_updates?
263
+ !queue(:presence_updates).empty?
264
+ end
265
+
266
+ # Returns an array of subscription notifications received since the last
267
+ # time new_subscriptions was called. Passing a block will yield each update
268
+ # in turn, allowing you to break part-way through processing (especially
269
+ # useful when your subscription handling code is not thread-safe (e.g.,
270
+ # ActiveRecord).
271
+ #
272
+ # e.g.:
273
+ #
274
+ # jabber.new_subscriptions do |friend, presence|
275
+ # puts "Received presence update from #{friend.to_s}: #{presence}"
276
+ # end
277
+ def new_subscriptions(&block)
278
+ dequeue(:new_subscriptions, &block)
279
+ end
280
+
281
+ # Returns true if there are unprocessed presence updates waiting in the
282
+ # queue, false otherwise.
283
+ def new_subscriptions?
284
+ !queue(:new_subscriptions).empty?
285
+ end
286
+
287
+ # Returns an array of subscription notifications received since the last
288
+ # time subscription_requests was called. Passing a block will yield each update
289
+ # in turn, allowing you to break part-way through processing (especially
290
+ # useful when your subscription handling code is not thread-safe (e.g.,
291
+ # ActiveRecord).
292
+ #
293
+ # e.g.:
294
+ #
295
+ # jabber.subscription_requests do |friend, presence|
296
+ # puts "Received presence update from #{friend.to_s}: #{presence}"
297
+ # end
298
+ def subscription_requests(&block)
299
+ dequeue(:subscription_requests, &block)
300
+ end
301
+
302
+ # Returns true if auto-accept subscriptions (friend requests) is enabled
303
+ # (default), false otherwise.
304
+ def accept_subscriptions?
305
+ @accept_subscriptions = true if @accept_subscriptions.nil?
306
+ @accept_subscriptions
307
+ end
308
+
309
+ # Change whether or not subscriptions (friend requests) are automatically accepted.
310
+ def accept_subscriptions=(accept_status)
311
+ @accept_subscriptions = accept_status
312
+ end
313
+
314
+ # Direct access to the underlying Roster helper.
315
+ def roster
316
+ return @roster if @roster
317
+ self.roster = Roster::Helper.new(client)
318
+ end
319
+
320
+ # Direct access to the underlying Jabber client.
321
+ def client
322
+ connect!() unless connected?
323
+ @client
324
+ end
325
+
326
+ # Send a Jabber stanza over-the-wire.
327
+ def send!(msg)
328
+ attempts = 0
329
+ begin
330
+ attempts += 1
331
+ client.send(msg)
332
+ rescue Errno::EPIPE, IOError => e
333
+ sleep 1
334
+ disconnect
335
+ reconnect
336
+ retry unless attempts > 3
337
+ raise e
338
+ rescue Errno::ECONNRESET => e
339
+ sleep (attempts^2) * 60 + 60
340
+ disconnect
341
+ reconnect
342
+ retry unless attempts > 3
343
+ raise e
344
+ end
345
+ end
346
+
347
+ # Use this to force the client to reconnect after a force_disconnect.
348
+ def reconnect
349
+ @disconnected = false
350
+ connect!
351
+ end
352
+
353
+ # Use this to force the client to disconnect and not automatically
354
+ # reconnect.
355
+ def disconnect
356
+ disconnect!
357
+ end
358
+
359
+ # Queue messages for delivery once a user has accepted our authorization
360
+ # request. Works in conjunction with the deferred delivery thread.
361
+ #
362
+ # You can use this method if you want to manually add friends and still
363
+ # have the message queued for later delivery.
364
+ def deliver_deferred(jid, message, type)
365
+ msg = {:to => jid, :message => message, :type => type}
366
+ queue(:pending_messages) << [msg]
367
+ end
368
+
369
+ private
370
+
371
+ def client=(client)
372
+ self.roster = nil # ensure we clear the roster, since that's now associated with a different client.
373
+ @client = client
374
+ end
375
+
376
+ def roster=(new_roster)
377
+ @roster = new_roster
378
+ end
379
+
380
+ def connect!
381
+ raise ConnectionError, "Connections are disabled - use Jabber::Simple::force_connect() to reconnect." if @disconnected
382
+ # Pre-connect
383
+ @connect_mutex ||= Mutex.new
384
+
385
+ # don't try to connect if another thread is already connecting.
386
+ return if @connect_mutex.locked?
387
+
388
+ @connect_mutex.lock
389
+ disconnect!(false) if connected?
390
+
391
+ # Connect
392
+ jid = JID.new(@jid)
393
+ my_client = Client.new(@jid)
394
+ my_client.connect
395
+ my_client.auth(@password)
396
+ self.client = my_client
397
+
398
+ # Post-connect
399
+ register_default_callbacks
400
+ status(@presence, @status_message)
401
+ @connect_mutex.unlock
402
+ end
403
+
404
+ def disconnect!(auto_reconnect = true)
405
+ if client.respond_to?(:is_connected?) && client.is_connected?
406
+ begin
407
+ client.close
408
+ rescue Errno::EPIPE, IOError => e
409
+ # probably should log this.
410
+ nil
411
+ end
412
+ end
413
+ client = nil
414
+ @disconnected = auto_reconnect
415
+ end
416
+
417
+ def register_default_callbacks
418
+ client.add_message_callback do |message|
419
+ queue(:received_messages) << message unless message.body.nil?
420
+ end
421
+
422
+ roster.add_subscription_callback do |roster_item, presence|
423
+ if presence.type == :subscribed
424
+ queue(:new_subscriptions) << [roster_item, presence]
425
+ end
426
+ end
427
+
428
+ roster.add_subscription_request_callback do |roster_item, presence|
429
+ if accept_subscriptions?
430
+ roster.accept_subscription(presence.from)
431
+ else
432
+ queue(:subscription_requests) << [roster_item, presence]
433
+ end
434
+ end
435
+
436
+ @presence_updates = {}
437
+ @presence_mutex = Mutex.new
438
+ roster.add_presence_callback do |roster_item, old_presence, new_presence|
439
+ simple_jid = roster_item.jid.strip.to_s
440
+ presence = case new_presence.type
441
+ when nil then new_presence.show || :online
442
+ when :unavailable then :unavailable
443
+ else
444
+ nil
445
+ end
446
+
447
+ if presence && @presence_updates[simple_jid] != presence
448
+ queue(:presence_updates) << simple_jid
449
+ @presence_mutex.synchronize { @presence_updates[simple_jid] = [presence, new_presence.status] }
450
+ end
451
+ end
452
+ end
453
+
454
+ # This thread facilitates the delivery of messages to users who haven't yet
455
+ # accepted an invitation from us. When we attempt to deliver a message, if
456
+ # the user hasn't subscribed, we place the message in a queue for later
457
+ # delivery. Once a user has accepted our authorization request, we deliver
458
+ # any messages that have been queued up in the meantime.
459
+ def start_deferred_delivery_thread #:nodoc:
460
+ Thread.new {
461
+ loop {
462
+ messages = [queue(:pending_messages).pop].flatten
463
+ messages.each do |message|
464
+ if subscribed_to?(message[:to])
465
+ deliver(message[:to], message[:message], message[:type])
466
+ else
467
+ queue(:pending_messages) << message
468
+ end
469
+ end
470
+ }
471
+ }
472
+ end
473
+
474
+ def queue(queue)
475
+ @queues ||= Hash.new { |h,k| h[k] = Queue.new }
476
+ @queues[queue]
477
+ end
478
+
479
+ def dequeue(queue, non_blocking = true, max_items = 100, &block)
480
+ queue_items = []
481
+ max_items.times do
482
+ queue_item = queue(queue).pop(non_blocking) rescue nil
483
+ break if queue_item.nil?
484
+ queue_items << queue_item
485
+ yield queue_item if block_given?
486
+ end
487
+ queue_items
488
+ end
489
+ end
490
+ end
491
+
492
+ true
@@ -0,0 +1,242 @@
1
+ # Jabber::Simple - An extremely easy-to-use Jabber client library.
2
+ # Copyright 2006 Blaine Cook <blaine@obvious.com>, Obvious Corp.
3
+ #
4
+ # Jabber::Simple is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # Jabber::Simple is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with Jabber::Simple; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
+ #
18
+ $:.unshift "#{File.dirname(__FILE__)}/../lib"
19
+
20
+ require 'test/unit'
21
+ require 'timeout'
22
+ require 'xmpp4r-simple'
23
+
24
+ class JabberSimpleTest < Test::Unit::TestCase
25
+
26
+ def setup
27
+ @@connections ||= {}
28
+
29
+ if @@connections.include?(:client1)
30
+ @client1 = @@connections[:client1]
31
+ @client2 = @@connections[:client2]
32
+ @client1.accept_subscriptions = true
33
+ @client2.accept_subscriptions = true
34
+ @jid1_raw = @@connections[:jid1_raw]
35
+ @jid2_raw = @@connections[:jid2_raw]
36
+ @jid1 = @jid1_raw.strip.to_s
37
+ @jid2 = @jid2_raw.strip.to_s
38
+ return true
39
+ end
40
+
41
+ logins = []
42
+ begin
43
+ logins = File.readlines(File.expand_path("~/.xmpp4r-simple-test-config")).map! { |login| login.split(" ") }
44
+ raise StandardError unless logins.size == 2
45
+ rescue => e
46
+ puts "\nConfiguration Error!\n\nYou must make available two unique Jabber accounts in order for the tests to pass."
47
+ puts "Place them in ~/.xmpp4r-simple-test-config, one per line like so:\n\n"
48
+ puts "user1@example.com/res password"
49
+ puts "user2@example.com/res password\n\n"
50
+ raise e
51
+ end
52
+
53
+ @@connections[:client1] = Jabber::Simple.new(*logins[0])
54
+ @@connections[:client2] = Jabber::Simple.new(*logins[1])
55
+
56
+ @@connections[:jid1_raw] = Jabber::JID.new(logins[0][0])
57
+ @@connections[:jid2_raw] = Jabber::JID.new(logins[1][0])
58
+
59
+ # Force load the client and roster, just to be safe.
60
+ @@connections[:client1].roster
61
+ @@connections[:client2].roster
62
+
63
+ # Re-run this method to setup the local instance variables the first time.
64
+ setup
65
+ end
66
+
67
+ def test_ensure_the_jabber_clients_are_connected_after_setup
68
+ assert @client1.client.is_connected?
69
+ assert @client2.client.is_connected?
70
+ end
71
+
72
+ def test_inspect_should_be_custom
73
+ assert_equal "Jabber::Simple #{@jid1_raw}", @client1.inspect
74
+ end
75
+
76
+ def test_inspect_contact_should_be_custom
77
+ assert_equal "Jabber::Contact #{@jid2}", @client1.contacts(@jid2).inspect
78
+ end
79
+
80
+ def test_remove_users_from_our_roster_should_succeed
81
+ @client2.remove(@jid1)
82
+ @client1.remove(@jid2)
83
+
84
+ sleep 3
85
+
86
+ assert_equal false, @client1.subscribed_to?(@jid2)
87
+ assert_equal false, @client2.subscribed_to?(@jid1)
88
+ end
89
+
90
+ def test_add_users_to_our_roster_should_succeed_with_automatic_approval
91
+ @client1.remove(@jid2)
92
+ @client2.remove(@jid1)
93
+
94
+ assert_before 60 do
95
+ assert_equal false, @client1.subscribed_to?(@jid2)
96
+ assert_equal false, @client2.subscribed_to?(@jid1)
97
+ end
98
+
99
+ @client1.new_subscriptions
100
+ @client1.add(@jid2)
101
+
102
+ assert_before 60 do
103
+ assert @client1.subscribed_to?(@jid2)
104
+ assert @client2.subscribed_to?(@jid1)
105
+ end
106
+
107
+ new_subscriptions = @client1.new_subscriptions
108
+ assert_equal 1, new_subscriptions.size
109
+ assert_equal @jid2, new_subscriptions[0][0].jid.strip.to_s
110
+ end
111
+
112
+ def test_sent_message_should_be_received
113
+ # First clear the client's message queue, just in case.
114
+ assert_kind_of Array, @client2.received_messages
115
+
116
+ # Next ensure that we're not subscribed, so that we can test the deferred message queue.
117
+ @client1.remove(@jid2)
118
+ @client2.remove(@jid1)
119
+ sleep 2
120
+
121
+ # Deliver the messages; this should be received by the other client.
122
+ @client1.deliver(@jid2, "test message")
123
+
124
+ sleep 2
125
+
126
+ # Fetch the message; allow up to ten seconds for the delivery to occur.
127
+ messages = []
128
+ begin
129
+ Timeout::timeout(20) {
130
+ loop do
131
+ messages = @client2.received_messages
132
+ break unless messages.empty?
133
+ sleep 1
134
+ end
135
+ }
136
+ rescue Timeout::Error
137
+ flunk "Timeout waiting for message"
138
+ end
139
+
140
+ # Ensure that the message was received intact.
141
+ assert_equal @jid1, messages.first.from.strip.to_s
142
+ assert_equal "test message", messages.first.body
143
+ end
144
+
145
+ def test_presence_updates_should_be_received
146
+
147
+ @client2.add(@client1)
148
+ @client1.add(@client2)
149
+
150
+ assert_before(60) { assert @client2.subscribed_to?(@jid1) }
151
+ assert_before(60) { assert_equal 0, @client2.presence_updates.size }
152
+
153
+ @client1.status(:away, "Doing something else.")
154
+
155
+ new_statuses = []
156
+ assert_before(60) do
157
+ new_statuses = @client2.presence_updates
158
+ assert_equal 1, new_statuses.size
159
+ end
160
+
161
+ new_status = new_statuses.first
162
+ assert_equal @jid1, new_status[0]
163
+ assert_equal "Doing something else.", new_status[2]
164
+ assert_equal :away, new_status[1]
165
+ end
166
+
167
+ def test_disable_auto_accept_subscription_requests
168
+ @client1.remove(@jid2)
169
+ @client2.remove(@jid1)
170
+
171
+ assert_before(60) do
172
+ assert_equal false, @client1.subscribed_to?(@jid2)
173
+ assert_equal false, @client2.subscribed_to?(@jid1)
174
+ end
175
+
176
+ @client1.accept_subscriptions = false
177
+ assert_equal false, @client1.accept_subscriptions?
178
+
179
+ assert_before(60) { assert_equal 0, @client1.subscription_requests.size }
180
+
181
+ @client2.add(@jid1)
182
+
183
+ new_subscription_requests = []
184
+ assert_before(60) do
185
+ new_subscription_requests = @client1.subscription_requests
186
+ assert_equal 1, new_subscription_requests.size
187
+ end
188
+
189
+ new_subscription = new_subscription_requests.first
190
+ assert_equal @jid2, new_subscription[0].jid.strip.to_s
191
+ assert_equal :subscribe, new_subscription[1].type
192
+ end
193
+
194
+ def test_automatically_reconnect
195
+ @client1.client.close
196
+
197
+ assert_before 60 do
198
+ assert_equal false, @client1.connected?
199
+ end
200
+
201
+ # empty client 2's received message queue.
202
+ @client2.received_messages
203
+
204
+ @client1.deliver(@jid2, "Testing")
205
+
206
+ assert_before(60) { assert @client1.connected? }
207
+ assert @client1.roster.instance_variable_get('@stream').is_connected?
208
+ assert_before(60) { assert_equal 1, @client2.received_messages.size }
209
+ end
210
+
211
+ def test_disconnect_doesnt_allow_auto_reconnects
212
+ @client1.disconnect
213
+
214
+ assert_equal false, @client1.connected?
215
+
216
+ assert_raises Jabber::ConnectionError do
217
+ @client1.deliver(@jid2, "testing")
218
+ end
219
+
220
+ @client1.reconnect
221
+ end
222
+
223
+ private
224
+
225
+ def assert_before(seconds, &block)
226
+ error = nil
227
+ begin
228
+ Timeout::timeout(seconds) {
229
+ begin
230
+ yield
231
+ rescue => e
232
+ error = e
233
+ sleep 0.5
234
+ retry
235
+ end
236
+ }
237
+ rescue Timeout::Error
238
+ raise error
239
+ end
240
+ end
241
+
242
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xmpp4r-simple-19
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Blaine Cook
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: xmpp4r
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.3.2
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.3.2
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rcov
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: ! " Jabber::Simple takes the strong foundation laid by xmpp4r\n and
63
+ hides the relatively high complexity of maintaining a simple instant\n messenger
64
+ bot in Ruby.\n"
65
+ email: romeda@gmail.com
66
+ executables: []
67
+ extensions: []
68
+ extra_rdoc_files:
69
+ - README
70
+ - COPYING
71
+ files:
72
+ - test/test_xmpp4r_simple.rb
73
+ - lib/xmpp4r-simple.rb
74
+ - README
75
+ - COPYING
76
+ - CHANGELOG
77
+ homepage: https://github.com/treeder/xmpp4r-simple
78
+ licenses: []
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 1.8.24
98
+ signing_key:
99
+ specification_version: 3
100
+ summary: A simplified Jabber client library.
101
+ test_files:
102
+ - test/test_xmpp4r_simple.rb