xtdo 0.2 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +4 -0
- data/README.rdoc +13 -0
- data/TODO +4 -2
- data/bin/xtdo_completion.zsh +0 -0
- data/lib/xtdo.rb +32 -17
- data/spec/add_spec.rb +4 -0
- data/spec/recurring_spec.rb +5 -1
- data/spec/spec_helper.rb +4 -4
- metadata +3 -2
data/HISTORY
CHANGED
data/README.rdoc
CHANGED
@@ -37,6 +37,19 @@ There is a command line completion script for ZSH in bin/xtdo_completion.zsh tha
|
|
37
37
|
|
38
38
|
By default tasks are stored in ~/.xtdo. Customize this by setting the XTDO_PATH environment variable. I store mine in Dropbox.
|
39
39
|
|
40
|
+
== More speed!
|
41
|
+
|
42
|
+
Load paths and gems and friends can add about 300ms to the load time of xtdo. Unacceptable! I haven't worked out a neat way around this yet, but for now my hack is to create my own script to run xtdo which I place in my path, with load paths hard coded in to it. This has the benefit of also working if you switch gemsets. Here is mine, yours will look slightly different:
|
43
|
+
|
44
|
+
#!/Users/xavier/.rvm/rubies/ruby-1.9.2-p0/bin/ruby
|
45
|
+
|
46
|
+
$LOAD_PATH.unshift "/Users/xavier/.rvm/gems/ruby-1.9.2-p0/gems/xtdo-0.2/lib"
|
47
|
+
|
48
|
+
require 'xtdo'
|
49
|
+
file = ENV['XTDO_PATH'] || "~/.xtdo"
|
50
|
+
result = Xtdo.run file, ARGV.join(" ")
|
51
|
+
puts result if result && result.is_a?(String) && result.length > 0
|
52
|
+
|
40
53
|
== Why?
|
41
54
|
|
42
55
|
I used to use a small subset of Things.app, and I really dug the workflow. I spend my life on the command-line though. Existing command line apps didn't quite match my workflow, or were too slow.
|
data/TODO
CHANGED
data/bin/xtdo_completion.zsh
CHANGED
File without changes
|
data/lib/xtdo.rb
CHANGED
@@ -62,9 +62,14 @@ class Xtdo
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def add(task)
|
65
|
-
|
66
|
-
|
67
|
-
|
65
|
+
match = /^(?:(\d+)([dwmy])? )?(.+)/.match(task)
|
66
|
+
if match
|
67
|
+
number, period, task = match.captures
|
68
|
+
@tasks[make_key task] = {:name => task}
|
69
|
+
@tasks[make_key task][:scheduled] = parse_relative_time(number.to_i, period) if number
|
70
|
+
else
|
71
|
+
"Invalid command"
|
72
|
+
end
|
68
73
|
end
|
69
74
|
|
70
75
|
def bump(task)
|
@@ -110,10 +115,10 @@ class Xtdo
|
|
110
115
|
groups.map do |group|
|
111
116
|
t = tasks.select {|name, opts| task_selector[group][opts[:scheduled]] }
|
112
117
|
next if t.empty?
|
113
|
-
"===== #{group.to_s.upcase}\n" + t.map { |name, attrs|
|
114
|
-
attrs[:name]
|
118
|
+
"\n\e[43;30m===== #{group.to_s.upcase} =====\e[0m\n\n" + t.map { |name, attrs|
|
119
|
+
" #{attrs[:name]}"
|
115
120
|
}.join("\n")
|
116
|
-
end.join("\n")
|
121
|
+
end.join("\n") + "\n\n"
|
117
122
|
end
|
118
123
|
end
|
119
124
|
|
@@ -125,13 +130,17 @@ class Xtdo
|
|
125
130
|
when 'a' then
|
126
131
|
number, period, start, name = self.class.extract_recur_tokens(task)
|
127
132
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
133
|
+
if name
|
134
|
+
period_string = "#{number}#{period}"
|
135
|
+
period_string += ",#{start}" if start
|
136
|
+
recurring[make_key name] = {
|
137
|
+
:name => name,
|
138
|
+
:next => Date.today + 1,
|
139
|
+
:period => period_string
|
140
|
+
}
|
141
|
+
else
|
142
|
+
"Invalid command"
|
143
|
+
end
|
135
144
|
when 'd' then
|
136
145
|
if recurring.delete make_key(task)
|
137
146
|
"Recurring task removed"
|
@@ -139,9 +148,9 @@ class Xtdo
|
|
139
148
|
"No such recurring task"
|
140
149
|
end
|
141
150
|
when 'l' then
|
142
|
-
"===== RECURRING\n" + recurring.map do |name, task|
|
143
|
-
"%-
|
144
|
-
end.join("\n")
|
151
|
+
"\n\e[43;30m===== RECURRING =====\e[0m\n\n" + recurring.map do |name, task|
|
152
|
+
" %-8s%s" % [task[:period], task[:name]]
|
153
|
+
end.join("\n") + "\n\n"
|
145
154
|
when 'c' then
|
146
155
|
recurring.keys.join "\n"
|
147
156
|
end
|
@@ -187,8 +196,14 @@ class Xtdo
|
|
187
196
|
end
|
188
197
|
|
189
198
|
def self.extract_recur_tokens(task)
|
190
|
-
/^(\d+)([dwmy])?(?:,(#{days.join('|')}|\d{1,2}))? (.*)/.match(task)
|
199
|
+
match = /^(\d+)([dwmy])?(?:,(#{days.join('|')}|\d{1,2}))? (.*)/.match(task)
|
200
|
+
if match
|
201
|
+
match.captures
|
202
|
+
else
|
203
|
+
[]
|
204
|
+
end
|
191
205
|
end
|
206
|
+
|
192
207
|
def self.days
|
193
208
|
days = %w(sun mon tue wed thu fri sat)
|
194
209
|
end
|
data/spec/add_spec.rb
CHANGED
data/spec/recurring_spec.rb
CHANGED
@@ -26,7 +26,7 @@ feature 'recurring' do
|
|
26
26
|
|
27
27
|
scenario 'list' do
|
28
28
|
t('r a 1d T1')
|
29
|
-
t('r l').should have_task('1d
|
29
|
+
t('r l').should have_task('1d T1')
|
30
30
|
end
|
31
31
|
|
32
32
|
scenario 'daily' do
|
@@ -90,4 +90,8 @@ feature 'recurring' do
|
|
90
90
|
time_travel Date.new(2011,1,4)
|
91
91
|
t('l').should have_task('T1')
|
92
92
|
end
|
93
|
+
|
94
|
+
scenario 'error' do
|
95
|
+
t('r a').should == "Invalid command"
|
96
|
+
end
|
93
97
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -75,12 +75,12 @@ RSpec::Matchers.define(:have_task) do |task, opts = {}|
|
|
75
75
|
def parse(data)
|
76
76
|
@parsed ||= begin
|
77
77
|
group = nil
|
78
|
-
data.to_s.lines.inject({}) do |a, line|
|
79
|
-
if line =~
|
80
|
-
group = line[
|
78
|
+
data.to_s.lines.reject {|x| x.chomp.length == 0 }.inject({}) do |a, line|
|
79
|
+
if line =~ /^[^\s]+=+ (.+) =/
|
80
|
+
group = line[/^[^\s]+=+ (.+) =/, 1].downcase.to_sym
|
81
81
|
else
|
82
82
|
a[group] ||= []
|
83
|
-
a[group] << line.
|
83
|
+
a[group] << line.strip
|
84
84
|
end
|
85
85
|
a
|
86
86
|
end
|
metadata
CHANGED
@@ -5,7 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 2
|
8
|
-
|
8
|
+
- 1
|
9
|
+
version: 0.2.1
|
9
10
|
platform: ruby
|
10
11
|
authors:
|
11
12
|
- Xavier Shay
|
@@ -13,7 +14,7 @@ autorequire:
|
|
13
14
|
bindir: bin
|
14
15
|
cert_chain: []
|
15
16
|
|
16
|
-
date: 2010-
|
17
|
+
date: 2010-12-03 00:00:00 +11:00
|
17
18
|
default_executable:
|
18
19
|
dependencies:
|
19
20
|
- !ruby/object:Gem::Dependency
|