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 CHANGED
@@ -1,8 +1,9 @@
1
- == 0.7.0 / 2007-05-26
2
-
3
- * Initial release on Rubyforge
4
-
5
1
  == 0.8.0 / 2007-06-07
6
2
 
7
3
  * Now writing UTF8 files with BOM
8
4
  * Packaged as gem
5
+
6
+ == 0.7.0 / 2007-05-26
7
+
8
+ * Initial release on Rubyforge
9
+
data/Manifest.txt CHANGED
@@ -1,7 +1,28 @@
1
- History.txt
2
- Manifest.txt
3
- README.txt
4
- Rakefile
5
- bin/gtdfiles
6
- lib/gtd_files.rb
7
- test/test_gtd_files.rb
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
- * gtd_files --process C:\GTD
44
- * gtd_files --simulate --process C:\GTD
45
- * gtd_files --reset C:\GTD
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
@@ -32,7 +32,7 @@ require 'choice'
32
32
  require File.dirname(__FILE__)+'/persistence'
33
33
  require File.dirname(__FILE__)+'/logging'
34
34
  class GTDFiles
35
- VERSION = '0.8.0'
35
+ VERSION = '0.8.1'
36
36
  end
37
37
 
38
38
 
@@ -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
@@ -0,0 +1,7 @@
1
+ def log(message,importance=0)
2
+ begin
3
+ puts Iconv.conv(GTDConfig[:console_encoding],'utf-8',message)
4
+ rescue
5
+ puts message + " (not decoded)"
6
+ end
7
+ end
@@ -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
@@ -0,0 +1,9 @@
1
+ require 'iconv'
2
+ class UTF8Encoder
3
+ def initialize(bom,current_encoding='utf-8')
4
+ @converter = Iconv.new('utf-8',current_encoding)
5
+ end
6
+ def encode(string)
7
+ "\xEF\xBB\xBF"<<@converter.iconv(string)
8
+ end
9
+ end
@@ -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,7 @@
1
+ require File.dirname(__FILE__)+'/../lib/gtd_files'
2
+
3
+ describe "A project file persister" do
4
+ before(:each) do
5
+ @project_persister = ProjectFilePersister.new
6
+ end
7
+ 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.0
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: * gtd_files --process C:\\GTD * gtd_files --simulate --process C:\\GTD * gtd_files --reset C:\\GTD == REQUIREMENTS:"
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