achoo 0.3 → 0.4.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.
Files changed (53) hide show
  1. data/CHANGES +12 -0
  2. data/README.rdoc +26 -30
  3. data/Rakefile +3 -0
  4. data/bin/achoo +2 -2
  5. data/lib/achoo.rb +1 -139
  6. data/lib/achoo/achievo.rb +13 -0
  7. data/lib/achoo/achievo/form.rb +22 -0
  8. data/lib/achoo/achievo/hour_administration_form.rb +91 -0
  9. data/lib/achoo/achievo/hour_registration_form.rb +230 -0
  10. data/lib/achoo/achievo/hour_registration_form_ranged.rb +49 -0
  11. data/lib/achoo/achievo/lock_month_form.rb +44 -0
  12. data/lib/achoo/achievo/login_form.rb +27 -0
  13. data/lib/achoo/achievo/table.rb +30 -0
  14. data/lib/achoo/app.rb +153 -0
  15. data/lib/achoo/awake.rb +86 -100
  16. data/lib/achoo/extensions.rb +24 -0
  17. data/lib/achoo/ical.rb +47 -40
  18. data/lib/achoo/rc_loader.rb +42 -42
  19. data/lib/achoo/system.rb +8 -3
  20. data/lib/achoo/system/cstruct.rb +67 -0
  21. data/lib/achoo/system/log_entry.rb +24 -0
  22. data/lib/achoo/system/pm_suspend.rb +17 -20
  23. data/lib/achoo/system/utmp_record.rb +64 -0
  24. data/lib/achoo/system/wtmp.rb +14 -10
  25. data/lib/achoo/temporal.rb +8 -0
  26. data/lib/achoo/temporal/open_timespan.rb +16 -0
  27. data/lib/achoo/temporal/timespan.rb +122 -0
  28. data/lib/achoo/term.rb +58 -56
  29. data/lib/achoo/term/menu.rb +2 -2
  30. data/lib/achoo/term/table.rb +59 -60
  31. data/lib/achoo/ui.rb +13 -9
  32. data/lib/achoo/ui/commands.rb +60 -38
  33. data/lib/achoo/ui/common.rb +10 -7
  34. data/lib/achoo/ui/date_chooser.rb +69 -65
  35. data/lib/achoo/ui/date_choosers.rb +15 -14
  36. data/lib/achoo/ui/exception_handling.rb +14 -12
  37. data/lib/achoo/ui/month_chooser.rb +37 -24
  38. data/lib/achoo/ui/optionally_ranged_date_chooser.rb +29 -25
  39. data/lib/achoo/ui/register_hours.rb +116 -114
  40. data/lib/achoo/vcs.rb +32 -30
  41. data/lib/achoo/vcs/git.rb +18 -14
  42. data/lib/achoo/vcs/subversion.rb +25 -23
  43. metadata +30 -24
  44. data/lib/achoo/binary.rb +0 -7
  45. data/lib/achoo/binary/cstruct.rb +0 -60
  46. data/lib/achoo/binary/utmp_record.rb +0 -59
  47. data/lib/achoo/form.rb +0 -18
  48. data/lib/achoo/hour_administration_form.rb +0 -131
  49. data/lib/achoo/hour_registration_form.rb +0 -227
  50. data/lib/achoo/hour_registration_form_ranged.rb +0 -45
  51. data/lib/achoo/lock_month_form.rb +0 -40
  52. data/lib/achoo/open_timespan.rb +0 -13
  53. data/lib/achoo/timespan.rb +0 -119
@@ -1,18 +1,22 @@
1
- require 'achoo/binary'
2
1
  require 'achoo/system'
3
2
 
4
- class Achoo::System::Wtmp < Array
3
+ module Achoo
4
+ module System
5
5
 
6
- def initialize(glob='/var/log/wtmp*')
7
- super()
8
- chunk_size = Achoo::Binary::UTMPRecord.bin_size
9
- Dir.glob(glob).sort.reverse.each do |file|
10
- File.open(file, 'r') do |io|
11
- while (bytes = io.read(chunk_size))
12
- self << Achoo::Binary::UTMPRecord.new(bytes)
6
+ class Wtmp < Array
7
+
8
+ def initialize(glob='/var/log/wtmp*')
9
+ super()
10
+ chunk_size = UTMPRecord.bin_size
11
+ Dir.glob(glob).sort.reverse.each do |file|
12
+ File.open(file, 'r') do |io|
13
+ while (bytes = io.read(chunk_size))
14
+ self << UTMPRecord.new(bytes)
15
+ end
16
+ end
13
17
  end
14
18
  end
19
+
15
20
  end
16
21
  end
17
-
18
22
  end
@@ -0,0 +1,8 @@
1
+ require 'achoo'
2
+
3
+ module Achoo
4
+ module Temporal
5
+ autoload :Timespan, 'achoo/temporal/timespan.rb'
6
+ autoload :OpenTimespan, 'achoo/temporal/open_timespan.rb'
7
+ end
8
+ end
@@ -0,0 +1,16 @@
1
+ require 'achoo/temporal'
2
+
3
+ module Achoo
4
+ module Temporal
5
+ class OpenTimespan < Timespan
6
+
7
+ def to_s
8
+ s = super
9
+ s.sub!(/^\([^)]+\)/, '(?+??:??)')
10
+ s.sub!(/- .*$/, '- ?')
11
+ s
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,122 @@
1
+ require 'achoo/temporal'
2
+ require 'time'
3
+
4
+ module Achoo
5
+ module Temporal
6
+ class Timespan
7
+
8
+ SECONDS_IN_A_DAY = 86400
9
+ SECONDS_IN_AN_HOUR = 3600
10
+ SECONDS_IN_A_MINUTE = 60
11
+
12
+ attr :start
13
+ attr :end
14
+
15
+ def initialize(start, end_)
16
+ raise ArgumentError.new('Nil in parameters not allowed') if start.nil? || end_.nil?
17
+
18
+ self.start = start
19
+ self.end = end_
20
+ end
21
+
22
+ def start=(timeish)
23
+ @start = to_time(timeish)
24
+ end
25
+
26
+ def end=(timeish)
27
+ @end = to_time(timeish)
28
+ end
29
+
30
+ def to_s
31
+ duration = duration_string
32
+ from_to = from_to_string
33
+
34
+ sprintf("(%s) %s", duration, from_to)
35
+ end
36
+
37
+ def contains?(timeish_or_timespan)
38
+ if timeish_or_timespan.is_a? Timespan
39
+ timespan = timeish_or_timespan
40
+ return start <= timespan.start && self.end >= timespan.end
41
+ else
42
+ time = to_time(timeish_or_timespan)
43
+ return start <= time && self.end >= time
44
+ end
45
+ end
46
+
47
+ def overlaps?(timespan)
48
+ start <= timespan.start && self.end >= timespan.start \
49
+ || start <= timespan.end && self.end >= timespan.end \
50
+ || contains?(timespan) || timespan.contains?(self)
51
+ end
52
+
53
+ private
54
+
55
+ def to_time(timeish)
56
+ case timeish
57
+ when Time
58
+ timeish.clone
59
+ when DateTime
60
+ Time.local(timeish.year, timeish.month, timeish.day, timeish.hour,timeish.minute, timeish.second)
61
+ when Date
62
+ Time.local(timeish.year, timeish.month, timeish.day)
63
+ else
64
+ if timeish.respond_to?(:to_s)
65
+ Time.parse(timeish.to_s)
66
+ else
67
+ raise ArgumentError.new("Don't know how to convert #{timeish.class} to Time")
68
+ end
69
+ end
70
+ end
71
+
72
+
73
+ def duration_string
74
+ delta = @end - @start
75
+ d = delta.to_i / SECONDS_IN_A_DAY
76
+
77
+ delta = delta - d*SECONDS_IN_A_DAY
78
+ h = delta.to_i / SECONDS_IN_AN_HOUR
79
+
80
+ delta = delta - h*SECONDS_IN_AN_HOUR
81
+ m = delta.to_i / SECONDS_IN_A_MINUTE
82
+
83
+ sprintf "%d+%02d:%02d", d, h, m
84
+ end
85
+
86
+ def from_to_string
87
+ today = Date.today
88
+ start_date = start.send(:to_date)
89
+ end_date = self.end.send(:to_date)
90
+
91
+ format = if start_date == today
92
+ "Today"
93
+ elsif start_date.month == today.month &&
94
+ start_date.year == today.year
95
+ "%a %e."
96
+ elsif start_date.year == today.year
97
+ "%a %e. %b"
98
+ else
99
+ "%a %e. %b %Y"
100
+ end
101
+ from = start.strftime(format << " %R")
102
+
103
+ format = if end_date == start_date
104
+ "%R"
105
+ elsif end_date == today
106
+ "Today %R"
107
+ elsif end_date.month == today.month &&
108
+ end_date.year == today.year
109
+ "%a %e. %R"
110
+ elsif end_date.year == today.year
111
+ "%a %e. %b %R"
112
+ else
113
+ "%a %e. %b %Y %R"
114
+ end
115
+ to = self.end.strftime(format)
116
+
117
+ sprintf "%s - %s", from, to
118
+ end
119
+
120
+ end
121
+ end
122
+ end
data/lib/achoo/term.rb CHANGED
@@ -1,76 +1,78 @@
1
1
  # encoding: utf-8
2
2
 
3
- class Achoo; class Term; end; end
3
+ require 'achoo'
4
4
 
5
- Achoo::Term.autoload :Menu, 'achoo/term/menu'
6
- Achoo::Term.autoload :Table, 'achoo/term/table'
5
+ module Achoo
6
+ class Term
7
7
 
8
- class Achoo::Term
8
+ autoload :Menu, 'achoo/term/menu'
9
+ autoload :Table, 'achoo/term/table'
9
10
 
10
- def self.bold(text); "\e[1m#{text}\e[0m"; end
11
+ def self.bold(text); "\e[1m#{text}\e[0m"; end
12
+
13
+ def self.underline(text); "\e[4m#{text}\e[0m"; end
11
14
 
12
- def self.underline(text); "\e[4m#{text}\e[0m"; end
15
+ def self.warn(text); "\e[1;33m#{text}\e[0m"; end
13
16
 
14
- def self.warn(text); "\e[1;33m#{text}\e[0m"; end
17
+ def self.fatal(text); "\e[1;31m#{text}\e[0m"; end
15
18
 
16
- def self.fatal(text); "\e[1;31m#{text}\e[0m"; end
17
-
18
- def self.password
19
- `stty -echo`
20
- pas = ask('Password')
21
- `stty echo`
22
- pas
23
- end
24
-
25
- def self.ask(question='')
26
- answer = nil
27
- loop do
28
- print bold("#{question}> ")
29
- $stdout.flush
30
- answer = gets
19
+ def self.password
20
+ `stty -echo`
21
+ pas = ask('Password')
22
+ `stty echo`
23
+ pas
24
+ end
31
25
 
32
- # Answer is nil if user hits C-d on an empty input
33
- if answer.nil?
34
- puts
35
- exit
26
+ def self.ask(question='')
27
+ answer = nil
28
+ loop do
29
+ print bold("#{question}> ")
30
+ $stdout.flush
31
+ answer = gets
32
+
33
+ # Answer is nil if user hits C-d on an empty input
34
+ if answer.nil?
35
+ puts
36
+ exit
37
+ end
38
+
39
+ answer.strip! unless answer.nil?
40
+
41
+ # FIX move this to achoo.rb?
42
+ unless $stdin.tty?
43
+ puts answer
44
+ end
45
+ break unless a_little_something(answer)
36
46
  end
37
-
38
- answer.strip! unless answer.nil?
47
+ answer
48
+ end
39
49
 
40
- # FIX move this to achoo.rb?
41
- unless $stdin.tty?
42
- puts answer
43
- end
44
- break unless a_little_something(answer)
50
+ def self.choose(question, entries, special=nil, additional_valid_answers=[])
51
+ menu = Menu.new(question, entries, special, additional_valid_answers)
52
+ menu.print_ask_and_validate
45
53
  end
46
- answer
47
- end
48
54
 
49
- def self.choose(question, entries, special=nil, additional_valid_answers=[])
50
- menu = Achoo::Term::Menu.new(question, entries, special, additional_valid_answers)
51
- menu.print_ask_and_validate
52
- end
55
+ private
53
56
 
54
- private
57
+ def self.a_little_something(answer)
58
+ return false if answer.nil?
55
59
 
56
- def self.a_little_something(answer)
57
- return false if answer.nil?
60
+ case answer.downcase
61
+ when 'bless you!', 'gesundheit!'
62
+ puts "Thank you!"
63
+ return true
64
+ else
65
+ return false
66
+ end
67
+ end
58
68
 
59
- case answer.downcase
60
- when 'bless you!', 'gesundheit!'
61
- puts "Thank you!"
62
- return true
63
- else
64
- return false
69
+ def self.shadowbox(text)
70
+ x = "┌──────────────────────────────────────────┐ \n"
71
+ x << " #{text.center(40)} " << "│▒\n"
72
+ x << "└──────────────────────────────────────────┘▒\n"
73
+ x << " ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒\n"
74
+ x
65
75
  end
66
- end
67
76
 
68
- def self.shadowbox(text)
69
- x = "┌──────────────────────────────────────────┐ \n"
70
- x << "│ #{text.center(40)} " << "│▒\n"
71
- x << "└──────────────────────────────────────────┘▒\n"
72
- x << " ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒\n"
73
- x
74
77
  end
75
-
76
78
  end
@@ -1,6 +1,6 @@
1
1
  require 'achoo/term'
2
2
 
3
- class Achoo
3
+ module Achoo
4
4
  class Term
5
5
  class Menu
6
6
 
@@ -22,7 +22,7 @@ class Achoo
22
22
  return '1' if only_one_option?
23
23
 
24
24
  loop do
25
- answer = Achoo::Term.ask(@question)
25
+ answer = Term.ask(@question)
26
26
  if @valid[answer]
27
27
  return answer
28
28
  else
@@ -1,9 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
- if RUBY_VERSION < "1.9"
4
- $KCODE = 'u'
5
- require 'jcode'
6
- end
3
+ require 'achoo/term'
4
+
7
5
 
8
6
  # Unicode box drawing characters
9
7
  #
@@ -28,74 +26,75 @@ end
28
26
  #
29
27
  # 2570 ╰ ╱ ╲ ╳ ╴ ╵ ╶ ╷ ╸ ╹ ╺ ╻ ╼ ╽ ╾ ╿
30
28
 
31
- class Achoo; class Term; end; end
32
-
33
- class Achoo::Term::Table
29
+ module Achoo
30
+ class Term
31
+ class Table
34
32
 
35
- def initialize(headers, data_rows, summaries=nil)
36
- @headers = headers
37
- @data_rows = data_rows
38
- @summaries = summaries
39
- end
33
+ def initialize(headers, data_rows, summaries=nil)
34
+ @headers = headers
35
+ @data_rows = data_rows
36
+ @summaries = summaries
37
+ end
40
38
 
41
- def print(io=$stdout)
42
- lengths = calculate_table_cell_widths
43
- format = build_format(lengths)
44
- headers = center_table_headers(lengths)
45
- separator = lengths.map {|length| '─'*(length+2)}
39
+ def print(io=$stdout)
40
+ lengths = calculate_table_cell_widths
41
+ format = build_format(lengths)
42
+ headers = center_table_headers(lengths)
43
+ separator = lengths.map {|length| '─'*(length+2)}
46
44
 
47
45
 
48
- io.print '┌' << separator.join('┬') << "┐\n"
49
- io.print '│ ' << headers.join(' │ ') << " │\n"
50
- io.print '├' << separator.join('┼') << "┤\n"
51
- @data_rows.each {|r| io.printf format, *r }
52
- unless @summaries.nil? || @data_rows.length == 1
53
- io.print '├' << separator.join('┼') << "┤\n"
54
- io.printf format, *@summaries
55
- end
56
- io.print '└' << separator.join('┴') << "┘\n"
57
- end
46
+ io.print '┌' << separator.join('┬') << "┐\n"
47
+ io.print '│ ' << headers.join(' │ ') << " │\n"
48
+ io.print '├' << separator.join('┼') << "┤\n"
49
+ @data_rows.each {|r| io.printf format, *r }
50
+ unless @summaries.nil? || @data_rows.length == 1
51
+ io.print '├' << separator.join('┼') << "┤\n"
52
+ io.printf format, *@summaries
53
+ end
54
+ io.print '└' << separator.join('┴') << "┘\n"
55
+ end
58
56
 
59
- private
57
+ private
60
58
 
61
- def center_table_headers(lengths)
62
- headers = @headers.dup
63
- lengths.each_with_index do |len,i|
64
- headers[i] = headers[i].center(len)
65
- end
66
- headers
67
- end
59
+ def center_table_headers(lengths)
60
+ headers = @headers.dup
61
+ lengths.each_with_index do |len,i|
62
+ headers[i] = headers[i].center(len)
63
+ end
64
+ headers
65
+ end
68
66
 
69
- def calculate_table_cell_widths
70
- lengths = []
71
- @headers.each_with_index do |h, i|
72
- lengths[i] = RUBY_VERSION < '1.9' ? h.jlength : h.length
73
- end
74
- @data_rows.each do |r|
75
- r.each_with_index do |d, i|
76
- len = RUBY_VERSION < '1.9' ? d.jlength : d.length
77
- lengths[i] = [len, lengths[i]].max
67
+ def calculate_table_cell_widths
68
+ lengths = []
69
+ @headers.each_with_index do |h, i|
70
+ lengths[i] = h.length
71
+ end
72
+ @data_rows.each do |r|
73
+ r.each_with_index do |d, i|
74
+ lengths[i] = [d.length, lengths[i]].max
75
+ end
76
+ end
77
+ lengths
78
78
  end
79
- end
80
- lengths
81
- end
82
79
 
83
- def build_format(lengths)
84
- is_column_left_justified = Array.new(lengths.count)
85
- is_column_left_justified.fill(false)
80
+ def build_format(lengths)
81
+ is_column_left_justified = Array.new(lengths.count)
82
+ is_column_left_justified.fill(false)
86
83
 
87
- @data_rows.each do |r|
88
- r.each_index do |c|
89
- if !r[c].strip.empty? && !r[c].match(/^\d+[:.,]?\d*$/)
90
- is_column_left_justified[c] = true
84
+ @data_rows.each do |r|
85
+ r.each_index do |c|
86
+ if !r[c].strip.empty? && !r[c].match(/^\d+[:.,]?\d*$/)
87
+ is_column_left_justified[c] = true
88
+ end
89
+ end
91
90
  end
91
+
92
+ lengths.reduce('│') do |f, l|
93
+ justify = is_column_left_justified.shift ? '-' : ''
94
+ f + " %#{justify}#{l}s │"
95
+ end + "\n"
92
96
  end
97
+
93
98
  end
94
-
95
- lengths.reduce('│') do |f, l|
96
- justify = is_column_left_justified.shift ? '-' : ''
97
- f + " %#{justify}#{l}s │"
98
- end + "\n"
99
99
  end
100
-
101
100
  end