require 'smarter_dates/version'
require 'chronic'
module SmarterDates
def self.included(klass) # :nodoc:
bootstrap_smarter_dates(klass)
@dt_attributes.each do |meth|
klass.send(:define_method, "#{meth}=".to_sym, convert_to_dt(meth))
end
end
# :call-seq:
# Module.convert_to_dt method
#
# attempts to convert using Chronic, DateTime and-or Date, whichever works first
# else simply yield the value
def self.convert_to_dt(convert_me)
Proc.new do |val|
dt = val.respond_to?(:to_chronic_datetime) ? val.to_chronic_datetime : val
if defined?(Rails)
set_rails_dt_attributes!(convert_me, dt)
else
instance_variable_set(:"@#{convert_me}", dt)
end
end
end
private
# :call-seq:
# Module.dt_attributes Klass
#
# Any attribute ending in _at, _on, _dt, or _d are parsed by Chronic.parse
# (for flexibility). Values are passed as is to Chronic.parse()
#
# == Arguments
# string:: A string
def self.dt_attributes(klass)
klass.instance_methods.select { |meth| meth.match(/_(?:at|on|dt|d)$/) }
end
def self.connected? # :nodoc:
ActiveRecord::Base.connected?
end
def self.migrated? # :nodoc:
return unless connected?
ActiveRecord::Base.connection.table_exists? 'schema'
end
# :call-seq:
# Module.rails_dt_attributes Klass
#
# Any attribute ending in _at, _on, _dt, or _d are parsed by Chronic.parse
# (for flexibility). Values are passed as is to Chronic.parse()
#
# == Arguments
# string:: A string
def self.rails_dt_attributes(klass)
dt_attrs = []
logger = Rails.logger
begin
dt_attrs = klass.column_names.select { |meth| meth.match(/_(?:at|on|dt|d)$/) }
logger.debug(RuntimeError, "unused include - #{self.class.to_s} does not have any attributes ending in _at, _on, _dt, or _d") if migrated? && dt_attrs.empty?
rescue ActiveRecord::StatementInvalid => _
end
dt_attrs.uniq
end
# :call-seq:
# Module.set_rails_dt_attributes! attribute, value
#
# set attribute ending in _at, _on, _dt, or _d to date datetime
#
# == Arguments
# attribute:: attribute to set
# value:: a value
def set_rails_dt_attributes!(meth,dt)
if dt && meth.match(/_(?:on|d)$/)
write_attribute meth, dt.to_date
elsif dt && meth.match(/_(?:at|dt)$/)
write_attribute meth, dt.to_datetime
else
write_attribute meth, dt
end
end
# :call-seq:
# Module.bootstrap_smarter_dates class
#
# Any attribute ending in _at, _on, _dt, or _d are parsed by Chronic.parse
# (for flexibility). Values are passed as is to Chronic.parse()
#
# == Arguments
# class:: A class to have chronically-parsed dates
def self.bootstrap_smarter_dates(klass)
@dt_attributes = []
if defined?(Rails)
@dt_attributes.concat(rails_dt_attributes(klass))
else
@dt_attributes.concat(dt_attributes(klass))
end
end
end