todo.txt 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +7 -0
  3. data/Gemfile.lock +29 -0
  4. data/MIT-LICENSE +21 -0
  5. data/NOTES.md +0 -0
  6. data/README.md +11 -0
  7. data/lib/todo.rb +20 -0
  8. data/lib/todo/cli.rb +39 -0
  9. data/lib/todo/cli/archive.rb +39 -0
  10. data/lib/todo/cli/cmd.rb +26 -0
  11. data/lib/todo/cli/list.rb +42 -0
  12. data/lib/todo/cli/push.rb +42 -0
  13. data/lib/todo/cli/toggle.rb +22 -0
  14. data/lib/todo/data/item.rb +58 -0
  15. data/lib/todo/data/list.rb +64 -0
  16. data/lib/todo/data/matcher.rb +42 -0
  17. data/lib/todo/data/parser.rb +41 -0
  18. data/lib/todo/helpers/hash/compact.rb +13 -0
  19. data/lib/todo/helpers/hash/format.rb +13 -0
  20. data/lib/todo/helpers/hash/slice.rb +17 -0
  21. data/lib/todo/helpers/object/presence.rb +26 -0
  22. data/lib/todo/helpers/string/camelize.rb +16 -0
  23. data/lib/todo/src/file.rb +24 -0
  24. data/lib/todo/src/idonethis.rb +52 -0
  25. data/lib/todo/src/io.rb +29 -0
  26. data/lib/todo/support/http.rb +31 -0
  27. data/lib/todo/support/options_parser.rb +31 -0
  28. data/lib/todo/version.rb +3 -0
  29. data/lib/todo/view.rb +49 -0
  30. data/spec/spec_helper.rb +9 -0
  31. data/spec/support/io.rb +9 -0
  32. data/spec/todo/cli/archive_spec.rb +16 -0
  33. data/spec/todo/cli/list_spec.rb +13 -0
  34. data/spec/todo/cli/toggle_spec.rb +25 -0
  35. data/spec/todo/data/item_spec.rb +94 -0
  36. data/spec/todo/data/list_spec.rb +59 -0
  37. data/spec/todo/data/parser_spec.rb +27 -0
  38. data/spec/todo/helpers/hash/format_spec.rb +9 -0
  39. data/spec/todo/helpers/hash/slice_spec.rb +8 -0
  40. data/spec/todo/helpers/object/presence_spec.rb +42 -0
  41. data/spec/todo/helpers/string/string_spec.rb +7 -0
  42. data/spec/todo/src/file_spec.rb +33 -0
  43. data/spec/todo/src/io_spec.rb +18 -0
  44. data/spec/todo/view_spec.rb +17 -0
  45. data/todo.gemspec +20 -0
  46. data/todo.md +10 -0
  47. metadata +89 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7408515630ff0a7a199d208e569d5af69a0a9b07
4
+ data.tar.gz: fe754ec5f2d4d9c0c1bb99bfec0832bfeeff7f09
5
+ SHA512:
6
+ metadata.gz: 4ee0e1ce12f4c1a5b06cd06e0ae551df1f81d9a8a1be0bd14a23c46054e7d6cad2c557667b1397775a8f0ffc5cef6af5b919c84b3df6db956cb4ad8fb24a3e21
7
+ data.tar.gz: da17c317209c29584e18fe58a2c34cd1769dc93bb3242d84cfbdb2871d1b4a8dd731aac551ccdcce04e61010ae59f198027546f2a31345277cf15bbf811e7b1c
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ group :test do
4
+ gem 'rspec'
5
+ gem 'mocha'
6
+ gem 'capture_stdout'
7
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,29 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ capture_stdout (0.0.1)
5
+ diff-lcs (1.2.5)
6
+ metaclass (0.0.4)
7
+ mocha (1.1.0)
8
+ metaclass (~> 0.0.1)
9
+ rspec (3.3.0)
10
+ rspec-core (~> 3.3.0)
11
+ rspec-expectations (~> 3.3.0)
12
+ rspec-mocks (~> 3.3.0)
13
+ rspec-core (3.3.2)
14
+ rspec-support (~> 3.3.0)
15
+ rspec-expectations (3.3.1)
16
+ diff-lcs (>= 1.2.0, < 2.0)
17
+ rspec-support (~> 3.3.0)
18
+ rspec-mocks (3.3.2)
19
+ diff-lcs (>= 1.2.0, < 2.0)
20
+ rspec-support (~> 3.3.0)
21
+ rspec-support (3.3.0)
22
+
23
+ PLATFORMS
24
+ ruby
25
+
26
+ DEPENDENCIES
27
+ capture_stdout
28
+ mocha
29
+ rspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT LICENSE
2
+
3
+ Copyright (c) 2015 Sven Fuchs <me@svenfuchs.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/NOTES.md ADDED
File without changes
data/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # TODO.txt
2
+
3
+ My version of the [Todo.txt](https://github.com/ginatrapani/todo.txt-cli/wiki/The-Todo.txt-Format) format.
4
+
5
+ Integration:
6
+
7
+ * Vim mapping to [toggle](https://github.com/svenfuchs/vim-todo.txt/blob/master/ftplugin/todo.vim#L1) a todo item status
8
+ * Vim mapping to [push](https://github.com/svenfuchs/vim-todo.txt/blob/master/ftplugin/todo.vim#L2) done items to idonethis
9
+ * Vim mapping to [archive](https://github.com/svenfuchs/vim-todo.txt/blob/master/ftplugin/todo.vim#L3) done items to a separate file
10
+ * [launchctl](https://github.com/svenfuchs/todo.txt/blob/master/etc/me.todo-watch.plist) to `fswatch` the file, and [git push](https://github.com/svenfuchs/todo.txt/blob/master/bin/push) changes to a Gist
11
+
data/lib/todo.rb ADDED
@@ -0,0 +1,20 @@
1
+ module Todo
2
+ MSGS = {
3
+ unknown_cmd: 'Unknown command %p',
4
+ item_not_found: 'Could not find item for: %s',
5
+ multiple_items: 'Multiple items found for: %s'
6
+ }
7
+
8
+ DATES = {
9
+ two_weeks_ago: (Time.now - 60 * 60 * 24 * 14).strftime('%Y-%m-%d'),
10
+ yesterday: (Time.now - 60 * 60 * 24).strftime('%Y-%m-%d'),
11
+ today: Time.now.strftime('%Y-%m-%d')
12
+ }
13
+
14
+ STATUSES = {
15
+ done: 'x',
16
+ pend: '-'
17
+ }
18
+
19
+ Error = Class.new(StandardError)
20
+ end
data/lib/todo/cli.rb ADDED
@@ -0,0 +1,39 @@
1
+ require 'optparse'
2
+ require 'todo'
3
+ require 'todo/cli/archive'
4
+ require 'todo/cli/list'
5
+ require 'todo/cli/push'
6
+ require 'todo/cli/toggle'
7
+ require 'todo/helpers/string/camelize'
8
+
9
+ module Todo
10
+ class Cli
11
+ include Helpers::String::Camelize
12
+
13
+ attr_reader :cmd, :args, :opts
14
+
15
+ def initialize(args)
16
+ @args = args
17
+ @cmd = @args.shift || raise('No command given')
18
+ end
19
+
20
+ def run
21
+ args, opts = const.parse(self.args)
22
+ const.new(args, opts).run
23
+ rescue Error => e
24
+ abort "Error: #{e.message}"
25
+ end
26
+
27
+ private
28
+
29
+ def normalize_date(date)
30
+ DATES[date.to_sym] ? DATES[date.to_sym] : date
31
+ end
32
+
33
+ def const
34
+ @const ||= Cli.const_get(camelize(cmd))
35
+ rescue NameError
36
+ fail Error.new(MSGS[:unknown_cmd] % cmd)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,39 @@
1
+ require 'todo/cli/cmd'
2
+
3
+ module Todo
4
+ class Cli
5
+ class Archive < Cmd
6
+ opt '-a', '--archive FILENAME', 'Archive filename' do |opts, file|
7
+ opts[:archive] = file
8
+ end
9
+
10
+ opt '-b', '--before DATE', 'Before date' do |opts, date|
11
+ opts[:before] = normalize_date(date)
12
+ end
13
+
14
+ def run
15
+ items = list.select(status: :done, before: before).items
16
+ archive.write(render(items))
17
+ io.write(render(list.items - items))
18
+ end
19
+
20
+ private
21
+
22
+ def list
23
+ @list ||= Data::List.parse(io.read)
24
+ end
25
+
26
+ def archive
27
+ @archive ||= Src::File.new(archive_path, mode: 'a+')
28
+ end
29
+
30
+ def archive_path
31
+ opts[:archive] || File.expand_path('../archive.txt', opts[:file])
32
+ end
33
+
34
+ def before
35
+ opts[:before] || DATES[:two_weeks_ago]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,26 @@
1
+ require 'todo/helpers/hash/slice'
2
+ require 'todo/src/file'
3
+ require 'todo/src/io'
4
+ require 'todo/support/options_parser'
5
+ require 'todo/view'
6
+
7
+ module Todo
8
+ class Cli
9
+ class Cmd < Struct.new(:args, :opts)
10
+ extend Support::OptionsParser
11
+ include Helpers::Hash::Slice
12
+
13
+ opt '-f', '--file FILENAME', 'Filename' do |opts, file|
14
+ opts[:file] = file
15
+ end
16
+
17
+ def io
18
+ opts[:file] ? Src::File.new(opts[:file]) : Src::Io.new(slice(opts, :in, :out))
19
+ end
20
+
21
+ def render(list, cols = nil)
22
+ View.new(list, cols).render
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,42 @@
1
+ require 'todo/cli/cmd'
2
+ require 'todo/data/list'
3
+
4
+ module Todo
5
+ class Cli
6
+ class List < Cmd
7
+ opt '-s', '--since DATE', 'Since date' do |date|
8
+ opts[:since] = normalize_date(date)
9
+ end
10
+
11
+ opt '-b', '--before DATE', 'Before date' do |date|
12
+ opts[:before] = normalize_date(date)
13
+ end
14
+
15
+ opt '--status STATUS', 'Status' do |status|
16
+ opts[:status] = status
17
+ end
18
+
19
+ def run
20
+ out.write(render(list.items, [:done_date, :text]))
21
+ end
22
+
23
+ private
24
+
25
+ def list
26
+ list = Data::List.parse(io.read)
27
+ list = list.select(data)
28
+ list.sort_by { |item| item.done_date.to_s }
29
+ end
30
+
31
+ def data
32
+ data = slice(opts, :status, :before, :since)
33
+ # data = data.merge(text: args.first)
34
+ data
35
+ end
36
+
37
+ def out
38
+ Src::Io.new(out: opts[:out])
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,42 @@
1
+ require 'todo'
2
+ require 'todo/cli/cmd'
3
+ require 'todo/data/list'
4
+ require 'todo/src/idonethis'
5
+
6
+ module Todo
7
+ class Cli
8
+ class Push < Cmd
9
+ opt '-s', '--since DATE', 'Since date' do |date|
10
+ opts[:since] = normalize_date(date)
11
+ end
12
+
13
+ CONFIG = {
14
+ team: ENV['IDONETHIS_TEAM'],
15
+ username: ENV['IDONETHIS_USERNAME'],
16
+ token: ENV['IDONETHIS_TOKEN']
17
+ }
18
+
19
+ def run
20
+ lines = render(list.items, [:text, :tags, :id])
21
+ src.write(lines)
22
+ io.write(lines)
23
+ end
24
+
25
+ private
26
+
27
+ def list
28
+ list = Data::List.parse(io.read)
29
+ list = list.select(status: :done, since: since)
30
+ list.reject { |item| store.include?(item) }
31
+ end
32
+
33
+ def src
34
+ Src::Idonethis.new(config, since: since)
35
+ end
36
+
37
+ def since
38
+ opts[:since] || DATES[:yesterday]
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,22 @@
1
+ require 'todo/cli/cmd'
2
+ require 'todo/data/list'
3
+ require 'todo/data/parser'
4
+
5
+ module Todo
6
+ class Cli
7
+ class Toggle < Cmd
8
+ def run
9
+ list = Data::List.parse(io.read)
10
+ list.toggle(data)
11
+ io.write(render(list.items))
12
+ end
13
+
14
+ private
15
+
16
+ def data
17
+ data = Data::Parser.new(args.first).parse
18
+ slice(data, :id, :text)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,58 @@
1
+ require 'todo/data/matcher'
2
+
3
+ module Todo
4
+ module Data
5
+ class Item < Struct.new(:data)
6
+ PROJECT = /\s*\+([\w\-]+)/
7
+
8
+ def initialize(data)
9
+ data[:tags] ||= {}
10
+ super
11
+ end
12
+
13
+ [:id, :status, :tags, :text].each do |name|
14
+ define_method(name) { data[name] }
15
+ end
16
+
17
+ def done?
18
+ status == :done
19
+ end
20
+
21
+ def due_date
22
+ tags[:due].to_s if tags[:due]
23
+ end
24
+
25
+ def done_date
26
+ tags[:done].to_s if tags[:done]
27
+ end
28
+
29
+ def projects
30
+ @projects ||= text.scan(PROJECT).flatten
31
+ end
32
+
33
+ def toggle
34
+ done? ? pend : done
35
+ end
36
+
37
+ def matches?(data)
38
+ Matcher.new(self, data).matches?
39
+ end
40
+
41
+ private
42
+
43
+ def done
44
+ data[:status] = :done
45
+ data[:tags][:done] = today
46
+ end
47
+
48
+ def pend
49
+ data[:status] = :pend
50
+ data[:tags].delete(:done)
51
+ end
52
+
53
+ def today
54
+ Time.now.strftime('%Y-%m-%d')
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,64 @@
1
+ require 'forwardable'
2
+ require 'todo'
3
+ require 'todo/data/item'
4
+ require 'todo/data/parser'
5
+ require 'todo/helpers/hash/format'
6
+
7
+ module Todo
8
+ module Data
9
+ class List < Struct.new(:items)
10
+ class << self
11
+ def parse(lines)
12
+ List.new(lines.map { |line| Item.new(Parser.new(line).parse) })
13
+ end
14
+ end
15
+
16
+ extend Forwardable
17
+ include Helpers::Hash::Format
18
+
19
+ def_delegators :items, :size, :first
20
+
21
+ attr_accessor :max_id
22
+
23
+ def initialize(*)
24
+ super
25
+ @max_id = items.map(&:id).map(&:to_i).max || 0
26
+ end
27
+
28
+ def toggle(data)
29
+ find(data).toggle
30
+ end
31
+
32
+ def select(data)
33
+ self.class.new(items.select { |item| item.matches?(data) })
34
+ end
35
+
36
+ def reject(&block)
37
+ self.class.new(items.reject(&block))
38
+ end
39
+
40
+ def sort_by(&block)
41
+ self.class.new(items.sort_by(&block))
42
+ end
43
+
44
+ def find(data)
45
+ select(data).tap { |list| validate(data, list) }.first
46
+ end
47
+
48
+ def ids
49
+ items.map(&:id).compact
50
+ end
51
+
52
+ def next_id
53
+ @max_id += 1
54
+ end
55
+
56
+ private
57
+
58
+ def validate(data, list)
59
+ raise Error.new(MSGS[:item_not_found] % to_pairs(data)) if list.size == 0
60
+ raise Error.new(MSGS[:multiple_items] % to_pairs(data)) if list.size > 1
61
+ end
62
+ end
63
+ end
64
+ end