0

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:...

Ian Ellis
  • 471
  • 4
  • 16

3 Answers3

2

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.

max pleaner
  • 23,403
  • 8
  • 50
  • 99
  • I think this is what I'm looking for, thank you for your help. However, does this mean I have to define crf_status(crf) in each model that I'm trying to retrieve a status from? Thanks again. – Ian Ellis Sep 12 '16 at 22:03
  • there are ways to share a method between your models without copying and pasting it. Please see [this question](http://stackoverflow.com/questions/14541823/how-to-use-concerns-in-rails-4) about "concerns" in Rails. More generally / outside Rails you can use a module or inheritance. – max pleaner Sep 12 '16 at 22:05
1

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)

arieljuod
  • 13,456
  • 2
  • 19
  • 31
0

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

Andy Gauge
  • 1,313
  • 1
  • 11
  • 24
  • Thank you for your response. I get an error that says 'nil is not a symbol'. Do I need to cast this in some other way? – Ian Ellis Sep 12 '16 at 22:25
  • Can I see the crf_status() method you rewrote? The last line is returned, if there's any sort of code after the case/end block it will be returned instead. You can shortcut this by using `return case crf.class` It is also possible you don't want to running `.class` on crf. Are you sending instances and checking classes? – Andy Gauge Sep 14 '16 at 17:27
  • This type of comparison is not correct. Case statements in ruby use the `===` method for comparison. You can't use that method to compare classes. For example: `String === String # false` and `String === "" # true`. The correct solution is to use: `case crf` (i.e. don't check the class). – Kevin Sylvestre Sep 26 '16 at 23:56