davetron5000-gliffy 0.1.1
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 +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
|
+
|