slaw 1.0.0.alpha.6 → 1.0.0

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.
@@ -1,3 +1,3 @@
1
1
  module Slaw
2
- VERSION = "1.0.0.alpha.6"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -8,9 +8,9 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Slaw::VERSION
9
9
  spec.authors = ["Greg Kempe"]
10
10
  spec.email = ["greg@kempe.net"]
11
- spec.summary = %q{A lightweight library for using Akoma Ntoso acts in Ruby.}
12
- spec.description = %q{Slaw is a lightweight library for rendering and generating Akoma Ntoso acts from plain text and PDF documents.}
13
- spec.homepage = ""
11
+ spec.summary = "A lightweight library for using Akoma Ntoso acts in Ruby."
12
+ spec.description = "Slaw is a lightweight library for rendering and generating Akoma Ntoso acts from plain text and PDF documents."
13
+ spec.homepage = "https://github.com/longhotsummer/slaw"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slaw
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.alpha.6
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Kempe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-16 00:00:00.000000000 Z
11
+ date: 2018-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -167,9 +167,6 @@ files:
167
167
  - Rakefile
168
168
  - bin/slaw
169
169
  - lib/slaw.rb
170
- - lib/slaw/act.rb
171
- - lib/slaw/bylaw.rb
172
- - lib/slaw/collection.rb
173
170
  - lib/slaw/extract/extractor.rb
174
171
  - lib/slaw/extract/html_to_akn_text.xsl
175
172
  - lib/slaw/extract/yomu_patch.rb
@@ -179,6 +176,7 @@ files:
179
176
  - lib/slaw/grammars/inlines_nodes.rb
180
177
  - lib/slaw/grammars/pl/act.treetop
181
178
  - lib/slaw/grammars/pl/act_nodes.rb
179
+ - lib/slaw/grammars/pl/act_text.xsl
182
180
  - lib/slaw/grammars/schedules.treetop
183
181
  - lib/slaw/grammars/schedules_nodes.rb
184
182
  - lib/slaw/grammars/tables.treetop
@@ -187,7 +185,6 @@ files:
187
185
  - lib/slaw/grammars/za/act.treetop
188
186
  - lib/slaw/grammars/za/act_nodes.rb
189
187
  - lib/slaw/grammars/za/act_text.xsl
190
- - lib/slaw/lifecycle_event.rb
191
188
  - lib/slaw/logging.rb
192
189
  - lib/slaw/namespace.rb
193
190
  - lib/slaw/parse/blocklists.rb
@@ -195,17 +192,11 @@ files:
195
192
  - lib/slaw/parse/cleanser.rb
196
193
  - lib/slaw/parse/error.rb
197
194
  - lib/slaw/parse/grammar_helpers.rb
198
- - lib/slaw/render/html.rb
199
- - lib/slaw/render/xsl/act.xsl
200
- - lib/slaw/render/xsl/elements.xsl
201
- - lib/slaw/render/xsl/fragment.xsl
202
195
  - lib/slaw/schemas/akomantoso20.xsd
203
196
  - lib/slaw/schemas/xml.xsd
204
197
  - lib/slaw/version.rb
205
198
  - lib/slaw/xml_support.rb
206
199
  - slaw.gemspec
207
- - spec/act_spec.rb
208
- - spec/bylaw_spec.rb
209
200
  - spec/extract/extractor_spec.rb
210
201
  - spec/fixtures/community-fire-safety.xml
211
202
  - spec/generator_spec.rb
@@ -218,7 +209,7 @@ files:
218
209
  - spec/za/act_inline_spec.rb
219
210
  - spec/za/act_schedules_spec.rb
220
211
  - spec/za/act_table_spec.rb
221
- homepage: ''
212
+ homepage: https://github.com/longhotsummer/slaw
222
213
  licenses:
223
214
  - MIT
224
215
  metadata: {}
@@ -233,9 +224,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
233
224
  version: '0'
234
225
  required_rubygems_version: !ruby/object:Gem::Requirement
235
226
  requirements:
236
- - - ">"
227
+ - - ">="
237
228
  - !ruby/object:Gem::Version
238
- version: 1.3.1
229
+ version: '0'
239
230
  requirements: []
240
231
  rubyforge_project:
241
232
  rubygems_version: 2.6.12
@@ -243,8 +234,6 @@ signing_key:
243
234
  specification_version: 4
244
235
  summary: A lightweight library for using Akoma Ntoso acts in Ruby.
245
236
  test_files:
246
- - spec/act_spec.rb
247
- - spec/bylaw_spec.rb
248
237
  - spec/extract/extractor_spec.rb
249
238
  - spec/fixtures/community-fire-safety.xml
250
239
  - spec/generator_spec.rb
@@ -1,452 +0,0 @@
1
- module Slaw
2
- class AknBase
3
- include Slaw::Namespace
4
-
5
- attr_accessor :doc
6
-
7
- # Serialise the XML for this act, passing `args` to the Nokogiri serialiser.
8
- # The most useful argument is usually `indent: 2` if you like your XML perdy.
9
- #
10
- # @return [String] serialized XML
11
- def to_xml(*args)
12
- @doc.to_xml(*args)
13
- end
14
-
15
- # Parse the XML contained in the file-like or String object `io`
16
- #
17
- # @param io [String, file-like] io object or String with XML
18
- def parse(io)
19
- self.doc = Nokogiri::XML(io)
20
- end
21
- end
22
-
23
- # A fragment is a part of a larger document and doesn't have the context associated
24
- # with the document.
25
- class Fragment < AknBase
26
- alias_method :fragment, :doc
27
- end
28
-
29
- # An Act wraps a single {http://www.akomantoso.org/ AkomaNtoso 2.0 XML} act document in the form of a
30
- # Nokogiri::XML::Document object.
31
- #
32
- # The Act object provides quick access to certain sections of the document,
33
- # such as the metadata and the body, as well as common operations such as
34
- # identifying whether it has been amended ({#amended?}), repealed
35
- # ({#repealed?}) or what chapters ({#chapters}), parts ({#parts}) and
36
- # sections ({#sections}) it contains.
37
- class Act < AknBase
38
-
39
- # Allow us to jump from the XML document for an act to the
40
- # Act instance itself
41
- @@acts = {}
42
-
43
- # [Nokogiri::XML::Document] The underlying {Nokogiri::XML::Document} instance
44
- attr_accessor :doc
45
-
46
- # [Nokogiri::XML::Node] The `meta` XML node
47
- attr_reader :meta
48
-
49
- # [Nokogiri::XML::Node] The `body` XML node
50
- attr_reader :body
51
-
52
- # [String] The year this act was published
53
- attr_reader :year
54
-
55
- # [String] The act number in the year this act was published
56
- attr_reader :num
57
-
58
- # [String] The FRBR URI of this act, which uniquely identifies it globally
59
- attr_reader :id_uri
60
-
61
- # [String, nil] The source filename, or nil
62
- attr_reader :filename
63
-
64
- # [Time, nil] The mtime of when the source file was last modified
65
- attr_reader :mtime
66
-
67
- # [String] The underlying nature of this act, usually `act` although subclasses my override this.
68
- attr_reader :nature
69
-
70
- # [Nokogiri::XML::Schema] schema to validate against
71
- attr_accessor :schema
72
-
73
- # Get the act that wraps the document that owns this XML node
74
- # @param node [Nokogiri::XML::Node]
75
- # @return [Act] owning act
76
- def self.for_node(node)
77
- @@acts[node.document]
78
- end
79
-
80
- # Create a new instance, loading from `filename` if given.
81
- # @param filename [String] filename to load XML from
82
- def initialize(filename=nil)
83
- self.load(filename) if filename
84
- @schema = nil
85
- end
86
-
87
- # Load the XML in `filename` into this instance
88
- # @param filename [String] filename
89
- def load(filename)
90
- @filename = filename
91
- @mtime = File::mtime(@filename)
92
-
93
- File.open(filename) { |f| parse(f) }
94
- end
95
-
96
- # Set the XML document backing this bylaw.
97
- #
98
- # @param doc [Nokogiri::XML::Document] document
99
- def doc=(doc)
100
- @doc = doc
101
- @meta = @doc.at_xpath('/a:akomaNtoso/a:act/a:meta', a: NS)
102
- @body = @doc.at_xpath('/a:akomaNtoso/a:act/a:body', a: NS)
103
-
104
- @@acts[@doc] = self
105
-
106
- extract_id_uri
107
- end
108
-
109
- # Directly set the FRBR URI of this act. This must be a well-formed URI,
110
- # such as `/za/act/2002/2`. This will, in turn, update the {#year}, {#nature},
111
- # {#country} and {#num} attributes.
112
- #
113
- # You probably don't want to use this method. Instead, set each component
114
- # (such as {#date}) manually.
115
- #
116
- # @param uri [String] new URI
117
- def id_uri=(uri)
118
- for component, xpath in [['main', '//a:act/a:meta/a:identification'],
119
- ['schedules', '//a:component/a:doc/a:meta/a:identification']] do
120
- ident = @doc.at_xpath(xpath, a: NS)
121
- next if not ident
122
-
123
- # work
124
- ident.at_xpath('a:FRBRWork/a:FRBRthis', a: NS)['value'] = "#{uri}/#{component}"
125
- ident.at_xpath('a:FRBRWork/a:FRBRuri', a: NS)['value'] = uri
126
-
127
- # expression
128
- ident.at_xpath('a:FRBRExpression/a:FRBRthis', a: NS)['value'] = "#{uri}/#{component}/eng@"
129
- ident.at_xpath('a:FRBRExpression/a:FRBRuri', a: NS)['value'] = "#{uri}/eng@"
130
-
131
- # manifestation
132
- ident.at_xpath('a:FRBRManifestation/a:FRBRthis', a: NS)['value'] = "#{uri}/#{component}/eng@"
133
- ident.at_xpath('a:FRBRManifestation/a:FRBRuri', a: NS)['value'] = "#{uri}/eng@"
134
- end
135
-
136
- extract_id_uri
137
- end
138
-
139
- # The date at which this act was first created/promulgated.
140
- #
141
- # @return [String] date, YYYY-MM-DD
142
- def date
143
- node = @meta.at_xpath('./a:identification/a:FRBRWork/a:FRBRdate[@name="Generation"]', a: NS)
144
- node && node['date']
145
- end
146
-
147
- # Set the date at which this act was first created/promulgated. This is usually the same
148
- # as the publication date but this is not enforced.
149
- #
150
- # This also updates the {#year} of this act, which in turn updates the {#id_uri}.
151
- #
152
- # @param date [String] date, YYYY-MM-DD
153
- def date=(value)
154
- for frbr in ['FRBRWork', 'FRBRExpression'] do
155
- @meta.at_xpath("./a:identification/a:#{frbr}/a:FRBRdate[@name=\"Generation\"]", a: NS)['date'] = value
156
- end
157
-
158
- self.year = value.split('-')[0]
159
- end
160
-
161
- # Set the year for this act. You probably want to call {#date=} instead.
162
- #
163
- # This will also update the {#id_uri} but will not change {#date} at all.
164
- #
165
- # @param year [String, Number] year
166
- def year=(year)
167
- @year = year.to_s
168
- rebuild_id_uri
169
- end
170
-
171
- # An applicable short title for this act, either from the `FRBRalias` element
172
- # or based on the act number and year.
173
- # @return [String]
174
- def title
175
- node = @meta.at_xpath('./a:identification/a:FRBRWork/a:FRBRalias', a: NS)
176
- node ? node['value'] : "Act #{num} of #{year}"
177
- end
178
-
179
- # Change the title of this act.
180
- def title=(value)
181
- node = @meta.at_xpath('./a:identification/a:FRBRWork/a:FRBRalias', a: NS)
182
- unless node
183
- node = @doc.create_element('FRBRalias')
184
- @meta.at_xpath('./a:identification/a:FRBRWork/a:FRBRuri', a: NS).after(node)
185
- end
186
-
187
- node['value'] = value
188
- end
189
-
190
- # Has this act been amended? This is determined by testing the `contains`
191
- # attribute of the `act` root element.
192
- #
193
- # @return [Boolean]
194
- def amended?
195
- @doc.at_xpath('/a:akomaNtoso/a:act', a: NS)['contains'] != 'originalVersion'
196
- end
197
-
198
- # Get a list of {Slaw::LifecycleEvent} objects for amendment events, in date order.
199
- # @return [Array<Slaw::LifecycleEvent>] possibly empty list of lifecycle events
200
- def amendment_events
201
- @meta.xpath('./a:lifecycle/a:eventRef[@type="amendment"]', a: NS).map do |event|
202
- LifecycleEvent.new(event)
203
- end.sort_by { |e| e.date }
204
- end
205
-
206
- # Mark this act as being amended by another act, either `act`
207
- # or the details in `opts`.
208
- #
209
- # It is assumed that there can be only one amendment event on a particular
210
- # date. An existing amendment on this date is overwritten.
211
- #
212
- # @option opts [String] :uri uri of the amending act
213
- # @option opts [String] :title title of the amending act
214
- # @option opts [String] :date date of the amendment (YYYY-MM-DD)
215
- def amended_by!(act, opts={})
216
- if act
217
- opts[:uri] ||= act.id_uri
218
- opts[:title] ||= act.short_title
219
- opts[:date] ||= act.publication['date']
220
- end
221
-
222
- date = opts[:date]
223
- source_id = "amendment-#{date}"
224
-
225
- # assume we now hold a single version and not the original version
226
- @doc.at_xpath('/a:akomaNtoso/a:act', a: NS)['contains'] = 'singleVersion'
227
-
228
- # add the lifecycle event
229
- lifecycle = @meta.at_xpath('./a:lifecycle', a: NS)
230
- if not lifecycle
231
- lifecycle = @doc.create_element('lifecycle', source: "#this")
232
- @meta.at_xpath('./a:publication', a: NS).after(lifecycle)
233
- end
234
-
235
- event = lifecycle.at_xpath('./a:eventRef[@date="' + date + '"][@type="amendment"]', a: NS)
236
- if event
237
- # clear up old event
238
- src = @doc.at_css(event['source'])
239
- src.remove if src
240
- else
241
- # new event
242
- event = @doc.create_element('eventRef', type: 'amendment')
243
- lifecycle << event
244
- end
245
-
246
- event['date'] = date
247
- event['id'] = "amendment-event-#{date}"
248
- event['source'] = '#' + source_id
249
-
250
- # add reference
251
- ref = @doc.create_element('passiveRef',
252
- id: source_id,
253
- href: opts[:uri],
254
- showAs: opts[:title])
255
-
256
- @meta.at_xpath('./a:references/a:TLCTerm', a: NS).before(ref)
257
- end
258
-
259
- # Does this Act have parts?
260
- # @return [Boolean]
261
- def parts?
262
- !parts.empty?
263
- end
264
-
265
- # Top-level parts of this act. Parts inside chapters are ignored.
266
- # @return [Array<Nokogiri::XML::Node>] part nodes
267
- def parts
268
- @body.xpath('./a:part', a: NS)
269
- end
270
-
271
- # Does this Act have chapters?
272
- # @return [Boolean]
273
- def chapters?
274
- !chapters.empty?
275
- end
276
-
277
- # Top-level chapters of this act. Chapters inside parts are ignored.
278
- # @return [Array<Nokogiri::XML::Node>] chapter nodes
279
- def chapters
280
- @body.xpath('./a:chapter', a: NS)
281
- end
282
-
283
- # Sections of this act
284
- # @return [Array<Nokogiri::XML::Node>] section nodes
285
- def sections
286
- @body.xpath('.//a:section', a: NS)
287
- end
288
-
289
- # The primary definitions section of this act, identified by
290
- # either an `id` of `definitions` or the first section with a heading
291
- # of `Definitions`.
292
- #
293
- # @return [Nokogiri::XML::Node, nil] definitions node or nil
294
- def definitions
295
- # try looking for the definition list
296
- defn = @body.at_css('#definitions')
297
- return defn.parent if defn
298
-
299
- # try looking for the heading
300
- defn = @body.at_xpath('.//a:section/a:heading[text() = "Definitions"]', a: NS)
301
- return defn.parent if defn
302
-
303
- nil
304
- end
305
-
306
- # An act can contain schedules, additional (generally free-form) documents
307
- # that are addendums to the the main body. A definition element must be
308
- # part of a separate `component` and have a `doc` element with a name attribute
309
- # of `schedules`.
310
- #
311
- # @return [Nokogiri::XML::Node, nil] schedules document node
312
- def schedules
313
- @doc.at_xpath('/a:akomaNtoso/a:components/a:component/a:doc[@name="schedules"]/a:mainBody', a: NS)
314
- end
315
-
316
- # Get a map from term ids to `[term, defn]` pairs,
317
- # where `term+ is the plain text term and `defn` is
318
- # the {Nokogiri::XML::Node} containing the definition.
319
- #
320
- # @return {String => List(String, Nokogiri::XML::Node)} map from strings to `[term, definition]` pairs
321
- def term_definitions
322
- terms = {}
323
-
324
- @meta.xpath('a:references/a:TLCTerm', a: NS).each do |node|
325
- # <TLCTerm id="term-affected_land" href="/ontology/term/this.eng.affected_land" showAs="affected land"/>
326
-
327
- # find the point with id 'def-term-foo'
328
- defn = @body.at_xpath(".//*[@id='def-#{node['id']}']", a: NS)
329
- next unless defn
330
-
331
- terms[node['id']] = [node['showAs'], defn]
332
- end
333
-
334
- terms
335
- end
336
-
337
- # Returns the publication element, if any.
338
- #
339
- # @return [Nokogiri::XML::Node, nil]
340
- def publication
341
- @meta.at_xpath('./a:publication', a: NS)
342
- end
343
-
344
- # Update the publication details of the act. All elements are optional.
345
- #
346
- # @option details [String] :name name of the publication
347
- # @option details [String] :number publication number
348
- # @option details [String] :date date of publication (YYYY-MM-DD)
349
- def published!(details)
350
- node = @meta.at_xpath('./a:publication', a: NS)
351
- unless node
352
- node = @doc.create_element('publication')
353
- @meta.at_xpath('./a:identification', a: NS).after(node)
354
- end
355
-
356
- node['showAs'] = details[:name] if details.has_key? :name
357
- node['name'] = details[:name] if details.has_key? :name
358
- node['date'] = details[:date] if details.has_key? :date
359
- node['number'] = details[:number] if details.has_key? :number
360
- end
361
-
362
- # Has this by-law been repealed?
363
- #
364
- # @return [Boolean]
365
- def repealed?
366
- !!repealed_on
367
- end
368
-
369
- # The date on which this act was repealed, or nil if never repealed
370
- #
371
- # @return [String] date of repeal or nil
372
- def repealed_on
373
- repeal_el = repeal
374
- repeal_el ? Time.parse(repeal_el['date']) : nil
375
- end
376
-
377
- # The element representing the reference that caused the repeal of this
378
- # act, or nil.
379
- #
380
- # @return [Nokogiri::XML::Node] element of reference to repealing act, or nil
381
- def repealed_by
382
- repeal_el = repeal
383
- return nil unless repeal_el
384
-
385
- source_id = repeal_el['source'].sub(/^#/, '')
386
- @meta.at_xpath("./a:references/a:passiveRef[@id='#{source_id}']", a: NS)
387
- end
388
-
389
- # The XML element representing the event of repeal of this act, or nil
390
- #
391
- # @return [Nokogiri::XML::Node]
392
- def repeal
393
- # <lifecycle source="#this">
394
- # <eventRef id="e1" date="2010-07-28" source="#original" type="generation"/>
395
- # <eventRef id="e2" date="2012-04-26" source="#amendment-1" type="amendment"/>
396
- # <eventRef id="e3" date="2014-01-17" source="#repeal" type="repeal"/>
397
- # </lifecycle>
398
- @meta.at_xpath('./a:lifecycle/a:eventRef[@type="repeal"]', a: NS)
399
- end
400
-
401
- # The date at which this particular XML manifestation of this document was generated.
402
- #
403
- # @return [String] date, YYYY-MM-DD
404
- def manifestation_date
405
- node = @meta.at_xpath('./a:identification/a:FRBRManifestation/a:FRBRdate[@name="Generation"]', a: NS)
406
- node && node['date']
407
- end
408
-
409
- # Validate the XML behind this document against the Akoma Ntoso schema and return
410
- # any errors.
411
- #
412
- # @return [Object] array of errors, possibly empty
413
- def validate
414
- @schema ||= Dir.chdir(File.dirname(__FILE__) + "/schemas") { Nokogiri::XML::Schema(File.read('akomantoso20.xsd')) }
415
- @schema.validate(@doc)
416
- end
417
-
418
- # Does this document validate against the schema?
419
- #
420
- # @see {#validate}
421
- def validates?
422
- validate.empty?
423
- end
424
-
425
- def inspect
426
- "<#{self.class.name} @id_uri=\"#{@id_uri}\">"
427
- end
428
-
429
- protected
430
-
431
- # Parse the FRBR Uri into its constituent parts
432
- def extract_id_uri
433
- @id_uri = @meta.at_xpath('./a:identification/a:FRBRWork/a:FRBRuri', a: NS)['value']
434
- empty, @country, @nature, date, @num = @id_uri.split('/')
435
-
436
- # yyyy-mm-dd
437
- @year = date.split('-', 2)[0]
438
- end
439
-
440
- def build_id_uri
441
- # /za/act/2002/3
442
- "/#{@country}/#{@nature}/#{@year}/#{@num}"
443
- end
444
-
445
- # This rebuild's the FRBR uri for this document using its constituent components. It will
446
- # update the XML then re-split the URI and grab its components.
447
- def rebuild_id_uri
448
- self.id_uri = build_id_uri
449
- end
450
- end
451
-
452
- end