Rails: Trying to understand how to do better fragment caching - ruby-on-rails

In my app I am trying to incorporate better fragment caching.
Is it a best practice to do call fragments like this:
<% cache("user/#{current_user.id}/info") do %>
<%= current_user.email %> information goes here
<% end %>

Yes you are doing it right!
Why?
The cache fragment's key must reflect the "uniqueness" of the content:
Statement: Your content is uniq for each user
Conclusion: Your fragment's key must be different for each user
Usage: using the user's id is the best choice since every user id is uniq!

Related

How to prevent users from setting sensitive fields from form submissions in Rails 3?

I take an example from Rails Tutorial about "Follow a user".
<%= form_for(current_user.relationships.build(followed_id: #user.id)) do |f| %>
<div><%= f.hidden_field :followed_id %></div>
<%= f.submit "Follow", class: "btn btn-large btn-primary" %>
<% end %>
This line: f.hidden_field :followed_id
is generated into something like this in the browser:
<input type="hidden" value="13">
'13' indicates the ID of a user who is going to be followed.
Perhaps not all of my website users are tech savvy, but some users may found a way to do some tricks, e.g by opening the Firebug and simply edit the html input tag and set the value with random number.
It is clearly fast and simple to make a user to follow another user through the following way in my controller:
def create
#user = User.find(params[:relationship][:followed_id])
current_user.follow!(#user)
end
Also, I need the attribute in my model to be accessible:
attr_accessible :followed_id
However, isn't it too dangerous to let this kind of data being manipulated easily by users? Is there any other solution instead of doing like the codes above?
Your solution is fine. If there are any cases where the user can tamper with data in a malicious way, you need to have some sort of server-side check. If the user may only follow certain other users, for example, then you need to check that they haven't tampered with that variable to follow a user that they aren't allowed to. It depends on the situation.
As a rule of thumb, data that comes from the user should not be considered trustworthy. Always assume that it may be tampered with. However, if they cannot tamper with it in a way that serves a malicious purpose (I.e. If users may follow any other user), then don't worry about it.

rails_digest caching per user

I my app, my views are generated according to users actions and rights.
I would like to implement rails_digest to cache my pages but I need to do this per user.
I know it's possible in fragment cache:
<% cache "mypage", project, user %>
But this doesn't seem to work in rails_digest.
Any clue?
I found the way to do it:
just write: <% cache [user, project] do %> ... <% end %>
When something changes in the project, all you need to do is project.touch. The cache will be reset for everyone.

Rails - How to exclude blocks of code from fragment cache

I'm using fragment cache but i have inline code that is user specific like:
<% cache #page do %>
stuff here
<% if current_user %>
user specific
<% end %>
more here
<% end %>
So i want to exclude the several blocks of code that are user specific. Is there a way to do that in Rails or should i make an if statement in the beginning and make different caches for logged users and regular visitors? (i will have major duplication of code this way).
For per-user fragments, you can put models in array an array:
<% cache [#page, current_user] do %>
Rails will make a cache-key out of them, like:
pages/page_id-page_timestamp/users/user_id-user_timestamp
This way your fragments will be invalidated on a user/page update since the time-stamps are coming from their updated_at (see cache_key for details).

Rails Check if User Id is in Array

I'm trying to build a condition based on wether or not a "user" is a "member". Basically I need a way of checking if the current_user.id matches any of the user_id of any members. The non-working code I have right now is:
<% if current_user = #page.members %>
you can view this content.
<% end %>
I'm looking for something along the lines of: "If current_user.id exists in the "user_id" of any members."
Something like this, based on the field names in your question:
<% if #page.members.map(&:user_id).include? current_user.id %>
You can view this content
<% end %>
Assuming your #page.members variable contains an array, you can use the include? method:
<% if #page.members.include? current_user %>
you can view this content.
<% end %>
If you're using an array of ids, you will of course need to change the test slightly to look for the current user's id:
<% if #page.members.include? current_user.id %>
you can view this content.
<% end %>
#member_ids = #page.members.map{|m| m.id()}
then check for the condition as below
#memeber_ids.include? current_user.id()
Has said before include? should do the thing.
I'm just answering to tell you about a gem called CanCan, that gives you easy access for authorization "helpers".
Why you should use CanCan instead of doing what you are actually doing?
Don't reinventing the weel most of the times it's a goob practice.
You are placing business logic on the view (bad practice).
CanCan most likely has been developed thinking on security, and all the best practices in mind.
You save some developing hours.
Sorry if I repeated myself.

Rails - How to show an alternative value to the stored value

I have simple select list to mark whether a building is for sale or not:
<%= select(:building, :for_sale, options_for_select([['Unknown', 'u'], ['Yes', 'y'], ['No', 'n']])) %>
This is in a New view. It will store for example 'u' when the user selects "Unknown".
However, when the record is created I am directed to the Show view where I see 'u' instead of "Unknown" which is what I would like to see (although I want to store 'u').
What is an efficient way of displaying the user (or Human) text? Maybe it is just a helper method but anything I have come up with seems to be a lot of code for something so simple. Any "The Rails Way" approaches to this?
UPDATE: I am currently doing this in my view:
<% case #building.for_sale when 'u' %>
Unknown
<% when 'y'%>
Yes
<% when 'n'%>
No
<% end %>
But this seems... well, dumb.
So, question still remains. Is there a best methods way of dealing with this scenario?
I usually use the I18n API to do this.
I store every constant under a key in the config/locales/LOCALE.yml:
en:
constants:
u: "Unknown"
y: "Yes"
n: "No"
Use a helper to retrieve the constants translation:
def translate_constant(constant)
t("constants.#{constant}")
end
# Usage
translate_constant(#building.for_sale)
You could also create a helper to create the select options:
def constant_options_for_select(options)
options_for_select(options.map {|option| [translate_constant(option), option] })
end
# Usage
constant_options_for_select(['u', 'y', 'n'])

Resources