date_values 0.1.4 → 0.2.0
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 +4 -0
- data/README.md +3 -89
- data/lib/date_values/month_day.rb +5 -4
- data/lib/date_values/version.rb +1 -1
- data/lib/date_values/year_month.rb +4 -4
- metadata +1 -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: f294856b7f5149ded52770b4d43d003b5cac6e8ee533f81ec003ac04caf9988b
|
|
4
|
+
data.tar.gz: 8c1f6046926d15cda1a01e55f89e2975b92e6cf515bbc21cb0b6892d816bf93b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 05e23c3cd91ddda9dcdf5ae63e81dd89c7a6bfd5150dab2893231ebe63c12bc64023cae0667c296e4e88db2dd5869ebdab39245370291cb29cbb9b2d906ad3e3
|
|
7
|
+
data.tar.gz: 28e82e5dc12f33c6e8af8b546338636ef2f6cda3273a2957bf7f86dcb4bb4a6074a4e2376c1c0ed0600f19e98b2783661710c8882b066eb05d3d9c1aa359aea8
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.0] - 2026-03-20
|
|
4
|
+
|
|
5
|
+
- **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.
|
|
6
|
+
|
|
3
7
|
## [0.1.4] - 2026-03-20
|
|
4
8
|
|
|
5
9
|
- Cast returns `nil` for invalid input instead of raising, following Rails convention
|
data/README.md
CHANGED
|
@@ -26,6 +26,7 @@ ym.to_date # => #<Date: 2026-03-01>
|
|
|
26
26
|
|
|
27
27
|
YearMonth.from(Date.today) # => #<DateValues::YearMonth 2026-03>
|
|
28
28
|
YearMonth.parse('2026-03') # => #<DateValues::YearMonth 2026-03>
|
|
29
|
+
YearMonth.parse('2026/3') # also works
|
|
29
30
|
|
|
30
31
|
ym + 1 # => #<DateValues::YearMonth 2026-04>
|
|
31
32
|
ym - 1 # => #<DateValues::YearMonth 2026-02>
|
|
@@ -47,6 +48,7 @@ md.to_date(2026) # => #<Date: 2026-03-19>
|
|
|
47
48
|
|
|
48
49
|
MonthDay.from(Date.today) # => #<DateValues::MonthDay --03-20>
|
|
49
50
|
MonthDay.parse('--03-19') # => #<DateValues::MonthDay --03-19>
|
|
51
|
+
MonthDay.parse('3/19') # also works
|
|
50
52
|
|
|
51
53
|
# Range membership
|
|
52
54
|
summer = MonthDay.new(6, 1)..MonthDay.new(8, 31)
|
|
@@ -92,95 +94,7 @@ end
|
|
|
92
94
|
|
|
93
95
|
## Rails Integration
|
|
94
96
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
```ruby
|
|
98
|
-
require 'date_values/rails'
|
|
99
|
-
|
|
100
|
-
class Shop < ApplicationRecord
|
|
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
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
Values are automatically serialized in queries:
|
|
108
|
-
|
|
109
|
-
```ruby
|
|
110
|
-
Shop.where(billing_month: YearMonth.new(2026, 3))
|
|
111
|
-
# SELECT * FROM shops WHERE billing_month = '2026-03'
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### Validation
|
|
115
|
-
|
|
116
|
-
All classes are `Comparable` and value-equal, so standard Rails validators work as-is:
|
|
117
|
-
|
|
118
|
-
```ruby
|
|
119
|
-
class Contract < ApplicationRecord
|
|
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:
|
|
132
|
-
|
|
133
|
-
```ruby
|
|
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"]
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
### I18n / `l` Helper
|
|
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
|
-
```
|
|
178
|
-
|
|
179
|
-
```ruby
|
|
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
|
-
```
|
|
97
|
+
See [date_values-rails](https://github.com/ursm/date_values-rails) for ActiveModel/ActiveRecord type casting, validation, and I18n support.
|
|
184
98
|
|
|
185
99
|
## License
|
|
186
100
|
|
|
@@ -18,10 +18,11 @@ module DateValues
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def self.parse(str)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
case str
|
|
22
|
+
when /\A--(\d{1,2})-(\d{1,2})\z/ then new($1.to_i, $2.to_i)
|
|
23
|
+
when /\A(\d{1,2})[\/\-](\d{1,2})\z/ then new($1.to_i, $2.to_i)
|
|
24
|
+
else raise ArgumentError, "invalid MonthDay: #{str}"
|
|
25
|
+
end
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
def <=>(other)
|
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)
|
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.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Keita Urashima
|
|
@@ -21,17 +21,10 @@ files:
|
|
|
21
21
|
- Rakefile
|
|
22
22
|
- lib/date_values.rb
|
|
23
23
|
- 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
24
|
- lib/date_values/time_of_day.rb
|
|
31
25
|
- lib/date_values/version.rb
|
|
32
26
|
- lib/date_values/year_month.rb
|
|
33
27
|
- sig/date_values.rbs
|
|
34
|
-
- sig/date_values/rails.rbs
|
|
35
28
|
homepage: https://github.com/ursm/date_values
|
|
36
29
|
licenses:
|
|
37
30
|
- 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
|