I am currently trying to make custom validations work with an input of dates, but, unfortunately, it doesn't seem to work.
There are two pages inside the application, Index page and Search page. Inside the index page there is a text field that takes in a date. I am using Chronic gem which parses text into dates. If the date is invalid, Chronic returns nil. If it is valid, it redirects to search page and shows the date.
The code I wrote so far doesn't seem to work properly, but what I want to achieve is..
1) to validate that Chronic doesn't return nil
2) to validate that date is greater than today's date
Please note that I am not using a database with this, I just want to be able to validate inputted date without ActiveRecord. If someone could help me with this, your help will be greatly appreciated.
views/main/index.html.erb
<%= form_tag({controller: "main", action: "search"}, method: "get") do %>
<%= label_tag(:q, "Enter Date:") %>
<%= text_field_tag(:q) %>
<%= submit_tag "Explore", name: nil %>
<% end %>
views/main/search.html.erb
<%= @show_date %>
main_controller.rb
def search
passed_info = Main.new
if passed_info.valid_date?
@show_date = passed_info
else
flash[:error] = "Please enter correct date!"
render :index => 'new'
end
end
models/main.rb
class Main
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :q
validates_presence_of :q
def initialize(params={})
params.each do |attr, value|
self.public_send("#{attr}=", value)
end if params
end
def persisted?
false
end
def valid_date?
require 'chronic'
if Chronic.parse(q).nil? || Chronic.parse(q) < Time.today
errors.add(:q, "is missing or invalid")
end
end
end
EDIT:
this is what goes wrong...
localhost:3000
then it redirects to ..
localhost:3000/main/search?utf8=%E2%9C%93&q=invalid+date+test
No validation, no date, nothing..
Just as correctly suggested by ABMagil, I would like to post the full solution to my answer. In fact, this answer can apply really to anyone who wants to use validations using ActiveModel, with or without Chronic gem or dates involved. It can act as a valid template so to speak.
Frankly, most of my mistakes came from a really poor, at the time, understanding of what I actually tried to achieve. Most of the code needed major refactoring, see below the updates that I had to make. I tried to keep the code as well documented as possible.
views/main/index.html.erb
<%= form_for @search, url: { action: "search" },
html: { method: :get } do |f| %>
# Displays error messages if there are any.
<% if @search.errors.any? %>
The form contains <%= pluralize(@search.errors.count, "error") %>.<br />
<% @search.errors.each do |attr, msg| %>
<%= msg %><br />
<% end %>
<% end %>
<%= f.label :q, "Enter Date:" %>
<%= f.text_field :q %>
<%= f.submit "Explore", :class => 'submit' %>
<% end %>
views/main/search.html.erb - same as before
<%= @show_date %>
main_controller.rb
def index
# Initializes a string from the form to a variable.
@search = Search.new
end
def search
# Retrieves the input from the form.
@search = Search.new(params[:search])
# Checks for validity,
# If valid, converts a valid string into a date.
# Redirects to search.html.erb
# If not valid, renders a new index.html.erb template.
if @search.valid?
@show_date = (Chronic.parse(params[:search][:q])).to_date
else
render :action => 'index'
end
end
models/main.rb
class Main
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
# Accepts the passed attribute from the form.
attr_accessor :q
# If form is submitted blank, then output the prompting message.
validates_presence_of :q, :message => "This text field can't be blank!"
# Two custom validations that check if passed string converts to a valid date.
validate :date_is_valid
validate :date_not_before_today
# Initializes the attributes from the form.
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
# Checks for persistence, i.e. if it's a new record and it wasn't destroyed.
# Otherwise returns false.
def persisted?
false
end
# ABMagil's code used for custom date validations
private
require 'chronic'
def date_is_valid
if Chronic.parse(q).nil?
errors.add(:base, "Date is invalid")
end
end
def date_not_before_today
if !Chronic.parse(q).nil?
if Chronic.parse(q) < Date.today
errors.add(:base, "Date cannot be before today")
end
end
end
end