I'm trying to think of the best way to allow users to create database objects without creating an account but then associating those objects with a user's account when they decide to sign up. I will describe an example application, how I would like it to behave, and the approaches I have come up with. I am looking for criticisms/enhancements to my approaches or a better approach purposed by one of you, my fellow programmers.
Example Application
Let's say we have a Rails application called Suggestion Box. This application allows users to visit the site and enter a suggestion. A Suggestion
requires a description and an email address. The user does not need to be logged in. Here are the models:
app/models/suggestion.rb
class Suggestion < ActiveRecord::Base
# == DB Columns
# id :integer
# user_id :integer
# description :text
# email :string
belongs_to :user
validates :description, :email, :presence => true
end
app/models/user.rb
class User < ActiveRecored::Base
# == DB Columns
# id :integer
# name :string
# email :string
has_many :suggestions
validates :name, :email, :presence => true
end
Use Case
Frank comes to the Suggestion Box application and enters in a suggestion. The next day Frank enters in another. If we ask the Rails console for all of suggestions we will get this:
# rails console
Suggestion.all
=> [#<Suggestion id: 1, user_id: nil, email: "frank@example.com", description: "Don't worry">,
#<Suggestion id: 2, user_id: nil, email: "frank@example.com", description: "Be happy">]
On the third day Frank decides to bite the bullet and sign up for an account. He then creates another suggestion. At this point if we ask the console for User
objects we get Frank:
# rails console
User.all
=> [#<User id: 1, name: "Frank", email: "frank@example.com" >]
If we ask for Frank's suggestions we get all 3 suggestions:
# rails console
frank = User.last
=> [#<User id: 1, name: "Frank", email: "frank@example.com" >
frank.suggestions
=> #<ActiveRecord::Associations::CollectionProxy [#<Suggestion id: 1, user_id: nil, email: "frank@example.com", description: "Don't worry">,
#<Suggestion id: 2, user_id: nil, email: "frank@example.com", description: "Be happy">,
#<Suggestion id: 3, user_id: nil, email: "frank@example.com", description: "Don't worry be happy now!">]
Now how do we make this connection between Frank and his past suggestions automatic?
Approaches
#1 Cookies
When a non logged in user submits a session I could store an indicator in a cookie about the created suggestions. Then when they sign up I could use this info to find the suggestions and assign them to the user.
I don't really like this approach because it breaks down when the user accesses more than one device.
#2 After Create Callback
When a user signs up I could run an after create callback that would look like this:
app/models.user.rb
class User < ActiveRecored::Base
...
after_create :assign_suggestions
def assign_suggestions
suggestions = Suggestion.where(:user_id => nil, :email => self.email)
suggestions.each do |s|
s.user = self
s.save
end
end
end
I really like this approach but there is one major downside. If Frank creates a fourth suggestion when not logged in it will not be added to his user object. To fix this I could write a callback on the Suggestion
class that could check for matching emails.
app/models/suggestion.rb
class Suggestion < ActiveRecord::Base
...
after_create :assign_users
def assign_users
if self.user.present?
user = User.where(:email => self.email).first
if user
self.user = user
end
end
end
end
This is pretty good but if Frank creates a suggestion with a different email address (which could happen, I have many) then the suggestion will not be connected to Frank's user object.
Question
What do you guys think of Approach #2? (good or bad) Is there another trick on top of this I could use to connect Frank to suggestions he makes with other email addresses? Can you think of a different approach that would work better than mine?
Thanks for the advice!