todo.txt 0.0.6 → 0.0.7

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