Using Helpers in Model: How to Include Helper Dependencies

Using helpers in model: how do I include helper dependencies?

Just change the first line as follows :

include ActionView::Helpers

that will make it works.

UPDATE: For Rails 3 use:

ActionController::Base.helpers.sanitize(str)

Credit goes to lornc's answer

Rails 5 include helper in class

Seeing as some people are coming here to find the answer, here is what I did:

require 'action_view'

module OurModule
class CheckReport

def self.our_method
start_time = Time.current
LOGGER.info "OurModule::CheckReport.our_method finished in #{ActionController::Base.helpers.distance_of_time_in_words(start_time, Time.current)}"
end
end
end

Using helper in model

I'd recommend taking the Presenter approach here, as it'll give you a place to put presentation logic that can work with the ruby models but whose logic does not belong in the model itself. You can use Draper or one of several other gems that do this, but here is some code to demonstrate how simple this concept really is:

A sample presenter:

class CompanyPresenter < Struct.new(:company)
def state_country_name
company.country.name
end

# act as proxy for unknown methods
def method_missing(method, *args, &block)
company.public_send(method, *args, &block)
end
end

@presentable_company = CompanyPresenter.new(@company)

Or if you want to take the decorator approach:

module CompanyPresenter
def state_country_name
country.name
end
end

class Company < ActiveRecord::Base
def decorate!
self.extend CompanyPresenter
end
end

@company.decorate!

How to Inject Helper Dependencies in Domain Model Entity Classes

1) How is EF supposed to inject a new one of these helpers for each entity?

It's not possible to use the IoC container for this. An IoC container knows how to inject dependencies into (root) objects it creates itself, not objects created otherwise. So if you want entities to have this dependency (which is disputable, see later), you can subscribe a handler to the wrapped ObjectContext's ObjectMaterialized event:

((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized += 
ObjectContext_ObjectMaterialized;

In the handler you can check whether a CrazyNumber was materialized and assign a ICalculationsHelper to it. So this is not constructor injection, but property injection (sort of, not by an IoC container). Preluding on the remarks below, you could also generate its Thenumber there without ever injecting the service.

2) ...It seems much cleaner to have this (...) service locator pattern

I agree. If IoC can't work for whatever reason, SL is second best.

3) how is this domain class supposed to be unit tested?

That's a general IoC issue. For unit tests your IoC container should be able to inject a mock service if the service itself has dependencies that can't be fulfilled in the context of unit tests


But I doubt whether you should inject this service into the entities at all. This touches a broad subject, but I'll give a thought or two here:

  • I especially don't like the fact that the service does its work in the constructor. The object is constructed by EF, so whatever the service does, it may interfere with object construction by EF.

  • Why should CrazyNumber create its TheNumber property itself (granted that your code is just a stand-in for the real case)? From an object-oriented point of view this should happen if it combines data and behavior. In other words, if CrazyNumber contains the state that's required to generate the number. But this state can't ever be guaranteed to be complete or stable in a constructor. (It is after ObjectMaterialized). And if this state is not required, then the behavior shouldn't be there at all (single responsibility).

  • Maybe your example is a bit contrived, maybe the service is needed to create the number later, e.g. when it's first accessed. Then too, CrazyNumber shouldn't have this dependency, because there are likely to be code paths in which the number is never generated. In that case, the service is a loose dependency and it will be hard to tell when it actually needs it. It would be better to "inject" it by method injection when it's really needed:

    public void CreateCrazyNumber(ICalculationsHelper helper)
    {
    TheNumber = helper.DoCompletelyCrazyCalculations();
    }

Common method in model and helper

Writing include ApplicationHelper in to your model is bad practice because ApplicationHelper is a nice place to put tons of helper functions you need in your views. These functions will end up being imported as instance methods of your model. These functions are mostly unrelated to your model and will not work if they depend on things like params or request. Here are two other options:

Option 1:

You can just define the method inside the Client class, and then call it from the view, like this:

class Client < ActiveRecord::Base
def self.my_class_method
end
def my_instance_method
end
end

And then in your view:

<%= Client.my_class_method %>
<%= @client.my_instance_method %>

Option 2:

Make a separate module in lib and include it in the places you need it. The file name should match the module name for auto-loading to work.

In lib/my_module.rb:

module MyModule
def my_method
end
end

In your model:

class Client < ActiveRecord::Base
include MyModule
def other_method
my_method
end
end

Include the module in ApplicationHelper so it is available to all your views:

module ApplicationHelper
include MyModule
end

Then in your view you can call it easily:

<%= my_method %>

How do I call a number_helper in my model?

You can include helpers in your model by including ActionView::Helpers:

# in model
include ActionView::Helpers

I wonder, though, if it wouldn't make better sense to keep the presentation on the presentation side. You could just wrap up the extra logic in an alternative helper instead:

# in helper
def firm_size_in_millions(firm)
size = number_with_delimiter(firm.size)
"$#{size}M"
end

<!-- in view -->
<%= firm_size_in_millions(@firm) %>


Related Topics



Leave a reply



Submit