etherpad-lite 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
-