class Hanami::Extensions::View::SliceConfiguredView
Provides slice-specific configuration and behavior for any view class defined within a slice’s module namespace.
@api public @since 2.1.0
Constants
- PARTS_DIR
- SCOPES_DIR
- TEMPLATES_DIR
- VIEWS_DIR
Attributes
slice[R]
Public Class Methods
new(slice)
click to toggle source
@api private @since 2.1.0
Calls superclass method
# File lib/hanami/extensions/view/slice_configured_view.rb, line 21 def initialize(slice) super() @slice = slice end
Public Instance Methods
extended(view_class)
click to toggle source
@api private @since 2.1.0
# File lib/hanami/extensions/view/slice_configured_view.rb, line 28 def extended(view_class) load_app_view configure_view(view_class) define_inherited end
inspect()
click to toggle source
@return [String]
@api public @since 2.1.0
# File lib/hanami/extensions/view/slice_configured_view.rb, line 38 def inspect "#<#{self.class.name}[#{slice.name}]>" end
Private Instance Methods
app?()
click to toggle source
# File lib/hanami/extensions/view/slice_configured_view.rb, line 237 def app? slice.app == slice end
configure_view(view_class)
click to toggle source
rubocop:disable Metrics/AbcSize
# File lib/hanami/extensions/view/slice_configured_view.rb, line 58 def configure_view(view_class) view_class.settings.each do |setting| next unless slice.config.views.respond_to?(setting.name) # Configure the view from config on the slice, _unless it has already been configured by # a parent slice_, and re-configuring it for this slice would make no change. # # In the case of most slices, its views config is likely to be the same as its parent # (since each slice copies its `config` from its parent), and if we re-apply the config # here, then it may possibly overwrite config customisations explicitly made in parent # view classes. # # For example, given an app-level base view class, with custom config: # # module MyApp # class View < Hanami::View # config.layout = "custom_layout" # end # end # # And then a view in a slice inheriting from it: # # module MySlice # module Views # class SomeView < MyApp::View # end # end # end # # In this case, `SliceConfiguredView` will be extended two times: # # 1. When `MyApp::View` is defined # 2. Again when `MySlice::Views::SomeView` is defined # # If we blindly re-configure all view settings each time `SliceConfiguredView` is # extended, then at the point of (2) above, we'd end up overwriting the custom # `config.layout` explicitly configured in the `MyApp::View` base class, leaving # `MySlice::Views::SomeView` with `config.layout` of `"app"` (the default as specified # at `Hanami.app.config.views.layout`), and not the `"custom_layout"` value configured # in its immediate superclass. # # This would be surprising behavior, and we want to avoid it. slice_value = slice.config.views.public_send(setting.name) parent_value = slice.parent.config.views.public_send(setting.name) if slice.parent next if slice.parent && slice_value == parent_value view_class.config.public_send( :"#{setting.name}=", setting.mutable? ? slice_value.dup : slice_value ) end view_class.config.inflector = inflector # Configure the paths for this view if: # - We are the app, and a user hasn't provided custom `paths` (in this case, we need to # set the defaults) # - We are a slice, and the view's inherited `paths` is identical to the parent's config # (which would result in the view in a slice erroneously trying to find templates in # the app) if view_class.config.paths.empty? || (slice.parent && view_class.config.paths.map(&:dir) == [templates_path(slice.parent)]) view_class.config.paths = templates_path(slice) end view_class.config.template = template_name(view_class) view_class.config.default_context = Extensions::View::Context.context_class(slice).new view_class.config.part_class = part_class view_class.config.scope_class = scope_class if (part_namespace = namespace_from_path("#{VIEWS_DIR}/#{PARTS_DIR}")) view_class.config.part_namespace = part_namespace end if (scope_namespace = namespace_from_path("#{VIEWS_DIR}/#{SCOPES_DIR}")) view_class.config.scope_namespace = scope_namespace end end
define_inherited()
click to toggle source
rubocop:enable Metrics/AbcSize
Calls superclass method
# File lib/hanami/extensions/view/slice_configured_view.rb, line 139 def define_inherited template_name = method(:template_name) define_method(:inherited) do |subclass| super(subclass) subclass.config.template = template_name.(subclass) end end
inflector()
click to toggle source
# File lib/hanami/extensions/view/slice_configured_view.rb, line 178 def inflector slice.inflector end
load_app_view()
click to toggle source
If the given view doesn’t inherit from the app view, attempt to load it anyway, since requiring the app view is necessary for its ‘SliceConfiguredView` hook to execute and define the app-level part and scope classes that we refer to here.
# File lib/hanami/extensions/view/slice_configured_view.rb, line 47 def load_app_view return if app? begin slice.app.namespace.const_get(:View, false) rescue NameError => e raise unless e.name == :View end end
namespace_from_path(path)
click to toggle source
# File lib/hanami/extensions/view/slice_configured_view.rb, line 156 def namespace_from_path(path) path = "#{slice.slice_name.path}/#{path}" begin require path rescue LoadError => exception raise exception unless exception.path == path end begin inflector.constantize(inflector.camelize(path)) rescue NameError # rubocop: disable Lint/SuppressedException end end
part_class()
click to toggle source
# File lib/hanami/extensions/view/slice_configured_view.rb, line 182 def part_class @part_class ||= if views_namespace.const_defined?(:Part) views_namespace.const_get(:Part) else views_namespace.const_set(:Part, Class.new(part_superclass).tap { |klass| # Give the slice to `configure_for_slice`, since it cannot be inferred when it is # called via `.inherited`, because the class is anonymous at this point klass.configure_for_slice(slice) }) end end
part_superclass()
click to toggle source
# File lib/hanami/extensions/view/slice_configured_view.rb, line 195 def part_superclass return Hanami::View::Part if app? begin inflector.constantize(inflector.camelize("#{slice.app.slice_name.name}/views/part")) rescue NameError Hanami::View::Part end end
scope_class()
click to toggle source
# File lib/hanami/extensions/view/slice_configured_view.rb, line 205 def scope_class @scope_class ||= if views_namespace.const_defined?(:Scope) views_namespace.const_get(:Scope) else views_namespace.const_set(:Scope, Class.new(scope_superclass).tap { |klass| # Give the slice to `configure_for_slice`, since it cannot be inferred when it is # called via `.inherited`, since the class is anonymous at this point klass.configure_for_slice(slice) }) end end
scope_superclass()
click to toggle source
# File lib/hanami/extensions/view/slice_configured_view.rb, line 218 def scope_superclass return Hanami::View::Scope if app? begin inflector.constantize(inflector.camelize("#{slice.app.slice_name.name}/views/scope")) rescue NameError Hanami::View::Scope end end
template_name(view_class)
click to toggle source
# File lib/hanami/extensions/view/slice_configured_view.rb, line 171 def template_name(view_class) inflector .underscore(view_class.name) .sub(/^#{slice.slice_name.path}\//, "") .sub(/^#{VIEWS_DIR}\//, "") end
templates_path(slice)
click to toggle source
# File lib/hanami/extensions/view/slice_configured_view.rb, line 148 def templates_path(slice) if slice.app.equal?(slice) slice.root.join(APP_DIR, TEMPLATES_DIR) else slice.root.join(TEMPLATES_DIR) end end
views_namespace()
click to toggle source
# File lib/hanami/extensions/view/slice_configured_view.rb, line 228 def views_namespace @slice_views_namespace ||= if slice.namespace.const_defined?(:Views) slice.namespace.const_get(:Views) else slice.namespace.const_set(:Views, Module.new) end end