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