swivel 0.0.21 → 0.0.96

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/Rakefile +0 -1
  2. data/bin/swivel +5 -16
  3. data/lib/swivel.rb +292 -91
  4. metadata +2 -12
data/Rakefile CHANGED
@@ -64,7 +64,6 @@ spec =
64
64
 
65
65
  s.add_dependency 'activesupport'
66
66
  s.add_dependency 'cobravsmongoose'
67
- s.add_dependency 'json'
68
67
  end
69
68
 
70
69
  Rake::GemPackageTask.new(spec) do |p|
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 "/rest/#{resource}/#{id}"
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 "/rest/#{resource}", options
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
- (self =~ /^-?\d+(\.\d+|\d*)$/) != nil
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.dashify
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
- @xml_tag = self.class.name.split('::').last.to_xml_tag
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 '/rest/data_sets/1005309'
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 '/rest/data_sets/1005309'
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 '/rest/data_sets/1005309'
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
- raise e
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 '/rest/users/1000010'
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 '/rest/data_sets/1005309'
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 "/rest/#{@xml_tag.undashify}s/#{id}"
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 '/rest/users/1000010'
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
- "Swivel::#{resource.undashify.classify}".constantize
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
- s.numeric? ? s.to_i : s
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
- %w/DataColumn User/.each do |class_name|
265
- class_eval <<-LAZY
266
- class #{class_name} < Response; end
267
- LAZY
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[:image_host]
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 /rest/graphs/0 denotes updating a transient graph. Swivel
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 '/rest/graphs/0', options, :put
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 '/rest/data_sets'
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 '/rest/users'
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 '/rest/data_sets'
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.keys.each do |key|
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 '/rest/users/1000010'
608
+ # user = swivel.call '/users/1000010'
470
609
  # user.class # => Swivel::User
471
610
  #
472
- # users = swivel.call '/rest/users'
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 '/rest/test/echo/howdy'
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
- # /rest/object_type or /rest/object_type/id
496
-
497
- def call path, params = Hash.new, method = :get
498
- response =
499
- Net::HTTP.start @config[:host], @config[:port] do |http|
500
- request_class = "Net::HTTP::#{method.to_s.camelize}".constantize
501
- request = request_class.new path, headers
502
- if [:delete, :post, :put].include? method
503
- http.read_timeout = @config[:timeout_up]
504
- http.request request, params.to_query_string
505
- else
506
- http.read_timeout = @config[:timeout_down]
507
- http.request request
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
- end
510
- response.error! if response.code.to_i >= 300
511
- xml = response.body
512
- doc = REXML::Document.new xml
513
- Response.class_for(doc.root.elements[1].name).new xml, self
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: #{xml}\n"
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
- %w/ data_columns data_sets graphs users /.each do |obj|
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 "/rest/#{obj}/#\{options[:id]\}", options
699
+ call "/#{obj}/#\{options[:id]\}", options
527
700
  else
528
- call "/rest/#{obj}", options, :post
701
+ call "/#{obj}", options, :post
529
702
  end
530
703
  when Numeric, String, Symbol
531
- call "/rest/#{obj}/#\{options\}"
704
+ call "/#{obj}/#\{options\}"
532
705
  else
533
- call '/rest/#{obj}'
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
- ["/rest/data_sets/#{opts[:id]}", :put]
778
+ ["/data_sets/#{opts[:id]}", :put]
599
779
  else
600
780
  force_auto_estimate =
601
- !opts.has_key?(:auto_estimate) && !opts.has_key?(:column_type)
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
- ['/rest/data_sets', :post]
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 '/rest/graphs', options, :post
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.21
7
- date: 2007-08-08 00:00:00 -07:00
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: