Reputation: 22643
I have a web app which is part Rails and part Backbone. Some things such as a commenting system I have implemented are written mostly in Javascript on the client side. The Rails backend simply handles persistance by passing JSON back and forth.
When I render pages from the server, handling who gets to see what is easy. I can say things such as
<li class="comment">
<span class="comment_text"><%= @comment.text %></span>
<% if user_signed_in? and current_user == @comment.author %>
<a class="delete" href="some delete url">Delete Comment</a>
<% end %>
</li>
And that will only render the link to delete a particular comment if the current user is the comment's author. No problem.
However, now that I'm rendering comments on the client side using JavaScript templates (which are cached afaik), I don't have access to current_user
. I can't tell if the user currently using my app is the author of the comment or not so I can't control what he gets to see.
Sure, he won't be able to delete the comment either way because I authorize on the server as well but I'd rather not show hin the link in the first place.
How can I accomplish this?
I'd love some links to resources on this topic as well as answers because I can't seem to find any, even though it seems to me like this is a topic that should have been covered in countless blogs.
Upvotes: 8
Views: 7840
Reputation: 1181
I prefer using following approach.
First, in your layout, generated on server-side, pass current user's data that you'll need on client side:
<script type="text/javascript">
window.currentUser = {
id : "<%=current_user.id%>"
}
</script>
It will be accessible in your EJS templates. Now in template, you can make the same check as on server-side:
<% if (window.currentUser && window.currnetUser.id == comment.author.id) { %>
<a class="delete" href="some delete url">Delete Comment</a>
<% } %>
Upvotes: 7
Reputation: 6743
This is sometimes called Client Side Personalization. It involves using css classes to hide and show certainly elements based on a value that javascript gets from a cookie or an ajax request.
I prefer setting user state, name, and other key data in a cookie that is set in a rack middleware that wraps the caching layer. This way, the logic for user session can be isolated from cached data. Then I use javascript to read this cookie and alter the page as needed. In the case of the comments example that you gave, I would render each comment with a digest of its ID (or just the id, if you aren't concerned about the security implications) in a data attribute, like so:
<div class="comment_203948">...</div>
and store the ids of a user's comments in the aforementioned cookie. Then javascript reads the cookie and finds all comments with those ids, and shows the 'delete' link for them.
A common problem with this approach involves what happens when the cookie overflows, as would happen in this example with a prolific commenter. Another approach is to use ajax to fetch the user's relevant JSON data, then to cache that in local storage. I like to combine that with keeping a version of the user's JSON data in a cookie that can be updated on the server-side with after_save callbacks and other cache-expiry strategies. Javascript then simply compares the state of the user's JSON found in local storage with the version in the cookie, and refreshes this JSON via an ajax request when it's stale.
For some further tips on Client Side Personalization, see this post under "client side cache personalization": http://www.tumblr.com/tagged/caching
and this one: http://pivotallabs.com/users/nick/blog/articles/297-making-rails-wicked-fast-pagecaching-highly-personalized-web-pages
Upvotes: 2
Reputation: 15530
Ryan Bates describes a common practice for that case, let me explain. It helps with Dynamic Page Caching, when you use page caching, but need to get something from the serverside and handle it.
Is about to render page without "Delete" link, then get a request to check if user session or not and assign the result to a variable.
One of implementation, a little bit deeper:
# controller
class UserSessionController < ActionController::Base
skip_before_filter :require_user, :only => [:new, :create, :user_sign_in]
def user_sign_in
if current_user
render :text => 'success'
else
render :text => 'false', :status => 403
end
end
end
class CommentsController < ApplicationController
def has_right
current_user == @comment.author
end
end
# view
<% javascript_tag do %>
var a = $.getJSON('/user_session/user_sign_in', function(data){
console.log(data)
});
<% end %>
Then handle the result and hide/show comments divs.
Upvotes: 0