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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce07496dcd6c713558c0f2aff72ba0d84f7ed5ccd56dedd561d18a055c968660
4
- data.tar.gz: ac50321b8fc3644d8061d81364bc6eafafd31206f0eeee0111310b3e91e55e64
3
+ metadata.gz: a05edfaea3aaf3667bfc1c6281827524f34cea6c924bb3bbaefe5c1cff53dd3d
4
+ data.tar.gz: c0d73bd22938295b7cdc86134d4e55ce1e52566f500e5e1320d4f5daec6371fc
5
5
  SHA512:
6
- metadata.gz: fd0e0588cd49d93043077f1cb195c6a63e575623a58b998c50a577f859614dbe2bf1f3fe689164f6e326182dcbdb1d5fc3dca9bf50d9e4434ac4815c876d72d7
7
- data.tar.gz: 850ebcaa28862ad8e05d98cb860b9642d0dd6b6fe675a18da8b18d2fd1b1c2cd1f872de72896a27ce51cd0c3ab364ba221cbf706aaea49d8f16bcf7f0faa5989
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.from_hash(
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
- return new unless block_given?
8
-
9
- builder = Builder.new
10
- builder.instance_eval(&block)
11
- builder.schedule
12
- end
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? { |window| window.cover?(current_time) }
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
@@ -29,9 +29,9 @@ module OpeningHours
29
29
  in OpeningHours::Schedule => schedule
30
30
  schedule
31
31
  in Hash => hash
32
- OpeningHours::Schedule.from_hash(hash)
32
+ OpeningHours::Schedule.build(hash)
33
33
  in candidate if (hash = hash_from(candidate))
34
- OpeningHours::Schedule.from_hash(hash)
34
+ OpeningHours::Schedule.build(hash)
35
35
  else
36
36
  raise ArgumentError, "Cannot cast #{value.inspect} to OpeningHours::Schedule"
37
37
  end
@@ -1,3 +1,3 @@
1
1
  module OpeningHours
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: power-hours
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Melchert