I have a Rails 3 app and am implementing a profile completeness sort of feature. When the user is logged in the app should show him/her progress on making a “complete” profile. To wit I am using a singleton class populated with requirements at app initialization. The singleton has an array, @requirements. It is correctly populated using my initializer. When I hit the ProfileController the requirements display. However, after the first, subsiquent requests to ProfileController#completeness list no @requirements. The array on the singleton is empty. I believe the singleton is not returning the same instance across controller requests. Where am I going wrong here?
Note: this class just holds rquirements, not a particular user’s progress towards fulfilling them. Requirements rarely change so I want to avoid a database lookup.
# lib/profile_completeness.rb
require 'singleton'
class ProfileCompleteness
include Singleton
include Enumerable
attr_reader :requirements
def add_requirement(args)
b = Requirement.new(args)
@requirements << b
b
end
def clear
@requirements = []
end
def each(&block)
@requirements.each { |r| block.call(r) }
end
class Requirement
# stuff
end
end
–
# config/initializers/profile_completeness.rb
collection = ProfileCompleteness.instance()
collection.clear
collection.add_requirement({ :attr_name => "facebook_profiles",
:count => 1,
:model_name => "User",
:instructions => "Add a Facebook profile" })
–
class ProfileController < ApplicationController
def completeness
@requirements = ProfileCompleteness.instance
end
end
–
<!-- app/views/profile/completeness.html.erb -->
<h2>Your Profile Progress</h2>
<table>
<%- @requirements.each do |requirement|
complete_class = requirement.is_fulfilled_for?(current_user) ? "complete" : "incomplete" -%>
<tr class="profile_requirement <%= complete_class -%>">
<td>
<%- if requirement.is_fulfilled_for?(current_user) -%>
✓
<%- end -%>
</td>
<td><%= raw requirement.instructions %></td>
</tr>
<%- end -%>
</table>
<p><%= link_to "Profile", profile_path -%></p>
this is not going to work (multi-threading, different rails workers etc.) you can’t expect to land in the same rails app thread on every request. If your server crashes, all progress is lost! So the way to go for persisting data (permanently) across requests/sessions is a database.
Model your completeness tracker as a Model and store it in your database.
Another solution is to use the Rails Application Cache.
Set a Key/Value pair:
Reading:
Read more about Rails Cache
If you want a solution for big data sets and fast access, I recommend you to use redis:
Here is a good article especially the section “Using Redis As Your Rails Cache Store” and look through the section “Redis Related Gems”.
The important thing is the key/value data structure, I’d go for keys like