I'm developing a Rails 3 app using Devise and CanCan.
The app allows anonymous (not registered) users to access some of the app, and registered users to access other parts.
One aspect of the app (a yoga workout app) is that users can create Yoga sequences by stringing together yoga poses, then they can play them back in a video player.
I currently have all the functioning for registered users, but now want to allow anonymous users to be able to create a sequence, and play it back, but to "save" it (ie. be able to use it again on any subsequent session), they need to register.
Currently, this requires the actual creation of a Sequence model instance, which belongs to a User (simplified below)
class Sequence < ActiveRecord::Base
validates_presence_of :name
validate :status_must_be_private_if_user_nil
# Associations
has_many :ordered_asana_sequences, :class_name => "OrderedAsanaSequence", :order => 'position ASC', :dependent => :destroy
has_many :asanas, :through => :ordered_asana_sequences
belongs_to :user
def status_must_be_private_if_user_nil
errors.add(:status, "must be private is user is nil") if
user == nil and status != :private
end
end
But, of course, Anonymous users do not have a User model.
I have sort of hacked something up where Sequences DO belong to Users, but its not mandatory (user_id can be nil, as you can see above), and the ability to edit is secured through CanCan:
class Ability
{snip}
# A guest can play with creating sequences
can :create, [Sequence]
# A guest can edit/update their own Sequences
can :update, [Sequence], :user_id => user.id
end
But, this means that any anonymous user (user_id = nil) can edit any other anonymous user's sequences. Which, I guess isn't the end of the world, but, I would prefer it to be more secure.
Here are the potential ways I've thought of doing it:
'Register' anonymous users
- If a guest user goes to make a sequence, generate an email and password and 'register' this "guest" user. Then assign the new sequence model to this new user. If the user decides to register for real, just update the existing user record with the new email and password.
- Advantages: Very few changes to existing app - only need to deal with it in the "registration" process.
- Drawbacks: may end up with lots of dead user accounts that I need to sweep up after
Don't persist the Sequence, but serialize it and store it in the session
- If a guest user goes to make a sequence, when they want to "save" it, shoot it to a controller method that serializes the model and stores it in the session. Then, the Sequence player and, I guess, the Sequence Editor can optionally pull it from the session instead of the Datastore
- Advantages: No creation of user accounts for guests, so no need for cleanup. No need to deal with modifying Abilities, as the guest won't be fetching models from the Datastore, but from their session.
- Disadvantages: Seems hacky. Will need a lot of touchpoints in the app to determine where to get the sequence from. Higher potential for bugs
Figure out some other way to assign a Sequence to a guest user, other then making the user_id nil
- Advantages: It will be brilliant
- Disadvantages: No clue what to do.
I'm hoping that there's something I haven't seen in Devise (inviteable, perhaps?) that would be cleaner then rolling it all myself.
Or, does anybody have any suggestions for this problem? This seems like a relatively common requirement, but was unable to unearth any guidance.
Thanks!
In Ryan's introduction to CanCan he offers the following suggestion:
Make a New User object in memory for guest users on the site, but don't save it. This way all of your functions that need to associate to a user will still work, but they won't save.
See the railscast here: http://railscasts.com/episodes/192-authorization-with-cancan
Ryan's code example is:
So, in your app if you used this approach, then in your view you could check for
current_user.new_record?
, and rendering a different "save" button for registered users versus guests.You could make this pretty simple (avoiding storing this in session etc.) by providing a hidden account signup form on the sequence creation page. Then just make your "save" button for guests reveal the account creation form, and when they submit that form they're submitting a user registration and a sequence creation at the same time.
Then all your Sequences#create action needs to do is something like:
You'll need to turn that into working code but I'm confident the basic idea would work.
Good luck!
I'm also working on a rails 3 project w/ devise and cancan. My needs are a little different in that I need to persist anonymous users' activity in the db (no need to sweep). Here's what I did to sign in the anonymous user. Hope this helps.