worked 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/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ %w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
2
+ require File.dirname(__FILE__) + '/lib/worked'
3
+
4
+ # Generate all the Rake tasks
5
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
6
+ $hoe = Hoe.new('worked', Worked::VERSION) do |p|
7
+ p.developer('Mirko Stocker', 'me@misto.ch')
8
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
9
+ p.rubyforge_name = p.name
10
+ p.extra_deps = [
11
+ ['treetop','>= 1.2.4'],
12
+ ]
13
+ p.extra_dev_deps = [
14
+ ['newgem', ">= #{::Newgem::VERSION}"]
15
+ ]
16
+
17
+ p.clean_globs |= %w[**/.DS_Store tmp *.log]
18
+ path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
19
+ p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
20
+ p.rsync_args = '-av --delete --ignore-errors'
21
+ end
22
+
23
+ require 'newgem/tasks' # load /tasks/*.rake
24
+ Dir['tasks/**/*.rake'].each { |t| load t }
25
+
26
+ # TODO - want other tests/tasks run by default? Add them to the list
27
+ # task :default => [:spec, :features]
data/bin/worked ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Mirko Stocker on 2009-2-12.
4
+ # Copyright (c) 2009. All rights reserved.
5
+
6
+ require File.expand_path(File.dirname(__FILE__) + "/../lib/worked")
7
+
8
+ require "worked/cli"
9
+
10
+ CONFIG = File.join(Dir.getwd, ".worked")
11
+
12
+ File.open(CONFIG, "a") do |file|
13
+ Worked::CLI.execute(STDOUT, ARGV.join(" "), file)
14
+ end
@@ -0,0 +1,2 @@
1
+ host: misto@rubyforge.org
2
+ remote_dir: /var/www/gforge-projects/worked
data/lib/worked.rb ADDED
@@ -0,0 +1,8 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'worked/inputparser'
5
+
6
+ module Worked
7
+ VERSION = '0.1.0'
8
+ end
data/lib/worked/cli.rb ADDED
@@ -0,0 +1,37 @@
1
+ require 'optparse'
2
+ require 'worked/recorder'
3
+
4
+ module Worked
5
+ class CLI
6
+ def self.execute(stdout, arguments, file)
7
+
8
+ Recorder.new(file).record(arguments)
9
+
10
+ #options = {
11
+ # :path => '~'
12
+ #}
13
+
14
+ #parser = OptionParser.new do |opts|
15
+ # opts.banner = <<-BANNER.gsub(/^ /,'')
16
+ # This application is wonderful because...
17
+
18
+ # Usage: #{File.basename($0)} [options]
19
+
20
+ # Options are:
21
+ # BANNER
22
+ # opts.separator ""
23
+ # opts.on("-p", "--path=PATH", String,
24
+ # "This is a sample message.",
25
+ # "For multiple lines, add more strings.",
26
+ # "Default: ~") { |arg| options[:path] = arg }
27
+ # opts.on("-h", "--help",
28
+ # "Show this help message.") { stdout.puts opts; exit }
29
+
30
+ # opts.parse!(arguments)
31
+ #end
32
+
33
+ #path = options[:path]
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,80 @@
1
+
2
+ grammar InputGrammar
3
+
4
+ rule root
5
+ time " on " activity
6
+ end
7
+
8
+ rule activity
9
+ .*
10
+ end
11
+
12
+ rule time
13
+ time_from / time_in_words / time_range / time_in_digits
14
+ end
15
+
16
+ rule time_from
17
+ "from " am_pm_time {
18
+ def from
19
+ am_pm_time.to_24h
20
+ end
21
+ }
22
+ end
23
+
24
+ rule time_range
25
+ f:am_pm_time " to " t:am_pm_time {
26
+ def from
27
+ f.to_24h
28
+ end
29
+
30
+ def to
31
+ t.to_24h
32
+ end
33
+ }
34
+ end
35
+
36
+ rule am_pm_time
37
+ time_in_digits am_pm:(" am" / " pm" / "am" / "pm")? {
38
+ def to_24h
39
+
40
+ if methods.include?("am_pm") && am_pm.text_value =~ /pm/
41
+ return time_in_digits.total + 12.hours
42
+ end
43
+
44
+ time_in_digits.total
45
+ end
46
+ }
47
+ end
48
+
49
+ rule time_in_words
50
+ minutes / hours m:(" "? minutes)? {
51
+ def total
52
+ minutes.to_i.minutes + hours.to_i.hours + m.to_i.minutes
53
+ end
54
+ }
55
+ end
56
+
57
+ rule time_in_digits
58
+ integer m:( ":" integer )? {
59
+ def total
60
+ integer.to_i.hours + m.to_i.minutes
61
+ end
62
+ }
63
+ end
64
+
65
+ rule hours
66
+ integer " "? ( "hour" "s"? / "h" )
67
+ end
68
+
69
+ rule minutes
70
+ integer " "? ( "minute" "s"? / "m" ) {
71
+ def total
72
+ integer.to_i.minutes
73
+ end
74
+ }
75
+ end
76
+
77
+ rule integer
78
+ [0-9]*
79
+ end
80
+ end
@@ -0,0 +1,52 @@
1
+ require 'active_support'
2
+ require 'treetop'
3
+ require 'worked/inputgrammar'
4
+
5
+ class Treetop::Runtime::SyntaxNode
6
+
7
+ # Remove all clutter ('hour', ':', etc) from the
8
+ # time and turn into an integer
9
+ def to_i
10
+ text_value.gsub(/\D/, '').to_i
11
+ end
12
+
13
+ def from
14
+ time_to_seconds_from_day(DateTime.now - total)
15
+ end
16
+
17
+ def to
18
+ time_to_seconds_from_day(DateTime.now)
19
+ end
20
+
21
+ def time_to_seconds_from_day t
22
+ t.hour.hours + t.min.minutes
23
+ end
24
+
25
+ # Default value so we don't have to care whether
26
+ # there really is a minute component
27
+ def minutes
28
+ 0
29
+ end
30
+ end
31
+
32
+ class InputParser
33
+
34
+ def self.parse source
35
+
36
+ root = InputGrammarParser.new.parse(source)
37
+
38
+ from, to = root.time.from, root.time.to
39
+
40
+ if from > to
41
+ from -= 24.hours
42
+ end
43
+
44
+ now = DateTime.now
45
+ midnight = DateTime.new(now.year, now.month, now.day)
46
+
47
+ [midnight + from.seconds, midnight + to.seconds, root.activity.text_value]
48
+ end
49
+ end
50
+
51
+
52
+
@@ -0,0 +1,13 @@
1
+ require 'worked/inputparser'
2
+
3
+ class Recorder
4
+
5
+ def initialize out
6
+ @out = out
7
+ end
8
+
9
+ def record line
10
+ from, to, activity = InputParser.parse(line)
11
+ @out << "#{ from }\t#{ to }\t#{ activity }\n"
12
+ end
13
+ end
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/worked.rb'}"
9
+ puts "Loading worked gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
data/script/txt2html ADDED
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ begin
5
+ require 'newgem'
6
+ rescue LoadError
7
+ puts "\n\nGenerating the website requires the newgem RubyGem"
8
+ puts "Install: gem install newgem\n\n"
9
+ exit(1)
10
+ end
11
+ require 'redcloth'
12
+ require 'syntax/convertors/html'
13
+ require 'erb'
14
+ require File.dirname(__FILE__) + '/../lib/worked.rb'
15
+
16
+ version = Worked::VERSION
17
+ download = 'http://rubyforge.org/projects/worked'
18
+
19
+ class Fixnum
20
+ def ordinal
21
+ # teens
22
+ return 'th' if (10..19).include?(self % 100)
23
+ # others
24
+ case self % 10
25
+ when 1: return 'st'
26
+ when 2: return 'nd'
27
+ when 3: return 'rd'
28
+ else return 'th'
29
+ end
30
+ end
31
+ end
32
+
33
+ class Time
34
+ def pretty
35
+ return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
36
+ end
37
+ end
38
+
39
+ def convert_syntax(syntax, source)
40
+ return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
41
+ end
42
+
43
+ if ARGV.length >= 1
44
+ src, template = ARGV
45
+ template ||= File.join(File.dirname(__FILE__), '/../website/template.rhtml')
46
+
47
+ else
48
+ puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html")
49
+ exit!
50
+ end
51
+
52
+ template = ERB.new(File.open(template).read)
53
+
54
+ title = nil
55
+ body = nil
56
+ File.open(src) do |fsrc|
57
+ title_text = fsrc.readline.gsub(/^=/, "")
58
+ body_text = fsrc.read
59
+ syntax_items = []
60
+ body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
61
+ ident = syntax_items.length
62
+ element, syntax, source = $1, $2, $3
63
+ syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
64
+ "syntax-temp-#{ident}"
65
+ }
66
+ body_text.gsub!(/^===/, "h3.")
67
+ body_text.gsub!(/^==/, "h2.")
68
+ body_text.gsub!(/^- /, "* ")
69
+ title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
70
+ body = RedCloth.new(body_text).to_html
71
+ body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
72
+ end
73
+ stat = File.stat(src)
74
+ created = stat.ctime
75
+ modified = stat.mtime
76
+
77
+ $stdout << template.result(binding)
@@ -0,0 +1,4 @@
1
+ desc 'Generate website files'
2
+ task :website_generate => :ruby_env do
3
+ sh %{ #{RUBY_APP} script/txt2html README.rdoc > website/index.html}
4
+ end
@@ -0,0 +1,68 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestGrammar < Test::Unit::TestCase
4
+
5
+ def test_hours
6
+ assert_equal 5, hours("5 hours")
7
+ assert_equal 5, hours("5hours")
8
+ assert_equal 5, hours("5 h")
9
+ assert_equal 5, hours("5h")
10
+ assert_equal 5, hours("5")
11
+ end
12
+
13
+ def test_minutes
14
+ assert_equal 10, minutes("5 h 10 minutes")
15
+ assert_equal 10, minutes("5 h 10 m")
16
+ assert_equal 10, minutes("5 h 10m")
17
+ assert_equal 10, minutes("5h10m")
18
+ assert_equal 10, minutes("5:10")
19
+ end
20
+
21
+ def test_hour_ranges
22
+ assert_equal 4.hours, range("13 to 17")
23
+ assert_equal 4.hours, range("1 to 5")
24
+ assert_equal 4.hours, range("1pm to 5pm")
25
+ assert_equal 4.hours, range("1 pm to 5 pm")
26
+ assert_equal 16.hours, range("1 am to 5 pm")
27
+ assert_equal 2.hours, range("11 to 1pm")
28
+ assert_equal 2.hours, range("11pm to 1am")
29
+ end
30
+
31
+ def test_hour_minute_ranges
32
+ assert_equal 4.hours + 30.minutes, range("1 to 5:30")
33
+ assert_equal 4.hours + 10.minutes, range("13:30 to 17:40")
34
+ end
35
+
36
+ def test_with_activity
37
+ assert_equal "Testing", activity("5 hours on Testing")
38
+ assert_equal "Testing and Refactoring", activity("1 to 5 on Testing and Refactoring")
39
+ end
40
+
41
+ def test_from_time
42
+ assert_in_delta 2.hours, range("from #{DateTime.now.hour - 2}:#{DateTime.now.min}"), 10 #seconds of delta
43
+ end
44
+
45
+ def activity source
46
+ parse(source).last
47
+ end
48
+
49
+ def range source
50
+ res = parse("#{source} on X")
51
+ ((res[1] - res[0]) * 24).hours
52
+ end
53
+
54
+ def hours source
55
+ res = parse("#{source} on X")
56
+ res[1].hour - res[0].hour
57
+ end
58
+
59
+ def minutes source
60
+ res = parse("#{source} on X")
61
+ res[1].min - res[0].min
62
+ end
63
+
64
+ def parse source
65
+ InputParser.parse(source)
66
+ end
67
+
68
+ end
@@ -0,0 +1,15 @@
1
+ require 'stringio'
2
+ require 'test/unit'
3
+ require File.dirname(__FILE__) + '/../lib/worked'
4
+
5
+ module WorkedTestHelper
6
+ def extract_tuple log
7
+
8
+ s_from, s_to, activity = /(\S*)\s+(\S*)\s+(.*)/.match(log).captures
9
+
10
+ from = DateTime.parse(s_from)
11
+ to = DateTime.parse(s_to)
12
+
13
+ [ ((to - from) * 24).hours, activity ]
14
+ end
15
+ end