Using Rails 3.1.3 with Devise 1.5.3. My app has accounts and users. Each account can have multiple users. A user can have one role, "user" or "account_admin". Devise signup routes to accounts#new. The account and an initial account_admin user are created simultaneously. Got this working as described here (although things have evolved some since then).
An account_admin signs should be able to create additional users in that account. That's where I'm running into trouble: instead of creating new users, it's just redirecting to the user_path (users#show) page with the message "You are already signed in." (user_path is my overridden after_sign_in_path.)
This article asks a similar question. The answer suggests a separate namespace for admins, but since admins are part of my regular app I don't think that applies.
I've defined a complete UsersController. According to the log, GET "/users/new" renders from my "UsersController#new". However POST "/users" is intercepted by Devise and rendered from "Devise::RegistrationsController#create".
config/routes.rb
devise_for :users
devise_scope :user do
get "signup", :to => "accounts#new"
get "signin", :to => "devise/sessions#new"
get "signout", :to => "devise/sessions#destroy"
end
resources :users
resources :accounts
app/controllers/users_controller.rb
class UsersController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource # CanCan
...
def new
# CanCan: #user = User.new
end
def create
# CanCan: #user = User.new(params[:user])
#user.skip_confirmation! # confirm immediately--don't require email confirmation
if #user.save
flash[:success] = "User added and activated."
redirect_to users_path # list of all users
else
render 'new'
end
end
...
end
I've tried overriding the Devise controller, thinking I could tell it to use my users#create action if the user is already signed in. The log tells me it is using my controller ("Processing by RegistrationsController#create as HTML"), but it doesn't seem to execute its code. I've commented out my custom actions and just left in the logger lines, but I don't get my logging messages. And in spite of super being commented out, the behavior doesn't change--it still redirects with "You are already signed in."
config/routes.rb
devise_for :users, :controllers => {:registrations => "registrations"}
app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def new
logger.info "Custom RegistrationsController: new"
super
end
def create
logger.info "Custom RegistrationsController: create"
# super unless user_signed_in?
# render "users#create"
end
def update
super
end
end
What am I missing? How can I let the account_admin user create additional users?
The main issue is that Devise was intercepting the POST "/users" (and a few other routes). Found this workaround to allow my Users controller handle those routes: change the devise_for to skip registrations, then add back in the routes for which Devise normally defines aliases:
routes.rb
devise_for :users, :skip => [:registrations]
devise_scope :user do
get "signup", :to => "accounts#new"
get "signin", :to => "devise/sessions#new"
get "signout", :to => "devise/sessions#destroy"
get "cancel_user_registration", :to => "devise/registrations#cancel"
post "user_registration", :to => "users#create"
get "new_user_registration", :to => "accounts#new"
get "edit_user_registration", :to => "users#edit"
end
resources :users
resources :accounts
Never figured out why the Devise controller override wasn't working.
A user on this thread pointed out the devise_invitable gem which might be an interesting alternative.
This is how I ended up getting this to work with my setup (Devise tied to my User model -- no separate Editor/Admin model):
class UsermakersController < ApplicationController
before_action :authenticate_user!
def new
#user = User.new
end
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to users_path, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render users_path }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
def user_params
userParams = params.require(:user).permit(:name, :email)
end
end
From here, when you want to add a new User as another User, simply call the Usermakers#new url in your form, e.g., I have this at the bottom of my Users index page:
<%= link_to 'Add User', new_usermaker_path, id: 'new_user_link' %>
And the Usermakers form looks like:
= simple_form_for #user, url: usermakers_path, html: {multipart: true} do |f|
=f.error_notification errors: true
= f.input :name
= f.input :email
%p
= f.button :submit, 'Create User', class: 'button'
Of course, you'd need to add a dummy new.html.erb file which simply renders _form.html.erb to use this.
Just add the new and create methods to your routes.rb file (whether by resources :usermakers, or more specific routes) and you should be good to go. Hope this helps!
Related
What did I miss?
ERROR: undefined local variable or method `feedback' for #<#<Class:0x007f66dc8dca30>:0x007f66dc8cee80>
Migration:
class CreateFeedbacks < ActiveRecord::Migration
def change
create_table :feedbacks do |t|
t.references :user
t.text :body
t.timestamps
end
end
end
Model:
class Feedback < ActiveRecord::Base
attr_accessible :body, :user
belongs_to :user
end
Controller:
class FeedbacksController < ApplicationController
before_action :authenticate_user!
def index
#feedback = Feedback.all
end
def new
#feedback = Feedback.new
end
def create
#user = User.find(params[:user])
#feedback = #user.feedbacks.create(params[:feedback])
respond_to do |format|
if #feedback.save
format.html { redirect_to #user, notice 'Comment was successfully created.' }
format.json { render json: #feedback, status: :created, location: #feedback }
else
format.html { render action: "new" }
format.json { render json: #feedback.errors, status: :unprocessable_entily }
end
end
end
User model:
has_many :feedback
Routes:
resources :feedbacks
resources :users do
resources :feedbacks
end
Firstly, it helps a lot to style your question and code correctly as well as mark your files (routes.rb, controllers, models correctly). Else it would be very difficult for readers to understand your question and spot issues with your code. You can refer to https://meta.stackoverflow.com/editing-help for the styleguide/ markdown.
Secondly, it looks like your routes.rb are incorrect - but again, this could be due to your formatting. Based on your question, it looks like your routes are:
resources :feedbacks
resources :users do
resources :feedbacks
end
end
When it should be:
resources: users do
resources: feedbacks
end
Thirdly, your seem to have a typo in your model (feedback instead of feedbacks. That is:
class User < ActiveRecord::Base
has_many :feedbacks
end
class Feedback < ActiveRecord::Base
belongs_to :user
end
Lastly, do add specifics and what exactly you ran that caused the error. E.g. did you run something in console, what page did you load?
I have created an app that splits up a Student's Information into multiple forms.
Everything works fine, but when I try to render Form_One after a Validation Error, it does not render the appropriate URL/Page.
EX.
adults/1/students/2/form_one
turns into
adults/1/students/2
I need to render the same url so I can proceed to form_2.
MODELS
class Adult < ActiveRecord::Base
has_many :students
end
class Student < ActiveRecord::Base
belongs_to :adult
validates :firstName, :presence => true,
length: { maximum: 50 }
validates :lastName, :presence => true,
length: { maximum: 50 }
end
CONTROLLER
def update
#adult = Adult.find(params[:adult_id])
#student = Student.find(params[:id])
if #student.update_attributes(student_params)
###IF PASS REDIRECT TO THE NEXT FORM
if URI(request.referer).path == form_one_adult_student_path(#adult, #student)
redirect_to form_two_adult_student_path(#adult, #student)
elsif URI(request.referer).path == form_two_adult_student_path(#adult, #student)
redirect_to form_three_adult_student_path(#adult, #student)
else
redirect_to adult_path(#district, #adult)
end
else
error_messages = #student.errors.messages
#adult = Adult.find(params[:adult_id])
#student = Student.find(params[:id])
#student.errors.messages.merge!(error_messages)
###IF ERROR AND ON FORM_ONE RENDER FORM_ONE
if URI(request.referer).path == form_one_adult_student_path(#adult, #student)
###FOR SOME REASON THIS RENDERS adults/1/students/2
###BUT I NEED IT TO RENDER adults/1/students/2/form_one
render 'form_one'
end
end
end
def form_one
#title = "Student Information"
#adult = Adult.find(params[:adult_id])
#student = Student.find(params[:id])
end
def form_two
#title = "Parent Information"
#adult = Adult.find(params[:adult_id])
#student = Student.find(params[:id])
end
ROUTES
resources :adults do
resources :students do
member do
get :form_one, :form_two
end
end
end
###FOR SOME REASON THIS RENDERS adults/1/students/2
###BUT I NEED IT TO RENDER adults/1/students/2/form_one
render 'form_one'
Try redirect_to instead of render.
Keep in mind the difference between rendering a template, and making an HTTP request. Try pretending you are an HTTP client and think about the requests and responses. Sometimes it helps to look through your server log to see which controller actions happen, in which order. Look for lines like Processing students#update or Redirecting to ....
In the future, you may want to try resourceful routes like #show instead.
Problem solved thanks to #skipchris (https://twitter.com/skipchris) Using debugger we found a spelling mistake. The error report didn't make much sense but i learned plenty investigating the problem!
I've spent last night and this morning trying to find a solution but i'm new to rails. From what i've read so far it think the problem is to do with association. I created a Pin without a User (association error?) and so there is an error with routing i.e something to do with Devise?
When i try to create a new Pin i get this error in browser on my local server-
NoMethodError in PinsController#create
pp/controllers/pins_controller.rb:48:in block in create'
app/controllers/pins_controller.rb:47:increate'
pins_controller.rb
def create
#pin = current_user.pins.new(params[:pin])
respond_to do |format|
if #pin.save
format.html { redirect_to #pin, notice: 'Pin was successfully created.' }
format.json { render json: #pin, status: :created, location: #pin }
else
format.html { render action: "new" }
format.json { render json: #pin.errors, status: :unprocessable_entity }
end
end
end
User Model (user.rb)
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, #:recoverable,
:rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation,
:remember_me, :name
has_many :pins
end
Yeah, I would definitely make a user model since you are trying to access current_user.pins in the methods. also do you have
resources :pins
or something similar in your routes?
I'm trying to learn Rails by creating a very simple application which just creates a website where someone can create a list of authors and books with an association that the book is written by an author. I was hoping this would be simple and DRY, but I've been having an unexpected amount of trouble with it.
First looking at my models, I've set up the association and made every data point required (author.name, book.title, and book.author). I do not want to add :author or :author_id to the attr_accessible lists because I want to use the appropriate Rails conventions.
app/models/author.rb:
class Author < ActiveRecord::Base
attr_accessible :name
validates_presence_of :name
has_many :books
end
app/models/book.rb:
class Book < ActiveRecord::Base
attr_accessible :title
validates_presence_of :title
belongs_to :author
validates_associated :author
end
The books controller and view I think is exactly from the scaffolding and very uninteresting. What is interesting is the books controller. Looking at the new method, all I did was add a collector which gets the array of author names with ids to pass to the view. (Honestly, I think I would prefer to not pass the id at all.)
app/controllers/books_controller.rb
# GET /books/new
# GET /books/new.json
def new
#book = Book.new
#authors = Author.all.collect {|a| [a.name, a.id]}
respond_to do |format|
format.html # new.html.erb
format.json { render json: #book }
end
end
Now over to the views, I used the default new.html.haml, but made changes to _form.html.haml. I added a select field using the values in #authors.
app/views/books/_form.html.haml
= form_for #book do |f|
- if #book.errors.any?
#error_explanation
%h2= "#{pluralize(#book.errors.count, "error")} prohibited this book from being saved:"
%ul
- #book.errors.full_messages.each do |msg|
%li= msg
.field
= f.label :name
= f.text_field :name
.field
= f.label :author
= f.select(:author, #authors, {:include_blank => ""})
.actions
= f.submit 'Save'
Lastly, back to my controller for the create method. I try to save the basic parameters and create an author association from the selected author.
app/controllers/books_controller.rb
# POST /books
# POST /books.json
def create
#book = Book.new(params[:book])
#book.author = Author.find_by_id(params[:author])
respond_to do |format|
if #book.save
format.html { redirect_to #book, notice: 'Book was successfully created.' }
format.json { render json: #book, status: :created, location: #book }
else
format.html { render action: "new" }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
end
When I click 'Save' I get the following error:
Can't mass-assign protected attributes: author
I understand that this is because the value I selected was put in params[:book] instead of params[:author]. So I have two questions.
1) How do I fix my select statement to have it send in params[:author] instead of params[:book]?
2) Is there an even better way to do this that completely hides the id association?
"Can't mass-assign protected attributes: author" means that your attribute isn't listed as attr_accessible in your model
I think I've mostly figured it out, and as I suspected, I didn't need to change my model code at all.
I changed my controllers new method definition of #authors to only return author names with the following:
#authors = Author.pluck(:name)
This accomplished my goal of hiding the id though it's probably a tad slower when I need to search by name instead of id in the controller create method (below).
Next, I fixed my views to set params[:author] instead of params[:book][:author].
= label :author, :name, 'Author'
= select_tag(:author, options_for_select(#authors), {:include_blank => ""})
Finally, I changed the new methods creation of the #book:
#book = Author.find_by_name(params[:author]).books.create(params[:book])
I'm fairly happy with this. The only thing I don't really like is that the label creates an "author_name" label instead of simply "author".
I have following controller:
class CarsController < ApplicationController
autocomplete :user, :name
before_filter :require_user, :except => [:my_action]
def index
end
...
def my_action
end
end
I want to allow to see all actions in this controller only for log in users - this works me fine. But the action my_action I would like to have accesible for everyone - also for a people who are not log in.
I tried to set :before_filter with the :except parameter, also with the :only parameter, but nothing works me... The app always want for me to be log in... what I am doing still wrong?
EDIT: require_user from application_controller.rb:
def require_no_user
logger.debug "ApplicationController::require_no_user"
if current_user
#store_location
flash[:warning] = "You must be logged out to access this page"
redirect_to account_url
return false
end
end
Use
skip_before_filter :require_user, :only => [:my_action]