module FoxPage::Builders::Pages

Public Instance Methods

build_all_the_pages(path, route) click to toggle source
# File lib/fox_page/builders/pages.rb, line 23
def build_all_the_pages(path, route)
  enumerable =
    if route.generate_all.is_a?(Symbol)
      model = route.generate_all.camelize.constantize
      model.all
    else
      route.generate_all.call
    end

  enumerable.each_with_index do |item, index|
    if route.generate_all_ids
      # generate_all returns ids
      id = enumerable.at(index)
      target_path = format(path, id: id)
    else
      id = (item.respond_to?(:id) && item.id) || index
      # have page numbers start with 1
      target_path = format(path, id: id == index ? id + 1 : id)
    end

    build_single_page(target_path, route, id: id)
  end
end
build_pages() click to toggle source
# File lib/fox_page/builders/pages.rb, line 12
def build_pages
  app.routes.each do |path, route|
    if route.generate_all
      build_all_the_pages(path, route)
      next
    end

    build_single_page(path, route)
  end
end
build_single_page(target_path, route, params = {}) click to toggle source
# File lib/fox_page/builders/pages.rb, line 47
def build_single_page(target_path, route, params = {})
  if params.empty?
    params_log_str = ""
  else
    params_log_str = "(#{params.inspect})"
    route = route.dup
    route.params = OpenStruct.new(route.params.to_h.merge(params))
  end

  puts "PAGE\t#{target_path} => #{route.base_name}##{route.method_name}#{params_log_str}"

  target_file = File.join(output_directory, target_path)
  unless route.single_file
    FileUtils.mkdir_p(target_file)
    target_file = File.join(target_file, "index.html")
  end

  File.open(target_file, "w") do |f|
    f.puts render_route(route, target_path)
  end
end
layout_for(controller, route) click to toggle source
# File lib/fox_page/builders/pages.rb, line 153
def layout_for(controller, route)
  layout = controller.class.superclass.instance_variable_get(:@__use_layout_for)&.[](route.method_name)
  return if layout == false

  Tilt.new(layout_path(controller, layout))
end
layout_path(controller, layout) click to toggle source
# File lib/fox_page/builders/pages.rb, line 160
def layout_path(controller, layout)
  layout ||= controller.class.layout

  File
    .join(views_path, layout)
    .tap(&method(:validate_file_exists))
end
page_path(route) click to toggle source
# File lib/fox_page/builders/pages.rb, line 168
def page_path(route)
  Dir
    .glob(
      File.join(views_path, route.base_name, "#{route.method_name}.*")
    )
    .first
    .tap { |file| validate_file_exists(file, route) }
end
render_route(route, path) click to toggle source
# File lib/fox_page/builders/pages.rb, line 69
def render_route(route, path)
  controller = spiced_controller(route, path).new
  controller.method(route.method_name).call

  layout = layout_for(controller, route)
  page = Tilt.new(page_path(route))

  controller.instance_eval do
    if layout
      layout.render(self) { page.render(self) }
    else
      page.render(self)
    end
  end
end
spiced_controller(route, path) click to toggle source

for the sake of keeping the original classes sane while building, we create a subclass of the original dynamically and inject common helpers to it and also run before_actions

Calls superclass method
# File lib/fox_page/builders/pages.rb, line 88
def spiced_controller(route, path) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/LineLength
  Class.new(route.controller).tap do |klass| # rubocop:disable Metrics/BlockLength, Metrics/LineLength
    klass.include(Helpers::AppHelper.new(app))
    klass.include(Helpers::AssetsHelper)
    klass.include(Helpers::RenderHelper)

    # include global ApplicationHelper if possible
    begin
      klass.include(ApplicationHelper)
    rescue NameError # rubocop:disable Lint/HandleExceptions
      # we don't have a global ApplicationHelper... which is fine
    end

    # find a controller-specific helper class and include it if we can
    begin
      helper = Kernel.const_get("#{route.base_name}_helper".camelize)
      klass.include(helper)
    rescue NameError # rubocop:disable Lint/HandleExceptions
      # same difference
    end

    klass.define_method(:params) do
      route.params
    end

    klass.define_method(:current_path) do
      path
    end

    klass.define_method(:current_controller_name) do
      route.base_name
    end

    klass.define_method(:inspect) do |*args|
      # report that we are actually the controller, not some random
      # anonymous class
      # append a + to it to indicate that it's different than an ordinary
      # class instance
      super(*args).sub(/#<Class:[^>]+>/, "#{route.controller}+")
    end

    klass.define_singleton_method(:inspect) do
      # for .ancestors to show up correctly
      "#{route.controller}+"
    end

    klass.define_method(:to_s) do |*args|
      # irb uses this method for displaying in the prompt
      super(*args).sub(/#<Class:[^>]+>/, "#{route.controller}+")
    end

    # inject filters
    route.controller.public_instance_methods(false).each do |method|
      klass.define_method(method) do |*args|
        # @__before_actions is set on the original class -- use it from
        # that one
        route.controller.instance_variable_get(:@__before_actions)&.each do |action| # rubocop:disable Metrics/LineLength
          send(action)
        end
        super(*args)
      end
    end
  end
end
validate_file_exists(file, route = nil) click to toggle source
# File lib/fox_page/builders/pages.rb, line 181
def validate_file_exists(file, route = nil)
  return if file && File.exist?(file)

  error_message = if route
                    "template for #{route.base_name}##{route.method_name}"
                  else
                    "layout template"
                  end

  raise ArgumentError, "Could not find #{error_message}"
end
views_path() click to toggle source
# File lib/fox_page/builders/pages.rb, line 177
def views_path
  @views_path ||= app.root.join("app", "views")
end