gtdfiles 0.8.0 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +5 -4
- data/Manifest.txt +28 -7
- data/README.txt +3 -3
- data/bin/gtdfiles +1 -0
- data/lib/abbreviations.rb +11 -0
- data/lib/action_persistence.rb +48 -0
- data/lib/context_persistence.rb +50 -0
- data/lib/encoding_handler.rb +48 -0
- data/lib/gtd.rb +215 -0
- data/lib/gtd_config.rb +53 -0
- data/lib/gtd_files.rb +1 -1
- data/lib/inbox_persistence.rb +21 -0
- data/lib/logging.rb +7 -0
- data/lib/persistence.rb +116 -0
- data/lib/project_persistence.rb +66 -0
- data/lib/status.rb +59 -0
- data/lib/utf8encoder.rb +9 -0
- data/spec/abbreviations_specs.rb +46 -0
- data/spec/action_file_persister_specs.rb +53 -0
- data/spec/action_specs.rb +49 -0
- data/spec/context_specs.rb +16 -0
- data/spec/persistence_specs.rb +13 -0
- data/spec/project_file_persister_specs.rb +7 -0
- data/spec/project_specs.rb +27 -0
- data/spec/status_specs.rb +32 -0
- data/spec/utf8encoder_specs.rb +24 -0
- metadata +23 -2
data/History.txt
CHANGED
data/Manifest.txt
CHANGED
@@ -1,7 +1,28 @@
|
|
1
|
-
History.txt
|
2
|
-
Manifest.txt
|
3
|
-
README.txt
|
4
|
-
Rakefile
|
5
|
-
bin/gtdfiles
|
6
|
-
lib/
|
7
|
-
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
bin/gtdfiles
|
6
|
+
lib/abbreviations.rb
|
7
|
+
lib/action_persistence.rb
|
8
|
+
lib/context_persistence.rb
|
9
|
+
lib/encoding_handler.rb
|
10
|
+
lib/gtd.rb
|
11
|
+
lib/gtd_config.rb
|
12
|
+
lib/gtd_files.rb
|
13
|
+
lib/inbox_persistence.rb
|
14
|
+
lib/logging.rb
|
15
|
+
lib/persistence.rb
|
16
|
+
lib/project_persistence.rb
|
17
|
+
lib/status.rb
|
18
|
+
lib/utf8encoder.rb
|
19
|
+
spec/abbreviations_specs.rb
|
20
|
+
spec/action_file_persister_specs.rb
|
21
|
+
spec/action_specs.rb
|
22
|
+
spec/context_specs.rb
|
23
|
+
spec/persistence_specs.rb
|
24
|
+
spec/project_file_persister_specs.rb
|
25
|
+
spec/project_specs.rb
|
26
|
+
spec/status_specs.rb
|
27
|
+
spec/utf8encoder_specs.rb
|
28
|
+
test/test_gtd_files.rb
|
data/README.txt
CHANGED
@@ -40,9 +40,9 @@ where Status can be one of the following:
|
|
40
40
|
|
41
41
|
== SYNOPSIS:
|
42
42
|
|
43
|
-
*
|
44
|
-
*
|
45
|
-
*
|
43
|
+
* gtdfiles --process C:\GTD
|
44
|
+
* gtdfiles --simulate --process C:\GTD
|
45
|
+
* gtdfiles --reset C:\GTD
|
46
46
|
|
47
47
|
== REQUIREMENTS:
|
48
48
|
|
data/bin/gtdfiles
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
ruby ../lib/gtd_files.rb %1 %2 %3 %4 %5 %6 %7
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/gtd_config'
|
2
|
+
module Abbreviations
|
3
|
+
def expand_text(text)
|
4
|
+
text =~ /\A(\d*)(\S*)/
|
5
|
+
numbers = $1
|
6
|
+
word = $2.downcase
|
7
|
+
expanded_text = (numbers == nil or numbers.empty?) ? ABBREVIATIONS[word] : (ABBREVIATIONS[numbers]+word.capitalize)
|
8
|
+
expanded_text == nil ? text : expanded_text.chomp("/")
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module ActionFileUtil
|
2
|
+
def file_name(folder)
|
3
|
+
"#{folder}/#{@description}.act"
|
4
|
+
end
|
5
|
+
end
|
6
|
+
module ActionInFile
|
7
|
+
include ActionFileUtil, Status
|
8
|
+
|
9
|
+
|
10
|
+
LINE_EXP = /(\+|-|\/|\!)?\s*(\S*)([^\(]*)(\(([^\)]*))?/u
|
11
|
+
def Action::parse(line)
|
12
|
+
begin
|
13
|
+
action_details = {}
|
14
|
+
line =~ LINE_EXP
|
15
|
+
action_details[:description] = $3.strip
|
16
|
+
action_details[:info] = $5 || ''
|
17
|
+
action_details[:status] = SYMBOLS_STATUS[$1] || :unprocessed
|
18
|
+
action_details[:context] = expand_text($2.strip)
|
19
|
+
rescue
|
20
|
+
log "Problem parsing #{line}"
|
21
|
+
throw
|
22
|
+
end
|
23
|
+
#action = Action.new(description,context,nil,status,info)
|
24
|
+
return action_details
|
25
|
+
end
|
26
|
+
|
27
|
+
def write(folder)
|
28
|
+
file_name=file_name(folder)
|
29
|
+
#log " Creating file #{file_name}"
|
30
|
+
project_substring = @project == nil ? "" : " (#{@project})"
|
31
|
+
|
32
|
+
write_to_encoded_file(file_name, project_file_string + "\nProject: #{@project.name}")
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
module ActionTextInProjectFile
|
37
|
+
def project_file_string
|
38
|
+
info_string = @info.size > 1 ? " (#{@info})" : ''
|
39
|
+
"#{symbol(@status)} #{@context.name} #{@description}#{info_string}".strip
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
class Action
|
45
|
+
include ActionInFile, ActionTextInProjectFile
|
46
|
+
extend Abbreviations
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/encoding_handler'
|
2
|
+
class ContextFromDirectory < Context
|
3
|
+
def initialize(name,base_folder)
|
4
|
+
super(name)
|
5
|
+
@folder = "#{base_folder}/#{name}"
|
6
|
+
@added_actions = []
|
7
|
+
@removed_actions = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def read_actions
|
11
|
+
action_files=Dir["#{@folder}/*.act"].collect{|action_file| utf_file_name action_file}
|
12
|
+
actions = []
|
13
|
+
action_files.each do |action_file|
|
14
|
+
text = read_and_decode(action_file)
|
15
|
+
if text == nil then return [] end
|
16
|
+
lines = text.split("\n")
|
17
|
+
log "Nil line in #{action_file}" if lines[0] == nil
|
18
|
+
begin
|
19
|
+
action_details = Action.parse(lines[0])
|
20
|
+
action_details[:project] = lines[1][9,-1]
|
21
|
+
rescue
|
22
|
+
log "Error trying to read #{action_file}"
|
23
|
+
raise
|
24
|
+
end
|
25
|
+
#log action_details
|
26
|
+
#action_details[:context] = self
|
27
|
+
actions << action_details
|
28
|
+
end
|
29
|
+
actions
|
30
|
+
end
|
31
|
+
def action_file_info
|
32
|
+
"Info:\n#{@info}" unless @info == nil || @info.empty?
|
33
|
+
end
|
34
|
+
def <<(action)
|
35
|
+
@added_actions << action unless @actions.include? action
|
36
|
+
super action
|
37
|
+
end
|
38
|
+
def write
|
39
|
+
File.makedirs(@folder) unless @added_actions.empty?
|
40
|
+
#log "Writing #{@folder}/#{action}.act:\n#{action_file_info}"
|
41
|
+
@added_actions.each {|action| action.write @folder}
|
42
|
+
@removed_actions.each {|action| File.delete("#{action.file_name(@folder)}")}
|
43
|
+
end
|
44
|
+
def delete(action)
|
45
|
+
super action
|
46
|
+
@removed_actions << action #if @actions.include? action
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/gtd_config'
|
2
|
+
require File.dirname(__FILE__)+'/utf8encoder'
|
3
|
+
require 'rchardet'
|
4
|
+
require 'iconv'
|
5
|
+
|
6
|
+
ENCODING_HANDLER = UTF8Encoder.new true
|
7
|
+
def os_file_name(file_name)
|
8
|
+
begin
|
9
|
+
Iconv.conv(GTDConfig[:file_name_encoding],'utf-8',file_name)
|
10
|
+
rescue
|
11
|
+
puts "Shit"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def utf_file_name(file_name)
|
16
|
+
begin
|
17
|
+
Iconv.conv('utf-8',GTDConfig[:file_name_encoding],file_name)
|
18
|
+
rescue
|
19
|
+
puts "Shit"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def read_and_decode(file_name)
|
24
|
+
fn = file_name
|
25
|
+
os_encoded_file_name = os_file_name(file_name)
|
26
|
+
file = File.new(os_encoded_file_name,'r')
|
27
|
+
cd = CharDet.detect(file.read)
|
28
|
+
#log "File #{file_name}: Encoding #{cd['encoding']} with probability #{cd['confidence']}"
|
29
|
+
iso = Iconv.new('utf-8',cd['encoding'] )
|
30
|
+
file.rewind
|
31
|
+
#log "File #{file_name} encoded in #{"
|
32
|
+
if cd['encoding'] == "UTF-8" then file.seek(3) end
|
33
|
+
begin
|
34
|
+
return iso.iconv(file.read)
|
35
|
+
rescue Iconv::IllegalSequence
|
36
|
+
log "Bad encoding in #{file_name}"
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
def write_to_encoded_file(file_name,text)
|
42
|
+
File.open(os_file_name(file_name),"w") do |file|
|
43
|
+
file.puts ENCODING_HANDLER.encode(text)
|
44
|
+
end
|
45
|
+
|
46
|
+
#log "To #{file_name} writing #{ENCODING_HANDLER.encode(text)}"
|
47
|
+
end
|
48
|
+
|
data/lib/gtd.rb
ADDED
@@ -0,0 +1,215 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/status'
|
2
|
+
require 'dependency'
|
3
|
+
|
4
|
+
module ObservingContainer
|
5
|
+
@dirty = false
|
6
|
+
def <<(item)
|
7
|
+
super item
|
8
|
+
log "Adding observer #{self} to #{item}"
|
9
|
+
item.dependents.add self
|
10
|
+
end
|
11
|
+
|
12
|
+
def update(attribute, value)
|
13
|
+
@dirty = true
|
14
|
+
#log "Updating #{self}"
|
15
|
+
end
|
16
|
+
def set_dirty
|
17
|
+
@dirty = true
|
18
|
+
end
|
19
|
+
def dirty?
|
20
|
+
@dirty
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Context
|
25
|
+
include Status
|
26
|
+
attr_reader :name, :actions
|
27
|
+
|
28
|
+
def initialize(name='None')
|
29
|
+
@name = name
|
30
|
+
@actions = []
|
31
|
+
end
|
32
|
+
|
33
|
+
def <<(action)
|
34
|
+
@actions << action
|
35
|
+
action.context = self
|
36
|
+
end
|
37
|
+
def include?(action)
|
38
|
+
@actions.include? action
|
39
|
+
end
|
40
|
+
def delete(action)
|
41
|
+
@actions.delete action
|
42
|
+
end
|
43
|
+
def to_s
|
44
|
+
"Context #{@name}:\n " + @actions.join("\n ")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Contexts
|
49
|
+
def initialize()
|
50
|
+
@name_map = {}
|
51
|
+
end
|
52
|
+
|
53
|
+
def with_name(name)
|
54
|
+
unless @name_map.include? name then
|
55
|
+
@name_map[name] = Context.new name
|
56
|
+
end
|
57
|
+
@name_map[name]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class Project < Context
|
62
|
+
include Status, Dependency::Methods, ObservingContainer
|
63
|
+
extend Dependency::ClassMethod
|
64
|
+
dependency_attr :status, :infos
|
65
|
+
def initialize(name,status=:processed)
|
66
|
+
super(name)
|
67
|
+
@status = status
|
68
|
+
@infos = []
|
69
|
+
end
|
70
|
+
def <<(action)
|
71
|
+
log "Setting project #{self} for action #{action}"
|
72
|
+
@actions << action
|
73
|
+
action.project = self
|
74
|
+
end
|
75
|
+
def to_s
|
76
|
+
"#{@status.id2name.capitalize} project #{@name}:\n " + @actions.join("\n ")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class GTDSystem
|
81
|
+
include Status
|
82
|
+
attr_reader :contexts, :projects, :actions
|
83
|
+
def initialize contexts, projects
|
84
|
+
@contexts = contexts
|
85
|
+
@projects = projects
|
86
|
+
end
|
87
|
+
def projects_to_review
|
88
|
+
projects_to_review = @projects.active.find_all {|project| project.actions.active.empty?}
|
89
|
+
#log "Projects to review: " << projects_to_review.join("\n")
|
90
|
+
projects_to_review
|
91
|
+
end
|
92
|
+
def inactive_actions
|
93
|
+
inactive_actions = @actions.inactive
|
94
|
+
inactive_projects = @projects.inactive
|
95
|
+
inactive_projects.each do |project|
|
96
|
+
#log "Inactive: #{project}"
|
97
|
+
inactive_actions += project.actions
|
98
|
+
end
|
99
|
+
#log inactive_actions
|
100
|
+
inactive_actions
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
def active_actions
|
105
|
+
active_actions = []
|
106
|
+
active_projects = @projects.active
|
107
|
+
#log "Active projects #{active_projects}"
|
108
|
+
active_projects.each do |project|
|
109
|
+
active_actions += project.actions.active
|
110
|
+
end
|
111
|
+
active_actions
|
112
|
+
end
|
113
|
+
|
114
|
+
def unprocess_active_actions
|
115
|
+
unprocess(active_actions)
|
116
|
+
end
|
117
|
+
|
118
|
+
def unprocess_all_actions
|
119
|
+
unprocess(@actions)
|
120
|
+
end
|
121
|
+
|
122
|
+
def unprocess(actions)
|
123
|
+
actions.each do |action|
|
124
|
+
action.status = :unprocessed
|
125
|
+
log "[Unprocessed]\t#{action}"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def do_process
|
130
|
+
process active_actions.unprocessed
|
131
|
+
unprocess inactive_actions
|
132
|
+
set_to_done done_actions
|
133
|
+
projects_to_review
|
134
|
+
empty_inbox
|
135
|
+
end
|
136
|
+
def unprocess(actions)
|
137
|
+
actions.each do |action|
|
138
|
+
if action.context.include? action then
|
139
|
+
log "[Unprocessed]\t#{action}"
|
140
|
+
if action.status == :processed then action.status = :unprocessed end
|
141
|
+
action.context.delete action
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
def process(actions)
|
148
|
+
actions.each do |action|
|
149
|
+
log "[Processed]\t#{action}"
|
150
|
+
action.status = :processed
|
151
|
+
action.context<<action
|
152
|
+
end
|
153
|
+
end
|
154
|
+
def done_actions
|
155
|
+
actions = active_actions.processed.find_all do |action|
|
156
|
+
#log "#{action} not in #{action.context}" if not action.context.include? action
|
157
|
+
not action.context.include? action
|
158
|
+
end
|
159
|
+
#log "Done actions #{actions}"
|
160
|
+
actions
|
161
|
+
end
|
162
|
+
|
163
|
+
def set_to_done(actions)
|
164
|
+
#log "Setting to done #{actions}"
|
165
|
+
actions.each do |action|
|
166
|
+
action.status = :done
|
167
|
+
log "[Done]\t\t#{action}"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def empty_inbox
|
172
|
+
@inbox.entries.each do |entry|
|
173
|
+
log "[Inbox]\t\t#{entry}"
|
174
|
+
project = Project.new(entry,:review)
|
175
|
+
project << Action.new(entry,@inbox)
|
176
|
+
project.set_dirty
|
177
|
+
@projects << project
|
178
|
+
end
|
179
|
+
@inbox.empty!
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
class Action
|
185
|
+
include Status, Dependency::Methods
|
186
|
+
extend Dependency::ClassMethod
|
187
|
+
dependency_attr :description, :info, :status
|
188
|
+
attr_accessor :context, :project
|
189
|
+
|
190
|
+
def initialize (description,context,project='',status=:unprocessed,info='')
|
191
|
+
@description = description.strip
|
192
|
+
@context=context
|
193
|
+
#@proposed_context
|
194
|
+
@project=project
|
195
|
+
@info=info
|
196
|
+
@status = status
|
197
|
+
end
|
198
|
+
|
199
|
+
def to_s
|
200
|
+
|
201
|
+
"#{@description} (#{@context.name}, #{@project.name}, #{status})"
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
class Inbox < Context
|
207
|
+
def initialize
|
208
|
+
super "Inbox"
|
209
|
+
end
|
210
|
+
def include?(entry)
|
211
|
+
true
|
212
|
+
end
|
213
|
+
def empty!
|
214
|
+
end
|
215
|
+
end
|
data/lib/gtd_config.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
GTDConfig = {
|
2
|
+
:encoding=>"utf-8",
|
3
|
+
:console_encoding=>"cp1252",
|
4
|
+
:file_name_encoding=>"cp1252"
|
5
|
+
}
|
6
|
+
ABBREVIATIONS = {
|
7
|
+
"�berall"=>"Irgendwo/",
|
8
|
+
"�berall"=>"Irgendwo/",
|
9
|
+
|
10
|
+
"online"=>"Computer/Online/",
|
11
|
+
"windows"=>"Computer/Windows/",
|
12
|
+
"linux"=>"Computer/Linux/",
|
13
|
+
"p2p"=>"Computer/P2P/",
|
14
|
+
"chandy"=>"Computer/Handy/",
|
15
|
+
"plus"=>"Einkaufen/Plus/",
|
16
|
+
"drogerie"=>"Einkaufen/Drogerie/",
|
17
|
+
"ikea"=>"Einkaufen/Ikea/",
|
18
|
+
|
19
|
+
|
20
|
+
"1"=>"Agenda/",
|
21
|
+
"2"=>"Computer/",
|
22
|
+
"22"=>"Computer/CD/",
|
23
|
+
"23"=>"Computer/Eclipse/",
|
24
|
+
"24"=>"Computer/Handy/",
|
25
|
+
"25"=>"Computer/Linux/",
|
26
|
+
"26"=>"Computer/Online/",
|
27
|
+
"264"=>"Computer/Online/GCal/",
|
28
|
+
"266"=>"Computer/Online/Mail/",
|
29
|
+
"27"=>"Computer/Online/P2P/",
|
30
|
+
"267"=>"Computer/Online/P2P/",
|
31
|
+
"29"=>"Computer/Windows/",
|
32
|
+
"294"=>"Computer/Windows/Handy/",
|
33
|
+
"296"=>"Computer/Windows/MindManager/",
|
34
|
+
|
35
|
+
"3"=>"Einkaufen/",
|
36
|
+
"37"=>"Einkaufen/Plus/",
|
37
|
+
"33"=>"Einkaufen/Drogerie/",
|
38
|
+
"34"=>"Einkaufen/Ikea/",
|
39
|
+
"4"=>"Irgendwo/",
|
40
|
+
"48"=>"Irgendwo/Telefon/",
|
41
|
+
"44"=>"Irgendwo/Handy/",
|
42
|
+
"42"=>"Irgendwo/Brainstorm/",
|
43
|
+
"6"=>"Mintraching/",
|
44
|
+
"625"=>"Mintraching/BlackLight/",
|
45
|
+
"627"=>"Mintraching/BeSharps/",
|
46
|
+
"67"=>"Mintraching/Praxis/",
|
47
|
+
"7"=>"Passau/",
|
48
|
+
"75"=>"Passau/Lesen/",
|
49
|
+
"76"=>"Passau/Mintraching/",
|
50
|
+
|
51
|
+
"8"=>"�berall/",
|
52
|
+
}
|
53
|
+
|
data/lib/gtd_files.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
class InboxFromFile < Inbox
|
2
|
+
attr_reader :entries
|
3
|
+
def initialize(file_name)
|
4
|
+
@file_name = file_name
|
5
|
+
end
|
6
|
+
def empty!
|
7
|
+
@entries = []
|
8
|
+
end
|
9
|
+
def read
|
10
|
+
content = File.exists?(@file_name) ? read_and_decode(@file_name):''
|
11
|
+
@entries = []
|
12
|
+
content.each_line do |line|
|
13
|
+
line.strip!
|
14
|
+
@entries << line unless line.empty?
|
15
|
+
end
|
16
|
+
#log @entries
|
17
|
+
end
|
18
|
+
def write
|
19
|
+
File.open(@file_name,"w") {} if File.exists?(@file_name) if @entries.empty?
|
20
|
+
end
|
21
|
+
end
|
data/lib/logging.rb
ADDED
data/lib/persistence.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/gtd'
|
2
|
+
require File.dirname(__FILE__)+'/status.rb'
|
3
|
+
require File.dirname(__FILE__)+'/abbreviations'
|
4
|
+
require File.dirname(__FILE__)+'/gtd_config'
|
5
|
+
require File.dirname(__FILE__)+'/encoding_handler'
|
6
|
+
require File.dirname(__FILE__)+'/project_persistence'
|
7
|
+
require File.dirname(__FILE__)+'/context_persistence'
|
8
|
+
require File.dirname(__FILE__)+'/inbox_persistence'
|
9
|
+
require File.dirname(__FILE__)+'/action_persistence'
|
10
|
+
require 'rubygems'
|
11
|
+
require 'ftools'
|
12
|
+
#require 'unroller'
|
13
|
+
#Unroller::trace
|
14
|
+
#
|
15
|
+
|
16
|
+
$KCODE = 'u'
|
17
|
+
|
18
|
+
class GTDFileSystem < GTDSystem
|
19
|
+
|
20
|
+
def initialize(base_dir)
|
21
|
+
@base_dir = base_dir
|
22
|
+
@inbox = InboxFromFile.new "#{@base_dir}/@Inbox/Inbox.txt"
|
23
|
+
end
|
24
|
+
|
25
|
+
def read
|
26
|
+
@inbox.read
|
27
|
+
@projects = read_projects_with_statuses(project_dir,
|
28
|
+
[ :unprocessed, :done, :someday, :tickled, :delegated])
|
29
|
+
#log @projects.active.join("\n")
|
30
|
+
action_details = @projects.collect do |project|
|
31
|
+
actions_and_infos = project.read_actions
|
32
|
+
actions = actions_and_infos[:actions]
|
33
|
+
infos = actions_and_infos[:infos]
|
34
|
+
project.infos = infos
|
35
|
+
actions
|
36
|
+
end.flatten
|
37
|
+
context_name_map = read_contexts action_details
|
38
|
+
#log context_name_map
|
39
|
+
@actions = []
|
40
|
+
action_details.each do |action_detail|
|
41
|
+
context = context_name_map[action_detail[:context]]
|
42
|
+
action = Action.new(action_detail[:description],context,
|
43
|
+
action_detail[:project],action_detail[:status],action_detail[:info])
|
44
|
+
@actions << action
|
45
|
+
action.project << action
|
46
|
+
end
|
47
|
+
@contexts = context_name_map.values
|
48
|
+
log "Read #{@projects.length} projects containing #{action_details.length} actions in #{@contexts.length} contexts"
|
49
|
+
end
|
50
|
+
|
51
|
+
def read_projects_with_statuses(folder,statuses)
|
52
|
+
projects = []
|
53
|
+
statuses.each do |status|
|
54
|
+
projects += read_projects(project_dir+directory_name(status), status)
|
55
|
+
end
|
56
|
+
projects
|
57
|
+
end
|
58
|
+
def read_projects(folder,status)
|
59
|
+
#log "In #{folder} reading projects with status #{status}"
|
60
|
+
Dir["#{folder}/*.prj"].collect{|project_file_name| ProjectFromFile.new(utf_file_name(project_file_name),status)}
|
61
|
+
end
|
62
|
+
|
63
|
+
def read_contexts(action_details)
|
64
|
+
context_names = action_details.collect{|action| action[:context]}.uniq
|
65
|
+
context_name_map = {}
|
66
|
+
context_names.each do|context_name|
|
67
|
+
context_name_map[context_name]= ContextFromDirectory.new(context_name,@base_dir)
|
68
|
+
end
|
69
|
+
context_name_map
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_action_files
|
73
|
+
@contexts.each do |context|
|
74
|
+
actions_in_context = context.read_actions
|
75
|
+
actions_in_context.each do |action_in_context|
|
76
|
+
action = @actions.detect {|an_action| an_action.description == action_in_context[:description] and an_action.context == context}
|
77
|
+
if (action) then
|
78
|
+
context << action
|
79
|
+
#log "Matched\t\t#{action}"
|
80
|
+
#else
|
81
|
+
#log "Unmatched\t#{action_string.description}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
def done_actions
|
88
|
+
super + read_actions_in_done_folder
|
89
|
+
end
|
90
|
+
def read_actions_in_done_folder
|
91
|
+
done_context = ContextFromDirectory.new("@Done",@base_dir)
|
92
|
+
done_actions = done_context.read_actions
|
93
|
+
real_done_actions = []
|
94
|
+
done_actions.each do |action|
|
95
|
+
real_done_actions << @actions.detect {|an_action| an_action.description == action[:description] and an_action.context.name == action[:context]}
|
96
|
+
#log "Real action for #{action[:description]} (#{action[:context]}): #{real_action}"
|
97
|
+
end
|
98
|
+
real_done_actions
|
99
|
+
end
|
100
|
+
def project_dir
|
101
|
+
"#{@base_dir}/@Projects/"
|
102
|
+
end
|
103
|
+
|
104
|
+
def update_contexts
|
105
|
+
@contexts.each {|context| context.update}
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
|
110
|
+
def persist
|
111
|
+
@projects.each {|project| project.write(project_dir+directory_name(project.status)) if project.dirty?}
|
112
|
+
@contexts.each {|context| context.write}
|
113
|
+
@inbox.write
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/gtd'
|
2
|
+
require File.dirname(__FILE__)+'/status'
|
3
|
+
class ProjectFromFile < Project
|
4
|
+
|
5
|
+
def initialize(file_name,status)
|
6
|
+
super(File.basename(file_name,'.prj'),status)
|
7
|
+
@file_name = file_name
|
8
|
+
end
|
9
|
+
def read_actions
|
10
|
+
actions = []
|
11
|
+
infos = []
|
12
|
+
text=read_and_decode(@file_name)
|
13
|
+
if text == nil then return {} end
|
14
|
+
text.each_line do |line|
|
15
|
+
line.strip!
|
16
|
+
unless line.empty? then
|
17
|
+
if line[0..0] == "#" then
|
18
|
+
infos << line[2..-1]
|
19
|
+
else
|
20
|
+
action = Action.parse(line)
|
21
|
+
action[:project] = self
|
22
|
+
actions << action
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
{:actions=>actions, :infos=>infos}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module ProjectFileWriter
|
31
|
+
include Status
|
32
|
+
|
33
|
+
|
34
|
+
def write(folder)
|
35
|
+
log "Updating #{folder}/#{@name}.prj"
|
36
|
+
# unless status== :processed
|
37
|
+
# folder += directory_name(@status)
|
38
|
+
# end
|
39
|
+
File.makedirs folder
|
40
|
+
@actions.sort! {|a,b| index(b.status) <=> index(a.status)}
|
41
|
+
|
42
|
+
write_to_encoded_file("#{folder}/#{@name}.prj", project_file_string)
|
43
|
+
end
|
44
|
+
|
45
|
+
def project_file_string
|
46
|
+
string = ""
|
47
|
+
@infos.each do |info|
|
48
|
+
string << "# #{info}\n"
|
49
|
+
end
|
50
|
+
@actions.each do |action|
|
51
|
+
string << action.project_file_string << "\n"
|
52
|
+
end
|
53
|
+
string
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
class Project
|
58
|
+
include ProjectFileWriter,ObservingContainer
|
59
|
+
def <<(item)
|
60
|
+
@actions << item
|
61
|
+
item.project = self
|
62
|
+
item.dependents.add self
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
data/lib/status.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module Status
|
2
|
+
|
3
|
+
STATUS_ORDER=[
|
4
|
+
:unprocessed,
|
5
|
+
:processed,
|
6
|
+
:done,
|
7
|
+
:tickled,
|
8
|
+
:delegated,
|
9
|
+
:someday,
|
10
|
+
:review
|
11
|
+
]
|
12
|
+
ACTIVE_STATES=[:unprocessed,:processed,:review]
|
13
|
+
INACTIVE_STATES=[:done,:tickled,:delegated,:someday]
|
14
|
+
STATUS_SYMBOLS= {
|
15
|
+
:unprocessed => '',
|
16
|
+
:processed => '-',
|
17
|
+
:done => '+',
|
18
|
+
:tickled=>'/',
|
19
|
+
:someday=>'!'
|
20
|
+
}
|
21
|
+
|
22
|
+
SYMBOLS_STATUS=STATUS_SYMBOLS.invert
|
23
|
+
|
24
|
+
#ACTIVE = 6
|
25
|
+
def with_status(status)
|
26
|
+
find_all {|item| item.status == status}
|
27
|
+
end
|
28
|
+
def active
|
29
|
+
active_items = []
|
30
|
+
ACTIVE_STATES.each do |active_status|
|
31
|
+
#log "Reading active status #{active_status}"
|
32
|
+
active_items += with_status active_status
|
33
|
+
end
|
34
|
+
return active_items
|
35
|
+
end
|
36
|
+
|
37
|
+
def inactive
|
38
|
+
inactive_items = []
|
39
|
+
INACTIVE_STATES.each do |inactive_status|
|
40
|
+
inactive_items += with_status inactive_status
|
41
|
+
end
|
42
|
+
return inactive_items
|
43
|
+
end
|
44
|
+
def symbol(status)
|
45
|
+
STATUS_SYMBOLS[status]
|
46
|
+
end
|
47
|
+
def index(symbol)
|
48
|
+
STATUS_ORDER.index(symbol) || 0
|
49
|
+
end
|
50
|
+
def directory_name(symbol)
|
51
|
+
symbol == :unprocessed ? '' : "@#{symbol.id2name.capitalize}/"
|
52
|
+
end
|
53
|
+
STATUS_ORDER.each do |stat|
|
54
|
+
define_method(stat) {with_status(stat)}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
class Array
|
58
|
+
include Status
|
59
|
+
end
|
data/lib/utf8encoder.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/../lib/abbreviations'
|
2
|
+
|
3
|
+
describe Abbreviations," module" do
|
4
|
+
include Abbreviations
|
5
|
+
it "should not change unknown words" do
|
6
|
+
expand_text("Hululu").should eql("Hululu")
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should not change known words" do
|
10
|
+
expand_text("Computer").should eql("Computer")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should expand a sole number to the corresponding word" do
|
14
|
+
expand_text("2").should eql("Computer")
|
15
|
+
expand_text("3").should eql("Einkaufen")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should expand two numbers to two words separated by a '/'" do
|
19
|
+
expand_text("23").should eql("Computer/Eclipse")
|
20
|
+
expand_text("37").should eql("Einkaufen/Plus")
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should expand a number followed by capitalized text to the corresponding word and the text" do
|
24
|
+
expand_text("1Bert").should eql("Agenda/Bert")
|
25
|
+
expand_text("2Torrent").should eql("Computer/Torrent")
|
26
|
+
expand_text("3Schlecker").should eql("Einkaufen/Schlecker")
|
27
|
+
end
|
28
|
+
it "should expand a number followed by text to the corresponding word and the capitalized text" do
|
29
|
+
expand_text("1bert").should eql("Agenda/Bert")
|
30
|
+
expand_text("2torrent").should eql("Computer/Torrent")
|
31
|
+
expand_text("3schlecker").should eql("Einkaufen/Schlecker")
|
32
|
+
end
|
33
|
+
it "should expand two numbers followed by text to the corresponding words and the capitalized text" do
|
34
|
+
expand_text("29handy").should eql("Computer/Windows/Handy")
|
35
|
+
end
|
36
|
+
it "should translate deprecated words into new ones" do
|
37
|
+
expand_text("überall").should eql("Irgendwo")
|
38
|
+
end
|
39
|
+
it "should be able to cope with upcase/downcase text" do
|
40
|
+
expand_text("Überall").should eql("Irgendwo")
|
41
|
+
expand_text("Online").should eql("Computer/Online")
|
42
|
+
expand_text("onlIne").should eql("Computer/Online")
|
43
|
+
expand_text("PlUs").should eql("Einkaufen/Plus")
|
44
|
+
expand_text("plus").should eql("Einkaufen/Plus")
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/../lib/gtd_files'
|
2
|
+
DESCRIPTION = "Something to do"
|
3
|
+
CONTEXT = Context.new "Online"
|
4
|
+
PROJECT = "Important project"
|
5
|
+
describe "A reading action file persister" do
|
6
|
+
before(:each) do
|
7
|
+
@done_action_without_info = "+ Online Do something"
|
8
|
+
@unprocessed_action_with_info = "Agenda Ask about something (0011 asdf)"
|
9
|
+
#@action_persister = ActionFilePersister.new
|
10
|
+
end
|
11
|
+
it "should recognize the 'done' status correctly" do
|
12
|
+
action = Action.parse @done_action_without_info
|
13
|
+
action[:status].should eql(:done)
|
14
|
+
end
|
15
|
+
it "should recognize the 'unprocessed' status correctly" do
|
16
|
+
action = Action.parse @unprocessed_action_with_info
|
17
|
+
action[:status].should eql(:unprocessed)
|
18
|
+
end
|
19
|
+
it "should recognize the 'info' field" do
|
20
|
+
action = Action.parse @unprocessed_action_with_info
|
21
|
+
action[:info].should eql("0011 asdf")
|
22
|
+
end
|
23
|
+
it "should return '' for 'info' field if no info is provided" do
|
24
|
+
action = Action.parse @done_action_without_info
|
25
|
+
action[:info].should eql('')
|
26
|
+
end
|
27
|
+
it "should return the given project name on 'project'" do
|
28
|
+
action = Action.parse @done_action_without_info
|
29
|
+
#action[:project].should eql(PROJECT)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
describe "A writing action file persister" do
|
33
|
+
before(:each) do
|
34
|
+
@done_action = Action.new(DESCRIPTION,CONTEXT,PROJECT,:done,'')
|
35
|
+
@processed_action = Action.new(DESCRIPTION,CONTEXT,PROJECT,:processed,'')
|
36
|
+
@unprocessed_action = Action.new(DESCRIPTION,CONTEXT,PROJECT,:unprocessed,'')
|
37
|
+
@tickled_action = Action.new(DESCRIPTION,CONTEXT,PROJECT,:tickled,'')
|
38
|
+
#@action_persister = ActionFilePersister.new
|
39
|
+
end
|
40
|
+
it "should prepend a '+' for 'done' actions on 'to_line'" do
|
41
|
+
@done_action.project_file_string.should match(/\+/)
|
42
|
+
end
|
43
|
+
it "should prepend a '-' for 'processed' actions on 'to_line'" do
|
44
|
+
@processed_action.project_file_string.should match(/-/)
|
45
|
+
end
|
46
|
+
it "should prepend a '/' for 'tickled' actions on 'to_line'" do
|
47
|
+
@tickled_action.project_file_string.should match(/\//)
|
48
|
+
end
|
49
|
+
it "should not prepend anything for 'unprocessed' actions on 'to_line'" do
|
50
|
+
@unprocessed_action.project_file_string.should match(/\w/)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/../lib/persistence'
|
2
|
+
DESCRIPTION = "Something to do"
|
3
|
+
CONTEXT = Context.new("Online")
|
4
|
+
PROJECT = "Important project"
|
5
|
+
|
6
|
+
|
7
|
+
describe "An action" do
|
8
|
+
before(:all) do
|
9
|
+
end
|
10
|
+
before(:each) do
|
11
|
+
@action = Action.new DESCRIPTION,CONTEXT,PROJECT,:processed
|
12
|
+
end
|
13
|
+
it "should have its description in project_file_string" do
|
14
|
+
@action.project_file_string.should include(DESCRIPTION)
|
15
|
+
end
|
16
|
+
it "should return the correct string on project_file_string" do
|
17
|
+
@action.project_file_string.should eql("- #{CONTEXT.name} #{DESCRIPTION}")
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
describe "A processed action" do
|
22
|
+
before(:each) do
|
23
|
+
@action = Action.new DESCRIPTION,CONTEXT,PROJECT,:processed
|
24
|
+
end
|
25
|
+
it "should return a string starting with '-' on 'project_file_string'" do
|
26
|
+
@action.project_file_string.should match(/-/)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "A done action" do
|
31
|
+
before(:each) do
|
32
|
+
@action = Action.new DESCRIPTION,CONTEXT,PROJECT,:done
|
33
|
+
end
|
34
|
+
it "should return a string starting with '+' on 'project_file_string'" do
|
35
|
+
@action.project_file_string.should match(/\+/)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "An observed action" do
|
40
|
+
before do
|
41
|
+
@action = Action.new DESCRIPTION,CONTEXT,PROJECT,:done
|
42
|
+
@observer= mock("observer")
|
43
|
+
@action.dependents.add @observer
|
44
|
+
end
|
45
|
+
it "should notify observers when changed" do
|
46
|
+
@observer.should_receive(:update)
|
47
|
+
@action.status = 2
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'persistence'
|
2
|
+
ACTION_DESCRIPTION = "Something to do"
|
3
|
+
PROJECT = "Project"
|
4
|
+
describe "A context" do
|
5
|
+
before(:each) do
|
6
|
+
@context = Context.new("Online")
|
7
|
+
end
|
8
|
+
it "should set an added actions context and nothing else" do
|
9
|
+
@action = Action.new ACTION_DESCRIPTION,"Not Context",PROJECT,:processed
|
10
|
+
@context << @action
|
11
|
+
@action.context.should == @context
|
12
|
+
@action.project.should == PROJECT
|
13
|
+
@action.description.should == ACTION_DESCRIPTION
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Specs for Persistence_specs
|
2
|
+
$:.unshift File.join(File.dirname(__FILE__),'..','lib')
|
3
|
+
require File.dirname(__FILE__)+'/../lib/persistence.rb'
|
4
|
+
|
5
|
+
describe Object, "" do
|
6
|
+
before do
|
7
|
+
#@Persistence_specs = Persistence_specs.new
|
8
|
+
end
|
9
|
+
it "" do
|
10
|
+
#@Persistence_specs.method_call().should == ""
|
11
|
+
nil.should_not == nil
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/../lib/persistence.rb'
|
2
|
+
|
3
|
+
DESCRIPTION = "Something to do"
|
4
|
+
CONTEXT = Context.new "Online"
|
5
|
+
PROJECT = "Important project"
|
6
|
+
describe "A project read from a file" do
|
7
|
+
before(:each) do
|
8
|
+
#@file = File.new('C:/Data/GTD/@Projects/Bundesschatzbriefe.prj')
|
9
|
+
@project = Project.new('Test')
|
10
|
+
#@project.read @file
|
11
|
+
end
|
12
|
+
it "should have its description in project_file_string" do
|
13
|
+
#@project.actions.should_not be_nil
|
14
|
+
end
|
15
|
+
it "should return the correct string on project_file_string" do
|
16
|
+
#@project.project_file_string.should eql("- #{CONTEXT} #{DESCRIPTION}")
|
17
|
+
end
|
18
|
+
it "should set an added actions project and nothing else" do
|
19
|
+
wrong_project = Project.new "Wrong Project"
|
20
|
+
@action = Action.new DESCRIPTION,CONTEXT,wrong_project,:processed
|
21
|
+
@project << @action
|
22
|
+
@action.context.should == CONTEXT
|
23
|
+
@action.project.should == @project
|
24
|
+
@action.description.should == DESCRIPTION
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/../lib/status'
|
2
|
+
class ObjectWithStatus
|
3
|
+
attr_accessor :status
|
4
|
+
def initialize(status)
|
5
|
+
@status = status
|
6
|
+
end
|
7
|
+
end
|
8
|
+
describe "An array with status" do
|
9
|
+
|
10
|
+
before do
|
11
|
+
@array = []
|
12
|
+
@array.extend Status
|
13
|
+
@processed_object1 = ObjectWithStatus.new :processed
|
14
|
+
@processed_object2 = ObjectWithStatus.new :processed
|
15
|
+
|
16
|
+
@array << @processed_object1
|
17
|
+
@array << @processed_object2
|
18
|
+
end
|
19
|
+
it "should accept 'processed'" do
|
20
|
+
@array.processed
|
21
|
+
end
|
22
|
+
it "should return processed objects on 'processed'" do
|
23
|
+
@array.processed.should == [ @processed_object1, @processed_object2]
|
24
|
+
end
|
25
|
+
it "should return unprocessed objects on 'unprocessed'" do
|
26
|
+
@array.unprocessed.should == []
|
27
|
+
end
|
28
|
+
it "should return processed, unprocessed and review objects on 'active'" do
|
29
|
+
@array.active.should == [ @processed_object1, @processed_object2]
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# "\xEF\xBB\xBF"
|
2
|
+
require File.dirname(__FILE__)+'/../lib/utf8encoder.rb'
|
3
|
+
|
4
|
+
SOME_STRING = "something"
|
5
|
+
UMLAUT_STRING = "�������"
|
6
|
+
UTF_ENCODED_UMLAUT_STRING = "\303\244\303\266\303\274\303\237\303\204\303\226\303\234"
|
7
|
+
describe UTF8Encoder, "with BOM enabled" do
|
8
|
+
before do
|
9
|
+
@encoder = UTF8Encoder.new true,'cp1252'
|
10
|
+
end
|
11
|
+
it "should return the BOM on 'encode' with empty string" do
|
12
|
+
@encoder.encode("").should == "\xEF\xBB\xBF"
|
13
|
+
end
|
14
|
+
it "should start with BOM on 'encode' with a string" do
|
15
|
+
@encoder.encode(SOME_STRING).should match(/\xEF\xBB\xBF/u)
|
16
|
+
end
|
17
|
+
it "should contain the ANSI string passed on 'encode'" do
|
18
|
+
@encoder.encode(SOME_STRING).should include(SOME_STRING)
|
19
|
+
end
|
20
|
+
it "should convert umlauts in cp1252 to correct unicode codepoints on 'encode'" do
|
21
|
+
@encoder.encode(UMLAUT_STRING).should include(UTF_ENCODED_UMLAUT_STRING)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
metadata
CHANGED
@@ -3,7 +3,7 @@ rubygems_version: 0.9.2
|
|
3
3
|
specification_version: 1
|
4
4
|
name: gtdfiles
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.8.
|
6
|
+
version: 0.8.1
|
7
7
|
date: 2007-06-07 00:00:00 +02:00
|
8
8
|
summary: The author was too lazy to write a summary
|
9
9
|
require_paths:
|
@@ -11,7 +11,7 @@ require_paths:
|
|
11
11
|
email: martin.mauch@gmail.com
|
12
12
|
homepage: " by Martin Mauch"
|
13
13
|
rubyforge_project: gtdfiles
|
14
|
-
description: "== FEATURES/PROBLEMS: == SYNOPSIS: *
|
14
|
+
description: "== FEATURES/PROBLEMS: == SYNOPSIS: * gtdfiles --process C:\\GTD * gtdfiles --simulate --process C:\\GTD * gtdfiles --reset C:\\GTD == REQUIREMENTS:"
|
15
15
|
autorequire:
|
16
16
|
default_executable:
|
17
17
|
bindir: bin
|
@@ -34,7 +34,28 @@ files:
|
|
34
34
|
- README.txt
|
35
35
|
- Rakefile
|
36
36
|
- bin/gtdfiles
|
37
|
+
- lib/abbreviations.rb
|
38
|
+
- lib/action_persistence.rb
|
39
|
+
- lib/context_persistence.rb
|
40
|
+
- lib/encoding_handler.rb
|
41
|
+
- lib/gtd.rb
|
42
|
+
- lib/gtd_config.rb
|
37
43
|
- lib/gtd_files.rb
|
44
|
+
- lib/inbox_persistence.rb
|
45
|
+
- lib/logging.rb
|
46
|
+
- lib/persistence.rb
|
47
|
+
- lib/project_persistence.rb
|
48
|
+
- lib/status.rb
|
49
|
+
- lib/utf8encoder.rb
|
50
|
+
- spec/abbreviations_specs.rb
|
51
|
+
- spec/action_file_persister_specs.rb
|
52
|
+
- spec/action_specs.rb
|
53
|
+
- spec/context_specs.rb
|
54
|
+
- spec/persistence_specs.rb
|
55
|
+
- spec/project_file_persister_specs.rb
|
56
|
+
- spec/project_specs.rb
|
57
|
+
- spec/status_specs.rb
|
58
|
+
- spec/utf8encoder_specs.rb
|
38
59
|
- test/test_gtd_files.rb
|
39
60
|
test_files:
|
40
61
|
- test/test_gtd_files.rb
|