habiter 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/README.rdoc +70 -0
  2. data/bin/habiter +126 -85
  3. metadata +3 -3
  4. data/README +0 -54
@@ -0,0 +1,70 @@
1
+ habiter is a simple tool for maintaining good habits
2
+
3
+ It can be used with the 'don't break the chain' technique, like
4
+ Joe's Goals or Sciral Consistency, but adopts more positive
5
+ techniques and uses a readable and editable YAML text file as
6
+ storage.
7
+
8
+ = Installation
9
+
10
+ % gem install habiter -s http://gemcutter.org/
11
+
12
+ = Demo
13
+
14
+ ~$ habiter did clean_room
15
+ clean_room is not a habit yet. create and complete it? (y/n) y
16
+ ~$ habiter did jog
17
+ jog is not a habit yet. create and complete it? (y/n) y
18
+ ~$ habiter log
19
+ jog |----------------------------+|(1/28)
20
+ project |-------------------------++-+|(3/28)
21
+ practice_music |-----------------------+++++-|(5/28)
22
+ read_30m |-----------------------+--++-|(3/28)
23
+ clean_room |-----------------------+-+---|(2/28)
24
+
25
+ = Scalars
26
+
27
+ Habiter also now supports scalars: you can input a value for your
28
+ activity, like
29
+
30
+ habiter did jog 5.5
31
+ habiter did heartbeat 78
32
+
33
+ And it will be recorded. There is no representation of this data yet,
34
+ and the representation within habiter will be minimal, only good output to
35
+ formats which are easily visualized.
36
+
37
+ = Install
38
+
39
+ Install with 'gem install habiter' if you have gemcutter set up as a
40
+ repository
41
+
42
+ = FAQ
43
+
44
+ * How do I add past data?
45
+ How do I delete recorded data?
46
+ How do I modify stuff I've recorded?
47
+
48
+ Edit ~/.habiter.yaml
49
+
50
+ * How do I reset habiter?
51
+
52
+ echo > ~/.habiter.yaml
53
+
54
+ = Changelog
55
+
56
+ == 3.0 (roadmap)
57
+
58
+ * Dump to CSV
59
+
60
+ == 2.5
61
+
62
+ * Allows 'did' in past and future
63
+ * Restructuring and cleanup internally
64
+ * Internal documentation
65
+
66
+ == 2.0
67
+
68
+ * Scalar input
69
+
70
+ Dedicated to Bryn Bellomy :)
@@ -21,112 +21,153 @@ class String
21
21
  end
22
22
  end
23
23
 
24
-
25
-
26
- @habiter_file = File.expand_path('~/.habiter.yaml')
27
-
28
- begin
29
- @habits = YAML::load_file(@habiter_file)
30
- rescue
31
- File.open(@habiter_file, 'w').close()
24
+ class Habiter
25
+ def initialize(data_path = '~/.habiter.yaml')
26
+ @habiter_file = File.expand_path(data_path)
27
+ begin
28
+ @habits = YAML::load_file(@habiter_file)
29
+ rescue
30
+ File.open(@habiter_file, 'w').close()
31
+ end
32
+ @habits = {} unless @habits
33
+ end
32
34
  end
33
35
 
34
- @habits = {} unless @habits
35
36
 
36
- options = {}
37
-
38
- optparse = OptionParser.new do |opts|
39
- opts.banner =
40
- "Usage:
41
- habiter did your_habit
42
- habiter log"
43
- options[:verbose] = false
44
- opts.on('-v', '--verbose', "Show weekday letters for log") do
45
- options[:verbose] = true
37
+ class HabiterBin < Habiter
38
+ def dump(options={})
39
+ # Output this YAML datastore as normalized CSV for processing by
40
+ # whatever wants it
41
+ cols = @habits.length
42
+ i = 0 # TODO: AVOID THIS
43
+ rotated = {}
44
+ @habits.each do |habit, times|
45
+ times.collect! do |time|
46
+ this_time = normalize_time(time)
47
+ day = rotated.fetch(this_time, Array.new(cols, 0))
48
+ day[i] = (day[i].nil?) ? 1 : day[i] + 1
49
+ rotated[this_time] = day
50
+ end
51
+ i += 1
52
+ end
53
+ puts rotated.sort.keys[0]
46
54
  end
47
- end
48
55
 
49
- def did(args)
50
- key = args[1]
51
- new_record = (args.length > 2) ? {Time.now => args[2]} : Time.now
52
- if not @habits.has_key?(key)
53
- print "#{key} is not a habit yet. create and complete it? (y/n) "
54
- if STDIN.gets.chomp == 'y'
55
- @habits[key] = [new_record]
56
+ def did(key, scalar = false, yester = 0)
57
+ # Record that somebody did something. Expects an unadulterated
58
+ # args array, from which it takes the second and optionally third
59
+ # parameters
60
+ day = Date.today - yester
61
+ new_record = (scalar) ? {day => scalar} : day
62
+ if not @habits.has_key?(key)
63
+ print "#{key} is not a habit yet. create and complete it? (y/n) "
64
+ if STDIN.gets.chomp == 'y'
65
+ @habits[key] = [new_record]
66
+ end
67
+ else
68
+ @habits[key] << new_record
69
+ end
70
+ File.open(@habiter_file, 'w') do |f|
71
+ YAML.dump(@habits, f)
56
72
  end
57
- else
58
- @habits[key] << new_record
59
- end
60
- File.open(@habiter_file, 'w') do |f|
61
- YAML.dump(@habits, f)
62
73
  end
63
- end
64
74
 
65
-
66
- def log(options={})
67
- options[:days] = 28 unless options[:days]
68
- name_column_width = @habits.max {|a,b| a[0].length <=> b[0].length}[0].length
69
- day = Date.today - options[:days]
70
- if options[:verbose]
71
- print " " * (name_column_width + 2)
72
- (0..options[:days]).each do |i|
73
- print (day + (i)).strftime('%a')[0].chr.swapcase.colorize(37)
75
+ def log(options={})
76
+ # Display a basic visualization of recent task completion
77
+ options[:days] = 28 unless options[:days]
78
+ name_column_width = @habits.max {|a,b| a[0].length <=> b[0].length}[0].length
79
+ day = Date.today - options[:days]
80
+ if options[:verbose]
81
+ print " " * (name_column_width + 2)
82
+ (0..options[:days]).each do |i|
83
+ print((day + (i)).strftime('%a')[0].chr.swapcase.colorize(37))
84
+ end
85
+ puts
74
86
  end
75
- puts
76
- end
77
- daily_counts = Array.new(29, 0)
78
- @habits.each do |habit, times|
79
- day = Date.today - (options[:days])
80
- print (sprintf("%#{name_column_width}s ", habit) + "|").colorize(37)
81
- times.collect! do |time|
82
- if time.is_a? Hash
83
- # For scalar values, normalize
84
- time = time.keys.first
87
+ daily_counts = Array.new(29, 0)
88
+ @habits.each do |habit, times|
89
+ day = Date.today - (options[:days])
90
+ print((sprintf("%#{name_column_width}s ", habit) + "|").colorize(37))
91
+ times.collect! do |time|
92
+ normalize_time(time)
85
93
  end
86
- if time.is_a? Time
87
- time.to_date
88
- elsif time.is_a? Date
89
- # For user-inputted values, normalize
90
- time
94
+ done_in_last_28, week, pre_week = 0, 0, 0
95
+ (0..options[:days]).each do |i|
96
+ if times.include? day
97
+ print "+".colorize(47)
98
+ daily_counts[i] += 1
99
+ done_in_last_28 += 1
100
+ pre_week += 1 unless i >= 20
101
+ week += 1 unless i < 20
102
+ else
103
+ print "-".colorize(37)
104
+ end
105
+ day += 1
91
106
  end
92
- end
93
- done_in_last_28, week, pre_week = 0, 0, 0
94
- (0..options[:days]).each do |i|
95
- if times.include? day
96
- print "+".colorize(47)
97
- daily_counts[i] += 1
98
- done_in_last_28 += 1
99
- pre_week += 1 unless i >= 20
100
- week += 1 unless i < 20
107
+ if ((pre_week / 3) > (week + 2))
108
+ print "|".colorize(37)
109
+ print "(#{done_in_last_28}/28)".colorize(31)
101
110
  else
102
- print "-".colorize(37)
111
+ print "|".colorize(37)
112
+ print "(#{done_in_last_28}/28)".colorize(32)
103
113
  end
104
- day += 1
114
+ puts
105
115
  end
106
- if ((pre_week / 3) > (week + 2))
107
- print "|".colorize(37)
108
- print "(#{done_in_last_28}/28)".colorize(31)
109
- else
110
- print "|".colorize(37)
111
- print "(#{done_in_last_28}/28)".colorize(32)
116
+ if options[:verbose]
117
+ print " " * (name_column_width + 2)
118
+ (0..options[:days]).each do |i|
119
+ print((daily_counts[i] > 0) ? daily_counts[i] : " ")
120
+ end
121
+ puts
112
122
  end
113
- puts
114
123
  end
115
- if options[:verbose]
116
- print " " * (name_column_width + 2)
117
- (0..options[:days]).each do |i|
118
- print (daily_counts[i] > 0) ? daily_counts[i] : " "
119
- end
120
- puts
124
+ end
125
+
126
+ def normalize_time(time)
127
+ if time.is_a? Hash
128
+ time = time.keys.first
129
+ end
130
+ if time.is_a? Time
131
+ time.to_date
132
+ elsif time.is_a? Date
133
+ time
134
+ end
135
+ end
136
+
137
+
138
+
139
+
140
+ hm = HabiterBin.new
141
+
142
+ #
143
+ # Command-line handling
144
+ #
145
+
146
+ options = {}
147
+ optparse = OptionParser.new do |opts|
148
+ opts.banner =
149
+ "Usage:
150
+ habiter did your_habit
151
+ habiter log"
152
+ options[:verbose] = false
153
+ opts.on('-v', '--verbose', "Show weekday letters for log") do
154
+ options[:verbose] = true
155
+ end
156
+ options[:yester] = 0
157
+ opts.on('-y', '--yester [DAYS]',
158
+ "Log an event in the past, given a number of days") do |days|
159
+ options[:yester] = days.to_i
121
160
  end
122
161
  end
123
162
 
124
163
  optparse.parse!
125
164
 
126
165
  if ARGV[0] == 'did'
127
- did(ARGV)
166
+ key = ARGV[1]
167
+ scalar = (ARGV.length > 3) ? ARGV[2] : false
168
+ hm.did(key, scalar, options[:yester])
128
169
  elsif ARGV[0] == 'log'
129
- log(options)
130
- else
131
- puts optparse
170
+ hm.log(options)
171
+ elsif ARGV[0] == 'dump'
172
+ hm.dump(options)
132
173
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: habiter
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.2"
4
+ version: "0.3"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom MacWright
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-11-06 00:00:00 -05:00
12
+ date: 2009-12-13 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -23,7 +23,7 @@ extra_rdoc_files: []
23
23
 
24
24
  files:
25
25
  - bin/habiter
26
- - README
26
+ - README.rdoc
27
27
  has_rdoc: true
28
28
  homepage: http://github.com/tmcw/habiter
29
29
  licenses: []
data/README DELETED
@@ -1,54 +0,0 @@
1
- habiter is a simple tool for maintaining good habits
2
-
3
- It can be used with the 'don't break the chain' technique, like
4
- Joe's Goals or Sciral Consistency, but adopts more positive
5
- techniques and uses a readable and editable YAML text file as
6
- storage.
7
-
8
- Demo
9
- ----
10
-
11
- ~$ habiter did clean_room
12
- clean_room is not a habit yet. create and complete it? (y/n) y
13
- ~$ habiter did jog
14
- jog is not a habit yet. create and complete it? (y/n) y
15
- ~$ habiter log
16
- jog |----------------------------+|(1/28)
17
- project |-------------------------++-+|(3/28)
18
- practice_music |-----------------------+++++-|(5/28)
19
- read_30m |-----------------------+--++-|(3/28)
20
- clean_room |-----------------------+-+---|(2/28)
21
-
22
- Scalars
23
- -------
24
-
25
- Habiter also now supports scalars: you can input a value for your
26
- activity, like
27
-
28
- habiter did jog 5.5
29
- habiter did heartbeat 78
30
-
31
- And it will be recorded. There is no representation of this data yet,
32
- and the representation within habiter will be minimal, only good output to
33
- formats which are easily visualized.
34
-
35
- Install
36
- -------
37
-
38
- Install with 'gem install habiter' if you have gemcutter set up as a
39
- repository
40
-
41
- FAQ
42
- ---
43
-
44
- * How do I add past data?
45
- How do I delete recorded data?
46
- How do I modify stuff I've recorded?
47
-
48
- Edit ~/.habiter.yaml
49
-
50
- * How do I reset habiter?
51
-
52
- echo > ~/.habiter.yaml
53
-
54
- Dedicated to Bryn Bellomy :)