Ruby on Rails form page caching including authenticity_token - ruby-on-rails

I have a simple Ruby on Rails form which includes an authenticity_token. Unfortunatly, I missed that when you page cache this page then the Authenticity Token becomes invalid. I'm glad I figured it out however.
How do you solve caching in such a case?

As Matchu posted, you could implement point two from this post (same link he posted, but found via my Googling as well). This adds a dependency on JavaScript, which may or may not be something you want.
Alternatively, you could look into Fragment Caching. This allows you to cache certain portions of a page, but still generate the dynamic portions (such as forms with authenticity tokens). Using this technique, you could cache the rest of the page, but generate a new form for every request.
One final solution (but the least favourable), is to disable the authenticity token for that specific action. You can do this by adding the following to the beginning of the controller generating that form:
protect_from_forgery :except => [:your_action]
You can also turn off protect_from_forgery for the entire controller by adding the following to the beginning:
skip_before_filter :verify_authenticity_token

It doesn't seem to be a well-solved problem. Point two on this blog post describes how to accomplish the task by using jQuery, but that introduces a Javascript dependency. Weigh your options, I suppose.

You could render a custom tag in the cached markup and replace it with the form rendered on every request.
module CacheHelper
# Our FORM is deeply nested in the CACHED_PARTIAl, which we
# cache. It must be rendered on every request because of its
# authenticity_token by protect_from_forgery. Instead of splitting up the
# cache in multiple fragments, we replace a special tag with the custom
# form.
def cache_with_bla_form(resource, &block)
form = nil
doc = Nokogiri::HTML::DocumentFragment.parse( capture { cache("your_cache_key",&block) } )
doc.css('uncachable_form').each do |element|
form ||= render(:partial => 'uncachable_form', :resource => resource)
element.replace form
end
doc.to_html
end
end
And in your view, you just render an empty uncachable_form tag.
<%- cache_with_bla_form resource do %>
# cachable stuff..
<uncachable_form />
# more cachable stuff
<%- end %>
Yes, this can be considered as a Hack, but it won't loosen forgery protection, needs no JS, and decrease the performance gain from caching just a bit. I think someone implemented a similar pattern as a Rack Middleware.

I followed Niklas Hofer's general solution, but I found that his implementation did not match the exact semantics of the Rails cache helper. Namely, it attempted to return the cached HTML from the helper, rather than writing it to the buffer using safe_concat, which is what the Rails helper does.
The Rails helper usage is like this:
- cache do
= something
Whereas his solution required this syntax:
= cache_with_updated_csrf do
= something
For consistency, I would prefer that these work the same way. Hence I used this syntax:
- cache_form do
= something
Here is my implementation. It will also skip caching when caching is disabled, like the Rails helper does.
module CacheHelper
# Cache a form with a fresh CSRF
def cache_form(name = {}, options = nil, &block)
if controller.perform_caching
fragment = fragment_for(name, options, &block)
fragment_with_fresh_csrf = Nokogiri::HTML::DocumentFragment.parse( fragment ).tap do |doc|
doc.css("input[name=#{request_forgery_protection_token}]").each { |e| e['value'] = form_authenticity_token }
end.to_html
safe_concat fragment_with_fresh_csrf
else
yield
end
nil
end
end

As a more general solution, you could also replace all cached authenticity_tokens with the current ones:
module CacheHelper
def cache_with_updated_csrf(*a, &block)
Nokogiri::HTML::DocumentFragment.parse( capture { cache(*a,&block) } ).tap do |doc|
doc.css("input[name=#{request_forgery_protection_token}]").each { |e| e['value'] = form_authenticity_token }
end.to_html.html_safe
end
end
And use = cache_with_updated_csrf do instead of - cache do in your views. Kudos to Bernard Potocki for the idea.

Related

How to replace erb with liquid?

I'd like to use liquid in my Rails app. I've installed the gem. In order to use in all templates, I've created a library (lib/liquid_view.rb:):
class LiquidView
def self.call(template)
"LiquidView.new(self).render(#{template.source.inspect}, local_assigns)"
end
def initialize(view)
#view = view
end
def render(template, local_assigns = {})
#view.controller.headers["Content-Type"] ||= 'text/html; charset=utf-8'
assigns = #view.assigns
if #view.content_for?(:layout)
assigns["content_for_layout"] = #view.content_for(:layout)
end
assigns.merge!(local_assigns.stringify_keys)
controller = #view.controller
filters = if controller.respond_to?(:liquid_filters, true)
controller.send(:liquid_filters)
elsif controller.respond_to?(:master_helper_module)
[controller.master_helper_module]
else
[controller._helpers]
end
liquid = Liquid::Template.parse(template)
liquid.render(assigns, :filters => filters, :registers => {:action_view => #view, :controller => #view.controller})
end
def compilable?
false
end
end
And added the following initialiser (config/initializers/liquid_template_handler.rb:):
require 'liquid_view'
ActionView::Template.register_template_handler :liquid, LiquidView
PS: I've followed these instructions.
Now, if rename a template file with liquid my_template.html.liquid the <%= stylesheet_link_tag 'mycss' %> stopped working, but more importantly the {{user.first_name}} variable did not print. In my controller I have #user = current_user
What am I missing?
My intention is to completely override erb with liquid in some templates, so ideally it should work like erb (in a sense that I can pass variables from the controller and simply render it in the template without using Liquid::Template.parse(#page.template) which by the way, I don't understand how it works on a file-based template.
PS: I'm also using [this] gem (https://github.com/yoolk/themes_on_rails) for separate templates. I'm not sure it does any impact on it.
PPS: I've seen this but doesn't apply as its a older version of Rails and I'm not using prepend.
PPPS: I'm using Ruby 2.2.2 and Rails 4.2
I hope this not the problem you are thinking it is . You can check the way as it was said here Github Description
Did you create a Drop to access #user?
https://github.com/Shopify/liquid/wiki/Introduction-to-Drops
Liquid is a safe template system, so we can interpret on the backend templates that are created by the user. To access anything non trivial (number, string, hashes or arrays) you need a Drop, which is a controlled interface to define what the templates can access.
This is by design and for security reasons.

Where is the best place to store layout logic in Rails?

In my Rails app, I have several controllers that makeup a site for my client. With end users and Admins using virtually all of the controllers.
I have created a partial for including navigation links. My problem is that I don't want them to appear inside of the admin section. The part that makes this tricky is that while the navigation should appear for things like pages/home, /galleries or /galleries/5 they should not appear for things like /pages/new or /galleries/5/edit.
Up until now I have been adding logic to application.html.erb, but things are beginning to get out of hand, there are so many rules in my if statement that I feel dirty about it. Here is an example:
snippet from application.html.erb
<% unless current_page?('/') or current_page?('/admin') or current_page?({action: :new}) %>
<%= render "layouts/top_links" %>
<% end %>
This is all I have written so far, but it will almost certainly get even longer if I continue this way. So here are my questions:
Where should I put the logic for this and how should I call it?
Is current_page? the best way to lay this out?
You can put such dirty logic into helper to make it cleaner
# layouts/application.html.erb
nav_links
# application_helper
def nav_links
return "" if action_name.in?['new', 'edit'] || controller_path == 'admin'
render partial: 'layouts/nav'
end
Done.
You could advance with a role based approach, as it is the most widely used method for access control in rails. Indeed, all those if-else loops in application.html.erb would be a little bit of code smell anyway.
Check Ryan Bates CanCan gem for this purpose.
One possibility is to make use of before_filter, which you can set up in your ApplicationController. What you can do
Apply your own flavour to this, but a goofy example might be:
class ApplicationController < ActionController::Base
before_filter :set_navigation_visibility
def set_navigation_visibility
# Your custom logic
end
That method can also be overridden in the subcontrollers if necessary, or modified using the :only and :except options.
Another possibility is making use of a role-based access-control system. There are many, one such library I have used is Simple-Navigation. A google search for role-based access control for rails seems to yield a number of results that might be helpful.

Is there a way to cache a string fragment within a helper method?

I have a helper that generates complex HTML for common components in an engine.
Helper (very simplified):
def component(name)
component = Component.find_by_name!(name)
# a whole lot of complex stuff that uses component to build some HTML
end
View:
<%= component(:my_component) %>
I want to implement fragment caching on these components but I want to do it within #component itself to keep things DRY, e.g.
def component(name)
...
cache some_unique_fragment_name do
html
end
# or, more succinctly:
cache(some_unique_fragment_name, html)
end
The problem is that Rails' cache helper expects it's going to wrap a block of HTML in Erb and therefore won't work as I've described above.
Is there a way to use #cache for a string fragment in a helper method?
I'm a big fan of the fetch block, you can read more in the Rails docs:
def component(name)
# ...
cache.fetch('some_unique_fragment_name') do
html
end
end
What this does is it will return the value of some_unique_fragment_name if it's available, otherwise it will generate it inside the block. It's a readable, clean way of showing that caching is occurring.

Ruby w/ Sinatra: what is the equivalent of a .js.erb from rails?

.js.erb's are nice, because you can use them to replace parts of a page without having to leave the current page, which gives a cleaner and unchopped up feel to the site / app.
Is there a way to use them in sinatra? or an equivalent?
Just add .js to the end of the symbol you're passing erb(). A la (to call mypage.js.erb):
erb "mypage.js".to_sym
Dirty, but it works.
Based on your description, I'm guessing that your desire is to have portions of a page editable and replaced via AJAX. If this is wrong, please clarify.
I do this in my Sinatra apps by including (my own) AJAXFetch jQuery library and writing code as shown below. This lets me use the partial both when rendering the page initially as well as when editing via AJAX, for maximum DRYness. The AJAXFetch library handles all AJAX fetch/swap through markup alone, without needing to write custom JS on the pages that use it.
helpers/partials.rb
require 'sinatra/base'
module Sinatra
module PartialPartials
ENV_PATHS = %w[ REQUEST_PATH PATH_INFO REQUEST_URI ]
def spoof_request( uri, headers=nil )
new_env = env.dup
ENV_PATHS.each{ |k| new_env[k] = uri.to_s }
new_env.merge!(headers) if headers
call( new_env ).last.join
end
def partial( page, variables={} )
haml page, {layout:false}, variables
end
end
helpers PartialPartials
end
routes/bug.rb
get '/bug/:bug_id' do
if #bug = Bug[params[:bug_id]]
# ...
haml :bug
end
end
# Generate routes for each known partial
partials = %w[ bugdescription bughistory bugtitle fixer
pain project relatedbugs status tags version votes ]
partials.each do |part|
[ part, "#{part}_edit" ].each do |name|
get "/partial/#{name}/:bug_id" do
id = params[:bug_id]
login_required
halt 404, "(no bug ##{id})" unless #bug = Bug[id]
partial :"_#{name}"
end
end
end
post "/update_bug/:partial" do
id = params[:bug_id]
unless params['cancel']=='cancel'
# (update the bug based on fields)
#bug.save
end
spoof_request "/partial/#{params[:partial]}/#{id}", 'REQUEST_METHOD'=>'GET'
end
views/bug.haml
#main
#bug.section
= partial :_bugtitle
.section-body
= partial :_bugdescription
<!-- many more partials used -->
views/_bugtitle.haml
%h1.ajaxfetch-andswap.editable(href="/partial/bugtitle_edit/#{#bug.pk}")= title
views/_bugtitle_edit.haml
%form.ajaxfetch-andswap(method='post' action='/update_bug/bugtitle')
%input(type="hidden" name="bug_id" value="#{#bug.id}")
%h1
%input(type="text" name="name" value="#{h #bug.name}")
%span.edit-buttons
%button(type="submit") update
%button(type="submit" name="cancel" value="cancel") cancel
sinatra really isn't meant to be a full stack framework. Its supposed to get you on the road very quickly. You could use an erb separately and then load into your sinatra code.

Best way to combine fragment and object caching for memcached and Rails

Lets say you have a fragment of the page which displays the most recent posts, and you expire it in 30 minutes. I'm using Rails here.
<% cache("recent_posts", :expires_in => 30.minutes) do %>
...
<% end %>
Obviously you don't need to do the database lookup to get the most recent posts if the fragment exists, so you should be able to avoid that overhead too.
What I'm doing now is something like this in the controller which seems to work:
unless Rails.cache.exist? "views/recent_posts"
#posts = Post.find(:all, :limit=>20, :order=>"updated_at DESC")
end
Is this the best way? Is it safe?
One thing I don't understand is why the key is "recent_posts" for the fragment and "views/recent_posts" when checking later, but I came up with this after watching memcached -vv to see what it was using. Also, I don't like the duplication of manually entering "recent_posts", it would be better to keep that in one place.
Ideas?
Evan Weaver's Interlock Plugin solves this problem.
You can also implement something like this yourself easily if you need different behavior, such as more fine grained control. The basic idea is to wrap your controller code in a block that is only actually executed if the view needs that data:
# in FooController#show
#foo_finder = lambda{ Foo.find_slow_stuff }
# in foo/show.html.erb
cache 'foo_slow_stuff' do
#foo_finder.call.each do
...
end
end
If you're familiar with the basics of ruby meta programming it's easy enough to wrap this up in a cleaner API of your taste.
This is superior to putting the finder code directly in the view:
keeps the finder code where developers expect it by convention
keeps the view ignorant of the model name/method, allowing more view reuse
I think cache_fu might have similar functionality in one of it's versions/forks, but can't recall specifically.
The advantage you get from memcached is directly related to your cache hit rate. Take care not to waste your cache capacity and cause unnecessary misses by caching the same content multiple times. For example, don't cache a set of record objects as well as their html fragment at the same time. Generally fragment caching will offer the best performance, but it really depends on the specifics of your application.
What happens if the cache expires between the time you check for it in the controller
and the time it's beeing checked in the view rendering?
I'd make a new method in the model:
class Post
def self.recent(count)
find(:all, :limit=> count, :order=>"updated_at DESC")
end
end
then use that in the view:
<% cache("recent_posts", :expires_in => 30.minutes) do %>
<% Post.recent(20).each do |post| %>
...
<% end %>
<% end %>
For clarity, you could also consider moving the rendering of a recent post into its own partial:
<% cache("recent_posts", :expires_in => 30.minutes) do %>
<%= render :partial => "recent_post", :collection => Post.recent(20) %>
<% end %>
You may also want to look into
Fragment Cache Docs
Which allow you to do this:
<% cache("recent_posts", :expires_in => 30.minutes) do %>
...
<% end %>
Controller
unless fragment_exist?("recent_posts")
#posts = Post.find(:all, :limit=>20, :order=>"updated_at DESC")
end
Although I admit the issue of DRY still rears its head needing the name of the key in two places. I usually do this similar to how Lars suggested but it really depends on taste. Other developers I know stick with checking fragment exist.
Update:
If you look at the fragment docs, you can see how it gets rid of needing the view prefix:
# File vendor/rails/actionpack/lib/action_controller/caching/fragments.rb, line 33
def fragment_cache_key(key)
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
end
Lars makes a really good point about there being a slight chance of failure using:
unless fragment_exist?("recent_posts")
because there is a gap between when you check the cache and when you use the cache.
The plugin that jason mentions (Interlock) handles this very gracefully by assuming that if you are checking for existence of the fragment, then you will probably also use the fragment and thus caches the content locally. I use Interlock for these very reasons.
just as a piece of thought:
in application controller define
def when_fragment_expired( name, time_options = nil )
# idea of avoiding race conditions
# downside: needs 2 cache lookups
# in view we actually cache indefinetely
# but we expire with a 2nd fragment in the controller which is expired time based
return if ActionController::Base.cache_store.exist?( 'fragments/' + name ) && ActionController::Base.cache_store.exist?( fragment_cache_key( name ) )
# the time_fraqgment_cache uses different time options
time_options = time_options - Time.now if time_options.is_a?( Time )
# set an artificial fragment which expires after given time
ActionController::Base.cache_store.write("fragments/" + name, 1, :expires_in => time_options )
ActionController::Base.cache_store.delete( "views/"+name )
yield
end
then in any action use
def index
when_fragment_expired "cache_key", 5.minutes
#object = YourObject.expensive_operations
end
end
in view
cache "cache_key" do
view_code
end

Resources