# frozen_string_literal: true require "pakyow/support/makeable" module Pakyow module Presenter # Decorates an object being bound to the view. # class Binder extend Support::Makeable # The object being bound. # attr_reader :object def initialize(object, app:) @object, @app = object, app @parts = {} @binding = false @memoized = {} end # Defines a binding part, which binds to an aspect of the view. # # @example # binder :post do # def title(value) # part :class do # [:first_classname, :second_classname] # end # # part :content do # value.to_s.reverse # end # end # end # def part(name, &block) parts_for(caller_locations(1, 1)[0].label.to_sym).define_part(name, block) end # Returns the value for a key (including parts). # # @api private def __value(key) if @memoized.include?(key) @memoized[key] else @memoized[key] = if respond_to?(key) value = public_send(key) if parts?(key) parts_for(key) else value end else @object[key] end end end # Returns the underlying object value for a key. # def [](key) @object[key] end # Returns only the content value for a key. # # @api private def __content(key, view) return_value = __value(key) if return_value.is_a?(BindingParts) if return_value.content? return_value.content(view) else @object[key] end else return_value end end # Returns +true+ if the binder might return a value for +key+. # def include?(key) return false if @binding && !@object.include?(key) && (parts?(key) && !parts_for(key).content?) respond_to?(key) || @object.include?(key) end # Returns +true+ if the a value is present for +key+. # def present?(key) !__value(key).nil? end # Flips a switch, telling the binder that we now only care about content, not other parts. # This is so that we can transform based on parts, but bind only based on content. # # @api private def binding! @binding = true end private def parts?(name) @parts.include?(name.to_sym) end def parts_for(name) @parts[name] ||= BindingParts.new end end end end