module Ecoportal module API class V2 class Page class Section < Ecoportal::API::Common::Content::DoubleModel INITIAL_WEIGHT = 9999 class << self def new_doc(split: false) { "id" => new_uuid, "type" => split ? "split" : "content", "weight" => INITIAL_WEIGHT }.tap do |out| component_ids = if split { "left_component_ids" => [], "right_component_ids" => [] } else { "component_ids" => [] } end out.merge!(component_ids) end end end passkey :id passforced :patch_ver, default: 1 passthrough :weight, :type passthrough :heading, :left_heading, :right_heading passarray :component_ids, :left_component_ids, :right_component_ids passboolean :minimized def ooze self._parent.ooze end # @return [Array] the stage(s) this section belongs to. def stages ooze.stages.select {|stg| stg.section?(self)} end # @return [Boolean] `true` if the section belongs to more than 1 stage, `false` otherwise def shared? stages.count > 1 end # @return [Array 0 end # @return [Boolean] whether or not the section appears in an ooze instance def attached? !ooze.stages? || stages.any? {|stg| stg.section?(self)} end # @return [Boolean] whether or not this section is a split section def split? doc && doc["type"] == "split" end # @return [Array] all the `ids` of the components/fields in this section def all_component_ids return component_ids.to_a unless split? left_component_ids.to_a | right_component_ids.to_a end # @note when removing a component, please make sure to either # add it to another section or to delete! it before save def remove_component!(*comps_or_ids) comps_or_ids.each do |com_or_id| case com_or_id when Ecoportal::API::V2::Page::Component remove_component!(com_or_id.id) when String if split? left_component_ids.delete!(com_or_id) right_component_ids.delete!(com_or_id) else component_ids.delete!(com_or_id) end else msg = "Missuse: com_or_id should be a Component or a String. Given: #{com_or_id.class}" raise ArgumentError.new(msg) end end end # @raise [ArgumentError] if `com_or_id` is not of any of the allowed types # @param com_or_id [Ecoportal::API::V2::Page::Component, String] Component or `id` thereof # @return [Boolean] whether or not a component/field belongs to this section. def component?(com_or_id) case com_or_id when Ecoportal::API::V2::Page::Component component?(com_or_id.id) when String all_component_ids.include?(com_or_id) else msg = "Missuse: com_or_id should be a Component or a String. Given: #{com_or_id.class}" raise ArgumentError.new(msg) end end # @return[Symbol, Nil] might be `:right`, maybe `:left`, or `nil` def component_side(com_or_id) return nil unless split? case com_or_id when Ecoportal::API::V2::Page::Component component_side(com_or_id.id) when String if left_component_ids.include?(com_or_id) :left elsif right_component_ids.include?(com_or_id) :right end else msg = "Missuse: com_or_id should be a Component or a String. Given: #{com_or_id.class}" raise ArgumentError.new(msg) end end # It looks up the components that belong to this section. # @return [Array] def components(side: nil) case side when :right right_components when :left left_components when NilClass components_by_id(*all_component_ids) else raise "Side should be one of [nil, :right, :left]. Given: #{side}" end end # It looks up the components that belong to the `left` side of this section. # @raise [Exception] if this is not a `split` section # @return [Array] def left_components raise "You are trying to retrieve side components in a Split Section" unless split? components_by_id(*left_component_ids) end # It looks up the components that belong to the `right` side of this section. # @raise [Exception] if this is not a `split` section # @return [Array] def right_components raise "You are trying to retrieve side components in a Split Section" unless split? components_by_id(*right_component_ids) end # Adds `field` to the section # @note # - To the specified `side`, when split section (default `:left`) # - To the end if `after` is not specified # - If `after` is specified, it searches field # - On the specific `side`, if specified (and split section) # - And adds the `field` after it, when found, or at the end if `after` is not found # @raise [ArgumentError] if `field` is not a Component # @raise [Exception] if the field does not belong to ooze.components # @raise [Exception] if the field belongs to another section # @param field [Ecoportal::API::V2::Page::Component] the field to be added. def add_component(field, before: nil, after: nil, side: nil) unless field.is_a?(Ecoportal::API::V2::Page::Component) msg = "Expected Ecoportal::API::V2::Page::Component. Given: #{field.class}" raise ArgumentError.new(msg) end unless ooze.components.include?(field) msg = "The field '#{field.label}' (#{field.id}) is not present in ooze.components.\n" msg += "Review your script (i.e. @var where you store previous ooze runs)." raise msg end # IMPORTANT NOTE: The code below creates objects, because field.section does a search on section.component_ids if field.section == self puts "Field with id '#{field.id}' already belongs to this section" elsif sec = field.section # Field belongs to another section raise "Field with id '#{field.id}' belongs to section '#{sec.heading || "Unnamed"}' (id: '#{sec.id}')" end if before if before_fld = to_component(before, side: side) side ||= component_side(before_fld) end elsif after if after_fld = to_component(after, side: side) side ||= component_side(after_fld) end end if split? ids_ary = side == :right ? right_component_ids : left_component_ids else ids_ary = component_ids end ids_ary.insert_one(field.id, before: before_fld&.id, after: after_fld&.id) self end private def components_by_id(*ids) ooze.components.values_at(*ids).select.with_index do |fld, i| puts "Warning: field id #{ids[i]} points to missing field" if !fld fld && (!block_given? || yield(fld)) end end def to_component(value, side: nil) components(side: side).find do |fld| found = nil found ||= fld.id == value.id if value.is_a?(Ecoportal::API::V2::Page::Component) found ||= fld.id == value found ||= same_string?(fld.label, value) end end end end end end end