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 itsTheNumber
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, ifCrazyNumber
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 afterObjectMaterialized
). 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
What Does ≪≪-Constant Do
How to Convert a Unix Timestamp (Seconds Since Epoch) to Ruby Datetime
How to Search Within an Array of Hashes by Hash Values in Ruby
Rails Update_Attributes Without Save
Failed to Build Gem Native Extension (Installing Compass)
How to Convert a String or Integer to Binary in Ruby
What Does the Question Mark At the End of a Method Name Mean in Ruby
How to Seed a Database in Rails
Open an Io Stream from a Local File or Url
Get List of a Class' Instance Methods
Ruby 2.0 Rails Gem Install Error "Cannot Load Such File - Openssl"
How to Generate a List of N Unique Random Numbers in Ruby
Understanding Private Methods in Ruby
Method to Parse HTML Document in Ruby
How to Find the Local Port a Rails Instance Is Running On
Validation for Non-Negative Integers and Decimal Values