i want to know how to make a commbobox that shows a list of all users in my database, let me explain myself:
I have two clases:
class User < ActiveRecord::Base
validates :password, presence: true, confirmation: {strict: true}
validates :password_confirmation, presence: true
validates :telephone, uniqueness: true, presence: true, numericality: { only_integer: true }, presence: true, length: { minimum: 9, maximum: 9 }
validates :name, presence: true, length: { minimum: 4, maximum: 30 }, format: { with: /^[\w\s-]*/u, multiline: true,
message: 'only allows letters' }
has_many :valorations
end
class Valoration < ActiveRecord::Base
validates :points, presence:true, numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: 100 }
belongs_to :user
belongs_to :hability
end
And i have a show create view for the validation class:
<%= form_for(#valoration) do |f| %>
...errors check...
<div class="field">
#combo box code to be added
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And what i wanna do is to create a combo box that does something like the following pseudocode using fselect:
Invented code:
<%= f.select :user_id, options_for_select( for_each user do |u|
[[u.name,u.id]]
end %>
So at the end i have an combobox with all the users, i'm a noob to rails really know how to do it so any help is welcome
You can do something like below to achieve what you want.
<%= f.select :user_id, User.all.collect { |u| [ u.name, u.id ] } %>
For more info, you can refer to this link
Related
I'm building a rails app that users get quotes on rental equipment. A user can have many quotes, but only 1 open quote. A quote can have many quote_line_items and each quote_line_item has the possibility of having 1 or many quote_line_item_components which are essentially just a self referential association through a Join Model
class Quote < ApplicationRecord
belongs_to :user, required: false
has_many :quote_line_items, autosave: true, inverse_of: :quote
has_many :quote_line_item_components, through: :quote_line_items
after_update :quote_after_update, if: :submitted_at_changed?
validates :contact_name, presence: true, length: { maximum: 255 }, if: :submitting_quote?
validates :contact_phone, presence: true, numericality: { only_integer: true }, length: { is: 10 }, if: :submitting_quote?
validates :contact_email, presence: true, length: { maximum: 255 }, email_format: { message: 'is not a valid email'}, if: :submitting_quote?
validates :business_name, presence: true, length: { maximum: 255 }, if: :submitting_quote?
validates :pickup_date, presence: true, if: :submitting_quote?
validates :return_date, presence: true, if: :submitting_quote?
validates :job_name, presence: true, length: { maximum: 255 }, if: :submitting_quote?
scope :latest, -> { order(:created_at) }
scope :submitted_quotes, -> { where.not(submitted_at: nil) }
accepts_nested_attributes_for :quote_line_items
accepts_nested_attributes_for :quote_line_item_components
# If the value of the submitted_at field is changing, we're submitting this quote.
def submitting_quote?
submitted_at_changed?
end
def quote_line_items_attributes=(quote_line_items_attributes)
quote_line_items_attributes.each do |i, quote_line_item_attributes|
self.quote_line_items.build(quote_line_item_attributes)
end
end
def add_or_update_item_quantity(**opts)
line_item = quote_line_items.where(inventory_id: opts[:inventory_id]).first_or_initialize
if line_item.new_record?
line_item.assign_attributes(opts)
elsif opts[:set_quantity].to_s =~ /true/i
line_item.update(quantity: opts.dig(:quantity)&.to_i)
else
new_quantity = line_item.quantity.to_i + opts.dig(:quantity).try(:to_i)
line_item.update(quantity: new_quantity)
end
end
end
Quote Line Item Model
class QuoteLineItem < ApplicationRecord
belongs_to :quote
has_many :quote_line_item_components, foreign_key: "quote_line_item_id", class_name: "QuoteLineItem"
accepts_nested_attributes_for :quote_line_item_components
validates :inventory_id, presence: true
validates :quantity, presence: true, numericality: { integer_only: true, greater_than_or_equal_to: 1 }
# This is a boolean/stringified boolean to tell the Quote model if we want to SET a quantity.
# VS add to it.
attr_accessor :set_quantity
def reject_components(attributes)
attributes['quantity'].to_i == 0
end
end
The QuoteLineItemComponent
class QuoteLineItemComponent < ApplicationRecord
belongs_to :quote_line_item, foreign_key: "quote_line_item_id", class_name: "QuoteLineItem"
validates :inventory_id, presence: true
validates :quantity, presence: true, numericality: { integer_only: true, greater_than_or_equal_to: 1 }
end
The form that is filled out looks like this. Using formtastic have the form referencing the current quote and the item, and if there are nested items semantic_fields_for is used.
= semantic_form_for current_quote, as: :quote, url: users_quote_path(id: :current) do |f|
= f.inputs
= f.semantic_fields_for(:quote_line_items, current_quote.quote_line_items.build) do |i|
= i.input :inventory_name, as: :hidden, input_html: { value: #item.description }
= i.input :inventory_id, as: :hidden, input_html: { value: #item.inventory_id }
= i.input :quantity, as: :number, min: 1, step: 1, wrapper_html: { class: "d-flex align-items-center gap-2 mb-3 formtastic-select"}, input_html: { class: "form-select form-select-sm"}
= i.input :price, as: :hidden, wrapper_html: { class: 'mb-0'}, input_html: { value: #item.daily_rate(#item.inventory_id) }
- if display_item_components?(#item)
li.copy-heading Components
- #item.parts.each do |part|
= i.semantic_fields_for(:quote_line_item_components, i.object.quote_line_item_components.build) do |c|
li.mb-4
= part.descriptions
- if part.included?
b * included
span.d-block
- if current_user
= price_for_item(part)
- else
| Sign in to to view prices
- if !part.included?
span.d-block
- if current_user
= price_for_item(part)
- else
| Sign in to view prices
ul.p-0.mt-3
= c.input :quote_id, as: :hidden
= c.input :inventory_id , as: :hidden, input_html: { value: part.inventory_ids }
= c.input :inventory_name, as: :hidden, input_html: { value: part.descriptions }
= c.input :quantity, as: :number, min: 0, step: 1, wrapper_html: { class: "d-flex align-items-center gap-2 formtastic-select m-0"}, input_html: { class: "form-select form-select-sm"}
= f.actions do
= f.action :submit, as: :button, label: "Add to Quote", button_html: { class: 'btn btn-pink hover-bg-white px-5' }, :wrapper_html => { :data => { :controller => "sidebar"} }
The opts/params submitted are as follows
Single Item
{"0"=>{"inventory_id"=>"00004OCW", "inventory_name"=>"1-TON GRIP VAN", "quantity"=>"1", "price"=>"325.0"}}
Item with Child Items
{"0"=>{"inventory_id"=>"0000SQ8J", "inventory_name"=>"JOLEKO 400 - 400 JOKER BUG-A-BEAM ELLIPSOIDAL KIT", "quantity"=>"1", "price"=>"210.0", "quote_line_item_components_attributes"=>{"3"=>{"inventory_id"=>"00004ILK", "inventory_name"=>"SOURCE FOUR 19 DEGREE BARREL", "quantity"=>"0", "quote_id"=>""}, "4"=>{"inventory_id"=>"00004ILS", "inventory_name"=>"SOURCE FOUR 26 DEGREE BARREL", "quantity"=>"0", "quote_id"=>""}, "5"=>{"inventory_id"=>"00004ILZ", "inventory_name"=>"SOURCE FOUR 36 DEGREE BARREL", "quantity"=>"0", "quote_id"=>""}, "6"=>{"inventory_id"=>"00004IM6", "inventory_name"=>"SOURCE FOUR 50 DEGREE BARREL", "quantity"=>"0", "quote_id"=>""}}}}
I had this working before I added nested items, but now the nested hashes are throwing the unknown attribute on the key "0".
Where it's failing is when it's assigning the opts here line_item.assign_attributes(opts)
def add_or_update_item_quantity(**opts)
line_item = quote_line_items.where(inventory_id: opts[:inventory_id]).first_or_initialize
if line_item.new_record?
line_item.assign_attributes(opts)
elsif opts[:set_quantity].to_s =~ /true/i
line_item.update(quantity: opts.dig(:quantity)&.to_i)
else
new_quantity = line_item.quantity.to_i + opts.dig(:quantity).try(:to_i)
line_item.update(quantity: new_quantity)
end
end
unknown attribute '0' for QuoteLineItem.
raise UnknownAttributeError.new(self, k.to_s)
If anyone can point out the issue, that'd be much appreciated.
The name of the join table is created by using the lexical order of the class names. So a join between author and book models will give the default join table name of 'authors_books' because 'a' outranks 'b' in lexical ordering.
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 !!!
With validation context we can do:
validates :title, presence: true, on: :published
validates :content, length: { maximum: 50 }, on: :published
Is it possible to wrap multiple validations that share a context something like the following?
on: :published do
validates :title, presence: true
validates :content, length: { maximum: 50 }
end
Yes, you can group validations using the with_options method:
with_options(on: :published) do |record|
record.validates :title, presence: true
record.validates :content, length: { maximum: 50 }
end
See the Rails Guides, this article and the sources for more info.
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
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.