2

Within a Plain Old Ruby Object (PORO) in my rails app: I have the following method:

def some_method
  content_tag(:li, link_to("Do something", somewhere_path(object.id)))
end

First: the object didn't understand the method content_tag, so I added the following which made the object understand that method:

include ActionView::Helpers::TagHelper

Then the object didn't understand link_to so I added the following which made the object understand that method:

include ActionView::Helpers::UrlHelper

Now, it doesn't understand my route: somewhere_path(object.id).

Question: How can I make the PORO in my rails app understand the helpers which generate routes?

Followup Question: Is there an easier way to include all of this functionality into my PORO object? Perhaps there is a way to only include one major module and get all of this functionality (as opposed to perhaps needing to require 3 different modules).

Neil
  • 4,578
  • 14
  • 70
  • 155
  • `init`? Or do you mean `initialize`? – tadman May 10 '17 at 04:05
  • @tadman no I literally ment the the method `init`. I answered my own question below, so you can see what the class would look like. – Neil May 10 '17 at 04:07
  • Having `init` and `initialize` is asking for confusion, especially as in other languages `init` is what the initializer method is called. – tadman May 10 '17 at 04:08

2 Answers2

6

You either have to do what you describe in your self-answer (link to revision I refer to), or inject some context into your POROs. Where context is something which knows all those methods. Something like this:

class ProjectsController
  def update
    project = Project.find(params[:id])
    presenter = Presenters::Project.new(project, context: view_context) # your PORO
    # do something with presenter
  end
end

And your PORO would look like this:

module Presenters
  class Project
    attr_reader :presentable, :context
    def initialize(presentable, context:)
      @presentable = presentable
      @context = context
    end

    def special_link
      context.somewhere_path(presentable)
    end
  end
end

Me, I like neither of them. But sometimes we have to choose a lesser evil.

If anyone happens to know of a current way to get access to all of these methods with one include statement then let me know.

Why, yes. There is a way.

module MyViewCompatibilityPack
  include ActionView::Helpers::TagHelper
  include ActionView::Helpers::UrlHelper

  def url_helpers
    Rails.application.routes.url_helpers
  end
end

class MyPoro
  include MyViewCompatibilityPack

  ...
end
Community
  • 1
  • 1
Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
  • see my updated answer. Let me know if you know of how to do this becuase it appears as though it just isn't possible inside a PORO object. – Neil Apr 22 '17 at 04:30
  • I updated my answer yet again. See above. Also, I believe your answer will not "always" build the paths right via `Rails.application.routes.url_helpers`. I ran into issues while using passenger, as it didn't know the domain. – Neil May 10 '17 at 04:23
  • Yeah, in these situations, you should specify the host explicitly. Or set it [like this](http://stackoverflow.com/a/5725343/125816). – Sergio Tulentsev May 10 '17 at 07:03
0

The issue is that actionview-related methods are not available to POROs.

In order to get all the great stuff from actionview: you need to utilize the view_context keyword. Then: you can simply call upon actionview-related methods from your view_context:

class BuildLink
  attr_accessor :blog, :view_context

  def initialize(blog, view_context)
    @blog = blog
    @view_context = view_context
  end

  def some_method
    content_tag(:li, link_to(“Show Blog“, view_context.blog_path(blog)))
  end
end

So for example: from your controller you would call upon this PORO like so:

BuildLink.new(@blog, view_context).some_method

For more information, see below references:

Neil
  • 4,578
  • 14
  • 70
  • 155
  • 1
    It's pretty dirty to self-post an answer and not even acknowledge what Sergio did with a simple upvote. He's also right, a presenter is what you're talking about. What you're doing here is highly irregular and not recommended. – tadman May 10 '17 at 04:10
  • his method has the limitation of not knowing the domain. Espcially if using passenger, the url will be wrong. Also: prior to updating my answer: I did post a comment to him and he did not respond back. "Dirty" is putting it pretty strong. – Neil May 10 '17 at 04:11
  • I think you're misunderstanding his code. There's a reason this sort of stuff doesn't leak out. If you want that sort of functionality, define helpers. This is exactly what they're for. – tadman May 10 '17 at 04:12