todo.txt 0.0.6 → 0.0.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9005f5878ad1e85af811526c8a82ad566d096ca2
4
- data.tar.gz: 8874fd1b652471cb6476ecdb3e939c490b2f263a
3
+ metadata.gz: 8ae39ffb0c0aead27cdce011859dfd69b13e81d0
4
+ data.tar.gz: d2853360fc12bac16a22e4ad79ba622469dfe681
5
5
  SHA512:
6
- metadata.gz: 9b55d6efd15701f7228361db0b20386d721872c077d461b1be734d5076344c1c813269f016f601ae9d39d4602d36ca47a8afe5fb7a59cff60b335f272485b54b
7
- data.tar.gz: e8a653e2279bdf69eeaa4afd53f8d1447773970376fe10fefe5e4279ab1b58dd0f74d74c41c02fcef2618553533728f01b2a209c1b8edc87bcfa588df898df88
6
+ metadata.gz: af94d2d5bd8d9dcfc9545dab80e6363e45f06109eee1be28ef113a5cb9cae0f7d8641ec0498e7557a56e305631b60ec520e86c29c8491adb3045003f6aca9b10
7
+ data.tar.gz: 26d81779892440e92a5b77be756f6c20f554fe672ab6ad7c14785a95834653e9dba2a5c7b94c0d9317487fa0c1f31ed129b2bac30d230342bf0126327514c9b8
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # TODO.txt
1
+ # TODO.txt [![Build Status](https://secure.travis-ci.org/svenfuchs/todo.txt.png?branch=master)](https://travis-ci.org/svenfuchs/todo.txt)
2
2
 
3
- - My personal flavor of the format. [Todo.txt](https://github.com/ginatrapani/todo.txt-cli/wiki/The-Todo.txt-Format)
3
+ My personal flavor of the format. [Todo.txt](https://github.com/ginatrapani/todo.txt-cli/wiki/The-Todo.txt-Format)
4
4
 
5
5
  ## Assumptions
6
6
 
@@ -12,9 +12,9 @@ todo.txt file personally. Your mileage will most probably vary.
12
12
  etc) is a good thing. A tool must play nice with this and must not rewrite
13
13
  any custom formatting.
14
14
  * An item can belong to one or many projects.
15
- * The concept of "contexts" (phone, work, home) seems like a stale from the GTD
16
- era. Does anybody actually use this? Also, the format `@name` indicates a
17
- person in most contexts nowadays.
15
+ * The concept of "contexts" (phone, work, home) seems like a stale relict from
16
+ the GTD era to me. Does anybody actually use this? Also, the format `@name`
17
+ indicates a person in most contexts nowadays.
18
18
  * Assigning explicit priorities don't really work well. Re-ordering items on
19
19
  the todo.txt list works better.
20
20
  * A concept of generic key/value pairs seems like a useful addition to make the
@@ -40,7 +40,6 @@ $ todo toggle foo # should assume ./TODO.txt but i can't figure
40
40
 
41
41
  # Filtering items
42
42
 
43
- $ todo list --since yesterday # by done date
44
43
  $ todo list --since 2015-12-01 --before 2015-11-01 # by done date
45
44
  $ todo list --status pending # by status
46
45
  $ todo list --status done # by status
@@ -50,13 +49,19 @@ $ todo list --text foo # by text
50
49
  $ todo list foo # by text
51
50
  $ todo list foo --since 2015-12-01 --status done # by text, done date, and status
52
51
 
53
- Named dates are: one_month_ago, two_weeks_ago, one_week_ago: two_days_ago,
54
- yesterday, today.
52
+ # Named dates
53
+
54
+ $ todo list --since yesterday
55
+ $ todo list --since one.week.ago
56
+ $ todo list --since 'two weeks ago'
57
+ $ todo list --since 1_year_ago
58
+ $ todo list --since 'last friday'
55
59
 
56
60
  # Formats
57
61
 
58
62
  $ todo list --format short
59
63
  $ todo list --format full
64
+ $ todo list --format id,text,tags
60
65
 
61
66
  # Toggling
62
67
 
@@ -1,4 +1,6 @@
1
1
  require 'todo/cli/cmd'
2
+ require 'todo/data/list'
3
+ require 'todo/src/file'
2
4
 
3
5
  module Todo
4
6
  class Cli
@@ -8,13 +10,13 @@ module Todo
8
10
  end
9
11
 
10
12
  opt '-b', '--before DATE', 'Before date' do |opts, date|
11
- opts[:before] = normalize_date(date)
13
+ opts[:before] = date
12
14
  end
13
15
 
14
16
  def run
15
17
  items = list.select(status: :done, before: before).items
16
- archive.write(render(items))
17
- io.write(render(list.items - items))
18
+ archive.write(format(items))
19
+ io.write(format(list.items - items))
18
20
  end
19
21
 
20
22
  private
@@ -32,7 +34,7 @@ module Todo
32
34
  end
33
35
 
34
36
  def before
35
- opts[:before] || DATES[:two_weeks_ago]
37
+ opts[:before] || Support::Date.new.format(:two_weeks_ago)
36
38
  end
37
39
  end
38
40
  end
data/lib/todo/cli/cmd.rb CHANGED
@@ -2,7 +2,8 @@ require 'todo/helpers/hash/slice'
2
2
  require 'todo/src/file'
3
3
  require 'todo/src/io'
4
4
  require 'todo/support/options_parser'
5
- require 'todo/view'
5
+ require 'todo/support/date'
6
+ require 'todo/format'
6
7
 
7
8
  module Todo
8
9
  class Cli
@@ -10,14 +11,15 @@ module Todo
10
11
  extend Support::OptionsParser
11
12
  include Helpers::Hash::Slice
12
13
 
13
- def self.normalize_date(date)
14
- DATES[date.to_sym] ? DATES[date.to_sym] : date
15
- end
16
-
17
14
  opt '-f', '--file FILENAME', 'Filename' do |opts, file|
18
15
  opts[:file] = file
19
16
  end
20
17
 
18
+ def initialize(args, opts)
19
+ opts = normalize_dates(opts)
20
+ super
21
+ end
22
+
21
23
  def io
22
24
  if opts[:file]
23
25
  Src::File.new(opts[:file])
@@ -26,8 +28,19 @@ module Todo
26
28
  end
27
29
  end
28
30
 
29
- def render(list, opts = {})
30
- View.new(list, opts).render
31
+ def format(list, opts = {})
32
+ Format.new(list, opts).apply
33
+ end
34
+
35
+ def normalize_dates(opts)
36
+ [:before, :after].inject(opts) do |opts, key|
37
+ opts[key] = normalize_date(opts[key]) if opts[key]
38
+ opts
39
+ end
40
+ end
41
+
42
+ def normalize_date(date)
43
+ Support::Date.new.apply(date)
31
44
  end
32
45
 
33
46
  # TODO how to test if stdin is attached?
data/lib/todo/cli/list.rb CHANGED
@@ -9,15 +9,15 @@ module Todo
9
9
  end
10
10
 
11
11
  opt '-a', '--after DATE', 'After date' do |opts, date|
12
- opts[:after] = normalize_date(date)
12
+ opts[:after] = date
13
13
  end
14
14
 
15
15
  opt '-s', '--since DATE', 'Since date' do |opts, date|
16
- opts[:after] = normalize_date(date)
16
+ opts[:after] = date
17
17
  end
18
18
 
19
19
  opt '-b', '--before DATE', 'Before date' do |opts, date|
20
- opts[:before] = normalize_date(date)
20
+ opts[:before] = date
21
21
  end
22
22
 
23
23
  opt '--status STATUS', 'Status' do |opts, status|
@@ -34,7 +34,7 @@ module Todo
34
34
  end
35
35
 
36
36
  def run
37
- out.write(render(list.items, format: opts[:format] || :short))
37
+ out.write(format(list.items, format: opts[:format] || :short))
38
38
  end
39
39
 
40
40
  private
data/lib/todo/cli/push.rb CHANGED
@@ -6,8 +6,12 @@ require 'todo/src/idonethis'
6
6
  module Todo
7
7
  class Cli
8
8
  class Push < Cmd
9
+ opt '-a', '--after DATE', 'After date' do |opts, date|
10
+ opts[:after] = date
11
+ end
12
+
9
13
  opt '-s', '--since DATE', 'Since date' do |opts, date|
10
- opts[:since] = normalize_date(date)
14
+ opts[:after] = date
11
15
  end
12
16
 
13
17
  CONFIG = {
@@ -17,7 +21,7 @@ module Todo
17
21
  }
18
22
 
19
23
  def run
20
- lines = render(list.items, format: [:text, :tags, :id])
24
+ lines = format(list.items, format: [:text, :tags, :id])
21
25
  src.write(lines)
22
26
  io.write(lines)
23
27
  end
@@ -35,7 +39,7 @@ module Todo
35
39
  end
36
40
 
37
41
  def since
38
- opts[:since] || DATES[:yesterday]
42
+ opts[:since] || Support::Dates.new.format(:yesterday)
39
43
  end
40
44
  end
41
45
  end
@@ -16,7 +16,7 @@ module Todo
16
16
  def run
17
17
  list = Data::List.parse(io.read)
18
18
  list.toggle(data)
19
- io.write(render(list.items))
19
+ io.write(format(list.items))
20
20
  end
21
21
 
22
22
  private
@@ -3,7 +3,7 @@ require 'todo/helpers/hash/format'
3
3
  require 'todo/helpers/object/presence'
4
4
 
5
5
  module Todo
6
- class View < Struct.new(:items, :opts)
6
+ class Format < Struct.new(:items, :opts)
7
7
  FORMATS = {
8
8
  full: [:status, :text, :tags, :id],
9
9
  short: [:status, :done_date, :text]
@@ -11,7 +11,7 @@ module Todo
11
11
 
12
12
  include Helpers::Hash::Format, Helpers::Object::Presence
13
13
 
14
- def render
14
+ def apply
15
15
  items.map { |item| format(item) }
16
16
  end
17
17
 
@@ -45,7 +45,7 @@ module Todo
45
45
  end
46
46
 
47
47
  def params
48
- { owner: config[:username], team: config[:team], done_date_after: opts[:since], page_size: 100 }
48
+ { owner: config[:username], team: config[:team], done_date_after: opts[:after], page_size: 100 }
49
49
  end
50
50
  end
51
51
  end
@@ -0,0 +1,93 @@
1
+ require 'date'
2
+
3
+ module Todo
4
+ module Support
5
+ class Date
6
+ NUMS = [:one, :two, :three, :four, :five, :six, :seven, :eight, :nine, :ten]
7
+ MONTHS = [:jan, :feb, :mar, :apr, :may, :jun, :jul, :aug, :sep, :oct, :nov, :dec]
8
+ WDAYS = [:sun, :mon, :tue, :wed, :thu, :fri, :sat]
9
+ FORMAT = '%Y-%m-%d'
10
+ MSGS = { unknown: 'Unrecognized identifier: %s' }
11
+
12
+ SEP = '(?: |_|\-|\.)'
13
+ AGO = /^(\d+|#{NUMS.join('|')})#{SEP}(days?|weeks?|months?|years?)#{SEP}ago$/
14
+ LAST = /^last#{SEP}(#{MONTHS.join('|')}|#{WDAYS.join('|')})/
15
+ DATE = /\d{4}-\d{2}-\d{2}/
16
+
17
+ attr_reader :date
18
+
19
+ def initialize(date = ::Date.today)
20
+ @date = date.to_date
21
+ end
22
+
23
+ def format(str, opts = {})
24
+ apply(str).strftime(opts[:format] || FORMAT)
25
+ end
26
+
27
+ private
28
+
29
+ def apply(str)
30
+ if str =~ DATE
31
+ str
32
+ elsif respond_to?(str, true)
33
+ send(str)
34
+ elsif str =~ AGO
35
+ ago(singularize($2).to_sym, to_number($1))
36
+ elsif str =~ LAST
37
+ last($1)
38
+ else
39
+ fail MSGS[:unknown] % str
40
+ end
41
+ end
42
+
43
+ def today
44
+ date
45
+ end
46
+
47
+ def yesterday
48
+ ago(:day, 1)
49
+ end
50
+
51
+ def ago(type, num)
52
+ type, num = :day, num * 7 if type == :week
53
+ 1.upto(num).inject(date) { |date, _| date.send(:"prev_#{type}") }
54
+ end
55
+
56
+ def last(str)
57
+ key = str.to_s.downcase[0, 3].to_sym
58
+ last_wday(key) || last_month(key) || fail(MSGS[:unknown] % str)
59
+ end
60
+
61
+ def last_month(key)
62
+ return unless num = MONTHS.index(key)
63
+ date = self.date.prev_month
64
+ date = date.prev_month until date.month == num + 1
65
+ date
66
+ end
67
+
68
+ def last_wday(key)
69
+ return unless num = WDAYS.index(key)
70
+ date = self.date.prev_day
71
+ date = date.prev_day until date.wday == num
72
+ date
73
+ end
74
+
75
+ def wday_ix(day)
76
+ WDAYS.index(day.to_s.downcase[0, 3].to_sym)
77
+ end
78
+
79
+ def to_number(str)
80
+ ix = NUMS.index(str.to_sym)
81
+ ix ? ix + 1 : str.to_i
82
+ end
83
+
84
+ def pluralize(str)
85
+ "#{str}s".sub(/ss$/, 's')
86
+ end
87
+
88
+ def singularize(str)
89
+ str.sub(/s$/, '')
90
+ end
91
+ end
92
+ end
93
+ end
data/lib/todo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Todo
2
- VERSION = '0.0.6'
2
+ VERSION = '0.0.7'
3
3
  end
data/lib/todo.rb CHANGED
@@ -5,15 +5,6 @@ module Todo
5
5
  multiple_items: 'Multiple items found for: %s'
6
6
  }
7
7
 
8
- DATES = {
9
- one_month_ago: (Time.now - 60 * 60 * 24 * 31).strftime('%Y-%m-%d'),
10
- two_weeks_ago: (Time.now - 60 * 60 * 24 * 14).strftime('%Y-%m-%d'),
11
- one_week_ago: (Time.now - 60 * 60 * 24 * 7).strftime('%Y-%m-%d'),
12
- two_days_ago: (Time.now - 60 * 60 * 24 * 2).strftime('%Y-%m-%d'),
13
- yesterday: (Time.now - 60 * 60 * 24).strftime('%Y-%m-%d'),
14
- today: Time.now.strftime('%Y-%m-%d')
15
- }
16
-
17
8
  STATUSES = {
18
9
  done: 'x',
19
10
  pend: '-'
data/spec/spec_helper.rb CHANGED
@@ -6,4 +6,5 @@ NOW = Time.parse('2015-12-01 00:02:00 +0200')
6
6
  RSpec.configure do |c|
7
7
  c.mock_with :mocha
8
8
  c.before { Time.stubs(:now).returns(NOW) }
9
+ c.before { Date.stubs(:today).returns(NOW.to_date) }
9
10
  end
@@ -12,42 +12,42 @@ describe Todo::Cli::List do
12
12
 
13
13
  describe 'unfiltered' do
14
14
  let(:opts) { {} }
15
- it { expect(out.readlines).to eq ['foo +abc', '2015-10-01 bar', '2015-11-01 baz', '2015-12-01 baz +abc'] }
15
+ it { expect(out.readlines).to eq ['- foo +abc', 'x 2015-10-01 bar', 'x 2015-11-01 baz', 'x 2015-12-01 baz +abc'] }
16
16
  end
17
17
 
18
18
  describe 'by done date' do
19
19
  let(:opts) { { after: '2015-11-31' } }
20
- it { expect(out.readlines).to eq ['2015-12-01 baz +abc'] }
20
+ it { expect(out.readlines).to eq ['x 2015-12-01 baz +abc'] }
21
21
  end
22
22
 
23
23
  describe 'by status :pending' do
24
24
  let(:opts) { { status: :pending } }
25
- it { expect(out.readlines).to eq ['foo +abc'] }
25
+ it { expect(out.readlines).to eq ['- foo +abc'] }
26
26
  end
27
27
 
28
28
  describe 'by status :done' do
29
29
  let(:opts) { { status: :done } }
30
- it { expect(out.readlines).to eq ['2015-10-01 bar', '2015-11-01 baz', '2015-12-01 baz +abc'] }
30
+ it { expect(out.readlines).to eq ['x 2015-10-01 bar', 'x 2015-11-01 baz', 'x 2015-12-01 baz +abc'] }
31
31
  end
32
32
 
33
33
  describe 'by status :done and :after' do
34
34
  let(:opts) { { status: :done, after: '2015-11-31' } }
35
- it { expect(out.readlines).to eq ['2015-12-01 baz +abc'] }
35
+ it { expect(out.readlines).to eq ['x 2015-12-01 baz +abc'] }
36
36
  end
37
37
 
38
38
  describe 'by status :done and :after' do
39
39
  let(:opts) { { status: :done, after: '2015-11-01', before: '2015-11-07' } }
40
- it { expect(out.readlines).to eq ['2015-11-01 baz'] }
40
+ it { expect(out.readlines).to eq ['x 2015-11-01 baz'] }
41
41
  end
42
42
 
43
43
  describe 'by project' do
44
44
  let(:opts) { { projects: ['abc'] } }
45
- it { expect(out.readlines).to eq ['foo +abc', '2015-12-01 baz +abc'] }
45
+ it { expect(out.readlines).to eq ['- foo +abc', 'x 2015-12-01 baz +abc'] }
46
46
  end
47
47
 
48
48
  describe 'by text' do
49
49
  let(:opts) { { text: 'baz' } }
50
- it { expect(out.readlines).to eq ['2015-11-01 baz', '2015-12-01 baz +abc'] }
50
+ it { expect(out.readlines).to eq ['x 2015-11-01 baz', 'x 2015-12-01 baz +abc'] }
51
51
  end
52
52
  end
53
53
 
@@ -56,12 +56,22 @@ describe Todo::Cli::List do
56
56
 
57
57
  describe 'short' do
58
58
  let(:opts) { { format: 'short' } }
59
- it { expect(out.readlines).to eq ['foo +abc', '2015-12-01 bar'] }
59
+ it { expect(out.readlines).to eq ['- foo +abc', 'x 2015-12-01 bar'] }
60
60
  end
61
61
 
62
62
  describe 'full' do
63
63
  let(:opts) { { format: 'full' } }
64
64
  it { expect(out.readlines).to eq ['- foo +abc [1]', 'x bar done:2015-12-01 [2]'] }
65
65
  end
66
+
67
+ describe 'custom: text,tags' do
68
+ let(:opts) { { format: 'text,tags' } }
69
+ it { expect(out.readlines).to eq ['foo +abc', 'bar done:2015-12-01'] }
70
+ end
71
+
72
+ describe 'custom: id:text' do
73
+ let(:opts) { { format: 'id:text' } }
74
+ it { expect(out.readlines).to eq ['[1] foo +abc', '[2] bar'] }
75
+ end
66
76
  end
67
77
  end
@@ -1,22 +1,22 @@
1
- require 'todo/view'
1
+ require 'todo/format'
2
2
 
3
- describe Todo::View do
3
+ describe Todo::Format do
4
4
  let(:lines) { ['- foo [1]', 'x bar done:2015-12-01 [2]'] }
5
5
  let(:items) { Todo::Data::List.parse(lines).items }
6
6
  subject { described_class.new(items, format: format) }
7
7
 
8
8
  describe 'default columns' do
9
9
  let(:format) {}
10
- it { expect(subject.render).to eq(lines) }
10
+ it { expect(subject.apply).to eq(lines) }
11
11
  end
12
12
 
13
13
  describe 'selected columns' do
14
14
  let(:format) { 'text:tags' }
15
- it { expect(subject.render).to eq(['foo', 'bar done:2015-12-01']) }
15
+ it { expect(subject.apply).to eq(['foo', 'bar done:2015-12-01']) }
16
16
  end
17
17
 
18
18
  describe 'format name' do
19
19
  let(:format) { :short }
20
- it { expect(subject.render).to eq(['foo', '2015-12-01 bar']) }
20
+ it { expect(subject.apply).to eq(['- foo', 'x 2015-12-01 bar']) }
21
21
  end
22
22
  end
@@ -0,0 +1,24 @@
1
+ require 'todo/support/date'
2
+
3
+ describe Todo::Support::Date do
4
+ let(:now) { Time.parse('2015-12-01') }
5
+ let(:date) { described_class.new(now) }
6
+
7
+ it { expect(date.format('yesterday')).to eq '2015-11-30' }
8
+ it { expect(date.format('1_day_ago')).to eq '2015-11-30' }
9
+ it { expect(date.format('3_days_ago')).to eq '2015-11-28' }
10
+
11
+ it { expect(date.format('two weeks ago')).to eq '2015-11-17' }
12
+ it { expect(date.format('3_weeks_ago')).to eq '2015-11-10' }
13
+
14
+ it { expect(date.format('two months ago')).to eq '2015-10-01' }
15
+ it { expect(date.format('3_months_ago')).to eq '2015-09-01' }
16
+
17
+ it { expect(date.format('one year ago')).to eq '2014-12-01' }
18
+ it { expect(date.format('2_years_ago')).to eq '2013-12-01' }
19
+
20
+ it { expect(date.format('last_tue')).to eq '2015-11-24' }
21
+ it { expect(date.format('last_friday')).to eq '2015-11-27' }
22
+ it { expect(date.format('last_sep')).to eq '2015-09-01' }
23
+ it { expect(date.format('last_december')).to eq '2014-12-01' }
24
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: todo.txt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sven Fuchs
@@ -31,6 +31,7 @@ files:
31
31
  - lib/todo/data/list.rb
32
32
  - lib/todo/data/matcher.rb
33
33
  - lib/todo/data/parser.rb
34
+ - lib/todo/format.rb
34
35
  - lib/todo/helpers/hash/compact.rb
35
36
  - lib/todo/helpers/hash/format.rb
36
37
  - lib/todo/helpers/hash/slice.rb
@@ -39,10 +40,10 @@ files:
39
40
  - lib/todo/src/file.rb
40
41
  - lib/todo/src/idonethis.rb
41
42
  - lib/todo/src/io.rb
43
+ - lib/todo/support/date.rb
42
44
  - lib/todo/support/http.rb
43
45
  - lib/todo/support/options_parser.rb
44
46
  - lib/todo/version.rb
45
- - lib/todo/view.rb
46
47
  - spec/spec_helper.rb
47
48
  - spec/support/io.rb
48
49
  - spec/todo/cli/archive_spec.rb
@@ -51,13 +52,14 @@ files:
51
52
  - spec/todo/data/item_spec.rb
52
53
  - spec/todo/data/list_spec.rb
53
54
  - spec/todo/data/parser_spec.rb
55
+ - spec/todo/format_spec.rb
54
56
  - spec/todo/helpers/hash/format_spec.rb
55
57
  - spec/todo/helpers/hash/slice_spec.rb
56
58
  - spec/todo/helpers/object/presence_spec.rb
57
59
  - spec/todo/helpers/string/string_spec.rb
58
60
  - spec/todo/src/file_spec.rb
59
61
  - spec/todo/src/io_spec.rb
60
- - spec/todo/view_spec.rb
62
+ - spec/todo/support/date_spec.rb
61
63
  - todo.gemspec
62
64
  - todo.md
63
65
  homepage: https://github.com/svenfuchs/todo.txt