pbcore 0.2.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.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +6 -0
  6. data/LICENSE.txt +674 -0
  7. data/README.md +37 -0
  8. data/Rakefile +6 -0
  9. data/bin/console +11 -0
  10. data/bin/setup +8 -0
  11. data/lib/pbcore.rb +28 -0
  12. data/lib/pbcore/annotation.rb +12 -0
  13. data/lib/pbcore/asset_date.rb +12 -0
  14. data/lib/pbcore/asset_type.rb +11 -0
  15. data/lib/pbcore/attributes.rb +6 -0
  16. data/lib/pbcore/attributes/common.rb +13 -0
  17. data/lib/pbcore/attributes/time_interval.rb +12 -0
  18. data/lib/pbcore/audience_level.rb +11 -0
  19. data/lib/pbcore/audience_rating.rb +11 -0
  20. data/lib/pbcore/contributor.rb +19 -0
  21. data/lib/pbcore/contributor/contributor.rb +12 -0
  22. data/lib/pbcore/contributor/role.rb +12 -0
  23. data/lib/pbcore/coverage.rb +20 -0
  24. data/lib/pbcore/coverage/coverage.rb +11 -0
  25. data/lib/pbcore/coverage/type.rb +11 -0
  26. data/lib/pbcore/creator.rb +19 -0
  27. data/lib/pbcore/creator/creator.rb +16 -0
  28. data/lib/pbcore/creator/role.rb +11 -0
  29. data/lib/pbcore/description.rb +13 -0
  30. data/lib/pbcore/description_document.rb +60 -0
  31. data/lib/pbcore/element.rb +199 -0
  32. data/lib/pbcore/errors.rb +55 -0
  33. data/lib/pbcore/extension.rb +19 -0
  34. data/lib/pbcore/extension/embedded.rb +15 -0
  35. data/lib/pbcore/extension/wrap.rb +26 -0
  36. data/lib/pbcore/extension/wrap/authority_used.rb +11 -0
  37. data/lib/pbcore/extension/wrap/element.rb +11 -0
  38. data/lib/pbcore/extension/wrap/value.rb +11 -0
  39. data/lib/pbcore/factories.rb +6 -0
  40. data/lib/pbcore/genre.rb +11 -0
  41. data/lib/pbcore/identifier.rb +11 -0
  42. data/lib/pbcore/instantiation.rb +87 -0
  43. data/lib/pbcore/instantiation/alternative_modes.rb +11 -0
  44. data/lib/pbcore/instantiation/annotation.rb +12 -0
  45. data/lib/pbcore/instantiation/channel_configuration.rb +11 -0
  46. data/lib/pbcore/instantiation/colors.rb +11 -0
  47. data/lib/pbcore/instantiation/data_rate.rb +13 -0
  48. data/lib/pbcore/instantiation/date.rb +13 -0
  49. data/lib/pbcore/instantiation/digital.rb +11 -0
  50. data/lib/pbcore/instantiation/dimensions.rb +13 -0
  51. data/lib/pbcore/instantiation/duration.rb +11 -0
  52. data/lib/pbcore/instantiation/essence_track.rb +59 -0
  53. data/lib/pbcore/instantiation/essence_track/annotation.rb +11 -0
  54. data/lib/pbcore/instantiation/essence_track/aspect_ratio.rb +11 -0
  55. data/lib/pbcore/instantiation/essence_track/bit_depth.rb +11 -0
  56. data/lib/pbcore/instantiation/essence_track/data_rate.rb +11 -0
  57. data/lib/pbcore/instantiation/essence_track/duration.rb +9 -0
  58. data/lib/pbcore/instantiation/essence_track/encoding.rb +9 -0
  59. data/lib/pbcore/instantiation/essence_track/frame_rate.rb +11 -0
  60. data/lib/pbcore/instantiation/essence_track/frame_size.rb +11 -0
  61. data/lib/pbcore/instantiation/essence_track/identifier.rb +9 -0
  62. data/lib/pbcore/instantiation/essence_track/language.rb +9 -0
  63. data/lib/pbcore/instantiation/essence_track/playback_speed.rb +11 -0
  64. data/lib/pbcore/instantiation/essence_track/sampling_rate.rb +11 -0
  65. data/lib/pbcore/instantiation/essence_track/standard.rb +9 -0
  66. data/lib/pbcore/instantiation/essence_track/time_start.rb +9 -0
  67. data/lib/pbcore/instantiation/essence_track/type.rb +9 -0
  68. data/lib/pbcore/instantiation/extension.rb +22 -0
  69. data/lib/pbcore/instantiation/file_size.rb +13 -0
  70. data/lib/pbcore/instantiation/generations.rb +11 -0
  71. data/lib/pbcore/instantiation/identifier.rb +11 -0
  72. data/lib/pbcore/instantiation/language.rb +11 -0
  73. data/lib/pbcore/instantiation/location.rb +11 -0
  74. data/lib/pbcore/instantiation/media_type.rb +11 -0
  75. data/lib/pbcore/instantiation/physical.rb +11 -0
  76. data/lib/pbcore/instantiation/relation.rb +19 -0
  77. data/lib/pbcore/instantiation/relation/identifier.rb +9 -0
  78. data/lib/pbcore/instantiation/relation/type.rb +9 -0
  79. data/lib/pbcore/instantiation/rights.rb +22 -0
  80. data/lib/pbcore/instantiation/rights/link.rb +11 -0
  81. data/lib/pbcore/instantiation/rights/summary.rb +11 -0
  82. data/lib/pbcore/instantiation/standard.rb +13 -0
  83. data/lib/pbcore/instantiation/time_start.rb +11 -0
  84. data/lib/pbcore/instantiation/tracks.rb +11 -0
  85. data/lib/pbcore/instantiation_document.rb +97 -0
  86. data/lib/pbcore/publisher.rb +19 -0
  87. data/lib/pbcore/publisher/publisher.rb +17 -0
  88. data/lib/pbcore/publisher/role.rb +11 -0
  89. data/lib/pbcore/relation.rb +19 -0
  90. data/lib/pbcore/relation/identifier.rb +11 -0
  91. data/lib/pbcore/relation/type.rb +11 -0
  92. data/lib/pbcore/rights_summary.rb +22 -0
  93. data/lib/pbcore/rights_summary/link.rb +11 -0
  94. data/lib/pbcore/rights_summary/summary.rb +11 -0
  95. data/lib/pbcore/subject.rb +15 -0
  96. data/lib/pbcore/title.rb +15 -0
  97. data/lib/pbcore/version.rb +3 -0
  98. data/lib/pbcore/vocab.rb +15 -0
  99. data/pbcore.gemspec +40 -0
  100. metadata +297 -0
@@ -0,0 +1,37 @@
1
+ # PBCore
2
+
3
+ PBCore model for Ruby applications.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'pbcore'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install pbcore
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Development
26
+
27
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
28
+
29
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
30
+
31
+ ## Contributing
32
+
33
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/pbcore.
34
+
35
+ ## License
36
+
37
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "pbcore"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require "pry"
11
+ Pry.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,28 @@
1
+ require "pbcore/version"
2
+ require "pbcore/errors"
3
+
4
+ module PBCore
5
+ autoload :Element, 'pbcore/element'
6
+ autoload :Attributes, 'pbcore/attributes'
7
+ autoload :DescriptionDocument, 'pbcore/description_document'
8
+ autoload :AssetType, 'pbcore/asset_type'
9
+ autoload :AssetDate, 'pbcore/asset_date'
10
+ autoload :Identifier, 'pbcore/identifier'
11
+ autoload :Title, 'pbcore/title'
12
+ autoload :Subject, 'pbcore/subject'
13
+ autoload :Description, 'pbcore/description'
14
+ autoload :Genre, 'pbcore/genre'
15
+ autoload :Creator, 'pbcore/creator'
16
+ autoload :Coverage, 'pbcore/coverage'
17
+ autoload :Relation, 'pbcore/relation'
18
+ autoload :AudienceLevel, 'pbcore/audience_level'
19
+ autoload :AudienceRating, 'pbcore/audience_rating'
20
+ autoload :Contributor, 'pbcore/contributor'
21
+ autoload :Publisher, 'pbcore/publisher'
22
+ autoload :RightsSummary, 'pbcore/rights_summary'
23
+ autoload :Annotation, 'pbcore/annotation'
24
+ autoload :Extension, 'pbcore/extension'
25
+ autoload :Instantiation, 'pbcore/instantiation'
26
+ autoload :InstantiationDocument, 'pbcore/instantiation_document'
27
+ autoload :Vocab, 'pbcore/vocab'
28
+ end
@@ -0,0 +1,12 @@
1
+ require 'pbcore/element'
2
+
3
+ module PBCore
4
+ class Annotation < Element
5
+ element :pbcoreAnnotation, as: :value
6
+ attribute :annotationType, as: :type
7
+
8
+ build_xml do |xml|
9
+ xml.pbcoreAnnotation(value, xml_attributes.compact)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'pbcore/element'
2
+
3
+ module PBCore
4
+ class AssetDate < Element
5
+ element :pbcoreAssetDate, as: :value
6
+ attribute :dateType, as: :type
7
+
8
+ build_xml do |xml|
9
+ xml.pbcoreAssetDate(value, xml_attributes.compact)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ require 'pbcore/element'
2
+
3
+ module PBCore
4
+ class AssetType < Element
5
+ element :pbcoreAssetType, as: :value
6
+
7
+ build_xml do |xml|
8
+ xml.pbcoreAssetType(value, xml_attributes.compact)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ module PBCore
2
+ module Attributes
3
+ autoload :Common, 'pbcore/attributes/common'
4
+ autoload :TimeInterval, 'pbcore/attributes/time_interval'
5
+ end
6
+ end
@@ -0,0 +1,13 @@
1
+ module PBCore
2
+ module Attributes
3
+ module Common
4
+ def self.included(base)
5
+ PBCore.fail_if_base_is_not_pbcore_element(included_module: self, base: base)
6
+ base.attribute :source
7
+ base.attribute :ref
8
+ base.attribute :annotation
9
+ base.attribute :version
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module PBCore
2
+ module Attributes
3
+ module TimeInterval
4
+ def self.included(base)
5
+ PBCore.fail_if_base_is_not_pbcore_element(included_module: self, base: base)
6
+ base.attribute :startTime, as: :start_time
7
+ base.attribute :endTime, as: :end_time
8
+ base.attribute :timeAnnotation, as: :time_annotation
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ require 'pbcore/element'
2
+
3
+ module PBCore
4
+ class AudienceLevel < Element
5
+ element :pbcoreAudienceLevel, as: :value
6
+
7
+ build_xml do |xml|
8
+ xml.pbcoreAudienceLevel(value, xml_attributes.compact)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'pbcore/element'
2
+
3
+ module PBCore
4
+ class AudienceRating < Element
5
+ element :pbcoreAudienceRating, as: :value
6
+
7
+ build_xml do |xml|
8
+ xml.pbcoreAudienceRating(value, xml_attributes.compact)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ require 'pbcore/element'
2
+
3
+ module PBCore
4
+ class Contributor < Element
5
+ autoload :Contributor, 'pbcore/contributor/contributor'
6
+ autoload :Role, 'pbcore/contributor/role'
7
+
8
+ element :pbcoreContributor, as: :value
9
+ element :contributor, as: :contributor, class: PBCore::Contributor::Contributor
10
+ element :contributorRole, as: :role, class: PBCore::Contributor::Role
11
+
12
+ build_xml do |xml|
13
+ xml.pbcoreContributor(xml_attributes.compact) do |xml|
14
+ contributor.build(xml) if contributor
15
+ role.build(xml) if role
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,12 @@
1
+ require 'pbcore/element'
2
+
3
+ module PBCore
4
+ class Contributor::Contributor < Element
5
+ element :contributor, as: :value
6
+ attribute :affiliation, as: :affiliation
7
+
8
+ build_xml do |xml|
9
+ xml.contributor(value, xml_attributes.compact)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'pbcore/element'
2
+
3
+ module PBCore
4
+ class Contributor::Role < Element
5
+ element :contributorRole, as: :value
6
+ attribute :portrayal, as: :portrayal
7
+
8
+ build_xml do |xml|
9
+ xml.contributorRole(value, xml_attributes.compact)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ require 'pbcore/element'
2
+
3
+ module PBCore
4
+ class Coverage < Element
5
+
6
+ autoload :Coverage, 'pbcore/coverage/coverage'
7
+ autoload :Type, 'pbcore/coverage/type'
8
+
9
+ element :pbcoreCoverage, as: :value
10
+ element :coverage, as: :coverage, class: PBCore::Coverage::Coverage
11
+ element :coverageType, as: :type, class: PBCore::Coverage::Type
12
+
13
+ build_xml do |xml|
14
+ xml.pbcoreCoverage(xml_attributes.compact) do |xml|
15
+ coverage.build(xml) if coverage
16
+ type.build(xml) if type
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ require 'pbcore/element'
2
+
3
+ module PBCore
4
+ class Coverage::Coverage < Element
5
+ element :coverage, as: :value
6
+
7
+ build_xml do |xml|
8
+ xml.coverage(value, xml_attributes.compact)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'pbcore/element'
2
+
3
+ module PBCore
4
+ class Coverage::Type < Element
5
+ element :coverageType, as: :value
6
+
7
+ build_xml do |xml|
8
+ xml.coverageType(value, xml_attributes.compact)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ require 'pbcore/element'
2
+
3
+ module PBCore
4
+ class Creator < Element
5
+ autoload :Creator, 'pbcore/creator/creator'
6
+ autoload :Role, 'pbcore/creator/role'
7
+
8
+ element :pbcoreCreator, as: :value
9
+ element :creator, as: :creator, class: PBCore::Creator::Creator
10
+ element :creatorRole, as: :role, class: PBCore::Creator::Role
11
+
12
+ build_xml do |xml|
13
+ xml.pbcoreCreator(xml_attributes.compact) do |xml|
14
+ creator.build(xml) if creator
15
+ role.build(xml) if role
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ require 'pbcore/element'
2
+
3
+ module PBCore
4
+ class Creator::Creator < Element
5
+ element :creator, as: :value
6
+ attribute :affiliation, as: :affiliation
7
+ attribute :affiliationSource, as: :affiliation_source
8
+ attribute :affiliationRef, as: :affiliation_ref
9
+ attribute :affiliationVersion, as: :affiliation_version
10
+ attribute :affiliationAnnotation, as: :affiliation_annotation
11
+
12
+ build_xml do |xml|
13
+ xml.creator(value, xml_attributes.compact)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ require 'pbcore/element'
2
+
3
+ module PBCore
4
+ class Creator::Role < Element
5
+ element :creatorRole, as: :value
6
+
7
+ build_xml do |xml|
8
+ xml.creatorRole(value, xml_attributes.compact)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ require 'pbcore/element'
2
+
3
+ module PBCore
4
+ class Description < Element
5
+ element :pbcoreDescription, as: :value
6
+ attribute :descriptionType, as: :type
7
+ attribute :descriptionTypeRef, as: :type_ref
8
+
9
+ build_xml do |xml|
10
+ xml.pbcoreDescription(value, xml_attributes.compact)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,60 @@
1
+ require 'pbcore/element'
2
+
3
+ module PBCore
4
+ class DescriptionDocument < Element
5
+ elements :pbcoreIdentifier, as: :identifiers, class: PBCore::Identifier
6
+ elements :pbcoreTitle, as: :titles, class: PBCore::Title
7
+ elements :pbcoreDescription, as: :descriptions, class: PBCore::Description
8
+ elements :pbcoreAssetDate, as: :asset_dates, class: PBCore::AssetDate
9
+ elements :pbcoreAssetType, as: :asset_types, class: PBCore::AssetType
10
+ elements :pbcoreSubject, as: :subjects, class: PBCore::Subject
11
+ elements :pbcoreGenre, as: :genres, class: PBCore::Genre
12
+ elements :pbcoreRelation, as: :relations, class: PBCore::Relation
13
+ elements :pbcoreCoverage, as: :coverages, class: PBCore::Coverage
14
+ elements :pbcoreAudienceLevel, as: :audience_levels, class: PBCore::AudienceLevel
15
+ elements :pbcoreAudienceRating, as: :audience_ratings, class: PBCore::AudienceRating
16
+ elements :pbcoreCreator, as: :creators, class: PBCore::Creator
17
+ elements :pbcoreContributor, as: :contributors, class: PBCore::Contributor
18
+ elements :pbcorePublisher, as: :publishers, class: PBCore::Publisher
19
+ elements :pbcoreRightsSummary, as: :rights_summaries, class: PBCore::RightsSummary
20
+ elements :pbcoreAnnotation, as: :annotations, class: PBCore::Annotation
21
+ elements :pbcoreExtension, as: :extensions, class: PBCore::Extension
22
+ elements :pbcoreInstantiation, as: :instantiations, class: PBCore::Instantiation
23
+
24
+ build_xml do |xml|
25
+ xml.pbcoreDescriptionDocument(namespace_attributes) do |xml|
26
+ asset_types.each { |asset_type| asset_type.build(xml) }
27
+ asset_dates.each { |asset_date| asset_date.build(xml) }
28
+ identifiers.each { |identifier| identifier.build(xml) }
29
+ titles.each { |title| title.build(xml) }
30
+ subjects.each { |subject| subject.build(xml) }
31
+ descriptions.each { |description| description.build(xml) }
32
+ genres.each { |genre| genre.build(xml) }
33
+ relations.each { |relation| relation.build(xml) }
34
+ coverages.each { |coverage| coverage.build(xml) }
35
+ audience_levels.each { |audience_level| audience_level.build(xml) }
36
+ audience_ratings.each { |audience_rating| audience_rating.build(xml) }
37
+ creators.each { |creator| creator.build(xml) }
38
+ contributors.each { |contributor| contributor.build(xml) }
39
+ publishers.each { |publisher| publisher.build(xml) }
40
+ rights_summaries.each { |rights_summary| rights_summary.build(xml) }
41
+ instantiations.each { |instantiation| instantiation.build(xml) }
42
+ annotations.each { |annotation| annotation.build(xml) }
43
+ # TODO: no pbcore part?!
44
+ extensions.each { |extension| extension.build(xml) }
45
+ end
46
+ end
47
+
48
+ # NOTE: For some reason, these attributes will not parse with SAXMachine
49
+ # attributes.
50
+ # TODO: Is there a better way to set namespace attributes?
51
+ def namespace_attributes
52
+ {
53
+ # changed to www.pbcore because aapb doesnt like it without
54
+ 'xmlns' => "http://www.pbcore.org/PBCore/PBCoreNamespace.html",
55
+ 'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
56
+ 'xsi:schemaLocation' => "http://www.pbcore.org/PBCore/PBCoreNamespace.html http://www.pbcore.org/xsd/pbcore-2.1.xsd"
57
+ }
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,199 @@
1
+ require 'sax-machine'
2
+
3
+ module SAXMachine::SAXAbstractHandler
4
+ NO_BUFFER = ''
5
+ end
6
+
7
+ module PBCore
8
+ # TODO: decouple XML building behavior from schema-related declarations.
9
+ class Element
10
+ include SAXMachine
11
+ include PBCore::Attributes::Common
12
+
13
+ # Defines which accessor is used to get the value within an element.
14
+ # Here we defined it be simply :value.
15
+ value(:value, {}) do |val|
16
+ val == :no_buffer ? nil : val
17
+ end
18
+
19
+ # Defind attributes common to all PBCore elements.
20
+ # attribute :source
21
+ # attribute :ref
22
+ # attribute :annotation
23
+ # attribute :version
24
+
25
+ # Returns a hash of child element instances.
26
+ def elements(key_by_xml_name: false)
27
+ key_value_pairs_array = self.class.element_config_all.map do |name, element_configs|
28
+ # SAXMachine allows you to declare multiple elements with the same name
29
+ # but we don't do tha with PBCore, so just grab the first and only one.
30
+ element_config = element_configs.first
31
+ # get the accessor name by which to get the value
32
+ accessor = element_config.instance_variable_get(:@as)
33
+
34
+ # fetch the value by calling the accessor
35
+ value = send(accessor)
36
+ # create the key, value pair that will go into the Hash[] construct.
37
+ key = key_by_xml_name ? name : accessor
38
+ [ key, value ]
39
+ end
40
+
41
+ Hash[ key_value_pairs_array ]
42
+ end
43
+
44
+ # Shortcut for getting xml attributes.
45
+ def xml_attributes; attributes(key_by_xml_name: true); end
46
+
47
+ # Returns a hash of attrubutes as the should appear in the XML.
48
+ def attributes(key_by_xml_name: false)
49
+ xml_attrs = Hash[
50
+ self.class.sax_config.top_level_attributes.map do |attr|
51
+ accessor = attr.instance_variable_get(:@as)
52
+ key = key_by_xml_name ? attr.name : accessor
53
+ [ key, send(accessor) ]
54
+ end
55
+ ]
56
+ xml_attrs
57
+ end
58
+
59
+ # Executes the block defined with the class method `build_xml`. Uses a
60
+ # Nokogiri::XML::Builder instance (either passed in or instantiated) to
61
+ # build the XML, and then returns the builder instance.
62
+ def build(builder=nil)
63
+ raise ArgumentError, "#{self.class}#build expects a Nokogiri::XML::Builder class, but #{builder.class} was given" unless builder.nil? || builder.is_a?(Nokogiri::XML::Builder)
64
+ PBCore.fail_if_missing_build_xml_block(element_class: self.class)
65
+ builder ||= Nokogiri::XML::Builder.new
66
+ instance_exec builder, &self.class.build_block
67
+ builder
68
+ end
69
+
70
+ # Builds the xml using #build with a new instance of Nokogiri::XML::Builder
71
+ # and immediately calls to_xml on it.
72
+ def to_xml
73
+ build.to_xml
74
+ end
75
+
76
+ # Define the class interfaces for all PBCore elements.
77
+ class << self
78
+ attr_reader :build_block
79
+
80
+ # Class method to allow extended classes to declaratively the logic used
81
+ # to build XML using Nokogiri::XML::Builder.
82
+ def build_xml(&block)
83
+ raise ArgumentError, "#{self.class}.build_xml requires a block with one parameter" unless block_given? && block.arity == 1
84
+ @build_block = block
85
+ end
86
+
87
+ # Returns true if the element is configured to contain a single instance
88
+ # of a child element that matches the name and opts parameters.
89
+ def has_one?(name, opts={})
90
+ element_config_has_one.any? do |element_config|
91
+ opts.all? do |key, val|
92
+ # In SAXMachine::SaxConfig::ElementConfig, the :class config option
93
+ # maps to instance variable @data_class.
94
+ key = :data_class if key == :class
95
+ val == element_config.instance_variable_get(:"@#{key}")
96
+ end
97
+ end
98
+ end
99
+
100
+ # Returns true if the element is configured to contain many instances of
101
+ # a child element that matches the name and opts parameters.
102
+ def has_many?(name, opts={})
103
+ element_config_has_many.any? do |element_config|
104
+ opts.all? do |key, val|
105
+ val == element_config.instance_variable_get(:"@#{key}")
106
+ end
107
+ end
108
+ end
109
+
110
+ # Returns true if the element is configured to contain a value.
111
+ def has_a_value?
112
+ !element_config_for_value.nil?
113
+ end
114
+
115
+ def attribute_config
116
+ sax_config.top_level_attributes
117
+ end
118
+
119
+ # Returns the SAXMachine::SaxConfig::ElementConfig instances that allow
120
+ # for a single instance of another element.
121
+ # NOTE: We use the "has_one" and "has_many" terminoolgy for child elements
122
+ # because it seems to be the most readable option.
123
+ def element_config_has_one
124
+ sax_config.top_level_elements.reject do |_name, element_configs|
125
+ element_configs.detect do |element_config|
126
+ element_config.instance_variable_get(:@as) == :value
127
+ end
128
+ end
129
+ end
130
+
131
+ # Returns the SAXMachine::SaxConfig::ElementConfig instance for the
132
+ # element that represents the value (i.e. text that is one or more
133
+ # child elements). This is essentially the inverse of
134
+ # element_config_has_one.
135
+ def element_config_for_value
136
+ sax_config.top_level_elements.detect do |_name, element_configs|
137
+ element_configs.detect do |element_config|
138
+ element_config.instance_variable_get(:@as) == :value
139
+ end
140
+ end
141
+ end
142
+
143
+ # Returns the SAXMachine::SaxConfig::ElementConfig that allow for
144
+ # multiple instances of other elements.
145
+ # NOTE: SAXMachine uses the term "collection" elements, but that's very
146
+ # confusing since PBCoreCollection is a thing, so we change it to the
147
+ # has_many terminology (and has_one for the singular element config for
148
+ # consistency).
149
+ def element_config_has_many
150
+ sax_config.collection_elements.reject do |element_config|
151
+ element_config.instance_variable_get(:@as) == :value
152
+ end
153
+ end
154
+
155
+ def element_config_all
156
+ element_config_has_one.merge(element_config_has_many) do |name, config_has_one, config_has_many|
157
+ config_has_many.present? ? config_has_many : config_has_one
158
+ end
159
+ end
160
+
161
+ def has_sax_machine_attribute?(name, opts={})
162
+ sax_config.top_level_attributes.any? do |attr_config|
163
+ opts.all? do |key, val|
164
+ val == attr_config.instance_variable_get("@#{key}".to_sym)
165
+ end
166
+ end
167
+ end
168
+
169
+ def has_sax_machine_value_element?
170
+ !sax_config.top_level_element_value.empty?
171
+ end
172
+
173
+ def has_sax_machine_top_level_element?(name, opts={})
174
+ Array(sax_config.top_level_elements.fetch(name.to_s, nil)).any? do |element_config|
175
+ opts.all? do |key, val|
176
+ key = :data_class if key == :class
177
+ val == element_config.instance_variable_get("@#{key}".to_sym)
178
+ end
179
+ end
180
+ end
181
+
182
+ def has_sax_machine_collection_element?(name, opts={})
183
+ # NOTE: Accessing collection_element configs with squarey brackets has
184
+ # the unwanted side affect of creating an entry in the
185
+ # collection_elements, which we don't want. So we use #fetch here
186
+ Array(sax_config.collection_elements.fetch(name.to_s, nil)).any? do |element_config|
187
+ opts.all? do |key, val|
188
+ # This is a quirk of SAXMachine; for some reason it converts the
189
+ # config option :as to a string when assigning it to the
190
+ # ConfigElement instance, so we need to convert opts[:as] param to
191
+ # string in order to compare them accurately.
192
+ val = val.to_s if key == :as
193
+ val == element_config.instance_variable_get(:"@#{key}")
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end