Class: Xtdo

Inherits:
Object
  • Object
show all
Defined in:
lib/xtdo.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tasks) ⇒ Xtdo

Returns a new instance of Xtdo.



49
50
51
52
# File 'lib/xtdo.rb', line 49

def initialize(tasks)
  @tasks = tasks[:tasks]
  @recurring = tasks[:recurring]
end

Instance Attribute Details

#recurringObject (readonly)

Returns the value of attribute recurring.



47
48
49
# File 'lib/xtdo.rb', line 47

def recurring
  @recurring
end

#tasksObject (readonly)

Returns the value of attribute tasks.



47
48
49
# File 'lib/xtdo.rb', line 47

def tasks
  @tasks
end

Class Method Details

.calculate_starting_day(date, number, period, start = nil) ⇒ Object



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/xtdo.rb', line 163

def self.calculate_starting_day(date, number, period, start = nil)
  days = %w(sun mon tue wed thu fri sat)
  number = (number || 1).to_i

  adjust = case period
  when 'd' then 1
  when 'w' then
    if days.index(start)
      (days.index(start) - date.wday) % 7 + (number - 1) * 7
    end
  when 'm' then
    start = start.to_i
    if start > 0
      year = date.year
      if start.to_i <= date.day
        month = date.month + 1
        if month > 12
          month = 1
          year += 1
        end
      else
        month = date.month
      end
      Date.new(year, month, start.to_i) - date
    end
  end
  if adjust
    if adjust == 0
      date + 7
    else
      date + adjust
    end
  end
end

.daysObject



207
208
209
# File 'lib/xtdo.rb', line 207

def self.days
  days = %w(sun mon tue wed thu fri sat)
end

.extract_recur_tokens(task) ⇒ Object



198
199
200
201
202
203
204
205
# File 'lib/xtdo.rb', line 198

def self.extract_recur_tokens(task)
  match = /^(\d+)([dwmy])?(?:,(#{days.join('|')}|\d{1,2}))? (.*)/.match(task)
  if match
    match.captures
  else
    []
  end
end

.run(store, operation) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/xtdo.rb', line 5

def self.run(store, operation)
  operation = operation.split(/\s+/)
  verb = operation.shift

#     tasks = {
#       'T1' => {:scheduled => Date.today}
#     }
  store = File.expand_path(store)
  tasks = if File.exists?(store)
    YAML.load(File.open(store))
  else
    {}
  end
  tasks[:tasks] ||= {}
  tasks[:recurring] ||= {}

  manager = Xtdo.new(tasks)

  ret = case verb
  when 'a' then # A is for add!
    manager.add operation.join(' ')
  when 'b' then # B is for bump!
    manager.bump operation.join(' ')
  when 'd' then # D is for done!
    manager.done operation.join(' ')
  when 'l' then # L is for list!
    case operation[0]
    when 'a' then
      manager.list [:today, :next, :scheduled]
    when 'c' then
      manager.list [:today, :next, :scheduled], :format => :completion
    else
      manager.list [:today]
    end
  when 'r' then # R is for recur!
    manager.recur operation.join(' ')
  end

  manager.save(store)
  ret
end

Instance Method Details

#add(task) ⇒ Object



64
65
66
67
68
69
70
71
72
73
# File 'lib/xtdo.rb', line 64

def add(task)
  match = /^(?:(\d+)([dwmy])? )?(.+)/.match(task)
  if match
    number, period, task = match.captures
    @tasks[make_key task] = {:name => task}
    @tasks[make_key task][:scheduled] = parse_relative_time(number.to_i, period) if number
  else
    "Invalid command"
  end
end

#bump(task) ⇒ Object



75
76
77
78
79
80
81
82
83
84
# File 'lib/xtdo.rb', line 75

def bump(task)
  number, period, task = /^(?:(\d+)([dwmy])? )?(.*)/.match(task).captures
  if !number
    "Invalid time"
  elsif tasks[make_key task]
    tasks[make_key task][:scheduled] = parse_relative_time(number.to_i, period)
  else
    "No such task"
  end
end

#done(task) ⇒ Object



86
87
88
89
90
91
92
# File 'lib/xtdo.rb', line 86

def done(task)
  if tasks.delete(make_key task)
    "Task done"
  else
    "No such task"
  end
end

#list(groups, opts = {}) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/xtdo.rb', line 94

def list(groups, opts = {})
  # Check for recurring
  recurring.each do |name, task|
    if task[:next] <= Date.today
      tasks[make_key name] = {:scheduled => Date.today, :name => task[:name] }
      number, period, start, _ = self.class.extract_recur_tokens(task[:period] + ' ' + name)

      task[:next] = self.class.calculate_starting_day(Date.today, number, period, start)
    end
  end

  if opts[:format] == :completion
    tasks.keys.join "\n"
  else
    # Print groups
    task_selector = {
      :today     => lambda {|x| x && x <= Date.today },
      :next      => lambda {|x| !x },
      :scheduled => lambda {|x| x && x > Date.today }
    }

    groups.map do |group|
      t = tasks.select {|name, opts| task_selector[group][opts[:scheduled]] }
      next if t.empty?
      "\n\e[43;30m===== #{group.to_s.upcase} =====\e[0m\n\n" + t.map { |name, attrs|
        "  #{attrs[:name]}"
      }.join("\n")
    end.join("\n") + "\n\n"
  end
end

#make_key(name) ⇒ Object



159
160
161
# File 'lib/xtdo.rb', line 159

def make_key(name)
  name.gsub /[^a-zA-Z0-9,.]/, '-' 
end

#parse_relative_time(number, period) ⇒ Object



54
55
56
57
58
59
60
61
62
# File 'lib/xtdo.rb', line 54

def parse_relative_time(number, period)
  adj = {
    'd' => 1,
    'w' => 7,
    'm' => 30,
    'y' => 365
  }[period] || 1
  Date.today + adj * number
end

#recur(task) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/xtdo.rb', line 125

def recur(task)
  tokens = task.split(/\s+/)
  verb = tokens.shift
  task = tokens.join(' ')
  case verb
  when 'a' then
    number, period, start, name = self.class.extract_recur_tokens(task)

    if name
      period_string = "#{number}#{period}"
      period_string += ",#{start}" if start
      recurring[make_key name] = {
        :name   => name,
        :next   => Date.today + 1,
        :period => period_string
      }
    else
      "Invalid command"
    end
  when 'd' then
    if recurring.delete make_key(task)
      "Recurring task removed"
    else
      "No such recurring task"
    end
  when 'l' then
    "\n\e[43;30m===== RECURRING =====\e[0m\n\n" + recurring.map do |name, task|
      "  %-8s%s" % [task[:period], task[:name]]
    end.join("\n") + "\n\n"
  when 'c' then
    recurring.keys.join "\n"
  end
end

#save(file) ⇒ Object



211
212
213
214
215
216
217
# File 'lib/xtdo.rb', line 211

def save(file)
  File.open(file, 'w') {|f| f.write({
    :tasks     => tasks,
    :recurring => recurring,
    :version   => 1
  }.to_yaml) } 
end