humane 0.4.0 → 0.6.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: 572a6a4a29165d48eec68db31af6cb222a88de6f989099d353811225898c4673
4
- data.tar.gz: cfc60adb65c39694c156f7b173d053431d469ec26d1bf98a584c83b05bfc5887
3
+ metadata.gz: 9f1d568f746432126417fdd6541c249d81475c663e4e9ae9a772fcbc1dc59e3f
4
+ data.tar.gz: f85e12c6dbd402a4a1d452ba4629e6921418ff8609d6222333b17c1b51ae6a05
5
5
  SHA512:
6
- metadata.gz: baa6946270a2a9bcaa4636993324df9cae89da3ff2a753552bc21bcdc5569a9fe10075dc48e4e2e910a2f50d811ff324bf7e7ce59168c03025cc2f2157626d5e
7
- data.tar.gz: 4dc6ee4899fe1ae32d33c36307a5a01bef49cf34fb368668b28bd584cae6bce38d9a00be13d9aef532020015d40f00375c1c9ad8299380882bdcc29935469f78
6
+ metadata.gz: fa9e176a20d540c3c325c6f1015c2353b2fbced72397d9bcb645eea021129341781543399ac0a94d1cde5b885e64738e7d723d14c2bfb8548c3b7f7d392664cd
7
+ data.tar.gz: e59a7270df069333babc2d10f6234bbb507853b9b2c1583e437e41f1451a1f0dd6bc82cba1f88ec83989bb31a2957132f8d8c38a4bd96a056fb1a1b293ebd39f
data/README.md CHANGED
@@ -22,6 +22,14 @@ time_formatter = Humane::TimeFormatter.new
22
22
  time_formatter.string(at: Time.now - 180, relative_to: Time.now) # "3 minutes ago"
23
23
  ```
24
24
 
25
+ Both methods also accept positional arguments, matching `humane` (Go)'s
26
+ positional-only calling convention:
27
+
28
+ ```ruby
29
+ Humane::SizeFormatter.new.string(225_935) # "226 KB"
30
+ time_formatter.string(Time.now - 180, Time.now) # "3 minutes ago"
31
+ ```
32
+
25
33
  Corresponding functions in Swift will have consistent output.
26
34
 
27
35
  ```swift
@@ -33,6 +41,11 @@ let formatter = RelativeDateTimeFormatter(); formatter.unitsStyle = .full
33
41
  formatter.localizedString(for: time, relativeTo: now) // "3 minutes ago"
34
42
  ```
35
43
 
44
+ If you're writing Swift directly rather than calling Foundation by hand,
45
+ [`humane-swift`](https://github.com/woodie/humane-swift) wraps these same two
46
+ formatters with the identical API shape -- including the
47
+ `includeSeconds`/`approximate` options below.
48
+
36
49
  ## Install
37
50
 
38
51
  ```
@@ -47,16 +60,21 @@ gem "humane"
47
60
 
48
61
  ## Beyond Foundation's defaults
49
62
 
50
- Two options on `Humane::TimeFormatter`, both off by default so it matches
51
- `RelativeDateTimeFormatter` exactly out of the box:
63
+ Foundation is the baseline every default matches exactly, in all three
64
+ languages -- these two options on `Humane::TimeFormatter` are how you layer
65
+ ActionView's wording on top of it, not a replacement for it. Both off by
66
+ default, so `Humane::TimeFormatter.new` and calling `RelativeDateTimeFormatter`
67
+ directly always agree:
52
68
 
53
- - `include_seconds` (default `false`): below a minute, collapses to "less than a
69
+ - `include_seconds` (default `false`): under 30 seconds, collapses to "less than a
54
70
  minute ago"/"in less than a minute" instead of an exact second count. Named after
55
71
  ActionView's `include_seconds`, which defaults the same way.
56
- - `approximate` (default `false`): prefixes "about"/"in about" on buckets of an hour
57
- or larger, the way ActionView's `distance_of_time_in_words` does past that same
58
- boundary -- for a render that can't refresh itself and shouldn't overstate its own
59
- precision.
72
+ - `approximate` (default `false`): prefixes "about"/"in about" on the hour-scale
73
+ buckets (1 hour, and 2..24 hours), the way ActionView's `distance_of_time_in_words`
74
+ does for those same buckets -- for a render that can't refresh itself and shouldn't
75
+ overstate its own precision. Matches ActionView's own table exactly (down to its
76
+ 44:30/89:30 rounding cutoffs), through the "1 day" bucket; week/month/year buckets
77
+ are out of scope. See [issue #1](https://github.com/woodie/humane-ruby/issues/1).
60
78
 
61
79
  ```ruby
62
80
  Humane::TimeFormatter.new(approximate: true).string(at: t - 15 * 3600, relative_to: t)
@@ -5,8 +5,11 @@ module Humane
5
5
  class SizeFormatter
6
6
  UNITS = %w[B KB MB GB TB PB EB].freeze
7
7
 
8
- # Returns from_byte_count as a Finder-style human-readable string.
9
- def string(from_byte_count:)
8
+ # Returns from_byte_count as a Finder-style human-readable string; accepts positional or from_byte_count: keyword argument -- see docs/COMMENTS.md.
9
+ def string(positional_from_byte_count = nil, from_byte_count: nil)
10
+ from_byte_count ||= positional_from_byte_count
11
+ raise ArgumentError, "from_byte_count is required" unless from_byte_count
12
+
10
13
  return "#{from_byte_count} B" if from_byte_count < 1000
11
14
 
12
15
  exponent = [(Math.log(from_byte_count) / Math.log(1000)).to_i, UNITS.size - 1].min
@@ -4,40 +4,54 @@ module Humane
4
4
  # Formats one time relative to another the way Finder-adjacent tools do.
5
5
  class TimeFormatter
6
6
  # include_seconds shows exact seconds under a minute instead of collapsing to "less than a minute ago/in less than a minute". Defaults to false, matching ActionView's include_seconds.
7
- # approximate prefixes "about"/"in about" on buckets of an hour or more, matching ActionView's distance_of_time_in_words past that same boundary. Defaults to false.
7
+ # approximate prefixes "about"/"in about" on the hour-scale buckets (1 hour, and 2..24 hours), matching ActionView's distance_of_time_in_words wording for those buckets. Defaults to false. See docs/COMMENTS.md and humane-ruby issue #1 for the full bucket table this ports.
8
8
  def initialize(include_seconds: false, approximate: false)
9
9
  @include_seconds = include_seconds
10
10
  @approximate = approximate
11
11
  end
12
12
 
13
- # Returns the time at `at` relative to `relative_to` as a human-readable string.
14
- def string(at:, relative_to:)
13
+ # Returns at relative to relative_to; accepts positional or at:/relative_to: keyword arguments -- see docs/COMMENTS.md.
14
+ def string(positional_at = nil, positional_relative_to = nil, at: nil, relative_to: nil)
15
+ at ||= positional_at
16
+ relative_to ||= positional_relative_to
17
+ raise ArgumentError, "at is required" unless at
18
+ raise ArgumentError, "relative_to is required" unless relative_to
19
+
15
20
  seconds = relative_to - at
16
21
  future = seconds.negative?
17
22
  seconds = seconds.abs
18
23
 
19
- if !@include_seconds && seconds < 60
24
+ if !@include_seconds && seconds < 30
20
25
  return future ? "in less than a minute" : "less than a minute ago"
21
26
  end
22
27
 
23
- text =
24
- if seconds < 60
25
- pluralize(seconds.to_i, "second")
26
- elsif seconds < 3600
27
- pluralize((seconds / 60.0).round, "minute")
28
- elsif seconds < 86_400
29
- pluralize((seconds / 3600.0).round, "hour")
30
- else
31
- pluralize((seconds / 86_400.0).round, "day")
32
- end
28
+ if @include_seconds && seconds < 60
29
+ return wrap(pluralize(seconds.to_i, "second"), future: future)
30
+ end
33
31
 
34
- text = "about #{text}" if @approximate && seconds >= 3600
32
+ # Buckets come from distance_in_minutes, not raw seconds re-divided per unit -- see docs/COMMENTS.md.
33
+ distance_in_minutes = (seconds / 60.0).round
34
+
35
+ text, approximable =
36
+ case distance_in_minutes
37
+ when 1 then ["1 minute", false]
38
+ when 2..44 then [pluralize(distance_in_minutes, "minute"), false]
39
+ when 45..89 then ["1 hour", true]
40
+ when 90..1439 then [pluralize((distance_in_minutes / 60.0).round, "hour"), true]
41
+ when 1440..2519 then ["1 day", false]
42
+ else [pluralize((distance_in_minutes / 1440.0).round, "day"), false]
43
+ end
35
44
 
36
- future ? "in #{text}" : "#{text} ago"
45
+ text = "about #{text}" if @approximate && approximable
46
+ wrap(text, future: future)
37
47
  end
38
48
 
39
49
  private
40
50
 
51
+ def wrap(text, future:)
52
+ future ? "in #{text}" : "#{text} ago"
53
+ end
54
+
41
55
  def pluralize(count, unit)
42
56
  count == 1 ? "1 #{unit}" : "#{count} #{unit}s"
43
57
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Humane
4
- VERSION = "0.4.0"
4
+ VERSION = "0.6.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: humane
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Woodell