gtdfiles 0.8.0 → 0.8.1
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.
- 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
|