Overbryd-mighty-mite 0.1.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/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.textile +156 -0
- data/Rakefile +23 -0
- data/TODO +4 -0
- data/VERSION +1 -0
- data/bin/mite +13 -0
- data/lib/mighty_mite/application.rb +169 -0
- data/lib/mighty_mite/autocomplete.rb +48 -0
- data/lib/mighty_mite.rb +34 -0
- data/lib/mite_ext.rb +53 -0
- data/lib/string_ext.rb +50 -0
- data/mighty-mite.gemspec +76 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/unit/mighty_mite/application_spec.rb +509 -0
- data/spec/unit/mighty_mite/autocomplete_spec.rb +100 -0
- data/spec/unit/mighty_mite_spec.rb +51 -0
- data/spec/unit/mite_ext_spec.rb +99 -0
- data/spec/unit/string_ext_spec.rb +67 -0
- data/vendor/yolk-mite-rb-0.0.3/CHANGES.txt +11 -0
- data/vendor/yolk-mite-rb-0.0.3/LICENSE +20 -0
- data/vendor/yolk-mite-rb-0.0.3/README.textile +70 -0
- data/vendor/yolk-mite-rb-0.0.3/Rakefile +24 -0
- data/vendor/yolk-mite-rb-0.0.3/VERSION.yml +4 -0
- data/vendor/yolk-mite-rb-0.0.3/lib/mite/customer.rb +9 -0
- data/vendor/yolk-mite-rb-0.0.3/lib/mite/project.rb +16 -0
- data/vendor/yolk-mite-rb-0.0.3/lib/mite/service.rb +7 -0
- data/vendor/yolk-mite-rb-0.0.3/lib/mite/time_entry.rb +54 -0
- data/vendor/yolk-mite-rb-0.0.3/lib/mite/time_entry_group.rb +36 -0
- data/vendor/yolk-mite-rb-0.0.3/lib/mite/tracker.rb +34 -0
- data/vendor/yolk-mite-rb-0.0.3/lib/mite/user.rb +19 -0
- data/vendor/yolk-mite-rb-0.0.3/lib/mite-rb.rb +105 -0
- metadata +90 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Overbryd
|
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.
|
data/README.textile
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
<pre>
|
2
|
+
|
3
|
+
_| _| _|
|
4
|
+
_|_|_| _|_| _|_|_| _|_|_| _|_|_|_| _| _|
|
5
|
+
_| _| _| _| _| _| _| _| _| _| _|
|
6
|
+
_| _| _| _| _| _| _| _| _| _| _|
|
7
|
+
_| _| _| _| _|_|_| _| _| _|_| _|_|_| _|
|
8
|
+
_| _|
|
9
|
+
_|_| _|_|
|
10
|
+
|
11
|
+
_| _|
|
12
|
+
_|_|_| _|_| _|_|_|_| _|_|
|
13
|
+
_| _| _| _| _| _|_|_|_|
|
14
|
+
_| _| _| _| _| _|
|
15
|
+
_| _| _| _| _|_| _|_|_|
|
16
|
+
</pre>
|
17
|
+
A simple command line interface for basic mite tasks.
|
18
|
+
|
19
|
+
* @mite.@ is an ingeniously sleek time tracking tool: http://mite.yo.lk
|
20
|
+
* @mighty-mite@ is a command line interface for it: http://github.com/Overbryd/mighty-mite
|
21
|
+
It provides a system wide command called @mite@.
|
22
|
+
|
23
|
+
h3. Installation instructions:
|
24
|
+
|
25
|
+
$ gem install overbryd-mighty-mite
|
26
|
+
|
27
|
+
I assume github is in your gem sources. You may need @active_support@ and @active_resource@ too.
|
28
|
+
|
29
|
+
h3. After installation instructions:
|
30
|
+
|
31
|
+
You'll need to configure the client prior using it.
|
32
|
+
To do this, you can just hammer into your console:
|
33
|
+
|
34
|
+
$ mite configure "Your Account Name" "Your API Key"
|
35
|
+
|
36
|
+
It will then generate a yml file in ~/.mite.yml with your account information.
|
37
|
+
|
38
|
+
The configure command also tries to install the bash auto completion hook into the following files: @~/.bash_completion@, @~/.bash_profile@, @~/.bash_login@, @~/.bashrc@.
|
39
|
+
|
40
|
+
If your system doesn't support one of the files above, install the hook by yourself.
|
41
|
+
In order to work as expected it needs bash to be configured for auto completion.
|
42
|
+
This is actually quite easy, just append this line to your bash config file.
|
43
|
+
|
44
|
+
complete -C "mite auto-complete" mite
|
45
|
+
|
46
|
+
You could use this command as an example (replace .bash_login with your bash configuration file):
|
47
|
+
|
48
|
+
$ echo "complete -C \"mite auto-complete\" mite" >> ~/.bash_login
|
49
|
+
|
50
|
+
h3. Create new time entries with ease:
|
51
|
+
|
52
|
+
$ mite "project name" [service name] [start time h:mm] [note]
|
53
|
+
|
54
|
+
$ mite "project name" "service name" [note]
|
55
|
+
|
56
|
+
$ mite "project name" [start time h:mm] [note]
|
57
|
+
|
58
|
+
$ mite [start time h:mm] [note]
|
59
|
+
|
60
|
+
h4. The magic of the moment err... start time:
|
61
|
+
|
62
|
+
Append a @+@ to the start time and it will start the tracker.
|
63
|
+
|
64
|
+
The most convenient syntax to set the start time is @h:mm@. So it will accept all these kinds of values: @1:15@, @0:01@, @24:00@.
|
65
|
+
|
66
|
+
Short-hand for hours:
|
67
|
+
* @3+@ will set the start time to 3 hours, and start the tracker
|
68
|
+
* @3.5@ will set it to 3 hours 30 minutes
|
69
|
+
* @0.25+@ will make you look like a floating-point fetish, and start the tracker
|
70
|
+
|
71
|
+
h4. Examples:
|
72
|
+
|
73
|
+
$ mite "World Domination" "W.o.M.D" 72:01+ "This takes way too long... Maybe I should try another way."
|
74
|
+
|
75
|
+
This will set a time entry at 72:01 for the project "World Domination" with a note saying "This takes way too long... Maybe I should try another way.".
|
76
|
+
The timer will start, because of the appended '+'. If there is a running timer, it will be stopped before.
|
77
|
+
|
78
|
+
$ mite HugEveryone 0:15 "Strange feelings..."
|
79
|
+
|
80
|
+
This will set a time entry at 0:15 for the project HugEveryone with a note saying "Strange feelings...".
|
81
|
+
The timer will not start.
|
82
|
+
|
83
|
+
$ mite HugEveryone Love "Everybody should be like me..."
|
84
|
+
|
85
|
+
This will set a time entry that starts at 0:00 for the Project HugEveryone, doing the service "Love" with a note "Everybody should be like me...".
|
86
|
+
If there is a running timer, it will be stopped before.
|
87
|
+
|
88
|
+
h4. Note:
|
89
|
+
|
90
|
+
If a project or a service doesn't exist it will be created.
|
91
|
+
|
92
|
+
h3. Amazing auto-completion:
|
93
|
+
|
94
|
+
This is very nifty. (But maybe you should read the After Installation Instructions before.)
|
95
|
+
The client was designed to save keystrokes, so I've baked in a very handy auto-completion feature.
|
96
|
+
|
97
|
+
Try this:
|
98
|
+
|
99
|
+
$ mite [tab]
|
100
|
+
|
101
|
+
It will try to auto-complete your projects.
|
102
|
+
|
103
|
+
$ mite Project1 [tab]
|
104
|
+
|
105
|
+
It will try to auto-complete your services.
|
106
|
+
|
107
|
+
$ mite Project1 "System Administration" [tab]
|
108
|
+
|
109
|
+
It will try to auto-complete common times!
|
110
|
+
|
111
|
+
$ mite Project1 "System Administration" 0:48 [tab]
|
112
|
+
|
113
|
+
It will try to auto-complete your notes!
|
114
|
+
|
115
|
+
h4. Amazingly fast auto-completion:
|
116
|
+
|
117
|
+
The auto-completion feature creates a cache in ~/.mite.cache, if you want to rebuild this cache just hit:
|
118
|
+
|
119
|
+
$ mite rebuild-cache
|
120
|
+
|
121
|
+
h4. Note: The first run without the cache might be a bit slow.
|
122
|
+
|
123
|
+
h3. Controlling timers:
|
124
|
+
|
125
|
+
$ mite start
|
126
|
+
|
127
|
+
This little cutey will start today's last time entry, if there is one.
|
128
|
+
|
129
|
+
$ mite stop
|
130
|
+
|
131
|
+
This will just stop the current timer. (If you like you can use `mite pause` or `mite lunch` too)
|
132
|
+
|
133
|
+
h3. Simple reports:
|
134
|
+
|
135
|
+
$ mite today
|
136
|
+
|
137
|
+
This will generate a report of today's activity, summarizing your earnings at the bottom.
|
138
|
+
|
139
|
+
$ mite yesterday
|
140
|
+
|
141
|
+
This will generate a report of yesterday's activity, summarizing your earnings at the bottom.
|
142
|
+
Also works using @this_week@, @last_week@, @this_month@, @last_month@ as argument.
|
143
|
+
|
144
|
+
h3. More simple stuff:
|
145
|
+
|
146
|
+
$ mite
|
147
|
+
|
148
|
+
If there is a running timer, it will output it. Otherwise you should better not listen to it.
|
149
|
+
|
150
|
+
$ mite +
|
151
|
+
|
152
|
+
Just create a new time entry and start tracking it.
|
153
|
+
|
154
|
+
$ mite open
|
155
|
+
|
156
|
+
Opens your mite account in a new browser window (Max OSX only) or prints the url to your account.
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "mighty-mite"
|
8
|
+
gem.executables = "mite"
|
9
|
+
gem.summary = "A simple command line interface for basic mite tasks."
|
10
|
+
gem.email = "l.rieder@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/Overbryd/mighty-mite"
|
12
|
+
gem.description = "A simple command line interface for mite, a sleek time tracking webapp."
|
13
|
+
gem.authors = ["Lukas Rieder"]
|
14
|
+
end
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'spec/rake/spectask'
|
20
|
+
task :spec do
|
21
|
+
sh "spec spec/unit/* --format specdoc --color"
|
22
|
+
end
|
23
|
+
task :default => :spec
|
data/TODO
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/bin/mite
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
4
|
+
|
5
|
+
require 'mighty_mite'
|
6
|
+
|
7
|
+
begin
|
8
|
+
MightyMite.calling_script = __FILE__
|
9
|
+
MightyMite.run(ARGV)
|
10
|
+
rescue MightyMite::Exception => e
|
11
|
+
puts "#{"Epic fail: #{e.message}".colorize(:color => :red, :background => :black)} Type `mite help` to get help."
|
12
|
+
exit(1)
|
13
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
module MightyMite
|
2
|
+
class Application
|
3
|
+
TIME_FORMAT = /^(\d+(\.\d+)?:?\+?)|(\d+:\d+\+?)|\+$/
|
4
|
+
FLIRTS = [
|
5
|
+
'I like your hairstyle.', 'What a nice console you have.', 'My favorite color is red on black, monospaced.',
|
6
|
+
"What a lovely operation system this #{`uname`} is.", 'What about dinner tonight?', 'Your keystrokes are tingling.'
|
7
|
+
]
|
8
|
+
|
9
|
+
def initialize(arguments=[])
|
10
|
+
@arguments = arguments
|
11
|
+
MightyMite.load_configuration unless arguments.first == 'configure'
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
if @arguments.first == 'open'
|
16
|
+
open_or_echo Mite.account_url
|
17
|
+
|
18
|
+
elsif @arguments.first == 'help'
|
19
|
+
open_or_echo 'http://github.com/Overbryd/mighty-mite'
|
20
|
+
|
21
|
+
elsif @arguments.first == 'configure'
|
22
|
+
raise MightyMite::Exception.new('mite configure needs two arguments, the account name and the apikey') if @arguments.size < 3 # lol boobs, err... an ice cone!
|
23
|
+
write_configuration({:account => @arguments[1], :apikey => @arguments[2]})
|
24
|
+
tell("Could't set up bash completion. I'm terribly frustrated. Maybe 'mite help' helps out.") unless try_to_setup_bash_completion
|
25
|
+
|
26
|
+
elsif @arguments.first == 'auto-complete'
|
27
|
+
autocomplete = MightyMite::Autocomplete.new(MightyMite.calling_script)
|
28
|
+
autocomplete.completion_table = if File.exist?(cache_file)
|
29
|
+
Marshal.load File.read(cache_file)
|
30
|
+
else
|
31
|
+
rebuild_completion_table
|
32
|
+
end
|
33
|
+
autocomplete.suggestions.map(&:quote_if_spaced).each { |s| tell s }
|
34
|
+
|
35
|
+
elsif @arguments.first == 'rebuild-cache'
|
36
|
+
File.delete(cache_file) if File.exist? cache_file
|
37
|
+
rebuild_completion_table
|
38
|
+
tell 'The rebuilding of the cache has been done, Master. Your wish is my command.'
|
39
|
+
|
40
|
+
elsif ['today', 'yesterday', 'this_week', 'last_week', 'this_month', 'last_month'].include? @arguments.first
|
41
|
+
total_revenue = Mite::TimeEntry.all(:params => {:at => @arguments.first, :user_id => 'current'}).each do |time_entry|
|
42
|
+
tell time_entry.inspect
|
43
|
+
end.map(&:revenue).compact.sum
|
44
|
+
tell ("%.2f $" % total_revenue).colorize(:lightgreen)
|
45
|
+
|
46
|
+
elsif ['stop', 'pause'].include? @arguments.first
|
47
|
+
if current_tracker = (Mite::Tracker.current ? Mite::Tracker.current.stop : nil)
|
48
|
+
tell current_tracker.time_entry.inspect
|
49
|
+
end
|
50
|
+
|
51
|
+
elsif @arguments.first == 'start'
|
52
|
+
if time_entry = Mite::TimeEntry.first(:params => {:at => 'today'})
|
53
|
+
time_entry.start_tracker
|
54
|
+
tell time_entry.inspect
|
55
|
+
else
|
56
|
+
tell "Oh my dear! I tried hard, but I could'nt find any time entry for today."
|
57
|
+
end
|
58
|
+
|
59
|
+
elsif (1..4).include?(@arguments.size)
|
60
|
+
attributes = {}
|
61
|
+
if time_string = @arguments.select { |a| a =~ TIME_FORMAT }.first
|
62
|
+
attributes[:minutes] = parse_minutes(time_string) unless time_string == '+'
|
63
|
+
start_tracker = (time_string =~ /\+$/)
|
64
|
+
end
|
65
|
+
if project = find_or_create_project(@arguments.first)
|
66
|
+
attributes[:project_id] = project.id
|
67
|
+
end
|
68
|
+
if @arguments[1] && service = find_or_create_service(@arguments[1])
|
69
|
+
attributes[:service_id] = service.id
|
70
|
+
end
|
71
|
+
if note = parse_note(@arguments, time_string)
|
72
|
+
attributes[:note] = note
|
73
|
+
end
|
74
|
+
time_entry = Mite::TimeEntry.create attributes
|
75
|
+
time_entry.start_tracker if start_tracker
|
76
|
+
tell time_entry.inspect
|
77
|
+
|
78
|
+
elsif @arguments.size == 0
|
79
|
+
tell Mite::Tracker.current ? Mite::Tracker.current.inspect : flirt
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def say(what)
|
84
|
+
puts what
|
85
|
+
end
|
86
|
+
alias_method :tell, :say
|
87
|
+
|
88
|
+
def flirt
|
89
|
+
FLIRTS[rand(FLIRTS.size)]
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def find_or_create_project(name)
|
95
|
+
project = Mite::Project.first(:params => {:name => name})
|
96
|
+
return nil if name =~ TIME_FORMAT
|
97
|
+
project ? project : Mite::Project.create(:params => {:name => name})
|
98
|
+
end
|
99
|
+
|
100
|
+
def find_or_create_service(name)
|
101
|
+
service = Mite::Service.first(:params => {:name => name})
|
102
|
+
return nil if name =~ TIME_FORMAT
|
103
|
+
service ? service : Mite::Service.create(:params => {:name => name})
|
104
|
+
end
|
105
|
+
|
106
|
+
def parse_note(args, time_string)
|
107
|
+
if args[3]
|
108
|
+
args[3]
|
109
|
+
elsif time_string.nil? && args[2]
|
110
|
+
args[2]
|
111
|
+
elsif time_string && args[args.index(time_string)+1]
|
112
|
+
args[args.index(time_string)+1]
|
113
|
+
else
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def parse_minutes(string)
|
119
|
+
string = string.sub(/\+$/, '')
|
120
|
+
if string =~ /^\d+:\d+$/
|
121
|
+
string.split(':').first.to_i*60 + string.split(':').last.to_i
|
122
|
+
elsif string =~ /^\d+(\.\d+)?:?$/
|
123
|
+
(string.to_f*60).to_i
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def rebuild_completion_table
|
128
|
+
completion_table = {
|
129
|
+
0 => Mite::Project.all.map(&:name),
|
130
|
+
1 => Mite::Service.all.map(&:name),
|
131
|
+
2 => ['0:05', '0:05+', '0:15', '0:15+', '0:30', '0:30+', '1:00', '1:00+'].map(&:quote),
|
132
|
+
3 => Mite::TimeEntry.all.map(&:note).compact
|
133
|
+
}
|
134
|
+
File.open(cache_file, 'w') { |f| Marshal.dump(completion_table, f) }
|
135
|
+
completion_table
|
136
|
+
end
|
137
|
+
|
138
|
+
def cache_file
|
139
|
+
File.expand_path('~/.mite.cache')
|
140
|
+
end
|
141
|
+
|
142
|
+
def open_or_echo(open_argument)
|
143
|
+
exec "open '#{open_argument}' || echo '#{open_argument}'"
|
144
|
+
end
|
145
|
+
|
146
|
+
def write_configuration(config)
|
147
|
+
File.open(File.expand_path('~/.mite.yml'), 'w') do |f|
|
148
|
+
YAML.dump(config, f)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def try_to_setup_bash_completion
|
153
|
+
bash_code = "\n\n#{MightyMite::BASH_COMPLETION}"
|
154
|
+
|
155
|
+
['~/.bash_completion', '~/.bash_profile', '~/.bash_login', '~/.bashrc'].each do |file|
|
156
|
+
bash_config_file = File.expand_path file
|
157
|
+
next unless File.exist?(bash_config_file)
|
158
|
+
unless File.read(bash_config_file) =~ /#{bash_code}/
|
159
|
+
File.open(bash_config_file, 'a') do |f|
|
160
|
+
f.puts bash_code
|
161
|
+
end
|
162
|
+
return true
|
163
|
+
end
|
164
|
+
end
|
165
|
+
return false
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module MightyMite
|
4
|
+
class Autocomplete
|
5
|
+
include Shellwords
|
6
|
+
|
7
|
+
attr_accessor :completion_table
|
8
|
+
attr_reader :calling_script
|
9
|
+
|
10
|
+
def initialize(calling_script)
|
11
|
+
@calling_script = calling_script
|
12
|
+
end
|
13
|
+
|
14
|
+
def bash_line
|
15
|
+
ENV['COMP_LINE'].to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def argument_string
|
19
|
+
bash_line.sub(/^(.*)#{File.basename calling_script}\s*/, '').close_unmatched_quotes
|
20
|
+
end
|
21
|
+
|
22
|
+
def partial_argument_string
|
23
|
+
bash_line[0..cursor_position+1].sub(/^(.*)#{File.basename calling_script}\s*/, '').close_unmatched_quotes
|
24
|
+
end
|
25
|
+
|
26
|
+
def current_word
|
27
|
+
return nil if argument_string =~ /\s$/ && bash_line.length == cursor_position
|
28
|
+
shellwords(partial_argument_string).last
|
29
|
+
end
|
30
|
+
|
31
|
+
def current_argument_index
|
32
|
+
return args.size if argument_string =~ /\s$/ && bash_line.length == cursor_position
|
33
|
+
args.index(current_word) || 0
|
34
|
+
end
|
35
|
+
|
36
|
+
def cursor_position
|
37
|
+
ENV['COMP_POINT'].to_i
|
38
|
+
end
|
39
|
+
|
40
|
+
def args
|
41
|
+
shellwords(argument_string)
|
42
|
+
end
|
43
|
+
|
44
|
+
def suggestions
|
45
|
+
completion_table[current_argument_index] ? completion_table[current_argument_index].select {|s| s =~ /^#{current_word}/} : []
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/mighty_mite.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
Dir[File.join(File.dirname(__FILE__), *%w[.. vendor * lib])].each do |path|
|
3
|
+
$LOAD_PATH.unshift path
|
4
|
+
end
|
5
|
+
require 'mite-rb'
|
6
|
+
|
7
|
+
require 'string_ext'
|
8
|
+
require 'mite_ext'
|
9
|
+
require 'mighty_mite/application'
|
10
|
+
require 'mighty_mite/autocomplete'
|
11
|
+
|
12
|
+
module MightyMite
|
13
|
+
BASH_COMPLETION = "complete -C \"mite auto-complete\" mite"
|
14
|
+
|
15
|
+
CONFIG_FILE = File.expand_path '~/.mite.yml'
|
16
|
+
|
17
|
+
mattr_accessor :calling_script
|
18
|
+
|
19
|
+
def self.load_configuration
|
20
|
+
if File.exist?(CONFIG_FILE)
|
21
|
+
configuration = YAML.load(File.read(CONFIG_FILE))
|
22
|
+
Mite.account = configuration[:account]
|
23
|
+
Mite.key = configuration[:apikey]
|
24
|
+
else
|
25
|
+
raise Exception.new("Configuration file is missing.")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.run(args)
|
30
|
+
Application.new(args).run
|
31
|
+
end
|
32
|
+
|
33
|
+
class Exception < StandardError; end
|
34
|
+
end
|
data/lib/mite_ext.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
Mite.class_eval do
|
2
|
+
def self.account_url
|
3
|
+
host_format % [protocol, domain_format % account, (port.blank? ? '' : ":#{port}")]
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
Mite::TimeEntry.class_eval do
|
8
|
+
# I need the full class path, otherwise there will be LoadErrors
|
9
|
+
def service
|
10
|
+
@service ||= Mite::Service.find(service_id) unless service_id.blank?
|
11
|
+
end
|
12
|
+
|
13
|
+
# I need the full class path, otherwise there will be LoadErrors
|
14
|
+
def project
|
15
|
+
@project ||= Mite::Project.find(project_id) unless project_id.blank?
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
output = []
|
20
|
+
output << formatted_time.colorize(tracking? ? :lightyellow : :lightred)
|
21
|
+
output << formatted_revenue.colorize(:lightgreen) if revenue
|
22
|
+
output << "\tdoing #{service.name}" if service
|
23
|
+
output << "\tfor #{project.name}" if project
|
24
|
+
output << "\n\t\t|_ #{note}" unless note.blank?
|
25
|
+
output.join(' ')
|
26
|
+
end
|
27
|
+
|
28
|
+
def formatted_revenue
|
29
|
+
revenue.nil? ? '' : "%.2f $" % (revenue / 100.0)
|
30
|
+
end
|
31
|
+
|
32
|
+
def formatted_time
|
33
|
+
minutes = tracking? ? tracker.minutes : self.minutes
|
34
|
+
if minutes > 59
|
35
|
+
h = minutes/60
|
36
|
+
m = minutes-h*60
|
37
|
+
"#{h}:%.2d" % m
|
38
|
+
else
|
39
|
+
"0:%.2d" % minutes
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
Mite::Tracker.class_eval do
|
46
|
+
def time_entry
|
47
|
+
@time_entry ||= Mite::TimeEntry.find(id)
|
48
|
+
end
|
49
|
+
|
50
|
+
def inspect
|
51
|
+
time_entry.inspect
|
52
|
+
end
|
53
|
+
end
|
data/lib/string_ext.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
String.class_eval do
|
2
|
+
def quote(q="\"")
|
3
|
+
"#{q}#{self}#{q}"
|
4
|
+
end
|
5
|
+
|
6
|
+
def quote_if_spaced(q="\"")
|
7
|
+
self =~ /\s/ ? self.quote : self
|
8
|
+
end
|
9
|
+
|
10
|
+
def close_unmatched_quotes(q="\"")
|
11
|
+
(self.scan(/"/).size % 2) != 0 ? self+q : self
|
12
|
+
end
|
13
|
+
|
14
|
+
BASH_COLOR = {
|
15
|
+
:black => 30,
|
16
|
+
:red => 31,
|
17
|
+
:green => 32,
|
18
|
+
:yellow => 33,
|
19
|
+
:blue => 34,
|
20
|
+
:magenta => 35,
|
21
|
+
:cyan => 36,
|
22
|
+
:white => 37,
|
23
|
+
:default => 39
|
24
|
+
}
|
25
|
+
BASH_EFFECT = {
|
26
|
+
:none => 0,
|
27
|
+
:bright => 1,
|
28
|
+
:underline => 4,
|
29
|
+
:blink => 5,
|
30
|
+
:exchange => 7,
|
31
|
+
:hidden => 8
|
32
|
+
}
|
33
|
+
def colorize(options={})
|
34
|
+
return '' if self == ''
|
35
|
+
options = {:color => options} if options.is_a?(Symbol)
|
36
|
+
options[:color] = :default unless options[:color]
|
37
|
+
if options[:color] != :default && options[:color].to_s =~ /^(light|bright)/
|
38
|
+
options[:color] = options[:color].to_s.sub(/^(light|bright)/, '').to_sym
|
39
|
+
options[:effect] = :bright
|
40
|
+
end
|
41
|
+
options[:background] = :default unless options[:background]
|
42
|
+
options[:effect] = :none unless options[:effect]
|
43
|
+
|
44
|
+
effect_code = "#{BASH_EFFECT[options[:effect]]}"
|
45
|
+
background_code = "#{BASH_COLOR[options[:background]]+10}m"
|
46
|
+
color_code = "#{BASH_COLOR[options[:color]]}"
|
47
|
+
reset_code = "\e[0m"
|
48
|
+
"\e[#{effect_code};#{color_code};#{background_code}#{self}#{reset_code}"
|
49
|
+
end
|
50
|
+
end
|
data/mighty-mite.gemspec
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{mighty-mite}
|
5
|
+
s.version = "0.1.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Lukas Rieder"]
|
9
|
+
s.date = %q{2009-05-22}
|
10
|
+
s.default_executable = %q{mite}
|
11
|
+
s.description = %q{A simple command line interface for mite, a sleek time tracking webapp.}
|
12
|
+
s.email = %q{l.rieder@gmail.com}
|
13
|
+
s.executables = ["mite"]
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"LICENSE",
|
16
|
+
"README.textile"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".gitignore",
|
20
|
+
"LICENSE",
|
21
|
+
"README.textile",
|
22
|
+
"Rakefile",
|
23
|
+
"TODO",
|
24
|
+
"VERSION",
|
25
|
+
"bin/mite",
|
26
|
+
"lib/mighty_mite.rb",
|
27
|
+
"lib/mighty_mite/application.rb",
|
28
|
+
"lib/mighty_mite/autocomplete.rb",
|
29
|
+
"lib/mite_ext.rb",
|
30
|
+
"lib/string_ext.rb",
|
31
|
+
"mighty-mite.gemspec",
|
32
|
+
"spec/spec_helper.rb",
|
33
|
+
"spec/unit/mighty_mite/application_spec.rb",
|
34
|
+
"spec/unit/mighty_mite/autocomplete_spec.rb",
|
35
|
+
"spec/unit/mighty_mite_spec.rb",
|
36
|
+
"spec/unit/mite_ext_spec.rb",
|
37
|
+
"spec/unit/string_ext_spec.rb",
|
38
|
+
"vendor/yolk-mite-rb-0.0.3/CHANGES.txt",
|
39
|
+
"vendor/yolk-mite-rb-0.0.3/LICENSE",
|
40
|
+
"vendor/yolk-mite-rb-0.0.3/README.textile",
|
41
|
+
"vendor/yolk-mite-rb-0.0.3/Rakefile",
|
42
|
+
"vendor/yolk-mite-rb-0.0.3/VERSION.yml",
|
43
|
+
"vendor/yolk-mite-rb-0.0.3/lib/mite-rb.rb",
|
44
|
+
"vendor/yolk-mite-rb-0.0.3/lib/mite/customer.rb",
|
45
|
+
"vendor/yolk-mite-rb-0.0.3/lib/mite/project.rb",
|
46
|
+
"vendor/yolk-mite-rb-0.0.3/lib/mite/service.rb",
|
47
|
+
"vendor/yolk-mite-rb-0.0.3/lib/mite/time_entry.rb",
|
48
|
+
"vendor/yolk-mite-rb-0.0.3/lib/mite/time_entry_group.rb",
|
49
|
+
"vendor/yolk-mite-rb-0.0.3/lib/mite/tracker.rb",
|
50
|
+
"vendor/yolk-mite-rb-0.0.3/lib/mite/user.rb"
|
51
|
+
]
|
52
|
+
s.has_rdoc = true
|
53
|
+
s.homepage = %q{http://github.com/Overbryd/mighty-mite}
|
54
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
55
|
+
s.require_paths = ["lib"]
|
56
|
+
s.rubygems_version = %q{1.3.1}
|
57
|
+
s.summary = %q{A simple command line interface for basic mite tasks.}
|
58
|
+
s.test_files = [
|
59
|
+
"spec/spec_helper.rb",
|
60
|
+
"spec/unit/mighty_mite/application_spec.rb",
|
61
|
+
"spec/unit/mighty_mite/autocomplete_spec.rb",
|
62
|
+
"spec/unit/mighty_mite_spec.rb",
|
63
|
+
"spec/unit/mite_ext_spec.rb",
|
64
|
+
"spec/unit/string_ext_spec.rb"
|
65
|
+
]
|
66
|
+
|
67
|
+
if s.respond_to? :specification_version then
|
68
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
69
|
+
s.specification_version = 2
|
70
|
+
|
71
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
72
|
+
else
|
73
|
+
end
|
74
|
+
else
|
75
|
+
end
|
76
|
+
end
|