0

I have a posts view that contains links to the adjacent posts. I've added a class in posts_helper to decide what the links should be.

show.html.erb:

<ul>
  <% links = PostsHelper::Links.new(@post) %>
  <li><%= links.older %></li>
  <li><%= links.newer %></li>
  <li><%= link_to 'show all', posts_path %></li>
</ul>

posts_helper.rb:

module PostsHelper
  class Links
    def initialize(post)
      @post = post
    end

    def newer
      link_to 'newer', post_path(newer_post)
    end

    def older
      link_to 'older', post_path(older_post)
    end

    private

    attr_reader :post

    def newer_post
      Post.where(['id > ?', post.id]).last
    end

    def older_post
      Post.where(['id < ?', post.id]).first
    end
  end
end

Routes:

Prefix Verb URI Pattern          Controller#Action
 posts GET  /posts(.:format)     posts#index
  post GET  /posts/:id(.:format) posts#show

But I can't access post_path within the helper class. I get an error:

undefined method `post_path' for #<PostsHelper::Links:0x007fb884ad10d0>

This makes me think that I'm not going about things the 'rails' way.

How do I go about using the posts_path helper? Or have I veered too far from the ideal method?

user3574603
  • 3,364
  • 3
  • 24
  • 59

3 Answers3

1

You're closer than you think! But, it looks to me like you're heading toward the Presenter pattern - not the Helper pattern. Check out RailsCast for a better discussion.

So, let's say you have something like:

app/presenters/posts/show_presenter.rb

class Posts::ShowPresenter

    def initialize(controller)
      @controller = controller
    end

    def newer_link
      link_to 'newer', post_path(newer_post)
    end

    def older_link
      link_to 'older', post_path(older_post)
    end

  private

    def controller()          @controller                             end
    def params()              controller.params                       end
    def view_context()        controller.view_context                 end

    def newer_post
      Post.where(['id > ?', post.id]).last
    end

    def older_post
      Post.where(['id < ?', post.id]).first
    end    

    def post
      get_controller_variable(:post)
    end

    def get_controller_variable(sym)
      controller.instance_variable_get("@#{sym}")
    end

    def method_missing(*args, &block)
      view_context.send(*args, &block)
    end

end

Now, let's say in your PostsController, you do something like:

class PostsController < ApplicationController

  def show
    @post = Post.find_some_clever_way
    @presenter = Posts::ShowPresenter.new(self)
    ...
  end

end

In your partial, you should now be able to do something like:

<ul>
  <li><%= @presenter.older_link %></li>
  <li><%= @presenter.newer %></li>
  <li><%= link_to 'show all', posts_path %></li>
</ul>

Most of the magic lies in that method_missing definition. It basically allows your presenter to use the view_context from the controller to do things like render link_to and generate post_path.

jvillian
  • 19,953
  • 5
  • 31
  • 44
0

You should pass the path to your helpers:

# View
PostsHelper::Links.new(@post, posts_path)

# Helper
module PostsHelper
  class Links
    def initialize(post, path)
      @post = post
      @path = path
    end

Alternatively, as this SO answer states, you can access route helpers like this:

Rails.application.routes.url_helpers.posts_path
Rails.application.routes.url_helpers.posts_url(:host => "example.com")
Uzbekjon
  • 11,655
  • 3
  • 37
  • 54
0

you need to include ActionView::Helpers::UrlHelper for view helper link_to to be available in your custom helper and Rails.application.routes for path generation.

try this:

include ActionView::Helpers::UrlHelper

module PostsHelper
  class Links
    delegate :url_helpers, to: 'Rails.application.routes'

    def initialize(post)
      @post = post
    end

    def newer
      link_to 'newer', url_helpers.post_path(newer_post)
    end

    def older
      link_to 'older', url_helpers.post_path(older_post)
    end

    private

    attr_reader :post

    def newer_post
      Post.where(['id > ?', post.id]).last
    end

    def older_post
      Post.where(['id < ?', post.id]).first
    end
  end
end
sa77
  • 3,563
  • 3
  • 24
  • 37