afterburn 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,3 @@
1
+ = Afterburn
2
+
3
+ This project rocks and uses MIT-LICENSE.
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'Afterburn'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ Bundler::GemHelper.install_tasks
24
+
25
+ load './lib/tasks/afterburn_tasks.rake'
26
+
27
+ begin
28
+ require 'rspec/core/rake_task'
29
+
30
+ desc "Run specs"
31
+ RSpec::Core::RakeTask.new do |t|
32
+ t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
33
+ # Put spec opts in a file named .rspec in root
34
+ end
35
+
36
+ desc "Generate code coverage"
37
+ RSpec::Core::RakeTask.new(:coverage) do |t|
38
+ t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
39
+ t.rcov = true
40
+ t.rcov_opts = ['--exclude', 'spec']
41
+ end
42
+
43
+ rescue LoadError
44
+ task :spec do
45
+ abort "Rspec is not available."
46
+ end
47
+ end
48
+
49
+ task :default => :spec
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+
5
+ require 'afterburn'
6
+ require 'optparse'
7
+
8
+ config = ENV['AFTERBURNCONFIG'] || './lib/afterburn/server/config/setup.rb'
9
+ load ::File.expand_path(config) if ::File.exists?(config)
10
+
11
+ parser = OptionParser.new do |opts|
12
+ opts.banner = "Usage: burn [options] COMMAND"
13
+
14
+ opts.separator ""
15
+ opts.separator "Options:"
16
+
17
+ # opts.on("-m", "--members", "List members") do |host|
18
+ # Resque.redis = host
19
+ # end
20
+
21
+ # opts.on("-N", "--namespace [NAMESPACE]", "Redis namespace") do |namespace|
22
+ # Resque.redis.namespace = namespace
23
+ # end
24
+
25
+ opts.on("-h", "--help", "Show this message") do
26
+ puts opts
27
+ exit
28
+ end
29
+
30
+ # opts.separator ""
31
+ # opts.separator "Commands:"
32
+ # opts.separator " remove WORKER Removes a worker"
33
+ # opts.separator " kill WORKER Kills a worker"
34
+ opts.separator " list List current members or projects"
35
+ end
36
+
37
+ # def kill(worker)
38
+ # abort "** resque kill WORKER_ID" if worker.nil?
39
+ # pid = worker.split(':')[1].to_i
40
+
41
+ # begin
42
+ # Process.kill("KILL", pid)
43
+ # puts "** killed #{worker}"
44
+ # rescue Errno::ESRCH
45
+ # puts "** worker #{worker} not running"
46
+ # end
47
+
48
+ # remove worker
49
+ # end
50
+
51
+ # def remove(worker)
52
+ # abort "** resque remove WORKER_ID" if worker.nil?
53
+
54
+ # Resque.remove_worker(worker)
55
+ # puts "** removed #{worker}"
56
+ # end
57
+
58
+ def list(method)
59
+ if Afterburn.respond_to?("current_#{method}")
60
+ collection = Afterburn.send("current_#{method}")
61
+
62
+ if collection.any?
63
+ collection.each do |item|
64
+ puts item.name
65
+ end
66
+ else
67
+ puts "None"
68
+ end
69
+ else
70
+ puts "#{method} unknown"
71
+ end
72
+ end
73
+
74
+ def record
75
+ Afterburn.current_projects.map(&:record_interval)
76
+ end
77
+
78
+ parser.parse!
79
+
80
+ case ARGV[0]
81
+ # when 'kill'
82
+ # kill ARGV[1]
83
+ # when 'remove'
84
+ # remove ARGV[1]
85
+ when 'list'
86
+ list ARGV[1]
87
+ when 'record'
88
+ record
89
+ else
90
+ puts parser.help
91
+ end
@@ -0,0 +1,35 @@
1
+ require 'trello'
2
+
3
+ module Afterburn
4
+ autoload :RedisConnection, "afterburn/redis_connection"
5
+ autoload :Helpers, "afterburn/helpers"
6
+ autoload :TrelloObjectWrapper, "afterburn/trello_object_wrapper"
7
+ autoload :Board, "afterburn/board"
8
+ autoload :Member, "afterburn/member"
9
+ autoload :List, "afterburn/list"
10
+ autoload :ListMetric, "afterburn/list_metric"
11
+ autoload :ListIntervalSeries, "afterburn/list_interval_series"
12
+ autoload :BoardInterval, "afterburn/board_interval"
13
+ autoload :Project, "afterburn/project"
14
+ autoload :Authorization, "afterburn/authorization"
15
+ autoload :Server, "afterburn/server"
16
+
17
+ extend RedisConnection
18
+ extend self
19
+
20
+ def authorize(member_name, &block)
21
+ Authorization.new(member_name).configure(&block)
22
+ end
23
+
24
+ def current_member
25
+ @current_member ||= Afterburn::Member.first
26
+ end
27
+
28
+ def current_members
29
+ Afterburn::Member.all
30
+ end
31
+
32
+ def current_projects
33
+ current_members.map { |member| Afterburn::Project.by_member(member) }.flatten
34
+ end
35
+ end
@@ -0,0 +1,36 @@
1
+ module Afterburn
2
+ class Authorization
3
+ include Trello
4
+ include Trello::Authorization
5
+
6
+ attr_accessor :trello_user_key, :trello_user_secret, :trello_app_token
7
+
8
+ def initialize(member_name)
9
+ @member_name = member_name
10
+ end
11
+
12
+ def configure
13
+ yield self if block_given?
14
+ Trello::Authorization.const_set :AuthPolicy, OAuthPolicy
15
+ OAuthPolicy.consumer_credential = OAuthCredential.new trello_user_key, trello_user_secret
16
+ OAuthPolicy.token = OAuthCredential.new trello_app_token, nil
17
+ Afterburn::Member.add_member(member)
18
+ end
19
+
20
+ def trello_user_key
21
+ @trello_user_key ||= member.trello_user_key
22
+ end
23
+
24
+ def trello_user_secret
25
+ @trello_user_secret ||= member.trello_user_secret
26
+ end
27
+
28
+ def trello_app_token
29
+ @trello_app_token ||= member.trello_app_token
30
+ end
31
+
32
+ def member
33
+ Afterburn::Member.find(@member_name)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,22 @@
1
+ module Afterburn
2
+ class Board < TrelloObjectWrapper
3
+ wrap :board
4
+
5
+ def self.fetch_by_member(member_name)
6
+ Trello::Member.find(member_name).boards.map { |trello_board| initialize_from_trello_object(trello_board) }
7
+ end
8
+
9
+ def lists
10
+ @lists ||= trello_lists.map { |trello_list| List.factory(trello_list) }
11
+ end
12
+
13
+ def trello_lists
14
+ trello_board.lists
15
+ end
16
+
17
+ def name
18
+ trello_board.name
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,47 @@
1
+ require "base64"
2
+ require 'redis/objects'
3
+
4
+ module Afterburn
5
+ class BoardInterval
6
+ include Redis::Objects
7
+
8
+ attr_reader :board, :timestamp
9
+
10
+ def self.find(id)
11
+ board_id, timestamp_string = Base64.decode64(id).split(":")
12
+ return nil if board_id.nil?
13
+ new(Board.find(board_id), Time.at(timestamp_string.to_i))
14
+ end
15
+
16
+ def self.find_all(ids)
17
+ ids.map { |interval_id| find(interval_id) }.compact
18
+ end
19
+
20
+ # TODO test
21
+ def self.record(board, timestamp)
22
+ new(board, timestamp).tap { |board_interval| board_interval.record! }
23
+ end
24
+
25
+ def initialize(board, timestamp = Time.now)
26
+ @board = board
27
+ @timestamp = timestamp
28
+ end
29
+
30
+ def id
31
+ @id ||= Base64.encode64("#{@board.id}:#{@timestamp.to_i}")
32
+ end
33
+
34
+ def list_metrics
35
+ @list_metrics ||= ListMetric.for_timestamp(@board.lists, timestamp)
36
+ end
37
+
38
+ def record!
39
+ list_metrics.map(&:count!)
40
+ end
41
+
42
+ def ==(other)
43
+ self.class == other.class && !other.id.nil? && self.id == other.id
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,11 @@
1
+ require 'afterburn'
2
+
3
+ module Afterburn
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace Afterburn
6
+
7
+ initializer 'require_afterburn_server' do
8
+ require 'afterburn/server'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,54 @@
1
+ module Afterburn
2
+ module Helpers
3
+
4
+ def redis
5
+ Afterburn.redis
6
+ end
7
+
8
+ # Tries to find a constant with the name specified in the argument string:
9
+ #
10
+ # constantize("Module") # => Module
11
+ # constantize("Test::Unit") # => Test::Unit
12
+ #
13
+ # The name is assumed to be the one of a top-level constant, no matter
14
+ # whether it starts with "::" or not. No lexical context is taken into
15
+ # account:
16
+ #
17
+ # C = 'outside'
18
+ # module M
19
+ # C = 'inside'
20
+ # C # => 'inside'
21
+ # constantize("C") # => 'outside', same as ::C
22
+ # end
23
+ #
24
+ # NameError is raised when the constant is unknown.
25
+ def constantize(camel_cased_word)
26
+ camel_cased_word = camel_cased_word.to_s
27
+
28
+ if camel_cased_word.include?('-')
29
+ camel_cased_word = classify(camel_cased_word)
30
+ end
31
+
32
+ names = camel_cased_word.split('::')
33
+ names.shift if names.empty? || names.first.empty?
34
+
35
+ constant = Object
36
+ names.each do |name|
37
+ args = Module.method(:const_get).arity != 1 ? [false] : []
38
+
39
+ if constant.const_defined?(name, *args)
40
+ constant = constant.const_get(name)
41
+ else
42
+ constant = constant.const_missing(name)
43
+ end
44
+ end
45
+ constant
46
+ end
47
+
48
+
49
+ def titleize(word)
50
+ word.gsub(%r{\b('?[a-z])}) { $1.capitalize }
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,98 @@
1
+ module Afterburn
2
+ class List < TrelloObjectWrapper
3
+
4
+ class UnknownRole < StandardError; end
5
+ module Role
6
+ extend self
7
+ VALID_ROLES = [
8
+ BACKLOG = 'backlog',
9
+ WIP = 'WIP', # work in progress
10
+ DEPLOYED = 'deployed',
11
+ IGNORED = 'ignored'
12
+ ]
13
+
14
+ HISTORICAL = [BACKLOG, DEPLOYED]
15
+
16
+ def valid?(role)
17
+ VALID_ROLES.include?(role)
18
+ end
19
+
20
+ def historical?(role)
21
+ HISTORICAL.include?(role)
22
+ end
23
+ end
24
+
25
+ wrap :list
26
+ value :role_store
27
+ set :historical_card_id_set
28
+ set :metric_timestamp_list
29
+
30
+ def self.roles
31
+ Role::VALID_ROLES
32
+ end
33
+
34
+ def self.factory(trello_list)
35
+ initialize_from_trello_object(trello_list).tap do |list|
36
+ list.extend(History) if Role.historical?(list.role)
37
+ end
38
+ end
39
+
40
+ def self.historical(list)
41
+ HistoricalList.initialize_from_trello_object(list.trello_list)
42
+ end
43
+
44
+ def name
45
+ trello_list.name
46
+ end
47
+
48
+ def role=(role)
49
+ raise UnknownRole.new("Tried to set unrecognized '#{role}'") unless Role.valid?(role)
50
+ role_store.value = role
51
+ end
52
+
53
+ def role
54
+ role_store.value
55
+ end
56
+
57
+ def trello_cards
58
+ trello_list.cards
59
+ end
60
+
61
+ def card_count
62
+ trello_cards.count
63
+ end
64
+
65
+ def timestamp_count_vector(timestamps)
66
+ ListMetric.timestamp_count_vector(self, timestamps)
67
+ end
68
+
69
+ def update_attributes(attributes)
70
+ attributes.each do |key, value|
71
+ send("#{key}=", value)
72
+ end
73
+ end
74
+
75
+ module History
76
+
77
+ def card_count
78
+ update_card_history
79
+ historical_card_count
80
+ end
81
+
82
+ def historical_card_ids
83
+ historical_card_id_set.members
84
+ end
85
+
86
+ def historical_card_count
87
+ historical_card_id_set.count
88
+ end
89
+
90
+ def update_card_history
91
+ trello_cards.map(&:id).each { |card_id| historical_card_id_set << card_id }
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+
98
+ end