How can I make rail helpers more object oriented
I would like for all the helpers throughout my Rails application to replace this syntax:
time_ago_in_words(@from_time)
with this:
@from_time.time_ago_in_words
Ideally, this change would also make helpers available anywhere in my application (as well, for example 5.times
).
Does anyone know of a plugin that does this? Would it be hard to roll me?
a source to share
If you are creating a new model, you can force it to inherit methods from the existing class.
Example (app / models / mykindofstring.rb):
class Mykindofstring < String
def all_caps_and_reverse
self.upcase.reverse
end
end
Then from the controller:
@my_string = Mykindofstring.new('This is my string')
and finally by calling it from the view:
<%= @my_string %>
displays like this is my string, while:
<%= @my_string.all_caps_and_reverse %>
displayed as GNIRTS YM SI SIHT.
The class inherits from the string class, so all methods applied to the string also apply to the mykindofstring object. The method in it is just a new name for a combination of the two existing string methods, but you can make it do whatever you want, such as a method to convert the time to a formatted string.
It's basically just extending the current Ruby class with native methods instead of using functions / helpers.
Main board
Based on Jan's comment on this answer and his own answer to the above question, I played around with Rails a bit and came up with adding this to application_helper.rb:
module ApplicationHelper
String.class_eval do
def all_caps_and_reverse
self.upcase.reverse
end
end
end
Now, just call the method on any line in your application:
@string = 'This is a string.'
@string.all_caps_and_reverse
.gnirts a si sihT
a source to share
You won't find an easy way to do this in your application for all (or even most) helpers.
Helpers are structured as modules to be included in view rendering classes. In order to use them you will need to keep a copy of the ActionView (not a HUGE deal, I suppose).
You can always open classes and list each helper you want:
class Time
def time_ago_in_words
ActionView::Base.new.time_ago_in_words self
end
end
>> t = Time.now
=> Tue May 12 10:54:07 -0400 2009
>> t.time_ago_in_words
=> "less than a minute"
(wait a minute)
>> t.time_ago_in_words
=> "1 minute"
If you take this approach, I recommend caching the ActionView :: Base instance you are using. Or, if you don't want to go for such a heavyweight, you can create your own class to just include the helpers you want (you don't want to include helpers directly in every time, date, string, etc., Class, though, since this clutters the method namespace pretty rigid - and contradicts the natural names you want).
a source to share
If you are using Ruby 2.1 you can use Refinements. They are an alternative to the ruby monkey protection classes. take @Jarrod monkey-patch example:
module MyString
refine String do
def all_caps_and_reverse
upcase.reverse
end
end
end
Now you can use this on any object you want:
class Post
using MyString
end
@post.title.all_caps_and_reverse
You can read more about Clarifications here.
a source to share
Make a model, TimeAgo or something similar. Implement the following idea here: code snippet to "mark" numbers
Then, in your controller, create instance variables using this class. Then, in your opinion, call@from_time.in_words
a source to share