swivel 0.0.21 → 0.0.96
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/Rakefile +0 -1
- data/bin/swivel +5 -16
- data/lib/swivel.rb +292 -91
- metadata +2 -12
data/Rakefile
CHANGED
data/bin/swivel
CHANGED
@@ -3,11 +3,8 @@
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'active_support' # TODO: make it work w/o this
|
5
5
|
require 'optparse'
|
6
|
-
require File.dirname(__FILE__) + '/../lib/swivel'
|
7
6
|
require 'yaml'
|
8
|
-
|
9
|
-
#require 'ruby-debug'
|
10
|
-
#Debugger.start
|
7
|
+
require File.dirname(__FILE__) + '/../lib/swivel'
|
11
8
|
|
12
9
|
options = Hash.new
|
13
10
|
config = Swivel::Connection::Config.new
|
@@ -67,12 +64,12 @@ class SwivelHelper
|
|
67
64
|
|
68
65
|
def show resource, id
|
69
66
|
resource = resource.pluralize
|
70
|
-
response = @swivel.call "
|
67
|
+
response = @swivel.call "/#{resource}/#{id}"
|
71
68
|
end
|
72
69
|
|
73
70
|
def list resource, options = Hash.new
|
74
71
|
resource = resource.pluralize
|
75
|
-
response = @swivel.call "
|
72
|
+
response = @swivel.call "/#{resource}", options
|
76
73
|
end
|
77
74
|
|
78
75
|
def upload name, options = Hash.new
|
@@ -89,21 +86,13 @@ class SwivelHelper
|
|
89
86
|
|
90
87
|
def append id, options = Hash.new
|
91
88
|
filename = options[:filename]
|
92
|
-
data_set = @swivel.append :id => id,
|
93
|
-
:original_asset_name => filename,
|
94
|
-
:original_asset_path => filename,
|
95
|
-
:auto_estimate => true,
|
96
|
-
:data => read(filename)
|
89
|
+
data_set = @swivel.append :id => id, :data => read(filename)
|
97
90
|
puts "appended #{data_set.id}"
|
98
91
|
end
|
99
92
|
|
100
93
|
def replace id, options = Hash.new
|
101
94
|
filename = options[:filename]
|
102
|
-
data_set = @swivel.replace :id => id,
|
103
|
-
:original_asset_name => filename,
|
104
|
-
:original_asset_path => filename,
|
105
|
-
:auto_estimate => true,
|
106
|
-
:data => read(filename)
|
95
|
+
data_set = @swivel.replace :id => id, :data => read(filename)
|
107
96
|
puts "replaced #{data_set.id}"
|
108
97
|
end
|
109
98
|
|
data/lib/swivel.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'active_support' # TODO: make this work w/o active_support?
|
3
3
|
require 'base64'
|
4
|
+
require 'benchmark'
|
4
5
|
require 'cgi'
|
5
6
|
require 'cobravsmongoose'
|
6
7
|
require 'fileutils'
|
@@ -8,6 +9,10 @@ require 'net/http'
|
|
8
9
|
require 'rexml/document'
|
9
10
|
require 'yaml'
|
10
11
|
|
12
|
+
class Time
|
13
|
+
RFC822_DATETIME_REGEX = /\w{3}, \d{2} \w{3} \d{4} \d{2}:\d{2}:\d{2} [+-]\d{4}/
|
14
|
+
end
|
15
|
+
|
11
16
|
class String
|
12
17
|
|
13
18
|
# Returns true if the string looks numeric
|
@@ -15,7 +20,9 @@ class String
|
|
15
20
|
# 'howdy'.numeric? # => false
|
16
21
|
|
17
22
|
def numeric?
|
18
|
-
|
23
|
+
# work around the bug in ruby ^ regexp matching
|
24
|
+
string_sans_newlines = self.gsub(/\r|\n/, ' ')
|
25
|
+
(string_sans_newlines =~ /^-?\d+(\.\d+|\d*)$/) != nil
|
19
26
|
end
|
20
27
|
|
21
28
|
# Returns the string with '_' translated to '-'
|
@@ -37,19 +44,7 @@ class String
|
|
37
44
|
# 'DataSet'.to_xml_tag # => "data-set"
|
38
45
|
|
39
46
|
def to_xml_tag
|
40
|
-
self.underscore.
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
class Hash
|
45
|
-
|
46
|
-
# Returns a query string generated from keys and values. CGI escaped, of course.
|
47
|
-
# { :name => 'huned', :year => 2007 }.to_query_string # => "name=huned&year=2007"
|
48
|
-
|
49
|
-
def to_query_string
|
50
|
-
keys.map do |k|
|
51
|
-
"#{CGI.escape k.to_s}=#{CGI.escape self[k].to_s}"
|
52
|
-
end.join('&')
|
47
|
+
self.underscore.dasherize
|
53
48
|
end
|
54
49
|
end
|
55
50
|
|
@@ -123,8 +118,6 @@ end
|
|
123
118
|
|
124
119
|
module Swivel
|
125
120
|
|
126
|
-
class ApiError < StandardError; end
|
127
|
-
|
128
121
|
# Encapsulates XML that Swivel returns from an API call. Generally, you'll
|
129
122
|
# never need to instantiate a Swivel::Response object. Use one of its subclasses
|
130
123
|
# instead:
|
@@ -135,23 +128,23 @@ module Swivel
|
|
135
128
|
# * Swivel::DataColumn
|
136
129
|
# * Swivel::Graph
|
137
130
|
# * Swivel::User
|
138
|
-
#
|
131
|
+
# * others
|
139
132
|
|
140
133
|
class Response
|
141
134
|
|
142
|
-
attr_accessor :refreshed_at
|
135
|
+
attr_accessor :refreshed_at, :disable_auto_refresh
|
136
|
+
|
137
|
+
def self.resource
|
138
|
+
nil
|
139
|
+
end
|
143
140
|
|
144
141
|
# Instantiate from XML returned from Swivel.
|
145
142
|
def initialize xml = nil, connection = nil
|
146
143
|
@connection = connection
|
147
|
-
@
|
144
|
+
@disable_auto_refresh = @connection && @connection.disable_auto_refresh
|
145
|
+
@xml_tag = self.class.name.demodulize.to_xml_tag
|
148
146
|
@doc = REXML::Document.new xml
|
149
147
|
if @response = REXML::XPath.first(@doc, '/response')
|
150
|
-
if error = REXML::XPath.first(@doc, '/response/error')
|
151
|
-
message = error.attribute('message').to_s
|
152
|
-
code = error.attribute('code').to_s
|
153
|
-
raise ApiError, "#{message} (#{code})"
|
154
|
-
end
|
155
148
|
# if it's a full response, strip away the outer cruft
|
156
149
|
@doc = REXML::Document.new @response.elements[1].to_s
|
157
150
|
end
|
@@ -161,29 +154,30 @@ module Swivel
|
|
161
154
|
# pretty flexible in what it returns:
|
162
155
|
#
|
163
156
|
# * text from attributes
|
164
|
-
# data_set = swivel.call '/
|
157
|
+
# data_set = swivel.call '/data_sets/1005309'
|
165
158
|
# data_set.to_xml # => "<data-set swivel-id=\"1005309\"> ..."
|
166
159
|
#
|
167
160
|
# # invokes method_missing
|
168
161
|
# data_set.id # => 1005309
|
169
162
|
# * text from elements
|
170
|
-
# data_set = swivel.call '/
|
163
|
+
# data_set = swivel.call '/data_sets/1005309'
|
171
164
|
# data_set.to_xml # => "<data-set ...><name>Swivel API</name> ..."
|
172
165
|
#
|
173
166
|
# # invokes method_missing
|
174
167
|
# data_set.name # => "Swivel API"
|
175
168
|
# * objects that inherit from Swivel::Response (including Swivel::List)
|
176
|
-
# data_set = swivel.call '/
|
169
|
+
# data_set = swivel.call '/data_sets/1005309'
|
177
170
|
# data_set.to_xml # => "<data-set ...><user swivel-id=\"1000010\"> ..."
|
178
171
|
#
|
179
172
|
# # invokes method_missing
|
180
173
|
# data_set.user.class # => Swivel::User
|
181
174
|
|
182
175
|
def method_missing method_id
|
183
|
-
select_element = "/#{@xml_tag}/#{method_id.to_s.to_xml_tag}"
|
184
|
-
select_attribute = "/#{@xml_tag}/@#{method_id.to_s.to_xml_tag}"
|
185
|
-
select_list = "/#{@xml_tag}/list[@resource=\"#{method_id.to_s.singularize.to_xml_tag}\"]"
|
176
|
+
select_element = "/#{self.class.resource ? self.class.resource : @xml_tag}/#{method_id.to_s.to_xml_tag}"
|
177
|
+
select_attribute = "/#{self.class.resource ? self.class.resource : @xml_tag}/@#{method_id.to_s.to_xml_tag}"
|
178
|
+
select_list = "/#{self.class.resource ? self.class.resource : @xml_tag}/list[@resource=\"#{method_id.to_s.singularize.to_xml_tag}\"]"
|
186
179
|
|
180
|
+
|
187
181
|
if el = REXML::XPath.first(@doc, select_element)
|
188
182
|
if el.attribute('swivel-id')
|
189
183
|
Response.class_for(el.name).new(el.to_s, @connection)
|
@@ -200,8 +194,8 @@ module Swivel
|
|
200
194
|
raise NoMethodError, "#{method_id} isn't a method of #{self.class.name}"
|
201
195
|
end
|
202
196
|
rescue Exception => e
|
203
|
-
if @retried || @refreshed_at
|
204
|
-
|
197
|
+
if @disable_auto_refresh || @retried || @refreshed_at
|
198
|
+
nil
|
205
199
|
else
|
206
200
|
@retried = true
|
207
201
|
refresh! true
|
@@ -209,25 +203,43 @@ module Swivel
|
|
209
203
|
end
|
210
204
|
end
|
211
205
|
|
206
|
+
# Alternate access to properties. Same as dot notation, so user[:name] == user.name
|
207
|
+
|
208
|
+
def [](attribute)
|
209
|
+
case attribute
|
210
|
+
when Symbol, String
|
211
|
+
self.send(attribute)
|
212
|
+
else
|
213
|
+
super
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
212
217
|
# Returns the unique id in swivel for this object. Ids are unique
|
213
218
|
# for each resource.
|
214
|
-
# user = swivel.call '/
|
219
|
+
# user = swivel.call '/users/1000010'
|
215
220
|
# user.id # => 1000010
|
216
221
|
# user.id == user.swivel_id # => true
|
217
222
|
|
218
223
|
def id
|
219
224
|
swivel_id
|
220
225
|
end
|
226
|
+
|
227
|
+
# Same as id method, added to make Swivel objects play well with url_for
|
228
|
+
# in Rails.
|
229
|
+
|
230
|
+
def to_param
|
231
|
+
id.to_s
|
232
|
+
end
|
221
233
|
|
222
234
|
# Refreshes the object's content from Swivel.
|
223
235
|
#
|
224
|
-
# data_set = swivel.call '/
|
236
|
+
# data_set = swivel.call '/data_sets/1005309'
|
225
237
|
# user = data_set.user
|
226
238
|
# user.refresh! # populate the object fully from Swivel
|
227
239
|
|
228
240
|
def refresh! force = false
|
229
241
|
if @connection && (force || @refreshed_at.blank?)
|
230
|
-
refreshed = @connection.call "
|
242
|
+
refreshed = @connection.call "/#{@xml_tag.undashify}s/#{id}"
|
231
243
|
if refreshed.is_a? self.class
|
232
244
|
@doc = REXML::Document.new refreshed.to_xml
|
233
245
|
@refreshed_at = Time.now
|
@@ -237,13 +249,19 @@ module Swivel
|
|
237
249
|
end
|
238
250
|
|
239
251
|
# Returns the underlying XML string for this object as a string.
|
240
|
-
# user = swivel.call '/
|
252
|
+
# user = swivel.call '/users/1000010'
|
241
253
|
# puts user.to_xml
|
242
254
|
|
243
255
|
def to_xml
|
244
256
|
@doc.to_s
|
245
257
|
end
|
246
258
|
|
259
|
+
# Try to be somewhat useful about to_s. Maybe not great.
|
260
|
+
|
261
|
+
def to_s
|
262
|
+
self.class.name.demodulize + ' ' + (name || title || inspect)
|
263
|
+
end
|
264
|
+
|
247
265
|
protected
|
248
266
|
# Return an appropriate Swivel::Response subclass for the given resource.
|
249
267
|
# Swivel::Response.class_for 'data-set' # => Swivel::DataSet
|
@@ -251,20 +269,72 @@ module Swivel
|
|
251
269
|
# Swivel::Response.class_for 'list' # => Swivel::List
|
252
270
|
|
253
271
|
def self.class_for resource
|
254
|
-
|
272
|
+
new_class_name = resource.undashify.classify
|
273
|
+
"Swivel::#{new_class_name}".constantize
|
274
|
+
rescue NameError => e
|
275
|
+
Swivel.const_set new_class_name.to_sym, Class.new(Swivel::Response)
|
276
|
+
returning "Swivel::#{new_class_name}".constantize do |c|
|
277
|
+
c.class_eval "def self.resource; '#{resource}'; end"
|
278
|
+
end
|
255
279
|
rescue
|
256
280
|
nil
|
257
281
|
end
|
258
282
|
|
259
283
|
def value_for s #:nordoc:
|
260
|
-
|
284
|
+
if s.match Time::RFC822_DATETIME_REGEX
|
285
|
+
Time.parse s
|
286
|
+
elsif s.numeric?
|
287
|
+
s.to_i
|
288
|
+
else
|
289
|
+
s
|
290
|
+
end
|
261
291
|
end
|
262
292
|
end
|
263
293
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
294
|
+
class Annotation < Response; end
|
295
|
+
class Comment < Response; end
|
296
|
+
class Page < Response; end
|
297
|
+
class PageAsset < Response; end
|
298
|
+
class Prose < Response; end
|
299
|
+
|
300
|
+
class Permission < Response
|
301
|
+
%w/accessor accessable/.each do |thing|
|
302
|
+
define_method thing.to_sym do
|
303
|
+
unless instance_variable_get "@#{thing}".to_sym
|
304
|
+
c =
|
305
|
+
begin
|
306
|
+
dashified = thing.dasherize
|
307
|
+
o = REXML::XPath.first(@doc, "/permission/#{dashified}").elements[1]
|
308
|
+
Response.class_for(o.name).new o.to_s
|
309
|
+
rescue
|
310
|
+
warn e
|
311
|
+
nil
|
312
|
+
end
|
313
|
+
instance_variable_set "@#{thing}", c
|
314
|
+
end
|
315
|
+
instance_variable_get "@#{thing}"
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
class Activity < Response
|
321
|
+
%w/source subject verb direct_object prepositional_object/.each do |part|
|
322
|
+
define_method part.to_sym do
|
323
|
+
unless instance_variable_get "@#{part}".to_sym
|
324
|
+
c =
|
325
|
+
begin
|
326
|
+
dashified = part.dasherize
|
327
|
+
o = REXML::XPath.first(@doc, "/activity/#{dashified}").elements[1]
|
328
|
+
Response.class_for(o.name).new o.to_s
|
329
|
+
rescue
|
330
|
+
warn e
|
331
|
+
nil
|
332
|
+
end
|
333
|
+
instance_variable_set "@#{part}", c
|
334
|
+
end
|
335
|
+
instance_variable_get "@#{part}"
|
336
|
+
end
|
337
|
+
end
|
268
338
|
end
|
269
339
|
|
270
340
|
class DataSet < Response
|
@@ -301,15 +371,61 @@ module Swivel
|
|
301
371
|
@doc = replaced_data_set.instance_variable_get :@doc
|
302
372
|
self
|
303
373
|
end
|
374
|
+
|
375
|
+
def csv
|
376
|
+
@connection.call "/data_sets/csv/#{id}"
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
class Visual < Response
|
381
|
+
|
382
|
+
## Forms the url for an image.
|
383
|
+
def image_url options={}
|
384
|
+
options = {:format => "png", :s => Time.now.to_i, :secret => secret}.merge(options)
|
385
|
+
host, port = @connection.config[:host], @connection.config[:port]
|
386
|
+
port = port == 80 ? '' : ":#{port}"
|
387
|
+
url = "http://#{host}#{port}/visuals/#{id}.#{options.delete(:format)}?#{@connection.requestify(options)}"
|
388
|
+
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
class User < Response
|
393
|
+
def admin?
|
394
|
+
admin.to_b
|
395
|
+
rescue
|
396
|
+
false
|
397
|
+
end
|
398
|
+
|
399
|
+
def to_param
|
400
|
+
name
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
class Group < Response
|
405
|
+
def members
|
406
|
+
@members ||=
|
407
|
+
begin
|
408
|
+
o = REXML::XPath.first(@doc, "/group/members").elements[1]
|
409
|
+
Response.class_for(o.name).new o.to_s
|
410
|
+
rescue
|
411
|
+
warn e
|
412
|
+
nil
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
def to_param
|
417
|
+
(!slug.blank? and slug) or super.to_param
|
418
|
+
end
|
304
419
|
end
|
305
420
|
|
306
421
|
class Graph < Response
|
307
422
|
def image_url options = Hash.new
|
308
|
-
host = @connection.config[:
|
423
|
+
host, port = @connection.config[:host], @connection.config[:port]
|
424
|
+
port = port == 80 ? '' : ":#{port}"
|
309
425
|
if variant == 'transient'
|
310
|
-
"http://#{host}/graphs/get/#{digest}"
|
426
|
+
"http://#{host}#{port}/graphs/get/#{digest}"
|
311
427
|
else
|
312
|
-
"http://#{host}/graphs/image/#{id}"
|
428
|
+
"http://#{host}#{port}/graphs/image/#{id}"
|
313
429
|
end
|
314
430
|
end
|
315
431
|
|
@@ -336,10 +452,10 @@ module Swivel
|
|
336
452
|
def save!
|
337
453
|
return self unless variant == 'transient'
|
338
454
|
options = {:save => 'true', :digest => self.digest}
|
339
|
-
# PUT /
|
455
|
+
# PUT /graphs/0 denotes updating a transient graph. Swivel
|
340
456
|
# will read the digest and save the transient graph that matches
|
341
457
|
# that digest.
|
342
|
-
saved_graph = @connection.call '/
|
458
|
+
saved_graph = @connection.call '/graphs/0', options, :put
|
343
459
|
@doc = saved_graph.instance_variable_get :@doc
|
344
460
|
@refreshed_at = nil
|
345
461
|
@retried = false
|
@@ -350,11 +466,11 @@ module Swivel
|
|
350
466
|
# Encapsulates lists of resources. Typically, items contained within the list are
|
351
467
|
# subclasses of Swivel::Response.
|
352
468
|
#
|
353
|
-
# data_sets = swivel.call '/
|
469
|
+
# data_sets = swivel.call '/data_sets'
|
354
470
|
# data_sets.class # => Swivel::List
|
355
471
|
# data_sets.collect do |d| d.class end # => [Swivel::DataSet, Swivel::DataSet, ...]
|
356
472
|
#
|
357
|
-
# users = swivel.call '/
|
473
|
+
# users = swivel.call '/users'
|
358
474
|
# users.class # => Swivel::List
|
359
475
|
# users.collect do |u| u.class end # => [Swivel::User, Swivel::User, ...]
|
360
476
|
|
@@ -366,6 +482,7 @@ module Swivel
|
|
366
482
|
unless @processed
|
367
483
|
@list = Array.new
|
368
484
|
resource = @doc.elements[1].attributes['resource']
|
485
|
+
resource ||= @doc.elements[1].elements[1].name
|
369
486
|
selector = "/list/#{resource}"
|
370
487
|
REXML::XPath.each @doc, selector do |e|
|
371
488
|
@list << Response.class_for(resource).new(e.to_s, @connection)
|
@@ -374,20 +491,36 @@ module Swivel
|
|
374
491
|
end
|
375
492
|
end
|
376
493
|
|
377
|
-
def refresh!
|
494
|
+
def refresh! force = false
|
378
495
|
self # don't call refresh! on a list
|
379
496
|
end
|
380
497
|
|
498
|
+
# Get the underlying Array for this Swivel::List object.
|
499
|
+
|
500
|
+
def to_a
|
501
|
+
@list
|
502
|
+
end
|
503
|
+
|
381
504
|
# Delegates methods to the underlying Array instance. Allows you to
|
382
505
|
# call Array methods on a Swivel::List.
|
383
506
|
#
|
384
|
-
# data_sets = swivel.call '/
|
507
|
+
# data_sets = swivel.call '/data_sets'
|
385
508
|
# # try some Array methods...
|
386
509
|
# data_sets.length.is_a? Integer # => true
|
387
510
|
# data_sets.first.is_a? Swivel::DataSet #=> true
|
388
511
|
|
389
512
|
def method_missing method_id, *args, &block
|
390
513
|
@list.send method_id, *args, &block
|
514
|
+
rescue NoMethodError => e
|
515
|
+
super
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
class ApiError < StandardError; end
|
520
|
+
|
521
|
+
class Error < Response
|
522
|
+
def raise
|
523
|
+
super ApiError, message, backtrace.split("\n")
|
391
524
|
end
|
392
525
|
end
|
393
526
|
|
@@ -408,11 +541,12 @@ module Swivel
|
|
408
541
|
|
409
542
|
class Config
|
410
543
|
CONFIG_DIR = ENV['HOME']
|
544
|
+
CONFIG_DIR = RAILS_ROOT if PLATFORM.include?('win32')
|
411
545
|
CONFIG_FILE = '.swivelrc'
|
412
546
|
DEFAULT_HOST = 'api.swivel.com'
|
413
547
|
DEFAULT_CONFIG = { :api_key => '', :protocol => 'http://',
|
414
548
|
:host => DEFAULT_HOST, :port => 80,
|
415
|
-
:timeout_up => 200, :timeout_down => 100 }
|
549
|
+
:timeout_up => 200, :timeout_down => 100, :path_prefix => '', :logfile => nil }
|
416
550
|
|
417
551
|
attr_accessor :config
|
418
552
|
|
@@ -433,8 +567,6 @@ module Swivel
|
|
433
567
|
else
|
434
568
|
YAML::load_file @filename
|
435
569
|
end
|
436
|
-
@config[:image_host] ||=
|
437
|
-
@config[:host] == DEFAULT_HOST ? 'www.swivel.com' : @config[:host]
|
438
570
|
@config
|
439
571
|
end
|
440
572
|
|
@@ -446,7 +578,7 @@ module Swivel
|
|
446
578
|
end
|
447
579
|
end
|
448
580
|
|
449
|
-
attr_accessor :config
|
581
|
+
attr_accessor :config, :disable_auto_refresh
|
450
582
|
|
451
583
|
# Instantiate a connection to Swivel. Use the connection to query or upload,
|
452
584
|
# append, or replace data. Passed in options will take precedence over values
|
@@ -454,26 +586,33 @@ module Swivel
|
|
454
586
|
|
455
587
|
def initialize options = Hash.new
|
456
588
|
@config = Config.new.config
|
457
|
-
@config.
|
458
|
-
@config[key] = options[key] if options.has_key? key
|
459
|
-
end
|
589
|
+
@config.merge! options
|
460
590
|
@headers = options[:headers] || Hash.new
|
461
591
|
@headers.merge! 'Accept' => 'application/xml'
|
462
592
|
end
|
463
593
|
|
594
|
+
# Keep track of how many calls have been made. Note that this means
|
595
|
+
# multiple Swivel::Connection instances increment the same call count
|
596
|
+
# if those Swivel::Connections run within the same thread.
|
597
|
+
cattr_accessor :calls
|
598
|
+
@@calls = []
|
599
|
+
def self.reset_calls!
|
600
|
+
@@calls = []
|
601
|
+
end
|
602
|
+
|
464
603
|
# Call Swivel's REST endpoint. This method actually performs the HTTP stuff
|
465
604
|
# that you need. and returns objects constructed from the returned XML. If an
|
466
605
|
# appropriate class is not available, just returns the XML string.
|
467
606
|
#
|
468
607
|
# # returns an object that's a subclass of Swivel::Response
|
469
|
-
# user = swivel.call '/
|
608
|
+
# user = swivel.call '/users/1000010'
|
470
609
|
# user.class # => Swivel::User
|
471
610
|
#
|
472
|
-
# users = swivel.call '/
|
611
|
+
# users = swivel.call '/users'
|
473
612
|
# users.class # => Swivel::List
|
474
613
|
#
|
475
614
|
# # returns a string (because an appropriate Swivel::Response subclass doesn't exist)
|
476
|
-
# echo = swivel.call '/
|
615
|
+
# echo = swivel.call '/test/echo/howdy'
|
477
616
|
# echo.class # => String
|
478
617
|
#
|
479
618
|
# However, calling all of those endpoints is tedious. For swivel's main objects
|
@@ -492,56 +631,97 @@ module Swivel
|
|
492
631
|
#
|
493
632
|
# All these convenience methoods (yeah, methoods) really do is prettify a get call
|
494
633
|
# to:
|
495
|
-
# /
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
634
|
+
# /object_type or /object_type/id
|
635
|
+
def call path, params = Hash.new, method = :get, extra_headers = Hash.new
|
636
|
+
path, query_string = path.split("?")
|
637
|
+
params = requestify(params)
|
638
|
+
query_string += "&" if params and query_string
|
639
|
+
params = "#{query_string}#{params}"
|
640
|
+
path = "/#{config[:path_prefix]}#{path}" unless config[:path_prefix].blank?
|
641
|
+
response = nil
|
642
|
+
elapsed_time = Benchmark.realtime do
|
643
|
+
response =
|
644
|
+
Net::HTTP.start config[:host], config[:port] do |http|
|
645
|
+
request_class = "Net::HTTP::#{method.to_s.camelize}".constantize
|
646
|
+
if [:delete, :post, :put].include? method
|
647
|
+
request = request_class.new path, headers.merge(extra_headers)
|
648
|
+
http.read_timeout = config[:timeout_up]
|
649
|
+
http.request request, params
|
650
|
+
else
|
651
|
+
request = request_class.new "#{path}?#{params}", headers.merge(extra_headers)
|
652
|
+
http.read_timeout = config[:timeout_down]
|
653
|
+
http.request request
|
654
|
+
end
|
508
655
|
end
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
656
|
+
end
|
657
|
+
@@calls << {:url => path, :time => elapsed_time}
|
658
|
+
body = response.body
|
659
|
+
log body
|
660
|
+
case response.content_type
|
661
|
+
when 'application/xml'
|
662
|
+
doc = REXML::Document.new body
|
663
|
+
Response.class_for(doc.root.elements[1].name).new body, self
|
664
|
+
when 'text/csv', 'text/plain'
|
665
|
+
body
|
666
|
+
else
|
667
|
+
raise "Unknown content type: #{response.content_type}"
|
668
|
+
end
|
514
669
|
rescue Exception => e
|
515
670
|
#xml.blank? ? nil : xml
|
516
|
-
warn "Failure while communicating with swivel
|
671
|
+
warn "Failure while communicating with swivel"
|
672
|
+
warn "Request: #{config[:host]}:#{config[:port]}#{path}"
|
673
|
+
warn "Response: #{body}"
|
517
674
|
raise e
|
518
675
|
end
|
519
676
|
|
520
|
-
|
677
|
+
def call! *args
|
678
|
+
returning call(*args) do |r|
|
679
|
+
r.raise if Swivel::Error === r
|
680
|
+
end
|
681
|
+
end
|
682
|
+
|
683
|
+
def log str
|
684
|
+
unless config[:logfile] == nil
|
685
|
+
unless @logger
|
686
|
+
@logger = Logger.new(config[:logfile])
|
687
|
+
@logger.level = Logger::INFO
|
688
|
+
end
|
689
|
+
@logger.info(str)
|
690
|
+
end
|
691
|
+
end
|
692
|
+
|
693
|
+
%w/ data_columns data_sets graphs roles submissions users groups /.each do |obj|
|
521
694
|
class_eval <<-LAZY
|
522
695
|
def #{obj} options = nil
|
523
696
|
case options
|
524
697
|
when Hash
|
525
698
|
if options[:id]
|
526
|
-
call "
|
699
|
+
call "/#{obj}/#\{options[:id]\}", options
|
527
700
|
else
|
528
|
-
call "
|
701
|
+
call "/#{obj}", options, :post
|
529
702
|
end
|
530
703
|
when Numeric, String, Symbol
|
531
|
-
call "
|
704
|
+
call "/#{obj}/#\{options\}"
|
532
705
|
else
|
533
|
-
call '
|
706
|
+
call '/#{obj}'
|
534
707
|
end
|
535
708
|
end
|
536
709
|
alias :#{obj.singularize} :#{obj}
|
710
|
+
|
711
|
+
def #{obj}! *args
|
712
|
+
returning #{obj}(*args) do |r|
|
713
|
+
r.raise if Swivel::Error === r
|
714
|
+
end
|
715
|
+
end
|
716
|
+
alias :#{obj.singularize}! :#{obj}!
|
537
717
|
LAZY
|
538
718
|
end
|
539
719
|
|
540
720
|
DEFAULT_UPLOAD_OPTIONS = {
|
541
721
|
:citation => 'swivel.rb',
|
542
|
-
:citation_url => 'swivel.com/developer',
|
722
|
+
:citation_url => 'www.swivel.com/developer',
|
543
723
|
:image_url => 'http://www.swivel.com/images/logo.png',
|
544
|
-
:image_source_url => 'http://swivel.com',
|
724
|
+
:image_source_url => 'http://www.swivel.com',
|
545
725
|
:display_tags => 'swivel api swivel.rb'
|
546
726
|
}
|
547
727
|
|
@@ -595,13 +775,13 @@ module Swivel
|
|
595
775
|
case opts[:mode]
|
596
776
|
when 'append', 'replace'
|
597
777
|
opts[:auto_estimate] = true
|
598
|
-
["/
|
778
|
+
["/data_sets/#{opts[:id]}", :put]
|
599
779
|
else
|
600
780
|
force_auto_estimate =
|
601
|
-
!opts.has_key?(:auto_estimate) && !opts.has_key?(:
|
781
|
+
!opts.has_key?(:auto_estimate) && !opts.has_key?(:column_types)
|
602
782
|
opts[:auto_estimate] = true if force_auto_estimate
|
603
783
|
opts.reverse_merge! DEFAULT_UPLOAD_OPTIONS
|
604
|
-
['/
|
784
|
+
['/data_sets', :post]
|
605
785
|
end
|
606
786
|
call uri, opts, method
|
607
787
|
end
|
@@ -698,10 +878,30 @@ module Swivel
|
|
698
878
|
options.merge! :order_by_column => options[:columns].split(',').first,
|
699
879
|
:order_by_direction => 'DESC'
|
700
880
|
end
|
701
|
-
call '/
|
881
|
+
call '/graphs', options, :post
|
882
|
+
end
|
883
|
+
|
884
|
+
# copied verbatim from actioncontroller::integration::session
|
885
|
+
|
886
|
+
def requestify(parameters, prefix=nil)
|
887
|
+
if Hash === parameters
|
888
|
+
return nil if parameters.empty?
|
889
|
+
parameters.map { |k,v| requestify(v, name_with_prefix(prefix, k)) }.join("&")
|
890
|
+
elsif Array === parameters
|
891
|
+
parameters.map { |v| requestify(v, name_with_prefix(prefix, "")) }.join("&")
|
892
|
+
elsif prefix.nil?
|
893
|
+
parameters
|
894
|
+
else
|
895
|
+
"#{CGI.escape(prefix)}=#{CGI.escape(parameters.to_s)}"
|
896
|
+
end
|
897
|
+
end
|
898
|
+
|
899
|
+
def name_with_prefix(prefix, name)
|
900
|
+
prefix ? "#{prefix}[#{name}]" : name.to_s
|
702
901
|
end
|
703
902
|
|
704
903
|
protected
|
904
|
+
|
705
905
|
def headers
|
706
906
|
if @config.has_key?(:api_key) && !@config[:api_key].blank?
|
707
907
|
encoded = Base64.encode64(':' + @config[:api_key])
|
@@ -710,5 +910,6 @@ module Swivel
|
|
710
910
|
@headers
|
711
911
|
end
|
712
912
|
end
|
913
|
+
|
713
914
|
end
|
714
915
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: swivel
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0.
|
7
|
-
date: 2007-
|
6
|
+
version: 0.0.96
|
7
|
+
date: 2007-10-12 00:00:00 -07:00
|
8
8
|
summary: Ruby interface to the Swivel API.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -33,7 +33,6 @@ files:
|
|
33
33
|
- README
|
34
34
|
- Rakefile
|
35
35
|
- lib/swivel.rb
|
36
|
-
- lib/test
|
37
36
|
- bin/swivel
|
38
37
|
- CHANGELOG
|
39
38
|
test_files: []
|
@@ -74,12 +73,3 @@ dependencies:
|
|
74
73
|
- !ruby/object:Gem::Version
|
75
74
|
version: 0.0.0
|
76
75
|
version:
|
77
|
-
- !ruby/object:Gem::Dependency
|
78
|
-
name: json
|
79
|
-
version_requirement:
|
80
|
-
version_requirements: !ruby/object:Gem::Version::Requirement
|
81
|
-
requirements:
|
82
|
-
- - ">"
|
83
|
-
- !ruby/object:Gem::Version
|
84
|
-
version: 0.0.0
|
85
|
-
version:
|