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.
- checksums.yaml +4 -4
- data/README.md +13 -147
- data/bin/slaw +2 -1
- data/lib/slaw.rb +0 -6
- data/lib/slaw/generator.rb +2 -8
- data/lib/slaw/grammars/pl/act.treetop +10 -14
- data/lib/slaw/grammars/pl/act_text.xsl +271 -0
- data/lib/slaw/version.rb +1 -1
- data/slaw.gemspec +3 -3
- metadata +6 -17
- data/lib/slaw/act.rb +0 -452
- data/lib/slaw/bylaw.rb +0 -62
- data/lib/slaw/collection.rb +0 -60
- data/lib/slaw/lifecycle_event.rb +0 -23
- data/lib/slaw/render/html.rb +0 -70
- data/lib/slaw/render/xsl/act.xsl +0 -15
- data/lib/slaw/render/xsl/elements.xsl +0 -120
- data/lib/slaw/render/xsl/fragment.xsl +0 -16
- data/spec/act_spec.rb +0 -56
- data/spec/bylaw_spec.rb +0 -49
data/lib/slaw/version.rb
CHANGED
data/slaw.gemspec
CHANGED
@@ -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 =
|
12
|
-
spec.description =
|
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
|
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-
|
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:
|
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
|
data/lib/slaw/act.rb
DELETED
@@ -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
|