doing 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 099152d20912d3303aaa11fe984099c26dd658c0
4
+ data.tar.gz: e8a2977882d97fe1b93a913fbd2a2059f2d2bd43
5
+ SHA512:
6
+ metadata.gz: 0e33df82c4ada897a07b08e129024b34c357c10438df188cc50a18cd00a9bd9e1ccf029e78448e6ed7993077352c17040533f4cfd39bfc72fb7f6794f58561a0
7
+ data.tar.gz: 07a8ff1889b6f7f8a7864cdc6561f3ae33cf01f0f1fd884ac135a279342699ab84fd1816485ef79045adf76b0a8dbd1cdeeb6e491098034a8c55b52750890aa3
data/README.rdoc ADDED
@@ -0,0 +1,44 @@
1
+ = doing
2
+
3
+ A basic CLI for adding and listing "what was I doing" reminders in a text file. Allows for multiple sections and flexible output formatting.
4
+
5
+ The format of the "WWID" file is TaskPaper-compatible. You can edit it by hand at any time, but it uses a specific format for parsing, so be careful. By default the file is created in "~/what_was_i_doing.md", but this can be modified in the config file.
6
+
7
+ The config file is stored in "~/.doingrc", and is created on the first run. It contains, among other things, templates for various command outputs. Include placeholders by placing a % before the keyword. The available placeholders are:
8
+
9
+ - %title: the "wwid" entry line
10
+ - %date: the date based on the templates "date_format" setting
11
+ - %shortdate: a custom date formatter that removes the day/month/year from the entry if they match the current day/month/year
12
+ - %note: Any note in the entry will be included here, a newline and tabs are automatically added.
13
+ - %odnote: The notes with a leading tab removed (outdented note)
14
+ - %hr: a horizontal rule (`-`) the width of the terminal
15
+ - %hr_under: a horizontal rule (`_`) the width of the terminal
16
+
17
+ Date formats are based on Ruby `strftime` formatting.
18
+
19
+
20
+
21
+ Usage:
22
+
23
+ doing [global options] command [command options] [arguments...]
24
+
25
+ Global options:
26
+
27
+ --help - Show this message
28
+ --[no-]notes - Output notes if included in the template (default: enabled)
29
+ --version - Display the program version
30
+
31
+ Commands:
32
+
33
+ archive - Archive all but the most recent 5 entries
34
+ choose - Select a section to display
35
+ config - Edit the default configuration
36
+ help - Shows a list of commands or help for one command
37
+ last - Show the last entry
38
+ later - Add an item to the Later section
39
+ now - Add an entry
40
+ recent - List recent entries
41
+ sections - List sections
42
+ show - List all entries
43
+ today - List entries from today
44
+
data/bin/doing ADDED
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env ruby
2
+ require 'gli'
3
+ require 'doing'
4
+
5
+ include GLI::App
6
+
7
+ wwid = WWID.new
8
+
9
+ program_desc 'A CLI for a What Was I Doing system'
10
+
11
+ version Doing::VERSION
12
+
13
+ default_command :recent
14
+ sort_help :manually
15
+
16
+ desc 'Output notes if included in the template'
17
+ default_value true
18
+ switch [:notes], :default_value => true, :negatable => true
19
+
20
+ # desc 'Wrap notes at X chars (0 for no wrap)'
21
+ # flag [:w,:wrapwidth], :must_match => /^\d+$/, :type => Integer
22
+
23
+ desc 'Add an entry'
24
+ arg_name 'entry'
25
+ command :now do |c|
26
+ c.desc 'Section'
27
+ c.default_value wwid.current_section
28
+ c.flag [:s,:section]
29
+
30
+ c.action do |global_options,options,args|
31
+ if args.length > 0
32
+ wwid.add_item(args.join(" ").cap_first, options[:s].cap_first)
33
+ wwid.write(wwid.doing_file)
34
+ else
35
+ raise "You must provide content when creating a new entry"
36
+ end
37
+ end
38
+ end
39
+
40
+ desc 'Add an item to the Later section'
41
+ arg_name 'entry'
42
+ command :later do |c|
43
+ c.action do |global_options,options,args|
44
+ if args.length > 0
45
+ wwid.add_item(args.join(" ").cap_first, "Later")
46
+ wwid.write(wwid.doing_file)
47
+ else
48
+ raise "You must provide content when creating a new entry"
49
+ end
50
+ end
51
+ end
52
+
53
+ desc 'List all entries'
54
+ arg_name 'section'
55
+ command :show do |c|
56
+ c.action do |global_options,options,args|
57
+ if args.length > 0
58
+ if wwid.sections.include? args.join(" ").cap_first
59
+ section = args.join(" ").cap_first
60
+ else
61
+ raise "No such section: #{args.join(" ")}"
62
+ end
63
+ else
64
+ section = wwid.current_section
65
+ end
66
+ puts wwid.list_section({:section => section, :count => 0})
67
+ end
68
+ end
69
+
70
+ desc 'List recent entries'
71
+ default_value 10
72
+ arg_name 'count'
73
+ command :recent do |c|
74
+ c.desc 'Section'
75
+ c.default_value wwid.current_section
76
+ c.flag [:s,:section]
77
+ c.action do |global_options,options,args|
78
+ if args.length > 0
79
+ count = args[0].to_i
80
+ else
81
+ count = 10
82
+ end
83
+ puts wwid.recent(count,options[:s].cap_first)
84
+ end
85
+ end
86
+
87
+ desc 'List entries from today'
88
+ command :today do |c|
89
+ c.action do |global_options,options,args|
90
+ puts wwid.today.strip
91
+ end
92
+ end
93
+
94
+ desc 'Show the last entry'
95
+ command :last do |c|
96
+ c.action do |global_options,options,args|
97
+ puts wwid.last.strip
98
+ end
99
+ end
100
+
101
+ desc 'List sections'
102
+ command :sections do |c|
103
+ c.action do |global_options,options,args|
104
+ puts wwid.sections.join(", ")
105
+ end
106
+ end
107
+
108
+ desc 'Select a section to display from a menu'
109
+ command :choose do |c|
110
+ c.action do |global_options,options,args|
111
+ section = wwid.choose_section
112
+ puts wwid.list_section({:section => section.cap_first, :count => 0})
113
+ end
114
+ end
115
+
116
+ desc 'Move all but the most recent 5 entries to the Archive section'
117
+ default_value wwid.current_section
118
+ arg_name 'section'
119
+ command :archive do |c|
120
+ c.action do |global_options,options,args|
121
+ if args.length > 0
122
+ section = args.join(" ").capitalize
123
+ else
124
+ section = wwid.current_section
125
+ end
126
+ wwid.archive(section)
127
+ end
128
+ end
129
+
130
+ desc 'Edit the default configuration'
131
+ command :config do |c|
132
+ c.action do |global_options,options,args|
133
+ system %Q{"#{ENV['EDITOR']}" "#{DOING_CONFIG}"}
134
+ end
135
+ end
136
+
137
+ pre do |global,command,options,args|
138
+ wwid.config[:include_notes] = false unless global[:notes]
139
+
140
+ # Return true to proceed; false to abort and not call the
141
+ # chosen command
142
+ # Use skips_pre before a command to skip this block
143
+ # on that command only
144
+ true
145
+ end
146
+
147
+ post do |global,command,options,args|
148
+ # Use skips_post before a command to skip this
149
+ # block on that command only
150
+ end
151
+
152
+ on_error do |exception|
153
+ # Error logic here
154
+ # return false to skip default error handling
155
+ true
156
+ end
157
+
158
+ exit run(ARGV)
data/doing.rdoc ADDED
@@ -0,0 +1 @@
1
+ = doing
@@ -0,0 +1,3 @@
1
+ module Doing
2
+ VERSION = '0.1.3'
3
+ end
data/lib/doing/wwid.rb ADDED
@@ -0,0 +1,307 @@
1
+ #!/usr/bin/ruby
2
+ require 'time'
3
+ require 'date'
4
+ require 'yaml'
5
+ require 'pp'
6
+
7
+ class String
8
+ def cap_first
9
+ self.sub(/^\w/) do |m|
10
+ m.upcase
11
+ end
12
+ end
13
+ end
14
+
15
+ class WWID
16
+ attr_accessor :content, :sections, :current_section, :doing_file, :config
17
+
18
+
19
+ def initialize(input=nil)
20
+ @content = {}
21
+
22
+ @config = read_config
23
+
24
+ @config['doing_file'] ||= "~/what_was_i_doing.md"
25
+ @config['current_section'] ||= 'Currently'
26
+
27
+ @config['templates'] ||= {}
28
+ @config['templates']['default'] ||= {
29
+ 'date_format' => '%Y-%m-%d %H:%M',
30
+ 'template' => '%date | %title%note',
31
+ 'wrap_width' => 0
32
+ }
33
+ @config['templates']['today'] ||= {
34
+ 'date_format' => '%_I:%M%P',
35
+ 'template' => '%date: %title%note',
36
+ 'wrap_width' => 0
37
+ }
38
+ @config['templates']['last'] ||= {
39
+ 'date_format' => '%_I:%M%P',
40
+ 'template' => '%date: %title%note',
41
+ 'wrap_width' => 60
42
+ }
43
+ @config['templates']['recent'] ||= {
44
+ 'date_format' => '%_I:%M%P',
45
+ 'template' => '%shortdate: %title',
46
+ 'wrap_width' => 60
47
+ }
48
+
49
+ @doing_file = File.expand_path(config['doing_file'])
50
+ @current_section = config['current_section']
51
+ @default_template = config['templates']['default']['template']
52
+ @default_date_format = config['templates']['default']['date_format']
53
+
54
+ @config[:include_notes] ||= true
55
+
56
+ File.open(File.expand_path(DOING_CONFIG), 'w') { |yf| YAML::dump(config, yf) }
57
+
58
+ if input.nil?
59
+ create(@doing_file) unless File.exists?(@doing_file)
60
+ input = IO.read(@doing_file)
61
+ input = input.force_encoding('utf-8') if input.respond_to? :force_encoding
62
+ elsif File.exists?(File.expand_path(input)) && File.file?(File.expand_path(input)) && File.stat(File.expand_path(input)).size > 0
63
+ input = IO.read(File.expand_path(input))
64
+ input = input.force_encoding('utf-8') if input.respond_to? :force_encoding
65
+ @doing_file = File.expand_path(input)
66
+ elsif input.length < 256
67
+ create(input)
68
+ end
69
+
70
+ @other_content_top = []
71
+ @other_content_bottom = []
72
+
73
+ section = "Uncategorized"
74
+ lines = input.split(/\n/)
75
+ current = 0
76
+
77
+ lines.each {|line|
78
+ next if line =~ /^\s*$/
79
+ if line =~ /^(\w[\w ]+):\s*(@\S+\s*)*$/
80
+ section = $1
81
+ @content[section] = {}
82
+ @content[section]['original'] = line
83
+ @content[section]['items'] = []
84
+ current = 0
85
+ elsif line =~ /^\s*- (\d{4}-\d\d-\d\d \d\d:\d\d) \| (.*)/
86
+ date = Time.parse($1)
87
+ title = $2
88
+ @content[section]['items'].push({'title' => title, 'date' => date})
89
+ current += 1
90
+ else
91
+ # if content[section]['items'].length - 1 == current
92
+ if current == 0
93
+ @other_content_top.push(line)
94
+ else
95
+ if line =~ /^\S/
96
+ @other_content_bottom.push(line)
97
+ else
98
+ unless @content[section]['items'][current - 1].has_key? 'note'
99
+ @content[section]['items'][current - 1]['note'] = []
100
+ end
101
+ @content[section]['items'][current - 1]['note'].push(line)
102
+ end
103
+ end
104
+ # end
105
+ end
106
+ }
107
+ end
108
+
109
+ def create(filename=nil)
110
+ filename = @doing_file
111
+ unless File.exists?(filename) && File.stat(filename).size > 0
112
+ File.open(filename,'w+') do |f|
113
+ f.puts @current_section + ":"
114
+ end
115
+ end
116
+ end
117
+
118
+ def read_config
119
+ if File.exists? File.expand_path(DOING_CONFIG)
120
+ return YAML.load_file(File.expand_path(DOING_CONFIG))
121
+ else
122
+ return {}
123
+ end
124
+ end
125
+
126
+ def sections
127
+ @content.keys
128
+ end
129
+
130
+ def add_section(title)
131
+ @content[title.cap_first] = {'original' => "#{title}:", 'items' => []}
132
+ end
133
+
134
+ def add_item(title,section=nil,opt={})
135
+ section = @current_section if section.nil?
136
+ add_section(section) unless @content.has_key?(section)
137
+ opt[:date] ||= Time.now
138
+ opt[:note] ||= ""
139
+
140
+ entry = {'title' => title.strip.cap_first, 'date' => opt[:date]}
141
+ unless opt[:note] =~ /^\s*$/s
142
+ entry['note'] = opt[:note]
143
+ end
144
+ @content[section]['items'].push(entry)
145
+ end
146
+
147
+ def write(file=nil)
148
+ if @other_content_top.empty?
149
+ output = ""
150
+ else
151
+ output = @other_content_top.join("\n") + "\n"
152
+ end
153
+ @content.each {|title, section|
154
+ output += section['original'] + "\n"
155
+ output += list_section({:section => title, :template => "\t- %date | %title%note"})
156
+ }
157
+ output += @other_content_bottom.join("\n")
158
+ if file.nil?
159
+ $stdout.puts output
160
+ else
161
+ if File.exists?(File.expand_path(file))
162
+ File.open(File.expand_path(file),'w+') do |f|
163
+ f.puts output
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ def choose_section
170
+ sections.each_with_index {|section, i|
171
+ puts "% 3d: %s" % [i+1, section]
172
+ }
173
+ print "> "
174
+ num = STDIN.gets
175
+ return false if num =~ /^[a-z ]*$/i
176
+ return sections[num.to_i - 1]
177
+ end
178
+
179
+ def list_section(opt={})
180
+ opt[:count] ||= 0
181
+ count = opt[:count] - 1
182
+ opt[:section] ||= nil
183
+ opt[:format] ||= @default_date_format
184
+ opt[:template] ||= @default_template
185
+ opt[:order] ||= "desc"
186
+ opt[:today] ||= false
187
+
188
+ if opt[:section].nil?
189
+ opt[:section] = @content[choose_section]
190
+ elsif opt[:section].class == String
191
+ if @content.has_key? opt[:section]
192
+ opt[:section] = @content[opt[:section]]
193
+ else
194
+ $stderr.puts "Section '#{opt[:section]}' not found"
195
+ return
196
+ end
197
+ end
198
+
199
+ if opt[:section].class != Hash
200
+ $stderr.puts "Invalid section object"
201
+ return
202
+ end
203
+
204
+ items = opt[:section]['items'].sort_by{|item| item['date'] }
205
+
206
+ if opt[:today]
207
+ items.delete_if {|item|
208
+ item['date'] < Date.today.to_time
209
+ }.reverse!
210
+ else
211
+ items = items.reverse[0..count]
212
+ end
213
+
214
+ items.reverse! if opt[:order] =~ /^asc/i
215
+
216
+ out = ""
217
+
218
+ items.each {|item|
219
+ if (item.has_key?('note') && !item['note'].empty?) && @config[:include_notes]
220
+ note_lines = item['note'].delete_if{|line| line =~ /^\s*$/ }.map{|line| "\t\t" + line.sub(/^\t\t/,'') }
221
+ if opt[:wrap_width] && opt[:wrap_width] > 0
222
+ width = opt[:wrap_width]
223
+ note_lines.map! {|line|
224
+ line.strip.gsub(/(.{1,#{width}})(\s+|\Z)/, "\t\\1\n")
225
+ }
226
+ end
227
+ note = "\n#{note_lines.join("\n").chomp}"
228
+ else
229
+ note = ""
230
+ end
231
+ output = opt[:template].dup
232
+ output.sub!(/%date/,item['date'].strftime(opt[:format]))
233
+ output.sub!(/%shortdate/) {
234
+ if item['date'] > Date.today.to_time
235
+ item['date'].strftime('%_I:%M%P')
236
+ elsif item['date'] > (Date.today - 7).to_time
237
+ item['date'].strftime('%a %-I:%M%P')
238
+ elsif item['date'].year == Date.today.year
239
+ item['date'].strftime('%b %d, %-I:%M%P')
240
+ else
241
+ item['date'].strftime('%b %d %Y, %-I:%M%P')
242
+ end
243
+ }
244
+ output.sub!(/%title/) {|m|
245
+ if opt[:wrap_width] && opt[:wrap_width] > 0
246
+ item['title'].gsub(/(.{1,#{opt[:wrap_width]}})(\s+|\Z)/, "\\1\n\t ").strip
247
+ else
248
+ item['title'].strip
249
+ end
250
+ }
251
+ output.sub!(/%note/,note)
252
+ output.sub!(/%odnote/,note.gsub(/\t\t/,"\t"))
253
+ output.gsub!(/%hr(_under)?/) do |m|
254
+ o = ""
255
+ `tput cols`.to_i.times do
256
+ o += $1.nil? ? "-" : "_"
257
+ end
258
+ o
259
+ end
260
+ out += output + "\n"
261
+ }
262
+
263
+ return out
264
+ end
265
+
266
+ def archive(section=nil)
267
+ section = choose_section if section.nil? || section =~ /choose/i
268
+ if sections.include?(section)
269
+ items = @content[section]['items']
270
+ return if items.length < 10
271
+ @content[section]['items'] = items[0..5]
272
+ add_section('Archive') unless sections.include?('Archive')
273
+ @content['Archive']['items'] += items[6..-1]
274
+ write(@doing_file)
275
+ end
276
+ end
277
+
278
+ def all(order="")
279
+ order = "asc" if order == ""
280
+ cfg = @config['templates']['default_template']
281
+ list_section({:section => @current_section, :wrap_width => cfg['wrap_width'], :count => 0, :format => cfg['date_format'], :template => cfg['template'], :order => order})
282
+ end
283
+
284
+ def today
285
+ cfg = @config['templates']['today']
286
+ list_section({:section => @current_section, :wrap_width => cfg['wrap_width'], :count => 0, :format => cfg['date_format'], :template => cfg['template'], :order => "asc", :today => true})
287
+ end
288
+
289
+ def recent(count=10,section=nil)
290
+ cfg = @config['templates']['recent']
291
+ section ||= @current_section
292
+ list_section({:section => section, :wrap_width => cfg['wrap_width'], :count => count, :format => cfg['date_format'], :template => cfg['template'], :order => "asc"})
293
+ end
294
+
295
+ def last
296
+ cfg = @config['templates']['last']
297
+ list_section({:section => @current_section, :wrap_width => cfg['wrap_width'], :count => 1, :format => cfg['date_format'], :template => cfg['template']})
298
+ end
299
+
300
+ end
301
+
302
+ # infile = "~/Dropbox/nvALT2.2/?? What was I doing.md"
303
+
304
+ # wwid = WWID.new(infile)
305
+
306
+ # wwid.add_item("Getting freaky with wwid CLI","Currently",{:date => Time.now})
307
+ # wwid.write(infile)
data/lib/doing.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'doing/version.rb'
2
+ require 'doing/wwid.rb'
3
+
4
+ DOING_CONFIG = "~/.doingrc"
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: doing
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ platform: ruby
6
+ authors:
7
+ - Brett Terpstra
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rdoc
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '4.1'
34
+ - - '>='
35
+ - !ruby/object:Gem::Version
36
+ version: 4.1.1
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: '4.1'
44
+ - - '>='
45
+ - !ruby/object:Gem::Version
46
+ version: 4.1.1
47
+ - !ruby/object:Gem::Dependency
48
+ name: aruba
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: gli
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - '='
66
+ - !ruby/object:Gem::Version
67
+ version: 2.7.0
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - '='
73
+ - !ruby/object:Gem::Version
74
+ version: 2.7.0
75
+ description: A tool for managing a TaskPaper-like file of recent activites. Perfect
76
+ for the late-night hacker on too much caffeine to remember what they accomplished
77
+ at 2 in the morning.
78
+ email: me@brettterpstra.com
79
+ executables:
80
+ - doing
81
+ extensions: []
82
+ extra_rdoc_files:
83
+ - README.rdoc
84
+ - doing.rdoc
85
+ files:
86
+ - README.rdoc
87
+ - bin/doing
88
+ - doing.rdoc
89
+ - lib/doing.rb
90
+ - lib/doing/version.rb
91
+ - lib/doing/wwid.rb
92
+ homepage: http://brettterpstra.com
93
+ licenses:
94
+ - MIT
95
+ metadata: {}
96
+ post_install_message:
97
+ rdoc_options:
98
+ - --title
99
+ - doing
100
+ - --main
101
+ - README.rdoc
102
+ - -ri
103
+ require_paths:
104
+ - lib
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubyforge_project:
118
+ rubygems_version: 2.2.2
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: A command line tool for managing What Was I Doing reminders
122
+ test_files: []