Caching active record associations with fresh_when - caching

What's the best way to cache ActiveRecord associations? Here's what I am trying to do in the controller:
def all_posts
#posts = User.find(params[:id]).posts
fresh_when #posts
end
Whenever a new post is added to the user model I need to clear the cache.

You're solution works perfect.
In case you are just adding posts there is a more performant solution: You'll have to had a touch: true to the belongs_to :user part of your Post model. ActiveRecord will touch the User object every time a post gets changed and a new post gets created. When you have that implemented you can use this controller code:
def all_posts
user = User.find(params[:id])
#posts = user.posts
fresh_when user
end
It is much faster to generate a cache key from one user than from many posts.
Have a look at http://www.xyzpub.com/en/ruby-on-rails/3.2/caching.html to get an introduction to caching in Rails.

Related

Getting Sequel associations through Sinatra

I'm trying to return json-formatted data from my Sinatra REST API. I currently have a bunch of associations set up, but I'm having trouble getting the views I want from my API despite getting them easily in Ruby.
For example, from my tables:
DB.create_table?(:calendars) do
primary_key :id
end
DB.create_table?(:schedules) do
primary_key :id
foreign_key :resource_id, :resources
foreign_key :task_id, :tasks
foreign_key :calendar_id, :calendars
end
In Ruby, I'm able to run a block like this and display all the info I need through my associations:
Calendar.each do |c|
c.schedules.each do |s|
puts "RESOURCE ##{s.resource_id}"
s.tasks.each do |t|
p t
end
puts
end
end
the c.schedules call works because my calendar model contains a one_to_many :schedules association.
Now, I'm wondering how this translates to my Sinatra API. In my simple GET route, I've tried many variations trying to get the schedules associated with a calendar, and convert it to JSON:
get '/calendars' do
c = DB[:calendar].first
c.schedules.to_json
content_type :json
end
... but I'll end up with an error like undefined method 'schedules' for {:id=>1}:Hash
So it looks like it's returning a hash here, but I've tried a bunch of stuff and haven't figured out how I'm supposed to work with my associations in Sinatra. How can I do this?
Thanks!
The reason your first block works but the second doesn't is because in the first case, you're using a Sequel model instance of class Calendar, whereas in the second case you're using a Sequel dataset.
When you iterate over Calendar.each do |c|, the c variable gets populated with an instance of a Calendar class Sequel model object. This object has relationship methods defined (one_to_many) and you're able to query schedules and run other model methods on it.
However, c = DB[:calendar].first gets you a Sequel dataset. This object is different than a model instance, it returns a standard Ruby hash (or an array of hashes).
You can change your 2nd block to use a model instead and it will get the result you want:
get '/calendars' do
c = Calendar.first # <=== CHANGE FROM DATASET TO MODEL
c.schedules.to_json
content_type :json
end

How are view helpers available in the controllers?

I have come across a bit of confusion regarding the use of view helpers controllers. The kind of scenario I have is:
session_helper.rb:
module SessionsHelper
# logs in the given user.
def log_in(user)
session[:user_id]=user.id
end
sessions_controller.rb:
class SessionsController < ApplicationController
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
redirect_to user
end
end
def destroy
log_out
redirect_to root_url()
end
Now, as per the documentation that I have read, it mentions that helpers are used in views, to reduce the amount of coding to be done there.
My question is: how I am able to use the log_in and log_out methods defined in the session_helper in my controller?
If anyone can clear me on this concept would be very much helpful.
Answer on your question:
ActionController::Base.helpers.log_in(user)
But, it's better to place those methods in the controller.

Ruby on Rails: Getting a route with Devise

TL;DR How can I achieve this to always return routes with Devise User model ID in custom controllers (i.e. redirecting to localhost:3000/profiles/504026426) with Mongoid?
I have a project that has a social profile controller that returns of a user id with Devise but it will always complain that Mongoid needs a valid ID number in order to work with this route: localhost:3000/profiles/.
# Let's say I want to return a route with id (Devise) without having
# Rails to complain that Mongoid needs to a id of some number.
# So, I have controller containing profiles in the files.
# It goes like..
class ProfilesController < ApplicationController
before_filter :authenticate_user!
# GET /profile/
def index
#user = User.find(params[:id])
end
# .. Snipped for brevity.
end
Is this the right way to do it?
Firstly, the 'params[:id]' you're trying to find the user by won't work in the index view, because you're not passing in any parameter via the url ('profiles/:id' or: localhost:3000/profiles/504026426). If you want to store a user to be available in the index view, use sessions and the current user helper method. See here.
If you want your GET method to work with the 'params[:id]' it would look like:
get 'profile/:id' => 'profiles#show'
make sure it matches a show function in your controller
def show
# this would work with: localhost:3000/profiles/504026426
#user = User.find(params[:id])
end

Troublesome Wice::WiceGridArgumentError

I am working on a Rails 3.1.1 app that is using WICE_GRID and I am stuck on this error.
I want to show a grid of Roles on the User show page. I am setting up the data in the controller like this.
User and Role are related by has_many through user_role.
def show
#user = User.find(params[:id])
#roles = initialize_grid(#user.roles)
end
When I run the site I get this error
Wice::WiceGridArgumentError in UsersController#show
WiceGrid: ActiveRecord model class (second argument) must be a Class derived from ActiveRecord::Base
The error is pointing to #roles = init.... line. initialize_grid does take a record arguent but that is a hash of options, not an activerecord model collection.
When I run the code in the console I see that #user.roles is
[#<Role id: 1, title: "Role1>, #<Role id: 2, title: "Role2">]
Looks like an ActiveRecord collection to me.
Any help gratefully accepted!
initialize_grid takes a class. You're passing in an array of objects. It appears you want to display a user's roles in the grid. You want something like this:
def show
#user = User.find(params[:id])
#roles = initialize_grid(Role, :conditions => ['user_id = ?', #user.id])
end
However, I'm guessing your roles table doesn't have user_id in it. You likely have a mapping table called user_roles. In which case, you will want to refactor the code above. Try just running this code instead to make sure you can view Roles in a grid (unscoped).
def show
#user = User.find(params[:id])
#roles = initialize_grid(Role)
end

Rails Active Record: Calling Build Method Should Not Save To Database Before Calling Save Method

I have a simple user model
class User < ActiveRecord::Base
has_one :user_profile
end
And a simple user_profile model
class UserProfile < ActiveRecord::Base
belongs_to :user
end
The issue is when I call the following build method, without calling the save method, I end up with a new record in the database (if it passes validation)
class UserProfilesController < ApplicationController
def create
#current_user = login_from_session
#user_profile = current_user.build_user_profile(params[:user_profile])
##user_profile.save (even with this line commented out, it still save a new db record)
redirect_to new_user_profile_path
end
Anyyyyyy one have anyyy idea what's going on.
The definition of this method says the following but it's still saving for me
build_association(attributes = {})
Returns a new object of the associated type that has been instantiated with attributes and linked to this object through a foreign key, but has not yet been saved.
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_one
Ok, I'm sure experienced vets already know this, but as a rookie I had to figure it out the long way...let me see if I can explain this without screwing it up
Although I was not directly saving the user_profile object, I noticed in my logs that something was updating the user model's last_activity_time (and the user_profile model) each time I submitted the form (the user model's last_activity date was also updated when the logged in user did various other things too - I later realized this was set in the Sorcery gem configuration).
Per http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html
AutosaveAssociation is a module that takes care of automatically saving associated records when their parent is saved. In my case, the user mode is the parent and the scenario they provide below mirrors my experience.
class Post
has_one :author, :autosave => true
end
post = Post.find(1)
post.title # => "The current global position of migrating ducks"
post.author.name # => "alloy"
post.title = "On the migration of ducks"
post.author.name = "Eloy Duran"
post.save
post.reload
post.title # => "On the migration of ducks"
post.author.name # => "Eloy Duran"
The following resolutions resolved my problem
1. Stopping Sorcery (config setting) from updating the users last_activity_time (for every action)
or
2. Passing an ':autosave => false' option when I set the association in the user model as follows
class User < ActiveRecord::Base
has_one :user_profile, :autosave => false
end

Resources