Rails returning full object instead of integer - ruby-on-rails

Rails's pluralize method was not working like I wanted (words not in english) so I set out to try my own solution. I started out simple with this method in ApplicationController:
def inflect(number, word)
if number.to_i > 1
word = word + "s"
end
return "#{number} #{word}"
end
And called it as such in my view:
<% #articles.each do |article| %>
<%= inflect(article.word_count, "word") %>
<%= inflect(article.paragraph_count, "paragraph") %>
...
<% end %>
But this got me:
undefined method `inflect' for #<#<Class:0x3ea79f8>:0x3b07498>
I found it weird that it referenced a full-fledged object when I thought it was supposed to be just an integer, so I tested it on the console:
article = Article.first
=> (object hash)
article.word_count
=> 10
article.word_count.is_a?(Integer)
=> true
So I threw in a quick words = article.word_count.to_i, but it doesn't throw a TypeError, it actually doesn't do anything, and still returns the same error: undefined method ``inflect' for #<#<Class:0x3ea79f8>:0x3b07498> in reference to the `inflect(article.word_count, "word") line.
Then I thought maybe inflect was already a Rails method and it was some sort of naming conflict, but doesn't matter what I change the method's name to, it keeps giving me the same error: undefined method ``whatever' for #<#<Class:0x3ea79f8>:0x3b07498>
I then tested it on the console and it worked fine. What's going on?

Put your inflect method in ApplicationHelper, not ApplicationController
by default all code in your helpers are mixed into the views
the view is its own entity, it is not part of the controller, when a view instance gets created (automatically when your controller action executes) it gets passed any instance variables you define in your controller action, but does not have access to controller methods directly
NOTE: you can define methods in your controller to expose them to your views by using the helper_method macro - see this post for more info on that - Controller helper_method
but in general you would define the view helper methods in the helpers classes and not in the controller

Related

Ruby on Rails: User helper method to read attribute

I'm trying to use a helper method to determine the value of an attribute for several records. Here is the basic function I am trying to get working (from the view):
<% if Baseline.where(subject_id: sub.subject_id).first.crf_status(crf) == 1 %>
<td bgcolor="#98FB98" >
<% else %>
My helper function is crf_status(crf), and it looks like this:
application_helper.rb
def crf_status(crf)
case crf
when Baseline then 'baseline_status'
when FollowUp3Week then 'follow_up_3_week'
...
end
end
So a working example would be if crf_status(Baseline) would return:
<% if Baseline.where(subject_id: sub.subject_id).first.baseline_status == 1 %>
<td bgcolor="#98FB98" >
<% else %>
Right now, the error is 'undefined method 'crf_status' for Baseline'. So based on what I've read, perhaps I have to reference ApplicationHelper in each controller? That doesn't sound right. Please let me know what you think.
Thanks.
edit. I forgot to make this more clear: crf_status(crf) is being passed an object from an array [Baseline, FollowUp3Week...].
The actual line starts with it as well -> if crf.where(subject_id:...
When you do method chaining like .first.crf_status(crf) you don't get a fresh global scope every time. I.e. to get this example to work your crf_status would need to be defined as an instance method on the Baseline model.
From a MVC design perspective, it's frowned upon to do database queries (i.e. where) from your views; you should do it from the controller instead. The choice to use helpers here is totally optional. By putting it in a helper all you're doing is making it inaccessible from code outside your views.
To cut to the chase, here's what you should write in your Baseline model file:
def crf_status(crf)
case crf
when Baseline then baseline_status
when FollowUp3Week then follow_up_3_week
end
end
Note that the baseline_status and follow_up_3_week are actually method calls with the implicit receiver self.
You are calling "crf_status" on an instance of a model, helpers can only be called on views and controllers.
You have to do something like this
<% if crf.where(subject_id: sub.subject_id).first.send(crf_status(crf)) == 1 %>
<td bgcolor="#98FB98" >
<% else %>
Anyway, that looks like a weird code smell (making queries on view is not right and that crf_status looks like something that you should move inside your models)
If you want to return a method that is to be called in the context, use the .send method.
Baseline.where(subject_id: sub.subject_id).first.send(crf_status(crf))
Whatever is returned from your method will be executed. This is a great metaprogramming example. You want to test against the class of the instance, so use the .class method on your case line. You'll want to return symbols not strings though, so do this:
def crf_status(crf)
case crf
when Baseline then :baseline_status
when FollowUp3Week then :follow_up_3_week
else :default
end
end
Edit: Changed case for type comparison

How to call a helper method in view

I have this code in my helper:
def app_state(app)
state = app.operator_apps.map(&:is_production)
if state.include?(true) and state.include?(false)
"Sandbox/Production"
elsif state.include?(true)
"Production"
else
"Sandbox"
end
end
and in my view I have done this:
<%= app.app_state %>
I get the following error:
ActionView::Template::Error (undefined method `app_state' for #):
Please help me out in solving this.
The error you are getting will persist unless an app_state method is defined somewhere within app's model.
Try calling the helper method like so:
<%= app_state(app) %>
Now the app object is being passed in as the argument of the app_state method, instead of the method being called on the object itself.
Hope that helps!

Helper method with the same name as partial

I have a helper with a method named search_form like this:
module Admin::BaseHelper
def search_form(*args)
# my great code here
end
end
To call this method in my HAML code, I can do this:
= search_form
= search_form()
= search_form(param1: "value1", param2: "value2"...)
My problem is with this first call. When I do this in any HAML file, it renders my helper. Except if my file name is _search_form.html.haml. Is that case, it returns nil.
If I put a raise error in the helper, I notice that my method isn't being called, but I am not able to find what is being called and why.
If I use the syntax on the second and third lines, it works as expected by calling my helper method.
So my question is: is this standard Rails behavior or a bug?
By default, Rails will look for a local variable with the same name as your partial, which may conflict with existing method names.
One way to get around this is to simply redefine the method inside your partial:
<% search_form = self.search_form %>
# Rest of the partial's code

undefined method `fragment_for' for nil:NilClass on render partial with cache

I have this piece of code in a partial on some code for rails 2.3.14:
<% cache "some_partial_#{some_id}" do %>
....
<% end %>
Works fine when rendering it in a view but I get:
undefined method `fragment_for' for nil:NilClass
when I try to do this in a model:
ActionView::Base.new("app/views").render(:partial => "home/temp"}
I can see the issue occuring in actionpack-2.3.14/lib/action_view/helpers/cache_helper.rb:35
def cache(name = {}, options = nil, &block)
#controller.fragment_for(output_buffer, name, options, &block)
end
I'm not sure what exactly it expects to find in #controller.
In short: don't render partials from models - they should contain only business-logic. Error occurs since cache invokes controller object which you don't have initialized since you are bypassing view rendering logic here.
UPDATE:
The only way i see it is to get controller instance and pass it as param. How to get controller instance inside model is up to you. I think this question could be helpful Try
ActionView::Base.new("app/views", {}, #your_controller_instance).render(:partial => "home/temp")
You may be able to add:
include ActionController::Caching
to your class.

Rails: method defined in application_helper.rb not recognized by categories_controller.rb

More newbie issues.
I understand that if I define a method in my application helper, it is available to the entire app code.
In my applicaton helper, I have:
def primary_user_is_admin
if current_user
user_login_roles = JSON.parse(current_user.role)
if user_login_roles["admin"]
return 1
end
end
return nil
end
If I call it from the categories_controller:
if !primary_user_is_admin
redirect_to root_url
end
I get an error message: undefined local variable or method `primary_user_is_admin'
This also happens if I put the primary_user_is_admin code in the registrations_helper.rb file
However, if I use it in any of the views (views/user/edit.html.erb for instance)
<% if primary_user_is_admin %>
<% end >
then it works. What am I missing?
Helpers are not included into a controller by default. You can
include ApplicationHelper
To gain access to the methods in the ApplicationHelper module. The previous discussion has a bunch of useful solutions for accessing helpers in controller.
Methods defined in helpers are only available to views by default. You have to 'include ApplicationHelper' in the applications controller to get access to this method in the controllers.

Resources