tt 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.
- data/README +21 -0
- data/Rakefile +390 -0
- data/bin/tt +226 -0
- data/lib/tt.rb +158 -0
- data/tt.gemspec +34 -0
- metadata +83 -0
data/README
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Treetop Parsing Expression Grammar (PEG) Comand Line Compiler
|
2
|
+
Usage: tt [options] grammar_file[.treetop|.tt] ...
|
3
|
+
|
4
|
+
Examples:
|
5
|
+
tt foo.tt # 1 grammar -> 1 parser source
|
6
|
+
tt foo bar.treetop # 2 grammars -> 2 separate parsers
|
7
|
+
tt -o alt_name.rb foo # alternately named output file
|
8
|
+
|
9
|
+
|
10
|
+
NOTE: while treetop grammar files *must* have one of the following
|
11
|
+
filename extensions, the extension name is not required when calling
|
12
|
+
the compiler with grammar file names.
|
13
|
+
|
14
|
+
Valid extensions: .treetop, .tt
|
15
|
+
|
16
|
+
|
17
|
+
Options:
|
18
|
+
-o, --output FILENAME Write parser source to FILENAME
|
19
|
+
-f, --force Overwrite existing output file(s)
|
20
|
+
-v, --version Show Treetop version
|
21
|
+
-h, --help Show this help message
|
data/Rakefile
ADDED
@@ -0,0 +1,390 @@
|
|
1
|
+
This.rubyforge_project = 'codeforpeople'
|
2
|
+
This.author = "Ara T. Howard"
|
3
|
+
This.email = "ara.t.howard@gmail.com"
|
4
|
+
This.homepage = "https://github.com/ahoward/#{ This.lib }"
|
5
|
+
|
6
|
+
|
7
|
+
task :default do
|
8
|
+
puts((Rake::Task.tasks.map{|task| task.name.gsub(/::/,':')} - ['default']).sort)
|
9
|
+
end
|
10
|
+
|
11
|
+
task :test do
|
12
|
+
run_tests!
|
13
|
+
end
|
14
|
+
|
15
|
+
namespace :test do
|
16
|
+
task(:unit){ run_tests!(:unit) }
|
17
|
+
task(:functional){ run_tests!(:functional) }
|
18
|
+
task(:integration){ run_tests!(:integration) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def run_tests!(which = nil)
|
22
|
+
which ||= '**'
|
23
|
+
test_dir = File.join(This.dir, "test")
|
24
|
+
test_glob ||= File.join(test_dir, "#{ which }/**_test.rb")
|
25
|
+
test_rbs = Dir.glob(test_glob).sort
|
26
|
+
|
27
|
+
div = ('=' * 119)
|
28
|
+
line = ('-' * 119)
|
29
|
+
|
30
|
+
test_rbs.each_with_index do |test_rb, index|
|
31
|
+
testno = index + 1
|
32
|
+
command = "#{ File.basename(This.ruby) } -I ./lib -I ./test/lib #{ test_rb }"
|
33
|
+
|
34
|
+
puts
|
35
|
+
say(div, :color => :cyan, :bold => true)
|
36
|
+
say("@#{ testno } => ", :bold => true, :method => :print)
|
37
|
+
say(command, :color => :cyan, :bold => true)
|
38
|
+
say(line, :color => :cyan, :bold => true)
|
39
|
+
|
40
|
+
system(command)
|
41
|
+
|
42
|
+
say(line, :color => :cyan, :bold => true)
|
43
|
+
|
44
|
+
status = $?.exitstatus
|
45
|
+
|
46
|
+
if status.zero?
|
47
|
+
say("@#{ testno } <= ", :bold => true, :color => :white, :method => :print)
|
48
|
+
say("SUCCESS", :color => :green, :bold => true)
|
49
|
+
else
|
50
|
+
say("@#{ testno } <= ", :bold => true, :color => :white, :method => :print)
|
51
|
+
say("FAILURE", :color => :red, :bold => true)
|
52
|
+
end
|
53
|
+
say(line, :color => :cyan, :bold => true)
|
54
|
+
|
55
|
+
exit(status) unless status.zero?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
task :gemspec do
|
61
|
+
ignore_extensions = ['git', 'svn', 'tmp', /sw./, 'bak', 'gem']
|
62
|
+
ignore_directories = ['pkg']
|
63
|
+
ignore_files = ['test/log', 'a.rb'] + Dir['db/*'] + %w'db'
|
64
|
+
|
65
|
+
shiteless =
|
66
|
+
lambda do |list|
|
67
|
+
list.delete_if do |entry|
|
68
|
+
next unless test(?e, entry)
|
69
|
+
extension = File.basename(entry).split(%r/[.]/).last
|
70
|
+
ignore_extensions.any?{|ext| ext === extension}
|
71
|
+
end
|
72
|
+
list.delete_if do |entry|
|
73
|
+
next unless test(?d, entry)
|
74
|
+
dirname = File.expand_path(entry)
|
75
|
+
ignore_directories.any?{|dir| File.expand_path(dir) == dirname}
|
76
|
+
end
|
77
|
+
list.delete_if do |entry|
|
78
|
+
next unless test(?f, entry)
|
79
|
+
filename = File.expand_path(entry)
|
80
|
+
ignore_files.any?{|file| File.expand_path(file) == filename}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
lib = This.lib
|
85
|
+
object = This.object
|
86
|
+
version = This.version
|
87
|
+
files = shiteless[Dir::glob("**/**")]
|
88
|
+
executables = shiteless[Dir::glob("bin/*")].map{|exe| File.basename(exe)}
|
89
|
+
#has_rdoc = true #File.exist?('doc')
|
90
|
+
test_files = test(?e, "test/#{ lib }.rb") ? "test/#{ lib }.rb" : nil
|
91
|
+
summary = object.respond_to?(:summary) ? object.summary : "summary: #{ lib } kicks the ass"
|
92
|
+
description = object.respond_to?(:description) ? object.description : "description: #{ lib } kicks the ass"
|
93
|
+
|
94
|
+
if This.extensions.nil?
|
95
|
+
This.extensions = []
|
96
|
+
extensions = This.extensions
|
97
|
+
%w( Makefile configure extconf.rb ).each do |ext|
|
98
|
+
extensions << ext if File.exists?(ext)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
extensions = [extensions].flatten.compact
|
102
|
+
|
103
|
+
# TODO
|
104
|
+
if This.dependencies.nil?
|
105
|
+
dependencies = []
|
106
|
+
else
|
107
|
+
case This.dependencies
|
108
|
+
when Hash
|
109
|
+
dependencies = This.dependencies.values
|
110
|
+
when Array
|
111
|
+
dependencies = This.dependencies
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
template =
|
116
|
+
if test(?e, 'gemspec.erb')
|
117
|
+
Template{ IO.read('gemspec.erb') }
|
118
|
+
else
|
119
|
+
Template {
|
120
|
+
<<-__
|
121
|
+
## <%= lib %>.gemspec
|
122
|
+
#
|
123
|
+
|
124
|
+
Gem::Specification::new do |spec|
|
125
|
+
spec.name = <%= lib.inspect %>
|
126
|
+
spec.version = <%= version.inspect %>
|
127
|
+
spec.platform = Gem::Platform::RUBY
|
128
|
+
spec.summary = <%= lib.inspect %>
|
129
|
+
spec.description = <%= description.inspect %>
|
130
|
+
|
131
|
+
spec.files =\n<%= files.sort.pretty_inspect %>
|
132
|
+
spec.executables = <%= executables.inspect %>
|
133
|
+
|
134
|
+
spec.require_path = "lib"
|
135
|
+
|
136
|
+
spec.test_files = <%= test_files.inspect %>
|
137
|
+
|
138
|
+
<% dependencies.each do |lib_version| %>
|
139
|
+
spec.add_dependency(*<%= Array(lib_version).flatten.inspect %>)
|
140
|
+
<% end %>
|
141
|
+
|
142
|
+
spec.extensions.push(*<%= extensions.inspect %>)
|
143
|
+
|
144
|
+
spec.rubyforge_project = <%= This.rubyforge_project.inspect %>
|
145
|
+
spec.author = <%= This.author.inspect %>
|
146
|
+
spec.email = <%= This.email.inspect %>
|
147
|
+
spec.homepage = <%= This.homepage.inspect %>
|
148
|
+
end
|
149
|
+
__
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
153
|
+
Fu.mkdir_p(This.pkgdir)
|
154
|
+
gemspec = "#{ lib }.gemspec"
|
155
|
+
open(gemspec, "w"){|fd| fd.puts(template)}
|
156
|
+
This.gemspec = gemspec
|
157
|
+
end
|
158
|
+
|
159
|
+
task :gem => [:clean, :gemspec] do
|
160
|
+
Fu.mkdir_p(This.pkgdir)
|
161
|
+
before = Dir['*.gem']
|
162
|
+
cmd = "gem build #{ This.gemspec }"
|
163
|
+
`#{ cmd }`
|
164
|
+
after = Dir['*.gem']
|
165
|
+
gem = ((after - before).first || after.first) or abort('no gem!')
|
166
|
+
Fu.mv(gem, This.pkgdir)
|
167
|
+
This.gem = File.join(This.pkgdir, File.basename(gem))
|
168
|
+
end
|
169
|
+
|
170
|
+
task :readme do
|
171
|
+
samples = ''
|
172
|
+
prompt = '~ > '
|
173
|
+
lib = This.lib
|
174
|
+
version = This.version
|
175
|
+
|
176
|
+
Dir['sample*/*'].sort.each do |sample|
|
177
|
+
samples << "\n" << " <========< #{ sample } >========>" << "\n\n"
|
178
|
+
|
179
|
+
cmd = "cat #{ sample }"
|
180
|
+
samples << Util.indent(prompt + cmd, 2) << "\n\n"
|
181
|
+
samples << Util.indent(`#{ cmd }`, 4) << "\n"
|
182
|
+
|
183
|
+
cmd = "ruby #{ sample }"
|
184
|
+
samples << Util.indent(prompt + cmd, 2) << "\n\n"
|
185
|
+
|
186
|
+
cmd = "ruby -e'STDOUT.sync=true; exec %(ruby -I ./lib #{ sample })'"
|
187
|
+
samples << Util.indent(`#{ cmd } 2>&1`, 4) << "\n"
|
188
|
+
end
|
189
|
+
|
190
|
+
template =
|
191
|
+
if test(?e, 'readme.erb')
|
192
|
+
Template{ IO.read('readme.erb') }
|
193
|
+
else
|
194
|
+
Template {
|
195
|
+
<<-__
|
196
|
+
NAME
|
197
|
+
#{ lib }
|
198
|
+
|
199
|
+
DESCRIPTION
|
200
|
+
|
201
|
+
INSTALL
|
202
|
+
gem install #{ lib }
|
203
|
+
|
204
|
+
SAMPLES
|
205
|
+
#{ samples }
|
206
|
+
__
|
207
|
+
}
|
208
|
+
end
|
209
|
+
|
210
|
+
open("README", "w"){|fd| fd.puts template}
|
211
|
+
end
|
212
|
+
|
213
|
+
|
214
|
+
task :clean do
|
215
|
+
Dir[File.join(This.pkgdir, '**/**')].each{|entry| Fu.rm_rf(entry)}
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
task :release => [:clean, :gemspec, :gem] do
|
220
|
+
gems = Dir[File.join(This.pkgdir, '*.gem')].flatten
|
221
|
+
raise "which one? : #{ gems.inspect }" if gems.size > 1
|
222
|
+
raise "no gems?" if gems.size < 1
|
223
|
+
|
224
|
+
cmd = "gem push #{ This.gem }"
|
225
|
+
puts cmd
|
226
|
+
puts
|
227
|
+
system(cmd)
|
228
|
+
abort("cmd(#{ cmd }) failed with (#{ $?.inspect })") unless $?.exitstatus.zero?
|
229
|
+
|
230
|
+
cmd = "rubyforge login && rubyforge add_release #{ This.rubyforge_project } #{ This.lib } #{ This.version } #{ This.gem }"
|
231
|
+
puts cmd
|
232
|
+
puts
|
233
|
+
system(cmd)
|
234
|
+
abort("cmd(#{ cmd }) failed with (#{ $?.inspect })") unless $?.exitstatus.zero?
|
235
|
+
end
|
236
|
+
|
237
|
+
|
238
|
+
|
239
|
+
|
240
|
+
|
241
|
+
BEGIN {
|
242
|
+
# support for this rakefile
|
243
|
+
#
|
244
|
+
$VERBOSE = nil
|
245
|
+
|
246
|
+
require 'ostruct'
|
247
|
+
require 'erb'
|
248
|
+
require 'fileutils'
|
249
|
+
require 'rbconfig'
|
250
|
+
require 'pp'
|
251
|
+
|
252
|
+
# fu shortcut
|
253
|
+
#
|
254
|
+
Fu = FileUtils
|
255
|
+
|
256
|
+
# cache a bunch of stuff about this rakefile/environment
|
257
|
+
#
|
258
|
+
This = OpenStruct.new
|
259
|
+
|
260
|
+
This.file = File.expand_path(__FILE__)
|
261
|
+
This.dir = File.dirname(This.file)
|
262
|
+
This.pkgdir = File.join(This.dir, 'pkg')
|
263
|
+
|
264
|
+
# grok lib
|
265
|
+
#
|
266
|
+
lib = ENV['LIB']
|
267
|
+
unless lib
|
268
|
+
lib = File.basename(Dir.pwd).sub(/[-].*$/, '')
|
269
|
+
end
|
270
|
+
This.lib = lib
|
271
|
+
|
272
|
+
# grok version
|
273
|
+
#
|
274
|
+
version = ENV['VERSION']
|
275
|
+
unless version
|
276
|
+
require "./lib/#{ This.lib }"
|
277
|
+
This.name = lib.capitalize
|
278
|
+
This.object = eval(This.name)
|
279
|
+
version = This.object.send(:version)
|
280
|
+
end
|
281
|
+
This.version = version
|
282
|
+
|
283
|
+
# see if dependencies are export by the module
|
284
|
+
#
|
285
|
+
if This.object.respond_to?(:dependencies)
|
286
|
+
This.dependencies = This.object.dependencies
|
287
|
+
end
|
288
|
+
|
289
|
+
# we need to know the name of the lib an it's version
|
290
|
+
#
|
291
|
+
abort('no lib') unless This.lib
|
292
|
+
abort('no version') unless This.version
|
293
|
+
|
294
|
+
# discover full path to this ruby executable
|
295
|
+
#
|
296
|
+
c = RbConfig::CONFIG
|
297
|
+
bindir = c["bindir"] || c['BINDIR']
|
298
|
+
ruby_install_name = c['ruby_install_name'] || c['RUBY_INSTALL_NAME'] || 'ruby'
|
299
|
+
ruby_ext = c['EXEEXT'] || ''
|
300
|
+
ruby = File.join(bindir, (ruby_install_name + ruby_ext))
|
301
|
+
This.ruby = ruby
|
302
|
+
|
303
|
+
# some utils
|
304
|
+
#
|
305
|
+
module Util
|
306
|
+
def indent(s, n = 2)
|
307
|
+
s = unindent(s)
|
308
|
+
ws = ' ' * n
|
309
|
+
s.gsub(%r/^/, ws)
|
310
|
+
end
|
311
|
+
|
312
|
+
def unindent(s)
|
313
|
+
indent = nil
|
314
|
+
s.each_line do |line|
|
315
|
+
next if line =~ %r/^\s*$/
|
316
|
+
indent = line[%r/^\s*/] and break
|
317
|
+
end
|
318
|
+
indent ? s.gsub(%r/^#{ indent }/, "") : s
|
319
|
+
end
|
320
|
+
extend self
|
321
|
+
end
|
322
|
+
|
323
|
+
# template support
|
324
|
+
#
|
325
|
+
class Template
|
326
|
+
def initialize(&block)
|
327
|
+
@block = block
|
328
|
+
@template = block.call.to_s
|
329
|
+
end
|
330
|
+
def expand(b=nil)
|
331
|
+
ERB.new(Util.unindent(@template)).result((b||@block).binding)
|
332
|
+
end
|
333
|
+
alias_method 'to_s', 'expand'
|
334
|
+
end
|
335
|
+
def Template(*args, &block) Template.new(*args, &block) end
|
336
|
+
|
337
|
+
# colored console output support
|
338
|
+
#
|
339
|
+
This.ansi = {
|
340
|
+
:clear => "\e[0m",
|
341
|
+
:reset => "\e[0m",
|
342
|
+
:erase_line => "\e[K",
|
343
|
+
:erase_char => "\e[P",
|
344
|
+
:bold => "\e[1m",
|
345
|
+
:dark => "\e[2m",
|
346
|
+
:underline => "\e[4m",
|
347
|
+
:underscore => "\e[4m",
|
348
|
+
:blink => "\e[5m",
|
349
|
+
:reverse => "\e[7m",
|
350
|
+
:concealed => "\e[8m",
|
351
|
+
:black => "\e[30m",
|
352
|
+
:red => "\e[31m",
|
353
|
+
:green => "\e[32m",
|
354
|
+
:yellow => "\e[33m",
|
355
|
+
:blue => "\e[34m",
|
356
|
+
:magenta => "\e[35m",
|
357
|
+
:cyan => "\e[36m",
|
358
|
+
:white => "\e[37m",
|
359
|
+
:on_black => "\e[40m",
|
360
|
+
:on_red => "\e[41m",
|
361
|
+
:on_green => "\e[42m",
|
362
|
+
:on_yellow => "\e[43m",
|
363
|
+
:on_blue => "\e[44m",
|
364
|
+
:on_magenta => "\e[45m",
|
365
|
+
:on_cyan => "\e[46m",
|
366
|
+
:on_white => "\e[47m"
|
367
|
+
}
|
368
|
+
def say(phrase, *args)
|
369
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
370
|
+
options[:color] = args.shift.to_s.to_sym unless args.empty?
|
371
|
+
keys = options.keys
|
372
|
+
keys.each{|key| options[key.to_s.to_sym] = options.delete(key)}
|
373
|
+
|
374
|
+
color = options[:color]
|
375
|
+
bold = options.has_key?(:bold)
|
376
|
+
|
377
|
+
parts = [phrase]
|
378
|
+
parts.unshift(This.ansi[color]) if color
|
379
|
+
parts.unshift(This.ansi[:bold]) if bold
|
380
|
+
parts.push(This.ansi[:clear]) if parts.size > 1
|
381
|
+
|
382
|
+
method = options[:method] || :puts
|
383
|
+
|
384
|
+
Kernel.send(method, parts.join)
|
385
|
+
end
|
386
|
+
|
387
|
+
# always run out of the project dir
|
388
|
+
#
|
389
|
+
Dir.chdir(This.dir)
|
390
|
+
}
|
data/bin/tt
ADDED
@@ -0,0 +1,226 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
|
5
|
+
Main {
|
6
|
+
name <<-__
|
7
|
+
|
8
|
+
tt
|
9
|
+
|
10
|
+
__
|
11
|
+
|
12
|
+
synopsis <<-__
|
13
|
+
|
14
|
+
tt 2.5 @dojo4 doing something important
|
15
|
+
|
16
|
+
__
|
17
|
+
|
18
|
+
description <<-__
|
19
|
+
|
20
|
+
tt is a svelt time tracker for ruby
|
21
|
+
|
22
|
+
__
|
23
|
+
|
24
|
+
examples <<-__
|
25
|
+
|
26
|
+
. tt 4.5 dojo4 doing something important
|
27
|
+
|
28
|
+
. tt list project -a 2008-05-01 -b 2008-05-07
|
29
|
+
|
30
|
+
. tt total project -a 2008-05-01 -b 2008-05-07
|
31
|
+
|
32
|
+
. tt 4.2 project -- adding --dash feature to project
|
33
|
+
|
34
|
+
__
|
35
|
+
|
36
|
+
option('when', 'w'){
|
37
|
+
argument :required
|
38
|
+
desc 'iso8601 date work was performed'
|
39
|
+
cast{|value| Date.from(value)}
|
40
|
+
default Date.today
|
41
|
+
}
|
42
|
+
|
43
|
+
option('after', 'a'){
|
44
|
+
argument :required
|
45
|
+
desc 'limit data shown to entries *after* this iso8601 date'
|
46
|
+
default Time::Beginning.to_date
|
47
|
+
cast{|value| Date.from(value)}
|
48
|
+
}
|
49
|
+
|
50
|
+
option('before', 'b'){
|
51
|
+
argument :required
|
52
|
+
desc 'limit data shown to entries *before* this iso8601 date'
|
53
|
+
default Time::End.to_date
|
54
|
+
cast{|value| Date.from(value)}
|
55
|
+
}
|
56
|
+
|
57
|
+
option('noop', 'n'){
|
58
|
+
desc 'perform all operations on a tmp database'
|
59
|
+
cast{|value| $noop = value}
|
60
|
+
}
|
61
|
+
|
62
|
+
def run
|
63
|
+
mode = argv.shift if argv.first and respond_to?(argv.first)
|
64
|
+
if mode.nil?
|
65
|
+
if argv.empty?
|
66
|
+
mode = :list
|
67
|
+
else
|
68
|
+
mode = :hours
|
69
|
+
end
|
70
|
+
end
|
71
|
+
send(mode)
|
72
|
+
end
|
73
|
+
|
74
|
+
def hours options = {}
|
75
|
+
hours = argv.shift or abort("no hours")
|
76
|
+
project = argv.shift or abort("no project")
|
77
|
+
message = argv.join(' ')
|
78
|
+
|
79
|
+
hours = Float hours
|
80
|
+
project = String project
|
81
|
+
message = message.empty? ? edit_message! : message
|
82
|
+
|
83
|
+
abort('no message!') if message.empty?
|
84
|
+
|
85
|
+
record = new_record({
|
86
|
+
:when => params['when'].value,
|
87
|
+
:what => message,
|
88
|
+
:time => hours
|
89
|
+
})
|
90
|
+
|
91
|
+
db.transaction do
|
92
|
+
db[project] ||= []
|
93
|
+
db[project] << record
|
94
|
+
end
|
95
|
+
|
96
|
+
result = { project => record }
|
97
|
+
|
98
|
+
y result unless options[:display]==false
|
99
|
+
result
|
100
|
+
end
|
101
|
+
|
102
|
+
def edit_message!
|
103
|
+
require 'tmpdir'
|
104
|
+
require 'fileutils'
|
105
|
+
tmp = File.join(Dir.tmpdir, "tt-#{ Process.ppid }-#{ Process.pid }.#{ rand }.txt")
|
106
|
+
at_exit{ FileUtils.rm_f(tmp) }
|
107
|
+
editor = ENV['EDITOR'] || 'vim'
|
108
|
+
system("touch #{ tmp.inspect } && #{ editor.inspect } #{ tmp.inspect }")
|
109
|
+
msg = IO.read(tmp).strip
|
110
|
+
abort('no msg!') if msg.empty?
|
111
|
+
msg
|
112
|
+
end
|
113
|
+
|
114
|
+
def list options = {}
|
115
|
+
project = argv.shift
|
116
|
+
total = 0 if options['total'] || options[:total]
|
117
|
+
after = param['after'].value
|
118
|
+
before = param['before'].value
|
119
|
+
|
120
|
+
result = Hash.new
|
121
|
+
|
122
|
+
db.transaction do
|
123
|
+
projects = project ? [project] : db.roots
|
124
|
+
projects.each do |project|
|
125
|
+
list = db[project]
|
126
|
+
next unless list
|
127
|
+
result[project] = []
|
128
|
+
list.each do |element|
|
129
|
+
record = new_record element
|
130
|
+
if record['when'] >= after and record['when'] < before
|
131
|
+
result[project] << record
|
132
|
+
total += Float(record['time']) if total
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
result['total'] = total if total
|
137
|
+
end
|
138
|
+
|
139
|
+
y result unless options[:display]==false
|
140
|
+
result
|
141
|
+
end
|
142
|
+
|
143
|
+
def total
|
144
|
+
list :total => true
|
145
|
+
end
|
146
|
+
|
147
|
+
def status
|
148
|
+
if params[:when].given? and !params[:after].given? and !params[:before].given?
|
149
|
+
params[:after].values.replace([params[:when].value - 1])
|
150
|
+
params[:before].values.replace([params[:when].value + 1])
|
151
|
+
elsif !params[:after].given?
|
152
|
+
params[:after].values.replace(Array(Date.from('last sunday')))
|
153
|
+
end
|
154
|
+
|
155
|
+
result = list(:display => false)
|
156
|
+
|
157
|
+
dates = Hash.new
|
158
|
+
|
159
|
+
result.each do |project, entries|
|
160
|
+
entries.each do |entry|
|
161
|
+
synopsis = <<-__
|
162
|
+
@#{ project } #{ entry['time'] }
|
163
|
+
#{ entry['what'] }
|
164
|
+
__
|
165
|
+
synopsis.gsub!(/^[\s]{10}/, '')
|
166
|
+
synopsis.gsub!(/[\s]$/, '')
|
167
|
+
dates[entry['when']] ||= []
|
168
|
+
dates[entry['when']].push(synopsis)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
dates.each do |date, list|
|
173
|
+
puts date
|
174
|
+
list.each do |synopsis|
|
175
|
+
puts(synopsis)
|
176
|
+
puts
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def db
|
182
|
+
@db ||= (
|
183
|
+
if $noop
|
184
|
+
require 'fileutils'
|
185
|
+
require 'tempfile'
|
186
|
+
tmp = Tempfile.new Process.pid.to_s
|
187
|
+
File.open(File.join(TT.home, '.tt.yml')){|fd| tmp.write fd.read}
|
188
|
+
tmp.close
|
189
|
+
@db = YAML::Store.new(tmp.path)
|
190
|
+
else
|
191
|
+
@db = YAML::Store.new(File.join(TT.home, '.tt.yml'))
|
192
|
+
end
|
193
|
+
)
|
194
|
+
end
|
195
|
+
|
196
|
+
def new_record *args
|
197
|
+
pairs = []
|
198
|
+
args.map do |arg|
|
199
|
+
case arg
|
200
|
+
when Hash, Array
|
201
|
+
arg.to_a
|
202
|
+
else
|
203
|
+
arg
|
204
|
+
end
|
205
|
+
end.flatten.each_slice(2) do |first, last|
|
206
|
+
pairs << first.to_s << last
|
207
|
+
end
|
208
|
+
Hash[ *pairs ]
|
209
|
+
end
|
210
|
+
}
|
211
|
+
|
212
|
+
|
213
|
+
BEGIN {
|
214
|
+
require 'pathname'
|
215
|
+
this = Pathname.new(__FILE__).realpath.to_s
|
216
|
+
bindir = File.dirname(this)
|
217
|
+
rootdir = File.dirname(bindir)
|
218
|
+
libdir = File.join(rootdir, 'lib')
|
219
|
+
tt = File.join(libdir, 'tt.rb')
|
220
|
+
|
221
|
+
require(tt)
|
222
|
+
|
223
|
+
STDOUT.sync = true
|
224
|
+
STDERR.sync = true
|
225
|
+
STDIN.sync = true
|
226
|
+
}
|
data/lib/tt.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'yaml/store'
|
3
|
+
require 'time'
|
4
|
+
require 'date'
|
5
|
+
require 'pathname'
|
6
|
+
require 'tempfile'
|
7
|
+
require 'fileutils'
|
8
|
+
require 'enumerator'
|
9
|
+
|
10
|
+
module TT
|
11
|
+
Version = '0.0.1' unless defined?(Version)
|
12
|
+
|
13
|
+
def version
|
14
|
+
TT::Version
|
15
|
+
end
|
16
|
+
|
17
|
+
def dependencies
|
18
|
+
{
|
19
|
+
'main' => [ 'main' , ' >= 4.8.1' ] ,
|
20
|
+
'map' => [ 'map' , ' >= 5.1.0' ] ,
|
21
|
+
'chronic' => [ 'chronic' , ' >= 0.6.6' ]
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def libdir(*args, &block)
|
26
|
+
@libdir ||= File.expand_path(__FILE__).sub(/\.rb$/,'')
|
27
|
+
args.empty? ? @libdir : File.join(@libdir, *args)
|
28
|
+
ensure
|
29
|
+
if block
|
30
|
+
begin
|
31
|
+
$LOAD_PATH.unshift(@libdir)
|
32
|
+
block.call()
|
33
|
+
ensure
|
34
|
+
$LOAD_PATH.shift()
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def load(*libs)
|
40
|
+
libs = libs.join(' ').scan(/[^\s+]+/)
|
41
|
+
TT.libdir{ libs.each{|lib| Kernel.load(lib) } }
|
42
|
+
end
|
43
|
+
|
44
|
+
extend(TT)
|
45
|
+
end
|
46
|
+
|
47
|
+
# gems
|
48
|
+
#
|
49
|
+
begin
|
50
|
+
require 'rubygems'
|
51
|
+
rescue LoadError
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
|
55
|
+
if defined?(gem)
|
56
|
+
TT.dependencies.each do |lib, dependency|
|
57
|
+
gem(*dependency)
|
58
|
+
require(lib)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def TT.home
|
63
|
+
home =
|
64
|
+
catch :home do
|
65
|
+
["HOME", "USERPROFILE"].each do |key|
|
66
|
+
throw(:home, ENV[key]) if ENV[key]
|
67
|
+
end
|
68
|
+
if ENV["HOMEDRIVE"] and ENV["HOMEPATH"]
|
69
|
+
throw(:home, "#{ ENV['HOMEDRIVE'] }:#{ ENV['HOMEPATH'] }")
|
70
|
+
end
|
71
|
+
File.expand_path("~") rescue(File::ALT_SEPARATOR ? "C:/" : "/")
|
72
|
+
end
|
73
|
+
File.expand_path(home)
|
74
|
+
end
|
75
|
+
|
76
|
+
class Date
|
77
|
+
def self.from value
|
78
|
+
( Time.parse(value.to_s) || Date.parse(value.to_s) ).to_date
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_date
|
82
|
+
self
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
class Time
|
88
|
+
Beginning = Time.at(0)
|
89
|
+
|
90
|
+
End = Time.at((2**31)-1)
|
91
|
+
|
92
|
+
Now = Time.now
|
93
|
+
|
94
|
+
Null = Time.at(0).instance_eval do
|
95
|
+
def to_s() "" end
|
96
|
+
def inspect() "" end
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
def to_s(n=0) iso8601(n) end
|
101
|
+
alias_method 'inspect', 'to_s'
|
102
|
+
|
103
|
+
### hack to fix Time.parse bug
|
104
|
+
Parse = Time.method 'parse'
|
105
|
+
def self.parse string
|
106
|
+
if string =~ %r'^\d\d\d\d-\d\d-\d\dT\d\d:?$'
|
107
|
+
string = string.sub(%r/:$/,'') + ':00'
|
108
|
+
end
|
109
|
+
if string =~ %r/\ban\b/
|
110
|
+
string = string.sub(%r/\ban\b/, '1')
|
111
|
+
end
|
112
|
+
if string =~ %r'^\d\d\d\d-\d\d-\d\d'
|
113
|
+
Parse.call string
|
114
|
+
else
|
115
|
+
Chronic.parse string, :context => :past
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def to_date
|
120
|
+
Date.new year, month, day
|
121
|
+
end
|
122
|
+
|
123
|
+
def to_yaml( opts = {} )
|
124
|
+
YAML::quick_emit( object_id, opts ) do |out|
|
125
|
+
tz = "Z"
|
126
|
+
# from the tidy Tobias Peters <t-peters@gmx.de> Thanks!
|
127
|
+
unless self.utc?
|
128
|
+
utc_same_instant = self.dup.utc
|
129
|
+
utc_same_writing = Time.utc(year,month,day,hour,min,sec,usec)
|
130
|
+
difference_to_utc = utc_same_writing - utc_same_instant
|
131
|
+
if (difference_to_utc < 0)
|
132
|
+
difference_sign = '-'
|
133
|
+
absolute_difference = -difference_to_utc
|
134
|
+
else
|
135
|
+
difference_sign = '+'
|
136
|
+
absolute_difference = difference_to_utc
|
137
|
+
end
|
138
|
+
difference_minutes = (absolute_difference/60).round
|
139
|
+
tz = "%s%02d:%02d" % [ difference_sign, difference_minutes / 60, difference_minutes % 60]
|
140
|
+
end
|
141
|
+
standard = self.strftime( "%Y-%m-%dT%H:%M:%S" )
|
142
|
+
standard += ".%02d" % [usec] #if usec.nonzero?
|
143
|
+
standard += "%s" % [tz]
|
144
|
+
if to_yaml_properties.empty?
|
145
|
+
out.scalar( taguri, standard, :plain )
|
146
|
+
else
|
147
|
+
out.map( taguri, to_yaml_style ) do |map|
|
148
|
+
map.add( 'at', standard )
|
149
|
+
to_yaml_properties.each do |m|
|
150
|
+
map.add( m, instance_variable_get( m ) )
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
Tt=TT
|
data/tt.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
## tt.gemspec
|
2
|
+
#
|
3
|
+
|
4
|
+
Gem::Specification::new do |spec|
|
5
|
+
spec.name = "tt"
|
6
|
+
spec.version = "0.0.1"
|
7
|
+
spec.platform = Gem::Platform::RUBY
|
8
|
+
spec.summary = "tt"
|
9
|
+
spec.description = "description: tt kicks the ass"
|
10
|
+
|
11
|
+
spec.files =
|
12
|
+
["README", "Rakefile", "bin", "bin/tt", "lib", "lib/tt.rb", "tt.gemspec"]
|
13
|
+
|
14
|
+
spec.executables = ["tt"]
|
15
|
+
|
16
|
+
spec.require_path = "lib"
|
17
|
+
|
18
|
+
spec.test_files = nil
|
19
|
+
|
20
|
+
|
21
|
+
spec.add_dependency(*["main", " >= 4.8.1"])
|
22
|
+
|
23
|
+
spec.add_dependency(*["map", " >= 5.1.0"])
|
24
|
+
|
25
|
+
spec.add_dependency(*["chronic", " >= 0.6.6"])
|
26
|
+
|
27
|
+
|
28
|
+
spec.extensions.push(*[])
|
29
|
+
|
30
|
+
spec.rubyforge_project = "codeforpeople"
|
31
|
+
spec.author = "Ara T. Howard"
|
32
|
+
spec.email = "ara.t.howard@gmail.com"
|
33
|
+
spec.homepage = "https://github.com/ahoward/tt"
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tt
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ara T. Howard
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-01-31 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: main
|
16
|
+
requirement: &70240801841660 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 4.8.1
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70240801841660
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: map
|
27
|
+
requirement: &70240801840820 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 5.1.0
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70240801840820
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: chronic
|
38
|
+
requirement: &70240801839940 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.6.6
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70240801839940
|
47
|
+
description: ! 'description: tt kicks the ass'
|
48
|
+
email: ara.t.howard@gmail.com
|
49
|
+
executables:
|
50
|
+
- tt
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- README
|
55
|
+
- Rakefile
|
56
|
+
- bin/tt
|
57
|
+
- lib/tt.rb
|
58
|
+
- tt.gemspec
|
59
|
+
homepage: https://github.com/ahoward/tt
|
60
|
+
licenses: []
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements: []
|
78
|
+
rubyforge_project: codeforpeople
|
79
|
+
rubygems_version: 1.8.11
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: tt
|
83
|
+
test_files: []
|