I have a model where I'd like to restrict input for a field to either be nil or fall within a specified array of values. I can get the inclusion part working, but the allow_nil: true bit doesn't seem to work for me:
class Mock::Patient < ActiveRecord::Base
LANGUAGE_OPTIONS = %w[English Spanish French German Chinese Hindi Punjabi]
validates :preferred_language, inclusion: { in: LANGUAGE_OPTIONS }
end
I've tried modifying that last line to things like:
validates :preferred_language, inclusion: { in: LANGUAGE_OPTIONS }, allow_nil: true
But to no avail. What's the simplest way to express this combination of simple inclusion or nil?
the correct form to allow nil to validate while still allowing a limited array of values is the following:
validates :preferred_language, inclusion: { in: LANGUAGE_OPTIONS, allow_nil: true }
notice how the allow_nil option is inside the inclusion option hash
I solved this by making the validates line look like this:
validates :preferred_language, inclusion: { in: LANGUAGE_OPTIONS + [nil] }
This way, I allow nil, but I don't change the constant I use in my view for feeding the collection of select options. I've since also prepended the array with an element '' so I don't have to explicitly include blank in my form input helper.
Related
I can't seem to figure out how to accomplish what I am trying to do here on my create method.
What I have right now works if there are no values, the item is deleted. However, if 1 or more param values exist, it passes and is saved. Not what I needed. I need an all or nothing scenario. I want to save only if all the permitted keys have their value. params.permit(:name, :description, :copyright)
Before an entry is saved using organizations.save!, I need to make sure none of the params that are permitted are nil or empty.
I search all over and can't seem to narrow down on an answer to my exact issue.
Here is my code:
class OrganizationsController < ApplicationController
def index
query_params = params.permit(:id, :name,)
if query_params.blank?
organizations = Organization.all
else
organizations = Organization.where(query_params)
end
render json: organizations, root: "organizations"
end
def create
organizations = Organization.new(organization_params)
if organization_params.present?
organizations.delete
else
organizations.save!
render json: organizations
end
end
private
def organization_params
params.permit(:name, :description, :copyright)
end
end
You should add validations to your model.
From your question i understand that you want to save details only if you get values in all the field, if not you don't want to save, right?. If yes, then adding validations to your model will give you what you wanted.
Add the following to your organization model
validates_presence_of :name
validates_presence_of :description
validates_presence_of :copyright
by doing so, the user won't be allowed to save the details unless and until all three fields have some value in it.
There is no need to use delete as the incomplete information will not be saved.
for more and advanced info click here
To check none of the values of organization_params hash is empty, you can do something like this:
organization_params.values.all? { |x| !x.empty? }
or, this:
organization_params.all? { |k,v| !v.empty? }
You can also check if any param value is empty:
organization_params.any? { |k,v| v.empty? }
So, your create method can be re-written as:
def create
organizations = Organization.new(organization_params)
if organization_params.any? { |k,v| v.empty? }
# at least one param is empty, so delete the record
organizations.delete
else
# all the params values are present, so save the record
organizations.save!
render json: organizations
end
end
This should be a no brainer. I have a Sinatra application with RSpec and, for some reason, my validations are failing with the inclusion validation.
This is my code:
class Employee < ActiveRecord::Base
validates :department, inclusion: { in: w%(Sales, Finance, Marketing) }
end
This is the spec file:
it 'should save a valid employee' do
employee = Employee.new(name: 'Employee One',
email: 'example#gmail.com',
title: 'Software Engineer',
department: 'Sales')
expect(employee).to be_valid
end
This test fails because the department field is not 'included in the list'. This is aggravating because it fails no matter what field I put. Errors like these are usually from something simple.
What am I missing?
%w is a short-cut for building array of strings. I think you are trying to build an array of strings like this:
["Sales", "Finance", "Marketing"]
In that case the correct form of using %w would be:
%w(Sales Finance Marketing)
See this post for more information on %w syntax in Ruby.
Also, look at the Rails Documentation for inclusion where %w is used correctly.
So, to solve your problem, change your Employee class to look like this:
class Employee < ActiveRecord::Base
validates :department, inclusion: { in: %w(Sales Finance Marketing) }
end
It seems that you have a syntax error, I believe w% should be %w.
Your %w syntax is conflicting with your actual list. The %w makes commas not necessary and actually adds them to your list (which is not what you want). Remove them.
%w(Sales, Finance, Marketing)
=> ["Sales,", "Finance,", "Marketing"]
vs
%w(Sales Finance Marketing)
=> ["Sales", "Finance", "Marketing"]
# model.rb
validates :employee_id, presence: true, uniqueness: true
When left empty, the error message says "Employee can't be blank" when I want it to say "Employee ID can't be blank".
I resolved this by:
# model.rb
validates :employee_id, presence: { message: " ID can't be blank" }, uniqueness: true
which outputs "Employee ID can' be blank".
However, this isn't a really good solution IMO. I would like some means of customizing the entire message, including the attribute prefix.
Is there a simple way to do this?
There are several "correct" ways to go about this, but you definitely shouldn't do it via the validation itself, or by defining your own validation method.
On a model-by-model level, this is controlled by the class-level human_attribute_name method.
If you want your model's employee_id field to be a special case where the _id postfix isn't truncated, define that special case by overridding human_attribute_name:
class MyModel
validates :employee_id, presence: true
def self.human_attribute_name(attr, options = {})
attr == :employee_id ? 'Employee ID' : super
end
end
In broader terms, you can redefine human_attribute_name on ActiveRecord::Base to override handling of all _id attributes, but I doubt you want to do this. Generally, it's a good thing that Rails drops the _id postfix.
The second (and probably better) mechanism is to simply rely on localization. ActiveRecord ties into your locale YAML files for just about everything. If you want your employee_id field to humanize to Employee ID regardless of language, you'll need to edit your YAML files.
# config/locales/en.yml
en:
activerecord:
attributes:
employee_id: "Employee ID"
You can override human_attribute_name and always send default value with id
class MyModel
def self.human_attribute_name(attribute, options = {})
super(attribute, { default: attribute.to_s.humanize(keep_id_suffix: true) } )
end
end
You can write a custom validation. By doing it that way, you get to define the error message inside the validation method.
The relevant section from Rails Guides is here: Performing Custom Validations
Something like:
Class Paystub
validate :employee_id_is_not_blank
def employee_id_is_not_blank
errors[:base] << "Employee must be a part of the record.") if id.blank?
end
end
p = Paystub.create
p.errors.full_messages #=> ["Employee must be a part of the record."]
Section 7.4 in the Rails Guides specified using errors[:base]. Error messages shoveled into :base don't require an attribute to be tied to them.
Update: This is not the right answer. See #meagars answer above.
I try to config simple_form to always set all fields NOT required by default.
But I still need this when I put :required => true in the view.
Then I go to config/initializers/simple_form.rb and set it like this.
config.wrappers :default, :class => :input, :required => false,
:hint_class => :field_with_hint, :error_class => :field_with_errors do |b|
and set config.required_by_default = false
But asterisk still show up.
Thanks for any suggestion.
Rafaiel,
I had the same problem in Rails 4.0, not sure what you're using.
The best solution I've found is to go to config/locales/simple_form.en.yml and change the first lines like this (the mark: line is the one you change):
en:
simple_form:
"yes": 'Yes'
"no": 'No'
required:
text: 'required'
mark: '' #mark was previously '*', which puts an asterisk in the display!
It's also possible to change SimpleForm setup.
SimpleForm.setup do |config|
# Whether attributes are required by default (or not). Default is true.
config.required_by_default = false
end
Take this into account: (taken from simple form github page)
Required fields are marked with an * prepended to their labels.
By default all inputs are required. When the form object includes
ActiveModel::Validations (which, for example, happens with Active
Record models), fields are required only when there is presence
validation. Otherwise, Simple Form will mark fields as optional. For
performance reasons, this detection is skipped on validations that
make use of conditional options, such as :if and :unless.
I need to add status for an object, and need a hint about the Rails way to do this. Somewhere I've seen status was added into the model, but already lost where it was.
By status, I mean something that tracks about the item state. Like {0: :ORDERED, 1: :CHANGED, 2: :SHIPPED, 3: :ARCHIVED} for order in store. Looks like it needs id that stored in DB, constant or symbol that I could use in code instead of integer id, and one or two human readable messages for UI
There's a couple simple ways to do this. If the names of the statuses are short, I'd do basically what Samy suggested and store them directly in the model. So, in your migration, you'd do
add_column :orders, :status, :string
Then, in your model, you can use the status method to retrieve the status. You'll want to make sure you only store valid statuses, so you the :inclusion validator something like this:
class Order
validates :status, inclusion: { in: %w(ordered changed shipped archived) },
presence: true
end
If the statuses are longer, you can do something very much like the above with a short name for each status, then add an additional method to give you the full status message
class Order
STATUSES = { 'ordered' => 'Order placed',
'changed' => 'A change has been made to the order',
'shipped' => 'The order has been shipped',
'archived' => 'The order has been archived' }
def self.valid_statuses
STATUSES.keys
end
validates :status, inclusion: { in: valid_statuses },
presence: true
def extended_status
STATUSES[status]
end
end
If the problem has some complexity (f.e: lots of states, the object changes its behavior when changing its state...), you could use the gem StateMachine.
MagicFieldNames might be what you are looking for, it has a discriminator type column that you can use for Single Table Inheritance.
If you want simpler, you can use a status column which value can equal ordered, changed, or shipped. You don't even need to create constants in Rails or such a thing.