org_mode 0.0.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.
@@ -0,0 +1,55 @@
1
+ require 'org_mode/commands/agenda'
2
+ require 'tempfile'
3
+ require 'core_ext/string'
4
+ require 'support/capture_stdout'
5
+ require 'support/write_into_tempfile'
6
+ require 'timecop'
7
+
8
+ # Helper to execute agenda and compare the output
9
+ def execute_and_compare_stdout_with args, options, expected_output
10
+ output = capture_stdout do
11
+ @org_mode_commands_agenda.execute(args, options)
12
+ end
13
+ output.should == expected_output
14
+ end
15
+
16
+ describe OrgMode::Commands::Agenda do
17
+ before do
18
+ Timecop.freeze('2012-01-01 15:00')
19
+
20
+ @org_mode_commands_agenda = OrgMode::Commands::Agenda.new
21
+ end
22
+
23
+ context '#execute' do
24
+ context 'when loaded with one file' do
25
+ it 'extracts and displays scheduled tasks correctly' do
26
+ org_file = write_into_tempfile <<-eos.strip_indent(10)
27
+ * TODO Scheduled task <1-1-2012 Wed 15:15>
28
+ eos
29
+ execute_and_compare_stdout_with [org_file.path], stub, <<-eos.strip_indent(10)
30
+ Agenda ()
31
+ 2012-01-01
32
+ TODO Scheduled task
33
+ eos
34
+ end
35
+ end
36
+ context 'when loaded with two files' do
37
+ it 'displays both in expected format and sorted correctly' do
38
+ org_file = write_into_tempfile <<-eos.strip_indent(10)
39
+ * TODO Scheduled task on the 5th <5-1-2012 Wed 15:15>
40
+ eos
41
+ org_file2 = write_into_tempfile <<-eos.strip_indent(10)
42
+ * TODO Scheduled task on the 1th <1-1-2012 Wed 15:15>
43
+ eos
44
+ execute_and_compare_stdout_with [org_file, org_file2].map(&:path), stub,
45
+ <<-eos.strip_indent(10)
46
+ Agenda ()
47
+ 2012-01-01
48
+ TODO Scheduled task on the 1th
49
+ 2012-01-05
50
+ TODO Scheduled task on the 5th
51
+ eos
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,323 @@
1
+ require 'org_mode'
2
+ require 'org_mode/parser'
3
+
4
+ # Parser
5
+ # ------
6
+ #
7
+ # Two way parser:
8
+ # First pass
9
+ # Find file settings
10
+ # Find all nodes
11
+ # Title and content tree structure
12
+ # Second pass
13
+ # Parse all nodes
14
+ # (using information from file, ak TODO keywords)
15
+ # Parse title
16
+ # Parse content
17
+ #
18
+ # Node
19
+ # Title
20
+ # TodoStateKeyword
21
+ # Date
22
+ # Content
23
+ # CodeBlockParser
24
+ # PlainTextParser
25
+ #
26
+ # Nodes
27
+ # SettingsNode
28
+ # settings_hash
29
+ # SettingsAttachment
30
+ # PlainTextNode
31
+ # content
32
+ # CodeBlockNode
33
+ # language
34
+ # content
35
+
36
+
37
+ # Private: Loads org example
38
+ #
39
+ # name - takes part of filename
40
+ #
41
+ # Returns the contents of the file
42
+ def load_org_example name
43
+ File.open("spec/data/org-file-#{name}.org").read
44
+ end
45
+
46
+ describe OrgMode::FileParser do
47
+ context ".parse_into_tokens" do
48
+ it "should pars only one" do
49
+ org_data = <<-org.gsub(/^\s{8}/,'')
50
+ * First
51
+ org
52
+ b,n,e = OrgMode::FileParser.parse_into_tokens(org_data)
53
+ b.should be_empty
54
+ n.should == [["* First", ""]]
55
+ e.should be_empty
56
+ end
57
+ it "should pars only one without enter" do
58
+ org_data = "* First"
59
+ b,n,e = OrgMode::FileParser.parse_into_tokens(org_data)
60
+ b.should be_empty
61
+ n.should == [["* First", ""]]
62
+ e.should be_empty
63
+ end
64
+ it "should divide data up correctly" do
65
+ org_data = <<-org.gsub(/^\s{8}/,'')
66
+ * First
67
+ ** Second
68
+ org
69
+ b,n,e = OrgMode::FileParser.parse_into_tokens(org_data)
70
+ b.should be_empty
71
+ n.should == [["* First", ""], ["** Second", ""]]
72
+ e.should be_empty
73
+ end
74
+ it "should handle nodes with content" do
75
+ org_data = <<-org.gsub(/^\s{8}/,'')
76
+ * First
77
+ Content for first node
78
+ ** Second
79
+ Content for nested node
80
+ org
81
+ b,n,e = OrgMode::FileParser.parse_into_tokens(org_data)
82
+ b.should be_empty
83
+ n.should == [["* First", " Content for first node"], ["** Second", " Content for nested node"]]
84
+ e.should be_empty
85
+ end
86
+ it "should handle content with no nodes" do
87
+ org_data = <<-org.gsub(/^\s{8}/,'')
88
+ Just some textfile without any nodes
89
+ org
90
+ b,n,e = OrgMode::FileParser.parse_into_tokens(org_data)
91
+ b.should == 'Just some textfile without any nodes'
92
+ n.should == []
93
+ e.should be_empty
94
+ end
95
+ it "should take an empty string" do
96
+ org_data = <<-org.gsub(/^\s{8}/,'')
97
+ org
98
+ b,n,e = OrgMode::FileParser.parse_into_tokens(org_data)
99
+ b.should be_empty
100
+ n.should == []
101
+ e.should be_empty
102
+ end
103
+ end
104
+
105
+ context ".parse" do
106
+ let(:org_data) { load_org_example '01-simple-node-structure' }
107
+ it "should just parse the file" do
108
+ parsed = OrgMode::FileParser.parse(org_data)
109
+ end
110
+ context 'with a parsed org_file' do
111
+ let(:org_file) { OrgMode::FileParser.parse(org_data) }
112
+
113
+ it "should return an OrgMode::File" do
114
+ org_file.should be_an_instance_of( OrgMode::File )
115
+ end
116
+
117
+ it "parses the tree correctly" do
118
+ org_file.nodes.length.should == 12
119
+ end
120
+
121
+ it "has an empty beginning" do
122
+ org_file.header.should be_empty
123
+ end
124
+ it "has an empty ending" do
125
+ org_file.footer.should be_empty
126
+ end
127
+ context 'parent_child relations' do
128
+ it "root node has no parent" do
129
+ org_file.nodes[0].parent.should be_nil
130
+ end
131
+ it "root node has 2 children" do
132
+ org_file.nodes[0].children.length.should == 2
133
+ org_file.nodes[0].children[0].title.should == 'FirstChildMain'
134
+ org_file.nodes[0].children[1].title.should == 'SecondChildMain'
135
+ end
136
+ it "ThirdMain should be in business" do
137
+ org_file.nodes[8].children[0].children[0].children[0].title.should ==
138
+ 'FirstChildFirstChildFirstChildMain'
139
+ end
140
+ it "destilled the correct root nodes" do
141
+ org_file.root_nodes.map(&:title).should == %w[Main SecondMain ThirdMain]
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ describe OrgMode::NodeParser do
149
+ context ".parse" do
150
+ context "title" do
151
+ context "standard node title" do
152
+ let(:node) {OrgMode::NodeParser.parse('** Standard node title', nil)}
153
+ it "parses the title correctly" do
154
+ node.title.should == 'Standard node title'
155
+ end
156
+ it "determines the correct stars" do
157
+ node.stars.should == 2
158
+ end
159
+ it "determines the correct indent" do
160
+ node.indent.should == 3
161
+ end
162
+ end
163
+ context "level deeper node title" do
164
+ let(:node) {OrgMode::NodeParser.parse('*** Standard node title one level deeper', nil)}
165
+ it "parses the title correctly" do
166
+ node.title.should == 'Standard node title one level deeper'
167
+ end
168
+ it "determines the correct stars" do
169
+ node.stars.should == 3
170
+ end
171
+ it "determines the correct indent" do
172
+ node.indent.should == 4
173
+ end
174
+ end
175
+ context "node title with date" do
176
+ let(:node) {OrgMode::NodeParser.parse('** Date node title <2012-02-02 Wed>', nil) }
177
+ it "parses the date from the title correcty" do
178
+ node.date.strftime('%Y-%m-%d').should == '2012-02-02'
179
+ end
180
+ end
181
+ context "node title with date time" do
182
+ let(:node) {OrgMode::NodeParser.parse('** Date node title <2012-02-03 Wed 15:15>', nil)}
183
+ it "parses the date-time from the title correcty" do
184
+ node.date.strftime('%Y-%m-%d %H:%M').should == '2012-02-03 15:15'
185
+ end
186
+ end
187
+ context "node title with date-range" do
188
+ let(:node) {OrgMode::NodeParser.parse('** Date node title <2012-02-03 Wed 15:15-16:15>', nil)}
189
+ it "parses the date from the title correcty" do
190
+ node.date.strftime('%Y-%m-%d %H:%M').should == '2012-02-03 15:15'
191
+ end
192
+ it "parses the start_tiem correctly" do
193
+ node.date_start_time.strftime('%Y-%m-%d %H:%M').should == '2012-02-03 15:15'
194
+ end
195
+ it "parses the end_time correctly" do
196
+ node.date_end_time.strftime('%Y-%m-%d %H:%M').should == '2012-02-03 16:15'
197
+ end
198
+ end
199
+ context "parses TODO states correctly" do
200
+ let(:node) {OrgMode::NodeParser.parse('** TODO Date node title', nil)}
201
+ it "parses the TODO keyword correctly" do
202
+ node.todo_state.should == 'TODO'
203
+ end
204
+ context "parses DONE state correctly" do
205
+ let(:node) {OrgMode::NodeParser.parse('** DONE Date node title', nil)}
206
+ it "parses the TODO keyword correctly" do
207
+ node.todo_state.should == 'DONE'
208
+ end
209
+ end
210
+ end
211
+ end
212
+ context "content indentation" do
213
+ context "correctly indented" do
214
+ before do
215
+ org_title, *org_content = <<-eos.gsub(/^\s{10}/,'').lines.to_a
216
+ *** Title
217
+ Content belonging
218
+ at a certain indent
219
+ should be parsed correctly
220
+ eos
221
+ @node = OrgMode::NodeParser.parse(org_title, org_content.join)
222
+ end
223
+ it "parses content and removes indent" do
224
+ @node.content.should == <<-eos.gsub(/^\s{10}/,'')
225
+ Content belonging
226
+ at a certain indent
227
+ should be parsed correctly
228
+ eos
229
+ end
230
+ end
231
+ context "too far" do
232
+ before do
233
+ org_title, *org_content = <<-eos.gsub(/^\s{10}/,'').lines.to_a
234
+ *** Title
235
+ Content belonging
236
+ at a certain indent
237
+ should be parsed correctly
238
+ eos
239
+ @node = OrgMode::NodeParser.parse(org_title, org_content.join)
240
+ end
241
+ it "parses content and removes indent" do
242
+ @node.content.should == <<-eos.gsub(/^\s{10}/,'')
243
+ Content belonging
244
+ at a certain indent
245
+ should be parsed correctly
246
+ eos
247
+ end
248
+ end
249
+ context "indented in content block" do
250
+ before do
251
+ org_title, *org_content = <<-eos.gsub(/^\s{10}/,'').lines.to_a
252
+ *** Title
253
+ Content belonging
254
+ at a certain indent
255
+ should be parsed correctly
256
+ eos
257
+ @node = OrgMode::NodeParser.parse(org_title, org_content.join)
258
+ end
259
+ it "parses content and removes indent" do
260
+ @node.content.should == <<-eos.gsub(/^\s{10}/,'')
261
+ Content belonging
262
+ at a certain indent
263
+ should be parsed correctly
264
+ eos
265
+ end
266
+ end
267
+ context "one row outdented" do
268
+ before do
269
+ org_title, *org_content = <<-eos.gsub(/^\s{10}/,'').lines.to_a
270
+ *** Title
271
+ Content belonging
272
+ at a certain indent
273
+ should be parsed correctly
274
+ eos
275
+ @node = OrgMode::NodeParser.parse(org_title, org_content.join)
276
+ end
277
+ it "parses content and removes indent" do
278
+ @node.content.should == <<-eos.gsub(/^\s{10}/,'')
279
+ Content belonging
280
+ at a certain indent
281
+ should be parsed correctly
282
+ eos
283
+ end
284
+ end
285
+ end
286
+ context "content whitespace" do
287
+ it "removes whitespace at beginning" do
288
+ org_title, *org_content = <<-eos.gsub(/^\s{8}/,'').lines.to_a
289
+ *** Title
290
+
291
+
292
+
293
+ Content belonging
294
+ at a certain indent
295
+ should be parsed correctly
296
+ eos
297
+ @node = OrgMode::NodeParser.parse(org_title, org_content.join)
298
+ @node.content.should == <<-eos.gsub(/^\s{8}/,'')
299
+ Content belonging
300
+ at a certain indent
301
+ should be parsed correctly
302
+ eos
303
+ end
304
+ it "removes whitespace at ending" do
305
+ org_title, *org_content = <<-eos.gsub(/^\s{8}/,'').lines.to_a
306
+ *** Title
307
+ Content belonging
308
+ at a certain indent
309
+ should be parsed correctly
310
+
311
+
312
+
313
+ eos
314
+ @node = OrgMode::NodeParser.parse(org_title, org_content.join)
315
+ @node.content.should == <<-eos.gsub(/^\s{8}/,'')
316
+ Content belonging
317
+ at a certain indent
318
+ should be parsed correctly
319
+ eos
320
+ end
321
+ end
322
+ end
323
+ end
@@ -0,0 +1,74 @@
1
+ require 'org_mode/reporters/agenda'
2
+ require 'support/blueprints'
3
+ require 'support/include_hash'
4
+ require 'timecop'
5
+
6
+ describe OrgMode::Reporters::Agenda do
7
+ before do
8
+ Timecop.freeze('2012-02-02')
9
+ end
10
+ let(:file_collection) do
11
+ nodes = []
12
+ files = []
13
+ nodes << OrgMode::Node.make(:stars => 1, :date=>Date.parse('2012-02-01'))
14
+ nodes << OrgMode::Node.make(:stars => 2, :date=>Date.parse('2012-02-05'))
15
+ files << OrgMode::File.make(:nodes => nodes)
16
+ nodes = []
17
+ nodes << OrgMode::Node.make(:stars => 1,
18
+ :todo_state => 'DONE',
19
+ :date => Date.parse('2012-06-01'))
20
+ nodes << OrgMode::Node.make(:stars => 2, :date=>Date.parse('2012-02-03'))
21
+ nodes << OrgMode::Node.make(:stars => 3, :date=>DateTime.parse('2012-02-01 15:00'))
22
+ files << OrgMode::File.make(:nodes => nodes)
23
+ OrgMode::FileCollection.make(:files => files)
24
+ end
25
+ let(:reporter) { OrgMode::Reporters::Agenda.new(file_collection) }
26
+
27
+ context '#open_nodes_grouped_by_day' do
28
+
29
+ let (:reported) { reporter.open_nodes_grouped_by_day }
30
+
31
+ it "extracts the correct dates and in the correct order" do
32
+ dates = reported.map {|e| e[:date] }
33
+ dates.should == ["2012-02-01", "2012-02-03", "2012-02-05"]
34
+ end
35
+ it "should group the nodecounts correctly" do
36
+ nodecount_per_date = reported.map { |e| [e[:date], e[:nodes].length] }
37
+ nodecount_per_date.should == [["2012-02-01", 2], ["2012-02-03", 1], ["2012-02-05", 1]]
38
+ end
39
+ it "should result in the following subhashes" do
40
+ reported.should ==
41
+ [{:date=>"2012-02-01",
42
+ :nodes=>
43
+ [{:title=>"org-node",
44
+ :content=>"org-node content",
45
+ :todo_state=>"TODO",
46
+ :date=>"2012-02-01 00:00",
47
+ :stars=>1},
48
+ {:title=>"org-node",
49
+ :content=>"org-node content",
50
+ :todo_state=>"TODO",
51
+ :date=>"2012-02-01 15:00",
52
+ :stars=>3}]},
53
+ {:date=>"2012-02-03",
54
+ :nodes=>
55
+ [{:title=>"org-node",
56
+ :content=>"org-node content",
57
+ :todo_state=>"TODO",
58
+ :date=>"2012-02-03 00:00",
59
+ :stars=>2}]},
60
+ {:date=>"2012-02-05",
61
+ :nodes=>
62
+ [{:title=>"org-node",
63
+ :content=>"org-node content",
64
+ :todo_state=>"TODO",
65
+ :date=>"2012-02-05 00:00",
66
+ :stars=>2}]}]
67
+ end
68
+ it "it ignores the DONE task" do
69
+ dates = reported.map {|e| e[:date] }
70
+ dates.should_not include("2012-06-01")
71
+ end
72
+ end
73
+
74
+ end
@@ -0,0 +1,52 @@
1
+ require 'org_mode'
2
+ require 'support/blueprints'
3
+
4
+ describe OrgMode::File do
5
+ let(:file) do
6
+ nodes = []
7
+ files = []
8
+ nodes << OrgMode::Node.make(:stars => 1)
9
+ nodes << OrgMode::Node.make(:stars => 2, :date=>Time.now)
10
+ nodes << OrgMode::Node.make(:stars => 3)
11
+ nodes << OrgMode::Node.make(:stars => 1)
12
+ OrgMode::File.make(:nodes => nodes)
13
+ end
14
+
15
+ it "accumulates correctly" do
16
+ file.nodes.length.should == 4
17
+ end
18
+
19
+ it "detects all rootnodes correctly" do
20
+ file.root_nodes.length.should == 2
21
+ end
22
+
23
+ it "detects all scheduled nodes correctly" do
24
+ file.scheduled_nodes.length.should == 1
25
+ end
26
+ end
27
+
28
+ describe OrgMode::FileCollection do
29
+ let(:file_collection) do
30
+ nodes = []
31
+ files = []
32
+ nodes << OrgMode::Node.make(:stars => 1)
33
+ nodes << OrgMode::Node.make(:stars => 2)
34
+ files << OrgMode::File.make(:nodes => nodes)
35
+ nodes = []
36
+ nodes << OrgMode::Node.make(:stars => 1)
37
+ nodes << OrgMode::Node.make(:stars => 2, :date=>Time.now)
38
+ nodes << OrgMode::Node.make(:stars => 3)
39
+ files << OrgMode::File.make(:nodes => nodes)
40
+ OrgMode::FileCollection.make(:files => files)
41
+ end
42
+ it "accumulates correctly" do
43
+ file_collection.nodes.length.should == 5
44
+ end
45
+
46
+ it "detects all rootnodes correctly" do
47
+ file_collection.root_nodes.length.should == 2
48
+ end
49
+ it "detects all scheduled nodes correctly" do
50
+ file_collection.scheduled_nodes.length.should == 1
51
+ end
52
+ end
@@ -0,0 +1,27 @@
1
+ require 'org_mode'
2
+
3
+ class OrgMode::Node
4
+ def self.make(attrs={})
5
+ self.new.tap do |n|
6
+ n.title = attrs[:title] || "org-node"
7
+ n.stars = attrs[:stars] || rand(4)
8
+ n.content = attrs[:content] || "org-node content"
9
+ n.date = attrs[:date]
10
+ n.todo_state = attrs[:todo_state] || 'TODO'
11
+ end
12
+ end
13
+ end
14
+
15
+ class OrgMode::File
16
+ def self.make(attrs={})
17
+ nodes = attrs[:nodes] || Array.new(2) { OrgMode::Node.make }
18
+ self.new("aheader", nodes , "afooter")
19
+ end
20
+ end
21
+
22
+ class OrgMode::FileCollection
23
+ def self.make(attrs={})
24
+ files = attrs[:files] || Array.new(2) { OrgMode::File.make }
25
+ self.new( files )
26
+ end
27
+ end
@@ -0,0 +1,12 @@
1
+ require 'stringio'
2
+
3
+ def capture_stdout
4
+ out = StringIO.new
5
+ $stdout = out
6
+ yield
7
+ out.rewind
8
+ return out.read
9
+ ensure
10
+ $stdout = STDOUT
11
+ end
12
+
@@ -0,0 +1,7 @@
1
+ RSpec::Matchers.define :include_hash do |expected|
2
+
3
+ match do |actual|
4
+ actual.present? && actual.slice(*expected.keys) == expected
5
+ end
6
+
7
+ end
@@ -0,0 +1,6 @@
1
+ def write_into_tempfile string
2
+ tmpfile = Tempfile.new('org_mode_file')
3
+ tmpfile.write(string)
4
+ tmpfile.flush
5
+ tmpfile
6
+ end