davetron5000-gliffy 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +52 -0
- data/bin/gliffy +8 -0
- data/ext/array_has_response.rb +10 -0
- data/lib/gliffy.rb +1 -0
- data/lib/gliffy/account.rb +55 -0
- data/lib/gliffy/cli.rb +260 -0
- data/lib/gliffy/commands/commands.rb +149 -0
- data/lib/gliffy/config.rb +55 -0
- data/lib/gliffy/diagram.rb +100 -0
- data/lib/gliffy/folder.rb +63 -0
- data/lib/gliffy/gliffy.rb +367 -0
- data/lib/gliffy/response.rb +127 -0
- data/lib/gliffy/rest.rb +110 -0
- data/lib/gliffy/url.rb +67 -0
- data/lib/gliffy/user.rb +72 -0
- metadata +81 -0
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
require 'array_has_response'
|
3
|
+
require 'gliffy/rest'
|
4
|
+
|
5
|
+
include REXML
|
6
|
+
|
7
|
+
module Gliffy
|
8
|
+
|
9
|
+
# Base class for Gliffy response. This class is also the entry point
|
10
|
+
# for parsing Gliffy XML:
|
11
|
+
#
|
12
|
+
# xml = get_xml_from_gliffy
|
13
|
+
# response = self.from_xml(xml)
|
14
|
+
# # response is now a Response or subclass of response
|
15
|
+
# if response.success?
|
16
|
+
# if response.not_modified?
|
17
|
+
# # use the item(s) from your local cache
|
18
|
+
# else
|
19
|
+
# # it should be what you expect, e.g. Diagram, array of Users, etc.
|
20
|
+
# end
|
21
|
+
# else
|
22
|
+
# puts response.message # in this case it's an Error
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
#
|
26
|
+
class Response
|
27
|
+
|
28
|
+
# Creates a Response based on the XML passed in.
|
29
|
+
# The xml can be anything passable to REXML::Document.new, such as
|
30
|
+
# a Document, or a string containing XML
|
31
|
+
#
|
32
|
+
# This will return a Response, a subclass of Response, or an array of
|
33
|
+
# Response/subclass of Response objects. In the case where an array is returned
|
34
|
+
# both success? and not_modified? may be called, so the idiom in the class Rdoc should
|
35
|
+
# be usable regardless of return value
|
36
|
+
def self.from_xml(xml)
|
37
|
+
raise ArgumentError.new("xml may not be null to #{to_s}.from_xml") if !xml
|
38
|
+
root = Document.new(xml).root
|
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])
|
47
|
+
else
|
48
|
+
response = Response.new
|
49
|
+
end
|
50
|
+
|
51
|
+
response.success = success
|
52
|
+
response.not_modified = not_modified
|
53
|
+
|
54
|
+
response
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns true if the response represents a successful response
|
58
|
+
# false indicates failure and that element is most likely a Error
|
59
|
+
def success?; @success; end
|
60
|
+
|
61
|
+
# Returns true if this response indicates the requested resource
|
62
|
+
# was not modified, based on the headers provided with the request.
|
63
|
+
def not_modified?; @not_modified; end
|
64
|
+
|
65
|
+
def success=(s); @success = s; end
|
66
|
+
def not_modified=(s); @not_modified = s; end
|
67
|
+
|
68
|
+
# Provides access to the rest implementation
|
69
|
+
attr_accessor :rest
|
70
|
+
|
71
|
+
protected
|
72
|
+
|
73
|
+
def initialize
|
74
|
+
@success = true
|
75
|
+
@not_modified = false
|
76
|
+
end
|
77
|
+
|
78
|
+
# Converts a dash-delimited string to a camel-cased classname
|
79
|
+
def self.to_classname(name)
|
80
|
+
classname = ""
|
81
|
+
name.split(/-/).each do |part|
|
82
|
+
classname += part.capitalize
|
83
|
+
end
|
84
|
+
classname
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class ArrayResponseParser
|
89
|
+
def self.from_xml(element)
|
90
|
+
single_classname = self.to_s.gsub(/s$/,'').gsub(/Gliffy::/,'')
|
91
|
+
klass = Gliffy.const_get(single_classname)
|
92
|
+
list = Array.new
|
93
|
+
if (element)
|
94
|
+
element.each_element do |element|
|
95
|
+
list << klass.from_xml(element)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
list
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# An error from Gliffy
|
103
|
+
class Error < Response
|
104
|
+
# The HTTP status code that can help indicate the nature of the problem
|
105
|
+
attr_reader :http_status
|
106
|
+
# A description of the error that occured; not necessarily for human
|
107
|
+
# consumption
|
108
|
+
attr_reader :message
|
109
|
+
|
110
|
+
def self.from_xml(element)
|
111
|
+
message = element.text
|
112
|
+
http_status = element.attributes['http-status'].to_i
|
113
|
+
Error.new(message,http_status)
|
114
|
+
end
|
115
|
+
|
116
|
+
def initialize(message,http_status)
|
117
|
+
super()
|
118
|
+
@message = message
|
119
|
+
@http_status = http_status
|
120
|
+
end
|
121
|
+
|
122
|
+
def to_s
|
123
|
+
"#{@http_status}: #{@message}"
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
data/lib/gliffy/rest.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'cgi'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'request_errors'
|
5
|
+
require 'resource'
|
6
|
+
require 'rest_client'
|
7
|
+
|
8
|
+
require 'gliffy/config'
|
9
|
+
require 'gliffy/url'
|
10
|
+
|
11
|
+
require 'logger'
|
12
|
+
|
13
|
+
module Gliffy
|
14
|
+
|
15
|
+
# Provides REST access to Gliffy, handling the signing of the requests
|
16
|
+
# but not the parsing of the results. This class responds to the four primary HTTP methods:
|
17
|
+
#
|
18
|
+
# * get
|
19
|
+
# * put
|
20
|
+
# * post
|
21
|
+
# * delete
|
22
|
+
#
|
23
|
+
# Each method takes three parameters:
|
24
|
+
# [url] - the relative URL being requested
|
25
|
+
# [params] - a hash of parameters to include in the request (these are specific to the request, not things like apiKey or token)
|
26
|
+
# [headers] - any HTTP headers you want to set
|
27
|
+
#
|
28
|
+
# params and headers are optional. These methods return whatever Gliffy sent back. The context
|
29
|
+
# of the request should be used to determine the type, however it should be relatively safe to call
|
30
|
+
# Response#success? on whatever is returned to determine if everythign was OK
|
31
|
+
#
|
32
|
+
class Rest
|
33
|
+
|
34
|
+
# Provides access to the current token,
|
35
|
+
# returning nil if none has been set
|
36
|
+
attr_accessor :current_token
|
37
|
+
|
38
|
+
# Provides access to the logger
|
39
|
+
# Do not set this to nil
|
40
|
+
attr_accessor :logger
|
41
|
+
|
42
|
+
attr_accessor :rest_client
|
43
|
+
|
44
|
+
# Create an accessor to the Gliffy REST api
|
45
|
+
#
|
46
|
+
def initialize
|
47
|
+
@current_token = nil
|
48
|
+
self.rest_client=RestClient
|
49
|
+
@logger = Logger.new(Config.config.log_device)
|
50
|
+
@logger.level = Config.config.log_level
|
51
|
+
|
52
|
+
@logger.debug("Creating #{self.class.to_s} with api_key of #{Config.config.api_key} against #{Config.config.gliffy_root}")
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the complete URL that would be requested for
|
56
|
+
# the given URL and parameters
|
57
|
+
#
|
58
|
+
# [url] the URL, relative to the Gliffy API Root
|
59
|
+
# [params] a hash of parameters
|
60
|
+
#
|
61
|
+
def get_url(url,params=nil)
|
62
|
+
return create_url(url,params)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Implements the http methods
|
66
|
+
def method_missing(symbol,*args)
|
67
|
+
if HTTP_METHODS[symbol] && (args.length > 0)
|
68
|
+
url,params,headers = args
|
69
|
+
make_rest_request(symbol,url,params,headers)
|
70
|
+
else
|
71
|
+
if (HTTP_METHODS[symbol])
|
72
|
+
raise ArgumentError.new("Wrong number of arguments for method #{symbol.to_s}")
|
73
|
+
else
|
74
|
+
super.method_missing(symbol,*args)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Create the URL that would be needed to access the given resource with the given
|
80
|
+
# parameters
|
81
|
+
#
|
82
|
+
# [+url+] url, relative to the gliffy root, to retrieve
|
83
|
+
# [+params+] optional hash of parameters
|
84
|
+
#
|
85
|
+
def create_url(url,params=nil)
|
86
|
+
url = SignedURL.new(Config.config.api_key,Config.config.secret_key,Config.config.gliffy_root,url)
|
87
|
+
url.params=params if params
|
88
|
+
url['token'] = @current_token.token if @current_token
|
89
|
+
|
90
|
+
url.full_url
|
91
|
+
end
|
92
|
+
|
93
|
+
protected
|
94
|
+
|
95
|
+
def make_rest_request(method,url,params,headers)
|
96
|
+
headers = Hash.new if !headers
|
97
|
+
request_url = create_url(url,params)
|
98
|
+
@logger.debug("#{method.to_s.upcase} #{request_url}")
|
99
|
+
@rest_client.send(method,request_url,headers)
|
100
|
+
end
|
101
|
+
|
102
|
+
HTTP_METHODS = {
|
103
|
+
:get => true,
|
104
|
+
:put => true,
|
105
|
+
:delete => true,
|
106
|
+
:post => true,
|
107
|
+
};
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
data/lib/gliffy/url.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'gliffy/config'
|
2
|
+
|
3
|
+
module Gliffy
|
4
|
+
# Handles signing and assembling the URL
|
5
|
+
class SignedURL
|
6
|
+
|
7
|
+
def initialize(api_key,secret_key,url_root,url)
|
8
|
+
raise ArgumentError.new("api_key is required") if !api_key
|
9
|
+
raise ArgumentError.new("secret_key is required") if !secret_key
|
10
|
+
raise ArgumentError.new("url_root is required") if !url_root
|
11
|
+
raise ArgumentError.new("url is required") if !url_root
|
12
|
+
@logger = Logger.new(Config.config.log_device)
|
13
|
+
@logger.level = Config.config.log_level
|
14
|
+
@params = Hash.new
|
15
|
+
@params['apiKey'] = api_key
|
16
|
+
@secret_key = secret_key
|
17
|
+
@url_root = url_root
|
18
|
+
@url = url
|
19
|
+
end
|
20
|
+
|
21
|
+
# Sets a request parameter
|
22
|
+
#
|
23
|
+
# [param] the name of the parameter, as a string
|
24
|
+
# [value] the value of the parameter, unencoded
|
25
|
+
#
|
26
|
+
def []=(param,value)
|
27
|
+
if (param == 'apiKey')
|
28
|
+
raise ArgumentError.new('You may not override the api_key in this way')
|
29
|
+
end
|
30
|
+
@params[param] = value
|
31
|
+
end
|
32
|
+
|
33
|
+
# Sets all request parameters to those in the hash.
|
34
|
+
def params=(params_hash)
|
35
|
+
api_key = @params['apiKey']
|
36
|
+
@params.replace(params_hash)
|
37
|
+
@params['apiKey'] = api_key
|
38
|
+
end
|
39
|
+
|
40
|
+
# Gets the full URL, signed and ready to be requested
|
41
|
+
def full_url
|
42
|
+
@logger.debug("Getting full_url of #{@url}")
|
43
|
+
to_sign = @url
|
44
|
+
url_params = Hash.new
|
45
|
+
@params.keys.sort.each do |key|
|
46
|
+
@logger.debug("Adding param #{key} (#{to_sign.class.to_s}, #{key.class.to_s}) : #{to_sign.to_s}")
|
47
|
+
to_sign += key
|
48
|
+
to_sign += @params[key].to_s
|
49
|
+
url_params[key.to_s] = @params[key].to_s
|
50
|
+
end
|
51
|
+
to_sign += @secret_key
|
52
|
+
@logger.debug("Signing '#{to_sign}'")
|
53
|
+
signature = Digest::MD5.hexdigest(to_sign)
|
54
|
+
signature.gsub!(/^0/,'') while (signature =~ /^0/)
|
55
|
+
@logger.debug("signature == '#{signature}'")
|
56
|
+
url_params['signature'] = signature
|
57
|
+
|
58
|
+
url = @url_root + @url + '?'
|
59
|
+
url_params.keys.sort.each do |key|
|
60
|
+
val = CGI::escape(url_params[key])
|
61
|
+
url += "#{key}=#{val}&"
|
62
|
+
end
|
63
|
+
url.gsub!(/\&$/,'')
|
64
|
+
return url
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/gliffy/user.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
require 'array_has_response'
|
3
|
+
require 'gliffy/rest'
|
4
|
+
|
5
|
+
include REXML
|
6
|
+
|
7
|
+
module Gliffy
|
8
|
+
|
9
|
+
# A user token
|
10
|
+
class UserToken < Response
|
11
|
+
attr_reader :expiration
|
12
|
+
attr_reader :token
|
13
|
+
def self.from_xml(element)
|
14
|
+
expiration = Time.at(element.attributes['expiration'].to_i / 1000)
|
15
|
+
token = element.text
|
16
|
+
UserToken.new(expiration,token)
|
17
|
+
end
|
18
|
+
|
19
|
+
def expired?
|
20
|
+
Time.now > expiration
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(expiration,token)
|
24
|
+
super()
|
25
|
+
@expiration = expiration
|
26
|
+
@token = token
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
class Users < ArrayResponseParser; end
|
32
|
+
|
33
|
+
# A user of Gliffy and the main entry point to using the API (see #initiate_session)
|
34
|
+
class User < Response
|
35
|
+
|
36
|
+
# The user's username, which is their identifier within an account
|
37
|
+
attr_reader :username
|
38
|
+
# The user's email, which is unique to them in the entire Gliffy system.
|
39
|
+
# Note that this isn't guaranteed to be a real email, nor is
|
40
|
+
# it guaranteed to be the user's actual email. This is
|
41
|
+
# assigned by the system unless overridden by the API.
|
42
|
+
attr_reader :email
|
43
|
+
attr_reader :id
|
44
|
+
|
45
|
+
def self.from_xml(element)
|
46
|
+
id = element.attributes['id'].to_i
|
47
|
+
is_admin = false
|
48
|
+
is_admin = element.attributes['is-admin'] == 'true' if element.attributes['is-admin']
|
49
|
+
username = element.elements['username'] ? element.elements['username'].text : nil
|
50
|
+
email = element.elements['email'] ? element.elements['email'].text : nil
|
51
|
+
|
52
|
+
User.new(id,is_admin,username,email)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns true if this user is an admin of the account in which they live
|
56
|
+
def is_admin?
|
57
|
+
@is_admin
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
|
62
|
+
def initialize(id,is_admin,username,email)
|
63
|
+
super()
|
64
|
+
@id = id
|
65
|
+
@is_admin = is_admin
|
66
|
+
@username = username
|
67
|
+
@email = email
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: davetron5000-gliffy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Copeland
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-12-22 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: technoweenie-rest-client
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.5.1
|
23
|
+
version:
|
24
|
+
description:
|
25
|
+
email: davidcopeland@naildrivin5.com
|
26
|
+
executables:
|
27
|
+
- gliffy
|
28
|
+
extensions: []
|
29
|
+
|
30
|
+
extra_rdoc_files:
|
31
|
+
- README.rdoc
|
32
|
+
files:
|
33
|
+
- ext/array_has_response.rb
|
34
|
+
- lib/gliffy/account.rb
|
35
|
+
- lib/gliffy/cli.rb
|
36
|
+
- lib/gliffy/commands/commands.rb
|
37
|
+
- lib/gliffy/config.rb
|
38
|
+
- lib/gliffy/diagram.rb
|
39
|
+
- lib/gliffy/folder.rb
|
40
|
+
- lib/gliffy/gliffy.rb
|
41
|
+
- lib/gliffy/response.rb
|
42
|
+
- lib/gliffy/rest.rb
|
43
|
+
- lib/gliffy/url.rb
|
44
|
+
- lib/gliffy/user.rb
|
45
|
+
- lib/gliffy.rb
|
46
|
+
- bin/gliffy
|
47
|
+
- README.rdoc
|
48
|
+
has_rdoc: true
|
49
|
+
homepage: http://davetron5000.github.com/gliffy
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options:
|
52
|
+
- --title
|
53
|
+
- Gliffy Ruby Client
|
54
|
+
- --main
|
55
|
+
- README.rdoc
|
56
|
+
- -ri
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
- ext
|
60
|
+
- lib
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: "0"
|
66
|
+
version:
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: "0"
|
72
|
+
version:
|
73
|
+
requirements: []
|
74
|
+
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 1.2.0
|
77
|
+
signing_key:
|
78
|
+
specification_version: 2
|
79
|
+
summary: client to access the Gliffy API
|
80
|
+
test_files: []
|
81
|
+
|