habiter 0.2 → 0.3

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 (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 :)