Rendering a counter in the view. What's the proper way?

Hello there ppl.

I want to have a counter which will:

  • count the number of times that the ‘index’ controller action is called and store this in a cookie
  • will be displayed in the view only if the times visited is greater than 5
  • it must look like this: “You have visited this page 1 time” or “You have visited this page 2 times” (I know I can use the pluralize() method for that) We always say that the views must know as little as possible regarding the logic & data of our apps, so I guess the ideal way is to just render it through a <%= @counter %> piece of code. Right until here? I proceed,

in my ApplicationController I’ve added these methods regarding the counter:

def increment_counter session[:counter] ||= 0 session[:counter] += 1 @times_visited = session[:counter] end

def reset_counter session[:counter] = 0 unless session[:counter].nil? end

def display_counter if @times_visited > 5 @counter = “You have visited this page " + pluralize(@times_visited, " times”) end end

The last method is what I’m interested in right now. It gives me a undefined method `pluralize’ for #StoreController:0x007fdbab23c9c0 error cause I’m trying to access a helper from a controller and not from a view. Correct me if I’m wrong.

So I could hard-code the IF statement into the view but this would break the rules of MVC architecture, right? So what would be the best way to do this?

Where are you calling display_counter from? I would have expected it to be from the view, in which case put the method display_counter in a view helper not in the controller. Pass it the value @times_visited. So in the view: <%= display_counter @times_visited %>

Colin

No I'm calling it from the controller, should I call it from the view? If I try to call it from the view like this: counter_display I get

Please don't top post, insert your reply into the previous message at appropriate points. As you have done it here no-one reading this knows what question you are answer "no" to. Thanks.

undefined local variable or method `show_counter' for #<#<Class:0x007fdbab80c7b0>:0x007fdbab813f10>

There seems to be some confusion over what the method is called. In your original post you called it display_counter, above you have called it counter_display, but the error message appears to be for show_counter. Whatever you call it you have not noted my other point, that it should be in a view helper not in the controller. In addition, which I did not say, being a view helper it should return the string to be displayed directly, so something like def display_counter( visits )   "You have visited this page " + pluralize(@visits, " times") if visits > 5 end

Put it in application_helper.rb (not application_controller) if you need it from views for multiple controllers, or the appropriate view helper file if just for one controller.

Read up on view helpers if you need to.

Colin

I’ve done it as you said, in the helper. So this is how it should be done? Is there a general rule for deciding whether a function like this should go into the controller or into the helper?

My code in the helper:

def display_counter “You have visited this page " + pluralize(@times_visited, " time”) if @times_visited > 5 end

and then calling that function in the view. Would it be better if this wasn’t a function but a simple variable like this?

@counter = “You have visited this page " + pluralize(@times_visited, " time”) if @times_visited > 5

Thanks a bunch for your help so far!

I've done it as you said, in the helper. So this is how it should be done? Is there a general rule for deciding whether a function like this should go into the controller or into the helper?

My code in the helper:

  def display_counter     "You have visited this page " + pluralize(@times_visited, " time") if @times_visited > 5   end

Try this:

  def display_counter(times, limit = 5)     "You have visited this page #{pluralize(times,'time')}" if times > limit   end

in the view:

  <%= display_counter @times_visited %>

and then calling that function in the view. Would it be better if this wasn't a function but a simple variable like this? @counter = "You have visited this page " + pluralize(@times_visited, " time") if @times_visited > 5

It depends on where you want to go to edit this code. It generates HTML, so it (fairly categorically) should not be in the controller.

Walter

I presume you mean to put this code in the controller. That is not desirable because then you are formatting data for display in the controller. The controller should make the data available to the view (@times_visited in this case) and then the view decides how to format it. So in answer to your question about a general rule the controller makes the raw data available, the view formats it.

Colin

This makes sense. But on the other side, if I put this method in the helper (as I did) then I’m embedding business login in the helper (if > 5), am I not?

It’s more a logical question. Is the “if visits are more than 5 display the counter” business logic or not? Which part of the framework should be responsible for deciding if to render the counter?

Well I did something like this now, just in order to let the controller decide with how many visits the counter will appear.

Controller:

def increment_counter session[:counter] ||= 0 session[:counter] += 1 @times_visited = session[:counter] @min_times = 5 end

Helper:

**def display_counter “You have visited this page " + pluralize(@times_visited, " time”) if @times_visited > @min_times end

then on the view I just use <%= display_counter %>. Isn’t that better than before? Now the @min_times is defined in the controller, isn’t that the best place for it to be decided? I guess I should write it in another method like counter_settings but for now it’s ok I think.

I am not sure that is business logic. If it were it should be in a model not the controller. Is it not the specification of the human interface? There are always grey areas however, the real world does not conform to the MVC concept so there will always be judgments to be made. As long as the basic structure is ok then small details do not really matter. Go with whatever you are most comfortable with.

Colin