I have an enum in one of my model in my api
enum pay_method: {
cash: 0,
card: 1
}
I want to have validation for this enum but i can not do that . I wrote a validation in my model for that but it did not take any effect
A validation for enum is not going to work, because Rails does not even allow to assign an enum variable with a wrong value. You will get an error before a validation. There is a good discussion of this behaviour here https://github.com/rails/rails/issues/13971
rails enum functionality throws an error if the value submitted does not correspond to one of the keys or values of the hash. in this case it does not correspond to the key because the value you are submitting is a string so until here you are correct.
the error still appear because enums are set before the validation process. this could help you understand
#shippimg = Shippig.first
#shipping.status = 99
ArgumentError: '99' is not a valid status
rails developers say that programmers are the ones responsible taking care of what values they use assingning to enum attributes
i have made a gem for validating enums inclusion. this at least stops your server from crashing https://github.com/CristiRazvi/enum_attributes_validation
Related
Maybe I'm just completely overthinking this but I am trying to use enums to handle errors from an API I am integrating with.
From the swagger documentation of this API I can see all of the possible responses that could be returned. I have written these out as a BaseError enum:
enum BaseError: Error {
case badRequest // 400
case unauthorized // 401
case forbidden // 403
case unhandledError // XXX
...
}
Now in my client is where my problem starts.
My original hope was to take this BaseError enum and extend / add additional cases onto it depending on which client I am in.
Something like:
enum ClientSpecificError: BaseError {
case clientError
}
Which would allow me to return an error like ClientSpecificError.unauthorized
Now I know this is not possible as enums cannot inherit other enums but I am missing some understanding on how I should accomplish this.
Questions
Is there some other way I can use enums to accomplish this?
Is this even a "best practice"?
What you can do instead is use associated values and store the lowel-level error in a dedicated case. For instance:
enum ClientSpecificError: Error {
case clientError
case baseError(BaseError)
}
Apple documentation:
However, it is sometimes useful to be able to store associated values of other types alongside these case values. This enables you to store additional custom information along with the case value, and permits this information to vary each time you use that case in your code.
I've an array of objects containing title and salary which is used in typeahead directive.
I display department name and get entire object as value.
If none of the options match, I want user entered string to be converted to object still. Is there any way to update that?
This answer is pretty late, but I would just use ng-blur (to trap the end of the users input) along with a variable bound to typeahead-no-results. Then test if the variable is true in the method bound to ng-blur, and, if so, make an object out of the String supplied by the user and push it to your data source. Simple example found here.
All values coming from a web form are string. I have a class named Announcement which has a field kind and its data type is integer. On the model class I define an enum
enum kind: {
event: 1,
feature: 2
}
About mass assignment I have done it, no problem in general. The problem is when I'm doing this it will complain about '1' is not a valid kind because it's a string not an integer.
announcement=Announcement.new(announcement_params)
Is there any solution for this problem except manually set the value for the field?
Thank you
The answer just come to my mind, this is what i do to solve the problem
kind=params[:announcement][:kind].to_i
params[:announcement].delete(:kind)
params[:announcement].merge(kind: kind)
Get the kind param and convert it to an integer
Remove the kind which is a string from the params
Merge the new kind to the params
And the last is white listing parameters for mass assignment
Thank you #uzaif
You can redefine the setter for the kind attribute in your Announcement model, like this:
# app/models/announcement.rb
def kind=(value)
super(value.to_i)
# or
# super(Integer(value))
end
This converts the given value to integer first and then calls the original method defined by the enum. Note however, that to_i will convert anything, even non-numbers or nils - these will be converted to 0. So I'd advise to either never use 0 among your defined enum values or to use the Integer(value) form, which will raise an exception on non-numbers or nils.
The setter allows mass assignment again:
Announcement.new(kind: '1') # should be OK
I have a model to handle different application settings. Each setting has a value_type e.g. string, integer, boolean and enum. If a settings is defined as enum, it has many enum_options (just saved as key, value with a reference to the setting in database).
To handle these enums I want to use Enumerize gem. I tried a lot but I can't set enum option from database.
Here is my code how I try to define the different enum options.
enumerize :enum_values, in: -> { EnumOption.where(functional_parameter_id: self.id).map(&:value.to_sym) }
After that I try to call Setting.find(1).enum_values and I got nil.
If I call Setting.enum_values.values to get all available options to select, I just got ["#<Proc:0x007fd7e78e9218#/Users/path/to/app/app/models/setting.rb:31 (lambda)>"]
Is it possible to set the enum options dynamically?
I don't think you can use in with a lamdba literal in enumerize. This feature is not documented anywhere, and having a look at https://github.com/brainspec/enumerize/blob/c65867580967724479af1bffbfbea05cbfa62db4/lib/enumerize/attribute.rb#L15, it seems that enumerize strictly wants you to pass an array.
I have an object with an attribute called value which is of type big decimal. In the class definition i have validates_numericality_of.
However if i:
a.value = 'fire'
'fire' ends up getting typecast to the correct type before the validation fires so:
a.valid? => true
How do get the validation to fire before the typecast?
Thanks
Dan
From ActiveRecord::Base docs:
Sometimes you want to be able to read
the raw attribute data without having
the column-determined typecast run its
course first. That can be done by
using the <attribute>_before_type_cast
accessors that all attributes have.
For example, if your Account model has
a balance attribute, you can call
account.balance_before_type_cast or
account.id_before_type_cast.
This is especially useful in
validation situations where the user
might supply a string for an integer
field and you want to display the
original string back in an error
message. Accessing the attribute
normally would typecast the string to
0, which isn’t what you want.
A new gem has been created to help validate types in rails.
An explanatory blog post exists to answer more of the "why" it was created in the first place.
With this library your code could be:
class SomeObject < ActiveRecord::Base
validates_type :value, :big_decimal
end
This will throw an exception when anything except a float is assigned to value instead of quietly casting the value to a BigDecimal and saving it.