Caching a method that's got conditional output - ruby-on-rails

I have a method with is used to render news articles on every page. If you aren't on a care home page then it won't render specific news to that home. It's quite a heavy thing to process on every single page request. I was wondering if anyone could come up with a good way of caching it.
def articles_to_show
#articles = #care_home ? #care_home.news_items.latest.translated.limit(4).includes(:images) : Refinery::News::Item.latest.hidden_from_main.translated.limit(4).includes(:images)
end
Here's the view template - http://pastebin.com/BAmgSZia
I've tried fragment caching it, but then I realised every now and then if it expires and the first request is that of a care home, then the news articles will be populated incorrectly.

You have ugly code already so it wouldn't be very bad to make this code a little more obfuscated ...
#articles = lambda { YOUR_HEAVY_CODE }
Then in your view you're:
- cache do
- #articles = #articles.call
= #rest of the partial
Thing worth mentioning is that, if you can't use any embedded and suggested way of caching, it's probably something wrong with your design.
Relating your action instance variable with other instance variable probably initialized in some filter is bad design example.
#articles_to_show it's bad example of REST approach, you can instead do something like:
class Articles::CollectionController with show method
Don't use ternarny operator, probably at all, it's so obfuscated. ?: syntax is proper only for very easy statements.
Don't place so much to the controller, try to delegate your methods, scopes by merging them into another method - it would be cleaner

Related

Is it possible to cache a variable from a action after html.erb renders for css.erb? Rails

I have this action
class SearchesController < ApplicationController
...
def results
#users = User.search(params[:name], params[:gender])
render 'user_results'
end
...
The problem is rails is running back through this code again for when it renders my user_results.css.erb which is giving unsuspecting results as the params appear not to be available and I dont want to run back through the code again anyway.
I tried to cache the #users variable by doing #users ||= User.search(params... but it runs back through the code anyway and my search method which is giving different results than when it first runs through for the user_results.html.erb when the params are available.
How can I solve this?
Why are you running this code from user_results.css.erb at all? Is it necessary? Because this is a code smell to me. If you need to render custom CSS here then simply use a separate action, don't call it results call it user_results and the two calls will be separate.
The thing to remember is that there are actually 2 different web requests, one to the HTML and one to the CSS. They could be called individually in some cases, not necessarily tied together for all browsers, and that is why this is a code smell to me is you're blurring the lines between the two in your controller.
For more clarity, try adding this to your method:
logger.info "#{request.format}"
You could also enable/disable things based on the format:
if request.format.html?
If you are doing something like assigning a different color to each user in your CSS, then I would just move that into your HTML as an inline <style> block, and then you won't need any sort of caching. I'm 99% sure caching is not the right thing here.

ERB scope: instance variable vs locals

Suppose I have a controller with an index action and I would like to pass some data into the action's view (index.html.erb).
Typically, the rails way is to do: #some_var = some_value.
Does the above add to the global scope in a sense that #some_var is now available everywhere (helpers, etc...)? If so, is it better to do something like locals: {some_var: some_value} instead?
What are the tradeoffs if any?
Yes, #some_var is "global" in the sense that it added to the view context that is shared by views and helpers. So your controller, view, partials, and helpers can all access the same #some_var.
Using instance variables like this is the Rails convention: they just appear "like magic" in your views and helpers with no additional code. For simplicity, I would recommend it for most projects, especially as you are starting out.
However as your Rails app grows, here are some other best practices to keep in mind:
Try to limit your controller to assigning only one instance variable. If you find yourself assigning many instance variables in a single controller action, that is usually a sign that your controller is trying to do too much.
Avoid using instance variables in helpers. This makes your helper methods harder to reuse in other contexts, because they depend on instance variables being set "just right" by the controller. This can be a source of bugs.
Yes, I think you've got it. I prefer local arguments like that too, even though you're right Rails suggests you use instance variables.
I think instance variables may be okay when they are in a full template (never in a partial), and especially when you only have one of them that has the same name as the controller or whatever.
But in general, I agree with you. There is no downside to doing it with locals, unless you consider it confusing code, perhaps it would confuse someone who expects it to be the 'ordinary' Rails way.
I guess someone could argue that Rails templates are inherently tightly coupled to the controller, are meant to be that way, so it's no big deal to use instance variables -- the main negative of which is that it tightly couples your template to a particular controller implementation, but, they'd say, that's fine. I guess that's an opinion, apparently it is Rails' opinion! Certainly plenty of apps are written that way, and it's fine.
But there isn't really anything that's going to get in your way from going against the typical Rails way of doing things, and using local template arguments instead of instance variables. It works fine. I've done it.
I think you are right to be willing to question Rails -- sometimes Rails has made some odd choices. And also right to be cautious about going ahead doing something differently than Rails seems to wants you to, sometimes it does cause problems. In this case, I don't expect it will.
Nope, if you add #some_var = some_value to the index action then it will only be applied to that action. If you want to create a global action then you can apply it to the application.rb.
I highly recommend reading http://guides.rubyonrails.org/action_controller_overview.html
but something that might interest you in specific is this:
4.4 default_url_options You can set global default parameters for URL generation by defining a method called default_url_options in your
controller. Such a method must return a hash with the desired
defaults, whose keys must be symbols:
class ApplicationController < ActionController::Base def
default_url_options
{ locale: I18n.locale }
end end
These options will be used as a starting point when generating URLs,
so it's possible they'll be overridden by the options passed in
url_for calls.
If you define default_url_options in ApplicationController, as in the
example above, it would be used for all URL generation. The method can
also be defined in one specific controller, in which case it only
affects URLs generated there.

What's a good way to create subjective views in Rails?

I'm wondering what's the Rails Way™ to build a "subjective" view, that is, a view who's output is changed based on a provided param.
E.g., consider the following contrived case:
A user asks for a article in a blog app. In the request, she's sending the param time_zone=(-)1, to indicate that her timezone is -1. The app should then take that param, and in the output return the article and the time of the creation of that article - converted to her timezone.
The rationale for this server-side processing approach, is that it would be easy to change the client view, in particular if one's accessing the rails app through an exposed (mobile) api. (Mind you, it is no problem to handle the before-mentioned case with a client side script etc., but I'd like to avoid that.)
Somehow, I feel that this could easily belong to the model, had it not been for the fact that params isn't available there. I suppose that is for a reason. Controller is, from my point of view, definitely not the place to stick this.
The question is: should I stuff this logic in the view?
This logic would probably fit best in a subclass of your controllers. This subclass could define a filter method that sets appropriate instance variables, which you then reference in your view. I linked to a question that does exactly that for your timezone example (albeit without numeric timezone offsets, but the idea is the same). An alternate example might be say a different UI based on a URL parameter. In that case I might do something like this:
class ApplicationController
before_filter :set_ui
private
def set_ui
if params[:ui] == 'blah'
#ui = 'blah'
else
#ui = 'thing'
end
true
end
end
Then in your view you could have something like this:
<%= render :partial => #ui %>
Then any controllers that you create as subclasses of ApplicationController will have this functionality

Is it poor style to hide instance variables in filters?

I see this all the time in rails code:
before filter :get_post, only: [:edit, :update, :destroy]
def edit
# code .......
end
def update
# code .......
end
def destroy
# code .......
end
private
def get_post
#post = Post.find(params[:id])
end
I understand it keeps from repeating the same line of code three times, but isn't there a much easier to read and better way to accomplish the same thing by just refactoring the code into a private method without hiding the instance variable and the before filter?
private
def get_post(post_id)
Post.find(post_id)
end
Then you can keep the instance variable in the action
def edit
#post = get_post(params[:id])
end
It doesn't make sense conceptually to hide instance variables in private methods. Why is this so prevalent in rails?
You'll probably see a lot of differing opinions on this particular practice. Personally, I agree with you; I feel that the strict adherence to DRY only serves to introduce a bit of confusion by hiding away the instance variable in a method down at the bottom of the file. I'm not even sure a get_post method really buys you very much myself. That said, I tend to prefer explicicity (if that's a word) over terseness in many cases, and for a team that uses this trick consistently in every controller, there may not be as much (if any) confusion.
If the filter encapsulates a little more complexity, it may be worth it. For example, the popular authorization library CanCan by Ryan Bates introduces a controller class macro called load_and_authorize_resource that installes before_filters such that RESTful resources are automatically loaded and authorized for access against the current user. This could potentially be more than a line or two per method, and would not repeated verbatim each time.
There's a popular middle-ground that some Rails devs use, which is to specify the before_filter as a block, so you don't have to repeat yourself, but the instance variable is up at the top of the file:
before_filter(only: [:edit, :update, :destroy]) do
#post = Post.find(params[:id])
end
As mentioned in the comments, there are some gems, like decent_exposure, which help formalize this pattern and make it significantly less "hidden" or confusing (since it's declared explicitly and the API is known).
As other have stated, this is a matter of opinion. I started off using before filters to hide common instance variables between controllers. I still do this on occasion, but I have found it to be a code smell that the controller is bloated. If you have so many instance variables that you need to hide them in filters you are doing too much. My opinion is that the controller action should be very small, and if all the actions have one line in common that handles the instance variable its not an issue.
My remedy for getting my controller actions to a level that made me comfortable with having a common instance variable was to use service object/presenters to consolidate the information that was once in several instance variables into a single instance variable per controller action. I've found this been a huge improvement in the readability and maintainability of my controllers and views.
I think your suggestion is an improvement over the typical Rails convention. I'd take it a step farther, though and drop the instance variable entirely. It's misleading to use an instance variable because it's typically not representing shared state across multiple controller actions, and often not even shared between any methods (except the before filter that assigns it).
The Rails convention of populating an instance variable for use in a view is a bad idea whose use should disappear. In a clean MVC implementation, nothing from the controller would be available in the view. I think this is a much cleaner implementation which properly separates the layers:
def edit
post = get_post(params[:post_id])
render 'edit', locals: { post: post }
end
No leaking state, no reliance on an instance variable in the view, explicit intent, and a view that is more easily reusable and can be rendered from within other views without injecting a hacky inline instance variable assignment.
Rails has a number of conventions that make for quick bootstrapping, but they don't make for good code.
It's a convention over configuration thing.
Neither approach is wrong, but people who like Ruby often prefer minimalist coding patterns, and value using the quickest approach to solve a problem so they can get on to solving more complex and interesting problems
If one has seen this approach used often enough, it becomes second nature to have all of the member functions (obviously not the collection functions) preload the member variable for you before you define your code. It gets to the point where specifying the member variables in the function does not create a significant clarity advantage (if any).
There is also another approach using helper_method. It's looks almost as you want it.
http://rails-bestpractices.com/posts/57-substituting-before_filter-load_object

Ruby on Rails controller and architecture with cells

I decided to try to use the cells plugin from rails:
http://cells.rubyforge.org/community.html
given that I'm new to Ruby and very used to thinking in terms of components. Since I'm developing the app piecemeal and then putting it together piece by piece, it makes sense to think in terms of components.
So, I've been able to get cells working properly inside a single view, which calls a partial. Now, what I would like to be able to do (however, maybe my instincts need to be redirected to be more "Rails-y"), is call a single cell controller and use the parameters to render one output vs. another.
Basically, if there were a controller like:
def index
params[:responsetype]
end
def processListResponse
end
def processSearchResponse
end
And I have two different controller methods that I want to respond to based on the params response type, where I have a single template on the front end and want the inner "component" to render differently depending on what type of request is made. That allows me to reuse the same front-end code.
I suppose I could do this with an ajax call instead and just have it rerender the component on the front end, but it would be nice to have the option to do it either way and to understand how to architect Rails a bit better in the process.
It seems like there should be a "render" option from within the cells framework to render to a certain controller or view, but it's not working like I expect and I don't know if I'm even in the ballpark.
Thanks!
How would the cell know in which controller it is rendered? This would break encapsulation.
You can use #render_cell in your controller view and maybe put some decider around it? Is that what you're asking for?

Resources