mite.cmd 0.1.10

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,5 @@
1
+ *.gem
2
+ pkg
3
+ doc
4
+ coverage
5
+ .DS_Store
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.
@@ -0,0 +1,162 @@
1
+ <pre>
2
+
3
+ _ _
4
+ (_) | | |
5
+ _ __ ___ _| |_ ___ ___ _ __ ___ __| |
6
+ | '_ ` _ \| | __/ _ \ / __| '_ ` _ \ / _` |
7
+ | | | | | | | || __/| (__| | | | | | (_| |
8
+ |_| |_| |_|_|\__\___(_)___|_| |_| |_|\__,_|
9
+
10
+ </pre>
11
+ A simple command line interface for basic mite tasks.
12
+
13
+ * @mite.@ is an ingeniously sleek time tracking tool: http://mite.yo.lk
14
+ * @mite.cmd@ is a command line interface for it: http://github.com/Overbryd/mite.cmd
15
+ It provides a system wide command called @mite@.
16
+
17
+ h3. Installation instructions:
18
+
19
+ $ gem install Overbryd-mite.cmd
20
+
21
+ I assume github is in your gem sources. You may need @activesupport@ and @activeresource@ too.
22
+
23
+ $ gem install activesupport activeresource
24
+
25
+ h3. After installation instructions:
26
+
27
+ You'll need to configure the client prior using it.
28
+ To do this, you can just hammer into your console:
29
+
30
+ $ mite configure "Your Account Name" "Your API Key"
31
+
32
+ It will then generate a yml file in ~/.mite.yml with your account information.
33
+
34
+ The configure command also tries to install the bash auto completion hook into the following files: @~/.bash_completion@, @~/.bash_profile@, @~/.bash_login@, @~/.bashrc@.
35
+
36
+ If your system doesn't support one of the files above, install the hook by yourself.
37
+ In order to work as expected it needs bash to be configured for auto completion.
38
+ This is actually quite easy, just append this line to your bash config file.
39
+
40
+ complete -C "mite auto-complete" mite
41
+
42
+ You could use this command as an example (replace .bash_login with your bash configuration file):
43
+
44
+ $ echo "complete -C \"mite auto-complete\" mite" >> ~/.bash_login
45
+
46
+ h3. Create new time entries with ease:
47
+
48
+ $ mite "project name" "service name" "start time" "note"
49
+
50
+ $ mite "project name" "service name" "note"
51
+
52
+ $ mite "project name" "start time" "note"
53
+
54
+ $ mite "start time" "note"
55
+
56
+ h4. The magic of the moment err... start_time:
57
+
58
+ Append a @+@ to the start time and it will start the tracker.
59
+
60
+ 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@.
61
+
62
+ Short-hand for hours:
63
+ * @3+@ will set the start time to 3 hours, and start the tracker
64
+ * @3.5@ will set it to 3 hours 30 minutes
65
+ * @0.25+@ will make you look like a floating-point fetish, and start the tracker
66
+
67
+ h4. Examples:
68
+
69
+ $ mite "World Domination"
70
+
71
+ This will start a timer for the project World Domination.
72
+
73
+ $ mite HugEveryone 0:15
74
+
75
+ The time entry created by this command, is made for the project HugEveryone and is set to 15 minutes. No tracker will be started.
76
+
77
+ $ mite HugEveryone Love 1+
78
+
79
+ Start a time entry for the project HugEveryone with the service Love and set it to 1 hour. The tracker will be started, because of the @start time@ argument was suffixed with a @+@.
80
+
81
+ h4. Note:
82
+
83
+ If a project or a service doesn't exist it will be created.
84
+ You can omit one/some of the arguments, namely: @project name@, @service name@, @note@ or @start time@. But they have to be in order. So you can't set a @service name@ unless you specify a project. This will be fixed in future releases, till then, the arguments rely on their order.
85
+
86
+ h3. Amazing auto-completion:
87
+
88
+ This is very nifty. (But maybe you should read the After Installation Instructions before.)
89
+ The client was designed to save keystrokes, so I've baked in a very handy auto-completion feature. It even ignores the typed case.
90
+
91
+ Try this:
92
+
93
+ $ mite [tab]
94
+
95
+ It will try to auto-complete your projects.
96
+
97
+ $ mite Project1 [tab]
98
+
99
+ It will try to auto-complete your services.
100
+
101
+ $ mite Project1 "System Administration" [tab]
102
+
103
+ It will try to auto-complete common times!
104
+
105
+ $ mite Project1 "System Administration" 0:48 [tab]
106
+
107
+ It will try to auto-complete your notes!
108
+
109
+ h4. Amazingly fast auto-completion:
110
+
111
+ The auto-completion feature creates a cache in ~/.mite.cache, if you want to rebuild this cache just hit:
112
+
113
+ $ mite rebuild-cache
114
+
115
+ h4. Note: The first run without the cache might be a bit slow.
116
+
117
+ h3. Controlling timers:
118
+
119
+ $ mite start
120
+
121
+ This little cutey will start today's last time entry, if there is one.
122
+
123
+ $ mite stop
124
+
125
+ This will just stop the current timer. (If you like you can use `mite pause` or `mite lunch` too)
126
+
127
+ h3. Simple reports:
128
+
129
+ $ mite today
130
+
131
+ This will generate a report of today's activity, summarizing your earnings at the bottom.
132
+
133
+ $ mite yesterday
134
+
135
+ This will generate a report of yesterday's activity, summarizing your earnings and the total time at the bottom.
136
+ Also works using @this_week@, @last_week@, @this_month@, @last_month@ as argument.
137
+
138
+ h3. More simple stuff:
139
+
140
+ $ mite
141
+
142
+ If there is a running timer, it will output it. Otherwise you should better not listen to it.
143
+
144
+ $ mite +
145
+
146
+ Just create a new time entry and start tracking it.
147
+
148
+ $ mite open
149
+
150
+ Opens your mite account in a new browser window (Max OSX only) or prints the url to your account.
151
+
152
+ h3. Feature Request? Problems? Found a bug? Your girlfriend broke up with you?
153
+
154
+ Your problems are no longer "problems"! They are called issues now, and github has the solution.
155
+
156
+ If you want to tell me an idea, I'll happily take care of them in the issues tracker: http://github.com/Overbryd/mite.cmd/issues
157
+
158
+ |\3. **All time Highscore of people who were involved** |
159
+ | who? |\2. what? |
160
+ | Sebastian Munz (yolk) | 12 issues | found a lot of bugs, did some real use testing. Really cool, thank you! |
161
+ | Julia Soergel (juliasoergel) | 2 issues | found a bug and `I like your hairstyle!´ |
162
+ | Michael Bumann (burmi) | 2 issues | found two bugs and mentioned a security issue which is now fixed. Yea baby! |
@@ -0,0 +1,33 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "mite.cmd"
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/mite.cmd"
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
+ task :spec do
20
+ spec_files = Dir.glob('spec/**/*_spec.rb').join(' ')
21
+ sh "spec #{spec_files} --format specdoc --color"
22
+ end
23
+ task :default => :spec
24
+
25
+ task :rcov do
26
+ sh "rake run_rcov && open coverage/index.html"
27
+ end
28
+ require 'spec/rake/spectask'
29
+ Spec::Rake::SpecTask.new(:run_rcov) do |spec|
30
+ spec.libs << 'lib' << 'spec'
31
+ spec.pattern = 'spec/**/*_spec.rb'
32
+ spec.rcov = true
33
+ end
data/TODO ADDED
@@ -0,0 +1,4 @@
1
+ * Make it soft around the edges
2
+ * Improve auto-completion caching
3
+ * Waiting for some feedback
4
+ * Implementing feedback
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.10
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
4
+
5
+ require 'mite_cmd'
6
+
7
+ begin
8
+ MiteCmd.calling_script = __FILE__
9
+ MiteCmd.run(ARGV)
10
+ rescue MiteCmd::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,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 'mite_cmd/application'
10
+ require 'mite_cmd/autocomplete'
11
+
12
+ module MiteCmd
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
@@ -0,0 +1,180 @@
1
+ require 'ftools'
2
+
3
+ module MiteCmd
4
+ class Application
5
+ TIME_FORMAT = /^(\d+(\.\d+)?:?\+?)|(\d+:\d+\+?)|\+$/
6
+ FLIRTS = [
7
+ 'I like your hairstyle.', 'What a nice console you have.', 'My favorite color is red on black, monospaced.',
8
+ "What a lovely operation system this #{`uname`} is.", 'What about dinner tonight?', 'Your keystrokes are tingling.'
9
+ ]
10
+
11
+ def initialize(arguments=[])
12
+ @arguments = arguments
13
+ MiteCmd.load_configuration unless ['configure', 'help'].include?(arguments.first)
14
+ end
15
+
16
+ def run
17
+ if @arguments.first == 'open'
18
+ open_or_echo Mite.account_url
19
+
20
+ elsif @arguments.first == 'help'
21
+ open_or_echo 'http://github.com/Overbryd/mite.cmd'
22
+
23
+ elsif @arguments.first == 'configure'
24
+ raise MiteCmd::Exception.new('mite configure needs two arguments, the account name and the apikey') if @arguments.size < 3 # lol boobs, err... an ice cone!
25
+ write_configuration({:account => @arguments[1], :apikey => @arguments[2]})
26
+ tell("Couldn't set up bash completion. I'm terribly frustrated. Maybe 'mite help' helps out.") unless try_to_setup_bash_completion
27
+
28
+ elsif @arguments.first == 'auto-complete'
29
+ autocomplete = MiteCmd::Autocomplete.new(MiteCmd.calling_script)
30
+ autocomplete.completion_table = if File.exist?(cache_file)
31
+ Marshal.load File.read(cache_file)
32
+ else
33
+ rebuild_completion_table
34
+ end
35
+ autocomplete.suggestions.map(&:quote_if_spaced).each { |s| tell s }
36
+
37
+ elsif @arguments.first == 'rebuild-cache'
38
+ File.delete(cache_file) if File.exist? cache_file
39
+ rebuild_completion_table
40
+ tell 'The rebuilding of the cache has been done, Master. Your wish is my command.'
41
+
42
+ elsif ['today', 'yesterday', 'this_week', 'last_week', 'this_month', 'last_month'].include? @arguments.first
43
+ total_minutes = 0
44
+ total_revenue = Mite::TimeEntry.all(:params => {:at => @arguments.first, :user_id => 'current'}).each do |time_entry|
45
+ total_minutes += time_entry.minutes
46
+ tell time_entry.inspect
47
+ end.map(&:revenue).compact.sum
48
+ tell ("%s:%.2d" % [total_minutes/60, total_minutes-total_minutes/60*60]).colorize(:lightred) + ", " + ("%.2f $" % (total_revenue/100)).colorize(:lightgreen)
49
+
50
+ elsif ['stop', 'pause', 'lunch'].include? @arguments.first
51
+ if current_tracker = (Mite::Tracker.current ? Mite::Tracker.current.stop : nil)
52
+ tell current_tracker.time_entry.inspect
53
+ end
54
+
55
+ elsif @arguments.first == 'start'
56
+ if time_entry = Mite::TimeEntry.first(:params => {:at => 'today'})
57
+ time_entry.start_tracker
58
+ tell time_entry.inspect
59
+ else
60
+ tell "Oh my dear! I tried hard, but I could'nt find any time entry for today."
61
+ end
62
+
63
+ elsif (1..4).include?(@arguments.size)
64
+ attributes = {}
65
+ if time_string = @arguments.select { |a| a =~ TIME_FORMAT }.first
66
+ attributes[:minutes] = parse_minutes(time_string)
67
+ start_tracker = (time_string =~ /\+$/)
68
+ end
69
+ if project = find_or_create_project(@arguments.first)
70
+ attributes[:project_id] = project.id
71
+ end
72
+ if @arguments[1] && service = find_or_create_service(@arguments[1])
73
+ attributes[:service_id] = service.id
74
+ end
75
+ if note = parse_note(@arguments, time_string)
76
+ attributes[:note] = note
77
+ end
78
+ time_entry = Mite::TimeEntry.create attributes
79
+ time_entry.start_tracker if start_tracker
80
+ tell time_entry.inspect
81
+
82
+ elsif @arguments.size == 0
83
+ tell Mite::Tracker.current ? Mite::Tracker.current.inspect : flirt
84
+ end
85
+ end
86
+
87
+ def say(what)
88
+ puts what
89
+ end
90
+ alias_method :tell, :say
91
+
92
+ def flirt
93
+ FLIRTS[rand(FLIRTS.size)]
94
+ end
95
+
96
+ private
97
+
98
+ def find_or_create_project(name)
99
+ project = Mite::Project.first(:params => {:name => name})
100
+ return nil if name =~ TIME_FORMAT
101
+ project ? project : Mite::Project.create(:name => name)
102
+ end
103
+
104
+ def find_or_create_service(name)
105
+ service = Mite::Service.first(:params => {:name => name})
106
+ return nil if name =~ TIME_FORMAT
107
+ service ? service : Mite::Service.create(:name => name)
108
+ end
109
+
110
+ def parse_note(args, time_string)
111
+ if args[3]
112
+ args[3]
113
+ elsif time_string.nil? && args[2]
114
+ args[2]
115
+ elsif time_string && args[args.index(time_string)+1]
116
+ args[args.index(time_string)+1]
117
+ else
118
+ nil
119
+ end
120
+ end
121
+
122
+ def parse_minutes(string)
123
+ string = string.sub(/\+$/, '')
124
+
125
+ if string.blank?
126
+ 0
127
+ elsif string =~ /^\d+:\d+$/
128
+ string.split(':').first.to_i*60 + string.split(':').last.to_i
129
+ elsif string =~ /^\d+(\.\d+)?:?$/
130
+ (string.to_f*60).to_i
131
+ end
132
+ end
133
+
134
+ def rebuild_completion_table
135
+ completion_table = {
136
+ 0 => Mite::Project.all.map(&:name),
137
+ 1 => Mite::Service.all.map(&:name),
138
+ 2 => ['0:05', '0:05+', '0:15', '0:15+', '0:30', '0:30+', '1:00', '1:00+'].map(&:quote),
139
+ 3 => Mite::TimeEntry.all.map(&:note).compact
140
+ }
141
+ File.open(cache_file, 'w') { |f| Marshal.dump(completion_table, f) }
142
+ File.chmod(0600, cache_file)
143
+ completion_table
144
+ end
145
+
146
+ def cache_file
147
+ File.expand_path('~/.mite.cache')
148
+ end
149
+
150
+ def open_or_echo(open_argument)
151
+ exec "open '#{open_argument}' || echo '#{open_argument}'"
152
+ end
153
+
154
+ def write_configuration(config)
155
+ File.open(File.expand_path('~/.mite.yml'), 'w') do |f|
156
+ YAML.dump(config, f)
157
+ end
158
+ File.chmod(0600, File.expand_path('~/.mite.yml'))
159
+ end
160
+
161
+ def try_to_setup_bash_completion
162
+ bash_code = "\n\n#{MiteCmd::BASH_COMPLETION}"
163
+
164
+ ['~/.bash_completion', '~/.bash_profile', '~/.bash_login', '~/.bashrc'].each do |file|
165
+ bash_config_file = File.expand_path file
166
+ next unless File.file?(bash_config_file)
167
+ unless File.read(bash_config_file) =~ /#{bash_code}/
168
+ File.open(bash_config_file, 'a') do |f|
169
+ f.puts bash_code
170
+ end
171
+ return true
172
+ else
173
+ return true
174
+ end
175
+ end
176
+ return false
177
+ end
178
+
179
+ end
180
+ end