duration_in_words 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 61890a8cefb328cd0317b59e381a4482cec0426059036bb2b24f40a73dccb11e
4
- data.tar.gz: ecf684b6fca14b39e7a68126f19f85e9341f1a33ffa0ec886878223b145ae864
3
+ metadata.gz: 2e0b74fb2b1428b25bd58dd451ceb93073f58fa803c8f135a3d0661983dd07ab
4
+ data.tar.gz: 6439cb0650d16395fa3ec21ce206e6c7e03fce4dacc9829c27e6bae3a6cc2782
5
5
  SHA512:
6
- metadata.gz: da2221acad3383d7be4adf48dcbe7bf6d5fda42d120c1ce649c40cf298fc21fcc4b6275d0599706689d831151c9ef53b5aba6c0484ca2553a206422ef1dd3898
7
- data.tar.gz: c609b68e4ba85d0c68327b346a7a2d781ac6b3beae6d4031412e85d414074cf7e0f8435718a4dcf64e089e35da7ed20bc5626143638092ead316e41417b23e12
6
+ metadata.gz: ae86b0118a46b1a254d26d7bac7ce5376caaf30df65e76409326ccb8d771c0f511e1b113b126fe46ec424f25c1ca29519107c7d96cbd9384b0878eb7fbb1e9a0
7
+ data.tar.gz: a453cdea084d270100a7c0f0cfa984f07b55c428787365e4c2a36ab733928ef1914d2eb663cd0a1ef149c7a1b759a2416ff595c38e044bcdd2dd74bd1ea798f1
data/.rubocop.yml CHANGED
@@ -1,7 +1,17 @@
1
- require: rubocop-rspec
1
+ require:
2
+ - rubocop-rspec
3
+
4
+ plugins:
5
+ - rubocop-rake
2
6
 
3
7
  AllCops:
4
8
  TargetRubyVersion: 2.6
9
+ NewCops: enable
10
+
11
+ # This gem keeps development dependencies in the gemspec, which matches the
12
+ # standard Bundler gem template used to generate it.
13
+ Gemspec/DevelopmentDependencies:
14
+ Enabled: false
5
15
 
6
16
  # Commonly used screens these days easily fit more than 80 characters.
7
17
  Layout/LineLength:
@@ -25,7 +35,7 @@ Layout/SpaceAroundEqualsInParameterDefault:
25
35
  # Enforcing double quotes reduces the times where you need to change them
26
36
  # when introducing an interpolation. Use single quotes only if their semantics
27
37
  # are needed.
28
- Layout/StringLiterals:
38
+ Style/StringLiterals:
29
39
  EnforcedStyle: double_quotes
30
40
 
31
41
  Style/StringLiteralsInInterpolation:
@@ -36,10 +46,10 @@ Style/StringLiteralsInInterpolation:
36
46
  Style/SymbolArray:
37
47
  Enabled: true
38
48
 
39
- # Most readable form.
49
+ # The old style configuration for this cop has been removed in recent RuboCop
50
+ # versions, so keep it disabled instead of carrying invalid options.
40
51
  Style/OptionHash:
41
- EnforcedHashRocketStyle: table
42
- EnforcedColonStyle: table
52
+ Enabled: false
43
53
 
44
54
  # Mixing the styles looks just silly.
45
55
  Style/HashSyntax:
@@ -73,7 +83,7 @@ Style/RaiseArgs:
73
83
 
74
84
  # Indenting the chained dots beneath each other is not supported by this cop,
75
85
  # see https://github.com/bbatsov/rubocop/issues/1633
76
- Style/MultilineOperationIndentation:
86
+ Layout/MultilineOperationIndentation:
77
87
  Enabled: false
78
88
 
79
89
  # Fail is an alias of raise. Avoid aliases, it's more cognitive load for no gain.
@@ -134,3 +144,8 @@ Style/MethodDefParentheses:
134
144
 
135
145
  RSpec/MultipleExpectations:
136
146
  Max: 2
147
+
148
+ # This gem does not use Capybara, and the bundled cop currently crashes when
149
+ # inspecting these plain RSpec examples under the pinned plugin set.
150
+ Capybara/RSpec/PredicateMatcher:
151
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.1] - 2026-03-28
4
+
5
+ ### Fixed
6
+
7
+ - Respect the current `I18n.locale` by default when formatting durations.
8
+ - Delegate pluralization to I18n so locales with plural forms beyond `one` and `other` are handled correctly.
9
+
10
+ ### Changed
11
+
12
+ - Modernized the RuboCop configuration for the current toolchain and added RubyGems MFA metadata.
13
+
14
+ ## [0.3.0] - 2025-08-20
15
+
16
+ ### Added
17
+
18
+ - Support for `weeks` as a duration unit by adding the necessary translations to the `en` and `de` locale files.
19
+
20
+ ## [0.2.0] - 2023-02-04
21
+
22
+ ### Added
23
+
24
+ - Support for `:full` format for more descriptive output (e.g., "1 day, 2 hours, and 30 minutes").
25
+ - Internationalization (i18n) support using the `:locale` option.
26
+ - Handling of fractional duration values (e.g., `2.5.minutes`).
27
+
3
28
  ## [0.1.0] - 2023-02-04
4
29
 
5
30
  - Initial release
data/README.md CHANGED
@@ -1,22 +1,142 @@
1
- # DurationInWords
1
+ # DurationInWords ![Ruby](https://github.com/aslam/duration_in_words/actions/workflows/main.yml/badge.svg)
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/duration_in_words`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ `duration_in_words` provides a view helper to convert [ActiveSupport::Duration](https://api.rubyonrails.org/classes/ActiveSupport/Duration.html) objects into concise string like `1h 20m and 30s`, with locale support.
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ ## Why?
6
6
 
7
- ## Installation
7
+ Currently, there is no direct way to format `ActiveSupport::Duration` objects in `Rails`. You'd have to resort to using the `#inspect`, which provides limited configuration options. Also, the locale support in the `#inspect` was removed around `Rails 5.1`.
8
+
9
+ You could do something like the following to format an `ActiveSupport::Duration` object by hand in a view helper:
10
+
11
+ ```ruby
12
+ def duration_as_sentence(duration)
13
+ parts = duration.parts
14
+ units = [:days, :hours, :minutes]
15
+ map = {
16
+ :days => { :one => :d },
17
+ :hours => { :one => :h, :other => :hrs },
18
+ :minutes => { :one => :m, :other => :mins }
19
+ }
8
20
 
9
- Install the gem and add to the application's Gemfile by executing:
21
+ parts.
22
+ sort_by { |unit, _| units.index(unit) }.
23
+ map { |unit, val| "#{val} #{val == 1 ? map[unit][:one].to_s : map[unit][:other].to_s}" }.
24
+ to_sentence
25
+ end
26
+ ```
27
+
28
+ This gem does something similar along with providing a flexible way of defining formats in locale files and giving you an option to switch between `:compact`, and `:full` formats.
29
+
30
+ ## Installation
10
31
 
11
- $ bundle add duration_in_words
32
+ Add to your `Gemfile`:
12
33
 
13
- If bundler is not being used to manage dependencies, install the gem by executing:
34
+ ```ruby
35
+ gem "duration_in_words"
36
+ ```
14
37
 
15
- $ gem install duration_in_words
38
+ Install the gem by running `bundle install`.
16
39
 
17
40
  ## Usage
18
41
 
19
- TODO: Write usage instructions here
42
+ ```ruby
43
+ include ActionView::Helpers::DurationHelper
44
+
45
+ >> duration = 2.hours
46
+ >> duration_in_words(duration)
47
+ => "2h"
48
+ >> duration = 1.day + 2.hours + 30.minutes
49
+ >> duration_in_words(duration)
50
+ => "1d 2h and 30m"
51
+ >> duration = 2.5.minutes
52
+ >> duration_in_words(duration)
53
+ => "2.5m"
54
+ >> duration = 3.weeks
55
+ >> duration_in_words(duration)
56
+ => "3wks."
57
+ >> duration_in_words(duration)
58
+ => "2.5m"
59
+ ```
60
+
61
+ ### Using <tt>:format</tt> option:
62
+
63
+ There are two formats available, `:compact`, and `:full`. `:compact` being the default.
64
+
65
+ ```ruby
66
+ >> duration = 1.day + 2.hours + 30.minutes
67
+ >> duration_in_words(duration, format: :full)
68
+ => "1 day, 2 hours, and 30 minutes"
69
+ ```
70
+
71
+ ### Using <tt>:locale</tt> option:
72
+
73
+ If you do not pass `:locale`, the helper uses the current `I18n.locale`.
74
+
75
+ Given this locale dictionary:
76
+
77
+ ```yaml
78
+ de:
79
+ duration:
80
+ in_words:
81
+ format:
82
+ compact:
83
+ years:
84
+ one: '%{count}J'
85
+ other: '%{count}J'
86
+ months:
87
+ one: '%{count}M'
88
+ other: '%{count}M'
89
+ days:
90
+ one: '%{count}T'
91
+ other: '%{count}T'
92
+ hours:
93
+ one: '%{count}Std.'
94
+ other: '%{count}Std.'
95
+ minutes:
96
+ one: '%{count}Min'
97
+ other: '%{count}Min'
98
+ seconds:
99
+ one: '%{count}s'
100
+ other: '%{count}s'
101
+ support:
102
+ words_connector: ' '
103
+ two_words_connector: ' und '
104
+ last_word_connector: ' und '
105
+ full:
106
+ years:
107
+ one: '%{count} Jahr'
108
+ other: '%{count} Jahre'
109
+ months:
110
+ one: '%{count} Monat'
111
+ other: '%{count} Monate'
112
+ days:
113
+ one: '%{count} Tag'
114
+ other: '%{count} Tage'
115
+ hours:
116
+ one: '%{count} Stunde'
117
+ other: '%{count} Stunden'
118
+ minutes:
119
+ one: '%{count} Minute'
120
+ other: '%{count} Minuten'
121
+ seconds:
122
+ one: '%{count} Sekunde'
123
+ other: '%{count} Sekunden'
124
+ support:
125
+ words_connector: ', '
126
+ two_words_connector: ' und '
127
+ last_word_connector: ', und '
128
+ ```
129
+
130
+ ```ruby
131
+ >> duration = 1.day + 2.hours + 30.minutes
132
+ >> duration_in_words(duration, locale: :de)
133
+ => "1T 2Std. und 30Min"
134
+ >> duration = 3.weeks
135
+ >> duration_in_words(duration, locale: :de)
136
+ => "3W"
137
+ >> duration_in_words(duration, format: :full, locale: :de)
138
+ >> "1 Tag 2 Stunden und 30 Minuten"
139
+ ```
20
140
 
21
141
  ## Development
22
142
 
@@ -26,7 +146,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
26
146
 
27
147
  ## Contributing
28
148
 
29
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/duration_in_words.
149
+ Bug reports and pull requests are welcome on GitHub at https://github.com/aslam/duration_in_words.
30
150
 
31
151
  ## License
32
152
 
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/duration_in_words/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "duration_in_words"
7
+ spec.version = DurationInWords::VERSION
8
+ spec.authors = ["Syed Aslam"]
9
+ spec.email = ["aslam.maqsood@gmail.com"]
10
+
11
+ spec.summary = "Report ActiveSupport::Duration in concise human readable formats."
12
+ spec.description = "Convert ActiveSupport::Duration objects to concise human readable formats like '2h 30m 45s'
13
+ with locale support."
14
+ spec.homepage = "https://github.com/aslam/duration_in_words"
15
+ spec.license = "MIT"
16
+ spec.required_ruby_version = ">= 2.6.0"
17
+
18
+ spec.metadata["homepage_uri"] = spec.homepage
19
+ spec.metadata["source_code_uri"] = spec.homepage
20
+ spec.metadata["changelog_uri"] = "https://github.com/aslam/duration_in_words/blob/main/CHANGELOG.md"
21
+ spec.metadata["rubygems_mfa_required"] = "true"
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(__dir__) do
26
+ `git ls-files -z`.split("\x0").reject do |f|
27
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
28
+ end
29
+ end
30
+ spec.bindir = "exe"
31
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ["lib"]
33
+
34
+ spec.add_dependency "activesupport", ">= 6.0"
35
+ spec.add_dependency "i18n", "~> 1.12"
36
+
37
+ spec.add_development_dependency "racc"
38
+ spec.add_development_dependency "rake", "~> 13.0"
39
+ spec.add_development_dependency "rspec", "~> 3.0"
40
+ spec.add_development_dependency "rubocop", "~> 1.21"
41
+ spec.add_development_dependency "rubocop-rake"
42
+ spec.add_development_dependency "rubocop-rspec", "~> 2.18.1"
43
+ end
@@ -9,8 +9,8 @@ module ActionView
9
9
  #
10
10
  # === Options
11
11
  # * <tt>:format</tt> - The format to be used in reporting the duration, :full or :compact (default: :compact)
12
- # * <tt>:locale</tt> - If +I18n+ is available, you can set a locale and use the connector options defined on
13
- # the 'support.array' namespace in the corresponding dictionary file.
12
+ # * <tt>:locale</tt> - Defaults to the current +I18n.locale+. You can also pass a locale explicitly and use the
13
+ # connector options defined on the 'support.array' namespace in the corresponding dictionary file.
14
14
  #
15
15
  # === Examples
16
16
  # d = 1.day + 2.hours + 30.minutes
@@ -9,6 +9,9 @@ de:
9
9
  months:
10
10
  one: '%{count}M'
11
11
  other: '%{count}M'
12
+ weeks:
13
+ one: '%{count}W'
14
+ other: '%{count}W'
12
15
  days:
13
16
  one: '%{count}T'
14
17
  other: '%{count}T'
@@ -32,6 +35,9 @@ de:
32
35
  months:
33
36
  one: '%{count} Monat'
34
37
  other: '%{count} Monate'
38
+ weeks:
39
+ one: '%{count} Woche'
40
+ other: '%{count} Wochen'
35
41
  days:
36
42
  one: '%{count} Tag'
37
43
  other: '%{count} Tage'
@@ -9,6 +9,9 @@ en:
9
9
  months:
10
10
  one: '%{count}mo.'
11
11
  other: '%{count}mos.'
12
+ weeks:
13
+ one: '%{count}wk.'
14
+ other: '%{count}wks.'
12
15
  days:
13
16
  one: '%{count}d'
14
17
  other: '%{count}d'
@@ -32,6 +35,9 @@ en:
32
35
  months:
33
36
  one: '%{count} month'
34
37
  other: '%{count} months'
38
+ weeks:
39
+ one: '%{count} week'
40
+ other: '%{count} weeks'
35
41
  days:
36
42
  one: '%{count} day'
37
43
  other: '%{count} days'
@@ -10,10 +10,7 @@ module DurationInWords
10
10
  locale, scope = parse_options(options)
11
11
  parts = duration.parts
12
12
 
13
- if parts.empty?
14
- key = "seconds.#{duration.value == 1 ? 'one' : 'other'}"
15
- return I18n.t(key, count: duration.value, scope: scope, locale: locale)
16
- end
13
+ return I18n.t(:seconds, count: duration.value, scope: scope, locale: locale) if parts.empty?
17
14
 
18
15
  sentencify(parts, scope, locale)
19
16
  end
@@ -22,7 +19,7 @@ module DurationInWords
22
19
 
23
20
  def parse_options(options)
24
21
  format = options.fetch(:format, :compact)
25
- locale = options.fetch(:locale, :en)
22
+ locale = options.fetch(:locale, I18n.locale)
26
23
 
27
24
  scope = format.to_s == "full" ? I18N_SCOPE_FULL : DEFAULT_I18N_SCOPE
28
25
 
@@ -32,12 +29,7 @@ module DurationInWords
32
29
  def sentencify(parts, scope, locale)
33
30
  parts
34
31
  .sort_by { |unit, _| ActiveSupport::Duration::PARTS.index(unit) }
35
- .map { |unit, val|
36
- case val
37
- when 1 then I18n.t("#{unit}.one", count: val, scope: scope, locale: locale)
38
- else I18n.t("#{unit}.other", count: val, scope: scope, locale: locale)
39
- end
40
- }
32
+ .map { |unit, val| I18n.t(unit, count: val, scope: scope, locale: locale) }
41
33
  .to_sentence(sentence_options(scope, locale))
42
34
  end
43
35
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DurationInWords
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.1"
5
5
  end
@@ -13,8 +13,8 @@ module DurationInWords
13
13
  autoload :Methods, "duration_in_words/methods"
14
14
  end
15
15
 
16
- I18N_SCOPE_FULL = :'duration.in_words.format.full'
17
- DEFAULT_I18N_SCOPE = :'duration.in_words.format.compact'
16
+ I18N_SCOPE_FULL = :"duration.in_words.format.full"
17
+ DEFAULT_I18N_SCOPE = :"duration.in_words.format.compact"
18
18
 
19
19
  module_function
20
20
 
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: duration_in_words
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Syed Aslam
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2023-02-09 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -16,28 +15,42 @@ dependencies:
16
15
  requirements:
17
16
  - - ">="
18
17
  - !ruby/object:Gem::Version
19
- version: '0'
18
+ version: '6.0'
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
- version: '0'
25
+ version: '6.0'
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: i18n
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - "~>"
32
31
  - !ruby/object:Gem::Version
33
- version: 1.12.0
32
+ version: '1.12'
34
33
  type: :runtime
35
34
  prerelease: false
36
35
  version_requirements: !ruby/object:Gem::Requirement
37
36
  requirements:
38
37
  - - "~>"
39
38
  - !ruby/object:Gem::Version
40
- version: 1.12.0
39
+ version: '1.12'
40
+ - !ruby/object:Gem::Dependency
41
+ name: racc
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
41
54
  - !ruby/object:Gem::Dependency
42
55
  name: rake
43
56
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +93,20 @@ dependencies:
80
93
  - - "~>"
81
94
  - !ruby/object:Gem::Version
82
95
  version: '1.21'
96
+ - !ruby/object:Gem::Dependency
97
+ name: rubocop-rake
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
83
110
  - !ruby/object:Gem::Dependency
84
111
  name: rubocop-rspec
85
112
  requirement: !ruby/object:Gem::Requirement
@@ -110,6 +137,7 @@ files:
110
137
  - LICENSE.txt
111
138
  - README.md
112
139
  - Rakefile
140
+ - duration_in_words.gemspec
113
141
  - lib/duration_in_words.rb
114
142
  - lib/duration_in_words/action_view/helpers/duration_helper.rb
115
143
  - lib/duration_in_words/locales/de.yml
@@ -124,7 +152,7 @@ metadata:
124
152
  homepage_uri: https://github.com/aslam/duration_in_words
125
153
  source_code_uri: https://github.com/aslam/duration_in_words
126
154
  changelog_uri: https://github.com/aslam/duration_in_words/blob/main/CHANGELOG.md
127
- post_install_message:
155
+ rubygems_mfa_required: 'true'
128
156
  rdoc_options: []
129
157
  require_paths:
130
158
  - lib
@@ -139,8 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
167
  - !ruby/object:Gem::Version
140
168
  version: '0'
141
169
  requirements: []
142
- rubygems_version: 3.3.7
143
- signing_key:
170
+ rubygems_version: 3.7.2
144
171
  specification_version: 4
145
172
  summary: Report ActiveSupport::Duration in concise human readable formats.
146
173
  test_files: []