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 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