Rails expire fragment cache from models - ruby-on-rails

I am working with cache in my Rails project and wants to expire the cache of a particular url. I got the following command to expire fragment corresponding to the URL passed:
ActionController::Base.new.expire_fragment("localhost:3000/users/55-testing-devise/boards/")
I am confused where to put this code in my Rails project so that it gets executed as soon as the url in a text field is added and expire button is clicked.

You should probably consider a different approach. Models should not be concerned with how caching works, and traditionally the whole sweeper approach tends to become complex, unwieldy and out of sync with the rest of the code.
Basically, you should never have to expire fragments manually. Instead, you change your cache key/url once your model is updated (so that you have a new cache entry for the new version).
Common wisdom nowadays is to use the Russian Doll Caching approach. The link goes to an article that explains the basics, and the upcoming Rails 4 will contain even better support.
This is probably the best way to go for the majority of standard Rails applications.

The ActionController::Caching::Sweeper is a nice way to do this, its part of Rails observer.
http://api.rubyonrails.org/classes/ActionController/Caching/Sweeping.html
class MyModelSweeper < ActionController::Caching::Sweeper
observe MyModel
def after_save(object)
expire_fragment('...')
end
end

The expire_fragment won't work, as you don't have the digest added to the key. See DHH here: https://github.com/rails/cache_digests/issues/35
I've posted a related answer about caching a json response: https://stackoverflow.com/a/23783119/252799

Related

Does Rails cache templates?

I assume yes, and some tests point to yes, but I'd like to find the documentations that says Rails caches (unevaluated) templates in memory.
That is, for any .erb, .sass, .jbuilder, etc template Rails will:
read the template from file, only once
retrieve the template from memory when needed
apply data to the template on every invocation unless the generated output is cached.
All template/cache searches and documentation seem to be focused on point #3. And development Rails flags enable/disable class-caching. But finding docs that verify claim #1/#2 seem illusive. Does Rails re-read template files every time and rely on OS level file caching?
I had the same question and did some hunting.
Yes - Rails caches templates.
Pay attention to the fact cache takes locals as key, and I would say then that unevaluated template is not cached.
In production, it will if you add calls to do so. It is disabled in development mode however you can edit it to cache in development as well in your config/environments/development.rb file by changing the following line from false to true:
config.action_controller.perform_caching = false
In production, Rails has 3 main methods of caching when it comes to views (there are also rails methods for db caching). Page-caching, action-caching, and fragment-caching. To implement these in Rails you can use certain helpers like the caches_page macro-style method in a controller. This will cache the view in its entirety to disk with no further involvement by the Rails dispatcher. This should only be used when the page is completely static and has no dynamic content. caches_action and fragment caching provide much more fine-grained implementations and probably make up the majority of use-cases in Rails.

Does Rails automatically keep track of page name changes and create 301 redirects?

Is there a gem that does this or is it baked in even? My understanding of things now is that anytime I change the path for a page (perhaps I do it for SEO) I need to create a new 301 redirect. Shouldn't this be handled automatically?
If so, how is it handled automatically and how do I make use of it?
Thanks!!!
This is a rather complicated question and depends a lot on how your routes look right now. The simple answer is: no, this is not handled automatically by Rails.
The easiest way to handle redirects like this is in the routes.rb file, redirect from the old path to the new one. Something like this:
match '/products/:id', redirect("/new_products/%{id}")
You can learn more about this kind of redirect at the Rails routing guide.
If you're changing just one instance of a product, then obviously this doesn't make a whole lot of sense -- you can't do this for every changed object. In this case, we'll assume that you aren't using standard ID-based routing; if you were then this wouldn't be a problem. (Since the ID wouldn't change, instead it seems likely your routing is based off of a field that does change -- like name or date or something.)
In this case, you probably want to extract the routing field away from whatever it currently is and into a slug column of its own. I recommend the really excellent friendly_id gem for this: it allows you to automatically generate slugs, and its history module allows you to perform lookups with an object's old slug.
Then, you can look up an object by its ID, its slug, or any of its old slugs, if you find it necessary to do so.

Rails security concerns

I am a Java programmer mostly, and it's actually amazing that we don't have to worry about a lot of security concerns that php or even rails developers have to worry about. We have to worry about them, but I think our job is actually a lot easier. You just use Java (already big bonus points there) and use Spring with Spring security... and you're basically done. Java and servlets are actually really good in this respect.
Now that I'm working in Rails, I think the biggest security concerns that I am the most scared of are parameters - both in the ones that are coming from the controllers (since they dynamic hashes, unlike in SpringMVC) and having to include more hidden values in forms.
But that got me thinking - you really have to be careful what you accept when you create new models or even update models. If you just blindly pass in parameters to your models, bad things can happen. In fact, things like the user role and stuff could be changed if you're not too careful.
It's almost like I want to write the setter code by hand to make sure it's not overwriting something that it shouldn't. And even if there's a framework mechanism to handle this... I would still want to test every risky model attribute just to be extra sure that it won't get overwritten on a create and on an update.
As much as Java gets a bad rep about productivity, it feels like it handles this stuff a lot better.
Anyway, my question is - what is the best resource/tips/advice for dealing with common security pitfalls/concerns/gotchas using rails - especially geared towards a Java/Spring developer who got used to working in a more stateful environment.
Even better, what would be a good checklist to go through every once in awhile?
And last, what tests would you recommend to make sure things are solid?
At least for your concern about assigning data to your model objects without proper checking, look into the attr_accessible declaration; it allows only specified attributes to be assigned via the bulk assignment:
user = User.new(params[:user])
user.approved = params[:user][:approved]
user.role = params[:user][:role]
You might find the entire 27th chapter of the Ruby on Rails 3rd edition book useful. (I haven't updated my 4th Edition book yet, not sure which chapter to recommend from the newer book. :)
I don't use ActiveRecord (I use DataMapper), but as a rule, I never do mass-assignment and I always expressly pass only the attributes I want to change. Rails 3 defaults to escaping all content in your views, unless you expressly output that data raw into into the .erb.
Also, it really bugs me that ActiveRecord doesn't help you out very much if you need to drop down to using SQL for something. You have to escape input yourself, which can expose you to the risk of human error allowing arbitrary SQL to be executed in your queries. DataMapper's underlying DataObjects connection supports prepared statements out of the box and in fact, it would actually require more work to avoid using them.
Rails 3 does have CSRF protection turn on by default too. It also makes session cookies HTTP-only by default, which makes them harder to steal via JavaScript.
I actually think, aside from Rails encouraging the use of mass-assignment, you're pretty well-covered for security.

Page caching using delayed job

Hey all, if you've ever posted on [craigslist], this question should make sense to you. Whenever you post a listing (to sell furniture or an apartment, for example), your listing is not immediately thrown up on the site. Rather, listings will appear in batches (numbers vary) about every 10-15 minutes. At first I was really over-thinking this behavior, trying to hold records and then do mass inserts, but I realized it was much simpler. After talking with some colleagues, it made sense that Craigslist is caching their pages and then emptying that cache every 10-15 minutes. This severely decreases the load on their database.
Now, to my question. How do I accomplish the same thing in Rails? I know how to implement caching - I've read the [caching with Rails guide]. I will be using action caching and fragment caching (because I can't cache the whole page). I still need to do validations and access controls, so I can't fully cache the page...
To accomplish timed page caching you can utilize the standard Rails' caching plus a little timed cleverness.
First you want to determine your level of caching. You've got three options:
Page Caching - caches the whole page but subsequent requests don't go to through Rails stack. So if this is a Craiglist-esque page that will be hit thousands of times a second this request will only go to your webserver (e.g. apache) not Rails or your db making it much faster. The trade off is that you lose authentication, session variables, etc that Rails provides.
Action Caching - caches the whole page but brings the request into Rails so that it can execute any filters associated with that action.
Fragment Caching - caches a segment of the page, essentially bypassing the need to execute the code with in the block (and any consequential calls to the DB).
Then you'll need pick the appropriate level of caching and implement it in your app (check out the links above for implementation examples).
Once you have implemented the caching you now have to figure out a way to expire the cache. I can think of two ways to do this, both come with benefits and drawbacks. For now let's assume you've chosen to use action caching.
Reliable but more involved - create an action within your controller that expires the cache and a cron job task that makes a request to that action. I've asked a similar question that addresses this 'built in' scheduled task. For security precautions, you may want to include a generated hash or something similar so that someone can't manually expire your cache by going to '/products/expire_cache'.
class ProductsController < ApplicationController
caches_action :index
def index
# implementation
end
def expire_cache
if params[:verification_hash] == 'sa89sf8sfsfehiwaf89yfea98fh'
expire_action :action => :index
end
end
end
Unreliable but easier - simply expire the cache in your action with an arbitrary conditional. This implementation assumes that there will be enough traffic to regularly ensure that someone will come to your site on the 0, 15, 30, and 45 minutes. You could decrease this interval in order ensure that the cache will be reset at a more probable interval.
class ProductsController < ApplicationController
caches_action :index
def index
# implementation
expire_action :action => :index if Time.now.min % 15 == 0
end
end
I think there are two approaches to what you want to accomplish:
Simply cache with fragments and actions so that on the first hit of the page the database is accessed and the page loads normally, but every subsequent hit is from the cached version. The major upside of this approach is that you don't need to deal with delayed jobs and rendering your pages outside of the regular flow of things.
Create a delayed job that renders your page or the individual fragments that get cached. During the rendering in the delayed job the page fragments will actually get cached as if a user were viewing them (provided you have implemented fragment and action caching normally). Once the delayed job is done, populate a column in your database that indicates that this record/page is ready for viewing.
Probably the easiest way for caching in Rails is using Memcached with :expires_in option.
You will probably need a VPS server to use it which could be expensive for smaller sites.
But you can do good time based caching even without memcached. This little SimpleCache snippet has worked wonders for my shared hosted sites.

Good idea to access session in observer or not?

I want to log user's actions in my Ruby on Rails application.
So far, I have a model observer that inserts logs to the database after updates and creates. In order to store which user performed the action that was logged, I require access to the session but that is problematic.
Firstly, it breaks the MVC model. Secondly, techniques range from the hackish to the outlandish, perhaps maybe even tying the implementation to the Mongrel server.
What is the right approach to take?
Hrm, this is a sticky situation. You pretty much HAVE to violate MVC to get it working nicely.
I'd do something like this:
class MyObserverClass < ActiveRecord::Observer
cattr_accessor :current_user # GLOBAL VARIABLE. RELIES ON RAILS BEING SINGLE THREADED
# other logging code goes here
end
class ApplicationController
before_filter :set_current_user_for_observer
def set_current_user_for_observer
MyObserverClass.current_user = session[:user]
end
end
It is a bit hacky, but it's no more hacky than many other core rails things I've seen.
All you'd need to do to make it threadsafe (this only matters if you run on jruby anyway) is to change the cattr_accessor to be a proper method, and have it store it's data in thread-local storage
I find this to be a very interesting question. I'm going to think out loud here a moment...
Ultimately, what we are faced with is a decision to violate a design-pattern acceptable practice in order to achieve a specific set of functionality. So, we must ask ourselves
1) What are the possible solutions that would not violate MVC pattern
2) What are the possible solutions that would violate the MVC pattern
3) Which option is best? I consider design patterns and standard practices very important, but at the same time if holding to them makes your code more complex, then the right solution may very well be to violate the practice. Some people might disagree with me on that.
Lets consider #1 first.
Off the top of my head, I would think of the following possible solutions
A) If you are really interested in who is performing these actions, should this data be stored in the model any way? It would make this information available to your Observer. And it also means that any other front-end caller of your ActiveRecord class gets the same functionality.
B) If you are not really interested in understanding who created a entry, but more interested in logging the web actions themselves, then you might consider "observing" the controller actions. It's been some time since I've poked around Rails source, so I'm not sure who their ActiveRecord::Observer "observes" the model, but you might be able to adapt it to a controller observer. In this sense, you aren't observing the model anymore, and it makes sense to make session and other controller type data information to that observer.
C) The simplest solution, with the least "structure", is to simply drop your logging code at the end of your action methods that you're watching.
Consider option #2 now, breaking MVC practices.
A) As you propose, you could find the means to getting your model Observer to have access to the Session data. You've coupled your model to your business logic.
B) Can't think of any others here :)
My personal inclination, without knowing anymore details about your project, is either 1A, if I want to attach people to records, or 1C if there are only a few places where I'm interested in doing this. If you are really wanting a robust logging solution for all your controllers and actions, you might consider 1B.
Having your model observer find session data is a bit "stinky", and would likely break if you tried to use your model in any other project/situation/context.
You're right about it breaking MVC. I would suggest using callbacks in your controllers, mostly because there are situations (like a model which save is called but fails validation) where you wouldn't want an observer logging anything.
I found a clean way to do what is suggested by the answer I picked.
http://pjkh.com/articles/2009/02/02/creating-an-audit-log-in-rails
This solution uses an AuditLog model as well as a TrackChanges module to add tracking functionality to any model. It still requires you to add a line to the controller when you update or create though.
In the past, when doing something like this, I have tended towards extending the User model class to include the idea of the 'current user'
Looking at the previous answers, I see suggestions to store the actual active record user in the session. This has several disadvantages.
It stores a possibly large object in the session database
It means that the copy of the user is 'cached' for all time (or until logout is forced). This means that any changes in status of this user will not be recognised until the user logs out and logs back in. This means for instance, that attempting to disable the user will await him logging off and back on. This is probably not the behaviour you want.
So that at the beginning of a request (in a filter) you take the user_id from the session and read the user, setting User.current_user.
Something like this...
class User
cattr_accessor :current_user
end
class Application
before_filter :retrieve_user
def retrieve_user
if session[:user_id].nil?
User.current_user = nil
else
User.current_user = User.find(session[:user_id])
end
end
end
From then on it should be trivial.
http://www.zorched.net/2007/05/29/making-session-data-available-to-models-in-ruby-on-rails

Resources