Class: PBCore::Element

Inherits:
Object
  • Object
show all
Includes:
Attributes::Common, SAXMachine
Defined in:
lib/pbcore/element.rb

Overview

TODO: decouple XML building behavior from schema-related declarations.

Direct Known Subclasses

Annotation, AssetDate, AssetType, AudienceLevel, AudienceRating, Contributor, Contributor::Contributor, Contributor::Role, Coverage, Coverage::Coverage, Coverage::Type, Creator, Creator::Creator, Creator::Role, Description, DescriptionDocument, Extension, PBCore::Extension::Embedded, PBCore::Extension::Wrap, PBCore::Extension::Wrap::Element, Genre, Identifier, Instantiation, Instantiation::AlternativeModes, Instantiation::Annotation, Instantiation::ChannelConfiguration, Instantiation::Colors, Instantiation::DataRate, Instantiation::Date, Instantiation::Digital, Instantiation::Dimensions, Instantiation::Duration, Instantiation::EssenceTrack, Instantiation::EssenceTrack::Annotation, Instantiation::EssenceTrack::AspectRatio, Instantiation::EssenceTrack::BitDepth, Instantiation::EssenceTrack::DataRate, Instantiation::EssenceTrack::Duration, Instantiation::EssenceTrack::Encoding, Instantiation::EssenceTrack::FrameRate, Instantiation::EssenceTrack::FrameSize, Instantiation::EssenceTrack::Identifier, Instantiation::EssenceTrack::Language, Instantiation::EssenceTrack::PlaybackSpeed, Instantiation::EssenceTrack::SamplingRate, Instantiation::EssenceTrack::Standard, Instantiation::EssenceTrack::TimeStart, Instantiation::EssenceTrack::Type, Instantiation::Extension, Instantiation::FileSize, Instantiation::Generations, Instantiation::Identifier, Instantiation::Language, Instantiation::Location, Instantiation::MediaType, Instantiation::Physical, Instantiation::Relation, Instantiation::Relation::Identifier, Instantiation::Relation::Type, Instantiation::Rights, Instantiation::Rights::Link, Instantiation::Rights::Summary, Instantiation::Standard, Instantiation::TimeStart, Instantiation::Tracks, InstantiationDocument, Publisher, Publisher::Publisher, Publisher::Role, Relation, Relation::Identifier, Relation::Type, RightsSummary, RightsSummary::Link, RightsSummary::Summary, Subject, Title

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Attributes::Common

included

Class Attribute Details

.build_blockObject (readonly)

Returns the value of attribute build_block.



78
79
80
# File 'lib/pbcore/element.rb', line 78

def build_block
  @build_block
end

Class Method Details

.attribute_configObject



115
116
117
# File 'lib/pbcore/element.rb', line 115

def attribute_config
  sax_config.top_level_attributes
end

.build_xml(&block) ⇒ Object

Class method to allow extended classes to declaratively the logic used to build XML using Nokogiri::XML::Builder.

Raises:

  • (ArgumentError)


82
83
84
85
# File 'lib/pbcore/element.rb', line 82

def build_xml(&block)
  raise ArgumentError, "#{self.class}.build_xml requires a block with one parameter" unless block_given? && block.arity == 1
  @build_block = block
end

.element_config_allObject



155
156
157
158
159
# File 'lib/pbcore/element.rb', line 155

def element_config_all
  element_config_has_one.merge(element_config_has_many) do |name, config_has_one, config_has_many|
    config_has_many.present? ? config_has_many : config_has_one
  end
end

.element_config_for_valueObject

Returns the SAXMachine::SaxConfig::ElementConfig instance for the element that represents the value (i.e. text that is one or more child elements). This is essentially the inverse of element_config_has_one.



135
136
137
138
139
140
141
# File 'lib/pbcore/element.rb', line 135

def element_config_for_value
  sax_config.top_level_elements.detect do |_name, element_configs|
    element_configs.detect do |element_config|
      element_config.instance_variable_get(:@as) == :value
    end
  end
end

.element_config_has_manyObject

Returns the SAXMachine::SaxConfig::ElementConfig that allow for multiple instances of other elements. NOTE: SAXMachine uses the term “collection” elements, but that’s very confusing since PBCoreCollection is a thing, so we change it to the has_many terminology (and has_one for the singular element config for consistency).



149
150
151
152
153
# File 'lib/pbcore/element.rb', line 149

def element_config_has_many
  sax_config.collection_elements.reject do |element_config|
    element_config.instance_variable_get(:@as) == :value
  end
end

.element_config_has_oneObject

Returns the SAXMachine::SaxConfig::ElementConfig instances that allow for a single instance of another element. NOTE: We use the “has_one” and “has_many” terminoolgy for child elements because it seems to be the most readable option.



123
124
125
126
127
128
129
# File 'lib/pbcore/element.rb', line 123

def element_config_has_one
  sax_config.top_level_elements.reject do |_name, element_configs|
    element_configs.detect do |element_config|
      element_config.instance_variable_get(:@as) == :value
    end
  end
end

.has_a_value?Boolean

Returns true if the element is configured to contain a value.

Returns:

  • (Boolean)


111
112
113
# File 'lib/pbcore/element.rb', line 111

def has_a_value?
  !element_config_for_value.nil?
end

.has_many?(name, opts = {}) ⇒ Boolean

Returns true if the element is configured to contain many instances of a child element that matches the name and opts parameters.

Returns:

  • (Boolean)


102
103
104
105
106
107
108
# File 'lib/pbcore/element.rb', line 102

def has_many?(name, opts={})
  element_config_has_many.any? do |element_config|
    opts.all? do |key, val|
      val == element_config.instance_variable_get(:"@#{key}")
    end
  end
end

.has_one?(name, opts = {}) ⇒ Boolean

Returns true if the element is configured to contain a single instance of a child element that matches the name and opts parameters.

Returns:

  • (Boolean)


89
90
91
92
93
94
95
96
97
98
# File 'lib/pbcore/element.rb', line 89

def has_one?(name, opts={})
  element_config_has_one.any? do |element_config|
    opts.all? do |key, val|
      # In SAXMachine::SaxConfig::ElementConfig, the :class config option
      # maps to instance variable @data_class.
      key = :data_class if key == :class
      val == element_config.instance_variable_get(:"@#{key}")
    end
  end
end

.has_sax_machine_attribute?(name, opts = {}) ⇒ Boolean

Returns:

  • (Boolean)


161
162
163
164
165
166
167
# File 'lib/pbcore/element.rb', line 161

def has_sax_machine_attribute?(name, opts={})
  sax_config.top_level_attributes.any? do |attr_config|
    opts.all? do |key, val|
      val == attr_config.instance_variable_get("@#{key}".to_sym)
    end
  end
end

.has_sax_machine_collection_element?(name, opts = {}) ⇒ Boolean

Returns:

  • (Boolean)


182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/pbcore/element.rb', line 182

def has_sax_machine_collection_element?(name, opts={})
  # NOTE: Accessing collection_element configs with squarey brackets has
  # the unwanted side affect of creating an entry in the
  # collection_elements, which we don't want. So we use #fetch here
  Array(sax_config.collection_elements.fetch(name.to_s, nil)).any? do |element_config|
    opts.all? do |key, val|
      # This is a quirk of SAXMachine; for some reason it converts the
      # config option :as to a string when assigning it to the
      # ConfigElement instance, so we need to convert opts[:as] param to
      # string in order to compare them accurately.
      val = val.to_s if key == :as
      val == element_config.instance_variable_get(:"@#{key}")
    end
  end
end

.has_sax_machine_top_level_element?(name, opts = {}) ⇒ Boolean

Returns:

  • (Boolean)


173
174
175
176
177
178
179
180
# File 'lib/pbcore/element.rb', line 173

def has_sax_machine_top_level_element?(name, opts={})
  Array(sax_config.top_level_elements.fetch(name.to_s, nil)).any? do |element_config|
    opts.all? do |key, val|
      key = :data_class if key == :class
      val == element_config.instance_variable_get("@#{key}".to_sym)
    end
  end
end

.has_sax_machine_value_element?Boolean

Returns:

  • (Boolean)


169
170
171
# File 'lib/pbcore/element.rb', line 169

def has_sax_machine_value_element?
  !sax_config.top_level_element_value.empty?
end

Instance Method Details

#attributes(key_by_xml_name: false) ⇒ Object

Returns a hash of attrubutes as the should appear in the XML.



48
49
50
51
52
53
54
55
56
57
# File 'lib/pbcore/element.rb', line 48

def attributes(key_by_xml_name: false)
  xml_attrs = Hash[
    self.class.sax_config.top_level_attributes.map do |attr|
      accessor = attr.instance_variable_get(:@as)
      key = key_by_xml_name ? attr.name : accessor
      [ key, send(accessor) ]
    end
  ]
  xml_attrs
end

#build(builder = nil) ⇒ Object

Executes the block defined with the class method ‘build_xml`. Uses a Nokogiri::XML::Builder instance (either passed in or instantiated) to build the XML, and then returns the builder instance.

Raises:

  • (ArgumentError)


62
63
64
65
66
67
68
# File 'lib/pbcore/element.rb', line 62

def build(builder=nil)
  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)
  PBCore.fail_if_missing_build_xml_block(element_class: self.class)
  builder ||= Nokogiri::XML::Builder.new
  instance_exec builder, &self.class.build_block
  builder
end

#elements(key_by_xml_name: false) ⇒ Object

Returns a hash of child element instances.



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/pbcore/element.rb', line 26

def elements(key_by_xml_name: false)
  key_value_pairs_array = self.class.element_config_all.map do |name, element_configs|
    # SAXMachine allows you to declare multiple elements with the same name
    # but we don't do tha with PBCore, so just grab the first and only one.
    element_config = element_configs.first
    # get the accessor name by which to get the value
    accessor = element_config.instance_variable_get(:@as)

    # fetch the value by calling the accessor
    value = send(accessor)
    # create the key, value pair that will go into the Hash[] construct.
    key = key_by_xml_name ? name : accessor
    [ key,  value ]
  end

  Hash[ key_value_pairs_array ]
end

#to_xmlObject

Builds the xml using #build with a new instance of Nokogiri::XML::Builder and immediately calls to_xml on it.



72
73
74
# File 'lib/pbcore/element.rb', line 72

def to_xml
  build.to_xml
end

#xml_attributesObject

Shortcut for getting xml attributes.



45
# File 'lib/pbcore/element.rb', line 45

def xml_attributes; attributes(key_by_xml_name: true); end