decko-commons/decko

View on GitHub
card/lib/card/view.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
95%
class Card
  # Card::View manages {Options view options}, {Cache view caching}, and
  # {Permission view permissions}.
  #
  # View objects, which are instantiated whenever a view is rendered, are available as
  # in views and other format methods.  The view objects can be accessed using `#voo`.
  # We sometimes feebly pretend VOO is an acronym for "view option object," but really
  # we just needed a way not to confuse these Card::View options with the countless
  # references to viewnames that naturally arise when rendering views within views within
  # views.
  #
  # When view A renders view B within the same format object, A's voo is the parent of
  # B's voo. When card C nests card D, a new (sub)format object is initialized. C is then
  # the parent _format_ of D, but D has its own root voo.
  #
  # So a lineage might look something like this:
  #
  # `F1V1 -> F1V2 -> F1V3 -> F2V1 -> F2V2 -> F3V1 ...`
  #
  #
  class View
    include Options
    include View::Cache
    include Classy
    include Permission

    extend View::Cache::ClassMethods

    attr_reader :format, :parent, :card
    attr_accessor :interior

    class << self
      # @return [Symbol] viewname as Symbol
      def normalize view
        view.present? ? view.to_sym : nil
      end

      # @return [Array] list of viewnames as Symbols
      def normalize_list val
        case val
        when NilClass then []
        when Array    then val
        when String   then val.split(/[\s,]+/)
        when Symbol   then [val]
        else raise Card::Error, "bad show/hide argument: #{val}"
        end
      end
    end

    # @param format [Card::Format]
    # @param view [Symbol] viewname. Note: Card::View is initialized without a view
    #   when `voo` is called outside of a render,
    #   eg `subformat(cardname).method_with_voo_reference`.
    # @param raw_options [Hash]
    # @param parent [Card::View] (optional)
    def initialize format, view, raw_options={}, parent=nil
      @format = format
      @raw_view = view
      @raw_options = raw_options
      @parent = parent

      @card = @format.card
      normalize_options
    end

    # handle rendering, including optional visibility, permissions, and caching
    # @return [rendered view or a stub]
    def process
      return if process_live_options == :hide

      fetch { yield ok_view }
    end

    # the view to "attempt".  Typically the same as @raw_view, but @raw_view can
    # be overridden, eg for the main view (top view of the main card on a page)
    # @return [Symbol] view name
    def requested_view
      @requested_view ||= View.normalize live_options[:view]
    end

    # the final view.  can be different from @requested_view when there are
    # issues with permissions, recursions, unknown cards, etc.
    # @return [Symbol] view name
    def ok_view
      @ok_view ||= format.monitor_depth { altered_view || requested_view }
    end

    # @return [Card::View]
    def root
      @root = parent ? parent.root : self
    end

    # @return [true/false]
    def root?
      !parent
    end

    # the root voo of the root format
    def deep_root
      format.root.voo
    end

    # neither view nor format has a parent
    # @return [true/false]
    def deep_root?
      !parent && !format.parent
    end

    def depth
      @depth ||= parent ? (parent.depth + 1) : 0
    end

    # next voo object found tracing ancestry through parent voos and/or parent formats
    # @return [Card::View]
    def next_ancestor across_format=true
      parent || (across_format && next_format_ancestor) || nil
    end

    # voo object of format's parent
    def next_format_ancestor
      format.parent&.voo
    end
  end
end