Custom Validations with a Model: failing on save - ruby-on-rails

I am not finding any great examples online and so am asking an easy question for what I expect will get an easy answer. Be gentle. :)
I have a Post model.
p = Post.find(1)
p.title = "This is the Title of this Article"
p.url_title = "this-is-the-title-of-this-article--created-by-user-name"
When the Post is created, I create the :url_title based off the title. This is how I key off it in the database rather than exposing IDs which are also boring and non-descriptive.
I build this :url_title in a before_save so that is why I can't simply use 'validates_uniqueness_of' because it looks like the validations are done before the before_save kicks in and there is a :url_title to validate.
So, I need to ensure that :url_title is unique. I append the "--created-by-#{p.user.name}" to allow for there to be multiple uses of the same title from different users.
So, what I need to do is have a custom validation that confirms that :url_title is unique to the database before saving and, if it is not unique, raises and error and informs the user.
Thoughts on the best way to do this?
'

You can move your callback from before_save to before_validation (see here). This callback will be run on create and update action, so I think it will be sufficient for your needs.

Use this to create the url_title
before_validation_on_create :create_url_title
....
private
def create_url_title
url_title = .....
end
Then add the proper validation to url_title
validate_uniqueness_of :url_title, :message => "This title is taken, please choose a different title"

Just add
validates_uniqueness_of :url_title
in your Post model

Related

Check User Input Before Saving

Good Day All!
Edited for better understanding.
First model is Inventory and in this model I have Product_Type, Product_Name and User_ID.
Second model I have Users which consist of First_Name, Last_Name and Pin_Number.
On my Inventories page I have a form for checking out said Product_Type and Product_Name, also a place for a user to put their Pin_Number in. On submit, it will check the Pin_Number they have typed in and validate it in the Users model and if the Pin_Number is correct it will create an entry with said Product_Type, Product_Name and User_ID (which is pulled from Pin_Number that was submitted.)
I am just trying to figure out how to validate that Pin_Number they submitted.
Thats why I thought I had to do some kind of validation and an if statement based on that validation. Not sure how to go about that.
I hope this clears up any confusion.
I am just trying to figure out how to validate that Pin_Number they submitted.
What constitutes a valid pin_number? Just that it allows you to successfully look up a User? What if a user enters another user's pin_number? Is that considered 'valid'? Something to think about...
It would be helpful if you would add to your question what your params look like upon form submission. But, we can do some guess work.
So, let's assume that params looks something like:
{..., "inventory"=>{"product_type"=>"foo", "product_name"=>"Bar"}, "pin_number"=>5, ...}
In your controller, you'll probably do something like:
if #user = User.find_by(pin_number: params[:pin_number])
#inventory = Inventory.new(inventory_params)
#inventory.user = #user
if #inventory.valid?
#inventory.save
# perhaps do some other stuff...
else
# handle the case where the `#inventory` is not valid
end
else
# handle the case where the `#user` was not found
end
This assumes you have something like:
private
def inventory_params
params.require(:inventory).permit(:product_type, :product_name)
end
In your Inventory model, you probably want to do something like (I apologize, I'm not on Rails 5 yet, so some of the syntax may be incorrect):
class Inventory < ActiveRecord::Base
validates :user_id,
:product_type,
:product_name,
presence: true
belongs_to :user
end
You probably also want to consider adding an index on User.pin_number if you're going to be doing a lot of finding by it.
Not sure if I got the question right, but sounds like a custom validator to me.
You can learn more about custom validators in the official Rails documentation under "Custom Validators"
Also, consider moving the class for the custom validator you'll build to a concern, which is a great way to make it reusable. You can find more information on this StackOverflow question and this nice tutorial.

How to validate request parameters in rails 4 like laravel 5?

I have an action controller to validate params that are coming from view.
def duplicate
#params to be validated
params = params[:group_to_duplicate]
#params have to be validated to avoid method 'find' for some reason
group = Group.find(params[:id])
#validate to avoid this 'if'
if group
group.duplicate params
notice = 'Some message'
else
notice = 'Some other message'
end
redirect_to groups_path, notice: notice
end
How to validate the request parameters coming from view, like laravel 5 enter link description here?
Sometimes you do need to validate request params, and not the model.
API's for example. You often need to validate the params given. Maybe you require a date range for an API that returns stock quotes.
The quote model is valid, but in order to fetch it properly you need to know the range the user needs because you don't allow them to have the entire quote history. Something like that.
Also in the case of a search form. Maybe you have a form that requires the user to type the last name before searching for an employee.
In these cases you can use form or view objects to help with the validation.
Here's quick article on form objects:
https://robots.thoughtbot.com/activemodel-form-objects
I've been looking at this as well. I'm going to give this a shot when I get some free time:
https://github.com/nicolasblanco/rails_param
In most cases, you should not have to validate request parameters in your controller.
The best practice for model form validations (required, unique, length, max, etc. ) is to define validations in the model i.e. /models/group.rb, and use a form library such as simple_form which automatically handles the model validations you've defined.
For example, in your group.rb you'll have:
class Group < ActiveRecord::Base
validates :group, presence: true
end
If you absolutely need to validate in your controller, use Action Controller Filters: http://guides.rubyonrails.org/action_controller_overview.html#filters
References:
http://guides.rubyonrails.org/active_record_validations.html
https://github.com/plataformatec/simple_form

custom validations with acts-as-taggable-on

Hi I am working on a Rails 3 project and I am using acts-as-taggable-on and everything works perfectly! :)
I just have one question.
Does anyone know how I can add my 'custom' validation to ActsAsTaggableOn::Tag? Any callbacks I can hook into (e.g before_tag_save)? or something similar?
I need to run a regex on each 'tag' (to ensure that each tag does not contain any illegal characters) in my tag_list before I save my model and would like to know if there is a standard way of doing it.
The way I solved the problem is by adding a validation method in my PostController which just iterates over the list of Tags and runs the regex, but this seems ugly to me.
Any suggestions?
Thank you in advance! :)
I used two ways in the past. One via a custom validator, another with a validates call.
Custom Validation method
In your model, setup the following
validate :validate_tag
def validate_tag
tag_list.each do |tag|
# This will only accept two character alphanumeric entry such as A1, B2, C3. The alpha character has to precede the numeric.
errors.add(:tag_list, "Please enter the code in the right format") unless tag =~ /^[A-Z][0-9]$/
end
end
Obviously you will need to replace the validation logic and the error message text with something more appropriate in your circumstance.
Keep in mind that in this scenario you can evaluate each tag as a string.
Standard Validation Method
Include this in your model
validates :tag_list, :format => { :with => /^([A-Z][0-9],?\s?)*$/,
:message => "Just too awesomezz" }
With this method, you will have to keep in mind that you are validating the entire array that looks like a string. For this reason, you will need to allow for commas and white spaces between the tags.
Choose whichever method suits you most
You could do it in your model with a before_save callback. There you can manipulate the (let's say) Post's tags before they are saved in the database.
Also you can rewrite tag method on User model:
def tag(taggable, opts = {})
return unless user.have? taggable.article
super
end
it can be useful if user can tag only same article (article :has_many users)

Trigger email send when a rails model boolean field is set to true instead of false

I have two models in my application. Families has_many Individuals's. individuals's belongs_to Families.
I'm trying to make it so that when a boolean field in the Families model is set to "true" an email is sent to all of the email addresses stored against Individuals when the family is "updated".
Can anyone help with this? I've been able to get ActiveMailer sending an email when an Individual is created easily enough, but cannot find a way to trigger on the update of a specific field?
Any help would be greatly appreciated!
Toby has the right idea but I disagree that this should be implemented in the controller... this is much better suited to a model after_update callback. something like this:
def update_fired
if yourfield_changed?
#send mail here
end
end
Then just make sure in your model you set the method to be the after_update callback
Maybe you could use an after filter?
on the Family model
after_update :email_individuals
private
def email_individuals
if boolean_field_changed?
individuals.each do |i|
// send out emails here
end
end
end
The next step would be to move this out of the request cycle and into a queue.
Hope I grokked your question properly.
Easiest way would be to add something to the update action of your controller
if individual.boolean_field_changed? and individual.boolean_field
individuals.each do |ind|
Mailer.deliver_mail(ind)
end
end
Rails has an API for detecting changes in model fields (in the above case you add _changed? to the field name and it will tell you if the field has been changed.

Is it possible to validate that the title of a new post is not equal to an existing permalink?

In this simplified scenario, I have a model called Post
Each post has a title and a permalink
The permalink is used in the url - i.e. http://mysite.com/blog/permalink
Before a post is created, a callback sets the permalink to be the same as the title
I validate the uniqueness of each title but I do not explicitly validate the uniqueness of the permalink for three reasons:
the permalink is equal to the title, which has already been validated for uniqueness
users have no concept of the permalink, since they are never asked to enter one, so when they get an error telling them that the permalink needs to be unique, they don't have a clue what it means
i'm using mongoid for the ORM and the permalink is the key for each post, and mongoid doesn't seem to let you modify a key
Now imagine the following scenario:
A user creates a post titled "hello" and the url is http://mysite.com/blog/hello
The user changes the title to "goodbye", but the permalink stays the same since it is immutable by design. This is fine, it's what I want and helps to prevents link-rot.
The user then creates a new post, titled "hello" which does not fail the validations since we changed the title of the first post already
The problem here is that we've now got two posts with the same url thanks to our before_create callback.
As I said, I don't want to validate the uniqueness of the permalink as the user in the above scanrio, upon creating the second posts would receive the "permalink must be unique" error and I just know this will confuse them
So I was thinking, is there a validation, or a technique that allows me to prevent a user from creating a post which has a title that is equal to an existing permalink?
If all else fails, then I will just validate the uniqueness of the permalink and write out a really lengthy validation error message that details what the permalink is, and why this one is not deemed to be unique
The stuff we have to think about when making webites, what a carry-on
I can think of four possible solutions:
Validate the uniqueness of permalink like this:
validates_uniqueness_of :permalink, :message => "This title is already taken"
This will present the user with an understandable message.
change your before_create callback to create a numbered permalink
def what_ever_your_callback_is_named
self.permalink = self.title.permalinkify # or however you create the permalink
while self.find_by_permalink(self.permalink)
# check if it is an already numbered permalink
if self.permalink =~ /^(.*)(-(\d+))?$/
self.permalink = "#{$~[1]}-#{$~[3].to_i++}"
else
self.permalink << "-2"
end
end
end
change your before_create to a before_save callback, but than the resulting link is only permanent as long your title doesn't change.
use friendly_id for creating and managing the permalink. It's very powerfull.
Your method that creates the permalink name could check whether that permalink is unique and if not then append a YYYYMMDDHHMMSS value (or some other value to create a unique permalink) to the permalink.
You can write a custom validation method
class Post < ActiveRecord::Base
# You'll need to check only when creating a new post, right?
def validate_on_create
# Shows message if there's already a post with that permalink
if Post.first(:conditions => ["permalink = ?", self.title])
errors.add_to_base "Your error message"
end
end
end

Resources