rails - how to show and save a date in us format? - ruby-on-rails

I can get my content_date to show in mm/dd/yyyy format and I can save it correctly.
If I have a validation error in other data the form redisplays the date the desired way which is mm/dd/yyyy
I can also edit a record and see the date in format mm/dd/yy
The problem I have is that editing the record flips the month and year so that
08/02/2012
becomes
02/08/2012
and
08/19/2012
doesn't work at all.
Ironically If I record the record twice in a row and the day is not more than 12 it flips back to the original value
View:
= f.text_field :content_date, id: 'datepicker', size: 10
I got new and create to work with (links controller)
def new
#link = Link.new
#link.content_date=Time.new().strftime("%m/%d/%Y")
...
def edit
#link = Link.find(params[:id])
#link.content_date=Time.new().strftime("%m/%d/%Y") if #link.content_date.nil?
def create
#link = Link.new(link_params)
if #link.content_date.nil?
#link.content_date = Time.new().strftime("%Y/%m/%d")
else
#link.content_date = #link.content_date.strftime("%Y/%m/%d")
end
but update (rails 4.0.2) is now just
def update
redirect_to Link.find(params[:id]).tap { |link|
link.update!(link_params)
}
end
and I can't figure out how to change the :content_date in the update the way I did in the create
fyi I have the american_date gem in my Gemfile but it doesn't help (and doesn't help if I remove it either).
I don't currently have any date initializer in config/initializers/
js date picker:
$ cat app/assets/javascripts/date-picker.js
$(function() {
$( "#datepicker" ).datepicker();
});
$(function(){
var dateInput = $("#datepicker");
var format = 'yy-mm-dd';
dateInput.datepicker({dateFormat: format});
dateInput.datepicker('setDate', $.datepicker.parseDate(format, dateInput.val()));
});

I changed
/app/assets/javascripts/datepicker.js
changing
var format = 'yy-mm-dd';
to
var format = 'mm/dd/yyyy';
Then I added a file config/inititlaizers/date-format.js, with
# Date
# ----------------------------
Date::DATE_FORMATS[:default] = "%m/%d/%Y"
# DateTime
# ----------------------------
DateTime::DATE_FORMATS[:default] = "%m/%d/%Y"
# Time
# ----------------------------
Time::DATE_FORMATS[:default] = "%m/%d/%Y %H:%M:%S"
This helped in all the displays and input fields and date-picker but the date still flipped.
Finally (this bit fixes the date flipping part), I changed my controller to be:
def update
r = Link.find(params[:id])
r.tap { |link|
link.update!(link_params)
}
r.update!(:content_date => link_params[:content_date].to_date.strftime("%Y-%d-%m"))
redirect_to r
end

Related

Ruby on Rails: Handling invalid dates with multiparameter dates

I've added a form to my rails app which asks for a date. Rather than use the (IMO) clunky date_select helper, or a date popup solution, I'd like to use seperate input fields for date, month and year (as specified in the GDS service manual). I've written a custom input for simple_form here:
class TextDateInput < SimpleForm::Inputs::Base
def input(wrapper_options)
input_html_options[:pattern] = '[0-9]*'
#value = #builder.object.send(attribute_name)
#merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
"#{date_field} #{month_field} #{year_field}".html_safe
end
def date_field
#builder.label(attribute_name, class: 'grouped-date date') do
output = template.content_tag(:span, 'Date')
output += #builder.text_field(attribute_name,
#merged_input_options.merge(
name: "#{#builder.object_name}[#{attribute_name}(3i)]",
maxlength: 2,
value: #value&.day
))
output
end
end
def month_field
#builder.label(attribute_name, class: 'grouped-date month') do
output = template.content_tag(:span, 'Month')
output += #builder.text_field(attribute_name,
#merged_input_options.merge(
name: "#{#builder.object_name}[#{attribute_name}(2i)]",
maxlength: 2,
value: #value&.month
))
output
end
end
def year_field
#builder.label(attribute_name, class: 'grouped-date year') do
output = template.content_tag(:span, 'Year')
output += #builder.text_field(attribute_name,
#merged_input_options.merge(
name: "#{#builder.object_name}[#{attribute_name}(1i)]",
maxlength: 4,
value: #value&.year
))
output
end
end
end
And it works perfectly in the frontend, however, if the user enters an invalid date (for example 99/99/9999), Rails raises an ActiveRecord::MultiparameterAssignmentErrors error. Is there a clean way to handle this so rather than raising an error I can apply a validation error to the database object and show an invalid date error to the user?
You can parse date in before_filter callback or introduce params object which will replace invalid date with date that can trigger AR validation.
I decided to have a stab at this myself and added the following to my base model class (I'm using Rails 5):
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
def update_attributes(attrs = {})
super parse_dates(attrs)
end
def parse_dates(attrs)
# First fetch any nested attributes
attrs.clone.each do |k, v|
next unless v.is_a?(Hash)
# If this is a hash, it's a nested attribute, so check for dates
attrs = parse_nested_dates(k, attrs)
end
# Now marshal the rest of the dates
marshal_dates(self.class, attrs)
end
def parse_nested_dates(key, attrs)
klass = Object.const_get key.split('_attributes').first.classify
attrs[key] = marshal_dates(klass, attrs[key])
attrs
end
def marshal_dates(klass, attrs)
# Get all the columns in the class that have a date type
date_columns = klass.columns_hash.select { |_k, value| value.type == :date }.keys
date_columns.each { |c| attrs = parse_date(attrs, c) }
attrs
end
def parse_date(attrs, property)
# Gather up all the date parts
keys = attrs.keys.select { |k| k =~ /#{property}/ }.sort
return attrs if keys.empty?
values = []
keys.each do |k|
values << attrs[k]
attrs.delete(k)
end
# Set the date as a standard ISO8601 date
attrs[property] = values.join('-')
attrs
end
end
This seems to work perfectly for both standard attributes and nested attributes, and means it automatically works for all date columns without me having to do anything.

How to convert data into an Excel file

I am using the CSV class to convert data into an Excel file based on http://railscasts.com/episodes/362-exporting-csv-and-excel.
Upon downloading the Excel file it is not updated with the latest data which I see on my webpage using filters. My Excel file contains data which is being shown when the page first loads.
I tried to debug the problem and tried other gems like xlsx_writer, getting the same result:
def commission_report
today = Date.today
if params[:from_date].present?
from_date = params[:from_date]
to_date = params[:to_date]
elsif params[:filter] == 'monthly'
to_date = today
from_date = today - 30
else
to_date = today
from_date = today - 7
end
#commissions_report = UserOrderHistory.select("user_order_histories.*,SUM(user_order_histories.revenue_sharing) as revenue_total, restaurants.restaurant_name, managers.username,revenue_sharings.revenue").joins("LEFT JOIN revenue_sharings ON revenue_sharings.restaurant_id = user_order_histories.restaurant_id").joins("LEFT JOIN restaurants ON restaurants.id = user_order_histories.restaurant_id").joins("LEFT JOIN managers ON managers.id = restaurants.manager_id").where("user_order_histories.status != ''").where("revenue_sharing > 0").group("user_order_histories.restaurant_id,user_order_histories.deduction_date").where("user_order_histories.deduction_date BETWEEN ? AND ?",from_date,to_date).order(sort_column + " " + sort_direction)
#commissions_report = #commissions_report.as_json
#commissions_report = Kaminari.paginate_array(#commissions_report).page(params[:page]).per(10)
# raise #commissions.inspect
respond_to do |format|
format.html
format.csv { send_data #commissions_report.to_csv }
format.xls #{ send_data #commissions_report.to_csv(col_sep: "\t") }
end
end
When you're making the filtered request to download the CSV, the params are not being parsed:
<%= link_to 'Download CSV', your_path(from_date: params[:from_date], to_date: params[:to_date], filter: params[:filter]) %>
This way your params will get sent back again to your controller.
It's because when you click on:
link_to "CSV", products_path(format: "csv")
It goes to the index method in the products controllers and within that method you're again fetching all the records from the DB:
#products = Product.order(:name)
So all you've to do is pass the collection of filtered products to the CSV format respond. Something like this:
format.csv { send_data #filtered_products.to_csv }

Datetime format date received is in different format in rails

I have a table which contains a column named DOB whose data type is DATETIME.The problem is that created_at is also a datetime but its data received through JSON is in this format
"created_at":"2013-02-02 11:57:42",
"dob":"2013-02-18T18:30:00Z"
Both the dates are in different format but both of them has datetime data type.
Now I'm using Datejs
to format the date which will not parse DOB format it can only parse created_at format.
What should I do now ?
Here's what I'm doing to parse through datejs
if(value.dob != null){
alert(value.dob);
d1 = Date.parse(value.dob);
alert(d1);
dob = d1.toString("M-d-yyyy");
}
And I'm getting this error on console:
: d1 is null
[Break On This Error]
dob = d1.toString("M-d-yyyy");
My controller:
def get_oi_report_contact
#contactlist = CaseOiReportMap.select("rc.*").where("case_oi_report_maps.oireport_identifier = ?",identifier)
.joins("LEFT JOIN case_oi_report_contacts_maps cm on cm.case_oi_report_map_id = case_oi_report_maps.id")
.joins("LEFT JOIN oi_report_contacts rc on rc.id = cm.oi_report_contact_id ")
respond_to do |format|
format.json { render :json => #contactlist.to_json }
end
end
I have also used monkey patch:
class ActiveSupport::TimeWithZone
def as_json(options = {})
strftime('%Y-%m-%d %H:%M:%S')
end
end
But it doesn't seem to work.
Use strftime to format the time string http://www.ruby-doc.org/stdlib-1.9.3/libdoc/date/rdoc/DateTime.html#method-i-strftime

Array only saves the last value in ruby on rails

I have a loop which outputs information I grabbed from a website. To make the information display in an readable fashion, I insert it into an array that will be displayed on my view page. However, The array does not store all the values retrieved and instead only saves the last value appended to it. In the end I can only get the last value inserted into the array to be displayed.
My controller file...
def home
scrape()
end
private
def scrape
require 'rubygems'
require 'nokogiri'
require 'open-uri'
time = Time.new
month = I18n.t("date.abbr_month_names")[time.month]
day = time.day
#strings = []
#United States
cities = [
"sfbay", "losangeles", "athensga", "phoenix", "santabarbara", "denver",
"panamacity", "miami", "austin", "bakersfield", "keys", "newyork"
]
cities.map do |city|
#Search Terms
search_terms = ["mechanic", "car", "tech"]
search_terms.map do |term|
escaped_term = CGI.escape(term)
url = "http://#{city}.craigslist.org/search/jjj?query=#{escaped_term}&catAbb=jjj&
srchType=A"
doc = Nokogiri::HTML(open(url))
doc.css(".row").map do |row|
date = row.css(".itemdate").text
a_tag = row.css("a")[0]
text = a_tag.text
link = a_tag[:href]
#strings == []
if date = "#{month} #{day}"
#strings << "#{date} #{text} #{link}"
end
end
end
end
end
In the view home.html.erb file...
<%= raw(#strings.join('<br />')) %>
So when I go to the home page, I'm only display the last value inserted into the array. What is wrong and how do I fix it?
For one thing you create a new array for every row for every city. (But don't, actually; the assignment is a compare, ==, at the moment.)
For another you set date equal to "#{month} #{day}" instead of doing a comparison.

Can using Chronic impair your sense of time?

Haha..
I'm using Chronic to parse the time users add in the Calendar. Where the code works and implements the right time, the end result is that, IF a user adds a time, then it has no date, and because it has no date, it will not show in results. Any ideas?
def set_dates
unless self.natural_date.blank? || Chronic.parse(self.natural_date).blank?
# check if we are dealing with a date or a date + time
if time_provided?(self.natural_date)
self.date = nil
self.time = Chronic.parse(self.natural_date)
else
self.date = Chronic.parse(self.natural_date).to_date
self.time = nil
end
end
unless self.natural_end_date.blank? || Chronic.parse(self.natural_end_date).blank?
# check if we are dealing with a date or a date + time
if time_provided?(self.natural_end_date)
self.end_date = nil
self.end_time = Chronic.parse(self.natural_end_date)
else
self.end_date = Chronic.parse(self.natural_end_date).to_date
self.end_time = nil
end
end
end
Edit:
Here is the time_provided? method:
def time_provided?(natural_date_string)
date_span = Chronic.parse(natural_date_string, :guess => false)
(date_span.last - date_span.first).to_i == 1
end
First, I'm not really sure what are you asking about, because it looks like the code intentionally does what you describe... When there's time provided, the date fields are assigned nil. And I don't think that is Chronic is to blame because that's how your code works.
Not knowing your design (why there are separate date & time fields), the types of fields etc., I would suggest starting with a little kludge like this:
if time_provided?(self.natural_date)
self.time = Chronic.parse(self.natural_date)
self.date = self.time.to_date
or:
self.end_date = Chronic.parse(self.natural_date).to_date
if time_provided?(self.natural_date)
self.time = Chronic.parse(self.natural_date)
end
Or maybe the problem is outside the code you provided: in the part that is responsible for the "because it has no date, it will not show in results" behavior? Maybe you should make the conditions more flexible?

Resources