date_as_string 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/Gemfile +10 -0
- data/Rakefile +28 -0
- data/date_as_string.gemspec +22 -0
- data/lib/date_as_string.rb +222 -0
- data/lib/date_as_string/formats.rb +55 -0
- data/lib/date_as_string/version.rb +12 -0
- data/spec/date_as_string_spec.rb +80 -0
- data/spec/spec_helper.rb +15 -0
- metadata +68 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f8bde9b4c771dda4f6270bb5270de295af06337e4352a55f425e4aaaa277ac21
|
4
|
+
data.tar.gz: '087b9878efd66ca01a9e60a5aa74fd7d6dc057edb680c570f70eaefcf6508878'
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 44c6573aee04ef19c850c36daabf1093ce2684fd266e48850fd51bab1f055acb214721960071c5b6932eb38467f8f7f42f4a692276254f0b094cd2a8eaafa13c
|
7
|
+
data.tar.gz: b029b37c736ef35ecad4e3ffe4cf1ef67c12f7e37b21a025831198cbb7be137ab3281620f8651b645ab69a2fa601d99d4a71297ee12feb3309843ff13dd12b2f
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
|
3
|
+
#Override bundler to prevent releasing to RubyGems
|
4
|
+
module Bundler
|
5
|
+
class GemHelper
|
6
|
+
def release_gem
|
7
|
+
guard_clean
|
8
|
+
guard_already_tagged
|
9
|
+
built_gem_path = build_gem
|
10
|
+
tag_version {
|
11
|
+
git_push
|
12
|
+
}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
#http://asciicasts.com/episodes/245-new-gem-with-bundler
|
18
|
+
#$ rake -T
|
19
|
+
#rake build # Build mesav-#.#.#.gem into the pkg directory
|
20
|
+
#rake install # Build and install mesav-#.#.#.gem into system gems
|
21
|
+
#rake release # Create tag v#.#.#, build, and push
|
22
|
+
|
23
|
+
#RSPEC
|
24
|
+
require 'rspec/core/rake_task'
|
25
|
+
|
26
|
+
RSpec::Core::RakeTask.new('spec')
|
27
|
+
|
28
|
+
task :default => :spec
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "date_as_string/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "date_as_string"
|
7
|
+
s.version = DateAsString::VERSION::STRING
|
8
|
+
s.authors = ["Eric Sullivan"]
|
9
|
+
s.email = ["eric.sullivan@annkissam.com"]
|
10
|
+
s.homepage = "http://annkissam.com"
|
11
|
+
s.summary = %q{Convert from Date to String and vice versa by attaching _string suffix to an ActiveRecord field}
|
12
|
+
s.description = %q{Treat an ActiveRecord Date column as a String}
|
13
|
+
|
14
|
+
s.rubyforge_project = "date_as_string"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_development_dependency "rspec"
|
22
|
+
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
require "date_as_string/version"
|
2
|
+
|
3
|
+
require "date_as_string/formats"
|
4
|
+
|
5
|
+
module DateAsString
|
6
|
+
def self.guess_century(year, cutoff=20)
|
7
|
+
century = get_century_from_year_range(year, cutoff)
|
8
|
+
|
9
|
+
century.present? ? century : raise_century_error
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.included(base)
|
13
|
+
base.send(:extend, DateAsString::ClassMethods)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.parse_date(date)
|
17
|
+
date.respond_to?(:strftime) ? date.strftime('%m/%d/%Y') : nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.parse_string(date_string)
|
21
|
+
begin
|
22
|
+
case
|
23
|
+
when t_regex.match(date_string)
|
24
|
+
date_for_t_match(date_string)
|
25
|
+
when mmddyy_regex.match(date_string)
|
26
|
+
date_for_mmddyy_match(date_string)
|
27
|
+
when mmddyy_regex(with_slashes: true).match(date_string)
|
28
|
+
date_for_mmddyy_match(date_string, with_slashes: true)
|
29
|
+
when mmddyyyy_regex.match(date_string)
|
30
|
+
date_for_mmddyyyy_match(date_string)
|
31
|
+
when mmddyyyy_regex(with_slashes: true).match(date_string)
|
32
|
+
date_for_mmddyyyy_match(date_string, with_slashes: true)
|
33
|
+
else
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
rescue
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module ClassMethods
|
42
|
+
def date_as_string(*args)
|
43
|
+
defaults = {:input_formats => [:mm_dd_yyyy, :mm_dd_yyyy, :mmddyyyy, :mmddyy, :t_pmd],
|
44
|
+
:default_display_format => :mm_dd_yyyy,
|
45
|
+
:century_cutoff => 20,
|
46
|
+
:default => nil}
|
47
|
+
options = args.last.is_a?(Hash) ? defaults.merge(args.pop) : defaults
|
48
|
+
|
49
|
+
#We're current not doing anything with the options...
|
50
|
+
#We should support default formats, default value, and allowable formats...
|
51
|
+
|
52
|
+
unless self.respond_to?(:date_as_string_options)
|
53
|
+
cattr_accessor :date_as_string_options
|
54
|
+
self.date_as_string_options = {}
|
55
|
+
end
|
56
|
+
|
57
|
+
args.each do |attribute_name|
|
58
|
+
self.date_as_string_options[attribute_name] = options
|
59
|
+
|
60
|
+
validate :"#{attribute_name}_format"
|
61
|
+
|
62
|
+
define_method "#{attribute_name}_string" do |*display_format|
|
63
|
+
if instance_variable_get("@#{attribute_name}_string")
|
64
|
+
instance_variable_get("@#{attribute_name}_string")
|
65
|
+
else
|
66
|
+
if self.send("#{attribute_name}").nil?
|
67
|
+
if options[:default] && options[:default].is_a?(String)
|
68
|
+
eval(options[:default])
|
69
|
+
elsif options[:default] && options[:default].is_a?(Proc)
|
70
|
+
options[:default].call
|
71
|
+
else
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
else
|
75
|
+
#self.send("#{attribute_name}").strftime('%m/%d/%Y')
|
76
|
+
unless display_format.empty?
|
77
|
+
DateAsString::Formats.find_format(display_format[0]).strftime(self.send("#{attribute_name}"))
|
78
|
+
else
|
79
|
+
DateAsString::Formats.find_format(options[:default_display_format]).strftime(self.send("#{attribute_name}"))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
define_method "#{attribute_name}_string=" do |str|
|
86
|
+
instance_variable_set("@#{attribute_name}_string", str)
|
87
|
+
self.send("#{attribute_name}=", DateAsString.parse_string(str))
|
88
|
+
end
|
89
|
+
|
90
|
+
define_method "#{attribute_name}_format" do
|
91
|
+
re = /^(([tT]([\-\+]\d+)?)|(\d{6})|(\d{8})|([0-1]?\d\/[0-3]?\d\/\d\d)|([0-1]?\d\/[0-3]?\d\/(19|20)\d\d))$/
|
92
|
+
|
93
|
+
attribute_string = instance_variable_get("@#{attribute_name}_string")
|
94
|
+
|
95
|
+
#No string was entered - this clears the date
|
96
|
+
return if attribute_string.blank?
|
97
|
+
|
98
|
+
#the entered string did not match a regex...
|
99
|
+
if (attribute_string =~ re).nil?
|
100
|
+
errors.add("#{attribute_name}_string", "must be in the format of MM/DD/YY, MM/DD/YYYY, MMDDYY, MMDDYYYY or t, t+#, t-#")
|
101
|
+
return
|
102
|
+
end
|
103
|
+
|
104
|
+
#The mapping did not work for some reason...
|
105
|
+
if self.send("#{attribute_name}").nil?
|
106
|
+
errors.add("#{attribute_name}_string", :invalid)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def self.year_range(cutoff)
|
116
|
+
current_year = Time&.zone&.today&.year || Date.today.year
|
117
|
+
|
118
|
+
range_start = current_year - (99 - cutoff)
|
119
|
+
range_end = current_year + cutoff
|
120
|
+
|
121
|
+
range_start..range_end
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.get_century_from_year_range(year, cutoff=20)
|
125
|
+
year_range(cutoff).detect { |temp_year| temp_year % 100 == year }
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.raise_century_error
|
129
|
+
raise RangeError.new('Error converting year...')
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.t_regex
|
133
|
+
/^[tT]((?<operator>[\-\+])(?<days_count>\d+))?$/
|
134
|
+
end
|
135
|
+
|
136
|
+
#Allow MMDDYY or MM/DD/YY
|
137
|
+
def self.mmddyy_regex(options={})
|
138
|
+
with_slashes = options.fetch(:with_slashes, false)
|
139
|
+
|
140
|
+
slash_str = with_slashes ? '/' : nil
|
141
|
+
month_str = '(?<month>[0-1]?\d)'
|
142
|
+
day_str = '(?<day>[0-3]?\d)'
|
143
|
+
year_str = '(?<year>\d{2})'
|
144
|
+
|
145
|
+
date_tup = [month_str, day_str, year_str]
|
146
|
+
slashes = [slash_str]*2
|
147
|
+
|
148
|
+
inner_regex = date_tup.zip(slashes)
|
149
|
+
.flatten
|
150
|
+
.compact
|
151
|
+
.join
|
152
|
+
|
153
|
+
/^#{inner_regex}$/
|
154
|
+
end
|
155
|
+
|
156
|
+
#Allow MMDDYYYY or MM/DD/YYYY
|
157
|
+
def self.mmddyyyy_regex(options={})
|
158
|
+
with_slashes = options.fetch(:with_slashes, false)
|
159
|
+
|
160
|
+
slash_str = with_slashes ? '/' : nil
|
161
|
+
month_str = '(?<month>[0-1]?\d)'
|
162
|
+
day_str = '(?<day>[0-3]?\d)'
|
163
|
+
year_str = '(?<year>\d{4})'
|
164
|
+
|
165
|
+
date_tup = [month_str, day_str, year_str]
|
166
|
+
slashes = [slash_str]*2
|
167
|
+
|
168
|
+
inner_regex = date_tup.zip(slashes)
|
169
|
+
.flatten
|
170
|
+
.compact
|
171
|
+
.join
|
172
|
+
|
173
|
+
/^#{inner_regex}$/
|
174
|
+
end
|
175
|
+
|
176
|
+
# matches:
|
177
|
+
# e.g.)
|
178
|
+
# t => Date.today
|
179
|
+
# t+7 => Date.today + 1.week
|
180
|
+
# t-1 => Date.today - 1.day
|
181
|
+
def self.date_for_t_match(date_string)
|
182
|
+
today = Time&.zone&.today || Date.today
|
183
|
+
|
184
|
+
t_regex.match(date_string) do |match_data|
|
185
|
+
operator, days_count = *[match_data[:operator], match_data[:days_count]]
|
186
|
+
|
187
|
+
operator ? today.send(operator, days_count.to_i) : today
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.date_for_mmddyy_match(date_string, options={})
|
192
|
+
slashy_regex = options.fetch(:with_slashes, false)
|
193
|
+
month, day, year = nil, nil, nil
|
194
|
+
|
195
|
+
mmddyy_regex(with_slashes: slashy_regex).match(date_string) do |match_data|
|
196
|
+
month = match_data[:month] ? match_data[:month] : date_string[0..1]
|
197
|
+
day = match_data[:day] ? match_data[:day] : date_string[2..3]
|
198
|
+
year = match_data[:year] ? match_data[:year] : date_string[4..5]
|
199
|
+
|
200
|
+
century = DateAsString.guess_century(year.to_i)
|
201
|
+
|
202
|
+
Date.civil(century, month.to_i, day.to_i)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def self.date_for_mmddyyyy_match(date_string, options={})
|
207
|
+
slashy_regex = options.fetch(:with_slashes, false)
|
208
|
+
month, day, year = nil, nil, nil
|
209
|
+
|
210
|
+
mmddyyyy_regex(with_slashes: slashy_regex).match(date_string) do |match_data|
|
211
|
+
month = match_data[:month] ? match_data[:month] : date_string[0..1]
|
212
|
+
day = match_data[:day] ? match_data[:day] : date_string[2..3]
|
213
|
+
year = match_data[:year] ? match_data[:year] : date_string[4..5]
|
214
|
+
|
215
|
+
Date.civil(year.to_i, month.to_i, day.to_i)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
if Object.const_defined?("ActiveRecord")
|
221
|
+
ActiveRecord::Base.send(:include, DateAsString)
|
222
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module DateAsString
|
2
|
+
module Formats
|
3
|
+
def self.find_format(format_string)
|
4
|
+
case format_string
|
5
|
+
when :mm_dd_yyyy
|
6
|
+
Format1
|
7
|
+
when :mm_dd_yy
|
8
|
+
Format2
|
9
|
+
when :mmddyyyy
|
10
|
+
Format3
|
11
|
+
when :mmddyy
|
12
|
+
Format4
|
13
|
+
when :t_pmd
|
14
|
+
Format5
|
15
|
+
else
|
16
|
+
raise TypeError.new("Format does not exist: #{format_string}")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
#mm_dd_yyyy
|
21
|
+
class Format1
|
22
|
+
def self.strftime(value)
|
23
|
+
value.strftime('%m/%d/%Y')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
#mm_dd_yy
|
28
|
+
class Format2
|
29
|
+
def self.strftime(value)
|
30
|
+
value.strftime('%m/%d/%y')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
#mmddyyyy
|
35
|
+
class Format3
|
36
|
+
def self.strftime(value)
|
37
|
+
value.strftime('%m%d%Y')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
#mmddyy
|
42
|
+
class Format4
|
43
|
+
def self.strftime(value)
|
44
|
+
value.strftime('%m%d%y')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
#t_pmd
|
49
|
+
class Format5
|
50
|
+
def self.strftime(value)
|
51
|
+
raise NotImplementedError.new
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'DateAsString' do
|
4
|
+
let(:today) { Date.today }
|
5
|
+
let(:tomorrow) { today + 1.day }
|
6
|
+
let(:yesterday) { today - 1.day }
|
7
|
+
let(:new_years_day_1990) { Date.new(1990, 1, 1) }
|
8
|
+
|
9
|
+
describe '.parse_date(date)' do
|
10
|
+
subject { DateAsString.parse_date date_arg }
|
11
|
+
|
12
|
+
context 'when provided with a Date' do
|
13
|
+
let(:date_arg) { new_years_day_1990 }
|
14
|
+
|
15
|
+
it { is_expected.to eq "01/01/1990" }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'handling invalid arguments' do
|
19
|
+
context "argument is a String" do
|
20
|
+
let(:date_arg) { 'abc' }
|
21
|
+
|
22
|
+
it { is_expected.to eq nil }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe ".parse_string(string)" do
|
28
|
+
subject { DateAsString.parse_string date_string_arg }
|
29
|
+
|
30
|
+
context "format -> 't+#'" do
|
31
|
+
let(:date_string_arg) { "t+1" }
|
32
|
+
|
33
|
+
it { is_expected.to eq tomorrow }
|
34
|
+
end
|
35
|
+
|
36
|
+
context "format -> 't-#'" do
|
37
|
+
let(:date_string_arg) { "t-1" }
|
38
|
+
|
39
|
+
it { is_expected.to eq yesterday }
|
40
|
+
end
|
41
|
+
|
42
|
+
context "format -> 't'" do
|
43
|
+
let(:date_string_arg) { "t" }
|
44
|
+
|
45
|
+
it { is_expected.to eq today }
|
46
|
+
end
|
47
|
+
|
48
|
+
context "format -> '######'" do
|
49
|
+
let(:date_string_arg) { "010190" }
|
50
|
+
|
51
|
+
it { is_expected.to eq new_years_day_1990 }
|
52
|
+
end
|
53
|
+
|
54
|
+
context "format -> '########'" do
|
55
|
+
let(:date_string_arg) { "01011990" }
|
56
|
+
|
57
|
+
it { is_expected.to eq new_years_day_1990 }
|
58
|
+
end
|
59
|
+
|
60
|
+
context "format -> '##/##/##'" do
|
61
|
+
let(:date_string_arg) { "01/01/90" }
|
62
|
+
|
63
|
+
it { is_expected.to eq new_years_day_1990 }
|
64
|
+
end
|
65
|
+
|
66
|
+
context "format -> '##/##/####'" do
|
67
|
+
let(:date_string_arg) { "01/01/1990" }
|
68
|
+
|
69
|
+
it { is_expected.to eq new_years_day_1990 }
|
70
|
+
end
|
71
|
+
|
72
|
+
describe 'handling invalid arguments' do
|
73
|
+
context "argument is a String" do
|
74
|
+
let(:date_string_arg) { 'abc' }
|
75
|
+
|
76
|
+
it { is_expected.to eq nil }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "date_as_string/version"
|
2
|
+
require "date_as_string/formats"
|
3
|
+
|
4
|
+
require 'active_support/all'
|
5
|
+
|
6
|
+
require 'bundler/setup'
|
7
|
+
Bundler.setup
|
8
|
+
|
9
|
+
require 'date_as_string'
|
10
|
+
|
11
|
+
require 'byebug'
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
# some (optional) config here
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: date_as_string
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.6
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eric Sullivan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-12-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: Treat an ActiveRecord Date column as a String
|
28
|
+
email:
|
29
|
+
- eric.sullivan@annkissam.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ".gitignore"
|
35
|
+
- ".rspec"
|
36
|
+
- Gemfile
|
37
|
+
- Rakefile
|
38
|
+
- date_as_string.gemspec
|
39
|
+
- lib/date_as_string.rb
|
40
|
+
- lib/date_as_string/formats.rb
|
41
|
+
- lib/date_as_string/version.rb
|
42
|
+
- spec/date_as_string_spec.rb
|
43
|
+
- spec/spec_helper.rb
|
44
|
+
homepage: http://annkissam.com
|
45
|
+
licenses: []
|
46
|
+
metadata: {}
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
requirements: []
|
62
|
+
rubyforge_project: date_as_string
|
63
|
+
rubygems_version: 2.7.6
|
64
|
+
signing_key:
|
65
|
+
specification_version: 4
|
66
|
+
summary: Convert from Date to String and vice versa by attaching _string suffix to
|
67
|
+
an ActiveRecord field
|
68
|
+
test_files: []
|