Last active
August 29, 2015 14:00
-
-
Save mikeadeleke/68484c00fa4e1eaa685b to your computer and use it in GitHub Desktop.
Invite Roommates with Unique Tokens
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
property.rb | |
class Property < ActiveRecord::Base | |
before_create :generate_token | |
validates :total_bedrooms, presence: true | |
validates :total_bedrooms, numericality: true | |
validates :total_bathrooms, presence: true | |
validates :total_bathrooms, numericality: true | |
validates :address, presence: true | |
validates :address, length: { minimum: 5 } | |
validates :title, presence: true | |
validates :title, length: { maximum: 100 } | |
validates :description, presence: true | |
validates :description, length: { maximum: 400 } | |
validates :zip_code, presence: true | |
validates :zip_code, numericality: true | |
validates :city, presence: true | |
validates :city, length: { minimum: 5 } | |
validates :state, presence: true | |
validates :state, length: { minimum: 1 } | |
validate :has_at_least_3_images | |
validate :image_extensions | |
has_one :listing | |
has_many :rooms | |
has_many :images, :as => :attachable | |
has_many :tenants | |
accepts_nested_attributes_for :images, reject_if: Proc.new {|attr| attr[:photo].blank? } | |
geocoded_by :address | |
after_validation :geocode | |
reverse_geocoded_by :latitude, :longitude | |
after_validation :reverse_geocode | |
has_many :invites | |
has_paper_trail | |
def has_at_least_3_images | |
errors.add(:images, 'Please add at least 3 photos. Someone might live there :) ') unless images.size >= 3 | |
end | |
def image_extensions | |
errors.add(:images, | |
'Please add at least 3 photos with gif, jpg, or png extension' + | |
'. Someone might live there :) ') if images.length < 3 | |
end | |
private | |
def generate_token | |
self.token ||= SecureRandom.urlsafe_base64(8) | |
end | |
end | |
_form.html.erb (property) | |
<div class="property-form"> | |
<%= simple_nested_form_for @property, :html=> {:multipart => true } do |f| %> | |
<% if @property.errors.any? %> | |
<div id="error_explanation"> | |
<h2><%= pluralize(@property.errors.count, "error") %> prohibited this property from being saved:</h2> | |
<ul> | |
<% @property.errors.full_messages.each do |msg| %> | |
<li><%= msg %></li> | |
<% end %> | |
</ul> | |
</div> | |
<% end %> | |
<div class="field"> | |
<%= f.hidden_field :title %> | |
<div class="field"> | |
<%= f.hidden_field :total_bedrooms %> | |
<div class="field"> | |
<%= f.hidden_field :total_bathrooms %> | |
<div class="field"> | |
<%= f.hidden_field :address %> | |
<div class="field"> | |
<%= f.hidden_field :city %> | |
<div class="field"> | |
<%= f.hidden_field :state %> | |
<div class="field"> | |
<%= f.hidden_field :zip_code %> | |
<div class="field"> | |
<%= f.hidden_field :description %> | |
<div class="actions"> | |
</div> | |
</div></div></div></div></div></div> | |
</div> | |
<form class="form-horizontal"> | |
<fieldset> | |
<legend>Add your space</legend> | |
<div class="field"> | |
<ul> | |
<%= f.fields_for :images do |image_form| %> | |
<% unless image_form.object.new_record? %> | |
<ul class="present-images"> | |
<li> <%= image_tag image_form.object.photo.url %></li> | |
<li><%= image_form.link_to_remove "Remove this image" %></li> | |
</ul> | |
<% else %> | |
<li><%= image_form.file_field :photo %></li> | |
<li><%= image_form.link_to_remove "Remove this image" %></li> | |
<% end %> | |
<% end %> | |
</ul> | |
<p><%= f.link_to_add "Add photo", :images %></p> | |
</div> | |
<div class="form-group"> | |
<label for="inputEmail" class="col-lg-2 control-label">Title</label> | |
<div class="col-lg-10"> | |
<%= f.text_field :title, class: "form-control", id: "inputPassword" %> | |
</div> | |
</div> | |
<div class="form-group"> | |
<label for="inputPassword" class="col-lg-2 control-label">Total Bedrooms</label> | |
<div class="col-lg-10"> | |
<%= f.number_field :total_bedrooms, class: "form-control", id: "inputPassword" %> | |
<div class="checkbox"> | |
<label> | |
</label> | |
</div> | |
</div> | |
</div> | |
<div class="form-group"> | |
<label for="inputPassword" class="col-lg-2 control-label">Total Bathrooms</label> | |
<div class="col-lg-10"> | |
<%= f.number_field :total_bathrooms, class: "form-control", id: "inputEmail" %> | |
<div class="checkbox"> | |
<label> | |
</label> | |
</div> | |
</div> | |
</div> | |
<div class="form-group"> | |
<label for="inputPassword" class="col-lg-2 control-label">Address</label> | |
<div class="col-lg-10"> | |
<%= f.text_field :address, class: "form-control", id: "inputEmail" %> | |
<div class="checkbox"> | |
<label> | |
</label> | |
</div> | |
</div> | |
</div> | |
<div class="form-group"> | |
<label for="inputPassword" class="col-lg-2 control-label">City</label> | |
<div class="col-lg-10"> | |
<%= f.text_field :city, class: "form-control", id: "inputEmail" %> | |
<div class="checkbox"> | |
<label> | |
</label> | |
</div> | |
</div> | |
</div> | |
<div class="form-group"> | |
<label for="inputPassword" class="col-lg-2 control-label">State</label> | |
<div class="col-lg-10"> | |
<%= f.text_field :state, class: "form-control", id: "inputEmail" %> | |
<div class="checkbox"> | |
<label> | |
</label> | |
</div> | |
</div> | |
</div> | |
<div class="form-group"> | |
<label for="inputPassword" class="col-lg-2 control-label">Zip code</label> | |
<div class="col-lg-10"> | |
<%= f.text_field :zip_code, class: "form-control", id: "inputEmail" %> | |
<div class="checkbox"> | |
<label> | |
</label> | |
</div> | |
</div> | |
</div> | |
<div class="form-group"> | |
<label for="textArea" class="col-lg-2 control-label">Description</label> | |
<div class="col-lg-10"> | |
<%= f.text_area :description, class: "form-control", id: "textArea" %> | |
</div> | |
</div> | |
<div class="form-group"> | |
<label for="textArea" class="col-lg-2 control-label">Invite your roommates</label> | |
<div class="col-lg-10"> | |
<%= text_area_tag :emails %> | |
</div> | |
</div> | |
<div class="form-group"> | |
<div class="col-lg-10 col-lg-offset-2"> | |
<br><%= f.submit "Claim your place", class: "btn btn-primary" %> | |
</div> | |
</div> | |
</fieldset> | |
</form> | |
<% end %> | |
invites_controller.rb | |
class InvitesController < ApplicationController | |
def proceed | |
@invite = Invite.find_by_token(params[:token]) | |
if @invite | |
InviteMailer.invite_notification(@invite, @invite.user).deliver | |
redirect_to property_path(@invite.property) | |
end | |
end | |
end | |
mail_invite.rb | |
class MailInvite | |
def initialize(emails, property) | |
@property = Property.find(params[:id]) | |
@emails = separate_emails(email) | |
end | |
def call | |
@emails.each do |email| | |
invite = create_invite(email) | |
InviteMailer.invite_notification(@invite, @invite.user).deliver | |
end | |
end | |
private | |
def create_invite(email) | |
user = User.new(email: email) | |
@property.invites.create(user: user) | |
end | |
def separate_emails | |
params[:emails].gsub(' ','').split(',') if params[:emails].present? | |
end | |
end | |
routes.rb | |
Domimvp::Application.routes.draw do | |
get "invites/create" | |
devise_for :admin_users, ActiveAdmin::Devise.config | |
ActiveAdmin.routes(self) | |
get "comments/create" | |
get "faq" => "faq#index", as: :faq_index | |
get "how-it-works" => "about#index", as: :about_index | |
get "mission" => "mission#index", as: :mission_index | |
get "help" => "help#index", as: :help_index | |
get "inbox" => "dashboard#index", as: :dashboard_index | |
post 'twilio/voice' => 'twilio#voice' | |
match "sms/inbound" => "sms#inbound", via: [:get, :post] | |
# /beta_key/:invite_code | |
get 'invite/:token' => 'invites#proceed', as: :invite_token | |
resource :inbox, :controller => 'inbox', :only => [:show,:create] | |
resources :properties do | |
resource :image, only: [:create] | |
resources :rooms | |
end | |
resources :tenants | |
resources :dashboard, only: [:index] | |
resources :appointments do | |
resources :comments | |
end | |
resources :users | |
resources :listings do | |
resources :appointments | |
end | |
resources :profiles | |
resources :subletters | |
devise_for :users, controllers: {registrations: "users/registrations", sessions: "users/sessions", passwords: "users/passwords", omniauth_callbacks: "users/omniauth_callbacks"}, skip: [:sessions, :registrations] | |
# The priority is based upon order of creation: first created -> highest priority. | |
# See how all your routes lay out with "rake routes". | |
# You can have the root of your site routed with "root" | |
root 'homes#index' | |
devise_scope :user do | |
get "login" => "devise/sessions#new", as: :new_user_session | |
post "login" => "devise/sessions#create", as: :user_session | |
delete "signout" => "devise/sessions#destroy", as: :destroy_user_session | |
get "signup" => "devise/registrations#new", as: :new_user_registration | |
post "signup" => "devise/registrations#create", as: :user_registration | |
put "signup" => "devise/registrations#update", as: :update_user_registration | |
get "account" => "devise/registrations#edit", as: :edit_user_registration | |
end | |
end | |
invite_mailer.rb | |
class InviteMailer < ActionMailer::Base | |
default from: "[email protected]" | |
def invite_notification(invite, user) | |
@invite = invite | |
@url = invite_token_url(@invite.token) | |
mail(to: @user.email, subject: 'An invite with your name on it!') | |
end | |
end | |
properties_controller.rb | |
class PropertiesController < ApplicationController | |
#->Prelang (scaffolding:rails/scope_to_user) | |
before_filter :require_user_signed_in, only: [:new, :edit, :create, :update, :destroy] | |
before_action :set_property, only: [:show, :edit, :update, :destroy] | |
# GET /properties | |
# GET /properties.json | |
def index | |
@properties = Property.all | |
end | |
# GET /properties/1 | |
# GET /properties/1.json | |
def show | |
end | |
# GET /properties/new | |
def new | |
@property = Property.new | |
@property.images.build | |
end | |
# GET /properties/1/edit | |
def edit | |
@property = Property.includes(:images).find(params[:id]) | |
end | |
# POST /properties | |
# POST /properties.json | |
def create | |
@emails = separate_emails | |
@property = Property.new(property_params) | |
MailInvite.new(params[:email].call, @property) | |
respond_to do |format| | |
if @property.save | |
current_user.properties << @property | |
current_user.save | |
format.html { redirect_to new_listing_path, notice: 'Your place was claimed successfully.' } | |
else | |
format.html { render action: 'new' } | |
format.json { render json: @property.errors, status: :unprocessable_entity } | |
end | |
end | |
end | |
def invite_roommates | |
end | |
# PATCH/PUT /properties/1 | |
# PATCH/PUT /properties/1.json | |
def update | |
puts property_params.inspect | |
respond_to do |format| | |
if @property.update(property_params) | |
format.html { redirect_to @property, notice: 'Property was successfully updated.' } | |
format.json { head :no_content } | |
else | |
format.html { render action: 'edit' } | |
format.json { render json: @property.errors, status: :unprocessable_entity } | |
end | |
end | |
end | |
# DELETE /properties/1 | |
# DELETE /properties/1.json | |
def destroy | |
@property.destroy | |
respond_to do |format| | |
format.html { redirect_to properties_url } | |
format.json { head :no_content } | |
end | |
end | |
private | |
# Use callbacks to share common setup or constraints between actions. | |
def set_property | |
@property = Property.find(params[:id]) | |
end | |
def separate_emails | |
params[:emails].gsub(/ /,'').split(',') if params[:emails].present? | |
end | |
# Never trust parameters from the scary internet, only allow the white list through. | |
def property_params | |
params.require(:property).permit(:title, :total_bedrooms, :total_bathrooms, :address, :city, :state, :zip_code, :description, :commute_to_campus_in_minutes, :emails, :listing_id, :tenant_id, :token, images_attributes: [:photo,:id, :_destroy]) | |
end | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment