I have a Rails 6 project, I am Citusdata/activerecord-multi-tenant gem to handle my row based MultiTenancy I am trying to create a TimeSheet for a User
as it is right now I can't access current_user to save the relationship. My console output is not helpful at all:
It is connecting the relationship to the account model and auto saves my account_id, but fails to collect the user.id or current_user.id
I can drop a byebug in both new and create blocks, however when I pass the byebug below #time_sheet.save it comes back as nil.
Im stuck here, not sure where I'm going wrong here? I have done this plenty in the past.
Here is how its all set up:
class User < ApplicationRecord
extend FriendlyId
friendly_id :username, use: :slugged
multi_tenant :account
belongs_to :role
has_many :time_sheets
end
class TimeSheet < ApplicationRecord
multi_tenant :account
belongs_to :user (user_id in schema)
end
class TimeSheetsController < ApplicationController
before_action :authenticate_user!
def new
#user = current_user
#time_sheet = TimeSheet.new
end
def create
#user = current_user
#time_sheet = TimeSheet.new(time_sheet_params)
respond_to do |format|
if #time_sheet.save
format.html { redirect_to #time_sheet, flash: { success: "#{#time_sheet.start_date} - #{#time_sheet.end_date} has been sucessfully created." } }
else
format.html { render :new }
end
end
end
end
My new time sheet form
<%= form_for(#time_sheet, :html => { class: "flex w-full items-center mt-64" }) do |f| %>
<% if #time_sheet.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#time_sheet.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #time_sheet.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="w-full text-center">
<p class="text-3xl text-primary-700 mb-16">New Time Sheet</p>
<div class="flex flex-wrap w-full justify-center">
<div class="w-1/4 mr-3">
<%= f.label :start_date, "Start Date", class: 'form-label text-left' %>
<%= f.date_field :start_date, class: 'form-input w-full' %>
</div>
<div class="w-1/4 ml-3">
<%= f.label :end_date, "End Date", class: 'form-label text-left' %>
<%= f.date_field :end_date, class: 'form-input w-full' %>
</div>
</div>
<div class="flex w-full justify-center">
<div class="w-1/4 mt-6">
<%= f.submit "Start Period", class: "p-3 w-full bg-green-800" %>
</div>
</div>
</div>
<% end %>
my console output:
Started POST "/time_sheets" for 127.0.0.1 at 2020-03-26 09:43:34 -0600
Processing by TimeSheetsController#create as HTML
Parameters: {"authenticity_token"=>"TuSzdfVr5iYqpvHfoPU+W7dFKQHCSX9cYcvbtqiqwdKOmaxhqQVp0inOjI0/NYuMpBoZPzJggJdeVro9XyV/lQ==", "time_sheet"=>{"start_date"=>"2020-03-26", "end_date"=>"2020-03-27"}, "commit"=>"Start Period"}
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/controllers/application_controller.rb:10:in `set_account_as_tenant'
Account Load (0.3ms) SELECT "accounts".* FROM "accounts" LIMIT $1 [["LIMIT", 1]]
↳ app/controllers/application_controller.rb:12:in `set_account_as_tenant'
Completed 422 Unprocessable Entity in 10ms (ActiveRecord: 1.1ms | Allocations: 3992)
ActiveRecord::RecordInvalid (Validation failed: User must exist):
app/controllers/time_sheets_controller.rb:21:in `block in create'
app/controllers/time_sheets_controller.rb:20:in `create'
Looks like you are not passing the user_id correctly when creating the time_sheet.
Change your new method to include the current_user id by merging it into your time_sheet_params like this:
#time_sheet = TimeSheet.new(time_sheet_params.merge(user_id: current_user.id)
Related
I have tried a few methods and checked all of the existing SO questions I can find and can't find an answer to the problem. I'm a Rails newbie.
I have these models:
Project (has_many WeeklyReflections, has_many ProjectTasks)
WeeklyReflection (belongs_to Project)
ProjectTask (belongs_to Project)
Standard devise user model
My Project show page displays a list of ProjectTasks for the week. Using simplecalendar.
I can navigate forward and back a week at a time, which then changes the URL from www.site.com/projects/3 to www.site.com/projects/3?start_date=2108-05-30, or www.site.com/projects/3?start_date=2108-05-13, and this then shows all the tasks related to that week.
What I want to do as well, is for each page create a weekly journal entry, only one of which can be created for the week, however, I'm having problems saving it to my controller - I've tried a few ways and also tried putting it in as a hidden field on a form, and still can't get it to work.
I've put start_date:<%= render inline: params[:start_date].inspect %> on both the Project show page and form it returns a value correctly, i.e. "start_date:"2018-04-28"" or it returns nil if on the default show page which is to be expected.
The two methods I've tried: are - accessing the start_date param via the controller (in the controller below) and also adding the following to the form:
<%= hidden_field_tag(:weekending, params[:start_date]) %> (I've also tried using .to_date, as I think the string it returns isn't in the right format)
However, both methods are returning a nil value when the start_date param is returning 2018-04-28 on the view page.
Console log after create action:
Started POST "/projects/6/weekly_reflections" for 2.96.199.78 at 2018-05-07 16:20:14 +0000
Cannot render console from 2.96.199.78! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by WeeklyReflectionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"UFCXjIp1SZrql2JckHk1HrzDlzO2/WLVwcdvdh+FQKbdrVMfdaivtjS32oLaFwcFs0UcupVP+XV6VVnNwrM0XQ==", "week_reflection"=>{"reflectionentry"=>"adasda", "motivationscore"=>"1", "beliefscore"=>"1"}, "weekending"=>"2018-04-28", "commit"=>"Create Project reflection", "project_id"=>"6"}
Project Load (0.6ms) SELECT "projects".* FROM "projects" WHERE "projects"."id" = ? LIMIT ? [["id", 6], ["LIMIT", 1]]
(0.1ms) begin transaction
(0.1ms) commit transaction
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 2], ["LIMIT", 1]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
(0.1ms) begin transaction
SQL (1.3ms) INSERT INTO "week_reflections" ("reflectionentry", "user_id", "project_id", "motivationscore", "beliefscore", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?) [["reflectionentry", "adasda"], ["user_id", 2], ["project_id", 6], ["motivationscore", 1], ["beliefscore", 1], ["created_at", "2018-05-07 16:20:14.957131"], ["updated_at", "2018-05-07 16:20:14.957131"]]
(15.0ms) commit transaction
-
class WeeklyReflectionsController < ApplicationController
before_action :set_weekly_reflection, only: [:show, :edit, :update, :destroy]
before_action :set_project, only: [:new, :edit, :create, :show]
def index
#weekly_reflections= WeeklyReflection.all
end
def new
#weekly_reflection= WeeklyReflection.new
end
def create
#weekly_reflection= #project.weekly_reflections.create(project_reflection_params)
#weekly_reflection.user = current_user
#weekly_reflection.weekending = params[:start_date].to_date || Date.today.beginning_of_week
respond_to do |format|
if #weekly_reflections.save
format.html { redirect_to #weekly_reflection.project, notice: 'Weekly reflection was successfully created.' }
else
format.html { redirect_to #weekly_reflection.project, notice: 'Weekly reflection could not be added.' }
end
end
end
private
def weekly_reflection
#weekly_reflection= WeeklyReflection.find(params[:id])
end
def set_project
#project = Project.find(params[:project_id])
end
def weekly_reflection_params
params.require(:weekly_reflection).permit(:reflectionentry, :weekending, :motivationscore, :beliefscore)
end
end
Project show page:
<p id="notice"><%= notice %></p>
start_date:<%= render inline: params[:start_date].inspect %>
<div class="row well">
<div class="col-md-12">
<%= render 'project_detail' %>
</div>
<div class="col-md-12">
<% if #project.user == current_user && #project.active? %>
<br>
<%= render "project_tasks/task_modal" %>
<% end %>
</div>
<div class="col-md-12">
<%= render "project_reflection_calendar" %>
<% if #project.user == current_user && #project.active? %>
<h3>Add your thoughts for this week</h3>
<%= render "project_reflections/form_reflection" %>
<% end %>
</div
WeeklyEntry form, shown on the project show page:
<div class="row">
start_date:<%= render inline: params[:start_date].inspect %>
<%= form_for([#project, #project.weekly_reflections.build]) do |form| %>
<div class="col-md-12 projectform">
<%= form.label "Add your thoughts for this week" %>
<%= form.text_field :reflectionentry, :rows =>2, style: 'width:100%;' %>
</div>
<div class="col-md-7 projectform">
<%= form.label "How motivated are you for your project this week?" %>
<%= form.select :motivationscore, [1,2,3,4,5,6,7,8,9,10], class:"form-control" %>
</div>
<div class="col-md-7 projectform">
<%= form.label "How strongly do you believe you will reach your project?" %>
<%= form.select :beliefscore, [1,2,3,4,5,6,7,8,9,10], class:"form-control" %>
</div>
<%= hidden_field_tag(:weekending, params[:start_date].to_date) %>
<div class="col-md-12 projectform">
<%= form.submit class:"btn btn-primary" %>
</div>
<% end %>
</div>
ApplicationRecord code:
class WeeklyReflection < ApplicationRecord
belongs_to :project
belongs_to :user
validates :reflectionentry, presence:true, length: {minimum:3, maximum: 540}
#required for simple_calendar
def start_time
self.weekending
end
end
class Project < ApplicationRecord
has_many :project_tasks, dependent: :destroy
belongs_to :user
has_many :weekly_reflections, dependent: :destroy
end
=> WeeklyReflection Schema:
(id: integer, reflectionentry: text, user_id: integer, project_id: integer, weekending: date, motivationscore: integer, beliefscore: integer, created_at: datetime, updated_at: datetime)
I think, this should work:
Add to your ProjectController show action:
#weekly_reflection= #project.weekly_reflections.new
Change in WeeklyReflectionsController:
def create
#weekly_reflection = #project.weekly_reflections.new(weekly_reflection_params)
#weekly_reflection.user = current_user
respond_to do |format|
if #weekly_reflections.save
format.html { redirect_to #weekly_reflection.project, notice: 'Weekly reflection was successfully created.' }
else
format.html { redirect_to #weekly_reflection.project, notice: 'Weekly reflection could not be added.' }
end
end
end
private
def weekly_reflection_params
params.require(:weekly_reflection).permit(:reflectionentry, :weekending, :motivationscore, :beliefscore)
end
Change in WeeklyReflection form:
<div class="row">
<%= form_for #weekly_reflection do |form| %>
<div class="col-md-12 projectform">
<%= form.label "Add your thoughts for this week" %>
<%= form.text_field :reflectionentry, :rows =>2, style: 'width:100%;' %>
</div>
<div class="col-md-7 projectform">
<%= form.label "How motivated are you for your project this week?" %>
<%= form.select :motivationscore, [1,2,3,4,5,6,7,8,9,10], class:"form-control" %>
</div>
<div class="col-md-7 projectform">
<%= form.label "How strongly do you believe you will reach your project?" %>
<%= form.select :beliefscore, [1,2,3,4,5,6,7,8,9,10], class:"form-control" %>
</div>
<%= form.hidden_field :weekending, value: params[:start_date] || Date.today.beginning_of_week %>
<div class="col-md-12 projectform">
<%= form.submit class:"btn btn-primary" %>
</div>
<% end %>
</div>
Here we create #weekly_reflection variable inside the show action of ProjectsController, and just use it in form
That worked. Thanks so much.
With just a slight change to:
<%= form_for([#weekly_reflection.project, #weekly_reflection]) do |form| %>
I also had to add in:
<%= form.hidden_field :weekending, value: params[:start_date].try(:to_date).try(:beginning_of_week) || Date.today.beginning_of_week %>
So that I could capture the beginning of the week of the date in the param.
I think there must be something simple that I'm not setting up correctly, but I cannot figure out this behavior. The user submits and edit form, rails log shows the change is committed, but when I load the record again, it's still the same.
Here's the form
<%= form_for(#deal) do |f| %>
<div class="deal-<%= #deal.id %>">
<div class="field">
<%= f.label :headline %><br />
<%= f.text_field :headline, required: true %>
</div>
<div class="field">
<%= f.label :matter %>
<%= f.text_field :matter %>
</div>
<div class="field">
<%= f.label :summary %>
<%= f.text_area :summary %>
</div>
<%= f.submit "Update" %>
</div>
<% end %>
and relevant parts of controller
def edit
#deal = Deal.find(params[:id])
end
def update
#deal = Deal.find(params[:id])
if #deal.save
flash[:success] = 'Your deal was updated'
redirect_to root_path
else
render 'edit'
end
end
And the output on submit
Started PATCH "/deals/1" for ::1 at 2017-03-21 13:15:17 +0100
Processing by DealsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Rctg6qcVMfOutyXjZihiR5+zhagr+cduNfA6TSYqOHmjvh+dcQl9TZ/MxZbY+IMyt0LU2PQrsyCjcUJczGcTKg==", "deal"=>{"headline"=>"testheadline", "matter"=>"matter", "summary"=>"First test summary"}, "commit"=>"Update", "id"=>"1"}
Deal Load (0.1ms) SELECT "deals".* FROM "deals" WHERE "deals"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
(0.0ms) begin transaction
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 6], ["LIMIT", 1]]
(0.1ms) commit transaction
Redirected to http://localhost:3000/
Completed 302 Found in 5ms (ActiveRecord: 0.4ms)
def update
#deal = Deal.find(params[:id])
if #deal.update(deal_params)
flash[:success] = 'Your deal was updated'
redirect_to root_path
else
render 'edit'
end
end
private
def deal_params
params.require(:deal).permit(:headline, :matter, :summary)
end
I'm on week two of this issue and have recently used the railsCast #196 (revised). I know this is older - maybe that's my issue. As an extra spin I'm hosting my rails server off Cloud 9.
I've tried following a few different tutorials just to get one going & this is as far as I've gotten. The weird part is none of their syntex matches what the official ruby on rails documentation has ... Rails View templates.
In the railsCast the guy is able to get blank fields to show up ... I'm not sure how...so I haven't managed to populate the question or answer fields yet. I'm not even sure what the two rails console messages mean - besides there aren't records there to be had.
Thanks for reading & any suggestions!
-M
Without further ado, my senario ... nested forms via templates as shown in railsCast 196 ...
My rails console ...
2.2.1 :045 > cc = Survey.first.questions.first
Survey Load (0.5ms) SELECT "surveys".* FROM "surveys" ORDER BY "surveys"."id" ASC LIMIT 1
Question Load (0.2ms) SELECT "questions".* FROM "questions" WHERE "questions"."survey_id" = ? ORDER BY "questions"."id" ASC LIMIT 1 [["survey_id", 1]]
=> nil
2.2.1 :046 > cc = Survey.first.questions
Survey Load (0.3ms) SELECT "surveys".* FROM "surveys" ORDER BY "surveys"."id" ASC LIMIT 1
Question Load (0.2ms) SELECT "questions".* FROM "questions" WHERE "questions"."survey_id" = ? [["survey_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy []>
My terminal console log ...
Started GET "/surveys/5/edit" for 68.54.21.200 at 2015-11-27 02:46:48 +0000
Cannot render console from 68.54.21.200! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by SurveysController#edit as HTML
Parameters: {"id"=>"5"}
Survey Load (0.4ms) SELECT "surveys".* FROM "surveys" WHERE "surveys"."id" = ? LIMIT 1 [["id", 5]]
Question Load (0.2ms) SELECT "questions".* FROM "questions" WHERE "questions"."survey_id" = ? [["survey_id", 5]]
Rendered surveys/_form.html.erb (4.2ms)
Rendered surveys/edit.html.erb within layouts/application (7.3ms)
Completed 200 OK in 70ms (Views: 67.9ms | ActiveRecord: 0.5ms)
So my code ...
surveys_controller.rb
class SurveysController < ApplicationController
def index
#surveys = Survey.all
end
def show
#survey = Survey.find(params[:id])
end
def new
#survey = Survey.new
3.times do
question = #survey.questions.build
4.times { question.answers.build }
end
end
def create
#survey = Survey.new(survey_params)
if #survey.save
flash[:notice] = "Successfully created survey."
redirect_to #survey
else
render :action => 'new'
end
end
def edit
#survey = Survey.find(params[:id])
end
def update
#survey = Survey.find(params[:id])
if #survey.update_attributes(params[:survey])
flash[:notice] = "Successfully updated survey."
redirect_to #survey
else
render :action => 'edit'
end
end
def destroy
#survey = Survey.find(params[:id])
#survey.destroy
flash[:notice] = "Successfully destroyed survey."
redirect_to surveys_url
end
private
def survey_params
params.required(:survey).permit(:id, :survey, :notice)
end
end
Edit action view
<% title = "Edit Survey" %>
<%= render 'form' %>
<p>
<%= link_to "Show", #survey %> |
<%= link_to "View All", surveys_path %>
</p>
_form.html.erb
<%= form_for(#survey) do |f| %>
<% if #survey.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#survey.errors.count, "error") %> prohibited this survey from being saved:</h2>
<ul>
<% #survey.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<% f.fields_for :questions do |builder| %>
<%= render "question_fields", :f => builder %>
<% end %>
<p><%= f.submit "Submit" %></p>
<% end %>
_question_fields.html.erb
<p>
<%= f.label :content, "Question" %><br />
<%= f.text_area :content, :rows => 3 %><br />
<%= f.check_box :_destroy %>
<%= f.label :_destroy, "Remove Question" %>
</p>
<% f.fields_for :answers do |builder| %>
<%= render 'answer_fields', :f => builder %>
<% end %>
_answer_fields.html.erb
<p>
<%= f.label :content, "Answer" %>
<%= f.text_field :content %>
<%= f.check_box :_destroy %>
<%= f.label :_destroy, "Remove" %>
</p>
In each of the 7 projects I ran off the web...
It was the nesting of the array's within the params.require() that was the problem.. It's one thing to tell someone it has to be nested - it's another to show them the syntax when they are new :)
Example:
// Note this is from memory, as I deleted this version of the github..so it's not exactly right or tested...
params.require(:survey).permit(:id,:questions => [:id, :survey_id, :question, :answers => [:id, :question_id, :answer]])
Here's the break down of that same example in depth:
params.require(:survey).permit(
:id,
:questions => [:id, // This is the 1st nesting
:survey_id, :question, :answers => [:id, // This is 1st nested array ":questions"
:question_id, :answer] // End the 2nd nested array ":answers"
] // End the 2nd array ":questions"
) // End the ":surveys" array & the .permit as a whole
This question already has answers here:
Rails -- use type column without STI?
(5 answers)
Closed 8 years ago.
I'm getting this error and I don't know what to do. It says
Invalid single-table inheritance type: 1 is not a subclass of Game
It says that this error is occuring at the 9 in games_controller.rb.
i.e.
#game = current_user.created_games.build(game_params)
I don't understand what is happening and what I should write here to specify the cause of this error. I copy&paste game.rb, user.rb, application_controller.rb and games_controller.rb here.
game.rb is
class Game < ActiveRecord::Base
belongs_to :owner, class_name: 'User'
end
user.rb is
class User < ActiveRecord::Base
has_many :created_games, class_name: 'Game', foreign_key: :owner_id
def self.find_or_create_from_auth_hash(auth_hash)
provider = auth_hash[:provider]
uid = auth_hash[:uid]
nickname = auth_hash[:info][:nickname]
image_url = auth_hash[:info][:image]
User.find_or_create_by(provider: provider, uid: uid) do |user|
user.nickname = nickname
user.image_url = image_url
end
end
end
application_controller.rb is
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
helper_method :logged_in?
private
def current_user
return unless session[:user_id]
#current_user ||= User.find(session[:user_id])
end
def logged_in?
!!session[:user_id]
end
def authenticate
return if logged_in?
redirect_to root_path, alert:'Please log in.'
end
def created_by?(user)
return false unless user
owner_id == user.id
end
end
games_controller.rb
class GamesController < ApplicationController
before_action :authenticate
def new
#game = current_user.created_games.build
end
def create
#game = current_user.created_games.build(game_params)
if #game.save
redirect_to #game, notice: 'You created game.'
else
render :new
end
end
private
def game_params
params.require(:game).permit(
:name, :content, :type
)
end
end
Added
new.html.erb
<% now = Time.zone.now %>
<div class="page-header">
<h1>Create Game</h1>
</div>
<%= form_for(#game, class: 'form-horizontal', role:'form') do |f| %>
<% if #game.errors.any? %>
<div class="alert alert-danger">
<ul>
<% #game.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :type %>
<div>
<%= f.select :type, options_for_select: [['basic', 1]], class: 'form-control' %>
</div>
</div>
<div class="form-group">
<%= f.label :content %>
<%= f.text_area :content, class: 'form-control', row: 10 %>
</div>
<%= f.submit 'Create', class: 'btn btn-default', data: { disable_with: 'Creating...' } %>
<% end %>
When I try to create new game, console says as follows.
Started POST "/games" for 127.0.0.1 at 2015-01-29 01:50:29 +0900
Processing by GamesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"2W57cDKT2552Xgnh2MLi17uQpcrqDkmxhQJQa1qNuJNsZb0R10l/AU37y+KO9DysCp56aVTFBE/MqRoimMnjYQ==", "game"=>{"name"=>"this is name", "type"=>"1", "content"=>"this is content"}, "commit"=>"Create"}
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]]
Completed 500 Internal Server Error in 32ms
ActiveRecord::SubclassNotFound - Invalid single-table inheritance type: 1 is not a subclass of Game:
So to answer the question, you cannot have a column name called type in your migrations. I believe there are ways around this, but it's good practice to just rename your column something else.
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