swivel 0.0.103 → 0.0.146
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/README +126 -256
- data/Rakefile +5 -5
- data/bin/swivel +108 -96
- data/lib/swivel.rb +204 -69
- data/lib/swivel2.rb +11 -0
- metadata +6 -4
data/README
CHANGED
@@ -1,336 +1,206 @@
|
|
1
|
-
=
|
1
|
+
= swivel2.rb
|
2
2
|
|
3
|
-
|
3
|
+
swivel2.rb is a small library that lets you interface with Swivel's REST API.
|
4
|
+
We've only run the Swivel gem on Linux and OSX platforms, though it may work on
|
5
|
+
others.
|
4
6
|
|
5
|
-
|
6
|
-
# # # # # # # # # # # # # #
|
7
|
-
# # # # # # # # # # # # #
|
8
|
-
##### # # # # # # ##### # ###### ######
|
9
|
-
# # # # # # # # # ### # # # #
|
10
|
-
# # # # # # # # # # ### # # # #
|
11
|
-
##### ## ## ### # ####### ####### ### # # ######
|
7
|
+
You can always find the latest documentation for the Swivel API online at https://inviteonly.swivel.com/developer
|
12
8
|
|
13
|
-
|
9
|
+
== The Swivel API
|
14
10
|
|
15
|
-
|
11
|
+
The Swivel API is RESTful. At this time, you can perform CRUD operations on
|
12
|
+
data_set resources, which allows you to get your data into Swivel and keep
|
13
|
+
it updated. You can also retrieve your data as CSV.
|
16
14
|
|
17
|
-
|
15
|
+
----------- ----------- ------------
|
16
|
+
HTTP Method URI Description
|
17
|
+
----------- ----------- ------------
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
POST /data_sets Create a new data set (upload data)
|
20
|
+
DELETE /data_sets/#{id} Delete data set #{id}
|
21
|
+
GET /data_sets List data sets
|
22
|
+
GET /users/huned/data_sets List huned's data sets
|
23
|
+
GET /data_sets/#{id} Show data_set #{id}
|
24
|
+
PUT /data_sets/#{id} Update data_set #{id}
|
21
25
|
|
22
|
-
|
23
|
-
|
24
|
-
------ ----------- ------------
|
25
|
-
|
26
|
-
data_columns
|
27
|
-
get /rest/data_columns list data_columns
|
28
|
-
get /rest/data_columns/#{id} show data_column #{id}
|
29
|
-
put /rest/data_columns/#{id} update data_column #{id}
|
26
|
+
Most method calls take parameters. Check out the
|
27
|
+
{Swivel API Documentation}[https://inviteonly.swivel.com/developer] for the most up to date documentation. More REST resources will become available as the Swivel API matures.
|
30
28
|
|
31
|
-
|
32
|
-
post /rest/data_sets create a new data_set
|
33
|
-
delete /rest/data_sets/#{id} delete data_set #{id}
|
34
|
-
get /rest/data_sets list data_sets
|
35
|
-
get /rest/data_sets/#{id} show data_set #{id}
|
36
|
-
put /rest/data_sets/#{id} update data_set #{id}
|
29
|
+
== Installing
|
37
30
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
get /rest/graphs/#{id} show graph #{id}
|
43
|
-
put /rest/graphs/#{id} update graph #{id}
|
31
|
+
$ sudo gem install -y swivel
|
32
|
+
Successfully installed swivel, version 0.0.103
|
33
|
+
Installing ri documentation for swivel-0.0.103...
|
34
|
+
Installing RDoc documentation for swivel-0.0.103...
|
44
35
|
|
45
|
-
|
46
|
-
get /rest/users list users
|
47
|
-
get /rest/users/#{id} show user #{id}
|
48
|
-
put /rest/users/#{id} update user #{id}
|
36
|
+
== You need an API key
|
49
37
|
|
50
|
-
|
51
|
-
get /rest/test/echo/#{text} echo some text
|
52
|
-
get /rest/search search for stuff
|
38
|
+
You need an API key to perform any POST, PUT, or DELETE requests. You must also use an API key to perform a GET request to access your private or shared data. Get your API key from https://inviteonly.swivel.com/developer. Please treat your API key as you would a password.
|
53
39
|
|
54
|
-
|
55
|
-
{Swivel API Explorer}[http://swivel.com/api/list] to try calls interactively.
|
56
|
-
Also, check out some {sample code}[http://swivel.com/api/code].
|
40
|
+
Your API key is stored in ~/.swivelrc along with some other information. Copy the swivelrc.default provided with this gem to ~/.swivelrc and modify site parameter to include your API key. Please make sure you set appropriate permissions on your ~/.swivelrc file to prevent others from seeing your API key.
|
57
41
|
|
58
|
-
|
42
|
+
An example ~/.swivelrc:
|
59
43
|
|
60
|
-
$
|
61
|
-
|
62
|
-
|
63
|
-
|
44
|
+
$ cat ~/.swivelrc
|
45
|
+
--- !ruby/struct:Swivel2::Config
|
46
|
+
site: http://x:<YOUR API KEY>@api.swivel.com
|
47
|
+
timeout_read: 500
|
48
|
+
timeout_write: 1_000
|
49
|
+
extra_params: { noviews: true }
|
64
50
|
|
65
|
-
|
66
|
-
>> require 'swivel'
|
67
|
-
=> true
|
68
|
-
>> swivel = Swivel::Connection.new
|
69
|
-
=> #<Swivel::Connection:0xb7a29fb0 ...
|
70
|
-
>> puts swivel.call('/rest/test/echo/howdy')
|
71
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
72
|
-
<response at="Sat, 09 Jun 2007 23:59:29 -0700" success="true">
|
73
|
-
<echo version="0" text="howdy"/>
|
74
|
-
</response>
|
75
|
-
=> nil
|
51
|
+
TODO: describe what each parameter means.
|
76
52
|
|
77
|
-
==
|
53
|
+
== Examples
|
78
54
|
|
79
|
-
|
80
|
-
* Swivel::Connection::Config
|
81
|
-
* Swivel::Response
|
82
|
-
* Swivel::DataColumn
|
83
|
-
* Swivel::DataSet
|
84
|
-
* Swivel::Graph
|
85
|
-
* Swivel::User
|
86
|
-
* Swivel::List
|
87
|
-
* Swivel::ApiError
|
55
|
+
=== Basic set up
|
88
56
|
|
89
|
-
|
57
|
+
Before you can use this library, you need to know a bit about the Config and
|
58
|
+
Connection classes. After you set up your ~/.swivelrc, you can run these
|
59
|
+
examples through irb.
|
90
60
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
Other nice ones:
|
61
|
+
$ irb
|
62
|
+
>> require 'swivel2'
|
63
|
+
=> true
|
96
64
|
|
97
|
-
|
98
|
-
|
65
|
+
A Config instance encapsulates important settings stored in your ~/.swivelrc
|
66
|
+
file.
|
99
67
|
|
100
|
-
|
68
|
+
>> config = Swivel2::Config.load '/home/huned/.swivelrc'
|
69
|
+
=> #<struct Swivel2::Config ...>
|
101
70
|
|
102
|
-
|
103
|
-
Try to keep your API key close to your chest, safely tucked away from prying eyes
|
104
|
-
and dangers of the "real world"... dangers like parking tickets and untied shoelaces.
|
105
|
-
It's like a password, so don't share it.
|
71
|
+
A Connection instance is your main interface to Swivel's API. Construct a Connection instance by passing a Config instance into the constructor.
|
106
72
|
|
107
|
-
|
108
|
-
|
73
|
+
>> connection = Swivel2::Connection.new config
|
74
|
+
=> #<Swivel2::Connection:0xb7b34a54 ...>
|
109
75
|
|
110
|
-
|
111
|
-
|
76
|
+
The Connection class defines post, get, put, and delete methods that you can
|
77
|
+
call with paths to REST resources. These methods automatically send your API
|
78
|
+
key with each request. For example:
|
112
79
|
|
113
|
-
|
80
|
+
>> data_sets = connection.get '/data_sets'
|
114
81
|
|
115
|
-
|
116
|
-
>> require 'swivel'
|
117
|
-
=> true
|
82
|
+
=== Data sets
|
118
83
|
|
119
|
-
|
120
|
-
connection loads up (and possibly creates) your ~/.swivelrc and sets up
|
121
|
-
several parameters, such as your API key, that are used throughout calls to
|
122
|
-
Swivel.
|
84
|
+
==== Show a data set
|
123
85
|
|
124
|
-
|
125
|
-
=> #<Swivel::Connection:0xb7a2bb58 @config={:timeout_down=>10, :api_key=>"xxx", :host=>"api.swivel.com", :port=>80, :timeout_up=>200}, headers{"Accept"=>"application/xml"}
|
86
|
+
You can show a data set's XML by making a get call to its resource path:
|
126
87
|
|
127
|
-
|
128
|
-
|
129
|
-
it will try faithfully to get back something useful for you.
|
130
|
-
|
131
|
-
In many cases, Swivel::Connection#call will return an object whose class is
|
132
|
-
inherited from Swivel::Response.
|
133
|
-
|
134
|
-
# look! we got a Swivel::DataSet object! frabjous day!
|
135
|
-
>> data_set = swivel.call '/rest/data_sets/1000000'
|
136
|
-
=> #<Swivel::DataSet:0xb79dd688 @response=<response success='true' at='Mon, 04 Jun 2007 04:41:04 -0700'> .... , xml_tag"data-set", docUNDEFINED ....
|
137
|
-
|
138
|
-
Question: what if it can't find an appropriate class to instantiate? Then it
|
139
|
-
just gives you back the XML as a String, trusting that you'll love and care for
|
140
|
-
it.
|
88
|
+
>> data_set = connection.get '/data_sets/1234567'
|
89
|
+
=> #<Swivel2::Response::DataSet:0xb79fd244 ...>
|
141
90
|
|
142
|
-
|
143
|
-
|
144
|
-
>> puts swivel.call('/rest/test/echo/howdy')
|
145
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
146
|
-
<response success="true" at="Mon, 04 Jun 2007 03:34:49 -0700">
|
147
|
-
<echo text="howdy" version="0"/>
|
148
|
-
</response>
|
91
|
+
You don't need to go through the Swivel gem to get a data set's csv. It's
|
92
|
+
simply a GET:
|
149
93
|
|
150
|
-
|
151
|
-
encapsulate the XML response that the Swivel API sent.
|
94
|
+
GET http://api.swivel.com/data_sets/1234567.csv?api_key=<YOUR API KEY>
|
152
95
|
|
153
|
-
|
154
|
-
|
96
|
+
When getting the CSV, you can optionally specify +limit+ and +offset+
|
97
|
+
parameters.
|
155
98
|
|
156
|
-
|
157
|
-
surlily.
|
99
|
+
==== List data sets
|
158
100
|
|
159
|
-
>>
|
160
|
-
=> 1000000
|
161
|
-
>> data_set.user.name
|
162
|
-
=> "huned"
|
163
|
-
>> data_set.data_columns[3].name
|
164
|
-
=> "by-nc-nd-2.0"
|
101
|
+
>> data_sets = connection.get '/data_sets'
|
165
102
|
|
166
|
-
|
167
|
-
|
103
|
+
You can also list data sets for a specific user. For example, to list huned's
|
104
|
+
data sets:
|
168
105
|
|
169
|
-
|
170
|
-
>> data_set.swivel_id
|
171
|
-
=> 1000000 # huzzah!
|
172
|
-
# yet... it's not there in the list of methods
|
173
|
-
>> data_set.methods.grep /swivel_id/
|
174
|
-
=> [] # what the..?
|
106
|
+
>> data_sets = connection.get '/users/huned/data_sets'
|
175
107
|
|
176
|
-
|
177
|
-
the
|
108
|
+
When you list data sets, you can optionally provide +limit+ and +offset+
|
109
|
+
parameters to page through the results. For example, to list data sets 11
|
110
|
+
through 20:
|
178
111
|
|
179
|
-
>>
|
180
|
-
<data-set swivel-id='1000000' version='0'>
|
181
|
-
<name>name?</name>
|
182
|
-
<user swivel-id='1000010'>
|
183
|
-
<name>huned</name>
|
184
|
-
</user>
|
185
|
-
<created-at>Sat, 02 Jun 2007 20:35:45 -0700</created-at>
|
186
|
-
<updated-at>Sat, 02 Jun 2007 20:35:49 -0700</updated-at>
|
187
|
-
<source>
|
188
|
-
<citation>name?</citation>
|
189
|
-
<citation-url/>
|
190
|
-
</source>
|
191
|
-
<rows>367</rows>
|
192
|
-
<columns>7</columns>
|
193
|
-
...
|
194
|
-
</data-set>
|
112
|
+
>> data_sets = connection.get '/data_sets', :limit => 10, :offset => 10
|
195
113
|
|
196
|
-
|
114
|
+
==== Uploading new data
|
197
115
|
|
198
|
-
|
116
|
+
You just need a CSV and a name for your data set before you can upload it.
|
117
|
+
Without specifying any other options, Swivel will try to figure out the column
|
118
|
+
structure and types automatically.
|
199
119
|
|
200
|
-
|
201
|
-
|
120
|
+
>> params = {'file[data]' => `cat ~/data.csv`, 'data_set[name]' => 'My Data'}
|
121
|
+
>> data_set = connection.post '/data_sets', params
|
122
|
+
=> #<Swivel2::Response::DataSet:0xb7e0051c ...>
|
202
123
|
|
203
|
-
|
204
|
-
id = data_set.id
|
205
|
-
# get the data_set's resource... turns Swivel::DataSet into 'data_set'
|
206
|
-
resource = data_set.class.name.split('::').last.underscore # => "data_set"
|
124
|
+
See the {full list of options}[https://inviteonly.swivel.com/api/data_sets] that you can use for POST and PUT to /data_sets.
|
207
125
|
|
208
|
-
|
209
|
-
url = "http://swivel.com/#{resource}s/show/#{id}"
|
126
|
+
==== Updating an existing data set's attributes
|
210
127
|
|
211
|
-
|
128
|
+
>> params = {'data_set[name]' => 'new name'}
|
129
|
+
>> data_set = connection.put '/data_sets/1234567', params
|
130
|
+
=> #<Swivel2::Response::DataSet:0xb7e0051c ...>
|
212
131
|
|
213
|
-
|
214
|
-
id = data_set.id
|
215
|
-
resource = data_set.class.name.split('::').last.underscore # => "data_set"
|
132
|
+
See the {full list of options}[https://inviteonly.swivel.com/api/data_sets] that you can use for POST and PUT to /data_sets.
|
216
133
|
|
217
|
-
|
134
|
+
==== Appending more data to an existing data set
|
218
135
|
|
219
|
-
|
136
|
+
>> params = {'file[mode]' => 'append', 'file[data]' => `cat ~/more_data.csv`}
|
137
|
+
>> data_set = connection.put '/data_sets/1234567', params
|
138
|
+
=> #<Swivel2::Response::DataSet:0xb7e0051c ...>
|
220
139
|
|
221
|
-
|
140
|
+
See the {full list of options}[https://inviteonly.swivel.com/api/data_sets] that you can use for POST and PUT to /data_sets. Caveat: Your new data must have the same column structure as the original data.
|
222
141
|
|
223
|
-
|
142
|
+
==== Replacing data for an existing data set
|
224
143
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
* scale
|
229
|
-
* graph_type
|
230
|
-
* order_by_direction
|
231
|
-
* time_range
|
232
|
-
* time_scale
|
233
|
-
* aggregation_function
|
144
|
+
>> params = {'file[mode]' => 'replace', 'file[data]' => `cat ~/new_data.csv`}
|
145
|
+
>> data_set = connection.put '/data_sets/1234567', params
|
146
|
+
=> #<Swivel2::Response::DataSet:0xb7e0051c ...>
|
234
147
|
|
235
|
-
|
148
|
+
See the {full list of options}[https://inviteonly.swivel.com/api/data_sets] that you can use for POST and PUT to /data_sets. Caveat: Your new data must have the same column structure as the original data.
|
236
149
|
|
237
|
-
|
150
|
+
==== Deleting a data set
|
238
151
|
|
239
|
-
|
152
|
+
>> data_set = connection.delete '/data_sets/1234567'
|
153
|
+
=> #<Swivel2::Response::DataSet:0xb7e0051c ...>
|
240
154
|
|
241
|
-
|
155
|
+
== Quickest way to upload? The Swivel command line tool
|
242
156
|
|
243
|
-
|
157
|
+
The Swivel command line tool the quickest way to get your data into Swivel and
|
158
|
+
keep it updated if you have basic Unix and shell scripting knowledge.
|
159
|
+
Installing the Swivel gem also installs a command line tool, /usr/bin/swivel,
|
160
|
+
that you can use to upload and update data sets without writing a single line
|
161
|
+
of ruby code.
|
244
162
|
|
245
|
-
|
246
|
-
|
163
|
+
Before using this tool, please set up your ~/.swivelrc as described at the
|
164
|
+
beginning of this documentation.
|
247
165
|
|
248
166
|
$ which swivel
|
249
167
|
/usr/bin/swivel
|
250
168
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
$ ls -lh ~/.swivelrc
|
255
|
-
ls: /home/huned/.swivelrc: No such file or directory
|
256
|
-
$ swivel
|
257
|
-
Usage: swivel [options]
|
258
|
-
-h, --host=name Swivel hostname or IP address.
|
259
|
-
Default:
|
260
|
-
-p, --port=number Swivel host's port number.
|
261
|
-
Default:
|
262
|
-
-f, --file=file File to upload, append, or replace.
|
263
|
-
-r, --raw=path Perform a raw call and print the XML response.
|
264
|
-
-?, --help Show this help message.
|
265
|
-
-k, --key=api-key Set your API key.
|
266
|
-
$ ls -lh ~/.swivelrc
|
267
|
-
-rw-rw-r-- 1 huned huned 105 Jun 4 05:04 /home/huned/.swivelrc
|
268
|
-
|
269
|
-
Note, however, that the api_key setting is blank. Once you finagle an api key,
|
270
|
-
run `swivel -k <your api key>` to update your ~/.swivelrc. (Remember: {finagle an api
|
271
|
-
key}[http://swivel.com/api/key].)
|
272
|
-
|
273
|
-
$ cat ~/.swivelrc
|
274
|
-
---
|
275
|
-
protocol: http://
|
276
|
-
timeout_up: 200
|
277
|
-
api_key: ""
|
278
|
-
timeout_down: 100
|
279
|
-
host: api.swivel.com
|
280
|
-
port: 80
|
169
|
+
Aside from being useful on its own, the command line tool is also a nice
|
170
|
+
example of how to write ruby code that uses this gem.
|
281
171
|
|
282
|
-
|
172
|
+
=== Uploading new data
|
283
173
|
|
284
|
-
$ swivel upload "my
|
285
|
-
uploaded
|
174
|
+
$ cat data.csv | /usr/bin/swivel upload "my data"
|
175
|
+
uploaded 1234567
|
286
176
|
|
287
|
-
|
177
|
+
=== Appending more data to an existing data set
|
288
178
|
|
289
|
-
|
179
|
+
$ cat more_data.csv | /usr/bin/swivel append 1234567
|
180
|
+
appended 1234567
|
290
181
|
|
291
|
-
|
292
|
-
little mechanism that lets you rig arbitrary swivel uploads through unix
|
293
|
-
process piping (|).
|
182
|
+
Caveat: Your new data must have the same column structure as the original data.
|
294
183
|
|
295
|
-
|
296
|
-
uploaded data_set 1000234
|
184
|
+
=== Replacing data for an existing data set
|
297
185
|
|
298
|
-
|
186
|
+
$ cat more_data.csv | /usr/bin/swivel replace 1234567
|
187
|
+
replaced 1234567
|
299
188
|
|
300
|
-
|
301
|
-
appended data_set 1000234
|
189
|
+
Caveat: Your new data must have the same column structure as the original data.
|
302
190
|
|
303
|
-
|
304
|
-
fancier data:
|
191
|
+
== More options
|
305
192
|
|
306
|
-
$
|
307
|
-
replaced data_set 1000234
|
193
|
+
$ /usr/bin/swivel --help
|
308
194
|
|
309
|
-
One caveat when appending or replacing: Your new data must have the same
|
310
|
-
column structure as the original data. Or in other words, your new data must
|
311
|
-
have the same column structure as the original data.
|
312
|
-
|
313
|
-
In addition to being a fine way to use swivel, the command line program
|
314
|
-
serves as a nice example of how you might use swivel.rb. swivel.rb is the
|
315
|
-
piece of code that allows ruby and the swivel api to be superhero and sidekick.
|
316
|
-
(Stop here for a moment and visualize that.)
|
317
|
-
|
318
|
-
Edit by Visnu: Huned wrote this at 4am. He tired out right here.
|
319
195
|
|
320
196
|
== Feedback
|
321
197
|
|
322
198
|
Feedback, comments, and (especially) patches welcome at mailto:developer@swivel.com.
|
323
199
|
|
324
|
-
You can rcov
|
325
|
-
|
326
|
-
$ rcov -Ilib -t test/test_swivel.rb
|
327
|
-
|
328
|
-
== Respek
|
200
|
+
You can rcov this code by running rcov from the top level directory:
|
329
201
|
|
330
|
-
|
331
|
-
Wanna write code with us? {We're hiring!}[http://swivel.com/about/jobs]
|
202
|
+
$ rcov -Ilib -t test/*_test.rb
|
332
203
|
|
333
204
|
== License
|
334
205
|
|
335
|
-
This software is licensed under the exact same license as Ruby itself.
|
336
|
-
out.
|
206
|
+
This software is licensed under the exact same license as Ruby itself.
|
data/Rakefile
CHANGED
@@ -14,7 +14,7 @@ NAME = "swivel"
|
|
14
14
|
REV = `svn info`[/Revision: (\d+)/, 1] rescue nil
|
15
15
|
VERS = ENV['VERSION'] || "0.0" + (REV ? ".#{REV}" : "")
|
16
16
|
CLEAN.include ['doc', 'pkg']
|
17
|
-
RDOC_OPTS = ['--line-numbers', '--title', '
|
17
|
+
RDOC_OPTS = ['--line-numbers', '--title', 'swivel2.rb', '--main', 'README', '--inline-source']
|
18
18
|
|
19
19
|
desc "Does a full compile, test run"
|
20
20
|
task :default => [:package, :test, :rdoc]
|
@@ -28,7 +28,7 @@ task :release => [:package]
|
|
28
28
|
desc "Run all the tests"
|
29
29
|
Rake::TestTask.new do |t|
|
30
30
|
t.libs << "test"
|
31
|
-
t.test_files = FileList['test
|
31
|
+
t.test_files = FileList['test/*_test.rb']
|
32
32
|
t.verbose = true
|
33
33
|
end
|
34
34
|
|
@@ -36,7 +36,7 @@ Rake::RDocTask.new do |rdoc|
|
|
36
36
|
rdoc.rdoc_dir = 'doc/rdoc'
|
37
37
|
rdoc.options += RDOC_OPTS
|
38
38
|
rdoc.main = "README"
|
39
|
-
rdoc.rdoc_files.add ['README', 'CHANGELOG', 'COPYING', 'lib/*.rb']
|
39
|
+
rdoc.rdoc_files.add ['README', 'CHANGELOG', 'COPYING', 'lib/swivel2.rb', 'lib/swivel2/*.rb']
|
40
40
|
end
|
41
41
|
|
42
42
|
spec =
|
@@ -54,13 +54,13 @@ spec =
|
|
54
54
|
|
55
55
|
s.author = 'huned'
|
56
56
|
s.email = 'huned@swivel.com'
|
57
|
-
s.homepage = 'http://swivel.com/developer'
|
57
|
+
s.homepage = 'http://www.swivel.com/developer'
|
58
58
|
|
59
59
|
s.files = %w/COPYING README Rakefile/ + Dir['{lib,bin}/*']
|
60
60
|
s.require_path = "lib"
|
61
61
|
|
62
62
|
s.bindir = "bin"
|
63
|
-
s.executables =
|
63
|
+
s.executables = %w/swivel/
|
64
64
|
|
65
65
|
s.add_dependency 'activesupport'
|
66
66
|
s.add_dependency 'cobravsmongoose'
|
data/bin/swivel
CHANGED
@@ -1,81 +1,110 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
|
-
require '
|
4
|
+
require 'open-uri'
|
5
|
+
require 'active_support'
|
5
6
|
require 'optparse'
|
6
|
-
require '
|
7
|
-
|
7
|
+
require File.dirname(__FILE__) + '/../lib/swivel2'
|
8
|
+
|
9
|
+
COLUMN_TYPE_HELP = <<-EOS
|
10
|
+
Valid types:
|
11
|
+
currency: Currency values, including the
|
12
|
+
currency symbol.
|
13
|
+
date: Date values that don't include
|
14
|
+
timestamps.
|
15
|
+
datetime: Date and time values.
|
16
|
+
float: Floating point values. Swivel
|
17
|
+
groks scientific notation!
|
18
|
+
integer: Integer values.
|
19
|
+
ignore: Columns of this type will not
|
20
|
+
be uploaded.
|
21
|
+
percent: Percent values without the "%"
|
22
|
+
symbol. A value of 0.03 will
|
23
|
+
appear as 3% in Swivel.
|
24
|
+
string: String values.
|
25
|
+
EOS
|
26
|
+
|
27
|
+
SWIVELRC = <<-EOS
|
28
|
+
|
29
|
+
Your API key is read from ~/.swivelrc:
|
30
|
+
|
31
|
+
You need a ~/.swivelrc file before you can use this program! The contents
|
32
|
+
of your swivelrc file should look like this:
|
33
|
+
|
34
|
+
--- !ruby/struct:Swivel2::Config
|
35
|
+
site: https://x:<YOUR_API_KEY>@api.swivel.com
|
36
|
+
timeout_read: 500
|
37
|
+
timeout_write: 1_000
|
38
|
+
extra_params: { noviews: true }
|
39
|
+
EOS
|
40
|
+
|
41
|
+
EXAMPLES = <<-EOS
|
42
|
+
|
43
|
+
Examples:
|
44
|
+
|
45
|
+
Upload a data set
|
46
|
+
$ cat data.csv | #{$0} upload 1234567
|
47
|
+
|
48
|
+
Append data to data set with id 1234567
|
49
|
+
$ cat more_data.csv | #{$0} append 1234567
|
50
|
+
|
51
|
+
Replace underlying data for data set with id 1234567
|
52
|
+
$ cat new_data.csv | #{$0} replace 1234567
|
53
|
+
EOS
|
8
54
|
|
9
55
|
options = Hash.new
|
10
|
-
config =
|
11
|
-
config.load
|
12
|
-
config.save
|
56
|
+
config = Swivel2::Config.load
|
13
57
|
|
14
58
|
ARGV.options do |opts|
|
15
|
-
opts.banner = 'Usage: swivel <
|
59
|
+
opts.banner = 'Usage: swivel <upload, append, replace> [options]'
|
16
60
|
|
17
61
|
opts.separator ''
|
18
62
|
|
19
63
|
opts.separator 'Options:'
|
20
64
|
|
21
|
-
opts.on '-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
"Swivel host's port number.",
|
27
|
-
"Default: #{options[:port]}" do |v| options[:port] = v end
|
28
|
-
|
29
|
-
opts.on '-f', '--file=file', String,
|
30
|
-
"File to upload, append, or replace." do |v|
|
65
|
+
opts.on '-f', '--file=<file>', String,
|
66
|
+
'File to upload, append, or replace.',
|
67
|
+
'Your actual data is read from this file.',
|
68
|
+
'If omitted, data is read from STDIN.',
|
69
|
+
'OPTIONAL' do |v|
|
31
70
|
options[:filename] = v
|
32
71
|
end
|
33
72
|
|
34
|
-
opts.
|
35
|
-
"Column names." do |v|
|
36
|
-
options[:column_names] = v
|
37
|
-
end
|
73
|
+
opts.separator ''
|
38
74
|
|
39
|
-
opts.on '-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
'NumberDataColumnType'
|
46
|
-
when /^w/i
|
47
|
-
'WholeNumberDataColumnType'
|
48
|
-
when /^c/i
|
49
|
-
'CurrencyDataColumnType'
|
50
|
-
when /^t/i
|
51
|
-
'DateTimeDataColumnType'
|
52
|
-
when /^d/i
|
53
|
-
'DateDataColumnType'
|
54
|
-
when /^p/i
|
55
|
-
'PercentageDataColumnType'
|
56
|
-
else
|
57
|
-
'TextDataColumnType'
|
58
|
-
end
|
59
|
-
end.join ','
|
60
|
-
end
|
75
|
+
opts.on '-c', '--headings=<headings>', String,
|
76
|
+
'Column headings as a comma separated list.',
|
77
|
+
'Example: "Year,Revenue"',
|
78
|
+
'OPTIONAL' do |v|
|
79
|
+
options[:headings] = v
|
80
|
+
end
|
61
81
|
|
62
|
-
opts.
|
63
|
-
|
64
|
-
|
65
|
-
|
82
|
+
opts.separator ''
|
83
|
+
|
84
|
+
opts.on *['-t', '--types=<types>', String,
|
85
|
+
'Column types as a comma separated list.',
|
86
|
+
'Example: "date,currency"',
|
87
|
+
COLUMN_TYPE_HELP.split("\n"),
|
88
|
+
'OPTIONAL'].flatten do |v|
|
89
|
+
options[:types] = v
|
66
90
|
end
|
67
91
|
|
68
|
-
opts.
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
92
|
+
opts.separator ''
|
93
|
+
|
94
|
+
opts.on '-s', '--sep=<separator>', String,
|
95
|
+
'Column separator.',
|
96
|
+
'Example: "," or "\t"',
|
97
|
+
'OPTIONAL' do |v|
|
98
|
+
options[:sep] = v
|
75
99
|
end
|
76
100
|
|
101
|
+
opts.separator ''
|
102
|
+
|
77
103
|
opts.on_tail '-?', '--help',
|
78
|
-
"Show this help message." do
|
104
|
+
"Show this help message." do
|
105
|
+
puts opts
|
106
|
+
exit
|
107
|
+
end
|
79
108
|
|
80
109
|
opts.parse!
|
81
110
|
|
@@ -86,77 +115,60 @@ ARGV.options do |opts|
|
|
86
115
|
end
|
87
116
|
|
88
117
|
class SwivelHelper
|
89
|
-
def initialize
|
90
|
-
@swivel =
|
91
|
-
end
|
92
|
-
|
93
|
-
def show resource, id
|
94
|
-
resource = resource.pluralize
|
95
|
-
response = @swivel.call "/#{resource}/#{id}"
|
96
|
-
end
|
97
|
-
|
98
|
-
def list resource, options = Hash.new
|
99
|
-
resource = resource.pluralize
|
100
|
-
response = @swivel.call "/#{resource}", options
|
118
|
+
def initialize config
|
119
|
+
@swivel = Swivel2::Connection.new config
|
101
120
|
end
|
102
121
|
|
103
122
|
def upload name, options = Hash.new
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
123
|
+
opts = { 'data_set[name]' => name,
|
124
|
+
'data_set[citation]' => $0,
|
125
|
+
'data_set[public]' => 0,
|
126
|
+
'file[data]' => read(options[:filename]) }
|
127
|
+
opts.merge! 'file[types]' => options[:types] if options[:types]
|
128
|
+
opts.merge! 'file[headings]' => options[:headings] if options[:headings]
|
129
|
+
opts.merge! 'file[sep]' => options[:sep] if options[:sep]
|
130
|
+
data_set = @swivel.post '/data_sets', opts
|
109
131
|
puts "uploaded #{data_set.id}"
|
110
132
|
end
|
111
133
|
|
112
134
|
def append id, options = Hash.new
|
113
|
-
opts = {
|
114
|
-
|
135
|
+
opts = { 'file[mode]' => 'append',
|
136
|
+
'file[data]' => read(options[:filename]) }
|
137
|
+
data_set = update id, opts
|
115
138
|
puts "appended #{data_set.id}"
|
116
139
|
end
|
117
140
|
|
118
141
|
def replace id, options = Hash.new
|
119
|
-
opts = {
|
120
|
-
|
142
|
+
opts = { 'file[mode]' => 'replace',
|
143
|
+
'file[data]' => read(options[:filename]) }
|
144
|
+
data_set = update id, opts
|
121
145
|
puts "replaced #{data_set.id}"
|
122
146
|
end
|
123
147
|
|
124
148
|
private
|
149
|
+
def update id, options
|
150
|
+
@swivel.put "/data_sets/#{id}", options
|
151
|
+
end
|
152
|
+
|
125
153
|
def read filename = nil
|
126
154
|
if filename
|
127
|
-
open filename
|
128
|
-
f.readlines.join
|
129
|
-
end
|
155
|
+
open filename do |f| f.readlines.join end
|
130
156
|
else
|
131
157
|
readlines.join
|
132
158
|
end
|
133
159
|
end
|
134
|
-
|
135
|
-
def column_types options
|
136
|
-
if options[:column_types].blank?
|
137
|
-
{:auto_estimate => true}
|
138
|
-
else
|
139
|
-
{:column_types => options[:column_types],
|
140
|
-
:column_names => options[:column_names],
|
141
|
-
:column_separator => options[:column_separator] || ','}
|
142
|
-
end
|
143
|
-
end
|
144
160
|
end
|
145
161
|
|
146
|
-
helper = SwivelHelper.new
|
162
|
+
helper = SwivelHelper.new config
|
147
163
|
action = ARGV.shift
|
148
164
|
resource = ARGV.shift
|
149
165
|
id = ARGV.shift
|
150
166
|
|
151
167
|
case action.downcase
|
152
|
-
when
|
153
|
-
helper.list resource, options
|
154
|
-
when 'show'
|
155
|
-
helper.show resource, id
|
156
|
-
when 'upload'
|
168
|
+
when /upload/i
|
157
169
|
name = resource
|
158
170
|
helper.upload name, options
|
159
|
-
when
|
171
|
+
when /append/i, /replace/i
|
160
172
|
id = resource
|
161
173
|
helper.send action.to_sym, id, options
|
162
174
|
else
|
data/lib/swivel.rb
CHANGED
@@ -11,7 +11,8 @@ require 'yaml'
|
|
11
11
|
|
12
12
|
class Time
|
13
13
|
silence_warnings do
|
14
|
-
RFC822_DATETIME_REGEX = /\w{3}, \d{2} \w{3} \d{4} \d{2}:\d{2}:\d{2} [+-]\d{4}/
|
14
|
+
RFC822_DATETIME_REGEX = /\A\w{3}, \d{2} \w{3} \d{4} \d{2}:\d{2}:\d{2} [+-]\d{4}\Z/
|
15
|
+
XML_DATETIME_REGEX = /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+\-]\d{2}:\d{2}\Z/
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
@@ -134,22 +135,22 @@ module Swivel
|
|
134
135
|
|
135
136
|
class Response
|
136
137
|
|
137
|
-
attr_accessor :refreshed_at, :disable_auto_refresh
|
138
|
+
attr_accessor :refreshed_at, :disable_auto_refresh, :hash_doc
|
138
139
|
|
139
140
|
def self.resource
|
140
141
|
nil
|
141
142
|
end
|
142
143
|
|
143
144
|
# Instantiate from XML returned from Swivel.
|
144
|
-
def initialize xml
|
145
|
+
def initialize xml, connection, hashdoc
|
146
|
+
raise 'invalid arguments, either xml or hashdoc needs to be passed in' if hashdoc.nil? && xml.nil?
|
145
147
|
@connection = connection
|
146
148
|
@disable_auto_refresh = @connection && @connection.disable_auto_refresh
|
147
149
|
@xml_tag = self.class.name.demodulize.to_xml_tag
|
148
|
-
@
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
end
|
150
|
+
@hash_doc = hashdoc
|
151
|
+
@hash_doc ||= CobraVsMongoose.xml_to_hash xml
|
152
|
+
@hash_doc = @hash_doc['response'] if @hash_doc.is_a?(Hash) && @hash_doc['response']
|
153
|
+
@hash_doc = @hash_doc[self.class.resource] if @hash_doc.is_a?(Hash) && @hash_doc[self.class.resource]
|
153
154
|
end
|
154
155
|
|
155
156
|
# Most of the work in processing responses from Swivel happens here. It's
|
@@ -175,24 +176,55 @@ module Swivel
|
|
175
176
|
# data_set.user.class # => Swivel::User
|
176
177
|
|
177
178
|
def method_missing method_id
|
178
|
-
|
179
|
-
|
180
|
-
select_list = "/#{self.class.resource ? self.class.resource : @xml_tag}/list[@resource=\"#{method_id.to_s.singularize.to_xml_tag}\"]"
|
181
|
-
|
179
|
+
method_name = method_id.to_s.singularize.to_xml_tag
|
180
|
+
list_name = method_id.to_s.to_xml_tag
|
182
181
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
182
|
+
# 1)look in attributes
|
183
|
+
# 2)look in elements
|
184
|
+
# 2a) if value return it
|
185
|
+
# 2b) if hash return an obj
|
186
|
+
# 2c) if method_name.pluralize, return a list
|
187
|
+
# 3)look in lists
|
188
|
+
|
189
|
+
if @hash_doc["@#{method_name}"]
|
190
|
+
value_for @hash_doc["@#{method_name}"]
|
191
|
+
elsif @hash_doc[method_name]
|
192
|
+
if @hash_doc[method_name].is_a? Hash
|
193
|
+
if @hash_doc[method_name].has_key?('$')
|
194
|
+
value_for @hash_doc[method_name]['$']
|
195
|
+
elsif @hash_doc[method_name].size == 1 and @hash_doc[method_name].has_key? '@type'
|
196
|
+
nil
|
197
|
+
elsif @hash_doc[method_name].blank?
|
198
|
+
nil
|
199
|
+
else
|
200
|
+
Response.class_for(method_name).new(nil, @connection, @hash_doc[method_name])
|
201
|
+
end
|
188
202
|
else
|
189
|
-
value_for
|
203
|
+
value_for @hash_doc[method_name]
|
190
204
|
end
|
191
|
-
|
192
|
-
|
193
|
-
elsif
|
194
|
-
|
195
|
-
|
205
|
+
|
206
|
+
# 2c) from above - enables rails ActiveRecord::Base#to_xml style lists
|
207
|
+
elsif @hash_doc[list_name].is_a? Hash and
|
208
|
+
@hash_doc[list_name]['@type'] == 'array'
|
209
|
+
member_name = (@hash_doc[list_name].keys.find{|k| !/^@/.match(k) } || list_name.singularize)
|
210
|
+
list = (@hash_doc[list_name][member_name] rescue [])
|
211
|
+
list = [list] unless list.is_a?(Array)
|
212
|
+
# hack to get the gem to parse all array types correctly
|
213
|
+
hack_list = list.map{|i| {member_name => i}}
|
214
|
+
Swivel::List.new nil, @connection, hack_list, member_name, list.size, list.size
|
215
|
+
|
216
|
+
elsif @hash_doc['list'] && @hash_doc['list'].is_a?(Hash) && @hash_doc['list']['@resource'] == method_name
|
217
|
+
Swivel::List.new nil, @connection, (@hash_doc['list'][method_name].nil? ? [] : @hash_doc['list'][method_name]), method_name, @hash_doc['list']['@count'], @hash_doc['list']['@total']
|
218
|
+
elsif @hash_doc['list'] && @hash_doc['list'].is_a?(Array) && @hash_doc['list'].any?{|h| h['@resource'] == method_name}
|
219
|
+
list_el = @hash_doc['list'].select{|h| h['@resource'] == method_name}.first
|
220
|
+
Swivel::List.new nil, @connection,(list_el[method_name].nil? ? [] : list_el[method_name]), method_name, list_el['@count'], list_el['@total']
|
221
|
+
elsif @hash_doc[method_name.pluralize]
|
222
|
+
if @hash_doc[method_name.pluralize].has_key?('$')
|
223
|
+
value_for @hash_doc[method_name.pluralize]['$']
|
224
|
+
else
|
225
|
+
@hash_doc[method_name.pluralize]
|
226
|
+
end
|
227
|
+
else
|
196
228
|
raise NoMethodError, "#{method_id} isn't a method of #{self.class.name}"
|
197
229
|
end
|
198
230
|
rescue Exception => e
|
@@ -200,7 +232,7 @@ module Swivel
|
|
200
232
|
nil
|
201
233
|
else
|
202
234
|
@retried = true
|
203
|
-
refresh! true
|
235
|
+
# refresh! true
|
204
236
|
retry
|
205
237
|
end
|
206
238
|
end
|
@@ -223,7 +255,7 @@ module Swivel
|
|
223
255
|
# user.id == user.swivel_id # => true
|
224
256
|
|
225
257
|
def id
|
226
|
-
swivel_id
|
258
|
+
method_missing(:id) or swivel_id
|
227
259
|
end
|
228
260
|
|
229
261
|
# Same as id method, added to make Swivel objects play well with url_for
|
@@ -239,16 +271,16 @@ module Swivel
|
|
239
271
|
# user = data_set.user
|
240
272
|
# user.refresh! # populate the object fully from Swivel
|
241
273
|
|
242
|
-
def refresh! force = false
|
243
|
-
if @connection && (force || @refreshed_at.blank?)
|
244
|
-
refreshed = @connection.call "/#{@xml_tag.undashify}s/#{id}"
|
245
|
-
if refreshed.is_a? self.class
|
246
|
-
@doc = REXML::Document.new refreshed.to_xml
|
247
|
-
@refreshed_at = Time.now
|
248
|
-
end
|
249
|
-
end
|
250
|
-
self
|
251
|
-
end
|
274
|
+
# def refresh! force = false
|
275
|
+
# if @connection && (force || @refreshed_at.blank?)
|
276
|
+
# refreshed = @connection.call "/#{@xml_tag.undashify}s/#{id}"
|
277
|
+
# if refreshed.is_a? self.class
|
278
|
+
# @doc = REXML::Document.new refreshed.to_xml
|
279
|
+
# @refreshed_at = Time.now
|
280
|
+
# end
|
281
|
+
# end
|
282
|
+
# self
|
283
|
+
# end
|
252
284
|
|
253
285
|
# Returns the underlying XML string for this object as a string.
|
254
286
|
# user = swivel.call '/users/1000010'
|
@@ -283,8 +315,13 @@ module Swivel
|
|
283
315
|
end
|
284
316
|
|
285
317
|
def value_for s #:nordoc:
|
286
|
-
if s.match Time::RFC822_DATETIME_REGEX
|
287
|
-
|
318
|
+
if s.match Time::RFC822_DATETIME_REGEX or
|
319
|
+
s.match Time::XML_DATETIME_REGEX
|
320
|
+
begin
|
321
|
+
Time.parse s
|
322
|
+
rescue ArgumentError => e
|
323
|
+
DateTime.parse s
|
324
|
+
end
|
288
325
|
elsif s.numeric?
|
289
326
|
s.to_i
|
290
327
|
else
|
@@ -293,21 +330,56 @@ module Swivel
|
|
293
330
|
end
|
294
331
|
end
|
295
332
|
|
296
|
-
class Annotation < Response
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
333
|
+
class Annotation < Response
|
334
|
+
def self.resource
|
335
|
+
'annotation'
|
336
|
+
end
|
337
|
+
end
|
338
|
+
class Comment < Response
|
339
|
+
def self.resource
|
340
|
+
'comment'
|
341
|
+
end
|
342
|
+
end
|
343
|
+
class Invitation < Response
|
344
|
+
def self.resource
|
345
|
+
'invitation'
|
346
|
+
end
|
347
|
+
end
|
348
|
+
class Page < Response
|
349
|
+
def self.resource
|
350
|
+
'page'
|
351
|
+
end
|
352
|
+
end
|
353
|
+
class PageAsset < Response
|
354
|
+
def self.resource
|
355
|
+
'page-asset'
|
356
|
+
end
|
357
|
+
end
|
358
|
+
class Prose < Response
|
359
|
+
def self.resource
|
360
|
+
'prose'
|
361
|
+
end
|
362
|
+
end
|
301
363
|
|
302
364
|
class Permission < Response
|
365
|
+
def self.resource
|
366
|
+
'permission'
|
367
|
+
end
|
368
|
+
|
303
369
|
%w/accessor accessable/.each do |thing|
|
304
370
|
define_method thing.to_sym do
|
305
371
|
unless instance_variable_get "@#{thing}".to_sym
|
306
372
|
c =
|
307
373
|
begin
|
308
374
|
dashified = thing.dasherize
|
309
|
-
|
310
|
-
|
375
|
+
if @hash_doc[dashified]
|
376
|
+
raise 'unexpected document, expecting only one key to be name here' if @hash_doc[dashified].keys.size != 1
|
377
|
+
resource = @hash_doc[dashified].keys.first
|
378
|
+
h = @hash_doc[dashified][resource]
|
379
|
+
Response.class_for(resource).new nil, @connection, h
|
380
|
+
else
|
381
|
+
nil
|
382
|
+
end
|
311
383
|
rescue
|
312
384
|
warn e
|
313
385
|
nil
|
@@ -320,14 +392,24 @@ module Swivel
|
|
320
392
|
end
|
321
393
|
|
322
394
|
class Activity < Response
|
395
|
+
def self.resource
|
396
|
+
'activity'
|
397
|
+
end
|
398
|
+
|
323
399
|
%w/source subject verb direct_object prepositional_object/.each do |part|
|
324
400
|
define_method part.to_sym do
|
325
401
|
unless instance_variable_get "@#{part}".to_sym
|
326
402
|
c =
|
327
403
|
begin
|
328
404
|
dashified = part.dasherize
|
329
|
-
|
330
|
-
|
405
|
+
if @hash_doc[dashified]
|
406
|
+
raise 'unexpected document, expecting only one key to be name here' if @hash_doc[dashified].keys.size != 1
|
407
|
+
resource = @hash_doc[dashified].keys.first
|
408
|
+
h = @hash_doc[dashified][resource]
|
409
|
+
Response.class_for(resource).new nil, @connection, h
|
410
|
+
else
|
411
|
+
nil
|
412
|
+
end
|
331
413
|
rescue
|
332
414
|
warn e
|
333
415
|
nil
|
@@ -346,6 +428,9 @@ module Swivel
|
|
346
428
|
#
|
347
429
|
# data_set = swivel.data_set id
|
348
430
|
# data_set.append! :data => `/tmp/append.csv`
|
431
|
+
def self.resource
|
432
|
+
'data-set'
|
433
|
+
end
|
349
434
|
|
350
435
|
def append! options = Hash.new
|
351
436
|
options.merge! :mode => 'append', :id => self.id
|
@@ -380,6 +465,9 @@ module Swivel
|
|
380
465
|
end
|
381
466
|
|
382
467
|
class Visual < Response
|
468
|
+
def self.resource
|
469
|
+
'visual'
|
470
|
+
end
|
383
471
|
|
384
472
|
## Forms the url for an image.
|
385
473
|
def image_url options={}
|
@@ -392,35 +480,43 @@ module Swivel
|
|
392
480
|
end
|
393
481
|
|
394
482
|
class User < Response
|
395
|
-
def
|
396
|
-
|
397
|
-
rescue
|
398
|
-
false
|
483
|
+
def self.resource
|
484
|
+
'user'
|
399
485
|
end
|
400
|
-
|
401
486
|
def to_param
|
402
487
|
name
|
403
488
|
end
|
404
489
|
end
|
405
490
|
|
406
491
|
class Group < Response
|
407
|
-
def
|
408
|
-
|
409
|
-
begin
|
410
|
-
o = REXML::XPath.first(@doc, "/group/members").elements[1]
|
411
|
-
Response.class_for(o.name).new o.to_s
|
412
|
-
rescue
|
413
|
-
warn e
|
414
|
-
nil
|
415
|
-
end
|
492
|
+
def self.resource
|
493
|
+
'group'
|
416
494
|
end
|
417
|
-
|
418
495
|
def to_param
|
419
496
|
(!slug.blank? and slug) or super.to_param
|
420
497
|
end
|
498
|
+
def permission_for(member)
|
499
|
+
raise 'group has no permissions' unless permissions
|
500
|
+
perm = permissions.find do |p|
|
501
|
+
p.accessor_id == member.id and
|
502
|
+
p.accessor_type == member.class.name.sub(/^Swivel::/,'')
|
503
|
+
end
|
504
|
+
perm
|
505
|
+
end
|
506
|
+
def has_admin?(member)
|
507
|
+
permission = permission_for(member)
|
508
|
+
permission and permission.rights == ::Permission::WRITE
|
509
|
+
end
|
510
|
+
|
511
|
+
def has_member?(member)
|
512
|
+
!!permission_for(member)
|
513
|
+
end
|
421
514
|
end
|
422
515
|
|
423
516
|
class Graph < Response
|
517
|
+
def self.resource
|
518
|
+
'graph'
|
519
|
+
end
|
424
520
|
def image_url options = Hash.new
|
425
521
|
host, port = @connection.config[:host], @connection.config[:port]
|
426
522
|
port = port == 80 ? '' : ":#{port}"
|
@@ -478,16 +574,19 @@ module Swivel
|
|
478
574
|
|
479
575
|
class List < Response
|
480
576
|
|
577
|
+
attr_accessor :resource, :count, :total
|
578
|
+
|
481
579
|
# Instantiate a new Swivel::List. Calls super, then does a bit more extra processing.
|
482
|
-
def initialize
|
483
|
-
super
|
580
|
+
def initialize xml, connection, hashdoc, resource, count, total
|
581
|
+
super(xml, connection, hashdoc)
|
484
582
|
unless @processed
|
583
|
+
@count = count.nil? ? 0 : count.to_i
|
584
|
+
@total = total.nil? ? 0 : total.to_i
|
485
585
|
@list = Array.new
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
@list << Response.class_for(resource).new(e.to_s, @connection)
|
586
|
+
@hash_doc ||= []
|
587
|
+
@hash_doc = [@hash_doc] if !@hash_doc.is_a?(Array)
|
588
|
+
@hash_doc.each do |h|
|
589
|
+
@list << Response.class_for(resource).new(nil, @connection, h)
|
491
590
|
end
|
492
591
|
@processed = true
|
493
592
|
end
|
@@ -521,6 +620,9 @@ module Swivel
|
|
521
620
|
class ApiError < StandardError; end
|
522
621
|
|
523
622
|
class Error < Response
|
623
|
+
def self.resource
|
624
|
+
'error'
|
625
|
+
end
|
524
626
|
def raise
|
525
627
|
super ApiError, message, backtrace.split("\n")
|
526
628
|
end
|
@@ -595,6 +697,10 @@ module Swivel
|
|
595
697
|
@headers.merge! 'Accept' => 'application/xml'
|
596
698
|
end
|
597
699
|
|
700
|
+
def inspect
|
701
|
+
super.sub /:api_key=>"[^"]*"/, ':api_key=>[FILTERED]'
|
702
|
+
end
|
703
|
+
|
598
704
|
# Keep track of how many calls have been made. Note that this means
|
599
705
|
# multiple Swivel::Connection instances increment the same call count
|
600
706
|
# if those Swivel::Connections run within the same thread.
|
@@ -660,11 +766,40 @@ module Swivel
|
|
660
766
|
end
|
661
767
|
@@calls << {:url => path, :time => elapsed_time}
|
662
768
|
body = response.body
|
769
|
+
log "#{config[:host]}:#{config[:port]}/#{path}"
|
663
770
|
log body
|
664
771
|
case response.content_type
|
665
772
|
when 'application/xml'
|
666
773
|
doc = REXML::Document.new body
|
667
|
-
|
774
|
+
if doc.root.elements[1].name.downcase == 'list'
|
775
|
+
h = CobraVsMongoose.xml_to_hash body
|
776
|
+
h = h["response"] if h["response"]
|
777
|
+
resource = h['list']['@resource']
|
778
|
+
count = h['list']['@count']
|
779
|
+
total = h['list']['@total']
|
780
|
+
h = h['list'][resource]
|
781
|
+
h ||= []
|
782
|
+
List.new nil, self, h, resource, count, total
|
783
|
+
|
784
|
+
# 2c) enables rails ActiveRecord::Base#to_xml style lists (at root)
|
785
|
+
elsif doc.root.elements.to_a.size == 1 and
|
786
|
+
doc.root.elements[1].attributes['type'] == 'array'
|
787
|
+
h = CobraVsMongoose.xml_to_hash body
|
788
|
+
h = h["response"] if h["response"]
|
789
|
+
|
790
|
+
list_name = doc.root.elements[1].name
|
791
|
+
member_name = (h[list_name].keys.find{|k| !/^@/.match(k) } || list_name.singularize)
|
792
|
+
list = h[list_name][member_name] || []
|
793
|
+
|
794
|
+
list = [list] unless list.is_a?(Array)
|
795
|
+
total = (doc.root.attributes['total'].to_i rescue list.size)
|
796
|
+
# hack to get the gem to parse all array types correctly
|
797
|
+
hack_list = list.map{|i| {member_name => i}}
|
798
|
+
Swivel::List.new nil, @connection, hack_list, member_name, list.size, total
|
799
|
+
|
800
|
+
else
|
801
|
+
Response.class_for(doc.root.elements[1].name).new body, self, nil
|
802
|
+
end
|
668
803
|
when 'text/csv', 'text/plain'
|
669
804
|
body
|
670
805
|
else
|
data/lib/swivel2.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_support'
|
3
|
+
require File.dirname(__FILE__) + '/../vendor/activesupport-2.0.2-/lib/active_support/core_ext/module/attr_accessor_with_default'
|
4
|
+
require File.dirname(__FILE__) + '/../vendor/activesupport-2.0.2-/lib/active_support/core_ext/object/misc'
|
5
|
+
require File.dirname(__FILE__) + '/../vendor/activeresource-2.0.2-/lib/active_resource'
|
6
|
+
|
7
|
+
require File.dirname(__FILE__) + '/swivel2/config'
|
8
|
+
require File.dirname(__FILE__) + '/swivel2/connection'
|
9
|
+
require File.dirname(__FILE__) + '/swivel2/formats'
|
10
|
+
require File.dirname(__FILE__) + '/swivel2/performance'
|
11
|
+
require File.dirname(__FILE__) + '/swivel2/response'
|
metadata
CHANGED
@@ -3,13 +3,13 @@ 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:
|
6
|
+
version: 0.0.146
|
7
|
+
date: 2008-01-10 00:00:00 -08:00
|
8
8
|
summary: Ruby interface to the Swivel API.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
11
|
email: huned@swivel.com
|
12
|
-
homepage: http://swivel.com/developer
|
12
|
+
homepage: http://www.swivel.com/developer
|
13
13
|
rubyforge_project:
|
14
14
|
description: This gem installs client library for accessing Swivel through it's API.
|
15
15
|
autorequire:
|
@@ -32,6 +32,8 @@ files:
|
|
32
32
|
- COPYING
|
33
33
|
- README
|
34
34
|
- Rakefile
|
35
|
+
- lib/swivel2.rb
|
36
|
+
- lib/swivel2
|
35
37
|
- lib/swivel.rb
|
36
38
|
- bin/swivel
|
37
39
|
- CHANGELOG
|
@@ -40,7 +42,7 @@ test_files: []
|
|
40
42
|
rdoc_options:
|
41
43
|
- --line-numbers
|
42
44
|
- --title
|
43
|
-
-
|
45
|
+
- swivel2.rb
|
44
46
|
- --main
|
45
47
|
- README
|
46
48
|
- --inline-source
|