doing 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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: []