slaw 1.0.0.alpha.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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