gliffy 0.0.6
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.md +25 -0
- data/lib/gliffy.rb +33 -0
- data/lib/gliffy/account.rb +59 -0
- data/lib/gliffy/api.rb +89 -0
- data/lib/gliffy/api/facade.rb +49 -0
- data/lib/gliffy/api/response.rb +49 -0
- data/lib/gliffy/document.rb +69 -0
- data/lib/gliffy/document_png.rb +56 -0
- data/lib/gliffy/folder.rb +71 -0
- data/lib/gliffy/oauth/helper.rb +10 -0
- data/spec/fixtures/account.xml +26 -0
- data/spec/fixtures/document.xml +15 -0
- data/spec/fixtures/documents-empty.xml +4 -0
- data/spec/fixtures/documents.xml +35 -0
- data/spec/fixtures/folder.xml +33 -0
- data/spec/fixtures/token.xml +8 -0
- data/spec/lib/gliffy/account_spec.rb +107 -0
- data/spec/lib/gliffy/api/facade_spec.rb +111 -0
- data/spec/lib/gliffy/api/response_spec.rb +56 -0
- data/spec/lib/gliffy/api_spec.rb +206 -0
- data/spec/lib/gliffy/document_png_spec.rb +67 -0
- data/spec/lib/gliffy/document_spec.rb +108 -0
- data/spec/lib/gliffy/folder_spec.rb +175 -0
- data/spec/lib/gliffy_spec.rb +40 -0
- data/spec/spec_helper.rb +35 -0
- metadata +181 -0
data/README.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Gliffy API wrapper
|
2
|
+
==================
|
3
|
+
|
4
|
+
Basic usage
|
5
|
+
-----------
|
6
|
+
|
7
|
+
### Initialization
|
8
|
+
|
9
|
+
api = Gliffy::API.new(ACCOUNT_ID, API_KEY, API_SECRET)
|
10
|
+
api.impersonate('user@domain.com')
|
11
|
+
api.account.root.documents
|
12
|
+
|
13
|
+
### Working with documents
|
14
|
+
|
15
|
+
doc = account.document(DOCUMENT_ID)
|
16
|
+
doc.name
|
17
|
+
doc.editor(RETURN_TO, RETURN_BUTTON_TEXT)
|
18
|
+
doc.png.full
|
19
|
+
|
20
|
+
### Navigating folders
|
21
|
+
|
22
|
+
root = account.root
|
23
|
+
root.folders[0].documents
|
24
|
+
root.folders[1].name
|
25
|
+
root.folders[1].path
|
data/lib/gliffy.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
require 'gliffy/api'
|
4
|
+
require 'gliffy/api/facade'
|
5
|
+
require 'gliffy/api/response'
|
6
|
+
require 'gliffy/account'
|
7
|
+
require 'gliffy/document'
|
8
|
+
require 'gliffy/document_png'
|
9
|
+
require 'gliffy/folder'
|
10
|
+
|
11
|
+
require 'gliffy/oauth/helper'
|
12
|
+
|
13
|
+
module Gliffy
|
14
|
+
class << self
|
15
|
+
def default_application_name
|
16
|
+
"Gliffy Ruby Gem"
|
17
|
+
end
|
18
|
+
|
19
|
+
# some calls (e.g. OAuth token generation) should be done through
|
20
|
+
# secure HTTPS; other calls (e.g. actions performed by
|
21
|
+
# non-privileged accounts) should be done though plain HTTP
|
22
|
+
#
|
23
|
+
# We're using protocol-relative URL here.
|
24
|
+
def api_root
|
25
|
+
'//www.gliffy.com/api/1.0'
|
26
|
+
end
|
27
|
+
|
28
|
+
# We're using protocol-relative (//hosrname/path/ format) URL here.
|
29
|
+
def web_root
|
30
|
+
'//www.gliffy.com'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Gliffy
|
2
|
+
class Account
|
3
|
+
TYPE_BUSINESS = "Business"
|
4
|
+
|
5
|
+
attr_reader :api
|
6
|
+
attr_reader :id
|
7
|
+
attr_reader :name
|
8
|
+
attr_reader :max_users, :type, :terms_accepted
|
9
|
+
attr_reader :expiration_date
|
10
|
+
|
11
|
+
def self.load(api, response)
|
12
|
+
Gliffy::Account.new(
|
13
|
+
api,
|
14
|
+
:id => response.integer('//g:account/@id'),
|
15
|
+
:name => response.string('//g:account/g:name'),
|
16
|
+
:max_users => response.integer('//g:account/@max-users'),
|
17
|
+
:type => response.string('//g:account/@account-type'),
|
18
|
+
:terms_accepted => response.string('//g:account/@terms') == "true",
|
19
|
+
:expiration_date => response.timestamp('//g:account/g:expiration-date')
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def root
|
24
|
+
@root ||= load_root
|
25
|
+
end
|
26
|
+
|
27
|
+
def document(document_id)
|
28
|
+
response = api.get("/accounts/#{id}/documents/#{document_id}/meta-data.xml",
|
29
|
+
:action => 'get')
|
30
|
+
|
31
|
+
Gliffy::Document.load(
|
32
|
+
self,
|
33
|
+
response.node('//g:document')
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def initialize(api, params)
|
40
|
+
@api = api
|
41
|
+
|
42
|
+
@id = params[:id]
|
43
|
+
@name = params[:name]
|
44
|
+
@max_users = params[:max_users]
|
45
|
+
@type = params[:type]
|
46
|
+
@terms_accepted = params[:terms_accepted]
|
47
|
+
@expiration_date = params[:expiration_date]
|
48
|
+
end
|
49
|
+
|
50
|
+
def load_root
|
51
|
+
response = api.get_folders(id)
|
52
|
+
|
53
|
+
Gliffy::Folder.load(
|
54
|
+
self,
|
55
|
+
response.node("/g:response/g:folders/g:folder")
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/gliffy/api.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'oauth'
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'cgi'
|
5
|
+
|
6
|
+
module Gliffy
|
7
|
+
class API
|
8
|
+
attr_reader :consumer
|
9
|
+
attr_reader :account_id
|
10
|
+
attr_accessor :application_name
|
11
|
+
|
12
|
+
def initialize(account_id, key, secret)
|
13
|
+
@consumer = init_consumer(key, secret)
|
14
|
+
@account_id = account_id
|
15
|
+
@application_name = Gliffy.default_application_name
|
16
|
+
end
|
17
|
+
|
18
|
+
def plain
|
19
|
+
Gliffy::API::Facade.http(self)
|
20
|
+
end
|
21
|
+
|
22
|
+
def secure
|
23
|
+
Gliffy::API::Facade.https(self)
|
24
|
+
end
|
25
|
+
|
26
|
+
def get(url, params = {})
|
27
|
+
Gliffy::API::Response.new(Nokogiri.XML(raw(url, params)))
|
28
|
+
end
|
29
|
+
|
30
|
+
def raw(url, params = {})
|
31
|
+
r = token.get(url + '?' + query(params))
|
32
|
+
r.body
|
33
|
+
end
|
34
|
+
|
35
|
+
def post(url, params)
|
36
|
+
r = token.post(url, params)
|
37
|
+
Gliffy::API::Response.new(Nokogiri.XML(r.body))
|
38
|
+
end
|
39
|
+
|
40
|
+
def web(url, params)
|
41
|
+
consumer.create_signed_request(
|
42
|
+
:get,
|
43
|
+
url + '?' + query(params),
|
44
|
+
token
|
45
|
+
).path
|
46
|
+
end
|
47
|
+
|
48
|
+
def account
|
49
|
+
@account ||= load_account
|
50
|
+
end
|
51
|
+
|
52
|
+
def impersonate(user)
|
53
|
+
escaped_id = URI.escape @account_id.to_s
|
54
|
+
escaped_user = URI.escape user
|
55
|
+
|
56
|
+
response = secure.post(
|
57
|
+
"/accounts/#{escaped_id}/users/#{escaped_user}/oauth_token.xml",
|
58
|
+
:action => 'create',
|
59
|
+
:description => application_name
|
60
|
+
)
|
61
|
+
|
62
|
+
token.token = response.string('//g:oauth-token')
|
63
|
+
token.secret = response.string('//g:oauth-token-secret')
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def query(params)
|
69
|
+
params.map {|k, v| "#{CGI.escape k.to_s}=#{CGI.escape v.to_s}" }.join('&')
|
70
|
+
end
|
71
|
+
|
72
|
+
def token
|
73
|
+
@token ||= OAuth::AccessToken.new @consumer
|
74
|
+
end
|
75
|
+
|
76
|
+
def load_account
|
77
|
+
response = plain.get("/accounts/#{account_id}.xml",
|
78
|
+
{ :action => 'get' })
|
79
|
+
Gliffy::Account.load(plain, response)
|
80
|
+
end
|
81
|
+
|
82
|
+
def init_consumer(key, secret)
|
83
|
+
OAuth::Consumer.new(key,
|
84
|
+
secret,
|
85
|
+
:site => Gliffy.web_root,
|
86
|
+
:scheme => :query_string)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Gliffy
|
2
|
+
class API
|
3
|
+
class Facade
|
4
|
+
def self.http(api)
|
5
|
+
Facade.new("http", api)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.https(api)
|
9
|
+
Facade.new("https", api)
|
10
|
+
end
|
11
|
+
|
12
|
+
def raw(partial_url, params = {})
|
13
|
+
@api.raw(api_root + partial_url, params)
|
14
|
+
end
|
15
|
+
|
16
|
+
def get(partial_url, params)
|
17
|
+
@api.get(api_root + partial_url, params)
|
18
|
+
end
|
19
|
+
|
20
|
+
def post(partial_url, params)
|
21
|
+
@api.post(api_root + partial_url, params)
|
22
|
+
end
|
23
|
+
|
24
|
+
def web(partial_url, params)
|
25
|
+
@api.web(web_root + partial_url, params)
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_folders(account_id)
|
29
|
+
get("/accounts/#{account_id}/folders.xml",
|
30
|
+
:action => 'get')
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def initialize(protocol, api)
|
36
|
+
@protocol = protocol
|
37
|
+
@api = api
|
38
|
+
end
|
39
|
+
|
40
|
+
def api_root
|
41
|
+
@protocol + ":" + Gliffy.api_root
|
42
|
+
end
|
43
|
+
|
44
|
+
def web_root
|
45
|
+
@protocol + ":" + Gliffy.web_root
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Gliffy
|
2
|
+
class API::Response
|
3
|
+
def initialize(base)
|
4
|
+
@base = base
|
5
|
+
end
|
6
|
+
|
7
|
+
def content
|
8
|
+
@base.content
|
9
|
+
end
|
10
|
+
|
11
|
+
def nodes(path)
|
12
|
+
@base.xpath(path, namespaces).map { |n| Gliffy::API::Response.new(n) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def node(path)
|
16
|
+
Gliffy::API::Response.new(@base.at_xpath(path, namespaces))
|
17
|
+
end
|
18
|
+
|
19
|
+
def string(path)
|
20
|
+
node(path).content
|
21
|
+
end
|
22
|
+
|
23
|
+
def integer(path)
|
24
|
+
string(path).to_i
|
25
|
+
end
|
26
|
+
|
27
|
+
def timestamp(path)
|
28
|
+
parse_timestamp(string(path))
|
29
|
+
end
|
30
|
+
|
31
|
+
def exists(path)
|
32
|
+
!@base.at_xpath(path, namespaces).nil?
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Gliffy XML namespace to be used in XPath expressions
|
38
|
+
def namespaces
|
39
|
+
{ "g" => "http://www.gliffy.com" }
|
40
|
+
end
|
41
|
+
|
42
|
+
# As opposed to UNIX timestamp, timestamps returned by Gliffy API
|
43
|
+
# are in milliseconds, so we should divide them by 1000 before
|
44
|
+
# converting to real datetime objectsq
|
45
|
+
def parse_timestamp(value)
|
46
|
+
Time.at(value.to_i / 1000).to_datetime
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Gliffy
|
2
|
+
class Document
|
3
|
+
attr_reader :owner
|
4
|
+
attr_reader :id
|
5
|
+
attr_reader :is_public
|
6
|
+
attr_reader :versions
|
7
|
+
attr_reader :name
|
8
|
+
attr_reader :modified, :created, :published
|
9
|
+
|
10
|
+
def self.load(owner, node)
|
11
|
+
@loaded ||= {}
|
12
|
+
|
13
|
+
id = node.integer('@id')
|
14
|
+
if not @loaded.has_key? id then
|
15
|
+
@loaded[id] = Gliffy::Document.new(
|
16
|
+
owner,
|
17
|
+
id,
|
18
|
+
:is_public => node.exists('@is-public'),
|
19
|
+
:versions => node.integer('@num-versions'),
|
20
|
+
:name => node.string('g:name'),
|
21
|
+
:modified => node.timestamp('g:mod-date'),
|
22
|
+
:created => node.timestamp('g:create-date'),
|
23
|
+
:published => node.timestamp('g:published-date')
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
@loaded[id]
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.clear_cache
|
31
|
+
@loaded = {}
|
32
|
+
end
|
33
|
+
|
34
|
+
def editor(return_url, return_text)
|
35
|
+
api.web(
|
36
|
+
"/gliffy/",
|
37
|
+
:launchDiagramId => id,
|
38
|
+
:returnURL => return_url,
|
39
|
+
:returnButtonText => return_text
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
def png
|
44
|
+
Document::PNG.new(self)
|
45
|
+
end
|
46
|
+
|
47
|
+
def public?
|
48
|
+
is_public
|
49
|
+
end
|
50
|
+
|
51
|
+
def api
|
52
|
+
owner.api
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def initialize(owner, id, params)
|
58
|
+
@owner = owner
|
59
|
+
@id = id
|
60
|
+
|
61
|
+
@is_public = params[:is_public]
|
62
|
+
@versions = params[:versions]
|
63
|
+
@name = params[:name]
|
64
|
+
@modified = params[:modified]
|
65
|
+
@created = params[:created]
|
66
|
+
@published = params[:published]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Gliffy
|
2
|
+
class Document::PNG
|
3
|
+
attr_reader :document
|
4
|
+
|
5
|
+
SIZE_THUMBNAIL = "T"
|
6
|
+
SIZE_SMALL = "S"
|
7
|
+
SIZE_MEDIUM = "M"
|
8
|
+
SIZE_FULL = "L"
|
9
|
+
|
10
|
+
def initialize(document)
|
11
|
+
@document = document
|
12
|
+
end
|
13
|
+
|
14
|
+
def content(size)
|
15
|
+
api.raw(
|
16
|
+
"/accounts/#{account_id}/documents/#{document_id}.png",
|
17
|
+
:action => 'get',
|
18
|
+
:size => size
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def thumbnail
|
23
|
+
content(SIZE_THUMBNAIL)
|
24
|
+
end
|
25
|
+
|
26
|
+
def small
|
27
|
+
content(SIZE_SMALL)
|
28
|
+
end
|
29
|
+
|
30
|
+
def medium
|
31
|
+
content(SIZE_MEDIUM)
|
32
|
+
end
|
33
|
+
|
34
|
+
def full
|
35
|
+
content(SIZE_FULL)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def api
|
41
|
+
document.api
|
42
|
+
end
|
43
|
+
|
44
|
+
def account
|
45
|
+
document.owner
|
46
|
+
end
|
47
|
+
|
48
|
+
def document_id
|
49
|
+
document.id
|
50
|
+
end
|
51
|
+
|
52
|
+
def account_id
|
53
|
+
account.id
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Gliffy
|
4
|
+
class Folder
|
5
|
+
attr_reader :name, :path
|
6
|
+
attr_reader :owner, :parent
|
7
|
+
attr_reader :folders
|
8
|
+
|
9
|
+
def self.load(owner, node)
|
10
|
+
Gliffy::Folder.new(
|
11
|
+
owner,
|
12
|
+
node.string('g:name'),
|
13
|
+
node.string('g:path'),
|
14
|
+
node.nodes('g:folder').map {|n| Gliffy::Folder.load(owner, n)}
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(owner, name, path, folders)
|
19
|
+
@owner = owner
|
20
|
+
@name = name
|
21
|
+
@path = path
|
22
|
+
|
23
|
+
@folders = folders
|
24
|
+
@folders.each do |f|
|
25
|
+
f.parent = self
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def parent=(parent)
|
30
|
+
if path != parent.path + "/" + name then
|
31
|
+
raise "Invalid parent"
|
32
|
+
end
|
33
|
+
|
34
|
+
@parent = parent
|
35
|
+
end
|
36
|
+
|
37
|
+
def documents
|
38
|
+
@documents ||= load_documents
|
39
|
+
end
|
40
|
+
|
41
|
+
def root?
|
42
|
+
path == "ROOT"
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def api
|
48
|
+
owner.api
|
49
|
+
end
|
50
|
+
|
51
|
+
def account_id
|
52
|
+
owner.id
|
53
|
+
end
|
54
|
+
|
55
|
+
def escaped_path
|
56
|
+
path.gsub(' ', '+')
|
57
|
+
end
|
58
|
+
|
59
|
+
def load_documents
|
60
|
+
# Path is alphanumeric + spaces and '/'. Spaces should be
|
61
|
+
# escaped; slashes should NOT be escaped.
|
62
|
+
url = "/accounts/#{account_id}/folders/#{escaped_path}/documents.xml"
|
63
|
+
response = api.get(url,
|
64
|
+
:action => "get")
|
65
|
+
|
66
|
+
response
|
67
|
+
.nodes('//g:document')
|
68
|
+
.map { |n| Gliffy::Document.load(owner, n) }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|