Class: Slaw::Act
Overview
An Act wraps a single / AkomaNtoso 2.0 XML act document in the form of a Nokogiri::XML::Document object.
The Act object provides quick access to certain sections of the document, such as the metadata and the body, as well as common operations such as identifying whether it has been amended (#amended?), repealed (#repealed?) or what chapters (#chapters), parts (#parts) and sections (#sections) it contains.
Direct Known Subclasses
Constant Summary collapse
- @@acts =
Allow us to jump from the XML document for an act to the Act instance itself
{}
Constants included from Namespace
Instance Attribute Summary collapse
-
#body ⇒ Object
readonly
- Nokogiri::XML::Node
-
The ‘body` XML node.
-
#doc ⇒ Object
- Nokogiri::XML::Document
-
The underlying Nokogiri::XML::Document instance.
-
#filename ⇒ Object
readonly
- String, nil
-
The source filename, or nil.
-
#id_uri ⇒ Object
- String
-
The FRBR URI of this act, which uniquely identifies it globally.
-
#meta ⇒ Object
readonly
- Nokogiri::XML::Node
-
The ‘meta` XML node.
-
#mtime ⇒ Object
readonly
- Time, nil
-
The mtime of when the source file was last modified.
-
#nature ⇒ Object
readonly
- String
-
The underlying nature of this act, usually ‘act` although subclasses my override this.
-
#num ⇒ Object
readonly
- String
-
The act number in the year this act was published.
-
#schema ⇒ Object
- Nokogiri::XML::Schema
-
schema to validate against.
-
#year ⇒ Object
- String
-
The year this act was published.
Class Method Summary collapse
-
.for_node(node) ⇒ Act
Get the act that wraps the document that owns this XML node.
Instance Method Summary collapse
-
#amended? ⇒ Boolean
Has this act been amended? This is determined by testing the ‘contains` attribute of the `act` root element.
-
#amended_by!(act, opts = {}) ⇒ Object
Mark this act as being amended by another act, either ‘act` or the details in `opts`.
-
#amendment_events ⇒ Array<Slaw::LifecycleEvent>
Get a list of LifecycleEvent objects for amendment events, in date order.
-
#chapters ⇒ Array<Nokogiri::XML::Node>
Top-level chapters of this act.
-
#chapters? ⇒ Boolean
Does this Act have chapters?.
-
#date ⇒ String
The date at which this act was first created/promulgated.
-
#date=(value) ⇒ Object
Set the date at which this act was first created/promulgated.
-
#definitions ⇒ Nokogiri::XML::Node?
The primary definitions section of this act, identified by either an ‘id` of `definitions` or the first section with a heading of `Definitions`.
-
#initialize(filename = nil) ⇒ Act
constructor
Create a new instance, loading from ‘filename` if given.
- #inspect ⇒ Object
-
#load(filename) ⇒ Object
Load the XML in ‘filename` into this instance.
-
#manifestation_date ⇒ String
The date at which this particular XML manifestation of this document was generated.
-
#parts ⇒ Array<Nokogiri::XML::Node>
Top-level parts of this act.
-
#parts? ⇒ Boolean
Does this Act have parts?.
-
#publication ⇒ Nokogiri::XML::Node?
Returns the publication element, if any.
-
#published!(details) ⇒ Object
Update the publication details of the act.
-
#repeal ⇒ Nokogiri::XML::Node
The XML element representing the event of repeal of this act, or nil.
-
#repealed? ⇒ Boolean
Has this by-law been repealed?.
-
#repealed_by ⇒ Nokogiri::XML::Node
The element representing the reference that caused the repeal of this act, or nil.
-
#repealed_on ⇒ String
The date on which this act was repealed, or nil if never repealed.
-
#schedules ⇒ Nokogiri::XML::Node?
An act can contain schedules, additional (generally free-form) documents that are addendums to the the main body.
-
#sections ⇒ Array<Nokogiri::XML::Node>
Sections of this act.
-
#term_definitions ⇒ String => List(String, Nokogiri::XML::Node)
Get a map from term ids to ‘[term, defn]` pairs, where `term+ is the plain text term and `defn` is the Nokogiri::XML::Node containing the definition.
-
#title ⇒ String
An applicable short title for this act, either from the ‘FRBRalias` element or based on the act number and year.
-
#title=(value) ⇒ Object
Change the title of this act.
-
#validate ⇒ Object
Validate the XML behind this document against the Akoma Ntoso schema and return any errors.
-
#validates? ⇒ Boolean
Does this document validate against the schema?.
Methods inherited from AknBase
Constructor Details
#initialize(filename = nil) ⇒ Act
Create a new instance, loading from ‘filename` if given.
82 83 84 85 |
# File 'lib/slaw/act.rb', line 82 def initialize(filename=nil) self.load(filename) if filename @schema = nil end |
Instance Attribute Details
#body ⇒ Object (readonly)
- Nokogiri::XML::Node
-
The ‘body` XML node
50 51 52 |
# File 'lib/slaw/act.rb', line 50 def body @body end |
#doc ⇒ Object
- Nokogiri::XML::Document
-
The underlying Nokogiri::XML::Document instance
44 45 46 |
# File 'lib/slaw/act.rb', line 44 def doc @doc end |
#filename ⇒ Object (readonly)
- String, nil
-
The source filename, or nil
62 63 64 |
# File 'lib/slaw/act.rb', line 62 def filename @filename end |
#id_uri ⇒ Object
- String
-
The FRBR URI of this act, which uniquely identifies it globally
59 60 61 |
# File 'lib/slaw/act.rb', line 59 def id_uri @id_uri end |
#meta ⇒ Object (readonly)
- Nokogiri::XML::Node
-
The ‘meta` XML node
47 48 49 |
# File 'lib/slaw/act.rb', line 47 def @meta end |
#mtime ⇒ Object (readonly)
- Time, nil
-
The mtime of when the source file was last modified
65 66 67 |
# File 'lib/slaw/act.rb', line 65 def mtime @mtime end |
#nature ⇒ Object (readonly)
- String
-
The underlying nature of this act, usually ‘act` although subclasses my override this.
68 69 70 |
# File 'lib/slaw/act.rb', line 68 def nature @nature end |
#num ⇒ Object (readonly)
- String
-
The act number in the year this act was published
56 57 58 |
# File 'lib/slaw/act.rb', line 56 def num @num end |
#schema ⇒ Object
- Nokogiri::XML::Schema
-
schema to validate against
71 72 73 |
# File 'lib/slaw/act.rb', line 71 def schema @schema end |
#year ⇒ Object
- String
-
The year this act was published
53 54 55 |
# File 'lib/slaw/act.rb', line 53 def year @year end |
Class Method Details
.for_node(node) ⇒ Act
Get the act that wraps the document that owns this XML node
76 77 78 |
# File 'lib/slaw/act.rb', line 76 def self.for_node(node) @@acts[node.document] end |
Instance Method Details
#amended? ⇒ Boolean
Has this act been amended? This is determined by testing the ‘contains` attribute of the `act` root element.
194 195 196 |
# File 'lib/slaw/act.rb', line 194 def amended? @doc.at_xpath('/a:akomaNtoso/a:act', a: NS)['contains'] != 'originalVersion' end |
#amended_by!(act, opts = {}) ⇒ Object
Mark this act as being amended by another act, either ‘act` or the details in `opts`.
It is assumed that there can be only one amendment event on a particular date. An existing amendment on this date is overwritten.
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/slaw/act.rb', line 215 def amended_by!(act, opts={}) if act opts[:uri] ||= act.id_uri opts[:title] ||= act.short_title opts[:date] ||= act.publication['date'] end date = opts[:date] source_id = "amendment-#{date}" # assume we now hold a single version and not the original version @doc.at_xpath('/a:akomaNtoso/a:act', a: NS)['contains'] = 'singleVersion' # add the lifecycle event lifecycle = @meta.at_xpath('./a:lifecycle', a: NS) if not lifecycle lifecycle = @doc.create_element('lifecycle', source: "#this") @meta.at_xpath('./a:publication', a: NS).after(lifecycle) end event = lifecycle.at_xpath('./a:eventRef[@date="' + date + '"][@type="amendment"]', a: NS) if event # clear up old event src = @doc.at_css(event['source']) src.remove if src else # new event event = @doc.create_element('eventRef', type: 'amendment') lifecycle << event end event['date'] = date event['id'] = "amendment-event-#{date}" event['source'] = '#' + source_id # add reference ref = @doc.create_element('passiveRef', id: source_id, href: opts[:uri], showAs: opts[:title]) @meta.at_xpath('./a:references/a:TLCTerm', a: NS).before(ref) end |
#amendment_events ⇒ Array<Slaw::LifecycleEvent>
Get a list of LifecycleEvent objects for amendment events, in date order.
200 201 202 203 204 |
# File 'lib/slaw/act.rb', line 200 def amendment_events @meta.xpath('./a:lifecycle/a:eventRef[@type="amendment"]', a: NS).map do |event| LifecycleEvent.new(event) end.sort_by { |e| e.date } end |
#chapters ⇒ Array<Nokogiri::XML::Node>
Top-level chapters of this act. Chapters inside parts are ignored.
279 280 281 |
# File 'lib/slaw/act.rb', line 279 def chapters @body.xpath('./a:chapter', a: NS) end |
#chapters? ⇒ Boolean
Does this Act have chapters?
273 274 275 |
# File 'lib/slaw/act.rb', line 273 def chapters? !chapters.empty? end |
#date ⇒ String
The date at which this act was first created/promulgated.
142 143 144 145 |
# File 'lib/slaw/act.rb', line 142 def date node = @meta.at_xpath('./a:identification/a:FRBRWork/a:FRBRdate[@name="Generation"]', a: NS) node && node['date'] end |
#date=(value) ⇒ Object
153 154 155 156 157 158 159 |
# File 'lib/slaw/act.rb', line 153 def date=(value) for frbr in ['FRBRWork', 'FRBRExpression'] do @meta.at_xpath("./a:identification/a:#{frbr}/a:FRBRdate[@name=\"Generation\"]", a: NS)['date'] = value end self.year = value.split('-')[0] end |
#definitions ⇒ Nokogiri::XML::Node?
The primary definitions section of this act, identified by either an ‘id` of `definitions` or the first section with a heading of `Definitions`.
294 295 296 297 298 299 300 301 302 303 304 |
# File 'lib/slaw/act.rb', line 294 def definitions # try looking for the definition list defn = @body.at_css('#definitions') return defn.parent if defn # try looking for the heading defn = @body.at_xpath('.//a:section/a:heading[text() = "Definitions"]', a: NS) return defn.parent if defn nil end |
#inspect ⇒ Object
425 426 427 |
# File 'lib/slaw/act.rb', line 425 def inspect "<#{self.class.name} @id_uri=\"#{@id_uri}\">" end |
#load(filename) ⇒ Object
Load the XML in ‘filename` into this instance
89 90 91 92 93 94 |
# File 'lib/slaw/act.rb', line 89 def load(filename) @filename = filename @mtime = File::mtime(@filename) File.open(filename) { |f| parse(f) } end |
#manifestation_date ⇒ String
The date at which this particular XML manifestation of this document was generated.
404 405 406 407 |
# File 'lib/slaw/act.rb', line 404 def manifestation_date node = @meta.at_xpath('./a:identification/a:FRBRManifestation/a:FRBRdate[@name="Generation"]', a: NS) node && node['date'] end |
#parts ⇒ Array<Nokogiri::XML::Node>
Top-level parts of this act. Parts inside chapters are ignored.
267 268 269 |
# File 'lib/slaw/act.rb', line 267 def parts @body.xpath('./a:part', a: NS) end |
#parts? ⇒ Boolean
Does this Act have parts?
261 262 263 |
# File 'lib/slaw/act.rb', line 261 def parts? !parts.empty? end |
#publication ⇒ Nokogiri::XML::Node?
Returns the publication element, if any.
340 341 342 |
# File 'lib/slaw/act.rb', line 340 def publication @meta.at_xpath('./a:publication', a: NS) end |
#published!(details) ⇒ Object
Update the publication details of the act. All elements are optional.
349 350 351 352 353 354 355 356 357 358 359 360 |
# File 'lib/slaw/act.rb', line 349 def published!(details) node = @meta.at_xpath('./a:publication', a: NS) unless node node = @doc.create_element('publication') @meta.at_xpath('./a:identification', a: NS).after(node) end node['showAs'] = details[:name] if details.has_key? :name node['name'] = details[:name] if details.has_key? :name node['date'] = details[:date] if details.has_key? :date node['number'] = details[:number] if details.has_key? :number end |
#repeal ⇒ Nokogiri::XML::Node
The XML element representing the event of repeal of this act, or nil
392 393 394 395 396 397 398 399 |
# File 'lib/slaw/act.rb', line 392 def repeal # <lifecycle source="#this"> # <eventRef id="e1" date="2010-07-28" source="#original" type="generation"/> # <eventRef id="e2" date="2012-04-26" source="#amendment-1" type="amendment"/> # <eventRef id="e3" date="2014-01-17" source="#repeal" type="repeal"/> # </lifecycle> @meta.at_xpath('./a:lifecycle/a:eventRef[@type="repeal"]', a: NS) end |
#repealed? ⇒ Boolean
Has this by-law been repealed?
365 366 367 |
# File 'lib/slaw/act.rb', line 365 def repealed? !!repealed_on end |
#repealed_by ⇒ Nokogiri::XML::Node
The element representing the reference that caused the repeal of this act, or nil.
381 382 383 384 385 386 387 |
# File 'lib/slaw/act.rb', line 381 def repealed_by repeal_el = repeal return nil unless repeal_el source_id = repeal_el['source'].sub(/^#/, '') @meta.at_xpath("./a:references/a:passiveRef[@id='#{source_id}']", a: NS) end |
#repealed_on ⇒ String
The date on which this act was repealed, or nil if never repealed
372 373 374 375 |
# File 'lib/slaw/act.rb', line 372 def repealed_on repeal_el = repeal repeal_el ? Time.parse(repeal_el['date']) : nil end |
#schedules ⇒ Nokogiri::XML::Node?
An act can contain schedules, additional (generally free-form) documents that are addendums to the the main body. A definition element must be part of a separate ‘component` and have a `doc` element with a name attribute of `schedules`.
312 313 314 |
# File 'lib/slaw/act.rb', line 312 def schedules @doc.at_xpath('/a:akomaNtoso/a:components/a:component/a:doc[@name="schedules"]/a:mainBody', a: NS) end |
#sections ⇒ Array<Nokogiri::XML::Node>
Sections of this act
285 286 287 |
# File 'lib/slaw/act.rb', line 285 def sections @body.xpath('.//a:section', a: NS) end |
#term_definitions ⇒ String => List(String, Nokogiri::XML::Node)
Get a map from term ids to ‘[term, defn]` pairs, where `term+ is the plain text term and `defn` is the Nokogiri::XML::Node containing the definition.
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/slaw/act.rb', line 321 def term_definitions terms = {} @meta.xpath('a:references/a:TLCTerm', a: NS).each do |node| # <TLCTerm id="term-affected_land" href="/ontology/term/this.eng.affected_land" showAs="affected land"/> # find the point with id 'def-term-foo' defn = @body.at_xpath(".//*[@id='def-#{node['id']}']", a: NS) next unless defn terms[node['id']] = [node['showAs'], defn] end terms end |
#title ⇒ String
An applicable short title for this act, either from the ‘FRBRalias` element or based on the act number and year.
174 175 176 177 |
# File 'lib/slaw/act.rb', line 174 def title node = @meta.at_xpath('./a:identification/a:FRBRWork/a:FRBRalias', a: NS) node ? node['value'] : "Act #{num} of #{year}" end |
#title=(value) ⇒ Object
Change the title of this act.
180 181 182 183 184 185 186 187 188 |
# File 'lib/slaw/act.rb', line 180 def title=(value) node = @meta.at_xpath('./a:identification/a:FRBRWork/a:FRBRalias', a: NS) unless node node = @doc.create_element('FRBRalias') @meta.at_xpath('./a:identification/a:FRBRWork/a:FRBRuri', a: NS).after(node) end node['value'] = value end |
#validate ⇒ Object
Validate the XML behind this document against the Akoma Ntoso schema and return any errors.
413 414 415 416 |
# File 'lib/slaw/act.rb', line 413 def validate @schema ||= Dir.chdir(File.dirname(__FILE__) + "/schemas") { Nokogiri::XML::Schema(File.read('akomantoso20.xsd')) } @schema.validate(@doc) end |
#validates? ⇒ Boolean
Does this document validate against the schema?
421 422 423 |
# File 'lib/slaw/act.rb', line 421 def validates? validate.empty? end |