fresnel 0.5.4

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.
@@ -0,0 +1,151 @@
1
+ Fresnel
2
+ --------------
3
+
4
+ A console manager to LighthouseApp.com using the official lighthouse api
5
+
6
+ With Fresnel you can browse you complete lighthouse account.
7
+
8
+ Current features :
9
+ ==================
10
+
11
+ - list projects
12
+ - list ticket bins
13
+ - list tickets (also in bins)
14
+ - create tickets
15
+ - assign tickets
16
+ - comment on tickets
17
+ - state changes
18
+
19
+
20
+ How to install
21
+ ==============
22
+
23
+ Use 'rake gem' to build the gem or 'rake install' to build and install the gem.
24
+
25
+ Cache
26
+ =====
27
+
28
+ You can control the cache time in your global config file : ~/.fresnel
29
+
30
+ add a key like :
31
+ cache_timeout: 60
32
+
33
+ This will set the timeout of the cache to 1 minute.
34
+ When fresnel makes changes, the cache will be invalidated automagically.
35
+ We would recommend to use a minimum of 10 seconds.
36
+
37
+ Terminal size
38
+ =============
39
+
40
+ By default we detect your terminal size,
41
+ if that fails it will be set to 80.
42
+ Though if you like to override it you can !
43
+
44
+ in your global config file : ~/.fresnel
45
+ add a key like :
46
+ term_size: 60
47
+
48
+ The ticket overview table will add columns if it fits, in the following order :
49
+
50
+ * ticket number *
51
+ * state *
52
+ * title *
53
+ - assigned to
54
+ - by
55
+ - tags
56
+ - created at
57
+ - updated at
58
+
59
+ items with a * are always shown ;)
60
+
61
+ Getting started
62
+ ===============
63
+
64
+ Once you have Fresnel installed, run 'fresnel help' to see the syntax.
65
+
66
+ Problems, Comments and Suggestions
67
+ ==================================
68
+
69
+ Please post them on https://govannon.lighthouseapp.com/projects/42260-fresnel/
70
+ or mail it to : ticket+govannon.42260-7vwej7yr@lighthouseapp.com
71
+
72
+
73
+
74
+ Examples
75
+ ========
76
+
77
+ fresnel help
78
+
79
+ +-----------------------------------------------------------------------------+
80
+ | Fresnel - A lighthouseapp console manager - help |
81
+ +-----------------------------------------------------------------------------+
82
+ | |
83
+ | Fresnel is a Console App that helps manage Lighthouse (LH). |
84
+ | You can find LH at http://lighthouseapp.com |
85
+ | |
86
+ | fresnel help This screen |
87
+ | fresnel bins Show all ticket bins |
88
+ | fresnel bin <id> Show ticket in bin <id> |
89
+ | fresnel projects Show all projects |
90
+ | fresnel <id> comment Show comments for ticket |
91
+ | fresnel <id> Show ticket details |
92
+ | fresnel <id> assign Assign ticket to user |
93
+ | fresnel <id> claim Assign ticket to self |
94
+ | fresnel <id> online Open browser for ticket |
95
+ | fresnel <id> [open|closed|hold|resolved|invalid] Change ticket state |
96
+ | fresnel tickets Show all tickets |
97
+ | fresnel create Create a ticket |
98
+ | |
99
+ +-----------------------------------------------------------------------------+
100
+ | Created by Narnach & Smeevil - licence : mit |
101
+ +-----------------------------------------------------------------------------+
102
+
103
+ fresnel projects
104
+
105
+ fetching projects...
106
+ +---+--------------+--------+--------------+
107
+ | # | project name | public | open tickets |
108
+ +---+--------------+--------+--------------+
109
+ | 0 | Fresnel | true | 3 |
110
+ | 1 | Rails | true | 123 |
111
+ +---+--------------+--------+--------------+
112
+
113
+
114
+ fresnel tickets
115
+
116
+ Fetching unresolved tickets...
117
+ +----+-------+--------------------------------------------------------+--------+------------------+-------------+----------------+----------------+
118
+ | # | state | title | tags | by | assigned to | created at | updated at |
119
+ +----+-------+--------------------------------------------------------+--------+------------------+-------------+----------------+----------------+
120
+ | 10 | new | Add option to open Hoptoad link in browser | medium | Wes Oldenbeuving | nobody | Today 11:22 | Today 11:23 |
121
+ | 9 | new | cache invalidation | medium | Smeevil | nobody | Today 08:17 | Today 08:18 |
122
+ | 6 | open | would be nice if we could create a new lighthouse p... | low | Smeevil | Smeevil | 02-12-09 22:10 | 02-12-09 22:16 |
123
+ +----+-------+--------------------------------------------------------+--------+------------------+-------------+----------------+----------------+
124
+
125
+ fresnel 6
126
+
127
+ +-----------------------------------------------------------------------------+
128
+ | Ticket #6 : would be nice if we could create a new lighthouse projec... |
129
+ | Date : 02-12-09 22:10 by Smeevil |
130
+ | Tags : low |
131
+ +-----------------------------------------------------------------------------+
132
+ | |
133
+ | |
134
+ +-----------------------------------------------------------------------------+
135
+
136
+ Assignment changed 02-12-09 21:14 => Smeevil by Smeevil
137
+
138
+ +-----------------------------------------------------------------------------+
139
+ | Smeevil 02-12-09 21:24 |
140
+ +-----------------------------------------------------------------------------+
141
+ | |
142
+ | seems its changing state ! |
143
+ | now lets see if we can do it again ! |
144
+ | |
145
+ +-----------------------------------------------------------------------------+
146
+ | Assignment changed => Wes Oldenbeuving |
147
+ +-----------------------------------------------------------------------------+
148
+
149
+ Assignment changed 02-12-09 22:10 => Smeevil by Smeevil
150
+
151
+ Current state : open
@@ -0,0 +1,54 @@
1
+ require "rake"
2
+ require "rake/clean"
3
+ require "rake/gempackagetask"
4
+ require 'rubygems'
5
+
6
+ ################################################################################
7
+ ### Gem
8
+ ################################################################################
9
+
10
+ begin
11
+ # Parse gemspec using the github safety level.
12
+ file = Dir['*.gemspec'].first
13
+ data = File.read(file)
14
+ spec = nil
15
+ # FIXME: Lowered SAFE from 3 to 2 to work with Ruby 1.9 due to rubygems
16
+ # performing a require internally
17
+ Thread.new { spec = eval("$SAFE = 2\n%s" % data)}.join
18
+
19
+ # Create the gem tasks
20
+ Rake::GemPackageTask.new(spec) do |package|
21
+ package.gem_spec = spec
22
+ end
23
+ rescue Exception => e
24
+ printf "WARNING: Error caught (%s): %s\n%s", e.class.name, e.message, e.backtrace[0...5].map {|l| ' %s' % l}.join("\n")
25
+ end
26
+
27
+ desc 'Package and install the gem for the current version'
28
+ task :install => :gem do
29
+ system "sudo gem install -l pkg/%s-%s.gem" % [spec.name, spec.version]
30
+ end
31
+
32
+ desc 'Show files missing from gemspec'
33
+ task :diff do
34
+ files = %w[
35
+ Rakefile
36
+ *README* *readme*
37
+ *LICENSE*
38
+ *.gemspec deps.rip
39
+ bin/*
40
+ lib/**/*
41
+ spec/**/*
42
+ ].map {|pattern| Dir.glob(pattern)}.flatten.select{|f| File.file?(f)}
43
+ missing_files = files - spec.files
44
+ extra_files = spec.files - files
45
+ puts "Missing files:"
46
+ puts missing_files.join(" ")
47
+ puts "Extra files:"
48
+ puts extra_files.join(" ")
49
+ end
50
+
51
+ desc 'Local install the latest gem version'
52
+ task :reinstall do
53
+ system("rm -f pkg/*.gem && rake gem && gem install pkg/*.gem")
54
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ require 'fresnel'
3
+ require 'fresnel/cli'
4
+
5
+ COLOR=false
6
+ @@term_size=`tput cols`.to_i
7
+ @@term_size=80 if @@term_size==0 #if tputs does not exist
8
+ @@debug=false
9
+ @@cache_timeout=1.minute
10
+
11
+ Signal.trap("SIGINT") {
12
+ puts "Stopping..."
13
+ exit(0)
14
+ }
15
+
16
+ system("clear")
17
+ Cli.new(ARGV).run!
@@ -0,0 +1,36 @@
1
+ Gem::Specification.new do |s|
2
+ # Project
3
+ s.name = 'fresnel'
4
+ s.summary = "Fresnel is a console manager to LighthouseApp.com using the official lighthouse api."
5
+ s.description = s.summary
6
+ s.version = '0.5.4'
7
+ s.date = '2009-12-04'
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Gerard de Brieder", "Wes Oldenbeuving"]
10
+ s.email = "smeevil@gmail.com"
11
+ s.homepage = "http://www.github.com/smeevil/fresnel"
12
+
13
+ # Files
14
+ root_files = %w[README.markdown Rakefile fresnel.gemspec]
15
+ bin_files = %w[fresnel]
16
+ fresnel_files = %w[cache cli date_parser frame lighthouse setup_wizard]
17
+ lib_files = %w[fresnel] + fresnel_files.map {|f| "fresnel/#{f}"}
18
+ s.bindir = "bin"
19
+ s.require_path = "lib"
20
+ s.executables = bin_files
21
+ s.test_files = []
22
+ s.files = root_files + s.test_files + bin_files.map {|f| 'bin/%s' % f} + lib_files.map {|f| 'lib/%s.rb' % f}
23
+
24
+ # rdoc
25
+ s.has_rdoc = true
26
+ s.extra_rdoc_files = %w[ README.markdown]
27
+ s.rdoc_options << '--inline-source' << '--line-numbers' << '--main' << 'README.rdoc'
28
+
29
+ # Dependencies
30
+ s.add_dependency 'activesupport', ">= 2.3.0"
31
+ s.add_dependency 'terminal-table', ">= 1.3.0"
32
+ s.add_dependency 'highline', ">= 1.5.1"
33
+
34
+ # Requirements
35
+ s.required_ruby_version = ">= 1.8.0"
36
+ end
@@ -0,0 +1,479 @@
1
+ require 'active_support'
2
+ require 'terminal-table/import'
3
+ require 'highline/import'
4
+
5
+ require "fresnel/lighthouse"
6
+ require "fresnel/date_parser"
7
+ require "fresnel/cache"
8
+ require "fresnel/setup_wizard"
9
+ require "fresnel/frame"
10
+
11
+ HighLine.track_eof = false
12
+
13
+ class String
14
+ def wrap(col = 80)
15
+ self.gsub(/(.{1,#{col}})( +|$\n?)|(.{1,#{col}})/,
16
+ "\\1\\3\n")
17
+ end
18
+
19
+ def truncate(size)
20
+ "#{self.strip[0..size]}#{"..." if self.size>size}"
21
+ end
22
+ end
23
+
24
+ class Fresnel
25
+ attr_reader :global_config_file, :project_config_file, :app_description
26
+ attr_accessor :lighthouse, :current_project_id, :cache, :cache_timeout, :current_user_id
27
+
28
+ def initialize(options=Hash.new)
29
+ @global_config_file="#{ENV['HOME']}/.fresnel"
30
+ @project_config_file=File.expand_path('.fresnel')
31
+ @app_description="A lighthouseapp console manager"
32
+ @lighthouse=Lighthouse
33
+ @cache=Cache.new
34
+ Lighthouse.account, Lighthouse.token, @current_user_id = load_global_config
35
+ @current_project_id=load_project_config
36
+
37
+ end
38
+
39
+ def load_global_config
40
+ if File.exists? self.global_config_file
41
+ config = YAML.load_file(self.global_config_file)
42
+
43
+ @@cache_timeout=config['cache_timeout'] if config.has_key?('cache_timeout')
44
+ @@debug=config['debug'] if config.has_key?('debug')
45
+ @@term_size=config['term_size'] if config.has_key?('term_size')
46
+
47
+ if config && config.class==Hash && config.has_key?('account') && config.has_key?('token') && config.has_key?('user_id')
48
+ return [config['account'], config['token'], config['user_id']]
49
+ else
50
+ puts Frame.new(:header=>"Warning !",:body=>"global config did not validate , recreating")
51
+ SetupWizard.global(self)
52
+ load_global_config
53
+ end
54
+ else
55
+ puts Frame.new(:header=>"Notice",:body=>"global config not found at #{self.global_config_file}, starting wizard")
56
+ SetupWizard.global(self)
57
+ load_global_config
58
+ end
59
+ end
60
+
61
+ def load_project_config
62
+ if File.exists? self.project_config_file
63
+ config = YAML.load_file(self.project_config_file)
64
+ if config && config.class==Hash && config.has_key?('project_id')
65
+ return config['project_id']
66
+ else
67
+ puts Frame.new(:header=>"Warning !",:body=>"project config found but project_id was not declared")
68
+ load_project_config
69
+ end
70
+ else
71
+ puts Frame.new(:header=>"Notice",:body=>"project config not found at #{self.project_config_file}, starting wizard")
72
+ SetupWizard.project(self)
73
+ load_project_config
74
+ end
75
+ end
76
+
77
+ def account
78
+ lighthouse.account
79
+ end
80
+
81
+ def token
82
+ lighthouse.token
83
+ end
84
+
85
+ def ask_for_action(actions_available="")
86
+ if actions_available.present?
87
+ puts actions_available.wrap
88
+ regexp="^(#{actions_available.scan(/\[(.*?)\]/).flatten.join("|")}|[0-9]+)$"
89
+ else
90
+ regexp="^(q|[0-9]+)$"
91
+ end
92
+ ask("Action : ") do |q|
93
+ q.default="q"
94
+ q.validate=/#{regexp}/
95
+ end
96
+ end
97
+
98
+ def create_project
99
+ puts "create project is not implemented yet"
100
+ end
101
+
102
+ def projects(options=Hash.new)
103
+ options[:object]||=false
104
+ system("clear") unless options[:clear]==false || options[:object]
105
+ options[:selectable]||false
106
+ print "Fetching projects..." unless options[:object]
107
+ projects_data=cache.load(:name=>"fresnel_projects",:action=>"Lighthouse::Project.find(:all)")
108
+ puts " [done] - data is #{projects_data.age}s old , max is #{@@cache_timeout}s"
109
+ project_table = table do |t|
110
+ t.headings=[]
111
+ t.headings << '#' if options[:selectable]
112
+ t.headings += ['project name', 'public', 'open tickets']
113
+
114
+ projects_data.each_with_index do |project,i|
115
+ row=Array.new
116
+ row << i if options[:selectable]
117
+ row+=[project.name, project.public, {:value=>project.open_tickets_count, :alignment=>:right}]
118
+ t << row
119
+ end
120
+ end
121
+ if options[:object]
122
+ return projects_data
123
+ else
124
+ puts(project_table)
125
+ unless options[:setup]
126
+ action=ask_for_action("[q]uit, [c]reate or project #")
127
+ case action
128
+ when "c" then create_project
129
+ when /\d+/ then tickets(:project_id=>projects_data[action.to_i].id)
130
+ else
131
+ exit(0)
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ def tickets(options=Hash.new)
138
+ system("clear")
139
+ options[:all] ? print("Fetching all tickets#{" in bin #{options[:bin_name]}" if options[:bin_name].present?}...") : print("Fetching unresolved tickets#{" in bin #{options[:bin_name]}" if options[:bin_name].present?}...")
140
+ STDOUT.flush
141
+ project_id=options[:project_id]||self.current_project_id
142
+ tickets=options[:tickets]||cache.load(:name=>"fresnel_project_#{project_id}_tickets#{"_all" if options[:all]}", :action=>"Lighthouse::Ticket.find(:all, :params=>{:project_id=>#{project_id} #{",:q=>'not-state:closed'" unless options[:all]}})")
143
+ puts " [done] - data is #{tickets.age}s old , max is #{@@cache_timeout}s"
144
+ if tickets.any?
145
+ tickets_table = table do |t|
146
+ prepped_headings=[
147
+ {:value=>'#',:alignment=>:center},
148
+ {:value=>'state',:alignment=>:center},
149
+ {:value=>'title',:alignment=>:center},
150
+ ]
151
+ prepped_headings << {:value=>'assigned to',:alignment=>:center} if @@term_size>=90
152
+ prepped_headings << {:value=>'by',:alignment=>:center} if @@term_size>=105
153
+ prepped_headings << {:value=>'tags',:alignment=>:center} if @@term_size>=120
154
+ prepped_headings << 'created at' if @@term_size>=140
155
+ prepped_headings << 'updated at' if @@term_size>=160
156
+
157
+ t.headings = prepped_headings #must assign the heading at once, else it will b0rk the output
158
+
159
+ tickets.sort_by(&:number).reverse.each do |ticket|
160
+ cols=[
161
+ {:value=>ticket.number, :alignment=>:right},
162
+ {:value=>ticket.state,:alignment=>:center},
163
+ "#{ticket.title.truncate(50)}"
164
+ ]
165
+ cols << (ticket.assigned_user_name.split(" ").first.truncate(10) rescue "nobody") if @@term_size>=90
166
+ cols << ticket.creator_name.split(" ").first.truncate(10) if @@term_size>=105
167
+ cols << (ticket.tag.truncate(9) rescue "") if @@term_size>=120
168
+ cols << {:value=>DateParser.string(ticket.created_at.to_s), :alignment=>:right} if @@term_size>=140
169
+ cols << {:value=>DateParser.string(ticket.updated_at.to_s), :alignment=>:right} if @@term_size>=160
170
+
171
+ t << cols #must assign the cols at once, else it will b0rk the output
172
+ end
173
+ end
174
+ puts tickets_table
175
+ action=ask_for_action("[q]uit, [b]ins, [p]rojects, #{options[:all] ? "[u]nresolved" : "[a]ll"}, [c]reate or ticket #")
176
+ case action
177
+ when "b" then get_bins
178
+ when "c" then create
179
+ when "p" then projects(:selectable=>true)
180
+ when "a" then tickets(:all=>true)
181
+ when "u" then self.tickets
182
+ when /\d+/ then show_ticket(action)
183
+ else
184
+ exit(0)
185
+ end
186
+ else
187
+ puts Frame.new(:header=>"Notice",:body=>"no #{"unresolved " unless options[:all]}tickets #{"in bin #{options[:bin_name]}"}...")
188
+ action=ask_for_action("[q]uit, [b]ins, [p]rojects, [u]nresolved, [a]ll, [c]reate")
189
+ case action
190
+ when "b" then get_bins
191
+ when "c" then create
192
+ when "p" then projects(:selectable=>true)
193
+ when "a" then tickets(:all=>true)
194
+ when "u" then self.tickets
195
+ else
196
+ exit(0)
197
+ end
198
+
199
+ end
200
+
201
+ end
202
+
203
+ def get_bins(project_id=self.current_project_id)
204
+ system("clear")
205
+ print "Fetching ticket bins..."
206
+ STDOUT.flush
207
+ bins=cache.load(:name=>"fresnel_project_#{project_id}_bins",:action=>"Lighthouse::Project.find(#{project_id}).bins")
208
+ puts " [done] - data is #{bins.age}s old , max is #{@@cache_timeout}s"
209
+ bins.reject!{|b|true unless b.user_id==self.current_user_id || b.shared}
210
+ bins_table = table do |t|
211
+ t.headings = ['#', 'bin', 'tickets', 'query']
212
+ bins.each_with_index do |bin,i|
213
+ t << [i, bin.name,{:value=>bin.tickets_count, :alignment=>:right},bin.query]
214
+ end
215
+ end
216
+ puts bins_table
217
+ bin_id=ask_for_action
218
+ if bin_id=="q"
219
+ exit(0)
220
+ else
221
+ puts "Fetching tickets in bin : #{bins[bin_id.to_i].name}"
222
+ tickets(:tickets=>bins[bin_id.to_i].tickets)
223
+ def tickets.age=(seconds)
224
+ @age_in_seconds=seconds
225
+ end
226
+ def tickets.age
227
+ @age_in_seconds
228
+ end
229
+ tickets.age=0
230
+ return tickets
231
+ end
232
+ end
233
+
234
+ def get_tickets_in_bin(bin)
235
+ bins=cache.load(:name=>"fresnel_project_#{self.current_project_id}_bins",:action=>"Lighthouse::Project.find(#{self.current_project_id}).bins")
236
+ bins.reject!{|b|true unless b.user_id==self.current_user_id || b.shared}
237
+ data=bins[bin.to_i].tickets
238
+
239
+ def data.age=(seconds)
240
+ @age_in_seconds=seconds
241
+ end
242
+ def data.age
243
+ @age_in_seconds
244
+ end
245
+ data.age=0
246
+
247
+ tickets(:tickets=>data, :bin_name=>bins[bin.to_i].name)
248
+ end
249
+
250
+ def get_ticket(number)
251
+ cache.load(:name=>"fresnel_ticket_#{number}",:action=>"Lighthouse::Ticket.find(#{number}, :params => { :project_id => #{self.current_project_id} })")
252
+ end
253
+
254
+ def get_project(project_id=self.current_project_id)
255
+ cache.load(:name=>"fresnel_project_#{project_id}",:action=>"Lighthouse::Project.find(#{project_id})")
256
+ end
257
+
258
+ def get_project_members(project_id=self.current_project_id)
259
+ cache.load(:name=>"fresnel_project_#{project_id}_members", :action=>"Lighthouse::Project.find(#{project_id}).memberships", :timeout=>1.day)
260
+ end
261
+
262
+ def show_ticket(number)
263
+ system("clear")
264
+ ticket = get_ticket(number)
265
+ puts Frame.new(
266
+ :header=>[
267
+ "Ticket ##{number} : #{ticket.title.chomp.truncate(55)}",
268
+ "Date : #{DateParser.string(ticket.created_at.to_s)} by #{ticket.creator_name}",
269
+ "Tags : #{ticket.tag}"
270
+ ],
271
+ :body=>ticket.versions.first.body
272
+ )
273
+ ticket.versions.each_with_index do |v,i|
274
+ next if i==0
275
+ if v.respond_to?(:diffable_attributes) && v.body.nil?
276
+ puts "\nState changed #{DateParser.string(v.created_at.to_s)} from #{v.diffable_attributes.state} => #{v.state} by #{v.user_name}\n\n" if v.diffable_attributes.respond_to?(:state)
277
+ puts "\nAssignment changed #{DateParser.string(v.created_at.to_s)} => #{v.assigned_user_name rescue "Nobody"} by #{v.user_name}\n\n" if v.diffable_attributes.respond_to?(:assigned_user)
278
+ else
279
+ user_date=v.user_name.capitalize
280
+ date=DateParser.string(v.created_at.to_s)
281
+ user_date=user_date.ljust((@@term_size-5)-date.size)
282
+ user_date+=date
283
+
284
+ footer=Array.new
285
+ footer<<"State changed from #{v.diffable_attributes.state} => #{v.state}" if v.diffable_attributes.respond_to?(:state)
286
+ footer<<"Assignment changed => #{v.assigned_user_name}" if v.diffable_attributes.respond_to?(:assigned_user)
287
+
288
+ puts Frame.new(:header=>user_date,:body=>v.body,:footer=>footer)
289
+ end
290
+ end
291
+ puts "Current state : #{ticket.versions.last.state}"
292
+ action=ask_for_action("[q]uit, [t]ickets, [b]ins, [c]omment, [a]ssign, [r]esolve, [s]elf, [o]pen, [h]old, [w]eb, [l]inks")
293
+ case action
294
+ when "t" then tickets
295
+ when "b" then get_bins
296
+ when "c" then comment(number)
297
+ when "a" then assign(:ticket=>number)
298
+ when "r" then change_state(:ticket=>number,:state=>"resolved")
299
+ when "s" then claim(:ticket=>number)
300
+ when "o" then change_state(:ticket=>number,:state=>"open")
301
+ when "h" then change_state(:ticket=>number,:state=>"hold")
302
+ when "w" then open_browser_for_ticket(number)
303
+ when "l" then links(number)
304
+ else
305
+ exit(0)
306
+ end
307
+ end
308
+
309
+ def links(number)
310
+ ticket = get_ticket(number)
311
+ links = ticket.versions.map{ |version|
312
+ version.body.to_s.scan(/(http|https)(:\/\/)([a-zA-Z0-9.\/_-]+)| (www\.[a-zA-Z0-9.\/_-]+)/).map{ |url|
313
+ url.join
314
+ }
315
+ }.flatten.uniq
316
+ if links.size == 0
317
+ puts "No links found"
318
+ sleep 1
319
+ elsif links.size == 1
320
+ url = links.first
321
+ url="http://#{url}" unless url=~/^http/
322
+ `open '#{url}'`
323
+ else
324
+ link_table=table do |t|
325
+ t.headings=['#','link']
326
+ links.each_with_index{|link,i|t << [i,link]}
327
+ end
328
+ puts link_table
329
+ pick=ask("open link # : ", Integer) do |q|
330
+ q.below=links.size
331
+ q.above=-1
332
+ end
333
+ url=links[pick]
334
+ url="http://#{url}" unless url=~/^http/
335
+ `open '#{url}'`
336
+ end
337
+ show_ticket(number)
338
+ end
339
+
340
+ def comment(number,state=nil)
341
+ puts "create comment for #{number}"
342
+ ticket=get_ticket(number)
343
+ File.open("/tmp/fresnel_ticket_#{number}_comment", "w+") do |f|
344
+ f.puts "# Please enter the comment for this ticket. Lines starting"
345
+ f.puts "# with '#' will be ignored, and an empty message aborts the commit."
346
+ `echo "q" | fresnel #{number}`.each{ |l| f.write "# #{l}" }
347
+ end
348
+ system("mate -w /tmp/fresnel_ticket_#{number}_comment")
349
+ body=Array.new
350
+ File.read("/tmp/fresnel_ticket_#{number}_comment").each do |l|
351
+ body << l unless l=~/^#/
352
+ end
353
+
354
+ body=body.to_s
355
+ if body.blank?
356
+ puts Frame.new(:header=>"Warning !", :body=>"Aborting comment because it was blank !")
357
+ else
358
+ ticket.body=body
359
+ ticket.state=state unless state.nil?
360
+ if ticket.save
361
+ cache.clear(:name=>"fresnel_ticket_#{number}")
362
+ show_ticket(number)
363
+ else
364
+ puts "something went wrong"
365
+ puts $!
366
+ end
367
+ end
368
+ end
369
+
370
+ def create
371
+ system("mate -w /tmp/fresnel_new_ticket")
372
+ if File.exists?("/tmp/fresnel_new_ticket")
373
+ data=File.read("/tmp/fresnel_new_ticket")
374
+ body=Array.new
375
+ title=""
376
+ if data.blank?
377
+ puts Frame.new(:header=>"Warning !", :body=>"Aborting creation because the ticket was blank !")
378
+ else
379
+ data.each do |l|
380
+ if title.blank?
381
+ title=l
382
+ next
383
+ end
384
+ body << l
385
+ end
386
+ body=body.to_s
387
+ tags=ask("Tags : ")
388
+ tags=tags.split(" ")
389
+ end
390
+ puts "creating ticket..."
391
+ ticket = Lighthouse::Ticket.new(
392
+ :project_id=>self.current_project_id,
393
+ :title=>title,
394
+ :body=>body
395
+ )
396
+ ticket.tags=tags
397
+ if ticket.save
398
+ File.delete("/tmp/fresnel_new_ticket")
399
+ show_ticket(ticket.number)
400
+ else
401
+ puts "something went wrong !"
402
+ puts $!
403
+ end
404
+ else
405
+ puts Frame.new(:header=>"Warning !", :body=>"Aborting creation because the ticket was blank !")
406
+ end
407
+ end
408
+
409
+ def change_state(options)
410
+ puts "should change state to #{options.inspect}"
411
+ ticket=get_ticket(options[:ticket])
412
+ old_state=ticket.state
413
+ options[:state]="resolved" if options[:state]=~/closed?/
414
+ if options[:state]=~/resolved|invalid/
415
+ comment(options[:ticket],options[:state])
416
+ else
417
+ ticket.state=options[:state]
418
+ ticket.assigned_user_id=options[:user_id] if options[:user_id].present?
419
+ if ticket.save
420
+ cache.clear(:name=>"fresnel_ticket_#{options[:ticket]}")
421
+ if options[:user_id].present?
422
+ user_name = Lighthouse::User.find(options[:user_id]).name
423
+ puts Frame.new(:header=>"Success",:body=>"State has changed from #{old_state} to #{options[:state]} #{"and is reassigned to #{user_name}"}")
424
+ end
425
+ show_ticket(options[:ticket])
426
+ else
427
+ puts Frame.new(:header=>"Error !",:body=>"Something went wrong ! #{$!}")
428
+ end
429
+ end
430
+ end
431
+
432
+ def open_browser_for_ticket(number)
433
+ #fast
434
+ #`open "https://#{self.account}.lighthouseapp.com/projects/#{self.current_project_id}/tickets/#{number}"`
435
+ #or save
436
+ puts "opening ticket #{number}in browser"
437
+ `open "#{get_ticket(number).url}"`
438
+ show_ticket(number)
439
+ end
440
+
441
+ def assign(options)
442
+ unless options[:user_id]
443
+ puts "should assign ticket #{options[:ticket]} to someone :"
444
+ members=get_project_members
445
+ members_table = table do |t|
446
+ t.headings = ['#', 'user_id', 'username']
447
+ members.each_with_index do |member,i|
448
+ t << [i, member.user.id, member.user.name]
449
+ end
450
+ end
451
+ puts members_table
452
+ pick=ask("Assign to # : ",Integer) do |q|
453
+ q.above=-1
454
+ q.below=members.count
455
+ end
456
+ options[:user_id]=members[pick].user.id
457
+ end
458
+ ticket=get_ticket(options[:ticket])
459
+ ticket.assigned_user_id=options[:user_id]
460
+ if ticket.save
461
+ cache.clear(:name=>"fresnel_ticket_#{options[:ticket]}")
462
+ user_name = Lighthouse::User.find(options[:user_id]).name
463
+ puts Frame.new(:header=>"Success",:body=>"Reassigned ticket ##{options[:ticket]} to #{user_name} !")
464
+ else
465
+ puts Frame.new(:header=>"Error",:body=>"assigning failed !")
466
+ end
467
+ end
468
+
469
+ def claim(options)
470
+ puts "current user is : #{Lighthouse::User.find(self.current_user_id).name}"
471
+ ticket=get_ticket(options[:ticket])
472
+ if ticket.state=="new"
473
+ change_state(:ticket=>options[:ticket], :state=>"open", :user_id=>self.current_user_id) #get around the cache ...
474
+ else
475
+ assign(:ticket=>options[:ticket],:user_id=>self.current_user_id)
476
+ end
477
+
478
+ end
479
+ end