Save relation with omniauth identity - ruby-on-rails

I would like to let the user choose the service (free, standard, pro) in the registration step.
Here is the db model i'm working with:
(User)1---n(Subscription)n--1(Service)
In the "new user" form I have this customised code in order to let the user chose the service:
identities/new.hmtl.erb
<%= form_tag "/auth/identity/register", :html => {:role=>"form"} do %>
<div class="form-group">
<label>Service</label>
<%= select("service", "service_id", #available_services, {:selected => params[:service_id]}, {class:"form-control"}) %>
</div>
<div class="form-group">
<label>Name</label>
<%= text_field_tag :name, nil, class:"form-control" %>
<p class="help-block">Nome e cognome completo e corretto.</p>
</div>
<div class="form-group">
<label>Email</label>
<%= text_field_tag :email, #identity.try(:email), class:"form-control" %>
</div>
<div class="form-group">
<label>Password</label>
<%= password_field_tag :password, nil, class:"form-control"%>
</div>
<div class="form-group">
<label>Password confirmation</label>
<%= password_field_tag :password_confirmation, nil, class:"form-control"%>
</div>
<%= submit_tag "Sign up",class:"btn btn-default" %>
<% end %>
identities_controller.rb
class IdentitiesController < ApplicationController
skip_before_filter :require_login
layout "static"
def new
#available_services = Service.all.where(:id => [1, 2, 3]).collect {|p| [ p.name, p.id ] }
#identity = env['omniauth.identity']
end
end
Now, what's the way to create/save the user and the relation between the new user and the selected subscription?

I solved this by...
Adding an hidden field for the service:
#identitiy.new.html
<%= hidden_field_tag :service_id, params[:service_id] %>
Edited the omniauth initializer as follow:
#omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
provider :identity,:fields => [:service_id,:name,:email], on_failed_registration: lambda { |env|
IdentitiesController.action(:new).call(env)
}
end
Added attr_accessor and a after_create method in the Identity model:
#identity.rb
class Identity < OmniAuth::Identity::Models::ActiveRecord
attr_accessor :service_id
validates_presence_of :name
validates_uniqueness_of :email
#validates_format_of :email, :with => /^[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
after_create :add_subscription
private
def add_subscription
Subscription.create(:user_id => self.id, :service_id => service_id.to_f)
end
end

Related

Missing and inaccurate payload in Rails with ActiveModel and Forms

I have a model for Organisation like
class Organisation
include ActiveModel::Model
attr_accessor :orguid,
:title, :firstname, :lastname, :role, :telephone, :extension, :email,
:name, :branch, :address1, :address2, :address3, :city, :state, :country, :zip
end
In my controller I have the following actions:
# frozen_string_literal: true
require 'cgi'
require 'json'
class OrganisationsController < ApplicationController
include Secured
before_action :set_api, only: %i[dashboard create]
before_action :user_info, only: %i[dashboard register]
def dashboard
#registration = #api.registered?
end
def register
#organisation = Organisation.new
end
def create
organisation_params
register_data = params[:organisation].to_h
register_data['oruid'] = org_uid
#api.register(register_data)
end
private
def set_api
#api = CoreApi.new(org_uid)
end
def user_info
#user_info = session[:userinfo].to_h
end
def org_uid
CGI.escape(user_info['uid'])
end
def organisation_params
params.require(:organisation).permit!
end
end
in my register.html.erb I have:
<h1> Register Your Organisation</h1>
<%= form_with model: #organisation, url: org_register_path do |f| %>
<div class="container">
<h2>Your Details</h2>
<div class="form-row">
<div class="form-group col-md-2">
<%= f.label :title %>
<%= f.text_field :title, class: 'form-control' %>
</div>
<div class="form-group col-md-5">
<%= f.label :first_name %>
<%= f.text_field :firstname, class: 'form-control' %>
</div>
<div class="form-group col-md-5">
<%= f.label :last_name %>
<%= f.text_field :lastname, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<%= f.label :role %>
<%= f.text_field :role, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<%= f.label :telephone %>
<%= f.telephone_field :telephone, class: 'form-control' %>
</div>
<div class="form-group col-md-2">
<%= f.label :extension %>
<%= f.text_field :extension, class: 'form-control' %>
</div>
<div class="form-group col-md-6">
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control', readonly:'', value: #user_info['info']['name'] %>
</div>
</div>
</div>
<div class="container">
<h2>Organisation Details</h2>
<div class="form-row">
<div class="form-group col-md-6">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
</div>
<div class="form-group col-md-6">
<%= f.label :branch %>
<%= f.text_field :branch, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<%= f.label :address_line_1 %>
<%= f.text_field :address1, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<%= f.label :address_line_2 %>
<%= f.text_field :address2, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<%= f.label :address_line_3 %>
<%= f.text_field :address3, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<%= f.label :city %>
<%= f.text_field :city, class: 'form-control' %>
</div>
<div class="form-group col-md-4">
<%= f.label :state %>
<%= f.text_field :state, class: 'form-control' %>
</div>
<div class="form-group col-md-4">
<%= f.label :country %>
<%= f.text_field :country, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-2">
<%= f.label :zip %>
<%= f.text_field :zip, class: 'form-control' %>
</div>
</div>
</div>
<div class="container">
<div class="form-row">
<div class="form-group col-md-12">
<%= f.button :Register, class: 'btn btn-primary' %>
</div>
</div>
</div>
<% end %>
and finally the register method in my core_api.rb is this:
def register(data)
body = data.to_json
puts ">> >> >> >> #{body.class} :: #{body}"
options = { headers: { 'Content-Type' => 'application/json' }, body: body }
response = self.class.post('/organisations', options)
#puts ">>>>>>>>>>>> #{response}"
end
and finally my routes.rb file contains:
Rails.application.routes.draw do
get '/' => 'home#show'
get '/auth/auth0/callback' => 'auth0#callback'
get '/auth/failure' => 'auth0#failure'
get '/logout', to: 'logout#logout', as: 'logout'
get '/organisations/dashboard', to: 'organisations#dashboard', as: 'org_dashboard'
get '/organisations/register', to: 'organisations#register', as: 'org_register'
post '/organisations/register', to: 'organisations#create'
root 'home#show'
end
now when I run the server and submit the form in the logs I get:
>> >> >> >> String :: {"title":"","firstname":"","lastname":"","role":"","telephone":"","extension":"","email":"alijy3#yahoo.com","name":"we","branch":"we","address1":"we","address2":"","address3":"","city":"we","state":"","country":"we","zip":"","oruid":"auth0%7C5e5388493d670c11be833bca","contact_id":0}
which to me looks like a proper json. But, since the api response was continually unsuccessful I intercepted the outgoing post with Postman to see what payload is being sent. To my surprise the payload is not flat json, but comes out like this:
I have 2 problems with this:
The api accepts items like address1, address2, city, etc. I believe I should send those rather than the currently showing organisation[address1], organisation[address2], etc.
The second problem is that I add the orguid after the form is submitted and before calling/posting to the api. But, although I can see it in the log messages, I don't see the orguid in the postman payload in any form.
I don't have any database on the server. Everything is fetch/posted/saved through the api. I've been reading about how to work with Activemodel and forms for a while and I haven't managed to get this resolved yet. Any help or explanation would be much appreciated.
No offense but this is a train wreck. You don't need to break every rails convention just because you're not using ActiveRecord in this specific case.
Start off by using ActiveModel::Attributes#attribute instead of Ruby's built in attr_accessor.
class Organisation
include ActiveModel::Model
include ActiveModel::Attributes
[:orguid, :title, :firstname, :lastname, :role, :telephone,
:extension, :email, :name, :branch,
:address1, :address2, :address3, :city, :state, :country, :zip]
.each do |name|
attribute name
end
# #todo write validations!
end
This creates attributes that act like ActiveRecord attributes and you can serialize the model properly with #organization.as_json.
Then lets just start fresh on that controller as there is just too much smell for it to be worth salvaging.
# routes.rb
resources :organisations, only: [:new, :create]
class OganizationsController < ApplicationController
# GET /organizations/new
def new
#organization = Organization.new
end
# POST /organizations
def create
# You never manually parse out incoming params - thats Rack's job.
# also since you have a model - USE IT!
#organization = Organization.new(organization_params) do |o|
o.orguid = org_uid
end
# validate the user input before you send it to an external API
if #organization.valid? && #api.register(#organization)
redirect_to '/somewhere'
else
render :new
end
end
private
# use monads here instead of callbacks!
def user_info
# Rails will serialize/deserialize hashes automatically
# from the session
session[:userinfo]
end
def org_uid
# Have no clue what the heck you're doing with CGI escape.
#org_uid ||= user_info['uid']
end
def api
#api ||= CoreApi.new(org_uid)
end
def organization_params
# You don't have any reason to use 'permit!' and give
# yourself a potential mass assignment vunerablity
params.require(:organization)
.permit(
:title, :firstname, :lastname, :role, :telephone,
:extension, :email, :name, :branch,
:address1, :address2, :address3, :city,
:state, :country, :zip
)
end
end
Rename the view /organizations/new.html.rb. At this point you should be able to stub out the API and do an integration test with valid and invalid input.
That whole session[:userinfo] thing still smells really bad - if you are taking the response from OAuth and shoving it into the session your setting yourself up for a really bad time as that can cause cookie overflows. Also in general in Rails if you're ever manually casting/serializing then its a really good sign that your doing something very wrong.
Have no clue really whats going on in your CoreApi class but if you are using HTTParty you should not do ANY manual JSON encoding.
# #fixme name is way to generic.
class CoreApi
include HTTParty
format :json # sets content type and encodes the content
# ...
def register(organization)
response = self.class.post('/organisations', #organization.as_json)
if response.success?
true
else
#organization.errors.add(:base, 'Could not be registered')
false
end
end
end

Contacts view not updating from contacts/new view using the mail_form gem in rails

I am working with the mail_form gem. I have already created an specific controller for the contact form and everythink seems right but when I submit any form the view does´nt change from the create one to the create one but the url does. It shows the localhost3000/gmm/contacts(which has changed from localhost3000/gmm/contacts/new). I am worried about this issue; Moreover, the new view shows the e-mail in the name´s field as showed in the image:
"http://2.bp.blogspot.com/-Q4qUs1CHm-M/Voqc-VYiXII/AAAAAAAAAdE/nJMxgpTEu5s/s320/problem1.jpg"
Controller file:
class ContactsController < ApplicationController
def new
#contact = Contact.new
end
def create
#contact=Contact.new(params[:contact])
#contact.request=request
if #contact.deliver
flash.now[:error]=nil
else
flash.now[:error]='Cannot send message.'
render :new
end
end
end
New view:
<body>
<section>
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 text-center">
<h2 class="margin-top-0 wow fadeIn">Get in Touch</h2>
<hr class="primary">
<p>We love feedback. Fill out the form below and we'll get back to you as soon as possible.</p>
</div>
<div class="col-lg-10 col-lg-offset-1 text-center">
<%= form_for #contact do |f| %>
<div class="col-md-6 text-faded">
<%= f.label :name %>
<%= f.text_field :name, required: true, :class => "form-control", :placeholder => "Name"%>
</div>
<div class="col-md-6 text-faded">
<%= f.label :email %>
<%= f.email_field :name, required: true, :class => "form-control", :placeholder => "Email" %>
</div>
<div class="col-md-12 text-faded">
<%= f.label :message %>
<%= f.text_area :message, as: :text, :class => "form-control", :rows=>"9", :placeholder => "Your message here.."%>
</div>
<div class="hidden">
<%= f.label :nickname %><br>
<%= f.text_field :nickname, hint:"leave this field blank"%>
</div>
<div class="col-md-4 col-md-offset-4">
<%= f.submit "Send Message", :class=> "btn btn-primary btn-block btn-lg" %>
</div>
<%end%>
</div>
</div>
</div>
</section>
Model:
class Contact<MailForm::Base
attribute :name, :validate => true
attribute :email, :validate => /\A([\w\.%\+\-]+)#([\w\-]+\.)+([\w]{2,})\z/i
attribute :message, :validate => true
attribute :nickname,:captcha => true
def headers
{
:subject => "Contact Form",
:to => "kurtco_91#hotmail.com",
:to => "yomi.89gm#gmail.com",
:from => %("#{name}" <#email>)
}
end
end
Routes:
Rails.application.routes.draw do
resources :contacts, only: [:new,:create]
get 'gmm/home'
get 'gmm/about'
get 'gmm/services'
get 'gmm/contact'
get '/change_locale/:locale', to: 'settings#change_locale', as: :change_locale
I think it must be a problem with the post verb or my routing but I did rake: routes several times and no progress. Thanks a lot for your help. I really appreciate it
Answer to Email showing in name field is you are actually saving email into name field.
change email field from this:
<%= f.email_field :name, required: true, :class => "form-control", :placeholder => "Email" %>
to this:
<%= f.email_field :email, required: true, :class => "form-control", :placeholder => "Email" %>
Answer to URL issue is:
in your controller after according to my assessment you should save the contact if u need. if contact save successfully you should redirect to new_contact_path.
controller should look like this:
def create
#contact=Contact.new(params[:contact])
#contact.request=request
if #contact.deliver
flash.now[:error]=nil
else
flash.now[:error]='Cannot send message.'
redirect_to new_contact_path
end
end

Rails + Devise: Undefined Local Variable in UserRegistration#Edit

UPDATE:
I needed to put my controller code ABOVE super, like so and then I had access to the #company instance variable in the view:
User Registration Controller:
class UserRegistrationController < Devise::RegistrationsController
before_action :configure_permitted_parameters
skip_before_filter :load_customer_access, only: [:new, :create]
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:first_name, :last_name, :current_password, :password, :password_confirmation, :email) }
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:first_name, :last_name, :current_password, :password, :password_confirmation, :email) }
end
def edit
#customer = current_user.customer
#company = Company.find_by_domain(User.get_domain(#customer.email))
super
end
end
I want to pass through the Company ID of the currently logged-in user so I can then view all the users from the same company.
This page will be accessed via a button from the Edit Account page (automatically generated view with Devise). This is the page in which I get the below error:
Note: I only see this error with the button to 'Manage Users' page. If I comment out that line and look at my logs, I can see this fine:
User Model:
...
def self.get_domain(email_address)
email_address.gsub(/.+#([^.]+.+)/, '\1')
end
def confirm!
current_customer = Customer.where(email: self.email).first
super
end
def ensure_has_customer
customer = self.customer
company = Company.find_by_domain(User.get_domain(self.email))
end
...
User Registration Controller:
class UserRegistrationController < Devise::RegistrationsController
before_action :configure_permitted_parameters
skip_before_filter :load_customer_access, only: [:new, :create]
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:first_name, :last_name, :current_password, :password, :password_confirmation, :email) }
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:first_name, :last_name, :current_password, :password, :password_confirmation, :email) }
end
def edit
super
#customer = current_user.customer
#company = Company.find_by_domain(User.get_domain(#customer.email))
puts "HAPPY CUSTOMER"
puts #customer.email
puts "HAPPY COMPANY"
puts #company.id // See above screenshot for logs
end
end
Edit User Route:
match "company/:company_id/edit_users" => 'company#edit_users', via: [:get], :as => :edit_company_users
_edit_account.html.erb:
<%= form_for(current_user, :as => :user, :url => registration_path(:user), :html => { :method => :put }) do |f| %>
<div>
<h1 class="login-header">My Account</h1>
</div>
<div class="control-group">
<div class="controls">
<div class="input-group col-centered input-group-lg">
<%= f.text_field :first_name, :class => 'form-control custom-form-control', :placeholder => 'First name' %>
</div>
</div>
</div>
<div class="control-group">
<div class="controls">
<div class="input-group col-centered input-group-lg">
<%= f.text_field :last_name, :class => 'form-control custom-form-control', :placeholder => 'Last name' %>
</div>
</div>
</div>
<div class="control-group">
<div class="controls">
<div class="input-group col-centered input-group-lg">
<%= f.email_field :email, :autocomplete => "off", :class => 'form-control custom-form-control', :placeholder => 'Email' %>
</div>
</div>
</div>
<div class="control-group">
<div class="controls">
<div class="input-group col-centered input-group-lg">
<%= f.password_field :password, :autocomplete => "off", :class => 'form-control custom-form-control', :placeholder => 'New password' %>
</div>
</div>
</div>
<div class="control-group">
<div class="controls">
<div class="input-group col-centered input-group-lg">
<%= f.password_field :password_confirmation, :autocomplete => "off", :class => 'form-control custom-form-control', :placeholder => 'New password confirmation' %>
</div>
</div>
</div>
<div class="control-group">
<div class="controls">
<div class="input-group col-centered input-group-lg">
<%= f.password_field :current_password, :class => 'form-control custom-form-control', :placeholder => 'Current password' %>
</div>
</div>
</div>
<br>
<div class="form-actions">
<button type="submit" class="btn-account-primary">Update Account</button>
</div>
<% end %>
<% if #customerAccess.admin_role %>
<hr>
<%= link_to 'Manage Users', edit_company_users_path(#company), :class => 'btn btn-sm btn-default' %><br>
<% end %>
The code for my Company controller and views can reach the edit_company_users_path ok, and the Company controller code is shown below if that's helpful:
Company Controller:
class CompanyController < ApplicationController
def index
#companies = Company.all
respond_to do |format|
format.html
format.json { render json: #companies }
end
end
def edit_users
#company = Company.find(params[:company_id])
#company_name = #company.name
#domain = #company.domain
#find_domain = "%" + #domain + "%"
#users = User.find(:all, :conditions => ["email LIKE ?", #find_domain])
end
end
Company Index View:
<div class="col-md-12">
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-8">
<div class="page-header">
<h1>Manage Companies</h1>
</div>
<table class = "table table-striped table-bordered">
<tr>
<th>Company Name</th>
<th>Domain</th>
<th>Action</th>
</tr>
<% #companies.each do |company| %>
<tr>
<td><%= company.name %></td>
<td><%= company.domain %></td>
<td>
<%= link_to 'Manage Company Access', edit_company_path(company), :class => 'btn btn-sm btn-default' %>
<%= link_to 'Manage Applications', edit_company_applications_path(company), :class => 'btn btn-sm btn-default' %>
<%= link_to 'Manage Users', edit_company_users_path(company), :class => 'btn btn-sm btn-default' %><br>
</td>
</tr>
<% end %>
</table>
</div>
</div>
</div>
What's interesting to note is that when I do <%= #company %> in the view, no name of the Company appears (though the ID is 2, and name is 'Ryan Drake'), and displays ok in the view. When I do <%= #company.id %>, it throws "undefined method 'id' for nil:class".
Any guidance about accessing the company ID from the Edit Account Devise view in the easiest way possible, so I can pass that through as a parameter and hit the method would be superb.
In the code you show, there is an end and a if, I think you are probably closing the "each" loop before the line of the error, so the "company" variable is not found.
EDIT:
I see two potential problems:
It is definitely #company in edit_company_users_path(#company). If
it doesn't work, does it show the same error? Please show that.
I think you might be messing with devise routes in the alias
"edit_company_users_path", but I don't know how are you organizing
your resources. If you are nesting companies under users, the
default route for that would be something like
edit_company_users_path(#company, current_user). Run rake routes if
you get a routing error.

Rails 4 and Devise: Having on field value (email) in the registration form, entered into both parent and nested model tables at sign up

I am using devise.
I have a member model (which is the model that devise uses)
A member has a 1 to 1 relationship with a user.
I have a user model that contains all the profile information of the user.
The purpose of this is to keep the users info separate from devise.
I have a sign up form that has a nested form for user...therefore when the member signs up, on that same page they enter their info and that gets put in the user table.
The issue is simple enough.
I have one email field on the form (for member).
I want the value of email to also get entered into the database on save.
I tried
class Member < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_one :user, dependent: :destroy, autosave: true
accepts_nested_attributes_for :user
before_create :create_user
def create_user
user = User.new(:email => self.email)
user.save
end
end
But what is actually happening with this is that 2 users get saved into the user table. One with the email field filled in and the rest blank, and a second with all the other attributes filled in but no email.
Here is my custom registration controller
class Members::RegistrationsController < Devise::RegistrationsController
# GET /resource/sign_up
def new
build_resource({})
resource.build_user
respond_with self.resource
end
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u|
u.permit(:email, :password, :password_confirmation, :user_attributes => :first_name)
}
end
private
def sign_up_params
params.require(:member).permit(:email, :password, :password_confirmation, user_attributes: [:member_id, :email, :first_name, :last_name, :institution, :job_title, :about, :picture])
end
end
The question is how can I get the email value into the member table AND the user table (along with the other data)
Here is the form. Don't like posting all this, but I guess it maybe required.
<div class="col-md-8 col-md-offset-2 col-sm-8 col-sm-offset-2 col-xs-12">
<%= bootstrap_devise_error_messages! %>
<div class="panel panel-default">
<div class="panel-heading">
<h4><%= t('.sign_up', :default => "Sign up") %></h4>
</div>
<div class="panel-body">
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), html: { role: "form" }) do |f| %>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, autofocus: true, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :password %><br />
<%= f.password_field :password, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, class: "form-control" %>
</div>
<%= f.fields_for(:user) do |user_fields| %>
<div class="form-group">
<%= user_fields.label :first_name %><br>
<%= user_fields.text_field :first_name, autofocus: true, class: 'form-control' %>
</div>
<div class="form-group">
<%= user_fields.label :last_name %><br>
<%= user_fields.text_field :last_name, class: 'form-control' %>
</div>
<div class="form-group">
<%= user_fields.label :job_title %><br>
<%= user_fields.text_field :job_title, class: 'form-control' %>
</div>
<div class="form-group">
<%= user_fields.label :institution %><br>
<%= user_fields.text_field :institution, class: 'form-control' %>
</div>
<div class="form-group">
<%= user_fields.label :about %><br>
<%= user_fields.text_area :about, class: 'form-control' %>
</div>
<span class="picture">
<%= user_fields.label :picture, "Upload photo" %>
<%= user_fields.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %>
</span>
<% end %>
<%= f.submit t('.sign_up', :default => "Sign up"), class: "btn btn-primary" %>
<% end %>
</div>
</div>
<%= render "devise/shared/links" %>
</div>
def create_user
user = User.new(:email => self.email)
user.save
end
This is doing exactly what you are asking it to do, which is create a new User object.
The easiest way to accomplish what you want, is to update the associated User object once your Member object is saved. Like this
# member.rb
after_save :update_user
def update_user
user.email = email
user.save
end
Here, you are not creating a new User object, you are referencing the User object that is associated to the Member object via that has_one association you specified earlier.

Rails - Right / Better way to to a belongs_to / has_many relationship

I am creating an application through which a user will be able to create an account. When they create an account, in the same form they will be able to create an organization that will then be tied to their user. Once that user has created their account (and an organization) other users will be able to create an account and use an "access code" to join that organization as well. Looking at the code may explain it better.
The reason i'm posting on SO is because i have a feeling there is a better / more efficient way to do it than what i am currently doing. I'm using nested_forms (maybe not correctly) and i don't think i'm doing the associations the right way because, for example, i haven't been able to get the edit form to fill out the organization fields.
I am using sorcery for the authentication as well.
users_controller.rb
def new
#user = User.new
end
def create
#user = User.new(user_params)
if params[:user][:organization][:name].blank?
flash.now[:error] = "You must specify an organization name."
render :new
else
if params[:user][:organization][:access_code].blank?
# create new organization
#access_code = "#{SecureRandom.urlsafe_base64(16)}#{Time.now.to_i}"
#organization = Organization.create(:name => params[:user][:organization][:name], :access_code => #access_code)
#user.organization_id = #organization.id
#user.is_admin = true
else
# try and add someone to an organization
#organization = Organization.find(:all, conditions: ["name = ? AND access_code = ?", params[:user][:organization][:name], params[:user][:organization][:access_code]])
if #organization.empty?
flash.now[:error] = "No organization has been found with that name and access code."
render :new
return
else
#user.organization_id = #organization.first.id
end
end
if #user.save
user = login(#user.email, params[:user][:password])
if user
flash[:success] = "Your account has been successfully created!"
redirect_to admin_dashboard_path
end
else
flash.now[:error] = "Something went wrong! Please try again."
render :new
end
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.is_admin?
if params[:user][:organization][:name].blank? && params[:user][:organization][:name] != #user.organization.name
params[:user][:organization][:name] = #user.organization.name
end
if params[:user][:organization][:access_code].blank? && params[:user][:organization][:access_code] != #user.organization.access_code
params[:user][:organization][:access_code] = #user.organization.access_code
end
#organization = Organization.find(params[:user][:organization_id])
#organization.name = params[:user][:organization][:name]
#organization.access_code = params[:user][:organization][:access_code]
#organization.save
end
if #user.update(user_params)
flash[:success] = "Your settings have been updated!"
redirect_to edit_admin_user_path(#user.id)
else
flash.now[:error] = "Something went wrong! Please try again."
render :edit
end
end
private
def user_params
params.require(:user).permit(:organization_id, :email, :password, :password_confirmation, :full_name, :remember_me, {:organization_attributes => [:name, :website, :description, :access_code]})
end
users.rb
class User < ActiveRecord::Base
authenticates_with_sorcery!
belongs_to :organization
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates_presence_of :full_name
validates_presence_of :email
validates_uniqueness_of :email, :on => :create
validates_format_of :email, :with => VALID_EMAIL_REGEX, :on => :create
validates_presence_of :password, :on => :create
validates_confirmation_of :password
end
organization.rb
class Organization < ActiveRecord::Base
authenticates_with_sorcery!
has_many :users, :dependent => :destroy
accepts_nested_attributes_for :users
validates_presence_of :name
end
new.html.erb
<% provide(:title, 'Create a User') %>
<h1>Create a User</h1>
<p>Use the form below to create an account.</p>
<%= nested_form_for([:admin, #user], html: {role: "form"}) do |f| %>
<%= render "shared/error_messages", obj: #user %>
<fieldset>
<legend>User Information</legend>
<div class="form-group">
<%= f.label :full_name, "Full Name" %>
<span class="help-block">How should others see you?</span>
<%= f.text_field :full_name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :email %>
<span class="help-block">Your email address is used as your login.</span>
<%= f.text_field :email, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :password_confirmation, "Confirm Password" %>
<%= f.password_field :password_confirmation, class: "form-control" %>
</div>
</fieldset>
<%= f.fields_for :organization do |o| %>
<fieldset>
<legend>Associated Organization</legend>
<div class="form-group">
<%= o.label :name, "Organization Name" %>
<span class="help-block">This is the name of the organization you are a part of.</span>
<%= o.text_field :name, class: "form-control" %>
</div>
<div class="form-group">
<%= o.label :access_code, "Organization Access Code" %>
<span class="help-block">Leaving this field blank will setup a new organization.</span>
<%= o.text_field :access_code, class: "form-control" %>
</div>
</fieldset>
<% end %>
<div class="form-actions">
<%= f.submit "Create Account", class: "btn btn-primary" %>
<%= link_to "Cancel", :back, class: "text-btn" %>
</div>
<% end %>
edit.html.erb
<% provide(:title, "Edit User: #{#user.full_name} (#{#user.organization.name})") %>
<h1>Edit User: <%= #user.full_name %> (<%= #user.organization.name %>)</h1>
<p>Use the form below to manage your account.</p>
<%= nested_form_for([:admin, #user], html: {role: "form"}) do |f| %>
<%= render "shared/error_messages", obj: #user %>
<fieldset>
<legend>User Information</legend>
<div class="form-group">
<%= f.label :full_name, "Full Name" %>
<span class="help-block">How should others see you?</span>
<%= f.text_field :full_name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :email %>
<span class="help-block">Your email address is used as your login.</span>
<%= f.text_field :email, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, placeholder: "leave blank to keep password unchanged", class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :password_confirmation, "Confirm Password" %>
<%= f.password_field :password_confirmation, class: "form-control" %>
</div>
</fieldset>
<% if #user.is_admin? %>
<%= f.fields_for :organization do |o| %>
<fieldset>
<legend>Associated Organization</legend>
<div class="form-group">
<%= o.label :name, "Organization Name" %>
<span class="help-block">This is the name of the organization you are a part of.</span>
<%= o.text_field :name, class: "form-control", value: #user.organization.name %>
</div>
<div class="form-group">
<%= o.label :access_code, "Organization Access Code" %>
<span class="help-block">Leaving this field blank will setup a new organization.</span>
<%= o.text_field :access_code, class: "form-control", value: #user.organization.access_code %>
</div>
</fieldset>
<% end %>
<%= f.hidden_field :organization_id %>
<% end %>
<div class="form-actions">
<%= f.submit "Update User", class: "btn btn-primary" %>
<%= link_to "Cancel", :back, class: "text-btn" %>
</div>
<% end %>
Ok, those are all the files making it happen. Now, i have the application doing almost everything i need it to do but this doesn't feel like production-level code to me.
One issue i know i am having is that if a user types something in the organization field and nothing else the controller will create and save the organization and then render the form back with the user validation errors. I don't want it to save the organization if there are validation errors in the user model.
I'm really just asking for advice if there is a better way of doing what i am trying to do. If you can't tell exactly what i'm trying to do with this code or have any questions please let me know!
Take a look at this post: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
Of particular interest will be the section on "3. Extract Form Objects".

Resources