Cannot update models whith page caching - ruby-on-rails

I have a very fundamental understanding problem with Rails page caching mechanism.
On a rails 2.0.5 application, I use a lot of full page caching, everything's working fine, pages are served at great speed by apache.
So far all the content was handled in an admin section, the cache sweepers are working well.
But I have now opened the edition of some sections to the users, who don't go through the "admin" namespace, but the regular routes.
the problem is that it seems when the updates are performed, they don't go to mongrel with the "put" action, instead I just get the cached page (no action in the database, no flash message, no nothing....)
I feel like I missed something here, is it possible to use page caching with regular REST routes ? or do I have a problem with my mongrel/apache configuration ?
EDIT:
thanks for your answer, but it is not what I meant, the full page cache mechanism works fine, I have dedicated sweepers in the admin controllers that refresh the pages.
my problem is (or I think it is) that when the models are updated through the regular controllers, the form is submitted to the cached page and not to the appropriate action.
in the form
/pages/1-hello/edit
form should be posted with "put" method on "/pages/1-hello"
but I believe it goes to the cached page '/pages/1-hello.html' and is treated as a regular get....
When I submit the form, I just see the show action, no flash message, nothing updated.
then I erase the cached file in my public directory, submit the form again and suddenly everything works....
I read the cache section in "the rails way" and the tutorial on railsenvy.com but I never seen anything mentionned about cached pages that would bring such problems....

You're probably caching too much of the page. If you're caching the entire page and then altering the contents of that page via a put action, the page won't register the change unless you clear the cache explicitly.
If you have a copy 'The Rails Way', some great examples are given to expire pages so that they are reloaded on the next load.
More or less, you have to do something like the following within your create action:
expire_page :action => 'index'
You can also create an observer to observe your model and expire the cache without needing to explicitly call it after a create or update action.
Hope that helps.

Related

How should I be updating data with AJAX?

I am making a rails MVC style application.
When I am loading the pages, the controller gets the data from the model and forwards it to the view and the view renders itself.
However, I am wondering what the appropriate way is to interact between javascript and my database.
Say the user is playing a game and he has a bunch of clues on the screen. Clicking a clue will move it to his clue box and needs to update the UserClues model to add the clue.
The user also needs to be able to remove clues where the clue gets removed etc...
This needs to happen via AJAX.
I am wondering what the appropriate approach is.
Say he got his page from /Investigation which loaded the page using the InvestigationController.
Should the InvestigationController be the one calling the model to add/remove the clues, or should some sort of data controller do it.
For example:
/Investigation/AddClue
or another controller
/User/Clues/Add
The reason I am asking is I have a lot of data on my pages that is created/added/modified and I am wondering if my routes/controllers should be separate for data manipulation and pages, or if I should have a controller for each page and it also manages what the user can do on that page (like add / remove clues to and from a ClueBox)
Thanks
Ideally you will want to have a separate controller for each resource. Even though your current view is in the context of the Investigations, controller, dealing with clues should have its own resource. Otherwise things can get complex. If the clues are investigation specific, then you can scope them under the Investigation URL.
resources :investigations do
resources :clues
end
This will give you something like /investigations/1/clues/1, but this maybe not be necessary, and I would avoid nesting unless required.
The controller can then respond in the correct format. In this case Rails UJS provides you with the tools you need. You can render a create.js.erb or destroy.js.erb templates to handle user actions. The templates would have the required JavaScript and dynamic code to update the screen state.
If at all possible, try to use Rails' RESTful routes. In this case, that probably means line in your routes file that looks something like:
resources users do
resources clues
end
In your users/clues_controller.rb file, put in actions for the different AJAX requests, like create to add a clue to a user (via a POST request), destroy action to remove a clue from user (via a DELETE request, might need to simulate with POST + "_method=delete" parameter).

Does rails cache static pages and assets automatically?

I have been reading about caching and all the resources available out there, but I am just not sure if I need to use 3rd party add-ons like Memcachier in my app. Other than the front pages (Static Pages like Homepage, About, Contact Us, Terms, Privacy) all other pages require authentication and are all dynamically created. It's a small social networking app so the show page, index page, edit page are all dynamically created. The index action is constantly going to be updated.
I want to know if Rails will automatically cache my static pages and assets such as css, javascript, images? What kind of caching should I be using?
If what you call static page are HTML files located in your public folder, they are directly served by your web server (ex: Apache), and the request don't even go through Rails
If they are files located in your app/views controller, the request goes through Rails, and it could be a good idea to implement page or fragment caching. Know that you can cache just parts of the pages, this is called fragment caching, and it's useful for dynamic pages that have static parts.
Also, you can link a cache to a record, so first time the view related to this record is displayed, the cache is generated and used for the next requests. Then when you modify this record the cache is invalidated and the process start over.
You don't need cache for your assets, they are compiles and not interpreted by Rails anymore in your production environment.
There is so many things about caching, and you can do a lot of good to your application with it (or a lot of bad is used incorrectly) and I cannot cover it all, let me give you some pointers that will teach you a lot:
http://railscasts.com/episodes/387-cache-digests
http://railscasts.com/episodes/169-dynamic-page-caching
http://railscasts.com/episodes/93-action-caching
http://railscasts.com/episodes/90-fragment-caching
http://railscasts.com/episodes/89-page-caching

Rails redirect_to w/ post data (not get)

In rails, is there a way to execute a redirect that includes POST data?
I'm moving an old website to a rails app, and the old website has a lot of links pointing to a lot of non-DRY pages which use javascript, immediately upon loading, to post XML data to another website. Not very elegant (or portable).
Since the destination page is on another website (which we don't control), my options are limited, e.g. I can't just store the data in a cookie.
Sys: Rails 2.3.5, Ruby 1.8.6
(Yes, I could put a hidden form on each of the pages in question, and change the action of each of the links to submit the form data to the pertinent destination, but I would rather make fewer (and less irritating) changes, by catching the intermediate requests and executing a redirect from a controller.)
Short answer: yes, do redirect_to 'new url', :status => 307
Long answer: see http://ilia.ws/archives/152-Cross-Domain-POST-Redirection.html
Some people say it doesn't work, but I've just checked and it does in FF/Chrome/IE.

My Rails page_cache is attaching someone else's domain to links, how can I stop this?

Let me try to explain what the situation is as thoroughly as I can see it.
I have a Rails app using page caching for a specific part of the site. When looking through Google Analytics I noticed my own domain as a referrer which I found odd. Upon further investigation I discovered that when I go to one of those pages with my domain as the referrer, the links point to a different domain name but renders the page fine. For example:
I'm on domain.com. I go to domain.com/someones_profile, and when I hover over any links it will say anotherdomain.com/someones_profile/about. Now, if I click the link, it'll take me to anotherdomain.com/someones_profile/about yet it's my page being accessed on my server (logs verify it.)
I tested this out by taking one of my unused domain names and changing the A record to my server's IP. Then I cleared the page cache directory and visited a cacheable page using the domain I just set up, domain2.com/someones_profile, and it cached the links as domain2.com/someones_profile.
Hopefully this explanation made sense. The domain that is "infiltrating" my cache belongs to someone I don't know, and the intent doesn't seem in any way malicious, but I was wondering if Rails has any sort of built-in methods to circumvent something like this. Something like forcing page_cache links to use a specific domain rather than whatever the referrer domain is.
Any help would be appreciated, I would prefer to use built-in Rails method than to write something myself, maybe/maybe not because I'm lazy.
Ok, I think I get your problem. How about you create your links in pages with xxx_path instead of xxx_url? This should cause only relative paths to be generated. No domain names will be written into the html document. I would do a search and replace through the views for _url and change them to _path.
So for example, if you have this in your view:
<%= link_to "View all posts", posts_url %>
You'd change it to:
<%= link_to "View all posts", posts_path %>
By the way, this answer assumes that you're creating your links by using routes, not by putting a URL directly into the view!
Try using the refraction gem to redirect all accesses not going to a single defined host to that single defined host. This will also increase page ranking as it removes duplicate content:
# config/initializers/refraction_rules.rb
Refraction.configure do |req|
if req.host == "www.you-like-this-host.com"
# configure other redirects here
# eg. I'm using this to migrate URLs from the legacy application
# to this new application (eg when migrated from PHP to Rails)
else
# this is not your hostname, redirect!
req.permanent! :host => "www.you-like-this-host.com"
end
end
Refraction runs early in the rack stack so it's pretty lightweight. Apache's mod_rewrite won't work reliably with passenger for example, so I prefer refraction.
Be sure to load as a middleware plugin:
# config/environments/production.rb
config.middleware.insert_before ::Rack::Lock, ::Refraction, {}
Links with host name should never appear in the page cache. Are you sure you generate your in-site URLs without the host name? Maybe you tried to create unique links this way (by passing the current host name to link_to or url_for), better use refraction for that and cleanup your code.

Rails page caching with intra-page administration

I'd love to use page caching on a Rails site I run. The information on each page is mostly constant, but the queries that need to be run to collect the information are complicated and can be slow in some cases.The only obstacle to using page caching is that the administrative interface is built into the main site so that admin operations can be performed without leaving the page of interest.
I'm using Apache+mod_rails (Passenger). Is there a way to indicate to Apache that .html files should be ignored when the current user either has a session variable or a cookie named 'admin'*? The session variable need not be evaluated by Apache for validity (since it will be evaluated by Rails in this case).
Is there a way to indicate to Apache that .html files should be ignored when the current user either has a session variable or a cookie named 'admin'*?
I believe it is not really possible. Even if it is, I guess should be very tricky.
Instead you can use Action Caching. A quote from docs:
One of the issues with page caching is
that you cannot use it for pages that
require checking code to determine
whether the user should be permitted
access.
This sounds like exactly your case.
But if you still really need Page Caching via web server, I think you better implement separate pages for admin and non-admin.
This is because of one reason. When you enable Page Caching rails doesn't process the request at all and thus there is no way to know if user is authenticated or not.
You probably can overcome this using Dynamic Page Caching. The idea is basically to add the "admin" part from the JavaScript. I don't personally like this a lot though.
One more update: quick search brought me to this article.
The idea is to cache page conditionally and plug mod_rewrite to serve admin pages.
Will work for you but is pretty dirty solution.

Resources