davetron5000-gliffy 0.1.7 → 0.2.0
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.rdoc +175 -48
- data/bin/gliffy +298 -22
- data/lib/gliffy.rb +4 -0
- data/lib/gliffy/credentials.rb +78 -0
- data/lib/gliffy/handle.rb +269 -290
- data/lib/gliffy/request.rb +101 -0
- data/lib/gliffy/response.rb +248 -91
- data/lib/gliffy/url.rb +133 -36
- metadata +21 -21
- data/lib/gliffy/account.rb +0 -55
- data/lib/gliffy/cli.rb +0 -144
- data/lib/gliffy/commands.rb +0 -6
- data/lib/gliffy/commands/delete.rb +0 -10
- data/lib/gliffy/commands/edit.rb +0 -19
- data/lib/gliffy/commands/get.rb +0 -46
- data/lib/gliffy/commands/list.rb +0 -27
- data/lib/gliffy/commands/new.rb +0 -25
- data/lib/gliffy/commands/url.rb +0 -21
- data/lib/gliffy/config.rb +0 -55
- data/lib/gliffy/diagram.rb +0 -100
- data/lib/gliffy/folder.rb +0 -63
- data/lib/gliffy/rest.rb +0 -110
- data/lib/gliffy/user.rb +0 -72
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'httparty'
|
3
|
+
require 'logger'
|
4
|
+
require 'gliffy/url'
|
5
|
+
|
6
|
+
module Gliffy
|
7
|
+
# Handles making a request of the Gliffy server and all that that entails.
|
8
|
+
# This allows you to make requests using the "action" and the URL as
|
9
|
+
# described in the Gliffy documentation. For example, if you wish
|
10
|
+
# to get a user's folders, you could
|
11
|
+
#
|
12
|
+
# request = Request.get('https://www.gliffy.com/api/1.0',credentials)
|
13
|
+
# results = request.create('accounts/$account_id/users/$username/oauth_token.xml')
|
14
|
+
# credentials.update_access_token(
|
15
|
+
# results['response']['oauth_token_credentials']['oauth_token_secret'],
|
16
|
+
# results['response']['oauth_token_credentials']['oauth_token'])
|
17
|
+
# request.get('accounts/$account_id/users/$username/folders.xml')
|
18
|
+
#
|
19
|
+
# This will return a hash-referencable DOM objects, subbing the account id
|
20
|
+
# and username in when making the request (additionally, setting all needed
|
21
|
+
# parameters and signing the request).
|
22
|
+
class Request
|
23
|
+
|
24
|
+
attr_accessor :logger
|
25
|
+
# Modify the HTTP transport agent used. This should
|
26
|
+
# have the same interface as HTTParty.
|
27
|
+
attr_accessor :http
|
28
|
+
|
29
|
+
# Create a new request object.
|
30
|
+
#
|
31
|
+
# [+api_root+] the root of where all API calls are made
|
32
|
+
# [+credentials+] a Credentials object with all the current credentials
|
33
|
+
# [+http+] This should implement the HTTParty interface
|
34
|
+
def initialize(api_root,credentials,http=HTTParty,logger=nil)
|
35
|
+
@api_root = api_root
|
36
|
+
@api_root += '/' if !(@api_root =~ /\/$/)
|
37
|
+
@credentials = credentials
|
38
|
+
@logger = logger || Logger.new(STDOUT)
|
39
|
+
@logger.level = Logger::INFO
|
40
|
+
@http = http
|
41
|
+
end
|
42
|
+
|
43
|
+
# Implements getting a request and returning a response
|
44
|
+
# The implements methods that correspond to Gliffy's "action=" parameter.
|
45
|
+
# Based on this, it will know to do a GET or POST. The method signature is
|
46
|
+
#
|
47
|
+
# request.action_name(url,params)
|
48
|
+
# for example
|
49
|
+
# request.get('accounts/$account_id.xml',:showUsers => true)
|
50
|
+
# note that you can use `$account_id` and `$username` in any URL and it
|
51
|
+
# will be replaced accordingly.
|
52
|
+
#
|
53
|
+
# The return value is the return value from HTTParty, which is basically a hash
|
54
|
+
# that allows access to the returned DOM tree
|
55
|
+
def method_missing(symbol,*args)
|
56
|
+
if args.length >= 1
|
57
|
+
link_only = false
|
58
|
+
if symbol == :link_for
|
59
|
+
symbol = args.shift
|
60
|
+
link_only = true
|
61
|
+
end
|
62
|
+
@logger.debug("Executing a #{symbol} against gliffy for url #{args[0]}")
|
63
|
+
|
64
|
+
# exposing this for testing
|
65
|
+
protocol = determine_protocol(args[1])
|
66
|
+
@full_url_no_params = protocol + "://" + @api_root + replace_url(args[0])
|
67
|
+
url = SignedURL.new(@credentials,@full_url_no_params,symbol == :GET ? 'GET' : 'POST')
|
68
|
+
url.logger = @logger
|
69
|
+
url.params = args[1] if !args[1].nil?
|
70
|
+
url[:protocol_override] = nil
|
71
|
+
url[:action] = symbol
|
72
|
+
|
73
|
+
# These can be override for testing purposes
|
74
|
+
timestamp = args[2] if args[2]
|
75
|
+
nonce = args[3] if args[3]
|
76
|
+
|
77
|
+
full_url = url.full_url(timestamp,nonce)
|
78
|
+
if link_only
|
79
|
+
return full_url
|
80
|
+
else
|
81
|
+
response = @http.post(full_url)
|
82
|
+
return response
|
83
|
+
end
|
84
|
+
else
|
85
|
+
super(symbol,args)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def determine_protocol(params)
|
90
|
+
if params && params[:protocol_override]
|
91
|
+
params[:protocol_override].to_s
|
92
|
+
else
|
93
|
+
@credentials.default_protocol.to_s
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def replace_url(url)
|
98
|
+
return url.gsub('$account_id',@credentials.account_id.to_s).gsub('$username',@credentials.username)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/lib/gliffy/response.rb
CHANGED
@@ -1,127 +1,284 @@
|
|
1
|
-
require '
|
2
|
-
require 'array_has_response'
|
3
|
-
require 'gliffy/rest'
|
4
|
-
|
5
|
-
include REXML
|
1
|
+
require 'gliffy/request'
|
6
2
|
|
7
3
|
module Gliffy
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
#
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
|
25
|
-
|
4
|
+
# Indicates no response at all was received.
|
5
|
+
class NoResponseException < Exception
|
6
|
+
def initialize(message)
|
7
|
+
super(message)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
# Indicates that a response was received by that it wasn't
|
11
|
+
# parsable or readable as a Gliffy <response>
|
12
|
+
class BadResponseException < Exception
|
13
|
+
def initialize(message)
|
14
|
+
super(message)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
# Indicates that a valid Gliffy <response> was received and that it
|
18
|
+
# indicated failure. The message is the message sent by gliffy (if it was
|
19
|
+
# in the response)
|
20
|
+
class RequestFailedException < Exception
|
21
|
+
def initialize(message)
|
22
|
+
super(message)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
# Base class for all response from gliffy
|
26
26
|
class Response
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
not_modified = root.attributes['not-modified'] == "true"
|
40
|
-
success = root.attributes['success'] == "true"
|
41
|
-
|
42
|
-
response = nil
|
43
|
-
if ! root.elements.empty?
|
44
|
-
klassname = to_classname(root.elements[1].name)
|
45
|
-
klass = Gliffy.const_get(klassname)
|
46
|
-
response = klass.from_xml(root.elements[1])
|
27
|
+
@@normal_error_callback = Proc.new do |response,exception|
|
28
|
+
if response
|
29
|
+
message = response.inspect
|
30
|
+
if response['response'] && response['response']['error']
|
31
|
+
http_status = response['response']['error']['http_status']
|
32
|
+
if http_status = "404"
|
33
|
+
message = "Not Found"
|
34
|
+
else
|
35
|
+
message = "HTTP Status #{http_status}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
raise exception.class.new(message)
|
47
39
|
else
|
48
|
-
|
40
|
+
raise exception
|
49
41
|
end
|
42
|
+
end
|
50
43
|
|
51
|
-
|
52
|
-
response.not_modified = not_modified
|
44
|
+
@@error_callback = @@normal_error_callback
|
53
45
|
|
54
|
-
|
46
|
+
# Factory for creating actual response subclasses.
|
47
|
+
# This takes the results of HTTParty's response, which is a hash, essentially.
|
48
|
+
# This assumes that any checks for validity have been done.
|
49
|
+
# [error_response] Set this to a Proc to handle errors if you don't want the default behavior. The proc will get two arguments:
|
50
|
+
# [+response+] the raw response received (may be nil)
|
51
|
+
# [+exception+] One of NoResponseException, BadResponseException, or RequestFailedException. The message of that exception is a usable message if you want to ignore the exception
|
52
|
+
def self.from_http_response(response,error_callback=nil)
|
53
|
+
verify(response,error_callback)
|
54
|
+
root = response['response']
|
55
|
+
klass = nil
|
56
|
+
root.keys.each do |key|
|
57
|
+
klassname = to_classname(key)
|
58
|
+
begin
|
59
|
+
this_klass = Gliffy.const_get(klassname)
|
60
|
+
rescue NameError
|
61
|
+
this_klass = nil
|
62
|
+
end
|
63
|
+
klass = this_klass unless this_klass.nil?
|
64
|
+
end
|
65
|
+
return Response.new(response.body) if !klass
|
66
|
+
return klass.from_http_response(root)
|
55
67
|
end
|
56
68
|
|
57
|
-
#
|
58
|
-
#
|
59
|
-
def
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
69
|
+
# Verifies that the response represents success, calling
|
70
|
+
# the error callback if it doesn't
|
71
|
+
def self.verify(response,error_callback)
|
72
|
+
error_callback = @@error_callback if error_callback.nil?
|
73
|
+
return error_callback.call(response,NoResponseException.new('No response received at all')) if response.nil?
|
74
|
+
return error_callback.call(response,BadResponseException.new('Not a Gliffy response')) if !response['response']
|
75
|
+
return error_callback.call(response,BadResponseException.new('No indication of success from Gliffy')) if !response['response']['success']
|
76
|
+
if response['response']['success'] != 'true'
|
77
|
+
error = response['response']['error']
|
78
|
+
return error_callback.call(response,RequestFailedException.new('Request failed but no error inside response')) if !error
|
79
|
+
return error_callback.call(response,RequestFailedException.new(error))
|
80
|
+
end
|
81
|
+
end
|
64
82
|
|
65
|
-
def success=(s); @success = s; end
|
66
|
-
def not_modified=(s); @not_modified = s; end
|
67
83
|
|
68
|
-
|
69
|
-
attr_accessor :rest
|
84
|
+
attr_reader :body
|
70
85
|
|
71
|
-
|
86
|
+
def initialize(params)
|
87
|
+
@params = params
|
88
|
+
end
|
72
89
|
|
73
|
-
|
74
|
-
|
75
|
-
|
90
|
+
# Implements access to the object information.
|
91
|
+
# Parameters should be typed appropriately.
|
92
|
+
# The names are those as defined by the Gliffy XSD, save for dashes
|
93
|
+
# are replaced with underscores.
|
94
|
+
def method_missing(symbol,*args)
|
95
|
+
if args.length == 0
|
96
|
+
@params[symbol]
|
97
|
+
else
|
98
|
+
super(symbol,args)
|
99
|
+
end
|
76
100
|
end
|
77
101
|
|
78
|
-
|
102
|
+
private
|
79
103
|
def self.to_classname(name)
|
80
104
|
classname = ""
|
81
|
-
name.split(
|
105
|
+
name.split(/[-_]/).each do |part|
|
82
106
|
classname += part.capitalize
|
83
107
|
end
|
84
|
-
classname
|
108
|
+
classname + "Parser"
|
85
109
|
end
|
86
110
|
end
|
87
111
|
|
88
|
-
class
|
89
|
-
def self.
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
list <<
|
112
|
+
class ArrayParser # :nodoc:
|
113
|
+
def self.from_http_response(root,single_class,plural_name,single_name)
|
114
|
+
root = root[plural_name]
|
115
|
+
return nil if root.nil?
|
116
|
+
if root[single_name].kind_of?(Array)
|
117
|
+
list = Array.new
|
118
|
+
root[single_name].each do |item|
|
119
|
+
list << single_class.from_http_response(item)
|
96
120
|
end
|
121
|
+
list
|
122
|
+
else
|
123
|
+
[single_class.from_http_response(root[single_name])]
|
97
124
|
end
|
98
|
-
list
|
99
125
|
end
|
100
126
|
end
|
101
127
|
|
102
|
-
#
|
103
|
-
class
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
attr_reader :message
|
128
|
+
# Factory for parsing accounts
|
129
|
+
class AccountsParser # :nodoc:
|
130
|
+
def self.from_http_response(root)
|
131
|
+
return ArrayParser.from_http_response(root,AccountParser,'accounts','account')
|
132
|
+
end
|
133
|
+
end
|
109
134
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
135
|
+
# Factory for parsing folders
|
136
|
+
class FoldersParser # :nodoc:
|
137
|
+
def self.from_http_response(root)
|
138
|
+
return ArrayParser.from_http_response(root,FolderParser,'folders','folder')
|
114
139
|
end
|
140
|
+
end
|
115
141
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
142
|
+
# Factory for parsing versions
|
143
|
+
class VersionsParser # :nodoc:
|
144
|
+
def self.from_http_response(root)
|
145
|
+
return ArrayParser.from_http_response(root,VersionParser,'versions','version')
|
120
146
|
end
|
147
|
+
end
|
121
148
|
|
122
|
-
|
123
|
-
|
149
|
+
# Factory for parsing users
|
150
|
+
class UsersParser # :nodoc:
|
151
|
+
def self.from_http_response(root)
|
152
|
+
return ArrayParser.from_http_response(root,UserParser,'users','user')
|
124
153
|
end
|
154
|
+
end
|
125
155
|
|
156
|
+
# Factory for parsing documents
|
157
|
+
class DocumentsParser # :nodoc:
|
158
|
+
def self.from_http_response(root)
|
159
|
+
return ArrayParser.from_http_response(root,DocumentParser,'documents','document')
|
160
|
+
end
|
126
161
|
end
|
162
|
+
|
163
|
+
class BaseParser # :nodoc:
|
164
|
+
def self.from_http_response(root)
|
165
|
+
params = Hash.new
|
166
|
+
root.each do |key,value|
|
167
|
+
params[key.to_sym] = value
|
168
|
+
end
|
169
|
+
Response.new(params)
|
170
|
+
end
|
171
|
+
|
172
|
+
# Returns the item as an array, or nil if it was nil
|
173
|
+
def self.as_array(item)
|
174
|
+
if item.nil?
|
175
|
+
nil
|
176
|
+
else
|
177
|
+
[item].flatten
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.add_int(root,name,new_name=nil)
|
182
|
+
if root[name]
|
183
|
+
root[new_name.nil? ? name : new_name] = root[name].to_i
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.add_boolean(root,name)
|
188
|
+
root[name + "?"] = root[name] == 'true'
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.add_date(root,name)
|
192
|
+
if root[name]
|
193
|
+
root[name] = Time.at(root[name].to_i / 1000) unless root[name].kind_of? Time
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
class FolderParser < BaseParser # :nodoc:
|
199
|
+
def self.from_http_response(root)
|
200
|
+
add_int(root,'id','folder_id')
|
201
|
+
add_boolean(root,'is_default')
|
202
|
+
if root['folder']
|
203
|
+
if root['folder'].kind_of? Array
|
204
|
+
root['child_folders'] = Array.new
|
205
|
+
root['folder'].each do |one|
|
206
|
+
root['child_folders'] << from_http_response(one)
|
207
|
+
end
|
208
|
+
else
|
209
|
+
root['child_folders'] = [from_http_response(root['folder'])]
|
210
|
+
end
|
211
|
+
else
|
212
|
+
root['child_folders'] = Array.new
|
213
|
+
end
|
214
|
+
super(root)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
class UserParser < BaseParser # :nodoc:
|
219
|
+
def self.from_http_response(root)
|
220
|
+
add_int(root,'id','user_id')
|
221
|
+
add_boolean(root,'is_admin')
|
222
|
+
super(root)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# Parses the results from the test account request
|
227
|
+
class TestaccountParser < BaseParser # :nodoc:
|
228
|
+
def self.from_http_response(root)
|
229
|
+
root = root['testAccount']
|
230
|
+
add_int(root,'id','account_id')
|
231
|
+
add_int(root,'max_users')
|
232
|
+
add_boolean(root,'terms')
|
233
|
+
add_date(root,'expiration_date')
|
234
|
+
super(root)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# Factory for parsing an Account
|
239
|
+
class AccountParser < BaseParser # :nodoc:
|
240
|
+
def self.from_http_response(root)
|
241
|
+
add_int(root,'id','account_id')
|
242
|
+
add_int(root,'max_users')
|
243
|
+
add_boolean(root,'terms')
|
244
|
+
add_date(root,'expiration_date')
|
245
|
+
root['users'] = as_array(UsersParser.from_http_response(root))
|
246
|
+
super(root)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
# Factory for parsing an Account
|
251
|
+
class DocumentParser < BaseParser # :nodoc:
|
252
|
+
def self.from_http_response(root)
|
253
|
+
add_int(root,'id','document_id')
|
254
|
+
add_int(root,'num_versions')
|
255
|
+
add_boolean(root,'is_private')
|
256
|
+
add_boolean(root,'is_public')
|
257
|
+
add_date(root,'create_date')
|
258
|
+
add_date(root,'mod_date')
|
259
|
+
add_date(root,'published_date')
|
260
|
+
root['owner'] = UserParser.from_http_response(root['owner'])
|
261
|
+
root['versions'] = as_array(VersionsParser.from_http_response(root))
|
262
|
+
super(root)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
class VersionParser < BaseParser # :nodoc:
|
267
|
+
def self.from_http_response(root)
|
268
|
+
add_int(root,'id','version_id')
|
269
|
+
add_int(root,'num')
|
270
|
+
add_date(root,'create_date')
|
271
|
+
root['owner'] = UserParser.from_http_response(root['owner'])
|
272
|
+
super(root)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
class OauthTokenCredentialsParser # :nodoc:
|
277
|
+
def self.from_http_response(root)
|
278
|
+
token = root['oauth_token_credentials']['oauth_token']
|
279
|
+
secret = root['oauth_token_credentials']['oauth_token_secret']
|
280
|
+
return AccessToken.new(token,secret)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
127
284
|
end
|