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 ADDED
@@ -0,0 +1,52 @@
1
+ <em>The Gliffy API is currently in private beta. This will be a Ruby implementation to access your Gliffy account.</em>
2
+
3
+ This consists of a Ruby client for Gliffy as well as a simple command line tool to access your Gliffy account using that client.
4
+
5
+ == Client Library
6
+
7
+ Main entry point is Gliffy::Handle, though you should look at Gliffy::Config for configuration options. Here's an example of getting the list of diagrams and then downloading the first one as a JPEG
8
+
9
+ require 'gliffy'
10
+
11
+ Gliffy::Config.config.api_key='aa9d0e8d-449b-44ad-904a-bf87deb7c403'
12
+ Gliffy::Config.config.secret_key='a52585f7-5952-4229-8bbe-b5787521b571'
13
+ Gliffy::Config.config.account_name='Initech'
14
+
15
+ handle = Gliffy::Handle.new('lumberg@initech.com')
16
+
17
+ diagrams = handle.get_diagrams
18
+ diagram_name_safe = diagrams[0].name.gsub(/ /,'_')
19
+ handle.get_diagram_as_image(diagrams[0].id,:mime_type => :jpeg,:file=>"#{diagram_name_safe}.jpg")
20
+
21
+ The Gliffy::Handle is a <b>user-session based connection</b> to Gliffy. You shoudl therefore engineer your application to keep one of these per user who will access Gliffy and *not* make a global instance.
22
+
23
+ * RDoc is available a http://davetron5000.github.com/gliffy
24
+ * Source is available at http://github.com/davetron5000/gliffy/tree/master
25
+
26
+ == Command Line Client
27
+
28
+ +gliffy+ +help+ shows a list of commands. Currently, there are:
29
+
30
+ [+delete+] Delete a diagram (also: del,rm)
31
+ [+edit+] Edit a diagram
32
+ [+get+] Download a diagram as an image to a file
33
+ [+help+] Show commands
34
+ [+list+] List all diagrams in the account (also: ls)
35
+ [+new+] Create a new diagram
36
+ [+url+] Get the URL for an image
37
+
38
+ === Configuration
39
+
40
+ The first time you run the client, it will create a base configuration in your home directory called <tt>.gliffyrc</tt>, and will ask you to provide it with four pieces of information:
41
+
42
+ [API Key] This is the API Key given to you by Gliffy
43
+ [Secret Key] This is the Secret Key given to you by Gliffy. <b>Do not check this value into source control</b>
44
+ [Account Name] Your account's name. This should've been provided with your API Key
45
+ [Username] The username to work under. The command line client only allows single-user access to your Gliffy account. If you just signed up for Gliffy, this is probably the email address you used to sign up
46
+
47
+ If you are not on OS X, you will want to configure two other options, and you can do this by editing your <tt>.gliffyrc</tt> directly:
48
+
49
+ [<tt>open_image</tt>] Configures the command for opening an image from the command line. This should be a string of the form <tt>command %s</tt> where <tt>%s</tt> will be replaced with the name of the image file to open.
50
+ [<tt>open_url</tt>] Configures the command for opening an url from the command line. This should be a string of the form <tt>command %s</tt> where <tt>%s</tt> will be replaced with the name of the url to open.
51
+
52
+ The other options are all documented in Gliffy::Config. Note that your most previously received user token is stored here as well, to keep from getting it every time the client is run.
data/bin/gliffy ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ $: << File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $: << File.expand_path(File.dirname(__FILE__) + '/../ext')
5
+
6
+ require 'gliffy/cli'
7
+
8
+ Gliffy::Command.execute(ARGV)
@@ -0,0 +1,10 @@
1
+
2
+ class Array
3
+
4
+ def success?; @success; end
5
+ def success=(success); @success = success; end
6
+
7
+ def not_modified?; @not_modified; end
8
+ def not_modified=(not_modified); @not_modified = not_modified; end
9
+
10
+ end
data/lib/gliffy.rb ADDED
@@ -0,0 +1 @@
1
+ require 'gliffy/gliffy'
@@ -0,0 +1,55 @@
1
+ require 'rexml/document'
2
+ require 'array_has_response'
3
+ require 'gliffy/rest'
4
+
5
+ include REXML
6
+
7
+ module Gliffy
8
+
9
+ # Represents on account
10
+ class Account < Response
11
+
12
+ attr_reader :name
13
+ attr_reader :id
14
+ # Either :basic or :premium
15
+ attr_reader :type
16
+ attr_reader :max_users
17
+ # A Time representing the date on which this account expires
18
+ attr_reader :expiration_date
19
+
20
+ attr_reader :users
21
+
22
+ def self.from_xml(element)
23
+ id = element.attributes['id'].to_i
24
+ type = element.attributes['account-type']
25
+ if type == 'Basic'
26
+ type = :basic
27
+ elsif type == 'Premium'
28
+ type = :premium
29
+ else
30
+ raise "Unknown type #{type}"
31
+ end
32
+ max_users = element.attributes['max-users'].to_i
33
+ expiration_date = Time.at(element.elements['expiration-date'].text.to_i / 1000)
34
+ name = element.elements['name'].text
35
+ users = Users.from_xml(element.elements['users'])
36
+
37
+ Account.new(id,type,name,max_users,expiration_date,users)
38
+ end
39
+
40
+ protected
41
+
42
+ def initialize(id,type,name,max_users,expiration_date,users=nil)
43
+ super()
44
+ @id = id
45
+ @type = type
46
+ @name = name
47
+ @max_users = max_users
48
+ @expiration_date = expiration_date
49
+ @users = users
50
+ end
51
+
52
+ end
53
+
54
+ class Accounts < ArrayResponseParser; end
55
+ end
data/lib/gliffy/cli.rb ADDED
@@ -0,0 +1,260 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ require 'fileutils'
3
+ require 'yaml'
4
+ require 'gliffy'
5
+ require 'logger'
6
+
7
+ module Gliffy
8
+ extend self
9
+
10
+ # A command line option to the gliffy command line client
11
+ class Command
12
+
13
+ # Global flags
14
+ GLOBAL_FLAGS = {
15
+ '-v' => 'be more verbose (overrides config)',
16
+ }
17
+
18
+ # Global access to all configured commands
19
+ def self.commands
20
+ @@commands ||= {}
21
+ end
22
+
23
+ def self.aliases
24
+ @@aliases ||= {}
25
+ end
26
+
27
+ # The name of the command
28
+ attr_reader :name
29
+ # a short description
30
+ attr_reader :description
31
+ # a usage statement
32
+ attr_reader :usage
33
+
34
+ # Create a new command
35
+ #
36
+ # [+name+] the name of the command (should be short, no spaces)
37
+ # [+description+] short description of the command
38
+ # [+offline+] true if this command doesn't require a connection to Gliffy
39
+ # [+usage+] usage statement
40
+ # [+block+] A block that represents the command itself. This block will take the gliffy handle and the args array as arguments *or* just the arguments if it is an "offline"
41
+ # command
42
+ def initialize(name,description,offline,usage,block)
43
+ @name = name
44
+ @description = description
45
+ @block = block
46
+ @offline = offline
47
+ @usage = usage ? usage : ""
48
+ end
49
+
50
+ # Runs the command with the given arguments
51
+ def run(args)
52
+ if (@offline)
53
+ @block.call(args)
54
+ else
55
+ previous_token = CLIConfig.instance.config[:current_token]
56
+ handle = Gliffy::Handle.new(CLIConfig.instance.config[:username],previous_token)
57
+ @block.call(handle,args)
58
+ CLIConfig.instance.config[:current_token] = handle.current_token
59
+ CLIConfig.instance.save
60
+ end
61
+ end
62
+
63
+ # Executes the command line that was given
64
+ def self.execute(argv)
65
+ globals = Hash.new
66
+ command = argv.shift
67
+ while !command.nil? && (command =~ /^-/) && !argv.empty?
68
+ globals[command] = true
69
+ command = argv.shift
70
+ end
71
+ Gliffy::Config.config.log_level = Logger::DEBUG if globals['-v']
72
+ cmd = Gliffy::Command.commands[command.to_sym]
73
+ if cmd
74
+ cmd.run argv
75
+ else
76
+ puts "Unknown command #{command}"
77
+ Gliffy::Command.commands[:help].run []
78
+ end
79
+ end
80
+ end
81
+
82
+ # Represents the configuration for the command line client.
83
+ # This is a singleton
84
+ class CLIConfig
85
+
86
+ @@instance = nil
87
+
88
+ # Access to the singleton
89
+ def self.instance
90
+ @@instance = CLIConfig.new if !@@instance
91
+ @@instance
92
+ end
93
+
94
+ # Returns the config hash
95
+ attr_reader :config
96
+
97
+ # Loads the user's rc file if it exists, creating it if not
98
+ def load
99
+ if !File.exist?(@config_file_name)
100
+ puts "#{@config_file_name} not found. Create? [y/n]"
101
+ answer = STDIN.gets
102
+ if answer =~ /^[Yy]/
103
+ save
104
+ else
105
+ puts "Aborting..."
106
+ return false
107
+ end
108
+ end
109
+ yaml_config = File.open(@config_file_name) { |file| YAML::load(file) }
110
+ if (yaml_config)
111
+ @config = yaml_config
112
+ end
113
+ @config[:open_url] = default_open_url if !@config[:open_url]
114
+ @config[:open_image] = default_open_image if !@config[:open_image]
115
+ read_config_from_user('API Key',:api_key)
116
+ read_config_from_user('Secret Key',:secret_key)
117
+ read_config_from_user('Account Name',:account_name)
118
+ read_config_from_user('Username',:username)
119
+ save
120
+ config = Gliffy::Config.config
121
+ @config.each() do |key,value|
122
+ method = key.to_s + "="
123
+ if config.respond_to? method.to_sym
124
+ if (method == 'log_device=')
125
+ if (value == 'STDERR')
126
+ config.log_device = STDERR
127
+ elsif (value == 'STDOUT')
128
+ config.log_device = STDOUT
129
+ else
130
+ config.log_device = value
131
+ end
132
+ else
133
+ config.send(method.to_sym,value)
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+ # Saves the configration to the user's config file name as YAML
140
+ def save
141
+ fp = File.open(@config_file_name,'w') { |out| YAML::dump(@config,out) }
142
+ end
143
+
144
+ private
145
+ def read_config_from_user(name,symbol)
146
+ if (!@config[symbol])
147
+ puts "No #{name} configured. Enter #{name}"
148
+ @config[symbol] = STDIN.gets.chomp!
149
+ end
150
+ end
151
+
152
+ def initialize
153
+ @config_file_name = File.expand_path("~/.gliffyrc")
154
+ @config = {
155
+ :log_level => Gliffy::Config.config.log_level,
156
+ :log_device => Gliffy::Config.config.log_device.to_s,
157
+ :gliffy_app_root => Gliffy::Config.config.gliffy_app_root,
158
+ :gliffy_rest_context => Gliffy::Config.config.gliffy_rest_context,
159
+ :protocol => Gliffy::Config.config.protocol,
160
+ }
161
+ end
162
+
163
+ def default_open_url
164
+ # Cheesy defaults
165
+ if RUBY_PLATFORM =~ /win32/
166
+ # not sure, acutally
167
+ nil
168
+ elsif RUBY_PLATFORM =~ /linux/
169
+ 'firefox "%s"'
170
+ elsif RUBY_PLATFORM =~ /darwin/
171
+ 'open "%s"'
172
+ else
173
+ nil
174
+ end
175
+ end
176
+
177
+ def default_open_image
178
+ # Cheesy defaults
179
+ if RUBY_PLATFORM =~ /win32/
180
+ # not sure, acutally
181
+ nil
182
+ elsif RUBY_PLATFORM =~ /linux/
183
+ # not sure
184
+ nil
185
+ elsif RUBY_PLATFORM =~ /darwin/
186
+ 'open "%s"'
187
+ else
188
+ nil
189
+ end
190
+ end
191
+
192
+
193
+ end
194
+
195
+ # For defining commands, this specifies the description of the next command defined
196
+ def desc(description)
197
+ @next_desc = description
198
+ end
199
+
200
+ # For defining commands, this specifies if the next defined command is "offline"
201
+ def offline(bool)
202
+ @next_offline = bool
203
+ end
204
+
205
+ # For defining commands, specifies the usage statement of the next command
206
+ def usage(usage='')
207
+ @next_usage = usage
208
+ end
209
+
210
+ # defines a command. The only options supported is ":aliases" which is an array of aliases
211
+ # for the command
212
+ def command(name,options={},&block)
213
+ Command.commands[name] = Command.new(name,@next_desc,@next_offline,@next_usage,block)
214
+ if options[:aliases]
215
+ options[:aliases].each() do |a|
216
+ Command.commands[a] = Command.commands[name]
217
+ Command.aliases[a] = true
218
+ end
219
+ end
220
+ @next_usage = nil
221
+ @next_offline = false
222
+ @next_desc = nil
223
+ end
224
+
225
+ def parse_options(args)
226
+ options = Hash.new
227
+ return options if !args
228
+ i = 0
229
+ while i < args.length
230
+ inc = 2
231
+ if args[i] =~ /^-/
232
+ arg = args[i].gsub(/^-/,'')
233
+ if !args[i+1] || (args[i+1] =~ /^-/)
234
+ inc = 1
235
+ options[arg] = true
236
+ else
237
+ options[arg] = args[i+1]
238
+ end
239
+ else
240
+ break
241
+ end
242
+ i += inc
243
+ end
244
+ i.times { args.shift }
245
+ options
246
+ end
247
+
248
+ def format_date(date)
249
+ date.strftime('%m/%d/%y %H:%M')
250
+ end
251
+
252
+ end
253
+
254
+ include Gliffy
255
+ require 'gliffy/commands/commands'
256
+ if !CLIConfig.instance.load
257
+ exit -1
258
+ end
259
+
260
+
@@ -0,0 +1,149 @@
1
+ desc 'List all diagrams in the account'
2
+ usage <<eos
3
+ [-l]
4
+
5
+ -l - show all information
6
+ eos
7
+ command :list, :aliases => [:ls] do |gliffy,args|
8
+ diagrams = gliffy.get_diagrams
9
+ options = parse_options(args)
10
+ if options['l']
11
+ max = diagrams.inject(0) { |max,diagram| diagram.name.length > max ? diagram.name.length : max }
12
+ diagrams.sort.each do |diagram|
13
+ printf "%8d %s %-#{max}s %-3d %s %s %s\n",
14
+ diagram.id,
15
+ diagram.is_public? ? "P" : "-",
16
+ diagram.name,
17
+ diagram.num_versions,
18
+ format_date(diagram.create_date),
19
+ format_date(diagram.mod_date),
20
+ diagram.owner_username
21
+ end
22
+ else
23
+ printf_string = "%d %s\n"
24
+ diagrams.sort.each { |diagram| printf printf_string,diagram.id,diagram.name }
25
+ end
26
+ end
27
+
28
+ desc 'Delete a diagram'
29
+ usage 'diagram_id'
30
+ command :delete, :aliases => [:del,:rm] do |gliffy,args|
31
+ gliffy.delete_diagram(args[0])
32
+ end
33
+
34
+ desc 'Get the URL for an image'
35
+ usage <<eos
36
+ [-o] diagram_id
37
+
38
+ -o - open diagram's URL with configured :open_url command
39
+ eos
40
+ command :url do |gliffy,args|
41
+ open = args[0] == '-o'
42
+ args.shift if open
43
+ url = gliffy.get_diagram_as_url(args[0])
44
+ if open
45
+ if CLIConfig.instance.config[:open_image]
46
+ system(sprintf(CLIConfig.instance.config[:open_image],url))
47
+ else
48
+ puts "Nothing configured for #{:open_image.to_s} to open the image"
49
+ puts url
50
+ end
51
+ else
52
+ puts url
53
+ end
54
+ end
55
+
56
+ desc 'Download a diagram as an image to a file'
57
+ usage <<eos
58
+ [-v version_num] [-f filename] [-t image_type] diagram_id
59
+
60
+ image_type can be :jpeg, :png, :svg, or :xml
61
+ eos
62
+ command :get do |gliffy,args|
63
+
64
+ options = parse_options(args)
65
+ diagram_id = args.shift
66
+
67
+ version_number = options['v'].to_i if options['v']
68
+ filename = options['f'] if options['f']
69
+ type = options['t'].to_sym if options['t']
70
+ type = :jpeg if !type
71
+ if !filename
72
+ if version_number
73
+ filename = "#{diagram_id}_v#{version_number}.#{type.to_s}"
74
+ else
75
+ filename = "#{diagram_id}.#{type.to_s}"
76
+ end
77
+ end
78
+
79
+ get_options = { :mime_type => type, :file => filename }
80
+ get_options[:version] = version_number if version_number
81
+ gliffy.get_diagram_as_image(diagram_id,get_options)
82
+ puts filename
83
+ end
84
+
85
+ desc 'Edit a diagram'
86
+ usage 'diagram_id'
87
+ command :edit do |gliffy,args|
88
+ return_link = gliffy.get_diagram_as_url(args[0])
89
+ link = gliffy.get_edit_diagram_link(args[0],return_link,"Done")
90
+ if CLIConfig.instance.config[:open_url]
91
+ system(sprintf(CLIConfig.instance.config[:open_url],link.full_url))
92
+ else
93
+ puts "Nothing configured for #{:open_url.to_s} to open the url"
94
+ puts link.full_url
95
+ end
96
+ end
97
+
98
+ desc 'Create a new diagram'
99
+ usage <<eos
100
+ [-e] diagram_name
101
+
102
+ -e edit the diagram after creating it
103
+ eos
104
+ command :new do |gliffy,args|
105
+ edit = args[0] == '-e'
106
+ args.shift if edit
107
+ diagram = gliffy.create_diagram(args[0])
108
+ if edit
109
+ Gliffy::Command.commands[:edit].run [diagram.id]
110
+ else
111
+ puts "#{diagram.name} created with id #{diagram.id}"
112
+ end
113
+ end
114
+
115
+ desc 'Show commands'
116
+ offline true
117
+ usage 'command'
118
+ command :help do |args|
119
+ if args[0]
120
+ command = Command.commands[args[0].to_sym]
121
+ if command
122
+ printf "%s - %s\n",args[0],command.description
123
+ printf "usage: %s %s\n",args[0],command.usage
124
+ else
125
+ puts "No such command #{args[0]}"
126
+ end
127
+ else
128
+ command_string = " %-6s %s %s\n"
129
+ puts 'usage: gliffy [global_options] command [command_options]'
130
+ puts 'global_options:'
131
+ Command::GLOBAL_FLAGS.keys.sort.each do |flag|
132
+ printf command_string,flag,Command::GLOBAL_FLAGS[flag],''
133
+ end
134
+ puts
135
+ puts 'command:'
136
+ command_names = Command.commands.keys.sort { |a,b| a.to_s <=> b.to_s }
137
+ command_names.each() do |name|
138
+ command = Command.commands[name]
139
+ if !Command.aliases[name]
140
+ aliases = Array.new
141
+ Command.aliases.keys.each() do |a|
142
+ aliases << a if Command.commands[a] == command
143
+ end
144
+ alias_string = '(also: ' + aliases.join(',') + ')' if aliases.length > 0
145
+ printf command_string,name.to_s, command.description,alias_string
146
+ end
147
+ end
148
+ end
149
+ end