#124 Beta Invitations
You know those invitation systems where a given user can invite a number of other people to join? That's what I show you how to make in this episode.
- Download:
- source codeProject Files in Zip (386 KB)
- mp4Full Size H.264 Video (33.5 MB)
- m4vSmaller H.264 Video (22.8 MB)
- webmFull Size VP8 Video (62.5 MB)
- ogvFull Size Theora Video (47 MB)
Resources
- Episode 82: HTTP Basic Authentication
- Episode 61: Sending Email
- Episode 16: Virtual Attributes
- Nifty Generators
bash
script/generate nifty_scaffold invitation sender_id:integer recipient_email:string token:string sent_at:datetime new script/generate migration add_invitation_to_users invitation_id:integer invitation_limit:integer script/generate mailer Mailer invitation
models/invitation.rb
belongs_to :sender, :class_name => 'User' has_one :recipient, :class_name => 'User' validates_presence_of :recipient_email validate :recipient_is_not_registered validate :sender_has_invitations, :if => :sender before_create :generate_token before_create :decrement_sender_count, :if => :sender private def recipient_is_not_registered errors.add :recipient_email, 'is already registered' if User.find_by_email(recipient_email) end def sender_has_invitations unless sender.invitation_limit > 0 errors.add_to_base 'You have reached your limit of invitations to send.' end end def generate_token self.token = Digest::SHA1.hexdigest([Time.now, rand].join) end def decrement_sender_count sender.decrement! :invitation_limit end
models/user.rb
validates_presence_of :invitation_id, :message => 'is required' validates_uniqueness_of :invitation_id has_many :sent_invitations, :class_name => 'Invitation', :foreign_key => 'sender_id' belongs_to :invitation before_create :set_invitation_limit attr_accessible :login, :email, :name, :password, :password_confirmation, :invitation_token def invitation_token invitation.token if invitation end def invitation_token=(token) self.invitation = Invitation.find_by_token(token) end private def set_invitation_limit self.invitation_limit = 5 end
invitation_controller.rb
def new @invitation = Invitation.new end def create @invitation = Invitation.new(params[:invitation]) @invitation.sender = current_user if @invitation.save if logged_in? Mailer.deliver_invitation(@invitation, signup_url(@invitation.token)) flash[:notice] = "Thank you, invitation sent." redirect_to projects_url else flash[:notice] = "Thank you, we will notify when we are ready." redirect_to root_url end else render :action => 'new' end end
users_controller.b
def new @user = User.new(:invitation_token => params[:invitation_token]) @user.email = @user.invitation.recipient_email if @user.invitation end
routes.rb
map.signup '/signup/:invitation_token', :controller => 'users', :action => 'new'
models/mailer.rb
def invitation(invitation, signup_url) subject 'Invitation' recipients invitation.recipient_email from 'foo@example.com' body :invitation => invitation, :signup_url => signup_url invitation.update_attribute(:sent_at, Time.now) end
mailer/invitation.erb
You are invited to join our beta!
<%= @signup_url %>
invitations/new.html.erb
<% form_for @invitation do |f| %> <p> <%= f.label :recipient_email, "Friend's email address" %><br /> <%= f.text_field :recipient_email %> </p> <p><%= f.submit "Invite!" %></p> <% end %>
home/index.html.erb
<p>We are currently in private beta. Please submit your email address below, and we will notify you when we are ready to accept more users.</p> <% form_for Invitation.new do |f| %> <p> <%= f.label :recipient_email, "Your Email:" %> <%= f.text_field :recipient_email %> <%= f.submit 'Submit' %> </p> <% end %>
users/new.html.erb
<%= f.hidden_field :invitation_token %>