~hacktivista/leanweb

122ee2c848ec24bdd2f6c88172fb2cc1a1203959 — Felix Freeman 14 days ago 9d692e1
Nested Controller#render
3 files changed, 67 insertions(+), 11 deletions(-)

A CHANGELOG.md
M lib/leanweb/controller.rb
M test/controller_test.rb
A CHANGELOG.md => CHANGELOG.md +5 -0
@@ 0,0 1,5 @@
# Changelog

## 0.1.x

- Allow nested `Controller#render`, for layouts or partials.

M lib/leanweb/controller.rb => lib/leanweb/controller.rb +34 -11
@@ 12,7 12,7 @@ require 'rack'
module LeanWeb
  # Controller is a base controller with `@route`, `@request` and `@response`
  # private attributes that will be shared with your views when you {#render}
  # Haml documents.
  # documents.
  #
  # Even if you don't {#render}, you can use the `.finish` method from
  # `Rack::Response` on `@response` to return a proper `Rack` response.


@@ 32,34 32,57 @@ module LeanWeb
    #
    # - {Route#path} `/a/b/c` and `render('d')` will render `src/views/a/b/d`
    # - {Route#path} `/a/b/c/` and `render('d')` will render `src/views/a/b/c/d`
    # - {Route#path} `%r{/a/b/c/(.+)}` and `render('d')` will render
    #   `src/views/a/b/c/d`
    # - `render('~/custom')` will render `src/views/custom`
    # - `render('/custom')` will render `/custom`
    #
    # @param path [String] Might be an absolute path or a relative path to
    #   `src/views/` + the parent path of the last non-capture-group on
    #   `@route.path`. You can also make it relative to {LeanWeb::VIEW_PATH} by
    #   prepending `~`.
    #   `src/views/` + path of the last non-capture-group on `@route.path`. You
    #   can also make it relative to {LeanWeb::VIEW_PATH} by prepending `~`.
    # @param content_type [String] Defaults to the proper Content-Type for file
    #   extension, `text/plain` on unknown files.
    # @return [Rack::Request#finish] A valid rack response.
    def render(path, content_type = nil) # rubocop:disable Metrics/MethodLength
    # @yield Optionally pass a block for nested rendering.
    # @return [Rack::Request#finish] A valid Rack response.
    def render(path, content_type = nil, &block)
      @response.set_header(
        'Content-Type',
        content_type || guess_content_type(path)
      )
      @response.write(_render(path, &block))
      @response.finish
    end

    # Actual renderer. Useful for partials / nested views.
    #
    # @param path [String] Same as on {#render}.
    # @yield Same as on {#render}.
    # @return [String] Rendered `path`.
    def _render(path, &block)
      path = absolute_view_path(path)
      content = File.read(path)
      case File.extname(path)
      when '.haml'
        require('haml')
        @response.write(Haml::Engine.new(content).render(binding))
        @response.set_header('Content-Type', content_type || 'text/html')
        Haml::Engine.new(content).render(binding) { block.nil? || block.call }
      else
        require('erb')
        @response.write(ERB.new(content).result(binding))
        @response.set_header('Content-Type', content_type || 'text/plain')
        ERB.new(content).result(binding) { block.nil? || block.call }
      end
      @response.finish
    end

    protected

    # @param path [String]
    def guess_content_type(path)
      case File.extname(path)
      when '.haml'
        'text/html'
      else
        'text/plain'
      end
    end

    # @param relative_path [String]
    # @return [String] Full path.
    def absolute_view_path(relative_path)

M test/controller_test.rb => test/controller_test.rb +28 -0
@@ 99,6 99,20 @@ class ControllerTest < Minitest::Test
    end
  end

  def test_render__nested_haml # rubocop:disable Metrics/MethodLength
    write_view("%h1 haml\n= yield", '.haml') do |_, file_layout|
      write_view('%h2 nested!', '.haml') do |route_nested, file_nested|
        controller = LeanWeb::Controller.new(route_nested)
        assert_equal(
          "<h1>haml</h1>\n<h2>nested!</h2>\n\n",
          controller.render(file_layout.path) do
            controller._render(file_nested.path)
          end[2][0]
        )
      end
    end
  end

  def test_render__unknown_filetype
    write_view('<%= @request %>', '.asdasd') do |route, file|
      controller = LeanWeb::Controller.new(route, 'hey')


@@ 106,6 120,20 @@ class ControllerTest < Minitest::Test
    end
  end

  def test_render__nested_erb # rubocop:disable Metrics/MethodLength
    write_view('Hello <%= yield %>', '.any') do |_, file_layout|
      write_view('erb', '.any') do |route_nested, file_nested|
        controller = LeanWeb::Controller.new(route_nested)
        assert_equal(
          'Hello erb',
          controller.render(file_layout.path) do
            controller._render(file_nested.path)
          end[2][0]
        )
      end
    end
  end

  def test_render__with_content_type
    write_view('') do |route, file|
      controller = LeanWeb::Controller.new(route, 'hey')