date_values 0.1.4 → 0.2.1
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 +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +31 -77
- data/lib/date_values/configuration.rb +19 -0
- data/lib/date_values/month_day.rb +21 -3
- data/lib/date_values/time_of_day.rb +31 -0
- data/lib/date_values/version.rb +1 -1
- data/lib/date_values/year_month.rb +20 -4
- data/lib/date_values.rb +1 -0
- data/sig/date_values.rbs +35 -0
- metadata +2 -8
- data/lib/date_values/rails/date_value_validator.rb +0 -13
- data/lib/date_values/rails/i18n_backend.rb +0 -29
- data/lib/date_values/rails/month_day_type.rb +0 -31
- data/lib/date_values/rails/time_of_day_type.rb +0 -34
- data/lib/date_values/rails/year_month_type.rb +0 -31
- data/lib/date_values/rails.rb +0 -22
- data/sig/date_values/rails.rbs +0 -44
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2fcd39df5c13a6c209d893be43a2be980a20219c5e02c5cc1283a67ba84cf07e
|
|
4
|
+
data.tar.gz: 16ddf29780a17802e64bef957362fe4588fe4e1c9bd67af9a95798d4c65f1379
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b0611c9270e1237268eb2a1d17cca321455bebca402afc5ce4730922171364948498759deba7ba471474657aa2e445b066d7ab26b33e23318f6848bb95363e6b
|
|
7
|
+
data.tar.gz: 664a70b1c75f04341ba490823ae76073bc7d5618493497d7e64c513e3de8c198ae67b0c8ce84549fca3100608922fc79f8b63592180c529be468e45300082e1f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.1] - 2026-03-21
|
|
4
|
+
|
|
5
|
+
- Add `TimeOfDay` arithmetic (`+`, `-`), `advance`, `change`, `to_seconds`, `.from_seconds`
|
|
6
|
+
- Add `YearMonth#advance`, `YearMonth#change`, `YearMonth#days`
|
|
7
|
+
- Add `MonthDay#change`
|
|
8
|
+
- Add `#as_json` to all value classes
|
|
9
|
+
- Add `DateValues.config.month_day_order` for locale-dependent `MonthDay.parse`
|
|
10
|
+
|
|
11
|
+
## [0.2.0] - 2026-03-20
|
|
12
|
+
|
|
13
|
+
- **Breaking**: Rails integration extracted to [date_values-rails](https://github.com/ursm/date_values-rails). `require 'date_values/rails'` now requires installing `date_values-rails` gem.
|
|
14
|
+
|
|
3
15
|
## [0.1.4] - 2026-03-20
|
|
4
16
|
|
|
5
17
|
- Cast returns `nil` for invalid input instead of raising, following Rails convention
|
data/README.md
CHANGED
|
@@ -23,14 +23,19 @@ include DateValues
|
|
|
23
23
|
ym = YearMonth.new(2026, 3)
|
|
24
24
|
ym.to_s # => "2026-03"
|
|
25
25
|
ym.to_date # => #<Date: 2026-03-01>
|
|
26
|
+
ym.days # => 31
|
|
26
27
|
|
|
27
28
|
YearMonth.from(Date.today) # => #<DateValues::YearMonth 2026-03>
|
|
28
29
|
YearMonth.parse('2026-03') # => #<DateValues::YearMonth 2026-03>
|
|
30
|
+
YearMonth.parse('2026/3') # also works
|
|
29
31
|
|
|
30
32
|
ym + 1 # => #<DateValues::YearMonth 2026-04>
|
|
31
33
|
ym - 1 # => #<DateValues::YearMonth 2026-02>
|
|
32
34
|
YearMonth.new(2026, 3) - YearMonth.new(2025, 1) # => 14
|
|
33
35
|
|
|
36
|
+
ym.advance(years: 1, months: 2) # => #<DateValues::YearMonth 2027-05>
|
|
37
|
+
ym.change(year: 2025) # => #<DateValues::YearMonth 2025-03>
|
|
38
|
+
|
|
34
39
|
# Range support
|
|
35
40
|
(YearMonth.new(2026, 1)..YearMonth.new(2026, 3)).to_a
|
|
36
41
|
# => [#<DateValues::YearMonth 2026-01>, #<DateValues::YearMonth 2026-02>, #<DateValues::YearMonth 2026-03>]
|
|
@@ -47,6 +52,9 @@ md.to_date(2026) # => #<Date: 2026-03-19>
|
|
|
47
52
|
|
|
48
53
|
MonthDay.from(Date.today) # => #<DateValues::MonthDay --03-20>
|
|
49
54
|
MonthDay.parse('--03-19') # => #<DateValues::MonthDay --03-19>
|
|
55
|
+
MonthDay.parse('3/19') # also works (month/day order by default)
|
|
56
|
+
|
|
57
|
+
md.change(month: 12) # => #<DateValues::MonthDay --12-19>
|
|
50
58
|
|
|
51
59
|
# Range membership
|
|
52
60
|
summer = MonthDay.new(6, 1)..MonthDay.new(8, 31)
|
|
@@ -64,6 +72,16 @@ TimeOfDay.new(14, 30, 45).to_s # => "14:30:45"
|
|
|
64
72
|
TimeOfDay.from(Time.now) # => #<DateValues::TimeOfDay 14:30>
|
|
65
73
|
TimeOfDay.parse('14:30') # => #<DateValues::TimeOfDay 14:30>
|
|
66
74
|
|
|
75
|
+
tod + 3600 # => #<DateValues::TimeOfDay 15:30>
|
|
76
|
+
tod - 1800 # => #<DateValues::TimeOfDay 14:00>
|
|
77
|
+
tod.advance(hours: 2, minutes: 15) # => #<DateValues::TimeOfDay 16:45>
|
|
78
|
+
tod.change(minute: 0) # => #<DateValues::TimeOfDay 14:00>
|
|
79
|
+
tod.to_seconds # => 52200
|
|
80
|
+
TimeOfDay.from_seconds(52200) # => #<DateValues::TimeOfDay 14:30>
|
|
81
|
+
|
|
82
|
+
# Wraps at 24h boundaries
|
|
83
|
+
TimeOfDay.new(23, 30) + 3600 # => #<DateValues::TimeOfDay 00:30>
|
|
84
|
+
|
|
67
85
|
# Range membership
|
|
68
86
|
business_hours = TimeOfDay.new(9, 0)..TimeOfDay.new(17, 0)
|
|
69
87
|
business_hours.cover?(TimeOfDay.new(12, 30)) # => true
|
|
@@ -90,97 +108,33 @@ in { hour: (9..17) }
|
|
|
90
108
|
end
|
|
91
109
|
```
|
|
92
110
|
|
|
93
|
-
|
|
111
|
+
### JSON
|
|
94
112
|
|
|
95
|
-
|
|
113
|
+
All classes implement `#as_json`, returning the same string as `#to_s`:
|
|
96
114
|
|
|
97
115
|
```ruby
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
attribute :billing_month, :year_month # string column "2026-03"
|
|
102
|
-
attribute :anniversary, :month_day # string column "--03-19"
|
|
103
|
-
attribute :opens_at, :time_of_day # string or time column
|
|
104
|
-
end
|
|
116
|
+
YearMonth.new(2026, 3).as_json # => "2026-03"
|
|
117
|
+
MonthDay.new(3, 19).as_json # => "--03-19"
|
|
118
|
+
TimeOfDay.new(14, 30).as_json # => "14:30"
|
|
105
119
|
```
|
|
106
120
|
|
|
107
|
-
|
|
121
|
+
## Configuration
|
|
108
122
|
|
|
109
|
-
|
|
110
|
-
Shop.where(billing_month: YearMonth.new(2026, 3))
|
|
111
|
-
# SELECT * FROM shops WHERE billing_month = '2026-03'
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### Validation
|
|
123
|
+
### MonthDay parse order
|
|
115
124
|
|
|
116
|
-
|
|
125
|
+
By default, `MonthDay.parse` interprets ambiguous formats like `"3/19"` as month/day. For day/month (European convention):
|
|
117
126
|
|
|
118
127
|
```ruby
|
|
119
|
-
|
|
120
|
-
attribute :start_month, :year_month
|
|
121
|
-
attribute :opens_at, :time_of_day
|
|
122
|
-
|
|
123
|
-
validates :start_month, comparison: {greater_than: -> { YearMonth.from(Date.current) }}
|
|
124
|
-
validates :opens_at, comparison: {
|
|
125
|
-
greater_than_or_equal_to: TimeOfDay.new(9, 0),
|
|
126
|
-
less_than_or_equal_to: TimeOfDay.new(17, 0)
|
|
127
|
-
}
|
|
128
|
-
end
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
Invalid input (e.g. `"25:00"`) is cast to `nil` rather than raising, following the same convention as Rails' built-in types. The `date_value` validator detects this and gives a meaningful error message:
|
|
128
|
+
DateValues.config.month_day_order = :day_first
|
|
132
129
|
|
|
133
|
-
|
|
134
|
-
class Shop < ApplicationRecord
|
|
135
|
-
attribute :opens_at, :time_of_day
|
|
136
|
-
|
|
137
|
-
validates :opens_at, presence: true, date_value: true
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
Shop.new(opens_at: '25:00').errors[:opens_at] # => ["is invalid"]
|
|
141
|
-
Shop.new(opens_at: '').errors[:opens_at] # => ["can't be blank"]
|
|
130
|
+
MonthDay.parse('19/3') # => #<DateValues::MonthDay --03-19>
|
|
142
131
|
```
|
|
143
132
|
|
|
133
|
+
ISO 8601 format (`--MM-DD`) is always month/day regardless of this setting.
|
|
144
134
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
All classes implement `#strftime`, and the Rails integration extends `I18n.l` to support them. Define formats in your locale files:
|
|
148
|
-
|
|
149
|
-
```yaml
|
|
150
|
-
# config/locales/en.yml
|
|
151
|
-
en:
|
|
152
|
-
year_month:
|
|
153
|
-
formats:
|
|
154
|
-
default: '%B %Y'
|
|
155
|
-
month_day:
|
|
156
|
-
formats:
|
|
157
|
-
default: '%B %-d'
|
|
158
|
-
time_of_day:
|
|
159
|
-
formats:
|
|
160
|
-
default: '%-I:%M %p'
|
|
161
|
-
long: '%-I:%M:%S %p'
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
```yaml
|
|
165
|
-
# config/locales/ja.yml
|
|
166
|
-
ja:
|
|
167
|
-
year_month:
|
|
168
|
-
formats:
|
|
169
|
-
default: '%Y年%-m月'
|
|
170
|
-
month_day:
|
|
171
|
-
formats:
|
|
172
|
-
default: '%-m月%-d日'
|
|
173
|
-
time_of_day:
|
|
174
|
-
formats:
|
|
175
|
-
default: '%-H時%-M分'
|
|
176
|
-
long: '%-H時%-M分%-S秒'
|
|
177
|
-
```
|
|
135
|
+
## Rails Integration
|
|
178
136
|
|
|
179
|
-
|
|
180
|
-
I18n.l YearMonth.new(2026, 3), locale: :en # => "March 2026"
|
|
181
|
-
I18n.l YearMonth.new(2026, 3), locale: :ja # => "2026年3月"
|
|
182
|
-
I18n.l TimeOfDay.new(14, 30), format: :long # => "2:30:00 PM"
|
|
183
|
-
```
|
|
137
|
+
See [date_values-rails](https://github.com/ursm/date_values-rails) for ActiveModel/ActiveRecord type casting, validation, I18n, and ActiveJob support.
|
|
184
138
|
|
|
185
139
|
## License
|
|
186
140
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DateValues
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_accessor :month_day_order
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@month_day_order = :month_first
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.config
|
|
13
|
+
@config ||= Configuration.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.configure
|
|
17
|
+
yield config
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -18,10 +18,20 @@ module DateValues
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def self.parse(str)
|
|
21
|
-
|
|
21
|
+
case str
|
|
22
|
+
when /\A--(\d{1,2})-(\d{1,2})\z/
|
|
23
|
+
new($1.to_i, $2.to_i)
|
|
24
|
+
when /\A(\d{1,2})[\/\-](\d{1,2})\z/
|
|
25
|
+
a, b = $1.to_i, $2.to_i
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
if DateValues.config.month_day_order == :day_first
|
|
28
|
+
new(b, a)
|
|
29
|
+
else
|
|
30
|
+
new(a, b)
|
|
31
|
+
end
|
|
32
|
+
else
|
|
33
|
+
raise ArgumentError, "invalid MonthDay: #{str}"
|
|
34
|
+
end
|
|
25
35
|
end
|
|
26
36
|
|
|
27
37
|
def <=>(other)
|
|
@@ -30,6 +40,10 @@ module DateValues
|
|
|
30
40
|
[month, day] <=> [other.month, other.day]
|
|
31
41
|
end
|
|
32
42
|
|
|
43
|
+
def change(month: self.month, day: self.day)
|
|
44
|
+
self.class.new(month, day)
|
|
45
|
+
end
|
|
46
|
+
|
|
33
47
|
def strftime(format)
|
|
34
48
|
# 2000 is a leap year, so Feb 29 works
|
|
35
49
|
Date.new(2000, month, day).strftime(format)
|
|
@@ -39,6 +53,10 @@ module DateValues
|
|
|
39
53
|
Date.new(year, month, day)
|
|
40
54
|
end
|
|
41
55
|
|
|
56
|
+
def as_json(*)
|
|
57
|
+
to_s
|
|
58
|
+
end
|
|
59
|
+
|
|
42
60
|
def to_s
|
|
43
61
|
format('--%02d-%02d', month, day)
|
|
44
62
|
end
|
|
@@ -29,10 +29,41 @@ module DateValues
|
|
|
29
29
|
[hour, minute, second] <=> [other.hour, other.minute, other.second]
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
def +(seconds)
|
|
33
|
+
self.class.from_seconds((to_seconds + seconds) % 86_400)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def -(seconds)
|
|
37
|
+
self + (-seconds)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def advance(hours: 0, minutes: 0, seconds: 0)
|
|
41
|
+
self + (hours * 3600 + minutes * 60 + seconds)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def change(hour: self.hour, minute: self.minute, second: self.second)
|
|
45
|
+
self.class.new(hour, minute, second)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def to_seconds
|
|
49
|
+
hour * 3600 + minute * 60 + second
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.from_seconds(total)
|
|
53
|
+
total = total % 86_400
|
|
54
|
+
h, rest = total.divmod(3600)
|
|
55
|
+
m, s = rest.divmod(60)
|
|
56
|
+
new(h, m, s)
|
|
57
|
+
end
|
|
58
|
+
|
|
32
59
|
def strftime(format)
|
|
33
60
|
Time.new(2000, 1, 1, hour, minute, second).strftime(format)
|
|
34
61
|
end
|
|
35
62
|
|
|
63
|
+
def as_json(*)
|
|
64
|
+
to_s
|
|
65
|
+
end
|
|
66
|
+
|
|
36
67
|
def to_s
|
|
37
68
|
if second.zero?
|
|
38
69
|
format('%02d:%02d', hour, minute)
|
data/lib/date_values/version.rb
CHANGED
|
@@ -17,10 +17,10 @@ module DateValues
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def self.parse(str)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
case str
|
|
21
|
+
when /\A(\d{4})[\/\-](\d{1,2})\z/ then new($1.to_i, $2.to_i)
|
|
22
|
+
else raise ArgumentError, "invalid YearMonth: #{str}"
|
|
23
|
+
end
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def <=>(other)
|
|
@@ -49,6 +49,18 @@ module DateValues
|
|
|
49
49
|
self + 1
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
def advance(years: 0, months: 0)
|
|
53
|
+
self + (years * 12 + months)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def change(year: self.year, month: self.month)
|
|
57
|
+
self.class.new(year, month)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def days
|
|
61
|
+
Date.new(year, month, -1).day
|
|
62
|
+
end
|
|
63
|
+
|
|
52
64
|
def strftime(format)
|
|
53
65
|
to_date.strftime(format)
|
|
54
66
|
end
|
|
@@ -57,6 +69,10 @@ module DateValues
|
|
|
57
69
|
Date.new(year, month, 1)
|
|
58
70
|
end
|
|
59
71
|
|
|
72
|
+
def as_json(*)
|
|
73
|
+
to_s
|
|
74
|
+
end
|
|
75
|
+
|
|
60
76
|
def to_s
|
|
61
77
|
format('%04d-%02d', year, month)
|
|
62
78
|
end
|
data/lib/date_values.rb
CHANGED
data/sig/date_values.rbs
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
module DateValues
|
|
2
2
|
VERSION: String
|
|
3
3
|
|
|
4
|
+
def self.config: () -> Configuration
|
|
5
|
+
def self.configure: () { (Configuration) -> void } -> void
|
|
6
|
+
|
|
7
|
+
class Configuration
|
|
8
|
+
attr_accessor month_day_order: :month_first | :day_first
|
|
9
|
+
|
|
10
|
+
def initialize: () -> void
|
|
11
|
+
end
|
|
12
|
+
|
|
4
13
|
class YearMonth
|
|
5
14
|
include Comparable
|
|
6
15
|
|
|
@@ -23,10 +32,18 @@ module DateValues
|
|
|
23
32
|
|
|
24
33
|
def succ: () -> YearMonth
|
|
25
34
|
|
|
35
|
+
def advance: (?years: Integer, ?months: Integer) -> YearMonth
|
|
36
|
+
|
|
37
|
+
def change: (?year: Integer, ?month: Integer) -> YearMonth
|
|
38
|
+
|
|
39
|
+
def days: () -> Integer
|
|
40
|
+
|
|
26
41
|
def strftime: (String format) -> String
|
|
27
42
|
|
|
28
43
|
def to_date: () -> Date
|
|
29
44
|
|
|
45
|
+
def as_json: (*untyped) -> String
|
|
46
|
+
|
|
30
47
|
def to_s: () -> String
|
|
31
48
|
|
|
32
49
|
def inspect: () -> String
|
|
@@ -47,10 +64,14 @@ module DateValues
|
|
|
47
64
|
def <=>: (MonthDay other) -> Integer
|
|
48
65
|
| (untyped other) -> nil
|
|
49
66
|
|
|
67
|
+
def change: (?month: Integer, ?day: Integer) -> MonthDay
|
|
68
|
+
|
|
50
69
|
def strftime: (String format) -> String
|
|
51
70
|
|
|
52
71
|
def to_date: (Integer year) -> Date
|
|
53
72
|
|
|
73
|
+
def as_json: (*untyped) -> String
|
|
74
|
+
|
|
54
75
|
def to_s: () -> String
|
|
55
76
|
|
|
56
77
|
def inspect: () -> String
|
|
@@ -71,13 +92,27 @@ module DateValues
|
|
|
71
92
|
|
|
72
93
|
def self.from: (Time time) -> TimeOfDay
|
|
73
94
|
|
|
95
|
+
def self.from_seconds: (Integer total) -> TimeOfDay
|
|
96
|
+
|
|
74
97
|
def self.parse: (String str) -> TimeOfDay
|
|
75
98
|
|
|
76
99
|
def <=>: (TimeOfDay other) -> Integer
|
|
77
100
|
| (untyped other) -> nil
|
|
78
101
|
|
|
102
|
+
def +: (Integer seconds) -> TimeOfDay
|
|
103
|
+
|
|
104
|
+
def -: (Integer seconds) -> TimeOfDay
|
|
105
|
+
|
|
106
|
+
def advance: (?hours: Integer, ?minutes: Integer, ?seconds: Integer) -> TimeOfDay
|
|
107
|
+
|
|
108
|
+
def change: (?hour: Integer, ?minute: Integer, ?second: Integer) -> TimeOfDay
|
|
109
|
+
|
|
110
|
+
def to_seconds: () -> Integer
|
|
111
|
+
|
|
79
112
|
def strftime: (String format) -> String
|
|
80
113
|
|
|
114
|
+
def as_json: (*untyped) -> String
|
|
115
|
+
|
|
81
116
|
def to_s: () -> String
|
|
82
117
|
|
|
83
118
|
def inspect: () -> String
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: date_values
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Keita Urashima
|
|
@@ -20,18 +20,12 @@ files:
|
|
|
20
20
|
- README.md
|
|
21
21
|
- Rakefile
|
|
22
22
|
- lib/date_values.rb
|
|
23
|
+
- lib/date_values/configuration.rb
|
|
23
24
|
- lib/date_values/month_day.rb
|
|
24
|
-
- lib/date_values/rails.rb
|
|
25
|
-
- lib/date_values/rails/date_value_validator.rb
|
|
26
|
-
- lib/date_values/rails/i18n_backend.rb
|
|
27
|
-
- lib/date_values/rails/month_day_type.rb
|
|
28
|
-
- lib/date_values/rails/time_of_day_type.rb
|
|
29
|
-
- lib/date_values/rails/year_month_type.rb
|
|
30
25
|
- lib/date_values/time_of_day.rb
|
|
31
26
|
- lib/date_values/version.rb
|
|
32
27
|
- lib/date_values/year_month.rb
|
|
33
28
|
- sig/date_values.rbs
|
|
34
|
-
- sig/date_values/rails.rbs
|
|
35
29
|
homepage: https://github.com/ursm/date_values
|
|
36
30
|
licenses:
|
|
37
31
|
- MIT
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'active_model/validator'
|
|
4
|
-
|
|
5
|
-
class DateValueValidator < ActiveModel::EachValidator
|
|
6
|
-
def validate_each(record, attribute, value)
|
|
7
|
-
raw = record.read_attribute_before_type_cast(attribute)
|
|
8
|
-
return if raw.blank?
|
|
9
|
-
return unless value.nil?
|
|
10
|
-
|
|
11
|
-
record.errors.add(attribute, :invalid, **options)
|
|
12
|
-
end
|
|
13
|
-
end
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module DateValues
|
|
4
|
-
module Rails
|
|
5
|
-
module I18nBackend
|
|
6
|
-
TYPES = {
|
|
7
|
-
DateValues::YearMonth => :year_month,
|
|
8
|
-
DateValues::MonthDay => :month_day,
|
|
9
|
-
DateValues::TimeOfDay => :time_of_day
|
|
10
|
-
}.freeze
|
|
11
|
-
|
|
12
|
-
def localize(locale, object, format = :default, options = EMPTY_HASH)
|
|
13
|
-
type = TYPES[object.class]
|
|
14
|
-
return super unless type
|
|
15
|
-
|
|
16
|
-
format_key = format.is_a?(Symbol) ? format : nil
|
|
17
|
-
|
|
18
|
-
if format_key
|
|
19
|
-
entry = I18n.t("#{type}.formats.#{format_key}", locale: locale, default: nil)
|
|
20
|
-
raise I18n::MissingTranslationData.new(locale, "#{type}.formats.#{format_key}") unless entry
|
|
21
|
-
|
|
22
|
-
format = entry
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
object.strftime(format)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module DateValues
|
|
4
|
-
module Rails
|
|
5
|
-
class MonthDayType < ActiveModel::Type::Value
|
|
6
|
-
def type
|
|
7
|
-
:month_day
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def cast(value)
|
|
11
|
-
case value
|
|
12
|
-
when MonthDay then value
|
|
13
|
-
when String then MonthDay.parse(value)
|
|
14
|
-
when nil then nil
|
|
15
|
-
end
|
|
16
|
-
rescue ArgumentError
|
|
17
|
-
nil
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def serialize(value)
|
|
21
|
-
value&.to_s
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def deserialize(value)
|
|
25
|
-
return nil if value.nil?
|
|
26
|
-
|
|
27
|
-
MonthDay.parse(value)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module DateValues
|
|
4
|
-
module Rails
|
|
5
|
-
class TimeOfDayType < ActiveModel::Type::Value
|
|
6
|
-
def type
|
|
7
|
-
:time_of_day
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def cast(value)
|
|
11
|
-
case value
|
|
12
|
-
when TimeOfDay then value
|
|
13
|
-
when Time then TimeOfDay.new(value.hour, value.min, value.sec)
|
|
14
|
-
when String then TimeOfDay.parse(value)
|
|
15
|
-
when nil then nil
|
|
16
|
-
end
|
|
17
|
-
rescue ArgumentError
|
|
18
|
-
nil
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def serialize(value)
|
|
22
|
-
value&.to_s
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def deserialize(value)
|
|
26
|
-
case value
|
|
27
|
-
when nil then nil
|
|
28
|
-
when Time then TimeOfDay.new(value.hour, value.min, value.sec)
|
|
29
|
-
when String then TimeOfDay.parse(value)
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module DateValues
|
|
4
|
-
module Rails
|
|
5
|
-
class YearMonthType < ActiveModel::Type::Value
|
|
6
|
-
def type
|
|
7
|
-
:year_month
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def cast(value)
|
|
11
|
-
case value
|
|
12
|
-
when YearMonth then value
|
|
13
|
-
when String then YearMonth.parse(value)
|
|
14
|
-
when nil then nil
|
|
15
|
-
end
|
|
16
|
-
rescue ArgumentError
|
|
17
|
-
nil
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def serialize(value)
|
|
21
|
-
value&.to_s
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def deserialize(value)
|
|
25
|
-
return nil if value.nil?
|
|
26
|
-
|
|
27
|
-
YearMonth.parse(value)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
data/lib/date_values/rails.rb
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'date_values'
|
|
4
|
-
require 'active_support'
|
|
5
|
-
require 'active_model/type'
|
|
6
|
-
require_relative 'rails/year_month_type'
|
|
7
|
-
require_relative 'rails/month_day_type'
|
|
8
|
-
require_relative 'rails/time_of_day_type'
|
|
9
|
-
require_relative 'rails/i18n_backend'
|
|
10
|
-
require_relative 'rails/date_value_validator'
|
|
11
|
-
|
|
12
|
-
ActiveModel::Type.register(:year_month, DateValues::Rails::YearMonthType)
|
|
13
|
-
ActiveModel::Type.register(:month_day, DateValues::Rails::MonthDayType)
|
|
14
|
-
ActiveModel::Type.register(:time_of_day, DateValues::Rails::TimeOfDayType)
|
|
15
|
-
|
|
16
|
-
ActiveSupport.on_load(:active_record) do
|
|
17
|
-
ActiveRecord::Type.register(:year_month, DateValues::Rails::YearMonthType)
|
|
18
|
-
ActiveRecord::Type.register(:month_day, DateValues::Rails::MonthDayType)
|
|
19
|
-
ActiveRecord::Type.register(:time_of_day, DateValues::Rails::TimeOfDayType)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
I18n::Backend::Base.prepend(DateValues::Rails::I18nBackend)
|
data/sig/date_values/rails.rbs
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
module DateValues
|
|
2
|
-
module Rails
|
|
3
|
-
class YearMonthType < ActiveModel::Type::Value
|
|
4
|
-
def type: () -> :year_month
|
|
5
|
-
|
|
6
|
-
def cast: (YearMonth value) -> YearMonth
|
|
7
|
-
| (String value) -> YearMonth
|
|
8
|
-
| (nil value) -> nil
|
|
9
|
-
|
|
10
|
-
def serialize: (YearMonth? value) -> String?
|
|
11
|
-
|
|
12
|
-
def deserialize: (String value) -> YearMonth
|
|
13
|
-
| (nil value) -> nil
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
class MonthDayType < ActiveModel::Type::Value
|
|
17
|
-
def type: () -> :month_day
|
|
18
|
-
|
|
19
|
-
def cast: (MonthDay value) -> MonthDay
|
|
20
|
-
| (String value) -> MonthDay
|
|
21
|
-
| (nil value) -> nil
|
|
22
|
-
|
|
23
|
-
def serialize: (MonthDay? value) -> String?
|
|
24
|
-
|
|
25
|
-
def deserialize: (String value) -> MonthDay
|
|
26
|
-
| (nil value) -> nil
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
class TimeOfDayType < ActiveModel::Type::Value
|
|
30
|
-
def type: () -> :time_of_day
|
|
31
|
-
|
|
32
|
-
def cast: (TimeOfDay value) -> TimeOfDay
|
|
33
|
-
| (Time value) -> TimeOfDay
|
|
34
|
-
| (String value) -> TimeOfDay
|
|
35
|
-
| (nil value) -> nil
|
|
36
|
-
|
|
37
|
-
def serialize: (TimeOfDay? value) -> String?
|
|
38
|
-
|
|
39
|
-
def deserialize: (Time value) -> TimeOfDay
|
|
40
|
-
| (String value) -> TimeOfDay
|
|
41
|
-
| (nil value) -> nil
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|