class Hanami::View::PartBuilder

Decorates exposure values with matching parts.

@api public @since 2.1.0

Public Class Methods

call(name, value, as: nil, rendering:) click to toggle source

Decorates an exposure value.

@param name [Symbol] exposure name @param value [Object] exposure value @param as [Symbol, nil] alternative name to use for part class resolution

@return [Hanami::View::Part] decorated value

@api public @since 2.1.0

# File lib/hanami/view/part_builder.rb, line 21
def call(name, value, as: nil, rendering:)
  builder = value.respond_to?(:to_ary) ? :build_collection_part : :build_part

  send(builder, name: name, value: value, as: as, rendering: rendering)
end

Private Class Methods

build_collection_part(name:, value:, as: nil, rendering:) click to toggle source
# File lib/hanami/view/part_builder.rb, line 35
def build_collection_part(name:, value:, as: nil, rendering:)
  item_name, item_as = collection_item_name_as(name, as, inflector: rendering.inflector)
  item_part_class = part_class(name: item_name, as: item_as, rendering: rendering)

  arr = value.to_ary.map { |item|
    item_part_class.new(name: item_name, value: item, rendering: rendering)
  }

  collection_as = as.is_a?(Array) ? as.first : nil
  build_part(name: name, value: arr, as: collection_as, rendering: rendering)
end
build_part(name:, value:, as:, rendering:) click to toggle source
# File lib/hanami/view/part_builder.rb, line 29
def build_part(name:, value:, as:, rendering:)
  klass = part_class(name: name, as: as, rendering: rendering)

  klass.new(name: name, value: value, rendering: rendering)
end
collection_item_name_as(name, as, inflector:) click to toggle source
# File lib/hanami/view/part_builder.rb, line 47
def collection_item_name_as(name, as, inflector:)
  singular_name = inflector.singularize(name).to_sym
  singular_as =
    if as.is_a?(Array)
      as.last if as.length > 1
    else
      as
    end

  if singular_as && !singular_as.is_a?(Class)
    singular_as = inflector.singularize(singular_as.to_s)
  end

  [singular_name, singular_as]
end
part_class(name:, as:, rendering:) click to toggle source
# File lib/hanami/view/part_builder.rb, line 63
def part_class(name:, as:, rendering:)
  name = as || name

  if name.is_a?(Class)
    name
  else
    View.cache.fetch_or_store(:part_class, name, rendering.config) do
      resolve_part_class(name: name, rendering: rendering)
    end
  end
end
resolve_part_class(name:, rendering:) click to toggle source

rubocop:disable Metrics/PerceivedComplexity

# File lib/hanami/view/part_builder.rb, line 76
def resolve_part_class(name:, rendering:)
  namespace = rendering.config.part_namespace
  return rendering.config.part_class unless namespace

  name = rendering.inflector.camelize(name.to_s)

  # Give autoloaders a chance to act
  begin
    klass = namespace.const_get(name)
  rescue NameError # rubocop:disable Lint/HandleExceptions
  end

  if !klass && namespace.const_defined?(name, false)
    klass = namespace.const_get(name)
  end

  if klass && klass < Part
    klass
  else
    rendering.config.part_class
  end
end