class Enolib::Section

Public Class Methods

new(context, instruction, parent = nil) click to toggle source
Calls superclass method Enolib::ElementBase::new
# File lib/enolib/elements/section.rb, line 7
def initialize(context, instruction, parent = nil)
  super(context, instruction, parent)

  @all_elements_required = parent ? parent.all_elements_required? : false
end

Public Instance Methods

_missing_error(element) click to toggle source
# File lib/enolib/elements/section.rb, line 13
def _missing_error(element)
  case element
  when MissingField
    raise Errors::Validation.missing_element(@context, element.instance_variable_get(:@key), @instruction, 'missing_field')
  when MissingFieldset
    raise Errors::Validation.missing_element(@context, element.instance_variable_get(:@key), @instruction, 'missing_fieldset')
  when MissingList
    raise Errors::Validation.missing_element(@context, element.instance_variable_get(:@key), @instruction, 'missing_list')
  when MissingSection
    raise Errors::Validation.missing_element(@context, element.instance_variable_get(:@key), @instruction, 'missing_section')
  else
    raise Errors::Validation.missing_element(@context, element.instance_variable_get(:@key), @instruction, 'missing_element')
  end
end
_untouched() click to toggle source
# File lib/enolib/elements/section.rb, line 28
def _untouched
  return @instruction unless instance_variable_defined?(:@touched)

  _elements.each do |element|
    untouched_element = element._untouched
    return untouched_element if untouched_element
  end

  false
end
all_elements_required(required = true) click to toggle source
# File lib/enolib/elements/section.rb, line 39
def all_elements_required(required = true)
  @all_elements_required = required

  _elements.each do |element|
    if element.instruciton[:type] == :section && element.yielded?
      element.to_section.all_elements_required(required)
    elsif element.instruciton[:type] == :fieldset && element.yielded?
      element.to_fieldset.all_entries_required(required)
    end
  end
end
all_elements_required?() click to toggle source
# File lib/enolib/elements/section.rb, line 51
def all_elements_required?
  @all_elements_required
end
assert_all_touched(message = nil, except: nil, only: nil) click to toggle source
# File lib/enolib/elements/section.rb, line 55
def assert_all_touched(message = nil, except: nil, only: nil)
  message = Proc.new if block_given?

  _elements(map: true).each do |key, elements|
    next if except && except.include?(key) || only && !only.include?(key)

    elements.each do |element|
      untouched = element._untouched

      next unless untouched

      if message.is_a?(Proc)
        message = message.call(Element.new(@context, untouched, self))
      end

      raise Errors::Validation.unexpected_element(@context, message, untouched)
    end
  end
end
element(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 75
def element(key = nil)
  _element(key)
end
elements(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 79
def elements(key = nil)
  @touched = true

  if key
    elements_map = _elements(map: true)
    elements_map.has_key?(key) ? elements_map[key] : []
  else
    _elements
  end
end
empty(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 90
def empty(key = nil)
  _empty(key)
end
field(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 94
def field(key = nil)
  _field(key)
end
fields(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 98
def fields(key = nil)
  @touched = true

  if key
    elements_map = _elements(map: true)
    elements = elements_map.has_key?(key) ? elements_map[key] : []
  else
    elements = _elements
  end

  elements.map do |element|
    unless element.yields_field?
      raise Errors::Validation.unexpected_element_type(
        @context,
        key,
        element.instruction,
        'expected_fields'
      )
    end

    element.to_field
  end
end
fieldset(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 122
def fieldset(key = nil)
  _fieldset(key)
end
fieldsets(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 126
def fieldsets(key = nil)
  @touched = true

  if key
    elements_map = _elements(map: true)
    elements = elements_map.has_key?(key) ? elements_map[key] : []
  else
    elements = _elements
  end

  elements.map do |element|
    unless element.yields_fieldset?
      raise Errors::Validation.unexpected_element_type(
        @context,
        key,
        element.instruction,
        'expected_fieldsets'
      )
    end

    element.to_fieldset
  end
end
list(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 150
def list(key = nil)
  _list(key)
end
lists(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 154
def lists(key = nil)
  @touched = true

  if key
    elements_map = _elements(map: true)
    elements = elements_map.has_key?(key) ? elements_map[key] : []
  else
    elements = _elements
  end

  elements.map do |element|
    unless element.yields_list?
      raise Errors::Validation.unexpected_element_type(
        @context,
        key,
        element.instruction,
        'expected_lists'
      )
    end

    element.to_list
  end
end
optional_element(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 178
def optional_element(key = nil)
  _element(key, required: false)
end
optional_empty(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 182
def optional_empty(key = nil)
  _empty(key, required: false)
end
optional_field(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 186
def optional_field(key = nil)
  _field(key, required: false)
end
optional_fieldset(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 190
def optional_fieldset(key = nil)
  _fieldset(key, required: false)
end
optional_list(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 194
def optional_list(key = nil)
  _list(key, required: false)
end
optional_section(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 198
def optional_section(key = nil)
  _section(key, required: false)
end
parent() click to toggle source
# File lib/enolib/elements/section.rb, line 202
def parent
  if @instruction[:type] == :document
    nil
  else
    @parent || Section.new(@context, @instruction[:parent])
  end
end
required_element(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 210
def required_element(key = nil)
  _element(key, required: true)
end
required_empty(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 214
def required_empty(key = nil)
  _empty(key, required: true)
end
required_field(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 218
def required_field(key = nil)
  _field(key, required: true)
end
required_fieldset(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 222
def required_fieldset(key = nil)
  _fieldset(key, required: true)
end
required_list(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 226
def required_list(key = nil)
  _list(key, required: true)
end
required_section(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 230
def required_section(key = nil)
  _section(key, required: true)
end
section(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 234
def section(key = nil)
  _section(key)
end
sections(key = nil) click to toggle source
# File lib/enolib/elements/section.rb, line 238
def sections(key = nil)
  @touched = true

  if key
    elements_map = _elements(map: true)
    elements = elements_map.has_key?(key) ? elements_map[key] : []
  else
    elements = _elements
  end

  elements.map do |element|
    unless element.yields_section?
      raise Errors::Validation.unexpected_element_type(
        @context,
        key,
        element.instruction,
        'expected_sections'
      )
    end

    element.to_section
  end
end
to_s() click to toggle source
# File lib/enolib/elements/section.rb, line 262
def to_s
  if @instruction[:type] == :document
    "#<Enolib::Section document elements=#{elements.length}>"
  else
    "#<Enolib::Section key=#{@instruction[:key]} elements=#{elements.length}>"
  end
end
touch() click to toggle source
# File lib/enolib/elements/section.rb, line 270
def touch
  @touched = true

  _elements.each(&:touch)
end

Private Instance Methods

_element(key = nil, required: nil) click to toggle source
# File lib/enolib/elements/section.rb, line 278
def _element(key = nil, required: nil)
  @touched = true

  if key
    elements_map = _elements(map: true)
    elements = elements_map.has_key?(key) ? elements_map[key] : []
  else
    elements = _elements
  end

  if elements.empty?
    if required || required == nil && @all_elements_required
      raise Errors::Validation.missing_element(@context, key, @instruction, 'missing_element')
    elsif required == nil
      return MissingSectionElement.new(key, self)
    else
      return nil
    end
  end

  if elements.length > 1
    raise Errors::Validation.unexpected_multiple_elements(
      @context,
      key,
      elements.map(&:instruction),
      'expected_single_element'
    )
  end

  elements[0]
end
_elements(map: false) click to toggle source
# File lib/enolib/elements/section.rb, line 310
def _elements(map: false)
  unless instance_variable_defined?(:@instantiated_elements)
    @instantiated_elements = []
    @instantiated_elements_map = {}
    instantiate_elements(@instruction)
  end

  map ? @instantiated_elements_map : @instantiated_elements
end
_empty(key = nil, required: nil) click to toggle source
# File lib/enolib/elements/section.rb, line 320
def _empty(key = nil, required: nil)
  @touched = true

  if key
    elements_map = _elements(map: true)
    elements = elements_map.has_key?(key) ? elements_map[key] : []
  else
    elements = _elements
  end

  if elements.empty?
    if required || required == nil && @all_elements_required
      raise Errors::Validation.missing_element(@context, key, @instruction, 'missing_empty')
    elsif required == nil
      return MissingEmpty.new(key, self)
    else
      return nil
    end
  end

  if elements.length > 1
    raise Errors::Validation.unexpected_multiple_elements(
      @context,
      key,
      elements.map(&:instruction),
      'expected_single_empty'
    )
  end

  element = elements[0]

  # TODO: Other implementations use a direct check here (['type'] == :foo)
  #       Should this be unified across implementations? Follow up.
  #       (guess is that the main reason is stricter visibility in ruby currently)
  unless element.yields_empty?
    raise Errors::Validation.unexpected_element_type(
      @context,
      key,
      element.instruction,
      'expected_empty'
    )
  end

  element.to_empty
end
_field(key = nil, required: nil) click to toggle source
# File lib/enolib/elements/section.rb, line 366
def _field(key = nil, required: nil)
  @touched = true

  if key
    elements_map = _elements(map: true)
    elements = elements_map.has_key?(key) ? elements_map[key] : []
  else
    elements = _elements
  end

  if elements.empty?
    if required || required == nil && @all_elements_required
      raise Errors::Validation.missing_element(@context, key, @instruction, 'missing_field')
    elsif required == nil
      return MissingField.new(key, self)
    else
      return nil
    end
  end

  if elements.length > 1
    raise Errors::Validation.unexpected_multiple_elements(
      @context,
      key,
      elements.map(&:instruction),
      'expected_single_field'
    )
  end

  element = elements[0]

  unless element.yields_field?
    raise Errors::Validation.unexpected_element_type(
      @context,
      key,
      element.instruction,
      'expected_field'
    )
  end

  element.to_field
end
_fieldset(key = nil, required: nil) click to toggle source
# File lib/enolib/elements/section.rb, line 409
def _fieldset(key = nil, required: nil)
  @touched = true

  if key
    elements_map = _elements(map: true)
    elements = elements_map.has_key?(key) ? elements_map[key] : []
  else
    elements = _elements
  end

  if elements.empty?
    if required || required == nil && @all_elements_required
      raise Errors::Validation.missing_element(@context, key, @instruction, 'missing_fieldset')
    elsif required == nil
      return MissingFieldset.new(key, self)
    else
      return nil
    end
  end

  if elements.length > 1
    raise Errors::Validation.unexpected_multiple_elements(
      @context,
      key,
      elements.map(&:instruction),
      'expected_single_fieldset'
    )
  end

  element = elements[0]

  unless element.yields_fieldset?
    raise Errors::Validation.unexpected_element_type(
      @context,
      key,
      element.instruction,
      'expected_fieldset'
    )
  end

  element.to_fieldset
end
_list(key = nil, required: nil) click to toggle source
# File lib/enolib/elements/section.rb, line 475
def _list(key = nil, required: nil)
  @touched = true

  if key
    elements_map = _elements(map: true)
    elements = elements_map.has_key?(key) ? elements_map[key] : []
  else
    elements = _elements
  end

  if elements.empty?
    if required || required == nil && @all_elements_required
      raise Errors::Validation.missing_element(@context, key, @instruction, 'missing_list')
    elsif required == nil
      return MissingList.new(key, self)
    else
      return nil
    end
  end

  if elements.length > 1
    raise Errors::Validation.unexpected_multiple_elements(
      @context,
      key,
      elements.map(&:instruction),
      'expected_single_list'
    )
  end

  element = elements[0]

  unless element.yields_list?
    raise Errors::Validation.unexpected_element_type(
      @context,
      key,
      element.instruction,
      'expected_list'
    )
  end

  element.to_list
end
_section(key = nil, required: nil) click to toggle source
# File lib/enolib/elements/section.rb, line 518
def _section(key = nil, required: nil)
  @touched = true

  if key
    elements_map = _elements(map: true)
    elements = elements_map.has_key?(key) ? elements_map[key] : []
  else
    elements = _elements
  end

  if elements.empty?
    if required || required == nil && @all_elements_required
      raise Errors::Validation.missing_element(@context, key, @instruction, 'missing_section')
    elsif required == nil
      return MissingSection.new(key, self)
    else
      return nil
    end
  end

  if elements.length > 1
    raise Errors::Validation.unexpected_multiple_elements(
      @context,
      key,
      elements.map(&:instruction),
      'expected_single_section'
    )
  end

  element = elements[0]

  unless element.yields_section?
    raise Errors::Validation.unexpected_element_type(
      @context,
      key,
      element.instruction,
      'expected_section'
    )
  end

  element.to_section
end
instantiate_elements(section) click to toggle source
# File lib/enolib/elements/section.rb, line 452
def instantiate_elements(section)
  if section.has_key?(:mirror)
    instantiate_elements(section[:mirror])
  else
    filtered = section[:elements].reject { |element| @instantiated_elements_map.has_key?(element[:key]) }
    instantiated = filtered.map do |element|
      instance = SectionElement.new(@context, element, self)

      if @instantiated_elements_map.has_key?(element[:key])
        @instantiated_elements_map[element[:key]].push(instance)
      else
        @instantiated_elements_map[element[:key]] = [instance]
      end

      instance
    end

    @instantiated_elements.concat(instantiated) # TODO: Revisit order of this and the following

    instantiate_elements(section[:extend]) if section.has_key?(:extend)
  end
end