Trying to create user with membership using Devise - Need help with controller - ruby-on-rails-3

I have a standard devise installation, and I'm trying to add in functionality to add a user with a gym membership from an admin panel.
routes.rb
devise_for :users
resources :users
I am creating the user from the gym controller, so this is my gym action
def members
#gym = Gym.find(params[:id])
#user = User.new
#user.gym_users.build
#roles = Role.all
end
The gym_user is accepted in the user model
accepts_nested_attributes_for :gym_users
Then here is a portion of my form
<%= form_for #user do |f| %>
<%= f.label :email %><br />
<%= f.text_field :email %>
<% f.fields_for :gym_users do |builder| %>
<%= builder.label :item_id, "Membership Level" %><br />
<%= builder.collection_select(:item_id, #gym.membership_items, :id, :name, {:include_blank => true}) %>
<% end %>
<% end %>
What I'm having trouble with is my user controller where I actually create the user. This is the route for the membership page where the user is created
match 'gyms/:id/members' => 'gyms#members'
Finally, here's the create method on my users_controller
def create
#user = User.new(params[:user])
if #user.save
:notice => "User created successfully"
render :new
else
render :new
end
end
What I'm not sure is how to send back to that url when there is an error so that my model errors go with it, or redirect when it completes correctly.

I resolved this by making it an ajax call which eliminated the need for a redirect.

Related

Devise-Pundit: Create a page for Superadmin to create users, but it is not actually creating users

How do I enable the Superadmin to actually create Users? Do I need a policy CreateusersPolicy? My code currently takes me to a page/form where I can create a user, but it doesn't actually create the user.
Please let me know if I need to include more information!
config/routes.rb
Rails.application.routes.draw do
devise_for :users
resources :users, except: :create
root "pages#home"
get "index" => "users#index"
get 'create_user' => 'users#create', as: :create_user
controllers/application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
include Pundit
protect_from_forgery
def authorize_superadmin
redirect_to root_path, alert: 'Access Denied' unless current_user.superadmin?
end
end
I also don't know what to put here in the create section.
controllers/users_controller.rb
class UsersController < ApplicationController
before_filter :authenticate_user!
#before_filter :authorize_superadmin, except [:show]
#after_action :verify_authorized
def create
# user create code (can't get here if not admin)
end
def index
#users = User.all
authorize User
end
def show
#user = User.find(params[:id])
authorize #user
end
def update
#user = User.find(params[:id])
authorize #user
if #user.update_attributes(secure_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
user = User.find(params[:id])
authorize user
user.destroy
redirect_to users_path, :notice => "User deleted."
end
private
def secure_params
params.require(:user).permit(:role)
end
end
views/users/create.html.erb
<%= form_for User.new, url: create_user_path do |f| %>
<div><%= f.label :first_name %><br />
<%= f.text_field :first_name, autofocus: true %></div>
<div><%= f.label :last_name %><br />
<%= f.text_field :last_name, autofocus: true %></div>
<div><%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %></div>
<div><%= f.label :phone_number%><br />
<%= f.phone_field :phone_number, autofocus: true %></div>
<div><%= f.label :street %><br />
<%= f.text_field :street, autofocus: true %></div>
<div><%= f.label :city %><br />
<%= f.text_field :city, autofocus: true %></div>
<div><%= f.label :state %><br />
<%= f.text_field :state, autofocus: true %></div>
<div><%= f.label :zip %><br />
<%= f.text_field :zip, autofocus: true %></div>
<div><%= f.label :password %> <% if #validatable %><i>(<%= #minimum_password_length %> characters minimum)</i><% end %><br />
<%= f.password_field :password, autocomplete: "off" %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %></div>
<div><%= f.submit "Create" %></div>
<% end %>
app/polices/user_policy.rb
class UserPolicy
attr_reader :current_user, :model
def initialize(current_user, model)
#current_user = current_user
#user = model
end
def index?
#current_user.superadmin?
end
def show?
#current_user.superadmin? or #current_user == #user
end
def update?
#current_user.superadmin?
end
def destroy?
return false if #current_user == #user
#current_user.superadmin?
end
def permitted_attributes
if #current_user.superadmin?
[:role]
else
[:name, :email]
end
end
end
You don't have a create? method in the UserPolicy file so you aren't actually authorizing anything (as far as I can tell).
It should read like this:
# app/policies/user_policy.rb
def create?
#current_user.superadmin?
end
# app/controllers/users_controller.rb
def create
authorize User
# rest of method to create user
end
Also, you don't need to (or want to IMO) have the authorize_superadmin method (you do have the before_filter commented out in the controller, so you aren't calling it) because a) you will call the authorize method in your action and this would be redundant and b) you want to keep your authorization logic in one location: the UserPolicy class. If the authorization fails, it will raise an exception and will not call the rest of the action.
The Pundit documentation is a great resource to get everything setup, but it does take a little bit of trial and error.
I also highly suggest that you create an ApplicationPolicy that you inherit all of your model specific authorization from so that you can catch things that you may not have defined. It is all in the documentation.

Best steps for trouble shooting rails app

What's the best (simplest) way to walk through MVC and check if everything is set up right?
I get a bit frazzled and I feel like there must be a really simple fix to error messages like these:
undefined method `invitations_path' for #<#<Class:0x00000105ad5cb8>:0x00000105820b30>
After adding small amounts of code to my app things break and I want to trouble shoot them myself.
Thanks for the tips!
EDIT
Perhaps troubleshooting the specific issue will lead way to a generalized approach,
Link_to is not linking Used <%= %> instead of <% %>.
The above error is generated when visting localhost:3000/invitation/new
view (in home/index.erb.html)
<% if #user.invitation_limit > 0 %>
<% link_to 'Send Invitations', new_invitation_path %>
(<%= #user.invitation_limit %> left)
<% end %>
view (in invitation/new.erb.html)
<%= error_messages_for :invitation %>
<% 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 %>
controller
class InvitationController < ApplicationController
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
end
model
class Invitation < ActiveRecord::Base
belongs_to :sender, :class_name => 'User'
has_one :recipient, :class_name => 'User'
attr_accessible :recipient_email, :sender_id, :sent_at, :token
end
routes.rb
resources :home, :only => :index
resources :invitation
You can create request specs for each of your controller actions. Request specs follow the request all the way from the controller to rendering the view, and if there is an error it will show up in the request spec.
This may take time to set up, but will save you lots of time in the future, as you don't have to manually test every page when you want to roll out a new version of your website.

Rails Undefined Method 'model_name'

I have the following model:
class Contact
attr_accessor :name, :emails, :message
def initialize(attrs = {})
attrs.each do |k, v|
self.send "#{k}=", v
end
end
def persisted?
false
end
end
I am calling to a contact form in my view like so:
<div class="email_form">
<%= render 'form' %>
</div>
Here is the controller:
class ShareController < ApplicationController
layout "marketing_2013"
respond_to :html, :js
def index
#contact = Contact.new
end
end
Here is the Form:
<%= form_for(#contact) do |f| %>
<%= f.label :name, "Your Name" %>
<%= f.text_field :name %>
<%= f.label :text, "Send to (separate emails with a comma)" %>
<%= f.text_field :emails %>
<%= f.label :message, "Email Text" %>
<%= f.text_area :message %>
<%= f.submit %>
<% end %>
For some reason I keep getting this error:
undefined method model_name for Contact:Class
Any reason why what I have currently wouldn't work?
Besides the correct route in your config/routes.rb, you will also need these two instructions on your model:
include ActiveModel::Conversion
extend ActiveModel::Naming
Take a look at this question: form_for without ActiveRecord, form action not updating.
For the route part of these answer, you could add this to your config/routes.rb:
resources :contacts, only: 'create'
This will generate de following route:
contacts POST /contacts(.:format) contacts#create
Then you can use this action (contacts#create) to handle the form submission.
add include ActiveModel::Model to your Contact file
your route probably doesn't go where you think it's going and therefore #contact is probably nill
run "rake routes" and check the new path.. if you are using defaults, the route is
new_contact_path.. and the erb should be in file: app/views/contacts/new.html.erb
def new
#contact = Contact.new
end

uninitialized constant ConfirmationsController error while trying to overide devise confirmations controller

am trying to confirm a user account without using the built in devise confirmations controller but i happen to get the following error "uninitialized constant Confirmations Controller". Below is my confirmations controller class.
class ConfirmationsController < Devise::ConfirmationsController
def show
#user = User.find_by_confirmation_token(params[:confirmation_token])
if !#user.present?
render_with_scope :new
end
end
def confirm_account
#user = User.find(params[:user][:confirmation_token])
if #user.update_attributes(params[:user]) and #user.has_password?
#user = User.confirm_by_token(#user.confirmation_token)
flash[:notice] = "Hi " + #user.first_name + " your email has been verified. You can now start shopping and recommending other users to your supplier networks."
redirect_to #user
else
render :action => "show"
end
end
end
And in my routes.rb file i have the following:
devise_for :users, :controllers => { :confirmations => "confirmations" } do
match "confirm_account", :to => "confirmations#confirm_account"
end
And finally i have the following partial:
<p>Welcome <%= #user.first_name %>,</p><br/>
<%= form_for(resource, :url => confirm_account_path) do |f| %>
<%= f.label :email %>
<%= #user.email %>
<%= f.hidden_field :confirmation_token %>
<%= f.submit 'Confirm Account' %>
<p>Thank you for joining. Before you can purchase any item from your supplier or shared network, you will need to confirm your account first. Please follow the link below in order to confirm your account.</p>
<p><%= link_to 'Confirm my account', confirmation_url(#resource, :confirmation_token => #resource.confirmation_token) %></p><br/>
<p>Yours faithfully.</p>
<%end%>
Devise is can be easily modified for your needs. Here is a similar topic, which may be helpful for you:
Override devise registrations controller

Rails appends id to singular route when render edit after errors

I have the following singular route:
scope '/seller' do
resource :seller_profile, :path => "/profile", :only => [:show, :edit, :update]
end
and the following controller:
class SellerProfilesController < ApplicationController
before_filter :validate_user_as_seller
def show
#seller_profile = current_user.seller_profile
end
def edit
#seller_profile = current_user.seller_profile
end
def update
#seller_profile = current_user.seller_profile
if #seller_profile.update_attributes(params[:seller_profile])
redirect_to(seller_profile_path, :notice => 'Profile was successfully updated.')
else
render :action => "edit"
end
end
end
I use a singular route given that the user must be authenticated before gaining access to the controller and therefore I can get the seller_profile from the user logged in.
This works like a charm, with only one problem. When I edit the seller_profile and validation error happen, the form is edited again and the errors are displayed correctly. The problem is that rails appends to the url the id of the edited record. For instance,
when I first edit the record, the url is:
http://0.0.0.0:3000/seller/profile/edit
but if the form is submitted with validation errors, the form itself is redisplayed under
http://0.0.0.0:3000/seller/profile.2
where 2 is the ID of the record being edited.
The form is the following:
<%= simple_form_for #seller_profile do |f| %>
<%= f.input :name %>
<%= f.input :description %>
<%= f.submit %>
<% end %>
Everything, as said, works great but I would totally mask the ID in the url. What should I do?
I have not really worked too much with simple_form_for. But it looks like it is guessing your url always as if they were not single resources. You can provide a custom one:
<%= simple_form_for #seller_profile, :url => seller_profile_path do |f| %>
<%= f.input :name %>
<%= f.input :description %>
<%= f.submit %>
<% end %>