undefined method regular_user_path (oath object oriented approach) - ruby-on-rails

I have implemented an oath object oriented approach to my rails application using this tutorial. I've also split my User Form into an Edit & Account Form.
User Edit Form
-Name
-Username
-AboutMe
User Account Form
-Email
-Password
-Password Confirmation
Everything seems to be working okay, until I try to Edit a Regular User's Account or Profile. For some reason I keep getting this error.
undefined method `regular_user_path' for #<#<Class:0x007fc1952f6da0>:0x007fc18bf350a8>
I am not sure why this may be happening. Here is my code below.
Models
class User < ActiveRecord::Base
attr_accessible :name, :bio, :avatar, :username
end
class RegularUser < User
has_secure_password
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
VALID_UNAME_REGEX = /^[a-z](\w*[a-z0-9])*$/i
validates :name, length: { maximum: 50 }
validates :username, :presence => true,
:length => { :maximum => 15 },
:format => { :with => VALID_UNAME_REGEX },
:uniqueness => { :case_sensitive => false }
validates :bio, length: { maximum: 50 }
end
Controller
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:index, :profile, :update, :destroy, :following, :followers, :account]
before_filter :correct_user, only: [:edit, :update, :account]
before_filter :admin_user, only: [:destroy]
def edit
#user = User.find_by_username(params[:id])
end
def account
#title = "Account"
#user = User.find_by_username(params[:id])
end
def update
#user = RegularUser.find(current_user.id) ###I think this is causing it
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated"
sign_in #user
redirect_to root_url
else
if URI(request.referer).path == edit_user_path ###redirects to appropriate page
render 'edit'
else
render 'account'
end
end
end
def destroy
User.find_by_username(params[:id]).destroy
flash[:success] = "User destroyed."
redirect_to users_url
end
private
def signed_in_user
unless signed_in?
store_location
redirect_to (root_path), notice: "Please sign in."
end
end
def correct_user
#user = User.find_by_username(params[:id])
redirect_to(root_path) unless current_user?(#user)
end
def admin_user
redirect_to(root_path) unless current_user.admin?
end
end
Views
Edit View
<%= form_for #user, :html => { :multipart => true } do |f| %> ###error comes from this line
<%= render 'shared/error_messages', object: f.object %>
<div class="statictitle">Your Profile</div>
<%= f.text_field :username, placeholder: "Username..", :class => "form-control" %>
<%= f.text_field :name, placeholder: "Name", :class => "form-control" %>
<%= f.text_area :bio, placeholder: "About yourself in 160 characters or less...", class: "textinput" %>
<%= f.submit "Update Profile", class: "btn btn-primary" %><br>
<% end %>
Shared Errors
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-error">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li>* <%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
Routes
resources :users do
member do
get :account
end
end
LOGS
Started PUT "/users/ClickOnComics" for 127.0.0.1 at 2014-02-20 17:17:50 -0800
Processing by UsersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"1CUMHhkE10ubS6uuc26fu1yTGn1bABKNqRIJ67EhEO4=",
"user"=>{"email"=>"clickoncomics#gmail.com", "password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]"}, "commit"=>"Update Account", "id"=>"ClickOnComics"}
User Load (0.9ms) SELECT "users".* FROM "users"
WHERE "users"."remember_token" = 'Wqodv-nd6EhKseqQf9FhqA' LIMIT 1
User Load (1.1ms) SELECT "users".* FROM "users"
WHERE "users"."username" = 'ClickOnComics' LIMIT 1
RegularUser Load (0.5ms) SELECT "users".* FROM "users"
WHERE "users"."id" = $1 LIMIT 1 [["id", 1]]
(0.2ms) BEGIN
RegularUser Exists (2.7ms) SELECT 1 AS one FROM "users" WHERE
(LOWER("users"."username") = LOWER('ClickOnComics') AND "users"."id" != 1)
LIMIT 1
RegularUser Exists (2.7ms) SELECT 1 AS one FROM "users"
WHERE (LOWER("users"."email") = LOWER('clickoncomics#gmail.com') AND "users"."id" != 1) LIMIT 1
(0.2ms) ROLLBACK
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."username" = 'ClickOnComics' LIMIT 1
Completed 500 Internal Server Error in 237ms
NoMethodError (undefined method `errors=' for #<User:0x007fb6fadf87d0>):
app/controllers/users_controller.rb:40:in `update'

In your update action do as suggested below :
def update
#user = RegularUser.find(current_user.id)
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated"
sign_in #user
redirect_to root_url
else
error_messages = #user.errors.messages ### Capture the error messages
#user = User.find_by_username(params[:id]) ### Add this line
#user.errors.messages.merge!(error_messages) ### Set the error messages hash
if URI(request.referer).path == edit_user_path
render 'edit'
else
render 'account'
end
end
end
When your update fails, you need to reset #user to User instance otherwise when you render edit view it will receive RegularUser instance rather than User instance whereas your form is expecting User instance.

Try adding resources :regular_users to your routes.rb file

Related

User login authentication does not work in Ruby on Rails

I'm trying to log a user into my rails site via the login page. My user enters in their email and password in order to log in, but when they click the submit button, the page just refreshes itself - but it should be taking the user to their dashboard (user profile screen).
I can't seem to pinpoint why. Though here is what I get back from the database:
Started POST "/login" for 96.49.105.234 at 2016-01-19 16:50:41 +0000
Processing by SessionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"zo/bLDBFtOAthi/vD8nU5Iqdn3TcsdB/NS8SJKW/l190YC7uXMLw6+EFWl6FPf3hDmVTTN2ozZ4t/8lanYZHhg==", "session"=>{"email"=>"myuser#live.ca", "password"=>"[FILTERED]"}, "commit"=>"Log in"}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT 1 [["email", "myuser#live.ca"]]
Rendered sessions/new.html.erb within layouts/application (1.5ms)
Completed 200 OK in 153ms (Views: 71.4ms | ActiveRecord: 0.4ms)
See code below:
sessions_controller.rb
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email] .downcase)
if user && user.authenticate(params[:session][:password])
log_in user
redirect_to user
else
render 'new'
end
end
def destroy
end
end
sessions_helper.rb
module SessionsHelper
def log_in(user)
session[:user_id] = user.id
end
#Returns the current logged-in user
def current_user
#current_user ||= User.find_by(id: session[:user_id])
end
end
new.html.erb
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(:session, url: login_path) do |f| %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.submit "Log in", class: "btn btn-primary" %>
<% end %>
<p>New user? <%= link_to "Sign up now!", signup_path %> </p>
</div>
</div>
user.rb
class User < ActiveRecord::Base
has_secure_password
validates_length_of :password, :in => 6..20, :on => :create
validates :password_confirmation, presence: true, if: -> { new_record? || changes["password"] }
end
users_controller.rb
class UsersController < ApplicationController
def create
#user = User.new(user_params)
if #user.save
flash[:success] = "You signed up successfully"
flash[:color] = "valid"
redirect_to #user
else
flash[:notice] = "Form is invalid"
flash[:color] = "invalid"
render "new"
end
end
private
def user_params
params.require(:user).permit(:firstname, :lastname, :email, :password, :password_confirmation)
end
def show
#user = User.find(params[:id])
end
def new
end
end

Ruby on Rails - Adding comments to post

Trying to learn RoR. Currently adding comments to posts by user. So far I have a posts model, comments model and post_comments model (to associate the two). So for in 'rails console' I can run: (say I set p = Post.first and c = Comment.first)
p.comments << c
This forms an association so it works in the console. I just can't seem to get the comments to form this association from the UI. So far I am creating the comments at "comments/new" (not sure if this is the issue. Do they need to be created on the "show view" for "post").
Here are some code snippets
Controllers
comments_controller.rb
class CommentsController < ApplicationController
def index
#comment = Comment.all
end
def new
#comment = Comment.new
end
def create
#comment = Comment.new(commentParams)
if #comment.save
flash[:success] = "Comment successfully added"
redirect_to comments_path(#comment)
else
render 'new'
end
end
def show
#comment = Comment.find(params[:id])
end
private
def commentParams
params.require(:comment).permit(:comment)
end
end
posts_controller
class PostsController < ApplicationController
before_action :setPost, only: [:edit, :update, :show, :destroy, :sold]
before_action :requireUser, except: [:index, :show]
before_action :requireSameUser, only: [:edit, :update, :destroy, :sold]
def index
#posts = Post.paginate(page: params[:page], per_page: 20)
end
def new
#post = Post.new
end
def create
#post = Post.new(postParams)
#post.user = currentUser
if #post.save
flash[:success] = "Post successfully added."
redirect_to post_path(#post)
else
render 'new'
end
end
def update
if #post.update(postParams)
flash[:success] = "Post successfully updated."
redirect_to post_path(#post)
else
render 'edit'
end
end
def show
end
def edit
end
def sold
#post.toggle(:sold)
#post.save
redirect_to post_path(#post)
end
def destroy
#post.destroy
flash[:danger] = "Item successfully deleted."
redirect_to posts_path
end
private
def postParams
params.require(:post).permit(:title, :price, :description, category_ids:[])
end
def setPost
#post = Post.find(params[:id])
end
def requireSameUser
if currentUser != #post.user and !currentUser.admin
flash[:danger] = "You can only edit or delete your own items"
redirect_to root_path
end
end
end
Models
comment.rb
class Comment < ActiveRecord::Base
belongs_to :post_comments
belongs_to :user
belongs_to :post
end
post_comment.rb
class PostComment < ActiveRecord::Base
belongs_to :post
belongs_to :comment
end
post.rb
class Post < ActiveRecord::Base
belongs_to :user
has_many :post_categories
has_many :categories, through: :post_categories
has_many :post_comments
has_many :comments, through: :post_comments
validates :title, presence: true,
length: { minimum: 4, maximum: 20 }
validates :description, presence: true,
length: { maximum: 1000 }
validates :user_id, presence: true
end
Views
posts/show.html.erb
<p>Comments: <%= render #post.comments %></p>
This renders the partial below
comments/_comment.html.erb
<%= link_to comment.name, comment_path(comment) %>
Finally is the new comment page as it is.
comments/new.html.erb
<h1>New Comment</h1>
<%= render 'shared/errors', obj: #comment %>
<div class="row">
<div class="col-xs-12">
<%= form_for(#comment, :html => {class: "form-horizontal", role: "form"}) do |f| %>
<div class="form-group">
<div class="control-label col-sm-2">
<%= f.label :comment %>
</div>
<div class="col-sm-8">
<%= f.text_area :comment, rows: 3, class: "form-control", placeholder: "Please enter a comment", autofocus: true %>
</div>
</div>
<div class="form-group">
<div class="center col-sm-offset-1 col-sm-10">
<%= f.submit class: "btn btn-primary btn-lg" %>
</div>
</div>
<% end %>
Any help would be greatly received.
Update
Log
Started GET "/posts/2" for ::1 at 2016-01-15 12:39:55 +0000
Processing by PostsController#show as HTML
Parameters: {"id"=>"2"}
[1m[36mPost Load (0.1ms)[0m [1mSELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT 1[0m [["id", 2]]
[1m[35mUser Load (0.1ms)[0m SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]]
[1m[36m (0.1ms)[0m [1mSELECT COUNT(*) FROM "posts" WHERE "posts"."user_id" = ?[0m [["user_id", 1]]
[1m[35mCategory Exists (0.1ms)[0m SELECT 1 AS one FROM "categories" INNER JOIN "post_categories" ON "categories"."id" = "post_categories"."category_id" WHERE "post_categories"."post_id" = ? LIMIT 1 [["post_id", 2]]
[1m[36mCategory Load (0.0ms)[0m [1mSELECT "categories".* FROM "categories" INNER JOIN "post_categories" ON "categories"."id" = "post_categories"."category_id" WHERE "post_categories"."post_id" = ?[0m [["post_id", 2]]
Rendered categories/_category.html.erb (0.2ms)
[1m[35mComment Load (0.1ms)[0m SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 2]]
Rendered comments/_comment.html.erb (0.1ms)
Rendered posts/show.html.erb within layouts/application (6.5ms)
[1m[36mCategory Load (0.1ms)[0m [1mSELECT "categories".* FROM "categories"[0m
Rendered layouts/_navigation.html.erb (0.9ms)
Rendered layouts/_messages.html.erb (0.1ms)
Rendered layouts/_footer.html.erb (0.1ms)
Completed 200 OK in 52ms (Views: 50.3ms | ActiveRecord: 0.5ms)
As a comment can belong to a single post only, you do not need an association table (post_comments). You just need a simple one-to-many relationship.
Your post comment would be:
class Post < ActiveRecord::Base
has_many :comments
...
end
And comment would be like this:
class Comment < ActiveRecord::Base
belongs_to :post
...
end
Just make sure that you have the necessary post_id column in the comments table (you can check the db/schema.rb file). If that is missing, you can use the following migration to add it:
class AddPostIdToComments < ActiveRecord::Migration
def change
add_column :comments, :post_id, :integer
add_index :comments, :post_id
end
end
You also need to make sure you keep somewhere the reference to the post, whenever a user tries to create a comment to a post. You can add this in a hidden field to your comments/new.html.erb template. You could set the hidden field in the new action, in PostsController, after passing it through the URL.
So, in your posts/show.html.erb template you would have:
<%= link_to "Add Comment", new_comment_path(post_id: #post.id) %>
In your new action, in PostsController:
def new
#comment = Comment.new(post_id: params[:post_id])
end
And finally the hidden field in your form would be:
<%= f.hidden_field :post_id %>
Finally, add the post_id parameter to the list of permitted parameters in CommentsController.
instead of
In your new action, in PostsController:
def new
#comment = Comment.new(post_id: params[:post_id])
end
you can use this in the form views
<%= form.hidden_field :post_id, value: "#{params[:post_id]}" %>

Error validations failing in Rails 4 when using form_for

I have a Rails app that builds both a User and an Organization when the first User signs up (User belongs_to Organization). This works fine, and if you fail to put in e.g. the email address (or use an incorrect one) then the form renders the relevant rails error messages just fine. I have additional logic that ensures the first User to sign up an Organization becomes the Admin User of that Organization, and they are permitted to add other users to the site too. If you try and sign up with an existing Organization name it's not permitted.
My tests are all fine, and I'm using the skeleton logic for authentication and authorization provided by the Hartl tutorial (not the new draft one). What I recently noticed was that when an Admin User inputs a blank form for a new user, the app returns to the Organization page and displays a success message. No User object is built (so some part of the app is still working) - I just don't want this to happen (I want the error messages displayed). I can't quite work out what is happening here - why wouldn't the "validates" statements return the errors in this case? Anyway - here's my code:
User.rb:
class User < ActiveRecord::Base
belongs_to :organization
has_many :sales_opportunities
before_save { self.email = email.downcase }
before_destroy :allocate_sales_opportunities_to_admin
before_create :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(?:\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6 }
validates :organization, presence: true
Organization.rb:
class Organization < ActiveRecord::Base
validates :organization_name, presence: true, length: { maximum: 50 }, uniqueness: true
has_many :users, :inverse_of => :organization, dependent: :destroy
has_many :companies, :inverse_of => :organization, dependent: :destroy
has_many :products, :inverse_of => :organization, dependent: :destroy
has_many :competitors, :inverse_of => :organization, dependent: :destroy
accepts_nested_attributes_for :users
after_create :set_admin
def users_for_form
collection = users.where(organization_id: id)
collection.any? ? collection : users.build
end
private
def set_admin
if self.users.count == 1
self.users.first.update_attribute(:admin, true)
else
return true
end
end
end
Users controller:
class UsersController < ApplicationController
before_action :signed_in_user, only: [:index, :edit, :update]
before_action :correct_user, only: [:edit, :update, :show]
before_action :admin_user, only: :destroy
def update
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
def create
if signed_in?
#organization = Organization.find(params[:organization_id])
#organization.users.create(user_params)
flash[:success] = "You added a new user to your organization. Send them their login details today!"
redirect_to #organization
else
#user = User.new(user_params)
if #user.save
sign_in #user
flash[:success] = "Thanks for signing up with My App! This is your profile page, be sure to take a look at the support link in the footer"
redirect_to #user
else
render 'new'
end
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation, organization_attributes: [:organization_name, :organization_id])
end
#before filters
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in." unless signed_in?
end
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
def correct_user
#user = User.find(params[:id])
#organization = #user.organization
redirect_to root_url, notice: "You are not permitted to visit that page. Please create an account or sign in" unless current_user?(#user) || #user.organization == current_user.organization
end
end
Organization's controller:
class OrganizationsController < ApplicationController
before_action :signed_in_user, only: [:edit, :update, :show]
before_action :correct_org, only: [:edit, :update, :show]
def new
#organization = Organization.new
#organization.users.build
end
def create
#organization = Organization.new(organization_params)
if #organization.save
#user = #organization.users.first
sign_in #user
flash[:success] = "Thanks for signing up with My App! This is your profile page, be sure to take a look at the support link in the footer"
redirect_to #user
else
render 'new'
end
end
I'm sure I've got some ugly code in here (the Organizations and Users features were the first new functionality I built when learning RoR and deviating from the Hartl course), and I can't quite work out why the validations are working (no User object is created) when the error message handling is not. For completeness here's the add user form:
<% provide(:title, 'Add more users to your organization') %>
<div class-"container-fluid">
<h1>Sign up colleagues using the form below:</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div class="well">
<%= form_for([:organization, #user], :html => {:class => "form-horizontal"}) do |f| %>
<%= render 'shared/error_messages' %>
<div class="form-group">
<%= f.label :name, :class => "col-md-4 control-label" %>
<div class ="col-md-8">
<%= f.text_field :name, :placeholder => "What's their name?" %>
</div>
</div>
<div class="form-group">
<%= f.label :email, :class => "col-md-4 control-label" %>
<div class ="col-md-8">
<%= f.text_field :email, :placeholder => "Enter their email address" %>
</div>
</div>
<div class="form-group">
<%= f.label :password, :class => "col-md-4 control-label" %>
<div class ="col-md-8">
<%= f.password_field :password, :placeholder => "Provide a password" %>
</div>
</div>
<div class="form-group">
<%= f.label :password_confirmation, "Repeat Password", :class => "col-md-4 control-label" %>
<div class ="col-md-8">
<%= f.password_field :password_confirmation, :placeholder => "Repeat password" %>
</div>
</div>
<%= f.submit "Add new user account", class: "btn btn-large btn-success" %>
<% end %>
</div>
</div>
</div>
</div>
Here's the working form for a new Organization/User combo:
<% provide(:title, 'Sign up') %>
<div class="container-fluid">
<h1>Sign up!</h1>
<div class="row">
<div class="col-md-8 col-md-offset-2">
</div>
</div>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div class="well">
<%= form_for(#organization, :html => {:class => "form-horizontal"}) do |f| %>
<%= render 'shared/org_error_messages' %>
<div class="form-group">
<%= f.label :organization_name, :class => "col-md-4 control-label" %>
<div class ="col-md-8">
<%= f.text_field :organization_name, :placeholder => "Who do you work for?" %>
</div>
</div>
<%= f.fields_for :users, #organization.users_for_form do |user| %>
<div class="form-group">
<%= user.label :name, :class => "col-md-4 control-label" %>
<div class ="col-md-8">
<%= user.text_field :name, :placeholder => "What's your name?" %>
</div>
</div>
<div class="form-group">
<%= user.label :email, :class => "col-md-4 control-label" %>
<div class ="col-md-8">
<%= user.text_field :email, :placeholder => "Email" %>
</div>
</div>
<div class="form-group">
<%= user.label :password, :class => "col-md-4 control-label" %>
<div class ="col-md-8">
<%= user.password_field :password, :placeholder => "Enter password - minimum 6 characters" %>
</div>
</div>
<div class="form-group">
<%= user.label :password_confirmation, "Repeat Password", :class => "col-md-4 control-label" %>
<div class ="col-md-8">
<%= user.password_field :password_confirmation, :placeholder => "Repeat password" %>
</div>
</div>
<%= user.hidden_field :organization_id, input_html: {value: #organization.id} %>
<% end %>
<%= f.submit "Create my account", class: "btn btn-large btn-success" %>
<% end %>
</div>
</div>
</div>
<div>
Where am I going wrong?
EDIT - here is the log from my console when the above happens:
Processing by UsersController#new as HTML
Parameters: {"organization_id"=>"1"}
Rendered shared/_error_messages.html.erb (0.1ms)
Rendered users/new.html.erb within layouts/application (3.4ms)
Rendered layouts/_shim.html.erb (0.0ms)
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = 'b8cabbe1e1514f14be24f95d48248ad716e11342' LIMIT 1
Organization Load (0.3ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT 1 [["id", 1]]
Rendered layouts/_header.html.erb (3.9ms)
Rendered layouts/_footer.html.erb (0.2ms)
Completed 200 OK in 28ms (Views: 26.0ms | ActiveRecord: 0.9ms)
Started POST "/organizations/1/users" for 127.0.0.1 at 2014-10-05 22:22:15 +0630
Processing by UsersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"fKx7bdWtC7bmBKMRF3ivwBlmJXzrcWJ16dYYOfNLBC0=", "user"=>{"name"=>"", "email"=>"", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Add new user account", "organization_id"=>"1"}
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = 'b8cabbe1e1514f14be24f95d48248ad716e11342' LIMIT 1
Organization Load (0.4ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT 1 [["id", 1]]
(0.3ms) BEGIN
User Exists (0.5ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER('') LIMIT 1
(0.8ms) COMMIT
Redirected to http://0.0.0.0:3000/organizations/1
Completed 302 Found in 14ms (ActiveRecord: 2.7ms)
Started GET "/organizations/1" for 127.0.0.1 at 2014-10-05 22:22:15 +0630
Processing by OrganizationsController#show as HTML
Parameters: {"id"=>"1"}
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = 'b8cabbe1e1514f14be24f95d48248ad716e11342' LIMIT 1
Organization Load (0.4ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT 1 [["id", 1]]
CACHE (0.0ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT 1 [["id", "1"]]
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."organization_id" = $1 [["organization_id", 1]]
Rendered organizations/_users_index.html.erb (1.0ms)
Organization Load (0.4ms) SELECT "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT 1 [["id", 1]]
Company Load (0.6ms) SELECT "companies".* FROM "companies" WHERE "companies"."organization_id" = $1 [["organization_id", 1]]
Competitor Load (0.4ms) SELECT "competitors".* FROM "competitors" WHERE "competitors"."organization_id" = $1 [["organization_id", 1]]
Product Load (0.4ms) SELECT "products".* FROM "products" WHERE "products"."organization_id" = $1 [["organization_id", 1]]
Rendered organizations/show.html.erb within layouts/application (13.3ms)
Rendered layouts/_shim.html.erb (0.0ms)
Rendered layouts/_header.html.erb (0.6ms)
Rendered layouts/_footer.html.erb (0.2ms)
Completed 200 OK in 37ms (Views: 30.6ms | ActiveRecord: 3.1ms)
I think it's something to do with the way I'm handling creating a new user, after further testing. The following code is almost certainly where the error lies:
def create
if signed_in?
#organization = Organization.find(params[:organization_id])
#organization.users.create(user_params)
flash[:success] = "You added a new user to your organization. Send them their login details today!"
redirect_to #organization
else
#user = User.new(user_params)
if #user.save
sign_in #user
flash[:success] = "Thanks for signing up with My App! This is your profile page, be sure to take a look at the support link in the footer"
redirect_to #user
else
render 'new'
end
end
end
Where I'm testing if the User is signed in, the choice either leads to adding a new user to the current organization or creating both from scratch. What I need is a way to test whether #organization.users.create was successful (or maybe I need #organization.users.new) and if it fails the validations then I need to render "users/new" rather than heading back to the #organization path and displaying the (obviously incorrect) flash that a new user has been created.
Anyone got any ideas how I test for this? I tried a quick if/else test (along the lines of "if #organization.users.create ... else: render the users/new page"), but then I got a bunch of form errors because I wasn't defining the object that the page was creating.
I solved this with the change to the create function in the Users controller - I was creating a new #organization.user regardless of the error messages in the form. Instead I needed to use #user = #organization.users.new, and then use the same if #user.save ... test that works for a new user. See the below code for how it was fixed:
def create
if signed_in?
#organization = Organization.find(params[:organization_id])
#user = #organization.users.new(user_params)
if #user.save
flash[:success] = "You added a new user to your organization. Send them their login details today!"
redirect_to #organization
else
render 'new'
end
else
#user = User.new(user_params)
if #user.save
sign_in #user
flash[:success] = "Thanks for signing up with My App! This is your profile page, be sure to take a look at the support link in the footer"
redirect_to #user
else
render 'new'
end
end
end

Rails 4: Create new records through has_many association

I am trying to create a new record for 'campaign' via the 'customer' as follows. The form submits correctly, however no new campaign record is created for the customer. Any advise would be greatly appreciated.
Models
class Customer::Customer < User
has_one :library, dependent: :destroy
has_many :campaigns, dependent: :destroy
accepts_nested_attributes_for :campaigns, :library, allow_destroy: true
end
class Customer::Campaign < ActiveRecord::Base
belongs_to :customer
end
Customer Controller - Update Method only shown
class Customer::CustomersController < ApplicationController
layout "customer_layout"
def update
session[:return_to] ||= request.referer
#customer = User.find(params[:id])
if #customer.update_attributes(customer_params)
flash[:success] = "Profile updated"
redirect_to #customer
else
flash[:notice] = "Invalid"
redirect_to session.delete(:return_to)
end
end
def customer_params
params.require('customer_customer').permit(:name, :email, :password, :password_confirmation, :country_code, :state_code, :postcode, :first_name, :surname, campaigns_attributes:[:name, :description, :start_date, :complete_date ])
end
end
And the form I am using
<%= form_for #customer do |f| %>
<%= render 'shared/customer_error_messages' %>
<%= f.fields_for :campaign do |builder| %>
<%= builder.label :name, "Campaign Name" %></br>
<%= builder.text_field :name %>
<% end %>
<div class="actions">
<%= f.submit class: "btn btn-large btn-primary"%>
</div>
<% end %>
And the console output (with error, is as follows)
Started PATCH "/customer/customers/38" for 127.0.0.1 at 2014-05-26 23:13:10 +1000
Processing by Customer::CustomersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"9ChLYna0/VfZSMJOa2yGgvWTT61XkjoYwlVup/Pbbeg=", "customer_customer"=>{"campaign"=>{"name"=>"My new campaign"}}, "commit"=>"Update Customer", "id"=>"38"}
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = '0e8b4ba6dc957e76948fc22bae57673404deb9fe' LIMIT 1
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", "38"]]
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", "38"]]
Unpermitted parameters: campaign
If this form performs adding only one campaign i guess you should handly add this exect campaign to customer. If you update all campaign model in such way, you should have all your campaigns in array. So it implies another controller action for this.
Another way: to check if params contains field campain and then just add it to customer object:
def update
session[:return_to] ||= request.referer
#customer = User.find(params[:id])
if new_campaign_customer_params.has_key? :campaign
#customer.campaigns << Campaign.new(new_campaign_customer_params)
if #customer.save
flash[:success] = "Profile updated"
redirect_to #customer
else
flash[:notice] = "Invalid"
# you should rerender your view here, set it path
render 'your_view'
end
else
if #customer.update_attributes(customer_params)
flash[:success] = "Profile updated"
redirect_to #customer
else
flash[:notice] = "Invalid"
redirect_to session.delete(:return_to)
end
end
end
def new_campaign_customer_params
params.require('customer_customer').permit(campaign_attributes:[:name, :description, :start_date, :complete_date ])
end
Check this out, not sure it works.
Or maybe it would work how to suggests in comment: change campaigns_attributes => campaign_attributes in customer_params method, and f.fields_for :campaigns in form (belongs to #Nikita Chernov)

Table not updating in joined table and I can't figure out why

I am having troubles saving data that is related to a joined table. I am using Rails 4.01 with Devise 3.23 and rolify. I'm fairly new to Rails so this may be a noob question although I've searched everything on SO I can.
Basically when I try to change a role, it doesn't save the data in the joined table, but it doesn't seem to be throwing any errors. I can't find anything in the logs either.
I have the exact same issue with a second joined table. The common denominator is they both join to my users table and I'm using the update action on my User Controller.
I've spent the last two hours trying to find a solution. I thought it might be a strong parameters issue but I can't find the problem. I have tried downgrading to Rails 4.0.0 just in case but no good. I have another app with essentially the same code and no problems.
Here's my User Controller:
class UsersController < ApplicationController
before_filter :authenticate_user!
before_action :set_user, only: [:show, :edit, :update, :destroy]
def index
authorize! :index, #user, :message => 'Not authorized as an administrator.'
#users = User.all
end
def show
end
def update
authorize! :update, #user, :message => 'Not authorized as an administrator.'
if #user.update_attributes(user_params[:user])
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
authorize! :destroy, #user, :message => 'Not authorized as an administrator.'
user = User.find(params[:id])
unless user == current_user
user.destroy
redirect_to users_path, :notice => "User deleted."
else
redirect_to users_path, :notice => "Can't delete yourself."
end
end
# new function to set the password without knowing the current password used in our confirmation controller.
def attempt_set_password(params)
p = {}
p[:password] = params[:password]
p[:password_confirmation] = params[:password_confirmation]
update_attributes(p)
end
# new function to return whether a password has been set
def has_no_password?
self.encrypted_password.blank?
end
# new function to provide access to protected method unless_confirmed
def only_if_unconfirmed
unless_confirmed {yield}
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit( :name, :role_ids, :qualification_id, :role, :qualification )
end
end
My User Model:
class User < ActiveRecord::Base
belongs_to :qualification
rolify
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
validates :phone, presence: true, format: { with: /1.\d{3}.\d{3}.\d{4}/, message: "Must be in 1.555.555.5555 format" }
validates :name, presence: true
validates :email, presence: true
validates :acknowledgement, presence: true
# new function to set the password without knowing the current password used in our confirmation controller.
def attempt_set_password(params)
p = {}
p[:password] = params[:password]
p[:password_confirmation] = params[:password_confirmation]
update_attributes(p)
end
# new function to return whether a password has been set
def has_no_password?
self.encrypted_password.blank?
end
# new function to provide access to protected method unless_confirmed
def only_if_unconfirmed
pending_any_confirmation {yield}
end
def password_required?
# Password is required if it is being set, but not for new records
if !persisted?
false
else
!password.nil? || !password_confirmation.nil?
end
end
end
My Qualification Model:
class Qualification < ActiveRecord::Base
has_many :users
end
My Role Model:
class Role < ActiveRecord::Base
has_and_belongs_to_many :users, :join_table => :users_roles
belongs_to :resource, :polymorphic => true
scopify
end
Here is my index view to change the role:
<h2 class='subheader'>People</h2>
<% #users.each do |user| %>
<div class='row collapse'>
<div class='large-2 columns'><%= link_to user.name, user_path(user) %></div>
<div class='large-2 columns'><%= user.email %></div>
<div class='large-2 columns'><%= user.phone %></div>
<div class='large-2 columns'><%= user.qualification.name unless user.qualification.nil?%></div>
<div class='large-2 columns'><%= user.roles.first.name.titleize unless user.roles.first.nil? %></div>
<div class='large-2 columns'>
<% if user != current_user %>
<a data-reveal-id="role-options-<%= user.id %>" href="#" class="button tiny radius" type="button">Change role</a>
<% end %>
</div>
<div class='large-2 columns'>
<%= link_to("Delete Person", user_path(user), :data => { :confirm => "Are you sure?" }, method: :delete, class: 'button radius tiny') unless user == current_user %></div>
<hr>
</div>
<div id="role-options-<%= user.id %>" class="reveal-modal tiny" style="display: none;">
<%= simple_form_for user, url: user_path(user), html: {:method => :put, class: 'custom' } do |f| %>
<div>
<a class="close-reveal-modal">×</a>
<h3>Change Role</h3>
</div>
<div>
<%= f.input :role_ids, collection: Role.all, as: :radio_buttons, label_method: lambda {|t| t.name.titleize}, label: false, item_wrapper_class: 'inline', checked: user.role_ids.first %>
</div>
<div>
<%= f.submit "Change Role", :class => "button tiny radius" %>
</div>
<% end %>
</div>
<% end %>
Here's the relevant excerpt from my log:
Started PUT "/users/47" for 127.0.0.1 at 2013-11-20 22:27:59 -0700
Processing by UsersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"3kfL/K47ynC4LGF9kh/cAfAadGu8OXhH9kXhxeGPsvo=", "user"=> {"role_ids"=>"2"}, "commit"=>"Change Role", "id"=>"47"}
[1m[35mUser Load (0.2ms)[0m SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
[1m[36mUser Load (0.1ms)[0m [1mSELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1[0m [["id", "47"]]
[1m[35m (0.1ms)[0m SELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = ? AND (((roles.name = 'admin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL))) [["user_id", 1]]
[1m[36m (0.1ms)[0m [1mbegin transaction[0m
[1m[35m (0.1ms)[0m commit transaction
Redirected to http://localhost:3000/users
Completed 302 Found in 8ms (ActiveRecord: 0.7ms)
I'm pulling my hair out on this one so any help will be greatly appreciated.
I'm not super familiar with rolify but it doesn't look like your User model does anything with the role_ids attribute in the params...
Assuming it doesn't, you'd want to iterate over each role_id (if they exist) within your controller:
def update
authorize! :update, #user, :message => 'Not authorized as an administrator.'
#user.update_roles(user_params[:role_ids]) if user_params[:role_ids].present?
if #user.update_attributes(user_params[:user])
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
Then on your User model, you'd have:
def update_roles(role_ids)
role_names = Role.find_all_by_id(role_ids).map(&:name)
role_names.each {|role_name| self.add_role role_name.to_sym }
end
Again, I haven't tested this and I'm not overly familiar with rolify so someone else may have a better suggestion, but this should at least get you closer.

Resources