fresnel 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,72 @@
1
+ class Cache
2
+ attr_accessor :active, :timeout
3
+
4
+ def initialize(options=Hash.new)
5
+ @active=options[:active]||true
6
+ end
7
+ def timeout
8
+ @@cache_timeout
9
+ end
10
+ def log(str)
11
+ if @@debug
12
+ puts str
13
+ end
14
+ end
15
+
16
+ def create(options, &block)
17
+ log "eval #{options[:action]}"
18
+ if block
19
+ data = block.call
20
+ elsif options[:action]
21
+ data=eval(options[:action])
22
+ else
23
+ raise ArgumentError, "No block or code to eval for a cache-able value"
24
+ end
25
+ log "creating cache file #{options[:name]}..."
26
+ File.open("/tmp/fresnel_#{options[:name]}.yml",'w+'){ |f| f.write(YAML::dump(data)) }
27
+ def data.age=(seconds)
28
+ @age_in_seconds=seconds
29
+ end
30
+ def data.age
31
+ @age_in_seconds
32
+ end
33
+ data.age=0
34
+ return data
35
+ end
36
+
37
+ def load(options, &block)
38
+ if self.active
39
+ cache_timeout=options[:timeout]||@@cache_timeout
40
+ log "caching is active !"
41
+ if File.exists?("/tmp/fresnel_#{options[:name]}.yml")
42
+ created_at=File.mtime("/tmp/fresnel_#{options[:name]}.yml")
43
+ if (Time.now-created_at) < cache_timeout
44
+ log "returning cached info (age : #{(Time.now-created_at).round}, timeout : #{cache_timeout})"
45
+ data=YAML::load_file("/tmp/fresnel_#{options[:name]}.yml")
46
+ def data.age=(seconds)
47
+ @age_in_seconds=seconds
48
+ end
49
+ def data.age
50
+ @age_in_seconds
51
+ end
52
+ data.age=(Time.now-created_at).round
53
+ return data
54
+ else
55
+ log "refreshing data because the cached is older then the timeout (age : #{(Time.now-created_at).round}, timeout : #{cache_timeout})"
56
+ self.create(options, &block)
57
+ end
58
+ else
59
+ log "no cache data found, calling create..."
60
+ self.create(options, &block)
61
+ end
62
+ else
63
+ log "cache disabled, fetching life data (and creating cache file for future use...once cache is enabled)"
64
+ self.create(options, &block)
65
+ end
66
+ end
67
+
68
+ def clear(options)
69
+ log "clearing cache /tmp/fresnel_#{options[:name]}.yml"
70
+ File.delete("/tmp/fresnel_#{options[:name]}.yml") if File.exists?("/tmp/fresnel_#{options[:name]}.yml")
71
+ end
72
+ end
@@ -0,0 +1,87 @@
1
+ class Cli
2
+ def initialize(argv)
3
+ @argv = argv
4
+ @fresnel = Fresnel.new
5
+ end
6
+
7
+ def run!
8
+ case @argv[0]
9
+ when "projects"
10
+ @fresnel.projects
11
+ when "tickets"
12
+ @fresnel.tickets
13
+ when "bins"
14
+ @fresnel.get_bins
15
+ when "bin"
16
+ @fresnel.get_tickets_in_bin(ARGV[1])
17
+ when "create"
18
+ @fresnel.create
19
+ when "help"
20
+ help
21
+ when /\d+/
22
+ if @argv[1]
23
+ case @argv[1]
24
+ when "comment"
25
+ @fresnel.comment(@argv[0])
26
+ when /^(open|closed?|hold|resolved|invalid)$/
27
+ @fresnel.change_state(:ticket=>@argv[0],:state=>@argv[1])
28
+ when "online"
29
+ @fresnel.open_browser_for_ticket(@argv[0])
30
+ when "assign"
31
+ @fresnel.assign(:ticket=>@argv[0])
32
+ when "claim"
33
+ @fresnel.claim(:ticket=>@argv[0])
34
+ when "links"
35
+ @fresnel.links(@argv[0])
36
+ else
37
+ puts Frame.new(:header=>"Notice",:body=>"not sure what to do for #{@argv[1]}")
38
+ end
39
+ else
40
+ @fresnel.show_ticket(@argv[0])
41
+ end
42
+ else
43
+ @fresnel.tickets
44
+ #puts Frame.new(:header=>"Notice",:body=>"not sure what to do for #{@argv[0]}")
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def help
51
+ help = {
52
+ 'projects' => 'Show all projects',
53
+ 'tickets' => 'Show all tickets',
54
+ 'bins' => 'Show all ticket bins',
55
+ 'bin <id>' => 'Show ticket in bin <id>',
56
+ 'create' => 'Create a ticket',
57
+ 'help' => 'This screen',
58
+ '<id>' => {
59
+ '' => 'Show ticket details',
60
+ 'comment' => 'Show comments for ticket',
61
+ '[open|closed|hold|resolved|invalid]' => 'Change ticket state',
62
+ 'online' => 'Open browser for ticket',
63
+ 'assign' => 'Assign ticket to user',
64
+ 'claim' => 'Assign ticket to self',
65
+ 'links' => 'Extract all links from the ticket and its comment and open one in your browser.'
66
+ }
67
+ }
68
+ help_lines = []
69
+ help.each {|k,v|
70
+ if v.kind_of?(Hash)
71
+ v.each do |hk, hv|
72
+ help_lines << [(k + ' ' + hk), hv]
73
+ end
74
+ else
75
+ help_lines << [k, v]
76
+ end
77
+ }
78
+ longest_key = help_lines.map{|line| line.first.size}.max
79
+ body="Fresnel is a Console App that helps manage Lighthouse (LH).\nYou can find LH at http://lighthouseapp.com\n\n"
80
+ body+=help_lines.map {|line| "fresnel #{line.first}#{" "*(longest_key - line.first.size)} #{line.last}"}.join("\n")
81
+ puts Frame.new(
82
+ :header=>"Fresnel - A lighthouseapp console manager - help",
83
+ :body=>body,
84
+ :footer=>"Created by Narnach & Smeevil - licence : mit"
85
+ )
86
+ end
87
+ end
@@ -0,0 +1,5 @@
1
+ class DateParser
2
+ def self.string(date)
3
+ Date.today==Date.parse(date) ? DateTime.parse(date).strftime("Today %H:%M") : DateTime.parse(date).strftime("%d-%m-%y %H:%M")
4
+ end
5
+ end
@@ -0,0 +1,40 @@
1
+
2
+
3
+ class Frame
4
+ attr_accessor :header, :body, :footer, :output
5
+
6
+ def initialize(options)
7
+ @header=options[:header]||Array.new
8
+ @body=options[:body]||String.new
9
+ @footer=options[:footer]||Array.new
10
+ @output=String.new
11
+
12
+ line="+"
13
+ (@@term_size-3).times{line+="-"}
14
+ line+="+"
15
+
16
+ collect=Array.new
17
+ collect << line
18
+ if header.any?
19
+ header.each{|l|collect << l.chomp}
20
+ collect << line
21
+ end
22
+ collect << ""
23
+ collect += @body.chomp.wrap(@@term_size-5).split("\n")
24
+ collect << ""
25
+ collect << line
26
+ if footer.any?
27
+ footer.each{|l|collect << l.chomp}
28
+ collect << line
29
+ end
30
+
31
+ collect.each do |l|
32
+ self.output+="#{"| " unless l=~/^\+/}#{l.ljust(@@term_size-5)}#{" |" unless l=~/^\+/}\n"
33
+ end
34
+
35
+ end
36
+
37
+ def to_s
38
+ output
39
+ end
40
+ end
@@ -0,0 +1,386 @@
1
+ require 'rubygems'
2
+
3
+ begin
4
+ require 'uri'
5
+ require 'addressable/uri'
6
+
7
+ module URI
8
+ def decode(*args)
9
+ Addressable::URI.decode(*args)
10
+ end
11
+
12
+ def escape(*args)
13
+ Addressable::URI.escape(*args)
14
+ end
15
+
16
+ def parse(*args)
17
+ Addressable::URI.parse(*args)
18
+ end
19
+ end
20
+ rescue LoadError => e
21
+ puts "Install the Addressable gem (with dependencies) to support accounts with subdomains."
22
+ puts "# sudo gem install addressable --development"
23
+ puts e.message
24
+ end
25
+
26
+ require 'activesupport'
27
+ require 'activeresource'
28
+
29
+ # Ruby lib for working with the Lighthouse API's XML interface.
30
+ # The first thing you need to set is the account name. This is the same
31
+ # as the web address for your account.
32
+ #
33
+ # Lighthouse.account = 'activereload'
34
+ #
35
+ # Then, you should set the authentication. You can either use your login
36
+ # credentials with HTTP Basic Authentication or with an API Tokens. You can
37
+ # find more info on tokens at http://lighthouseapp.com/help/using-beacons.
38
+ #
39
+ # # with basic authentication
40
+ # Lighthouse.authenticate('rick@techno-weenie.net', 'spacemonkey')
41
+ #
42
+ # # or, use a token
43
+ # Lighthouse.token = 'abcdefg'
44
+ #
45
+ # If no token or authentication info is given, you'll only be granted public access.
46
+ #
47
+ # This library is a small wrapper around the REST interface. You should read the docs at
48
+ # http://lighthouseapp.com/api.
49
+ #
50
+ module Lighthouse
51
+ class Error < StandardError; end
52
+ class << self
53
+ attr_accessor :email, :password, :host_format, :domain_format, :protocol, :port
54
+ attr_reader :account, :token
55
+
56
+ # Sets the account name, and updates all the resources with the new domain.
57
+ def account=(name)
58
+ resources.each do |klass|
59
+ klass.site = klass.site_format % (host_format % [protocol, domain_format % name, ":#{port}"])
60
+ end
61
+ @account = name
62
+ end
63
+
64
+ # Sets up basic authentication credentials for all the resources.
65
+ def authenticate(email, password)
66
+ @email = email
67
+ @password = password
68
+ end
69
+
70
+ # Sets the API token for all the resources.
71
+ def token=(value)
72
+ resources.each do |klass|
73
+ klass.headers['X-LighthouseToken'] = value
74
+ end
75
+ @token = value
76
+ end
77
+
78
+ def resources
79
+ @resources ||= []
80
+ end
81
+ end
82
+
83
+ self.host_format = '%s://%s%s'
84
+ self.domain_format = '%s.lighthouseapp.com'
85
+ self.protocol = 'http'
86
+ self.port = ''
87
+
88
+ class Base < ActiveResource::Base
89
+ def self.inherited(base)
90
+ Lighthouse.resources << base
91
+ class << base
92
+ attr_accessor :site_format
93
+ end
94
+ base.site_format = '%s'
95
+ super
96
+ end
97
+ end
98
+
99
+ # Find projects
100
+ #
101
+ # Lighthouse::Project.find(:all) # find all projects for the current account.
102
+ # Lighthouse::Project.find(44) # find individual project by ID
103
+ #
104
+ # Creating a Project
105
+ #
106
+ # project = Lighthouse::Project.new(:name => 'Ninja Whammy Jammy')
107
+ # project.save
108
+ # # => true
109
+ #
110
+ # Creating an OSS project
111
+ #
112
+ # project = Lighthouse::Project.new(:name => 'OSS Project')
113
+ # project.access = 'oss'
114
+ # project.license = 'mit'
115
+ # project.save
116
+ #
117
+ # OSS License Mappings
118
+ #
119
+ # 'mit' => "MIT License",
120
+ # 'apache-2-0' => "Apache License 2.0",
121
+ # 'artistic-gpl-2' => "Artistic License/GPLv2",
122
+ # 'gpl-2' => "GNU General Public License v2",
123
+ # 'gpl-3' => "GNU General Public License v3",
124
+ # 'lgpl' => "GNU Lesser General Public License"
125
+ # 'mozilla-1-1' => "Mozilla Public License 1.1"
126
+ # 'new-bsd' => "New BSD License",
127
+ # 'afl-3' => "Academic Free License v. 3.0"
128
+
129
+ #
130
+ # Updating a Project
131
+ #
132
+ # project = Lighthouse::Project.find(44)
133
+ # project.name = "Lighthouse Issues"
134
+ # project.public = false
135
+ # project.save
136
+ #
137
+ # Finding tickets
138
+ #
139
+ # project = Lighthouse::Project.find(44)
140
+ # project.tickets
141
+ #
142
+ class Project < Base
143
+ def tickets(options = {})
144
+ Ticket.find(:all, :params => options.update(:project_id => id))
145
+ end
146
+
147
+ def messages(options = {})
148
+ Message.find(:all, :params => options.update(:project_id => id))
149
+ end
150
+
151
+ def milestones(options = {})
152
+ Milestone.find(:all, :params => options.update(:project_id => id))
153
+ end
154
+
155
+ def bins(options = {})
156
+ Bin.find(:all, :params => options.update(:project_id => id))
157
+ end
158
+
159
+ def changesets(options = {})
160
+ Changeset.find(:all, :params => options.update(:project_id => id))
161
+ end
162
+
163
+ def memberships(options = {})
164
+ ProjectMembership.find(:all, :params => options.update(:project_id => id))
165
+ end
166
+
167
+ def tags(options = {})
168
+ TagResource.find(:all, :params => options.update(:project_id => id))
169
+ end
170
+ end
171
+
172
+ class User < Base
173
+ def self.find(*args, &block)
174
+ if (user_id = args.first.to_i) > 0
175
+ cache = Cache.new
176
+ cache = Cache.new
177
+ cache.load(:name => "user_#{user_id}") do
178
+ super(*args, &block)
179
+ end
180
+ else
181
+ super(*args, &block)
182
+ end
183
+ end
184
+
185
+ def memberships(options = {})
186
+ Membership.find(:all, :params => {:user_id => id})
187
+ end
188
+ end
189
+
190
+ class Membership < Base
191
+ site_format << '/users/:user_id'
192
+ def save
193
+ raise Error, "Cannot modify memberships from the API"
194
+ end
195
+ end
196
+
197
+ class ProjectMembership < Base
198
+ self.element_name = 'membership'
199
+ site_format << '/projects/:project_id'
200
+
201
+ def url
202
+ respond_to?(:account) ? account : project
203
+ end
204
+
205
+ def save
206
+ raise Error, "Cannot modify memberships from the API"
207
+ end
208
+ end
209
+
210
+ class Token < Base
211
+ def save
212
+ raise Error, "Cannot modify Tokens from the API"
213
+ end
214
+ end
215
+
216
+ class Version < Base
217
+ class DiffableAttributes < Base
218
+ end
219
+ end
220
+
221
+
222
+ # Find tickets
223
+ #
224
+ # Lighthouse::Ticket.find(:all, :params => { :project_id => 44 })
225
+ # Lighthouse::Ticket.find(:all, :params => { :project_id => 44, :q => "state:closed tagged:committed" })
226
+ #
227
+ # project = Lighthouse::Project.find(44)
228
+ # project.tickets
229
+ # project.tickets(:q => "state:closed tagged:committed")
230
+ #
231
+ # Creating a Ticket
232
+ #
233
+ # ticket = Lighthouse::Ticket.new(:project_id => 44)
234
+ # ticket.title = 'asdf'
235
+ # ...
236
+ # ticket.tags << 'ruby' << 'rails' << '@high'
237
+ # ticket.save
238
+ #
239
+ # Updating a Ticket
240
+ #
241
+ # ticket = Lighthouse::Ticket.find(20, :params => { :project_id => 44 })
242
+ # ticket.state = 'resolved'
243
+ # ticket.tags.delete '@high'
244
+ # ticket.save
245
+ #
246
+ class Ticket < Base
247
+ attr_writer :tags
248
+ site_format << '/projects/:project_id'
249
+
250
+ def id
251
+ attributes['number'] ||= nil
252
+ number
253
+ end
254
+
255
+ def tags
256
+ attributes['tag'] ||= nil
257
+ @tags ||= tag.blank? ? [] : parse_with_spaces(tag)
258
+ end
259
+
260
+ def body
261
+ attributes['body'] ||= ''
262
+ end
263
+
264
+ def body=(value)
265
+ attributes['body'] = value
266
+ end
267
+
268
+ def body_html
269
+ attributes['body_html'] ||= ''
270
+ end
271
+
272
+ def body_html=(value)
273
+ attributes['body_html'] = value
274
+ end
275
+
276
+ def save_with_tags
277
+ self.tag = @tags.collect do |tag|
278
+ tag.include?(' ') ? tag.inspect : tag
279
+ end.join(" ") if @tags.is_a?(Array)
280
+ @tags = nil ; save_without_tags
281
+ end
282
+
283
+ alias_method_chain :save, :tags
284
+
285
+ private
286
+ # taken from Lighthouse Tag code
287
+ def parse_with_spaces(list)
288
+ tags = []
289
+
290
+ # first, pull out the quoted tags
291
+ list.gsub!(/\"(.*?)\"\s*/ ) { tags << $1; "" }
292
+
293
+ # then, get whatever's left
294
+ tags.concat list.split(/\s/)
295
+
296
+ cleanup_tags(tags)
297
+ end
298
+
299
+ def cleanup_tags(tags)
300
+ returning tags do |tag|
301
+ tag.collect! do |t|
302
+ unless tag.blank?
303
+ t = Tag.new(t,prefix_options[:project_id])
304
+ t.downcase!
305
+ t.gsub! /(^')|('$)/, ''
306
+ t.gsub! /[^a-z0-9 \-_@\!']/, ''
307
+ t.strip!
308
+ t.prefix_options = prefix_options
309
+ t
310
+ end
311
+ end
312
+ tag.compact!
313
+ tag.uniq!
314
+ end
315
+ end
316
+ end
317
+
318
+ class Message < Base
319
+ site_format << '/projects/:project_id'
320
+ end
321
+
322
+ class Milestone < Base
323
+ site_format << '/projects/:project_id'
324
+
325
+ def tickets(options = {})
326
+ Ticket.find(:all, :params => options.merge(prefix_options).update(:q => %{milestone:"#{title}"}))
327
+ end
328
+ end
329
+
330
+ class Bin < Base
331
+ site_format << '/projects/:project_id'
332
+
333
+ def tickets(options = {})
334
+ Ticket.find(:all, :params => options.merge(prefix_options).update(:q => query))
335
+ end
336
+ end
337
+
338
+ class Changeset < Base
339
+ site_format << '/projects/:project_id'
340
+ end
341
+
342
+ class Change < Array; end
343
+
344
+ class TagResource < Base
345
+ self.element_name = 'tag'
346
+ site_format << '/projects/:project_id'
347
+
348
+ def name
349
+ @name ||= Tag.new(attributes['name'], prefix_options[:project_id])
350
+ end
351
+
352
+ def tickets(options = {})
353
+ name.tickets(options)
354
+ end
355
+ end
356
+
357
+ class Tag < String
358
+ attr_writer :prefix_options
359
+ attr_accessor :project_id
360
+
361
+ def initialize(s, project_id)
362
+ @project_id = project_id
363
+ super(s)
364
+ end
365
+
366
+ def prefix_options
367
+ @prefix_options || {}
368
+ end
369
+
370
+ def tickets(options = {})
371
+ options[:project_id] ||= @project_id
372
+ Ticket.find(:all, :params => options.merge(prefix_options).update(:q => %{tagged:"#{self}"}))
373
+ end
374
+ end
375
+ end
376
+
377
+ module ActiveResource
378
+ class Connection
379
+ private
380
+ def authorization_header
381
+ (Lighthouse.email || Lighthouse.password ? { 'Authorization' => 'Basic ' + ["#{Lighthouse.email}:#{Lighthouse.password}"].pack('m').delete("\r\n") } : {})
382
+ end
383
+ end
384
+ end
385
+
386
+