to_param not working in nested polymorphic associated model - ruby-on-rails

This test is failing at the line get_users_path:
require 'test_helper'
class UsersIndexTest < ActionDispatch::IntegrationTest
def setup
#admin = users(:user_baz)
end
test "index as admin including pagination and delete links" do
log_in_as(#admin)
get users_path
.
.
end
end
Here is the error message:
ActionView::Template::Error: undefined method 'callsign' for nil:NilClass
app/models/user.rb:35:in 'to_param'
app/views/users/_user.html.erb:3:in '_app_views_users__user_html_erb___623096829469928541_2227766840'
app/views/users/index.html.erb:8:in '_app_views_users_index_html_erb__62429272046032246_2224894860'
test/integration/users_index_test.rb:9:in `block in <class:UsersIndexTest>'`
It's referring to the line self.character.callsign in the to_param method in the User model.
User.rb:
class User < ActiveRecord::Base
attr_accessor :remember_token, :activation_token, :reset_token
has_one :character, as: :sociable, dependent: :destroy
accepts_nested_attributes_for :character
has_secure_password
before_validation do
self.create_character unless character
end
before_save do
self.email.downcase!
end
before_create :create_activation_digest
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 }
validates :password, length: { minimum: 6 }, allow_blank: true
validates :character, presence: true
def to_param
self.character.callsign
end
.
.
end
Character.rb:
class Character < ActiveRecord::Base
belongs_to :sociable, polymorphic: true
before_save do
self.callsign.downcase!
end
VALID_CALLSIGN_REGEX = /\A[a-z\d\-.\_]+\z/i
validates :callsign, presence: true,
length: { maximum: 20 },
format: { with: VALID_CALLSIGN_REGEX },
uniqueness: { case_sensitive: false }
end
app/views/users/index.html.erb:
<% provide(:title, 'All users') %>
<h1>All users</h1>
<%= will_paginate %>
<ul class="users">
<%= render #users %>
</ul>
<%= will_paginate %>
app/views/users/_user.html.erb:
<li>
<%= gravatar_for_user user, size: 52 %>
<%= link_to user.name, user %>
<% if current_user.admin? && !current_user?(user) %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
</li>
Why is this test failing? Why is self.character.callsign in to_param not working?

It was because I hadn't manually given a character to all the users in test/fixtures. In characters.yml I created a character for every user, and the test passed.

Related

impossible to encrypt a password in a multi-step form with bcrypt

I'm a beginner in ruby on rails and I have a problem encrypting a password in my registration form. I installed the bcrypt gem but I don't understand why the form doesn't work...
my form model :
module Wizard
module User
STEPS = %w(step1 step2 step3).freeze
class Base
include ActiveModel::Model
include ActiveModel::SecurePassword
attr_accessor :user
has_secure_password
delegate *::User.attribute_names.map { |attr| [attr, "#{attr}="] }.flatten, to: :user
def initialize(user_attributes)
#user = ::User.new(user_attributes)
end
end
class Step1 < Base
validates :firstName, presence: {
message: 'Un prénom doit être renseigné'
}, length: { maximum: 50,
too_long: '50 caractères maximum pour le prénom' }
validates :lastName, presence: {
message: 'Un nom doit être renseigné'
}, length: { maximum: 50,
too_long: '50 caractères maximum pour le nom' }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255,
too_long: '255 caractères maximum pour l\'adresse mail' },
format: { with: VALID_EMAIL_REGEX,
message: 'Vous devez saisir une adresse mail valide' }
end
class Step2 < Step1
validates :password, presence: true, length: { in: 8..15 }, length: { in: 8..15 }
end
class Step3 < Step2
validates :school, presence: true
validates :typeOfTeacher, presence: true
validates :yearsOfExperience, presence: true
end
end
end
My model User.rb :
class User < ApplicationRecord
validates :firstName, presence: true, length: { maximum: 50 }
validates :lastName, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: true
has_secure_password
validates :password, presence: true
validates :school, presence: true
validates :typeOfTeacher, presence: true
validates :yearsOfExperience, presence: true
end
My controller :
When saving the user in my create function. The registration stops because of the password. I think I'm using wrong has_secure_password.
class WizardsController < ApplicationController
before_action :load_user_wizard, except: %i(validate_step)
def validate_step
current_step = params[:current_step]
#user_wizard = wizard_user_for_step(current_step)
#user_wizard.user.attributes = user_wizard_params
session[:user_attributes] = #user_wizard.user.attributes
if #user_wizard.valid?
next_step = wizard_user_next_step(current_step)
create and return unless next_step
redirect_to action: next_step
else
render current_step
end
end
def create
if #user_wizard.user.save
session[:user_attributes] = nil
redirect_to letsgo_path
else
redirect_to({ action: Wizard::User::STEPS.first } )
end
end
private
def load_user_wizard
#user_wizard = wizard_user_for_step(action_name)
end
def wizard_user_next_step(step)
Wizard::User::STEPS[Wizard::User::STEPS.index(step) + 1]
end
def wizard_user_for_step(step)
raise InvalidStep unless step.in?(Wizard::User::STEPS)
"Wizard::User::#{step.camelize}".constantize.new(session[:user_attributes])
end
def user_wizard_params
params.require(:user_wizard).permit(:firstName, :lastName, :email, :password, :password_confirmation, :school, :typeOfTeacher, :yearsOfExperience)
end
class InvalidStep < StandardError; end
end
My view :
<div class="div_Signup">
<div class="div_SignupBlock">
<%= render 'navbarSignup' %>
<div class="div_SignupBlockInt">
<div class="div_SignupText">
Inscription
</div>
<div class="div_SignupTextName">
Choisissez un mot de passe
</div>
<div class="div_SignupForm">
<%= form_for #user_wizard, as: :user_wizard, url: validate_step_wizard_path do |f| %>
<%= hidden_field_tag :current_step, 'step2' %>
<div class="div_SignupInputPassword">
<%= f.password_field :password, placeholder: 'Mot de passe', :class => field_class(#user_wizard, :password) %>
<% #user_wizard.errors[:password].each do |message| %>
<p style="color: red;"><%= message %></p>
<% end %>
</div>
<div class="div_SignupInputPasswordConfirmation">
<%= f.password_field :password_confirmation, placeholder: 'Confirmation du mot de passe', class: 'input_Signup' %>
</div>
</div>
<%= f.submit 'Suivant', class: 'button_Signup' %>
<% end %>
</div>
<footer class="footer_Signup">
<%= render 'footerSignup2' %>
</footer>
</div>
</div>
When I test my form when entering a password, I get error messages that show that the password cannot be empty, and the password confirmation field does not work.
However, I see that the password has been encrypted.
Thanks !!!

Nested forms, using form_with and fields_for, iteration not working

This is driving me crazy. Probably I'm overlooking something obvious, but I've read all other posts on the same topic and can't solve the problem. Thanks in advance for any help.
I have a model Klasse and a Model Klp. An instance of klasse has many klps. I would like to create klp in the same form as klasse. I've set this up using form_with and fields_for, and in klasses_controller.rb I have 3.times { #klasse.klps.build } But this iteration does not seem to work. The fields_for block is only shown once instead of 3 times.
The form in new.html was created following the rails "getting started" guide, and the fields_for block following the "form helpers" guide.
Here is my code:
app\models\klasse.rb:
class Klasse < ApplicationRecord
has_many :klps
has_many :people, :through => :klps
accepts_nested_attributes_for :klps, allow_destroy: true, :reject_if => :all_blank
validates :name, presence: true,
length: { minimum: 1 },
uniqueness: true
validates :klp_std_soll, presence: true,
length: { minimum: 1 }
def klp_std_ist
self.klps.sum("std")
end
end
app\models\person.rb
class Person < ApplicationRecord
has_many :klps
has_many :klasses, :through => :klps
validates :name, presence: true,
length: { minimum: 1 },
uniqueness: true
validates :vorname, presence: true,
length: { minimum: 1 }
def last_and_first_name
"#{name}, #{vorname}"
end
end
app\models\klp.rb
class Klp < ApplicationRecord
belongs_to :klasse
belongs_to :person
end
app\controllers\klasses_controller.rb
class KlassesController < ApplicationController
def new
#klasse = Klasse.new
3.times { #klasse.klps.build }
end
private
def klasse_params
params.require(:klasse).permit(:name, :klassentyp_id, :klp_std_soll, klps_attributes: [:id, :person_id, :std, :_destroy])
end
end
app\controllers\klps_controller.rb
class KlpsController < ApplicationController
def new
#klp = Klp.new
end
private
def klp_params
params.require(:klp).permit(:klasse_id, :person_id, :std)
end
end
app\views\klasses\new.html.erb
<%= form_with scope: :klasse, url: klasses_path, local: true do |form| %>
<ul>
<%= form.fields_for :klps do |klps_form| %>
<p>
<%= klps_form.label :person %>
<%= klps_form.collection_select :person_id, Person.all, :id, :last_and_first_name %>
<%= klps_form.label :std %>
<%= klps_form.text_field :std %>
</p>
<% end %>
</ul>
<p>
<%= form.submit %>
</p>
<% end %>
Try setting your collection of klps to an instance variable that you can then pass as an explicit "record object" argument to the fields_for method:
apps/controllers/klasses_controller.rb
def new
#klasse = Klasse.new
#klps = 3.times { #klasse.klps.build }
end
app\views\klasses\new.html.erb
<%= form.fields_for :klps, #klps do |klps_form| %>
... your form...
<% end %>
See also: How do I pass an array to fields_for in Rails?
Reference: https://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for
I am giving an example see
customer.rb
class Customer < ApplicationRecord
has_many :addresses, dependent: :destroy
accepts_nested_attributes_for :addresses, allow_destroy: true
end
address.rb
class Address < ApplicationRecord
belongs_to :customer
end
customers_controller.rb
class CustomersController < ApplicationController
def new
#customer = Customer.new
3.times {
#customer.addresses.build //You can use #customer.addresses.new
}
end
end
def customer_params
params.require(:customer).permit(:id, :name, addresses_attributes:[:id,:country, :state])
end
new.html.haml
= simple_form_for #customer do |f|
= f.input :name, label: false, wrapper: false, input_html: { class: "gui-input"}, placeholder: "Customer Name"
= f.fields_for :addresses do |a|
= render 'addresses/fields', a: a
view/addresses/_fields.html.haml
= a.input :country, as: :select, label:false, :wrapper => false, prompt: "Select country"
= a.input :city, label:false, :wrapper => false, input_html: {class: "gui-input", maxlength: 80, minlength: 2}, placeholder: "City"
for more info
https://www.pluralsight.com/guides/ruby-ruby-on-rails/ruby-on-rails-nested-attributes
use
<%= form.fields_for :klps_attributes do |klps_form| %>

Validation doesn't work as expected for related models in Rails 4?

I use one form to enter data for two models. When I save parent model (Tenant) the child model (User) also gets saved, but only if I don't validate tenant_id in User model. If I do validates :tenant_id, presence: true in User model then validation error "Users tenant can't be blank" is displayed. Any ideas why?
Tenant model:
class Tenant < ActiveRecord::Base
has_many :users, dependent: :destroy, inverse_of: :tenant
accepts_nested_attributes_for :users
before_validation do
self.status = 0
self.name = name_orig.upcase
email.downcase!
end
validates :name_orig, presence: true, length: { maximum: 255 }
validates :name, uniqueness: { case_sensitive: false }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :status, presence: true
end
User model:
class User < ActiveRecord::Base
belongs_to :tenant, inverse_of: :users
validates_presence_of :tenant
before_validation do
self.status = 0
self.email = email.downcase
end
VALID_USERNAME_REGEX = /\A\w+\s?\w*\z/i
validates :name, presence: true, length: { maximum: 50 },
format: { with: VALID_USERNAME_REGEX },
uniqueness: { case_sensitive: false }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }
validates :tenant_id, presence: true
validates :status, presence: true
end
Tenant controller:
class TenantsController < ApplicationController
def new
#tenant = Tenant.new
#tenant.users.build
end
def create
#tenant = Tenant.new(tenant_params)
#tenant.save
if #tenant.save
flash[:success] = "Welcome!"
redirect_to #tenant # redirects to tenant profile
else
render 'new'
end
end
private
def tenant_params
params.require(:tenant).permit(:name_orig, :email,
users_attributes: [:name, :email, :password, :password_confirmation])
end
end
Signup form:
<%= form_for(#tenant) do |f| %>
<%= render 'shared/tenant_error_messages' %>
<%= f.label :name_orig, "Company name" %>
<%= f.text_field :name_orig, class: 'form-control' %>
<%= f.label :email, "Company e-mail" %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.fields_for(:users) do |u| %>
<%= u.label :name, "User name" %>
<%= u.text_field :name, class: 'form-control' %>
<%= u.label :email, "User e-mail" %>
<%= u.email_field :email, class: 'form-control' %>
<%= u.label :password, "Password" %>
<%= u.password_field :password, class: 'form-control' %>
<%= u.label :password_confirmation, "Password confirmation" %>
<%= u.password_field :password_confirmation, class: 'form-control' %>
<% end %>
<%= f.submit "Save", class: "btn btn-primary" %>
<% end %>
Since you are using nested attributes and saving both models at the same time, you cannot validate tenant_id for user since they will be persisted in a transaction.
Because tenant is not persisted, it does not yet have an id. Since it does not have an id, there cannot be tenant_id for user.
In this case validating tenant_id is pointless, because you cannot persist a user without tenant since user is being built on top of tenant. So if your user will be persisted, then a corresponding tenant will also be present.
In the case you mentioned, where users can sign up on an independent form -
To validate that a tenant will be associated in the create action use:
tenant = Tenant.find_by(params[:tenant_id])
user = tenant.users.build(user_params)
instead of
user = User.new(user_params)
This way you will not have any orphan children

undefined method `name' for nil:NilClass

I have 2 id's stored in the database. I have not touched 'spec' folder. but still localhost:3000/users/1 is showing this error.
undefined method `name' for nil:NilClass
Extracted source (around line #1):
1: <%= #user.name %>, <%= #user.email %>
users_controller.rb
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def new
end
end
show.html.erb
<%= #user.name %>, <%= #user.email %>
models - > users.rb
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_secure_password
before_save { |user| user.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
end
Edit show.html.erb Like bellow:
<%- if #user.present? %>
<%= #user.name %>, <%= #user.email %>
<%end%>
The issue is arise due to #user is nil. So yu should check either the object of user is nil or not. Then Process the object as per your requirements.

cant sort active records

I am a noobie and I am trying to simply get all the records in the database and display them in alphabetical order. Whenever I display the index page the records are always sorted in descending by their id. I used the console to try calling EvalTest.order("name") and again I kept getting the records sorted by their id in descending order instead of by name. Do I need to add an index on the name column to sort by it? This seems like the answer should be so easy but I can't seem to figure it out...
Here is my code:
User Model:
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_secure_password
has_many :eval_tests
before_save { |user| user.email = email.downcase }
before_save :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
after_validation { self.errors.messages.delete(:password_digest) }
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
Eval_Test Model:
class EvalTest < ActiveRecord::Base
attr_accessible :description, :name
belongs_to :user
validates :user_id, presence: true
validates :name, presence: true
validates :description, presence: true, length: { maximum: 350 }
default_scope order: 'eval_tests.created_at DESC'
end
EvalTest Controller:
class EvalTestsController < ApplicationController
def show
#eval_test = EvalTest.find(params[:id])
end
def new
#eval_test = EvalTest.new
end
def index
#eval_tests = EvalTest.order("name")
end
def create
#eval_test = current_user.eval_tests.build(params[:eval_test])
if #eval_test.save
flash[:success] = "Nouveau test cree!"
redirect_to #eval_test
else
render 'new'
end
end
end
Evaluation Test index.html.erb:
<% provide(:title, 'Index des tests') %>
<h1>Index des tests</h1>
<ul class="eval_tests">
<% #eval_tests.each do |eval_test| %>
<li>
<%= link_to eval_test.name, eval_test %>
</li>
<% end %>
</ul>
This is happening because you have used default scope in your model. Try
#eval_tests = EvalTest.reorder("name")
This should solve your issue.

Resources