mm 0.2.0
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.
- data/Rakefile +0 -0
- data/bin/mm +6 -0
- data/data/config.rb.erb +9 -0
- data/lib/turbine.rb +7 -0
- data/lib/turbine/application.rb +85 -0
- data/lib/turbine/command_runner.rb +63 -0
- data/lib/turbine/commands/init.rb +42 -0
- data/lib/turbine/commands/standard/add.rb +8 -0
- data/lib/turbine/commands/standard/commit.rb +29 -0
- data/lib/turbine/commands/standard/drop.rb +9 -0
- data/lib/turbine/commands/standard/project.rb +66 -0
- data/lib/turbine/commands/standard/push.rb +11 -0
- data/lib/turbine/commands/standard/reset.rb +5 -0
- data/lib/turbine/commands/standard/rewind.rb +5 -0
- data/lib/turbine/commands/standard/staged.rb +11 -0
- data/lib/turbine/commands/standard/start.rb +11 -0
- data/lib/turbine/commands/standard/status.rb +24 -0
- data/lib/turbine/commands/standard/stop.rb +22 -0
- data/lib/turbine/queue.rb +40 -0
- data/lib/turbine/timer.rb +35 -0
- metadata +86 -0
data/Rakefile
ADDED
File without changes
|
data/bin/mm
ADDED
data/data/config.rb.erb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
Dir["#{Turbine::Application.config_dir}/commands/standard/*.rb"].each do |extension|
|
2
|
+
require extension
|
3
|
+
end
|
4
|
+
|
5
|
+
Dir["#{Turbine::Application.config_dir}/commands/custom/*.rb"].each do |extension|
|
6
|
+
require extension
|
7
|
+
end
|
8
|
+
|
9
|
+
Turbine::Application.api_key = "<%= key %>"
|
data/lib/turbine.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require "highline"
|
3
|
+
require "restclient"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
module Turbine
|
7
|
+
class Application
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def config_dir(dir = Pathname.new("."))
|
11
|
+
turbine_dir = dir + ".turbine"
|
12
|
+
if dir.children.include?(turbine_dir)
|
13
|
+
turbine_dir.expand_path
|
14
|
+
else
|
15
|
+
return nil if dir.expand_path.root?
|
16
|
+
config_dir(dir.parent)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def extensions
|
21
|
+
@extensions ||= []
|
22
|
+
end
|
23
|
+
|
24
|
+
def extension(&block)
|
25
|
+
extensions << Module.new(&block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def current_project
|
29
|
+
File.read("#{config_dir}/current_project").chomp
|
30
|
+
end
|
31
|
+
|
32
|
+
def current_project=(project)
|
33
|
+
File.open("#{config_dir}/current_project", "w") { |f| f << project }
|
34
|
+
end
|
35
|
+
|
36
|
+
def current_project_url
|
37
|
+
projects[current_project]
|
38
|
+
end
|
39
|
+
|
40
|
+
def projects
|
41
|
+
JSON.parse(File.read("#{config_dir}/projects.json"))
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_accessor :api_key
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize
|
48
|
+
@params = {}
|
49
|
+
@prompt = HighLine.new
|
50
|
+
@option_parser = OptionParser.new
|
51
|
+
@turbine_dir = nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def read_file(name)
|
55
|
+
File.read("#{self.class.config_dir}/#{name}")
|
56
|
+
end
|
57
|
+
|
58
|
+
def write_file(name, mode="w")
|
59
|
+
File.open("#{self.class.config_dir}/#{name}", mode) { |f| yield(f) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def delete_file(name)
|
63
|
+
rm_f "#{self.class.config_dir}/#{name}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def service
|
67
|
+
RestClient::Resource.new(self.class.current_project_url, self.class.api_key, "")
|
68
|
+
end
|
69
|
+
|
70
|
+
def load_log_data
|
71
|
+
JSON.parse(File.read("#{self.class.config_dir}/log/#{self.class.api_key}.json"))
|
72
|
+
end
|
73
|
+
|
74
|
+
def clear_log_data
|
75
|
+
write_file("log/#{self.class.api_key}.json") do |f|
|
76
|
+
f << [].to_json
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
attr_accessor :params, :prompt, :option_parser, :command
|
81
|
+
|
82
|
+
include ::Turbine::CommandRunner
|
83
|
+
include ::Turbine::StandardCommands
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Turbine
|
2
|
+
module CommandRunner
|
3
|
+
def setup
|
4
|
+
end
|
5
|
+
|
6
|
+
def run(argv)
|
7
|
+
init_and_exit(*argv) if argv[0] == "init"
|
8
|
+
|
9
|
+
unless self.class.config_dir
|
10
|
+
prompt.say "Cannot find config file. Did you forget to run turbine init?"
|
11
|
+
return
|
12
|
+
end
|
13
|
+
|
14
|
+
require "#{self.class.config_dir}/config"
|
15
|
+
|
16
|
+
self.class.extensions.each do |extension|
|
17
|
+
extend(extension)
|
18
|
+
end
|
19
|
+
|
20
|
+
setup
|
21
|
+
parse_options(*argv)
|
22
|
+
|
23
|
+
if respond_to?(command)
|
24
|
+
send(command)
|
25
|
+
else
|
26
|
+
prompt.say("Unknown command: #{command}")
|
27
|
+
end
|
28
|
+
|
29
|
+
teardown
|
30
|
+
end
|
31
|
+
|
32
|
+
def init_and_exit(*argv)
|
33
|
+
setup
|
34
|
+
parse_options(*argv)
|
35
|
+
init
|
36
|
+
teardown
|
37
|
+
exit
|
38
|
+
end
|
39
|
+
|
40
|
+
def teardown
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse_options(*argv)
|
44
|
+
option_parser.on("-t", "=HOURS", Float) do |hours|
|
45
|
+
params[:hours] = hours
|
46
|
+
end
|
47
|
+
|
48
|
+
option_parser.on("-m", "=MSG", String) do |msg|
|
49
|
+
params[:message] = msg
|
50
|
+
end
|
51
|
+
|
52
|
+
option_parser.on("-k", "=KEY", String) do |msg|
|
53
|
+
params[:api_key] = msg
|
54
|
+
end
|
55
|
+
|
56
|
+
command, *arguments = option_parser.parse(*argv)
|
57
|
+
|
58
|
+
self.command = command
|
59
|
+
params[:arguments] = arguments
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "json"
|
2
|
+
require "erb"
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
module Turbine
|
6
|
+
module StandardCommands
|
7
|
+
include FileUtils
|
8
|
+
|
9
|
+
def init
|
10
|
+
mkdir_p(".turbine")
|
11
|
+
mkdir_p(".turbine/log")
|
12
|
+
mkdir_p(".turbine/commands")
|
13
|
+
mkdir_p(".turbine/commands/custom")
|
14
|
+
|
15
|
+
key = params[:api_key] || ENV['TURBINE_API_KEY']
|
16
|
+
url = params[:arguments][0]
|
17
|
+
project_name = params[:arguments][1] || "default"
|
18
|
+
|
19
|
+
raise if key.nil?
|
20
|
+
|
21
|
+
current_dir = File.dirname(__FILE__)
|
22
|
+
|
23
|
+
write_file("log/#{key}.json") { |f| f << [].to_json }
|
24
|
+
|
25
|
+
write_file("config.rb") do |f|
|
26
|
+
template = File.read("#{current_dir}/../../../data/config.rb.erb")
|
27
|
+
f << ERB.new(template).result(binding)
|
28
|
+
end
|
29
|
+
|
30
|
+
write_file("current_project") do |f|
|
31
|
+
f << project_name
|
32
|
+
end
|
33
|
+
|
34
|
+
write_file("projects.json") do |f|
|
35
|
+
f << { project_name => url }.to_json
|
36
|
+
end
|
37
|
+
|
38
|
+
cp_r("#{current_dir}/standard",
|
39
|
+
"#{self.class.config_dir}/commands/")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
Turbine::Application.extension do
|
2
|
+
def commit
|
3
|
+
if message = params[:message]
|
4
|
+
queue = Turbine::Queue.new
|
5
|
+
duration = queue.sum.to_s
|
6
|
+
timestamp = Time.now.utc.to_s
|
7
|
+
|
8
|
+
if queue.empty?
|
9
|
+
prompt.say "ERROR: No entries!"
|
10
|
+
exit
|
11
|
+
end
|
12
|
+
|
13
|
+
log_data = load_log_data
|
14
|
+
|
15
|
+
log_data << { :description => message,
|
16
|
+
:recorded_timestamp => timestamp,
|
17
|
+
:recorded_date => Date.today.to_s,
|
18
|
+
:hours => duration }
|
19
|
+
|
20
|
+
write_file("log/#{self.class.api_key}.json") { |f| f << log_data.to_json }
|
21
|
+
|
22
|
+
prompt.say "Committed time entry totaling #{duration} hrs"
|
23
|
+
|
24
|
+
queue.clear
|
25
|
+
else
|
26
|
+
prompt.say "You need to supply a message"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
Turbine::Application.extension do
|
2
|
+
|
3
|
+
def project
|
4
|
+
_list_projects || _manage_projects
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def _list_projects
|
10
|
+
return false unless params[:arguments].empty?
|
11
|
+
|
12
|
+
prompt.say("\n")
|
13
|
+
|
14
|
+
self.class.projects.each do |name, url|
|
15
|
+
prompt.say(name == self.class.current_project ? " > " : " ")
|
16
|
+
prompt.say("#{name} : #{url}")
|
17
|
+
end
|
18
|
+
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def _manage_projects
|
23
|
+
command, project, *args = *params[:arguments]
|
24
|
+
case command
|
25
|
+
when "add"
|
26
|
+
url = args.shift
|
27
|
+
|
28
|
+
raise "Not enough arguments" unless url
|
29
|
+
|
30
|
+
prompt.say "adding project: #{project}"
|
31
|
+
projects = self.class.projects
|
32
|
+
write_file("projects.json") do |f|
|
33
|
+
f << projects.merge(project => url).to_json
|
34
|
+
end
|
35
|
+
when "rm"
|
36
|
+
projects = self.class.projects
|
37
|
+
|
38
|
+
unless projects.keys.include?(project)
|
39
|
+
prompt.say("No such project: #{project}")
|
40
|
+
return
|
41
|
+
end
|
42
|
+
|
43
|
+
prompt.say "removing project: #{project}"
|
44
|
+
|
45
|
+
projects.delete(project)
|
46
|
+
|
47
|
+
write_file("projects.json") do |f|
|
48
|
+
f << projects.to_json
|
49
|
+
end
|
50
|
+
when "select"
|
51
|
+
projects = self.class.projects
|
52
|
+
|
53
|
+
unless projects.keys.include?(project)
|
54
|
+
prompt.say("No such project: #{project}")
|
55
|
+
return
|
56
|
+
end
|
57
|
+
|
58
|
+
prompt.say "selecting project: #{project}"
|
59
|
+
|
60
|
+
self.class.current_project = project
|
61
|
+
else
|
62
|
+
prompt.say "Unknown command: project #{command}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Turbine::Application.extension do
|
2
|
+
def staged
|
3
|
+
log_data = load_log_data
|
4
|
+
|
5
|
+
prompt.say "These entries will be created upon push:\n\n"
|
6
|
+
|
7
|
+
log_data.each_with_index do |record, i|
|
8
|
+
prompt.say " [#{i}] #{record["description"]} (#{record["hours"]} hrs)"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Turbine::Application.extension do
|
2
|
+
def status
|
3
|
+
queue = Turbine::Queue.new
|
4
|
+
|
5
|
+
if queue.empty?
|
6
|
+
prompt.say "No entries for this commit yet\n\n"
|
7
|
+
else
|
8
|
+
entries = queue.entries.join(", ")
|
9
|
+
sum = queue.entries.inject {|sum, n| sum + n }
|
10
|
+
prompt.say "Entries for this commit: ( #{entries} )"
|
11
|
+
prompt.say "Total for this commit: #{sum}\n\n"
|
12
|
+
end
|
13
|
+
|
14
|
+
timer = Turbine::Timer.new
|
15
|
+
if timer.running?
|
16
|
+
prompt.say("Current timer started at #{timer.timestamp}")
|
17
|
+
prompt.say("Elapsed time: #{'%0.2f' % timer.elapsed_time} hrs")
|
18
|
+
else
|
19
|
+
prompt.say("Timer is not presently running")
|
20
|
+
end
|
21
|
+
|
22
|
+
prompt.say("\n")
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
Turbine::Application.extension do
|
2
|
+
def stop
|
3
|
+
timer = Turbine::Timer.new
|
4
|
+
if timer.running?
|
5
|
+
begin
|
6
|
+
duration = prompt.ask("Time to enter (CTRL-C to cancel): ", Float) do |q|
|
7
|
+
q.default = ("%0.2f" % timer.elapsed_time).to_f
|
8
|
+
end
|
9
|
+
|
10
|
+
queue = Turbine::Queue.new
|
11
|
+
queue << duration
|
12
|
+
|
13
|
+
timer.clear_timestamp
|
14
|
+
rescue Interrupt
|
15
|
+
prompt.say("\n")
|
16
|
+
end
|
17
|
+
else
|
18
|
+
prompt.say "ERROR: Timer was not running."
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Turbine
|
2
|
+
class Queue
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize(dir=Turbine::Application.config_dir)
|
6
|
+
@file = "#{dir}/queue"
|
7
|
+
end
|
8
|
+
|
9
|
+
def <<(duration)
|
10
|
+
File.open(@file, "a") { |f| f.puts(duration.to_s)}
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def empty?
|
15
|
+
!File.exist?(@file)
|
16
|
+
end
|
17
|
+
|
18
|
+
def entries
|
19
|
+
return [] if empty?
|
20
|
+
|
21
|
+
File.read(@file).split("\n").map { |e| e.to_f }
|
22
|
+
end
|
23
|
+
|
24
|
+
def each
|
25
|
+
entries.each { |e| yield(e) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def sum
|
29
|
+
return 0 if empty?
|
30
|
+
|
31
|
+
File.foreach(@file).inject(0) do |sum, line|
|
32
|
+
sum + line.to_f
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def clear
|
37
|
+
FileUtils.rm_f(@file)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "time"
|
3
|
+
require "date"
|
4
|
+
|
5
|
+
module Turbine
|
6
|
+
class Timer
|
7
|
+
MissingTimestampError = Class.new(StandardError)
|
8
|
+
|
9
|
+
def initialize(dir=Turbine::Application.config_dir)
|
10
|
+
@file = "#{dir}/timestamp"
|
11
|
+
end
|
12
|
+
|
13
|
+
def write_timestamp
|
14
|
+
File.open(@file, "w") { |f| f << Time.now.utc.to_s }
|
15
|
+
end
|
16
|
+
|
17
|
+
def timestamp
|
18
|
+
raise MissingTimestampError unless running?
|
19
|
+
Time.parse(File.read(@file)).localtime
|
20
|
+
end
|
21
|
+
|
22
|
+
def elapsed_time
|
23
|
+
(Time.now.utc - timestamp.utc) / 60.0 / 60.0
|
24
|
+
end
|
25
|
+
|
26
|
+
def clear_timestamp
|
27
|
+
FileUtils.rm_f(@file)
|
28
|
+
end
|
29
|
+
|
30
|
+
def running?
|
31
|
+
File.exist?(@file)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mm
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Gregory Brown
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-08-10 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: " Seeekrit\n"
|
23
|
+
email: " gregory.t.brown@gmail.com"
|
24
|
+
executables:
|
25
|
+
- mm
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- lib/turbine/application.rb
|
32
|
+
- lib/turbine/command_runner.rb
|
33
|
+
- lib/turbine/timer.rb
|
34
|
+
- lib/turbine/queue.rb
|
35
|
+
- lib/turbine/commands/standard/staged.rb
|
36
|
+
- lib/turbine/commands/standard/start.rb
|
37
|
+
- lib/turbine/commands/standard/add.rb
|
38
|
+
- lib/turbine/commands/standard/reset.rb
|
39
|
+
- lib/turbine/commands/standard/drop.rb
|
40
|
+
- lib/turbine/commands/standard/status.rb
|
41
|
+
- lib/turbine/commands/standard/push.rb
|
42
|
+
- lib/turbine/commands/standard/rewind.rb
|
43
|
+
- lib/turbine/commands/standard/project.rb
|
44
|
+
- lib/turbine/commands/standard/commit.rb
|
45
|
+
- lib/turbine/commands/standard/stop.rb
|
46
|
+
- lib/turbine/commands/init.rb
|
47
|
+
- lib/turbine.rb
|
48
|
+
- data/config.rb.erb
|
49
|
+
- bin/mm
|
50
|
+
- Rakefile
|
51
|
+
has_rdoc: true
|
52
|
+
homepage: http://pixelpowerhouse.com
|
53
|
+
licenses: []
|
54
|
+
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options: []
|
57
|
+
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
hash: 3
|
66
|
+
segments:
|
67
|
+
- 0
|
68
|
+
version: "0"
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 3
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
requirements: []
|
79
|
+
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 1.3.7
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: A seecret
|
85
|
+
test_files: []
|
86
|
+
|