slaw 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +7 -0
- data/lib/slaw/act.rb +243 -0
- data/lib/slaw/bylaw.rb +53 -0
- data/lib/slaw/collection.rb +32 -0
- data/lib/slaw/elasticsearch.rb +107 -0
- data/lib/slaw/lifecycle_event.rb +23 -0
- data/lib/slaw/logging.rb +14 -0
- data/lib/slaw/namespace.rb +7 -0
- data/lib/slaw/parse/blocklists.rb +181 -0
- data/lib/slaw/parse/builder.rb +263 -0
- data/lib/slaw/parse/bylaw.treetop +259 -0
- data/lib/slaw/parse/cleanser.rb +171 -0
- data/lib/slaw/parse/error.rb +26 -0
- data/lib/slaw/parse/grammar_helpers.rb +11 -0
- data/lib/slaw/parse/nodes.rb +371 -0
- data/lib/slaw/render/html.rb +53 -0
- data/lib/slaw/render/xsl/act.xsl +15 -0
- data/lib/slaw/render/xsl/elements.xsl +116 -0
- data/lib/slaw/render/xsl/fragment.xsl +16 -0
- data/lib/slaw/version.rb +3 -0
- data/lib/slaw/xml_support.rb +77 -0
- data/lib/slaw.rb +24 -0
- data/slaw.gemspec +30 -0
- data/spec/parse/builder_spec.rb +543 -0
- data/spec/parse/bylaw_spec.rb +365 -0
- data/spec/parse/cleanser_spec.rb +126 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/xml_helpers.rb +46 -0
- metadata +194 -0
@@ -0,0 +1,365 @@
|
|
1
|
+
require 'slaw'
|
2
|
+
require 'builder'
|
3
|
+
|
4
|
+
describe Slaw::Parse::Builder do
|
5
|
+
def parse(rule, s)
|
6
|
+
subject.text_to_syntax_tree(s, rule)
|
7
|
+
end
|
8
|
+
|
9
|
+
def should_parse(rule, s)
|
10
|
+
s << "\n" unless s.end_with?("\n")
|
11
|
+
tree = subject.text_to_syntax_tree(s, rule)
|
12
|
+
|
13
|
+
if not tree
|
14
|
+
raise Exception.new(subject.failure_reason || "Couldn't match to grammar") if tree.nil?
|
15
|
+
else
|
16
|
+
# count an assertion
|
17
|
+
tree.should_not be_nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_xml(node)
|
22
|
+
s = ""
|
23
|
+
b = Builder::XmlMarkup.new(target: s)
|
24
|
+
node.to_xml(b)
|
25
|
+
s
|
26
|
+
end
|
27
|
+
|
28
|
+
#-------------------------------------------------------------------------------
|
29
|
+
# Chapters
|
30
|
+
#
|
31
|
+
describe 'chapters' do
|
32
|
+
it 'should handle chapter headers' do
|
33
|
+
node = parse :chapter, <<EOS
|
34
|
+
ChaPTEr 2
|
35
|
+
The Chapter Heading
|
36
|
+
1. Section
|
37
|
+
Hello there
|
38
|
+
EOS
|
39
|
+
node.num.should == "2"
|
40
|
+
node.heading.title.should == 'The Chapter Heading'
|
41
|
+
to_xml(node).should == "<chapter id=\"chapter-2\"><num>2</num><heading>The Chapter Heading</heading><section id=\"section-1\"><num>1.</num><heading>Section</heading><subsection id=\"section-1.subsection-0\"><content><p>Hello there</p></content></subsection></section></chapter>"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
#-------------------------------------------------------------------------------
|
46
|
+
# Parts
|
47
|
+
|
48
|
+
describe 'parts' do
|
49
|
+
it 'should handle part headers' do
|
50
|
+
node = parse :part, <<EOS
|
51
|
+
pART 2
|
52
|
+
The Part Heading
|
53
|
+
1. Section
|
54
|
+
Hello there
|
55
|
+
EOS
|
56
|
+
node.num.should == "2"
|
57
|
+
node.heading.title.should == 'The Part Heading'
|
58
|
+
to_xml(node).should == "<part id=\"part-2\"><num>2</num><heading>The Part Heading</heading><section id=\"section-1\"><num>1.</num><heading>Section</heading><subsection id=\"section-1.subsection-0\"><content><p>Hello there</p></content></subsection></section></part>"
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should handle parts and odd section numbers' do
|
62
|
+
subject.parse_options = {section_number_after_title: false}
|
63
|
+
node = parse :bylaw, <<EOS
|
64
|
+
PART 1
|
65
|
+
PREVENTION AND SUPPRESSION OF HEALTH NUISANCES
|
66
|
+
1.
|
67
|
+
No owner or occupier of any shop or business premises or vacant land adjoining a shop or business premises shall cause a health nuisance.
|
68
|
+
EOS
|
69
|
+
|
70
|
+
part = node.elements[1].elements[0].elements[1].elements[0]
|
71
|
+
part.heading.num.should == "1"
|
72
|
+
part.heading.title.should == "PREVENTION AND SUPPRESSION OF HEALTH NUISANCES"
|
73
|
+
|
74
|
+
section = part.elements[1].elements[0]
|
75
|
+
section.section_title.title.should == ""
|
76
|
+
section.section_title.section_title_prefix.number_letter.text_value.should == "1"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
#-------------------------------------------------------------------------------
|
81
|
+
# Subsections
|
82
|
+
|
83
|
+
describe 'subsection' do
|
84
|
+
it 'should handle basic subsections' do
|
85
|
+
should_parse :subsection, <<EOS
|
86
|
+
(2) foo bar
|
87
|
+
EOS
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should handle a naked statement' do
|
91
|
+
should_parse :subsection, 'naked statement'
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should handle a naked statement and blocklist' do
|
95
|
+
node = parse :subsection, <<EOS
|
96
|
+
naked statement (c) blah
|
97
|
+
(a) foo
|
98
|
+
(b) bar
|
99
|
+
EOS
|
100
|
+
node.statement.content.text_value.should == "naked statement (c) blah"
|
101
|
+
node.blocklist.elements.first.num.should == "(a)"
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'should handle a blocklist' do
|
105
|
+
node = parse :subsection, <<EOS
|
106
|
+
(2) title
|
107
|
+
(a) one
|
108
|
+
(b) two
|
109
|
+
(c) three
|
110
|
+
(i) four
|
111
|
+
EOS
|
112
|
+
node.statement.num.should == "(2)"
|
113
|
+
node.statement.content.text_value.should == "title"
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'should handle a subsection that dives straight into a list' do
|
117
|
+
node = parse(:subsection, <<EOS
|
118
|
+
(1) (a) one
|
119
|
+
(b) two
|
120
|
+
(c) three
|
121
|
+
(i) four
|
122
|
+
EOS
|
123
|
+
)
|
124
|
+
node.statement.content.should be_nil
|
125
|
+
node.blocklist.elements.first.num.should == "(a)"
|
126
|
+
node.blocklist.elements.first.content.should == "one"
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'should handle a blocklist that dives straight into another list' do
|
130
|
+
node = parse(:subsection, <<EOS
|
131
|
+
(1) here's my really cool list,
|
132
|
+
(a) one
|
133
|
+
(b) (i) single
|
134
|
+
(ii) double
|
135
|
+
EOS
|
136
|
+
)
|
137
|
+
node.statement.content.text_value.should == "here's my really cool list,"
|
138
|
+
node.blocklist.elements.first.num.should == "(a)"
|
139
|
+
node.blocklist.elements.first.content.should == "one"
|
140
|
+
node.blocklist.elements[1].num.should == "(b)"
|
141
|
+
node.blocklist.elements[1].content.should be_nil
|
142
|
+
node.blocklist.elements[2].num.should == "(i)"
|
143
|
+
node.blocklist.elements[2].content.should == "single"
|
144
|
+
end
|
145
|
+
|
146
|
+
context 'dotted numbers' do
|
147
|
+
it 'should handle dotted number subsection numbers' do
|
148
|
+
node = parse :subsection, <<EOS
|
149
|
+
9.9. foo
|
150
|
+
EOS
|
151
|
+
node.statement.content.text_value.should == "foo"
|
152
|
+
node.statement.num.should == "9.9"
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'should handle dotted number sublists' do
|
156
|
+
node = parse(:subsection, <<EOS
|
157
|
+
9.9 foo
|
158
|
+
9.9.1 item1
|
159
|
+
9.9.2 item2
|
160
|
+
9.9.2.1 item3
|
161
|
+
EOS
|
162
|
+
)
|
163
|
+
node.statement.content.text_value.should == "foo"
|
164
|
+
node.blocklist.elements.first.num.should == "9.9.1"
|
165
|
+
node.blocklist.elements.first.content.should == "item1"
|
166
|
+
|
167
|
+
node.blocklist.elements[2].num.should == "9.9.2.1"
|
168
|
+
node.blocklist.elements[2].content.should == "item3"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
#-------------------------------------------------------------------------------
|
174
|
+
# Numbered statements
|
175
|
+
|
176
|
+
describe 'numbered_statement' do
|
177
|
+
it 'should handle basic numbered statements' do
|
178
|
+
should_parse :numbered_statement, '(1) foo bar'
|
179
|
+
should_parse :numbered_statement, '(1a) foo bar'
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
#-------------------------------------------------------------------------------
|
184
|
+
# Preamble
|
185
|
+
|
186
|
+
context 'preamble' do
|
187
|
+
it 'should consider any text at the start to be preamble' do
|
188
|
+
node = parse :bylaw, <<EOS
|
189
|
+
foo
|
190
|
+
bar
|
191
|
+
(1) stuff
|
192
|
+
(2) more stuff
|
193
|
+
baz
|
194
|
+
1. Section
|
195
|
+
(1) hello
|
196
|
+
EOS
|
197
|
+
|
198
|
+
node.elements.first.text_value.should == "foo
|
199
|
+
bar
|
200
|
+
(1) stuff
|
201
|
+
(2) more stuff
|
202
|
+
baz
|
203
|
+
"
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'should support an optional preamble' do
|
207
|
+
node = parse :bylaw, <<EOS
|
208
|
+
PREAMBLE
|
209
|
+
foo
|
210
|
+
1. Section
|
211
|
+
(1) hello
|
212
|
+
EOS
|
213
|
+
|
214
|
+
node.elements.first.text_value.should == "PREAMBLE\nfoo\n"
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'should support no preamble' do
|
218
|
+
node = parse :bylaw, <<EOS
|
219
|
+
1. Section
|
220
|
+
bar
|
221
|
+
EOS
|
222
|
+
|
223
|
+
node.elements.first.text_value.should == ""
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
#-------------------------------------------------------------------------------
|
229
|
+
# Sections
|
230
|
+
|
231
|
+
context 'sections' do
|
232
|
+
it 'should handle section numbers after title' do
|
233
|
+
subject.parse_options = {section_number_after_title: true}
|
234
|
+
node = parse :bylaw, <<EOS
|
235
|
+
Section
|
236
|
+
1. (1) hello
|
237
|
+
EOS
|
238
|
+
|
239
|
+
section = node.elements[1].elements[0].elements[1].elements[0].elements[1].elements[0]
|
240
|
+
section.section_title.content.text_value.should == "Section"
|
241
|
+
section.section_title.section_title_prefix.number_letter.text_value.should == "1"
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'should handle section numbers before title' do
|
245
|
+
subject.parse_options = {section_number_after_title: false}
|
246
|
+
node = parse :bylaw, <<EOS
|
247
|
+
1. Section
|
248
|
+
(1) hello
|
249
|
+
EOS
|
250
|
+
|
251
|
+
section = node.elements[1].elements[0].elements[1].elements[0].elements[1].elements[0]
|
252
|
+
section.section_title.title.should == "Section"
|
253
|
+
section.section_title.num.should == "1"
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'should handle section numbers without a dot' do
|
257
|
+
subject.parse_options = {section_number_after_title: false}
|
258
|
+
node = parse :bylaw, <<EOS
|
259
|
+
1 A section
|
260
|
+
(1) hello
|
261
|
+
2 Another section
|
262
|
+
(2) Another line
|
263
|
+
EOS
|
264
|
+
|
265
|
+
section = node.elements[1].elements[0].elements[1].elements[0].elements[1].elements[0]
|
266
|
+
section.section_title.title.should == "A section"
|
267
|
+
section.section_title.num.should == "1"
|
268
|
+
|
269
|
+
section = node.elements[1].elements[0].elements[1].elements[0].elements[1].elements[1]
|
270
|
+
section.section_title.title.should == "Another section"
|
271
|
+
section.section_title.num.should == "2"
|
272
|
+
end
|
273
|
+
|
274
|
+
it 'should handle sections without titles' do
|
275
|
+
subject.parse_options = {section_number_after_title: false}
|
276
|
+
node = parse :bylaw, <<EOS
|
277
|
+
1. No owner or occupier of any shop or business premises or vacant land, blah blah
|
278
|
+
2. Notwithstanding the provision of any other By-law or legislation no person shall—
|
279
|
+
EOS
|
280
|
+
|
281
|
+
section = node.elements[1].elements[0].elements[1].elements[0].elements[1].elements[0]
|
282
|
+
section.section_title.title.should == "No owner or occupier of any shop or business premises or vacant land, blah blah"
|
283
|
+
section.section_title.num.should == "1"
|
284
|
+
|
285
|
+
section = node.elements[1].elements[0].elements[1].elements[0].elements[1].elements[1]
|
286
|
+
section.section_title.title.should == ""
|
287
|
+
section.section_title.num.should == "2"
|
288
|
+
section.subsections.elements[0].statement.content.text_value.should == "Notwithstanding the provision of any other By-law or legislation no person shall—"
|
289
|
+
end
|
290
|
+
|
291
|
+
it 'should handle sections without titles and with subsections' do
|
292
|
+
subject.parse_options = {section_number_after_title: false}
|
293
|
+
node = parse :bylaw, <<EOS
|
294
|
+
10. (1) Transporters must remove medical waste.
|
295
|
+
(2) Without limiting generality, stuff.
|
296
|
+
EOS
|
297
|
+
|
298
|
+
section = node.elements[1].elements[0].elements[1].elements[0].elements[1].elements[0]
|
299
|
+
section.section_title.title.should == ""
|
300
|
+
section.section_title.num.should == "10"
|
301
|
+
section.subsections.elements[0].statement.num.should == "(1)"
|
302
|
+
section.subsections.elements[0].statement.content.text_value.should == "Transporters must remove medical waste."
|
303
|
+
end
|
304
|
+
|
305
|
+
it 'should realise complex section titles are actually section content' do
|
306
|
+
subject.parse_options = {section_number_after_title: false}
|
307
|
+
node = parse :bylaw, <<EOS
|
308
|
+
10. The owner of any premises which is let or sublet to more than one tenant, shall maintain at all times in a clean and sanitary condition every part of such premises as may be used in common by more than one tenant.
|
309
|
+
11. No person shall keep, cause or suffer to be kept any factory or trade premises so as to cause or give rise to smells or effluvia that constitute a health nuisance.
|
310
|
+
EOS
|
311
|
+
|
312
|
+
section = node.elements[1].elements[0].elements[1].elements[0].elements[1].elements[0]
|
313
|
+
section.section_title.title.should == ""
|
314
|
+
section.section_title.num.should == "10"
|
315
|
+
section.subsections.elements[0].statement.content.text_value.should == "The owner of any premises which is let or sublet to more than one tenant, shall maintain at all times in a clean and sanitary condition every part of such premises as may be used in common by more than one tenant."
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
#-------------------------------------------------------------------------------
|
320
|
+
# schedules
|
321
|
+
|
322
|
+
context 'schedules' do
|
323
|
+
it 'should handle a simple schedule' do
|
324
|
+
node = parse :schedules, <<EOS
|
325
|
+
Schedule
|
326
|
+
Subject to approval in terms of this By-Law, the erection:
|
327
|
+
1. Foo
|
328
|
+
2. Bar
|
329
|
+
EOS
|
330
|
+
|
331
|
+
sched = node.schedules.elements[0]
|
332
|
+
sched.schedule_heading.schedule_heading_prefix.text_value.should == "Schedule"
|
333
|
+
sched.statements.elements[0].content.text_value.should == "Subject to approval in terms of this By-Law, the erection:"
|
334
|
+
sched.statements.elements[1].content.text_value.should == "1. Foo"
|
335
|
+
sched.statements.elements[2].content.text_value.should == "2. Bar"
|
336
|
+
end
|
337
|
+
|
338
|
+
it 'should handle many schedules' do
|
339
|
+
node = parse :schedules, <<EOS
|
340
|
+
Schedule "1"
|
341
|
+
A Title
|
342
|
+
1. Foo
|
343
|
+
2. Bar
|
344
|
+
Schedule 2
|
345
|
+
Another Title
|
346
|
+
Baz
|
347
|
+
Boom
|
348
|
+
EOS
|
349
|
+
|
350
|
+
sched = node.schedules.elements[0]
|
351
|
+
sched.schedule_heading.schedule_heading_prefix.text_value.should == "Schedule"
|
352
|
+
sched.schedule_heading.schedule_title.content.text_value.should == "A Title"
|
353
|
+
sched.schedule_heading.num.text_value.should == "1"
|
354
|
+
sched.statements.elements[0].content.text_value.should == "1. Foo"
|
355
|
+
sched.statements.elements[1].content.text_value.should == "2. Bar"
|
356
|
+
|
357
|
+
sched = node.schedules.elements[1]
|
358
|
+
sched.schedule_heading.schedule_heading_prefix.text_value.should == "Schedule"
|
359
|
+
sched.schedule_heading.schedule_title.content.text_value.should == "Another Title"
|
360
|
+
sched.schedule_heading.num.text_value.should == "2"
|
361
|
+
sched.statements.elements[0].content.text_value.should == "Baz"
|
362
|
+
sched.statements.elements[1].content.text_value.should == "Boom"
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'slaw'
|
4
|
+
|
5
|
+
describe Slaw::Parse::Cleanser do
|
6
|
+
describe '#remove_empty_lines' do
|
7
|
+
it 'should remove empty lines' do
|
8
|
+
subject.remove_empty_lines("foo\n \n\n bar\n\n\nbaz\n").should == "foo\n bar\nbaz"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#unbreak_lines' do
|
13
|
+
it 'should unbreak simple lines' do
|
14
|
+
subject.unbreak_lines("""
|
15
|
+
8.2.3 an additional fee or tariff, which is
|
16
|
+
to be determined by the City in its
|
17
|
+
sole discretion, in respect of additional
|
18
|
+
costs incurred or services.
|
19
|
+
8.3 In the event that a person qualifies for
|
20
|
+
a permit, but has motivated in writing
|
21
|
+
the inability to pay the fee contemplated.""").should == """
|
22
|
+
8.2.3 an additional fee or tariff, which is to be determined by the City in its sole discretion, in respect of additional costs incurred or services.
|
23
|
+
8.3 In the event that a person qualifies for a permit, but has motivated in writing the inability to pay the fee contemplated."""
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should not unbreak section headers' do
|
27
|
+
subject.unbreak_lines("""
|
28
|
+
8.4.3 must be a South African citizen, failing which, must be in possession of
|
29
|
+
a valid work permit which includes, but is not limited to, a refugee
|
30
|
+
permit; and
|
31
|
+
8.4.4 must not employ and actively utilise the services of more than 20
|
32
|
+
(twenty) persons.""").should == """
|
33
|
+
8.4.3 must be a South African citizen, failing which, must be in possession of a valid work permit which includes, but is not limited to, a refugee permit; and
|
34
|
+
8.4.4 must not employ and actively utilise the services of more than 20
|
35
|
+
(twenty) persons."""
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#break_lines' do
|
40
|
+
it 'should break nested lists' do
|
41
|
+
subject.break_lines('stored, if known; (b) the number of trolleys').should == "stored, if known;\n(b) the number of trolleys"
|
42
|
+
|
43
|
+
subject.break_lines("(5) The officer-in-Charge may – (a) remove all withered natural flowers, faded or damaged artificial flowers and any receptacle placed on a grave; or\n(b) 30 days after publishing a general").should == "(5) The officer-in-Charge may –\n(a) remove all withered natural flowers, faded or damaged artificial flowers and any receptacle placed on a grave; or\n(b) 30 days after publishing a general"
|
44
|
+
|
45
|
+
subject.break_lines("(2) No person may – (a) plant, cut or remove plants, shrubs or flowers on a grave without the permission of the officer-in-charge; (b) plant, cut or remove plants, shrubs or flowers on the berm section; or").should == "(2) No person may –\n(a) plant, cut or remove plants, shrubs or flowers on a grave without the permission of the officer-in-charge;\n(b) plant, cut or remove plants, shrubs or flowers on the berm section; or"
|
46
|
+
|
47
|
+
subject.break_lines('(b) its successor in title; or (c) a structure or person exercising a delegated power or carrying out an instruction, where any power in these By-laws, has been delegated or sub-delegated or an instruction given as contemplated in, section 59 of the Local Government: Municipal Systems Act, 2000 (Act No. 32 of 2000); or (d) a service provider fulfilling a responsibility under these By-laws, assigned to it in terms of section 81(2) of the Local Government: Municipal Systems Act, 2000, or any other law, as the case may be;').should == "(b) its successor in title; or\n(c) a structure or person exercising a delegated power or carrying out an instruction, where any power in these By-laws, has been delegated or sub-delegated or an instruction given as contemplated in, section 59 of the Local Government: Municipal Systems Act, 2000 (Act No. 32 of 2000); or\n(d) a service provider fulfilling a responsibility under these By-laws, assigned to it in terms of section 81(2) of the Local Government: Municipal Systems Act, 2000, or any other law, as the case may be;"
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should break at likely subsections' do
|
51
|
+
subject.break_lines('(c) place a metal cot on any grave. (3) A person may only erect, place or leave, an object or decoration on a grave during the first 30 days following the burial. (4) Natural or artificial flowers contained in receptacles may be placed on a grave at any time, but in a grave within a berm section or with a headstone, such flowers may only be placed in the socket provided. (5) The officer-in-Charge may – (a) remove all withered natural flowers, faded or damaged artificial flowers and any receptacle placed on a grave; or').should == "(c) place a metal cot on any grave.\n(3) A person may only erect, place or leave, an object or decoration on a grave during the first 30 days following the burial.\n(4) Natural or artificial flowers contained in receptacles may be placed on a grave at any time, but in a grave within a berm section or with a headstone, such flowers may only be placed in the socket provided.\n(5) The officer-in-Charge may – (a) remove all withered natural flowers, faded or damaged artificial flowers and any receptacle placed on a grave; or"
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should break lines at likely section titles' do
|
55
|
+
subject.break_lines('foo bar. New section title 62. (1) For the purpose').should == "foo bar.\nNew section title\n62. (1) For the purpose"
|
56
|
+
subject.break_lines('New section title 62. (1) For the purpose').should == "New section title\n62. (1) For the purpose"
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should clean up wrapped definition lines after pdf' do
|
60
|
+
subject.break_lines('“agricultural holding” means a portion of land not less than 0.8 hectares in extent used solely or mainly for the purpose of agriculture, horticulture or for breeding or keeping domesticated animals, poultry or bees; “approved” means as approved by the Council; “bund wall” means a containment wall surrounding an above ground storage tank, constructed of an impervious material and designed to contain 110% of the contents of the tank; “certificate of fitness” means a certificate contemplated in section 20; “certificate of registration” means a certificate contemplated in section 35;').should == "“agricultural holding” means a portion of land not less than 0.8 hectares in extent used solely or mainly for the purpose of agriculture, horticulture or for breeding or keeping domesticated animals, poultry or bees;\n“approved” means as approved by the Council;\n“bund wall” means a containment wall surrounding an above ground storage tank, constructed of an impervious material and designed to contain 110% of the contents of the tank;\n“certificate of fitness” means a certificate contemplated in section 20;\n“certificate of registration” means a certificate contemplated in section 35;"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#strip_toc' do
|
65
|
+
it 'should handle no toc' do
|
66
|
+
s = "City of Johannesburg Metropolitan Municipality
|
67
|
+
CULTURE AND RECREATION BY-LAWS ( )PUBLISHED IN PROVINCIAL GAZETTE EXTRAORDINARY NO 179 DATED 21 MAY 2004 UNDER NOTICE NUMBER 825
|
68
|
+
|
69
|
+
CITY OF JOHANNESBURG METROPOLITAN MUNICIPALITY
|
70
|
+
CULTURE AND RECREATION BY-LAWS
|
71
|
+
The Municipal Manager of the City of Johannesburg Metropolitan Municipality hereby, in terms of Section 13(a) of the Local Government: Municipal Systems Act, 2000 (Act No. 32 of 2000), publishes the Culture and RecreationBy-laws for the City of Johannesburg Metropolitan Municipality, as approved by its Council, as set out hereunder.
|
72
|
+
CITY OF JOHANNESBURG METROPOLITAN MUNICIPALITY
|
73
|
+
CULTURE AND RECREATION BY-LAWS
|
74
|
+
CHAPTER 1 LIBRARY AND INFORMATION SERVICES
|
75
|
+
Definitions and interpretation
|
76
|
+
1. (1) In this Chapter, unless the context otherwise indicates-"
|
77
|
+
subject.strip_toc(s).should == s
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should strip table of contents' do
|
81
|
+
subject.strip_toc("City of Johannesburg Metropolitan Municipality
|
82
|
+
CULTURE AND RECREATION BY-LAWS ( )PUBLISHED IN PROVINCIAL GAZETTE EXTRAORDINARY NO 179 DATED 21 MAY 2004 UNDER NOTICE NUMBER 825
|
83
|
+
|
84
|
+
CITY OF JOHANNESBURG METROPOLITAN MUNICIPALITY
|
85
|
+
CULTURE AND RECREATION BY-LAWS
|
86
|
+
The Municipal Manager of the City of Johannesburg Metropolitan Municipality hereby, in terms of Section 13(a) of the Local Government: Municipal Systems Act, 2000 (Act No. 32 of 2000), publishes the Culture and RecreationBy-laws for the City of Johannesburg Metropolitan Municipality, as approved by its Council, as set out hereunder.
|
87
|
+
CITY OF JOHANNESBURG METROPOLITAN MUNICIPALITY
|
88
|
+
CULTURE AND RECREATION BY-LAWS
|
89
|
+
TABLE OF CONTENTS
|
90
|
+
CHAPTER 1
|
91
|
+
LIBRARY AND INFORMATION SERVICES
|
92
|
+
1. Definitions and interpretation 2. Admission to library buildings 3. Membership 4. Loan of library material 5. Return of library material 6. Overdue library material 7. Reservation of library material 8. Lost and damaged library material 9. Handling of library material 10. Exposure of library material to notifiable and infectious diseases 11. Library material for special and reference purposes 12. Reproduction of library material and objects and use of facsimile facilities 13. Library hours 14. Hire and use of auditoria and lecture rooms or library space 15. Internet viewing stations 16. Hiring of multimedia library space 17. Performing arts library 18. Availability of By-laws and notices in a library 19. Conduct in the libraries
|
93
|
+
CHAPTER 2
|
94
|
+
ARTS AND CULTURE AND COMMUNITY CENTRE FACILITIES
|
95
|
+
|
96
|
+
Part 1: Hire and use of community arts & culture facilities
|
97
|
+
20. Definitions and interpretation 21. Rights and status of artists 22. Co-operation between Council departments 23. Application for hiring of premises 24. Prescribed fees 25. Payment of fees 26. Period of hire 27. Adjustment of period of hire 28. Joint hire 29. Sub-letting 30. Condition of premises 31. Duties of the hirer 32. Advertisements and decorations 33. Admissions and sale of tickets 34. Overcrowding 35. Sale of refreshments 36. Services 37. Cancellation due to destruction of premises 38. Cancellation due to non-compliance 39. Termination of period of hire 40 Fire hazards and Insurance 41. Storage facilities 42. Equipment 43. Right of entry 44. Inspection 45. Regulations 46. Nuisance
|
98
|
+
Part 2: Community centres
|
99
|
+
47. Group activities 48. Membership 49. Membership fees 50. Use of centres for religious or personal purposes 51. Dress code 52. Conduct of children 53. Application of certain sections of part 1 of Chapter 2 to centres 54. Application of certain sections of Part 2 of Chapter 3 to centres
|
100
|
+
|
101
|
+
CHAPTER 3
|
102
|
+
RECREATION AND SPORT
|
103
|
+
Part 1: Camping and caravan parks
|
104
|
+
55. Definitions and interpretation 56. Lighting of fires prohibited 57. Permits 58. Extension of permits 59. Limitation on the period of occupancy of a camping site 60. Allocation and Use of sites 61. Proper use of roads and pathways 62. Reservation of sites 63. Right of refusal to issue or renew permits 64. Obligations of permit holders 65. Cancellation of permits 66. Access and loitering by members of the public prohibited 67. Site to be left in a clean condition 68. Washing of clothes and utensils and preparation of foodstuffs 69. Trading without permission 70. Damage to Vegetation or Property 71. Instructions of camping officer to be complied with 72 Registration and use of firearms 73. Protection of wildlife 74. Special requirements regarding caravan parks and caravans
|
105
|
+
Part 2: Sport Facilities
|
106
|
+
75. Definitions and interpretation 76. Administration 77. Access conditions 78. Smoking 79. Alcoholic beverages 80. Duties of hirer 81. Dress code 82. Hiring of sport facilities 83. Reservation of sport facilities by the Council 84. Group activities 85. Public decency 86. Clothing and personal effects 87. Prescribed fees 88. Generally prohibited conduct 89. Animals 90. Infectious Diseases 91. Firearms and Traditional Weapons 92. Disturbance by sound systems 93. Sale of food and refreshments 94. Filming and photographs 95. Sport advisory forum
|
107
|
+
|
108
|
+
CHAPTER 4 MISCELLANEOUS 96. Definitions and interpretation 97. Animals in facilities 98. Liability for acts and omissions 99. Offences and penalties 100.
|
109
|
+
Repeal
|
110
|
+
SCHEDULE 1 BY-LAWS REPEALED
|
111
|
+
CHAPTER 1 LIBRARY AND INFORMATION SERVICES
|
112
|
+
Definitions and interpretation
|
113
|
+
1. (1) In this Chapter, unless the context otherwise indicates-").should == "City of Johannesburg Metropolitan Municipality
|
114
|
+
CULTURE AND RECREATION BY-LAWS ( )PUBLISHED IN PROVINCIAL GAZETTE EXTRAORDINARY NO 179 DATED 21 MAY 2004 UNDER NOTICE NUMBER 825
|
115
|
+
|
116
|
+
CITY OF JOHANNESBURG METROPOLITAN MUNICIPALITY
|
117
|
+
CULTURE AND RECREATION BY-LAWS
|
118
|
+
The Municipal Manager of the City of Johannesburg Metropolitan Municipality hereby, in terms of Section 13(a) of the Local Government: Municipal Systems Act, 2000 (Act No. 32 of 2000), publishes the Culture and RecreationBy-laws for the City of Johannesburg Metropolitan Municipality, as approved by its Council, as set out hereunder.
|
119
|
+
CITY OF JOHANNESBURG METROPOLITAN MUNICIPALITY
|
120
|
+
CULTURE AND RECREATION BY-LAWS
|
121
|
+
CHAPTER 1 LIBRARY AND INFORMATION SERVICES
|
122
|
+
Definitions and interpretation
|
123
|
+
1. (1) In this Chapter, unless the context otherwise indicates-"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'xml_helpers'
|
data/spec/xml_helpers.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
def xml2doc(xml)
|
2
|
+
Nokogiri::XML(xml, &:noblanks)
|
3
|
+
end
|
4
|
+
|
5
|
+
def subsection(xml)
|
6
|
+
pre = <<XML
|
7
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
8
|
+
<akomaNtoso xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.akomantoso.org/2.0" xsi:schemaLocation="http://www.akomantoso.org/2.0 akomantoso20.xsd">
|
9
|
+
<act contains="singleVersion">
|
10
|
+
<body>
|
11
|
+
<section id="section-10">
|
12
|
+
<num>10.</num>
|
13
|
+
<heading>Effect of order for <term refersTo="#term-eviction" id="trm236">eviction</term></heading>
|
14
|
+
<subsection id="section-10.1">
|
15
|
+
<num>(1)</num>
|
16
|
+
<content>
|
17
|
+
XML
|
18
|
+
post = <<XML
|
19
|
+
</content>
|
20
|
+
</subsection>
|
21
|
+
</section>
|
22
|
+
</body>
|
23
|
+
</act>
|
24
|
+
</akomaNtoso>
|
25
|
+
XML
|
26
|
+
|
27
|
+
return pre + xml + post
|
28
|
+
end
|
29
|
+
|
30
|
+
def section(xml)
|
31
|
+
pre = <<XML
|
32
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
33
|
+
<akomaNtoso xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.akomantoso.org/2.0" xsi:schemaLocation="http://www.akomantoso.org/2.0 akomantoso20.xsd">
|
34
|
+
<act contains="singleVersion">
|
35
|
+
<body>
|
36
|
+
<section id="section-10">
|
37
|
+
XML
|
38
|
+
post = <<XML
|
39
|
+
</section>
|
40
|
+
</body>
|
41
|
+
</act>
|
42
|
+
</akomaNtoso>
|
43
|
+
XML
|
44
|
+
|
45
|
+
return pre + xml + post
|
46
|
+
end
|