etherpad-lite 0.1.0 → 0.2.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.
data/CHANGELOG CHANGED
@@ -1,3 +1,8 @@
1
+ ** RELEASE 0.2.0 (?) **
2
+
3
+ * Adds support for multiple API versions
4
+ * Use rest_client
5
+
1
6
  ** RELEASE 0.1.0 (07/07/2012) **
2
7
 
3
8
  * Requires Etherpad Lite 1.1+
@@ -7,13 +7,13 @@ See {github.com/Pita/etherpad-lite}[https://github.com/Pita/etherpad-lite] for i
7
7
  == Installation
8
8
  gem install etherpad-lite
9
9
 
10
- *NOTE* Support for Ruby 1.8.x is deprecated and will be removed in future versions. Upgrade to Ruby 1.9.x and stop being an old fuddie-duddie. It's the IE6 of Ruby.
10
+ *NOTE* Nobody likes you, Ruby 1.8. Now go away.
11
11
 
12
12
  == Basic usage
13
13
  require 'etherpad-lite'
14
14
 
15
15
  # Connect to your Etherpad Lite instance
16
- ether = EtherpadLite.connect('http://etherpad-lite.example.com', 'the api key')
16
+ ether = EtherpadLite.connect('http://etherpad-lite.example.com', 'the api key', 1.1)
17
17
 
18
18
  # Get a Pad (or create one if it doesn't exist)
19
19
  pad = ether.pad('my first etherpad lite pad')
@@ -55,14 +55,14 @@ Some example controller actions:
55
55
  class EtherpadController < ApplicationController
56
56
  # /etherpad
57
57
  def index
58
- # The idea is that your users are probably members of some kind of groups.
58
+ # Your users are probably members of some kind of groups.
59
59
  # These groups can be mapped to EtherpadLite Groups. List all the user's groups.
60
60
  @app_groups = current_user.groups
61
61
  end
62
62
 
63
63
  # /etherpad/groups/:id
64
64
  def group
65
- ether = EtherpadLite.connect(:local, File.new('/var/www/etherpad-lite/APIKEY.txt'))
65
+ ether = EtherpadLite.connect(9001, File.new('/srv/www/etherpad-lite/APIKEY.txt'))
66
66
  @app_group = YourAppGroup.find(params[:id])
67
67
  # Map your app's group to an EtherpadLite Group, and list all its pads
68
68
  group = ether.group("my_app_group_#{@app_group.id}")
@@ -71,7 +71,7 @@ Some example controller actions:
71
71
 
72
72
  # /etherpad/pads/:ep_group_id/:ep_pad_name
73
73
  def pad
74
- ether = EtherpadLite.connect(:local, File.new('/var/www/etherpad-lite/APIKEY.txt'))
74
+ ether = EtherpadLite.connect(9001, File.new('/srv/www/etherpad-lite/APIKEY.txt'))
75
75
  # Get the EtherpadLite Group and Pad by id
76
76
  @group = ether.get_group(params[:ep_group_id])
77
77
  @pad = @group.pad(params[:ep_pad_name])
@@ -89,24 +89,19 @@ Some example controller actions:
89
89
  end
90
90
  end
91
91
 
92
- == Why is the Ruby client so different from the others? What gives you the right?!?
93
- Most of the EPL clients are extremely thin wrappers around the HTTP API. The Ruby client offers a slightly higher level of abstraction,
94
- with the goal of making it as simple, powerful, and dare I say fun, as possible. That said, there are times when a bare-bones client may be
95
- preferable. In a hopefully not misguided attempt to please everyone, the Ruby client is written in two layers:
96
- - A high-level "models" interface starting with EtherpadLite::Instance (above)
97
- - A low-level client matching the HTTP API (EtherpadLite::Client, below)
92
+ == Low-level client
98
93
 
99
- In the examples above, the low-level client is accessible from the high-level interface:
94
+ There is an optional low-level client, which is a paper-thin wrapper around Etherpad Lite's HTTP JSON API. In the examples above, the low-level client is accessible from the high-level interface:
100
95
  client = ether.client
101
- client.getText('my first etherpad lite pad')
96
+ client.getText(padID: 'my first etherpad lite pad')
102
97
  => {:text => "What hath God wrought?"}
103
98
 
104
99
  or you can explicitly load just the low-level client:
105
100
  require 'etherpad-lite/client'
106
- client = EtherpadLite::Client.new('http://beta.etherpad.org/api', 'api key')
101
+ client = EtherpadLite.client('http://beta.etherpad.org/api', 'api key', 1.1)
102
+ client.setText(padID: 'my first etherpad lite pad', text: "lots of new text and stuff")
107
103
 
108
- The methods available to the low-level client should precisely match the HTTP API. In general, it behaves identically to the other
109
- clients, particularly PHP's and Python's.
104
+ The methods and parameters exactly match the HTTP API. For a full list of them, see {github.com/Pita/etherpad-lite/wiki/HTTP-API}[https://github.com/Pita/etherpad-lite/wiki/HTTP-API].
110
105
 
111
106
  == Testing
112
107
  Testing this library is fairly simple. It requires:
@@ -1,344 +1,77 @@
1
1
  require 'uri'
2
- require 'net/http'
3
- require 'net/https'
4
2
  require 'json'
5
-
6
- # Ruby 1.8.x may not work, and I don't care
7
- BAD_RUBY = RUBY_VERSION < '1.9.0' # :nodoc:
8
-
3
+ require 'rest_client'
4
+
5
+ # A client library for Etherpad Lite's JSON API.
6
+ #
7
+ # A thin wrapper around the HTTP API. See the API documentation at https://github.com/Pita/etherpad-lite/wiki/HTTP-API.
8
+ #
9
+ # client = EtherpadLite.client('http://localhost:9001', 'api key', '1.1')
10
+ # client.getText(:padID => 'foo')
11
+ # => {:text => "Pad text"}
12
+ # client.setText(:padID => 'padID', :text => 'new pad text')
13
+ #
14
+ # A higher-level interface to the API:
15
+ #
16
+ # ether = EtherpadLite.connect('http://localhost:9001', 'api key', '1.1')
17
+ # pad = ether.pad('padID')
18
+ # puts pad.text
19
+ # => 'Pad text'
20
+ # pad.text = 'new pad text'
9
21
  module EtherpadLite
10
22
  # An error returned by the server
11
- class APIError < StandardError
12
- MESSAGE = "Error while talking to the API (%s). Make sure you are running the latest version of the Etherpad Lite server. If that is not possible, try rolling this client back to an earlier version."
13
- end
23
+ Error = Class.new(StandardError)
14
24
 
15
- # A thin wrapper around Etherpad Lite's HTTP JSON API
25
+ # Returns a new EtherpadLite::Client.
16
26
  #
17
- # client1 = EtherpadLite::Client.new('https://etherpad.yoursite.com[https://etherpad.yoursite.com]', 'your api key')
27
+ # client = EtherpadLite.client('https://etherpad.yoursite.com', 'your api key', '1.1')
18
28
  #
19
- # # An alias for http://localhost:9001
20
- # client2 = EtherpadLite::Client.new(:local, File.new('/path/to/APIKEY.txt'))
21
- #
22
- # # An alias for http://localhost:9001
23
- # ether = EtherpadLite::Client.new(9001, File.new('/path/to/APIKEY.txt'))
24
- class Client
25
- # Aliases to common Etherpad Lite hosts
26
- HOST_ALIASES = {:local => 'http://localhost:9001',
27
- :localhost => 'http://localhost:9001'}
28
-
29
- # The Etherpad Lite HTTP API version this library version targets
30
- API_VERSION = 1
31
-
32
- # HTTP API response code for okay
33
- CODE_OK = 0
34
- # HTTP API response code for invalid method parameters
35
- CODE_INVALID_PARAMETERS = 1
36
- # HTTP API response code for Etherpad Lite error
37
- CODE_INTERNAL_ERROR = 2
38
- # HTTP API response code for invalid api method
39
- CODE_INVALID_METHOD = 3
40
- # HTTP API response code for invalid api key
41
- CODE_INVALID_API_KEY = 4
29
+ # client = EtherpadLite.client(9001, 'your api key', '1.1') # Alias to http://localhost:9001
30
+ def self.client(url_or_port, api_key_or_file, api_version=1)
31
+ Client.new(url_or_port, api_key_or_file, api_version)
32
+ end
42
33
 
34
+ # A thin wrapper around Etherpad Lite's HTTP JSON API. See the API documentation at https://github.com/Pita/etherpad-lite/wiki/HTTP-API.
35
+ class Client
43
36
  # A URI object containing the URL of the Etherpad Lite instance
44
37
  attr_reader :uri
45
38
  # The API key
46
39
  attr_reader :api_key
40
+ # The API version string
41
+ attr_reader :api_version
47
42
 
48
- class << self
49
- # Path to the system's CA certs (for connecting over SSL)
50
- attr_accessor :ca_path
51
- end
52
-
53
- # Instantiate a new Etherpad Lite Client.
54
- #
55
- # client1 = EtherpadLite::Client.new('https://etherpad.yoursite.com[https://etherpad.yoursite.com]', 'your api key')
56
- #
57
- # # An alias for http://localhost:9001
58
- # client2 = EtherpadLite::Client.new(:local, File.new('/path/to/APIKEY.txt'))
59
- #
60
- # # An alias for http://localhost:9001
61
- # client3 = EtherpadLite::Client.new(9001, File.new('/path/to/APIKEY.txt'))
62
- def initialize(host_or_alias, api_key_or_file)
63
- # Parse the host
64
- url = if host_or_alias.is_a? Symbol
65
- raise ArgumentError, %Q|Unknown host alias "#{host_or_alias}"| unless HOST_ALIASES.has_key? host_or_alias
66
- HOST_ALIASES[host_or_alias]
67
- elsif host_or_alias.is_a? Fixnum
68
- "http://localhost:#{host_or_alias}"
69
- else
70
- host_or_alias.clone
71
- end
72
- url << '/api' unless url =~ /\/api$/i
73
- @uri = URI.parse(url)
74
- raise ArgumentError, "#{url} does not contain a valid host and port" unless @uri.host and @uri.port
75
-
76
- # Parse the api key
77
- if api_key_or_file.is_a? File
78
- @api_key = begin
79
- api_key_or_file.read
80
- rescue IOError
81
- api_key_or_file.reopen(api_key_or_file.path, mode='r')
82
- api_key_or_file.read
83
- end
84
- api_key_or_file.close
85
- else
86
- @api_key = api_key_or_file
87
- end
88
-
89
- connect!
90
- end
91
-
92
- # Groups
93
- # Pads can belong to a group. There will always be public pads which don't belong to a group.
94
-
95
- # Creates a new Group
96
- def createGroup
97
- post :createGroup
98
- end
99
-
100
- # Creates a new Group for groupMapper if one doesn't already exist. Helps you map your application's groups to Etherpad Lite's groups.
101
- def createGroupIfNotExistsFor(groupMapper)
102
- post :createGroupIfNotExistsFor, :groupMapper => groupMapper
103
- end
104
-
105
- # Deletes a group
106
- def deleteGroup(groupID)
107
- post :deleteGroup, :groupID => groupID
108
- end
109
-
110
- # Returns all the Pads in the given Group
111
- def listPads(groupID)
112
- get :listPads, :groupID => groupID
113
- end
114
-
115
- # Creates a new Pad in the given Group
116
- def createGroupPad(groupID, padName, text=nil)
117
- params = {:groupID => groupID, :padName => padName}
118
- params[:text] = text unless text.nil?
119
- post :createGroupPad, params
120
- end
121
-
122
- # Authors
123
- # These authors are bound to the attributes the users choose (color and name).
124
-
125
- # Create a new Author
126
- def createAuthor(name=nil)
127
- params = {}
128
- params[:name] = name unless name.nil?
129
- post :createAuthor, params
130
- end
131
-
132
- # Creates a new Author for authorMapper if one doesn't already exist. Helps you map your application's authors to Etherpad Lite's authors.
133
- def createAuthorIfNotExistsFor(authorMapper, name=nil)
134
- params = {:authorMapper => authorMapper}
135
- params[:name] = name unless name.nil?
136
- post :createAuthorIfNotExistsFor, params
137
- end
138
-
139
- def listPadsOfAuthor(authorID)
140
- get :listPadsOfAuthor, :authorID => authorID
141
- end
142
-
143
- # Sessions
144
- # Sessions can be created between a group and an author. This allows
145
- # an author to access more than one group. The sessionID will be set as
146
- # a cookie to the client and is valid until a certian date.
147
-
148
- # Creates a new Session for the given Author in the given Group
149
- def createSession(groupID, authorID, validUntil)
150
- post :createSession, :groupID => groupID, :authorID => authorID, :validUntil => validUntil
151
- end
152
-
153
- # Deletes the given Session
154
- def deleteSession(sessionID)
155
- post :deleteSession, :sessionID => sessionID
156
- end
157
-
158
- # Returns information about the Session
159
- def getSessionInfo(sessionID)
160
- get :getSessionInfo, :sessionID => sessionID
161
- end
162
-
163
- # Returns all Sessions in the given Group
164
- def listSessionsOfGroup(groupID)
165
- get :listSessionsOfGroup, :groupID => groupID
166
- end
167
-
168
- # Returns all Sessions belonging to the given Author
169
- def listSessionsOfAuthor(authorID)
170
- get :listSessionsOfAuthor, :authorID => authorID
171
- end
172
-
173
- # Pad content
174
- # Pad content can be updated and retrieved through the API
175
-
176
- # Returns the text of the given Pad. Optionally pass a revision number to get the text for that revision.
177
- def getText(padID, rev=nil)
178
- params = {:padID => padID}
179
- params[:rev] = rev unless rev.nil?
180
- get :getText, params
43
+ # Instantiate a new Etherpad Lite Client. You may pass a full url or just a port number. The api key may be a string
44
+ # or a File object. If you do not specify an API version, it will default to the latest version that is supported.
45
+ def initialize(url_or_port, api_key_or_file, api_version=1)
46
+ url_or_port = "http://localhost:#{url_or_port}" if url_or_port.is_a? Integer
47
+ @uri = URI.parse(url_or_port)
48
+ @api_key = api_key_or_file.is_a?(IO) ? api_key_or_file.read : api_key_or_file
49
+ @api_version = api_version.to_s
181
50
  end
182
51
 
183
- # Sets the text of the given Pad
184
- def setText(padID, text)
185
- post :setText, :padID => padID, :text => text
52
+ # Call an API method
53
+ def method_missing(method, params={})
54
+ request = method =~ /^(set|create|delete)/ \
55
+ ? ->(url, params) { RestClient.post(url, params) } \
56
+ : ->(url, params) { RestClient.get(url, :params => params) }
57
+ call(method, params, &request)
186
58
  end
187
59
 
188
- # Returns the text of the given Pad as HTML. Optionally pass a revision number to get the HTML for that revision.
189
- def getHTML(padID, rev=nil)
190
- params = {:padID => padID}
191
- params[:rev] = rev unless rev.nil?
192
- get :getHTML, params
193
- end
194
-
195
- # Sets the HTML text of the given Pad
196
- def setHTML(padID, html)
197
- post :setHTML, :padID => padID, :html => html
198
- end
199
-
200
- # Pad
201
- # Group pads are normal pads, but with the name schema
202
- # GROUPID$PADNAME. A security manager controls access of them and
203
- # forbids normal pads from including a "$" in the name.
204
-
205
- # Create a new Pad. Optionally specify the initial text.
206
- def createPad(padID, text=nil)
207
- params = {:padID => padID}
208
- params[:text] = text unless text.nil?
209
- post :createPad, params
210
- end
211
-
212
- # Returns the number of revisions the given Pad contains
213
- def getRevisionsCount(padID)
214
- get :getRevisionsCount, :padID => padID
215
- end
216
-
217
- # Returns the number of users currently editing the pad
218
- def padUsersCount(padID)
219
- get :padUsersCount, :padID => padID
220
- end
221
-
222
- # Returns the time the pad was last edited as a Unix timestamp
223
- def getLastEdited(padID)
224
- get :getLastEdited, :padID => padID
225
- end
226
-
227
- # Delete the given Pad
228
- def deletePad(padID)
229
- post :deletePad, :padID => padID
230
- end
231
-
232
- # Returns the Pad's read-only id
233
- def getReadOnlyID(padID)
234
- get :getReadOnlyID, :padID => padID
235
- end
236
-
237
- def listAuthorsOfPad(padID)
238
- get :listAuthorsOfPad, :padID => padID
239
- end
240
-
241
- # Sets a boolean for the public status of a Pad
242
- def setPublicStatus(padID, publicStatus)
243
- post :setPublicStatus, :padID => padID, :publicStatus => publicStatus
244
- end
245
-
246
- # Gets a boolean for the public status of a Pad
247
- def getPublicStatus(padID)
248
- get :getPublicStatus, :padID => padID
249
- end
250
-
251
- # Sets the password on a pad
252
- def setPassword(padID, password)
253
- post :setPassword, :padID => padID, :password => password
254
- end
255
-
256
- # Returns true if the Pad has a password, false if not
257
- def isPasswordProtected(padID)
258
- get :isPasswordProtected, :padID => padID
259
- end
260
-
261
- # Returns true if the connection to the Etherpad Lite instance is using SSL/HTTPS.
262
- def secure?
263
- @uri.port == 443
264
- end
265
-
266
- protected
267
-
268
- # Alias to "call" using the GET HTTP method
269
- def get(method, params={})
270
- call method, params, :get
271
- end
272
-
273
- # Alias to "call" using the POST HTTP method
274
- def post(method, params={})
275
- call method, params, :post
276
- end
60
+ private
277
61
 
278
62
  # Calls the EtherpadLite API and returns the :data portion of the response Hash.
279
- #
280
- # "method" should be a valid API method name, as a String or Symbol.
281
- #
282
- # "params" should be any URL or form parameters as a Hash.
283
- #
284
- # "http_method" should be :get or :post, defaults to :get.
285
- #
286
- def call(method, params={}, http_method=:get)
63
+ # If the API response contains an error code, an exception is raised.
64
+ def call(api_method, params={}, &request)
287
65
  params[:apikey] = @api_key
288
- uri = [@uri.path, API_VERSION, method].compact.join('/')
289
- http_method = :post if BAD_RUBY # XXX A horrible, horrible hack for Ruby 1.8
290
- req = case http_method
291
- when :get then Net::HTTP::Get.new(uri << '?' << URI.encode_www_form(params))
292
- when :post
293
- post = Net::HTTP::Post.new(uri)
294
- post.set_form_data(params)
295
- post
296
- else raise ArgumentError, "#{http_method} is not a valid HTTP method"
297
- end
298
- response = @http.request(req)
299
- handleResult response.body
300
- end
66
+ url = [@uri.to_s, 'api', self.api_version, api_method].compact.join('/')
67
+ json = request.(url, params).to_s
68
+ response = JSON.parse(json, :symbolize_names => true)
301
69
 
302
- # Parses the JSON response from the server, returning the data object as a Hash with symbolized keys.
303
- # If the API response contains an error code, an exception is raised.
304
- def handleResult(response)
305
- begin
306
- response = JSON.parse(response, :symbolize_names => true)
307
- rescue JSON::ParserError
308
- raise APIError, APIError::MESSAGE % response
309
- end
310
70
  case response[:code]
311
- when CODE_OK then response[:data]
312
- when CODE_INVALID_PARAMETERS, CODE_INVALID_API_KEY, CODE_INVALID_METHOD
313
- raise ArgumentError, response[:message]
314
- else
315
- raise APIError, "An unknown error ocurrced while handling the response: #{response.to_s}"
316
- end
317
- end
318
-
319
- private
320
-
321
- # Initialize the HTTP connection object
322
- def connect!
323
- @http = Net::HTTP.new(@uri.host, @uri.port)
324
- if secure?
325
- @http.use_ssl = true
326
- if self.class.ca_path
327
- @http.verify_mode = OpenSSL::SSL::VERIFY_PEER
328
- @http.ca_path = self.class.ca_path
329
- else
330
- @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
331
- end
71
+ when 0 then response[:data]
72
+ when (1..4) then raise Error, response[:message]
73
+ else raise Error, "An unknown error ocurrced while handling the API response: #{response.to_s}"
332
74
  end
333
75
  end
334
76
  end
335
77
  end
336
-
337
- # Try to find the system's CA certs
338
- %w{/etc/ssl/certs /etc/ssl /usr/share/ssl /usr/lib/ssl /System/Library/OpenSSL /usr/local/ssl}.each do |path|
339
- EtherpadLite::Client.ca_path = path and break if File.exists? path
340
- end
341
- $stderr.puts %q|WARNING Ruby etherpad-lite client was unable to find your CA Certificates; HTTPS connections will *not* be verified! You may remedy this with "EtherpadLite::Client.ca_path = '/path/to/certs'"| unless EtherpadLite::Client.ca_path
342
-
343
- # Warn about old Ruby versions
344
- $stderr.puts "WARNING You are using an ancient version of Ruby which may not be supported. Upgrade Ruby!" if BAD_RUBY
@@ -7,7 +7,7 @@ module EtherpadLite
7
7
  #
8
8
  # Author Examples:
9
9
  #
10
- # @ether = EtherpadLite.connect(:local, 'api key')
10
+ # @ether = EtherpadLite.connect(9001, 'api key')
11
11
  #
12
12
  # # Create a new author with both a name and a mapper
13
13
  # author1 = @ether.create_author(:name => 'Theodor Seuss Geisel', :mapper => 'author_1')
@@ -45,8 +45,6 @@ module EtherpadLite
45
45
  attr_reader :instance
46
46
  # The author's id
47
47
  attr_reader :id
48
- # The author's name (if any)
49
- attr_reader :name
50
48
  # The author's foreign mapper (if any)
51
49
  attr_reader :mapper
52
50
 
@@ -61,8 +59,8 @@ module EtherpadLite
61
59
  # name => Author's name
62
60
  def self.create(instance, options={})
63
61
  result = options[:mapper] \
64
- ? instance.client.createAuthorIfNotExistsFor(options[:mapper], options[:name]) \
65
- : instance.client.createAuthor(options[:name])
62
+ ? instance.client.createAuthorIfNotExistsFor(authorMapper: options[:mapper], name: options[:name]) \
63
+ : instance.client.createAuthor(options)
66
64
  new instance, result[:authorID], options
67
65
  end
68
66
 
@@ -77,12 +75,16 @@ module EtherpadLite
77
75
  @instance = instance
78
76
  @id = id
79
77
  @mapper = options[:mapper]
80
- @name = options[:name]
78
+ end
79
+
80
+ # Returns the author's name
81
+ def name
82
+ @name ||= @instance.client.getAuthorName(authorID: @id)
81
83
  end
82
84
 
83
85
  # Returns an array of pad ids that this author has edited
84
86
  def pad_ids
85
- @instance.client.listPadsOfAuthor(@id)[:padIDs] || []
87
+ @instance.client.listPadsOfAuthor(authorID: @id)[:padIDs] || []
86
88
  end
87
89
 
88
90
  # Returns an array of Pads that this author has edited
@@ -97,13 +99,13 @@ module EtherpadLite
97
99
 
98
100
  # Returns all session ids from this Author
99
101
  def session_ids
100
- s = @instance.client.listSessionsOfAuthor(@id) || {}
102
+ s = @instance.client.listSessionsOfAuthor(authorID: @id) || {}
101
103
  s.keys
102
104
  end
103
105
 
104
106
  # Returns all sessions from this Author
105
107
  def sessions
106
- s = @instance.client.listSessionsOfAuthor(@id) || {}
108
+ s = @instance.client.listSessionsOfAuthor(authorID: @id) || {}
107
109
  s.map { |id,info| Session.new(@instance, id, info) }
108
110
  end
109
111
  end
@@ -64,7 +64,7 @@ module EtherpadLite
64
64
  # mapper => your foreign group id
65
65
  def self.create(instance, options={})
66
66
  result = options[:mapper] \
67
- ? instance.client.createGroupIfNotExistsFor(options[:mapper]) \
67
+ ? instance.client.createGroupIfNotExistsFor(groupMapper: options[:mapper]) \
68
68
  : instance.client.createGroup
69
69
  new instance, result[:groupID], options
70
70
  end
@@ -111,7 +111,7 @@ module EtherpadLite
111
111
 
112
112
  # Returns an array of all the Pad ids in this Group.
113
113
  def pad_ids
114
- @instance.client.listPads(@id)[:padIDs]
114
+ @instance.client.listPads(groupID: @id)[:padIDs]
115
115
  end
116
116
 
117
117
  # Create a new session for author that will last length_in_minutes.
@@ -121,19 +121,19 @@ module EtherpadLite
121
121
 
122
122
  # Returns all session ids in this Group
123
123
  def session_ids
124
- s = @instance.client.listSessionsOfGroup(@id) || {}
124
+ s = @instance.client.listSessionsOfGroup(groupID: @id) || {}
125
125
  s.keys
126
126
  end
127
127
 
128
128
  # Returns all sessions in this Group
129
129
  def sessions
130
- s = @instance.client.listSessionsOfGroup(@id) || {}
130
+ s = @instance.client.listSessionsOfGroup(groupID: @id) || {}
131
131
  s.map { |id,info| Session.new(@instance, id, info) }
132
132
  end
133
133
 
134
134
  # Deletes the Group
135
135
  def delete
136
- @instance.client.deleteGroup(@id)
136
+ @instance.client.deleteGroup(groupID: @id)
137
137
  end
138
138
 
139
139
  private
@@ -1,35 +1,23 @@
1
1
  module EtherpadLite
2
2
  # Returns an EtherpadLite::Instance object.
3
3
  #
4
- # ether1 = EtherpadLite.connect('https://etherpad.yoursite.com[https://etherpad.yoursite.com]', 'your api key')
4
+ # ether = EtherpadLite.client('https://etherpad.yoursite.com', 'your api key', '1.1')
5
5
  #
6
- # # An alias for http://localhost:9001
7
- # ether2 = EtherpadLite.connect(:local, File.new('/path/to/APIKEY.txt'))
8
- #
9
- # # An alias for http://localhost:9001
10
- # ether3 = EtherpadLite.connect(9001, File.new('/path/to/APIKEY.txt'))
11
- def self.connect(host_or_alias, api_key_or_file)
12
- Instance.new(host_or_alias, api_key_or_file)
6
+ # ether = EtherpadLite.client(9001, 'your api key', '1.1') # Alias to http://localhost:9001
7
+ def self.connect(url_or_port, api_key_or_file, api_version=1)
8
+ Instance.new(url_or_port, api_key_or_file, api_version)
13
9
  end
14
10
 
15
- # EtherpadLite::Instance provides a high-level interface to the EtherpadLite::Client class. See the EtherpadLite module
16
- # for examples of how to establish a connection.
11
+ # A high-level interface to EtherpadLite::Client.
17
12
  class Instance
18
13
  include Padded
19
14
  # Stores the EtherpadLite::Client object used to power this Instance
20
15
  attr_reader :client
21
16
 
22
- # Instantiate a new Etherpad Lite Instance. The url should include the protocol (i.e. http or https).
23
- #
24
- # ether1 = EtherpadLite::Instance.new('https://etherpad.yoursite.com[https://etherpad.yoursite.com]', 'your api key')
25
- #
26
- # # An alias for http://localhost:9001
27
- # ether2 = EtherpadLite::Instance.new(:local, File.new('/path/to/APIKEY.txt'))
28
- #
29
- # # An alias for http://localhost:9001
30
- # ether3 = EtherpadLite::Instance.new(9001, File.new('/path/to/APIKEY.txt'))
31
- def initialize(host_or_alias, api_key_or_file)
32
- @client = Client.new(host_or_alias, api_key_or_file)
17
+ # Instantiate a new Etherpad Lite Instance. You may pass a full url or just a port number. The api key may be a string
18
+ # or a File object.
19
+ def initialize(url_or_port, api_key_or_file, api_version=1)
20
+ @client = Client.new(url_or_port, api_key_or_file, api_version)
33
21
  end
34
22
 
35
23
  # Returns, creating if necessary, a Group mapped to your foreign system's group
@@ -52,6 +40,16 @@ module EtherpadLite
52
40
  Group.create self, options
53
41
  end
54
42
 
43
+ # Returns an array of all group IDs
44
+ def group_ids
45
+ @client.listAllGroups[:groupIDs]
46
+ end
47
+
48
+ # Returns an array of all Group objects
49
+ def groups
50
+ group_ids.map { |id| Group.new self, id }
51
+ end
52
+
55
53
  # Returns, creating if necessary, a Author mapped to your foreign system's author
56
54
  #
57
55
  # Options:
@@ -24,12 +24,12 @@ module EtherpadLite
24
24
  def self.create(instance, id, options={})
25
25
  if options[:groupID]
26
26
  group = Group.new instance, options[:groupID]
27
- instance.client.createGroupPad(group.id, degroupify_pad_id(id), options[:text])
27
+ instance.client.createGroupPad(groupID: group.id, padName: degroupify_pad_id(id), text: options[:text])
28
28
  else
29
29
  group = nil
30
- instance.client.createPad(id, options[:text])
30
+ instance.client.createPad(padID: id, text: options[:text])
31
31
  end
32
- new instance, id, :groupID => options[:groupID], :group => group
32
+ new instance, id, groupID: options[:groupID], group: group
33
33
  end
34
34
 
35
35
  # Remove the group id portion of a Group Pad's id
@@ -84,13 +84,14 @@ module EtherpadLite
84
84
  #
85
85
  # rev => revision_number
86
86
  def text(options={})
87
+ options[:padID] = @id
87
88
  options[:rev] ||= @rev unless @rev.nil?
88
- @instance.client.getText(@id, options[:rev])[:text]
89
+ @instance.client.getText(options)[:text]
89
90
  end
90
91
 
91
92
  # Writes txt to the Pad. There is no 'save' method; it is written immediately.
92
93
  def text=(txt)
93
- @instance.client.setText(@id, txt)
94
+ @instance.client.setText(padID: @id, text: txt)
94
95
  end
95
96
 
96
97
  # Returns the Pad's text as HTML. Unless you specified a :rev when instantiating the Pad, or specify one here, this will return the latest revision.
@@ -99,18 +100,19 @@ module EtherpadLite
99
100
  #
100
101
  # rev => revision_number
101
102
  def html(options={})
103
+ options[:padID] = @id
102
104
  options[:rev] ||= @rev unless @rev.nil?
103
- @instance.client.getHTML(@id, options[:rev])[:html]
105
+ @instance.client.getHTML(options)[:html]
104
106
  end
105
107
 
106
108
  # Writes HTML to the Pad. There is no 'save' method; it is written immediately.
107
109
  def html=(html)
108
- @instance.client.setHTML(@id, html)
110
+ @instance.client.setHTML(padID: @id, html: html)
109
111
  end
110
112
 
111
113
  # Returns an Array of all this Pad's revision numbers
112
114
  def revision_numbers
113
- max = @instance.client.getRevisionsCount(@id)[:revisions]
115
+ max = @instance.client.getRevisionsCount(padID: @id)[:revisions]
114
116
  (0..max).to_a
115
117
  end
116
118
 
@@ -119,25 +121,30 @@ module EtherpadLite
119
121
  revision_numbers.map { |n| Pad.new(@instance, @id, :rev => n) }
120
122
  end
121
123
 
124
+ # Returns an array of users hashes, representing users currently using the pad
125
+ def users
126
+ @instance.client.padUsers(padID: @id)[:padUsers]
127
+ end
128
+
122
129
  # Returns the number of users currently editing a pad
123
130
  def user_count
124
- @instance.client.padUsersCount(@id)[:padUsersCount]
131
+ @instance.client.padUsersCount(padID: @id)[:padUsersCount]
125
132
  end
126
133
  alias_method :users_count, :user_count
127
134
 
128
135
  # Returns the Pad's read-only id. This is cached.
129
136
  def read_only_id
130
- @read_only_id ||= @instance.client.getReadOnlyID(@id)[:readOnlyID]
137
+ @read_only_id ||= @instance.client.getReadOnlyID(padID: @id)[:readOnlyID]
131
138
  end
132
139
 
133
140
  # Returns the time the pad was last edited as a Unix timestamp
134
141
  def last_edited
135
- @instance.client.getLastEdited(@id)[:lastEdited]
142
+ @instance.client.getLastEdited(padID: @id)[:lastEdited]
136
143
  end
137
144
 
138
145
  # Returns an array of ids of authors who've edited this pad
139
146
  def author_ids
140
- @instance.client.listAuthorsOfPad(@id)[:authorIDs] || []
147
+ @instance.client.listAuthorsOfPad(padID: @id)[:authorIDs] || []
141
148
  end
142
149
 
143
150
  # Returns an array of Authors who've edited this pad
@@ -148,13 +155,13 @@ module EtherpadLite
148
155
  # Returns true if this is a public Pad (opposite of private?).
149
156
  # This only applies to Pads belonging to a Group.
150
157
  def public?
151
- @instance.client.getPublicStatus(@id)[:publicStatus]
158
+ @instance.client.getPublicStatus(padID: @id)[:publicStatus]
152
159
  end
153
160
 
154
161
  # Set the pad's public status to true or false (opposite of private=)
155
162
  # This only applies to Pads belonging to a Group.
156
163
  def public=(status)
157
- @instance.client.setPublicStatus(@id, status)
164
+ @instance.client.setPublicStatus(padID: @id, publicStatus: status)
158
165
  end
159
166
 
160
167
  # Returns true if this is a private Pad (opposite of public?)
@@ -172,18 +179,18 @@ module EtherpadLite
172
179
  # Returns true if this Pad has a password, false if not.
173
180
  # This only applies to Pads belonging to a Group.
174
181
  def password?
175
- @instance.client.isPasswordProtected(@id)[:isPasswordProtected]
182
+ @instance.client.isPasswordProtected(padID: @id)[:isPasswordProtected]
176
183
  end
177
184
 
178
185
  # Sets the Pad's password.
179
186
  # This only applies to Pads belonging to a Group.
180
187
  def password=(new_password)
181
- @instance.client.setPassword(@id, new_password)
188
+ @instance.client.setPassword(padID: @id, password: new_password)
182
189
  end
183
190
 
184
191
  # Deletes the Pad
185
192
  def delete
186
- @instance.client.deletePad(@id)
193
+ @instance.client.deletePad(padID: @id)
187
194
  end
188
195
  end
189
196
  end
@@ -8,7 +8,7 @@ module EtherpadLite
8
8
  begin
9
9
  Pad.create(instance, id, options)
10
10
  # Pad alreaded exists
11
- rescue ArgumentError
11
+ rescue Error
12
12
  Pad.new(instance, id, options)
13
13
  end
14
14
  end
@@ -16,7 +16,7 @@ module EtherpadLite
16
16
  # Creates a new Session between a Group and an Author. The session will expire after length_in_min.
17
17
  def self.create(instance, group_id, author_id, length_in_min)
18
18
  valid_until = Time.now.to_i + length_in_min * 60
19
- result = instance.client.createSession(group_id, author_id, valid_until)
19
+ result = instance.client.createSession(groupID: group_id, authorID: author_id, validUntil: valid_until)
20
20
  new instance, result[:sessionID]
21
21
  end
22
22
 
@@ -64,14 +64,14 @@ module EtherpadLite
64
64
 
65
65
  # Deletes the Session
66
66
  def delete
67
- @instance.client.deleteSession(@id)
67
+ @instance.client.deleteSession(sessionID: @id)
68
68
  end
69
69
 
70
70
  private
71
71
 
72
72
  # Request and cache session info from the server
73
73
  def get_info
74
- @info ||= @instance.client.getSessionInfo(@id)
74
+ @info ||= @instance.client.getSessionInfo(sessionID: @id)
75
75
  end
76
76
  end
77
77
  end
@@ -1,5 +1,4 @@
1
1
  module EtherpadLite
2
- MAJOR_VERSION, MINOR_VERSION, TINY_VERSION, PRE_VERSION = 0, 1, 0, nil # :nodoc:
3
- # The client version
4
- VERSION = [MAJOR_VERSION, MINOR_VERSION, TINY_VERSION, PRE_VERSION].compact.join '.'
2
+ # The library version
3
+ VERSION = '0.2.0'
5
4
  end
@@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/spec_helper'
2
2
 
3
3
  describe EtherpadLite::Author do
4
4
  before do
5
- @eth = EtherpadLite.connect TEST_CONFIG[:url], TEST_CONFIG[:api_key_file] || TEST_CONFIG[:api_key]
5
+ @eth = EtherpadLite.connect TEST_CONFIG[:url], TEST_CONFIG[:api_key_file] || TEST_CONFIG[:api_key], TEST_CONFIG[:api_version]
6
6
  end
7
7
 
8
8
  it "should be created" do
@@ -27,4 +27,11 @@ describe EtherpadLite::Author do
27
27
  author.pad_ids.should == []
28
28
  author.pads.should == []
29
29
  end
30
+
31
+ if TEST_CONFIG[:api_version] > 1
32
+ it "should get the name" do
33
+ author = @eth.create_author :mapper => 'Author B', :name => 'Jim Jimmers'
34
+ author.name.should == 'Jim Jimmers'
35
+ end
36
+ end
30
37
  end
@@ -1,3 +1,5 @@
1
- :url: 9003
2
- #:api_key: UrdBfgceHYDZs4yr1rQ12rVOzo31XfEW
1
+ :url: 9001
2
+ :api_key: #kvwTIROV9p2h2gjNRCrO8fVa6pNXSqOc
3
+ # Full filesystem path to APIKEY.txt, may be used instead of the above :api_key setting
3
4
  :api_key_file: /home/jhollinger/devel/etherpad-lite/APIKEY.txt
5
+ :api_version: 1.1
@@ -2,3 +2,4 @@
2
2
  :api_key: your api key
3
3
  # Full filesystem path to APIKEY.txt, may be used instead of the above :api_key setting
4
4
  :api_key_file:
5
+ :api_version: 1.1
@@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/spec_helper'
2
2
 
3
3
  describe EtherpadLite::Group do
4
4
  before do
5
- @eth = EtherpadLite.connect TEST_CONFIG[:url], TEST_CONFIG[:api_key_file] || TEST_CONFIG[:api_key]
5
+ @eth = EtherpadLite.connect TEST_CONFIG[:url], TEST_CONFIG[:api_key_file] || TEST_CONFIG[:api_key], TEST_CONFIG[:api_version]
6
6
  end
7
7
 
8
8
  it "should be created" do
@@ -81,6 +81,19 @@ describe EtherpadLite::Group do
81
81
  pad.text.should == "abc\n"
82
82
  end
83
83
 
84
+ if TEST_CONFIG[:api_version] > 1
85
+ it "should list all group ids" do
86
+ group_ids = @eth.group_ids
87
+ group_ids.size.should == 4
88
+ end
89
+
90
+ it "should list all groups" do
91
+ groups = @eth.groups
92
+ groups.size.should == 4
93
+ groups.first.class.name.should == 'EtherpadLite::Group'
94
+ end
95
+ end
96
+
84
97
  context 'Group Pad' do
85
98
  context 'Privacy' do
86
99
  it "should be private" do
@@ -2,24 +2,11 @@ require File.dirname(__FILE__) + '/spec_helper'
2
2
 
3
3
  describe EtherpadLite::Instance do
4
4
  before do
5
- @eth = EtherpadLite.connect TEST_CONFIG[:url], TEST_CONFIG[:api_key_file] || TEST_CONFIG[:api_key]
5
+ @eth = EtherpadLite.connect TEST_CONFIG[:url], TEST_CONFIG[:api_key_file] || TEST_CONFIG[:api_key], TEST_CONFIG[:api_version]
6
6
  end
7
7
 
8
8
  it "should have the right API key" do
9
- api_key = if TEST_CONFIG[:api_key_file]
10
- begin
11
- TEST_CONFIG[:api_key_file].read
12
- rescue IOError
13
- TEST_CONFIG[:api_key_file].reopen(TEST_CONFIG[:api_key_file].path, mode='r')
14
- TEST_CONFIG[:api_key_file].read
15
- end
16
- else
17
- TEST_CONFIG[:api_key]
18
- end
9
+ api_key = TEST_CONFIG[:api_key_file] || TEST_CONFIG[:api_key]
19
10
  @eth.client.api_key.should == api_key
20
11
  end
21
-
22
- it "shouldn't be secure" do
23
- @eth.client.secure?.should == false
24
- end
25
12
  end
@@ -2,14 +2,14 @@ require File.dirname(__FILE__) + '/spec_helper'
2
2
 
3
3
  describe EtherpadLite::Pad do
4
4
  before do
5
- @eth = EtherpadLite.connect TEST_CONFIG[:url], TEST_CONFIG[:api_key_file] || TEST_CONFIG[:api_key]
5
+ @eth = EtherpadLite.connect TEST_CONFIG[:url], TEST_CONFIG[:api_key_file] || TEST_CONFIG[:api_key], TEST_CONFIG[:api_version]
6
6
  end
7
7
 
8
8
  it "should blow up when querying a non-existing pad" do
9
9
  pad = @eth.get_pad 'a non-existant pad'
10
10
  begin
11
11
  txt = pad.text
12
- rescue ArgumentError => e
12
+ rescue EtherpadLite::Error => e
13
13
  end
14
14
  e.message.should == 'padID does not exist'
15
15
  end
@@ -22,7 +22,7 @@ describe EtherpadLite::Pad do
22
22
  it "should blow up when creating a Pad that already exists" do
23
23
  begin
24
24
  pad = @eth.create_pad 'my new pad', :text => 'The initial text'
25
- rescue ArgumentError => e
25
+ rescue EtherpadLite::Error => e
26
26
  end
27
27
  e.message.should == 'padID does already exist'
28
28
  end
@@ -104,8 +104,25 @@ describe EtherpadLite::Pad do
104
104
  pad.user_count.should == 0
105
105
  end
106
106
 
107
+ it "should not crash when setting null text" do
108
+ err_msg = nil
109
+ begin
110
+ @eth.client.setText(:padID => 'brand new pad3')
111
+ rescue EtherpadLite::Error => e
112
+ err_msg = e.message
113
+ end
114
+ err_msg.should == 'text is no string'
115
+ end
116
+
107
117
  it "should be deleted" do
108
118
  @eth.get_pad('another new pad').delete
109
119
  @eth.create_pad('another new pad').id.should_not == nil
110
120
  end
121
+
122
+ if TEST_CONFIG[:api_version] > 1
123
+ it "should get the pad users" do
124
+ puts @eth.pad('pad with users').users
125
+ @eth.pad('pad with users').users.should == []
126
+ end
127
+ end
111
128
  end
@@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/spec_helper'
2
2
 
3
3
  describe EtherpadLite::Session do
4
4
  before do
5
- @eth = EtherpadLite.connect TEST_CONFIG[:url], TEST_CONFIG[:api_key_file] || TEST_CONFIG[:api_key]
5
+ @eth = EtherpadLite.connect TEST_CONFIG[:url], TEST_CONFIG[:api_key_file] || TEST_CONFIG[:api_key], TEST_CONFIG[:api_version]
6
6
  end
7
7
 
8
8
  it "should be created for a Group" do
@@ -11,4 +11,4 @@ end
11
11
  # Load test config
12
12
  require 'yaml'
13
13
  TEST_CONFIG = YAML.load_file(File.dirname(__FILE__) + '/config.yml')
14
- TEST_CONFIG[:api_key_file] = File.new(TEST_CONFIG[:api_key_file]) unless TEST_CONFIG[:api_key_file].nil?
14
+ TEST_CONFIG[:api_key_file] = File.open(TEST_CONFIG[:api_key_file], &:read) unless TEST_CONFIG[:api_key_file].nil?
metadata CHANGED
@@ -1,84 +1,82 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: etherpad-lite
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 1
8
- - 0
9
- version: 0.1.0
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
10
6
  platform: ruby
11
- authors:
7
+ authors:
12
8
  - Jordan Hollinger
13
9
  autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
-
17
- date: 2012-07-07 00:00:00 -04:00
18
- default_executable:
19
- dependencies: []
20
-
12
+ date: 2012-07-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rest-client
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '1.6'
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: '1.6'
21
30
  description: etherpad-lite is a Ruby interface to Etherpad Lite's HTTP JSON API
22
31
  email: jordan@jordanhollinger.com
23
32
  executables: []
24
-
25
33
  extensions: []
26
-
27
- extra_rdoc_files:
34
+ extra_rdoc_files:
28
35
  - README.rdoc
29
- files:
36
+ files:
30
37
  - lib/etherpad-lite.rb
31
- - lib/etherpad-lite/models.rb
32
- - lib/etherpad-lite/models/session.rb
33
38
  - lib/etherpad-lite/models/padded.rb
39
+ - lib/etherpad-lite/models/group.rb
40
+ - lib/etherpad-lite/models/pad.rb
34
41
  - lib/etherpad-lite/models/author.rb
35
42
  - lib/etherpad-lite/models/instance.rb
36
- - lib/etherpad-lite/models/pad.rb
37
- - lib/etherpad-lite/models/group.rb
43
+ - lib/etherpad-lite/models/session.rb
38
44
  - lib/etherpad-lite/client.rb
39
45
  - lib/etherpad-lite/version.rb
46
+ - lib/etherpad-lite/models.rb
47
+ - spec/pad_spec.rb
48
+ - spec/config.yml.example
49
+ - spec/config.yml
40
50
  - spec/spec_helper.rb
41
51
  - spec/instance_spec.rb
42
52
  - spec/session_spec.rb
43
- - spec/group_spec.rb
44
- - spec/config.yml
45
- - spec/config.yml.example
46
53
  - spec/author_spec.rb
47
- - spec/pad_spec.rb
54
+ - spec/group_spec.rb
48
55
  - README.rdoc
49
56
  - CHANGELOG
50
57
  - LICENSE
51
- has_rdoc: true
52
58
  homepage: http://github.com/jhollinger/ruby-etherpad-lite
53
59
  licenses: []
54
-
55
60
  post_install_message:
56
61
  rdoc_options: []
57
-
58
- require_paths:
62
+ require_paths:
59
63
  - lib
60
- required_ruby_version: !ruby/object:Gem::Requirement
64
+ required_ruby_version: !ruby/object:Gem::Requirement
61
65
  none: false
62
- requirements:
63
- - - ">="
64
- - !ruby/object:Gem::Version
65
- segments:
66
- - 0
67
- version: "0"
68
- required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
71
  none: false
70
- requirements:
71
- - - ">="
72
- - !ruby/object:Gem::Version
73
- segments:
74
- - 0
75
- version: "0"
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
76
  requirements: []
77
-
78
77
  rubyforge_project:
79
- rubygems_version: 1.3.7
78
+ rubygems_version: 1.8.21
80
79
  signing_key:
81
80
  specification_version: 3
82
81
  summary: A Ruby client library for Etherpad Lite
83
82
  test_files: []
84
-