afterburn 0.0.1

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,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