power-hours 0.1.2 → 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/README.md +21 -1
- data/lib/opening_hours/schedule.rb +16 -12
- data/lib/opening_hours/time_window.rb +6 -0
- data/lib/opening_hours/type.rb +2 -2
- data/lib/opening_hours/version.rb +1 -1
- data/sig/power/hours.rbs +6 -3
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a05edfaea3aaf3667bfc1c6281827524f34cea6c924bb3bbaefe5c1cff53dd3d
|
|
4
|
+
data.tar.gz: c0d73bd22938295b7cdc86134d4e55ce1e52566f500e5e1320d4f5daec6371fc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a93d5ffd74e36026ea4e72702181655751aa6d42aa9319ce922f1809f2be06f756db8dadaaef251983aa4e3cc563b818bf9805ea8e7967e6db4280fe4f003f1c
|
|
7
|
+
data.tar.gz: 3494d1052057bf5ecabb2bb70f0d15ef38c52132c5acb18cf9865af943c3eedcaf6ad2dfb1306db8a93a744bbc6572798c1cae56cb0ad349fe8c79469c6f26de
|
data/README.md
CHANGED
|
@@ -5,6 +5,7 @@ Power Hours provides a small, explicit DSL for weekly opening-hours schedules.
|
|
|
5
5
|
It supports:
|
|
6
6
|
- multiple windows per day
|
|
7
7
|
- overnight windows (for example `22:00-02:00`)
|
|
8
|
+
- stable ordering of windows within each day
|
|
8
9
|
- serialization to/from hashes
|
|
9
10
|
- optional model mixin helpers via `OpeningHours::Model`
|
|
10
11
|
|
|
@@ -39,12 +40,14 @@ schedule.open?(at: Time.new(2024, 1, 6, 1, 0, 0)) # => true (Saturday, from Fri
|
|
|
39
40
|
### Construct directly from hash data
|
|
40
41
|
|
|
41
42
|
```ruby
|
|
42
|
-
schedule = OpeningHours::Schedule.
|
|
43
|
+
schedule = OpeningHours::Schedule.build(
|
|
43
44
|
"mon" => ["09:00-17:00"],
|
|
44
45
|
"fri" => ["22:00-02:00"]
|
|
45
46
|
)
|
|
46
47
|
```
|
|
47
48
|
|
|
49
|
+
If both a hash and a block are passed to `Schedule.build`, the block definition is used.
|
|
50
|
+
|
|
48
51
|
### Serialize for persistence
|
|
49
52
|
|
|
50
53
|
```ruby
|
|
@@ -68,6 +71,23 @@ schedule.as_json
|
|
|
68
71
|
schedule.as_json(include_empty: true) # include empty weekdays too
|
|
69
72
|
```
|
|
70
73
|
|
|
74
|
+
### Simple string output
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
schedule.to_s
|
|
78
|
+
# MON: 09:00-17:00
|
|
79
|
+
# FRI: 22:00-02:00
|
|
80
|
+
|
|
81
|
+
schedule.to_s(true) # include empty weekdays too
|
|
82
|
+
# SUN:
|
|
83
|
+
# MON: 09:00-17:00
|
|
84
|
+
# TUE:
|
|
85
|
+
# WED:
|
|
86
|
+
# THU:
|
|
87
|
+
# FRI: 22:00-02:00
|
|
88
|
+
# SAT:
|
|
89
|
+
```
|
|
90
|
+
|
|
71
91
|
### Optional mixin for app models
|
|
72
92
|
|
|
73
93
|
`OpeningHours::Model` is a Rails integration mixin (uses typed attributes).
|
|
@@ -3,16 +3,14 @@
|
|
|
3
3
|
module OpeningHours
|
|
4
4
|
class Schedule < Data.define(:sun, :mon, :tue, :wed, :thu, :fri, :sat)
|
|
5
5
|
class << self
|
|
6
|
-
def build(&block)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def from_hash(hash)
|
|
15
|
-
new(**hash.to_h.transform_keys(&:to_sym))
|
|
6
|
+
def build(hash = {}, &block)
|
|
7
|
+
if block_given?
|
|
8
|
+
builder = Builder.new
|
|
9
|
+
builder.instance_eval(&block)
|
|
10
|
+
builder.schedule
|
|
11
|
+
else
|
|
12
|
+
new(**hash.to_h.transform_keys(&:to_sym))
|
|
13
|
+
end
|
|
16
14
|
end
|
|
17
15
|
end
|
|
18
16
|
|
|
@@ -30,7 +28,7 @@ module OpeningHours
|
|
|
30
28
|
|
|
31
29
|
def open?(at: Time.now)
|
|
32
30
|
current_time = at.strftime("%H:%M")
|
|
33
|
-
windows_to_check(at).any? {
|
|
31
|
+
windows_to_check(at).any? { it.cover?(current_time) }
|
|
34
32
|
end
|
|
35
33
|
|
|
36
34
|
def to_h
|
|
@@ -42,10 +40,16 @@ module OpeningHours
|
|
|
42
40
|
include_empty ? to_h : to_h.reject { |_day, windows| windows.empty? }
|
|
43
41
|
end
|
|
44
42
|
|
|
43
|
+
def to_s(include_empty = false)
|
|
44
|
+
as_json(include_empty: include_empty).map do |day, windows|
|
|
45
|
+
"#{day.upcase}: #{windows.join(', ')}"
|
|
46
|
+
end.join("\n")
|
|
47
|
+
end
|
|
48
|
+
|
|
45
49
|
private
|
|
46
50
|
|
|
47
51
|
def normalize_day(values)
|
|
48
|
-
Array(values).map { TimeWindow[it] }.freeze
|
|
52
|
+
Array(values).map { TimeWindow[it] }.sort.freeze
|
|
49
53
|
end
|
|
50
54
|
|
|
51
55
|
# Check current day windows + previous day's overnight leftovers
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module OpeningHours
|
|
4
4
|
class TimeWindow < Data.define(:opens, :closes)
|
|
5
|
+
include Comparable
|
|
6
|
+
|
|
5
7
|
def self.[](input)
|
|
6
8
|
case input
|
|
7
9
|
in TimeWindow => tw
|
|
@@ -39,5 +41,9 @@ module OpeningHours
|
|
|
39
41
|
time >= opens && time <= closes
|
|
40
42
|
end
|
|
41
43
|
end
|
|
44
|
+
|
|
45
|
+
def <=>(other)
|
|
46
|
+
opens <=> TimeWindow[other].opens
|
|
47
|
+
end
|
|
42
48
|
end
|
|
43
49
|
end
|
data/lib/opening_hours/type.rb
CHANGED
|
@@ -29,9 +29,9 @@ module OpeningHours
|
|
|
29
29
|
in OpeningHours::Schedule => schedule
|
|
30
30
|
schedule
|
|
31
31
|
in Hash => hash
|
|
32
|
-
OpeningHours::Schedule.
|
|
32
|
+
OpeningHours::Schedule.build(hash)
|
|
33
33
|
in candidate if (hash = hash_from(candidate))
|
|
34
|
-
OpeningHours::Schedule.
|
|
34
|
+
OpeningHours::Schedule.build(hash)
|
|
35
35
|
else
|
|
36
36
|
raise ArgumentError, "Cannot cast #{value.inspect} to OpeningHours::Schedule"
|
|
37
37
|
end
|
data/sig/power/hours.rbs
CHANGED
|
@@ -23,12 +23,15 @@ module OpeningHours
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
class TimeWindow
|
|
26
|
+
include Comparable[time_window_input]
|
|
27
|
+
|
|
26
28
|
attr_reader opens: OpeningHours::TimeOfDay
|
|
27
29
|
attr_reader closes: OpeningHours::TimeOfDay
|
|
28
30
|
|
|
29
31
|
def self.[]: (time_window_input input) -> OpeningHours::TimeWindow
|
|
30
32
|
def initialize: (opens: time_input, closes: time_input) -> void
|
|
31
33
|
|
|
34
|
+
def <=>: (time_window_input other) -> Integer?
|
|
32
35
|
def to_a: () -> [OpeningHours::TimeOfDay, OpeningHours::TimeOfDay]
|
|
33
36
|
def to_s: () -> String
|
|
34
37
|
def overnight?: () -> bool
|
|
@@ -44,9 +47,8 @@ module OpeningHours
|
|
|
44
47
|
attr_reader fri: Array[OpeningHours::TimeWindow]
|
|
45
48
|
attr_reader sat: Array[OpeningHours::TimeWindow]
|
|
46
49
|
|
|
47
|
-
def self.build: () -> OpeningHours::Schedule
|
|
48
|
-
| () { () -> untyped } -> OpeningHours::Schedule
|
|
49
|
-
def self.from_hash: (Hash[Symbol | String, untyped] hash) -> OpeningHours::Schedule
|
|
50
|
+
def self.build: (?Hash[Symbol | String, untyped] hash) -> OpeningHours::Schedule
|
|
51
|
+
| (?Hash[Symbol | String, untyped] hash) { () -> untyped } -> OpeningHours::Schedule
|
|
50
52
|
def initialize: (
|
|
51
53
|
?mon: day_windows_input,
|
|
52
54
|
?tue: day_windows_input,
|
|
@@ -60,6 +62,7 @@ module OpeningHours
|
|
|
60
62
|
def open?: (?at: untyped) -> bool
|
|
61
63
|
def to_h: () -> serialized_schedule
|
|
62
64
|
def as_json: (?Hash[Symbol, untyped] options) -> serialized_schedule
|
|
65
|
+
def to_s: (?bool include_empty) -> String
|
|
63
66
|
end
|
|
64
67
|
|
|
65
68
|
class Builder
|