dotiw 3.1.1 → 5.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.
Files changed (65) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ruby.yml +45 -0
  3. data/.gitignore +2 -1
  4. data/.rspec +2 -0
  5. data/Appraisals +19 -0
  6. data/CHANGELOG.md +31 -6
  7. data/Gemfile +2 -0
  8. data/MIT-LICENSE +1 -1
  9. data/README.markdown +147 -62
  10. data/Rakefile +3 -1
  11. data/dotiw.gemspec +17 -17
  12. data/gemfiles/.bundle/config +2 -0
  13. data/gemfiles/rails_4.gemfile +9 -0
  14. data/gemfiles/rails_5.0.gemfile +9 -0
  15. data/gemfiles/rails_5.1.gemfile +9 -0
  16. data/gemfiles/rails_5.2.gemfile +9 -0
  17. data/gemfiles/rails_6.0.gemfile +9 -0
  18. data/lib/dotiw.rb +32 -13
  19. data/lib/dotiw/action_view/helpers/date_helper.rb +25 -0
  20. data/lib/dotiw/locale/ar.yml +104 -0
  21. data/lib/dotiw/locale/da.yml +35 -0
  22. data/lib/dotiw/locale/de.yml +36 -1
  23. data/lib/dotiw/locale/en.yml +35 -0
  24. data/lib/dotiw/locale/es.yml +36 -1
  25. data/lib/dotiw/locale/fr.yml +35 -0
  26. data/lib/dotiw/locale/id.yml +35 -0
  27. data/lib/dotiw/locale/it.yml +35 -0
  28. data/lib/dotiw/locale/ja.yml +35 -0
  29. data/lib/dotiw/locale/ko.yml +48 -0
  30. data/lib/dotiw/locale/nb.yml +36 -1
  31. data/lib/dotiw/locale/nl.yml +35 -0
  32. data/lib/dotiw/locale/pl.yml +42 -0
  33. data/lib/dotiw/locale/pt-BR.yml +35 -0
  34. data/lib/dotiw/locale/ru.yml +37 -0
  35. data/lib/dotiw/locale/sv.yml +48 -0
  36. data/lib/dotiw/locale/vi.yml +48 -0
  37. data/lib/dotiw/locale/zh-CN.yml +48 -0
  38. data/lib/dotiw/locale/zh-TW.yml +60 -0
  39. data/lib/dotiw/methods.rb +99 -0
  40. data/lib/dotiw/time_hash.rb +39 -33
  41. data/lib/dotiw/version.rb +2 -2
  42. data/spec/lib/dotiw_spec.rb +256 -157
  43. data/spec/lib/i18n/ar.yml +2 -0
  44. data/spec/lib/i18n/da.yml +2 -0
  45. data/spec/lib/i18n/de.yml +2 -0
  46. data/spec/lib/i18n/en.yml +2 -0
  47. data/spec/lib/i18n/es.yml +3 -0
  48. data/spec/lib/i18n/fr.yml +2 -0
  49. data/spec/lib/i18n/id.yml +2 -0
  50. data/spec/lib/i18n/it.yml +3 -0
  51. data/spec/lib/i18n/ja.yml +2 -0
  52. data/spec/lib/i18n/ko.yml +2 -0
  53. data/spec/lib/i18n/nb.yml +2 -0
  54. data/spec/lib/i18n/nl.yml +2 -0
  55. data/spec/lib/i18n/pl.yml +2 -0
  56. data/spec/lib/i18n/pt-BR.yml +2 -0
  57. data/spec/lib/i18n/ru.yml +5 -0
  58. data/spec/lib/i18n/sv.yml +2 -0
  59. data/spec/lib/i18n/vi.yml +22 -0
  60. data/spec/lib/i18n/zh-CN.yml +2 -0
  61. data/spec/lib/i18n/zh-TW.yml +2 -0
  62. data/spec/spec_helper.rb +2 -9
  63. metadata +98 -18
  64. data/.travis.yml +0 -11
  65. data/lib/dotiw/action_view_ext/helpers/date_helper.rb +0 -103
@@ -0,0 +1,48 @@
1
+ vi:
2
+ datetime:
3
+ dotiw:
4
+ seconds:
5
+ one: 1 giây
6
+ other: "%{count} giây"
7
+ minutes:
8
+ one: 1 phút
9
+ other: "%{count} phút"
10
+ hours:
11
+ one: 1 giờ
12
+ other: "%{count} giờ"
13
+ days:
14
+ one: 1 ngày
15
+ other: "%{count} ngày"
16
+ weeks:
17
+ one: 1 tuần
18
+ other: "%{count} tuần"
19
+ months:
20
+ one: 1 tháng
21
+ other: "%{count} tháng"
22
+ years:
23
+ one: 1 năm
24
+ other: "%{count} năm"
25
+ less_than_x: "ít hơn %{distance}"
26
+ dotiw_compact:
27
+ seconds:
28
+ one: 1 giây
29
+ other: "%{count}giây"
30
+ minutes:
31
+ one: 1 phút
32
+ other: "%{count}phút"
33
+ hours:
34
+ one: 1 giờ
35
+ other: "%{count}giờ"
36
+ days:
37
+ one: 1 ngày
38
+ other: "%{count}ngày"
39
+ weeks:
40
+ one: 1 tuần
41
+ other: "%{count}tuần"
42
+ months:
43
+ one: 1 tháng
44
+ other: "%{count}tháng"
45
+ years:
46
+ one: 1 năm
47
+ other: "%{count}năm"
48
+ less_than_x: "< %{distance}"
@@ -0,0 +1,48 @@
1
+ zh-CN:
2
+ datetime:
3
+ dotiw:
4
+ seconds:
5
+ one: 1 秒
6
+ other: "%{count} 秒"
7
+ minutes:
8
+ one: 1 分钟
9
+ other: "%{count} 分钟"
10
+ hours:
11
+ one: 1 小时
12
+ other: "%{count} 小时"
13
+ days:
14
+ one: 1 天
15
+ other: "%{count} 天"
16
+ weeks:
17
+ one: 1 周
18
+ other: "%{count} 周"
19
+ months:
20
+ one: 1 月
21
+ other: "%{count} 月"
22
+ years:
23
+ one: 1 年
24
+ other: "%{count} 年"
25
+ less_than_x: "小于 %{distance}"
26
+ dotiw_compact:
27
+ seconds:
28
+ one: 1秒
29
+ other: "%{count}秒"
30
+ minutes:
31
+ one: 1分钟
32
+ other: "%{count}分钟"
33
+ hours:
34
+ one: 1小时
35
+ other: "%{count}小时"
36
+ days:
37
+ one: 1天
38
+ other: "%{count}天"
39
+ weeks:
40
+ one: 1周
41
+ other: "%{count}周"
42
+ months:
43
+ one: 1月
44
+ other: "%{count}月"
45
+ years:
46
+ one: 1年
47
+ other: "%{count}年"
48
+ less_than_x: "小于 %{distance}"
@@ -0,0 +1,60 @@
1
+ zh-TW:
2
+ datetime:
3
+ dotiw:
4
+ seconds:
5
+ one: 1 秒
6
+ other: "%{count} 秒"
7
+ minutes:
8
+ one: 1 分鐘
9
+ other: "%{count} 分鐘"
10
+ hours:
11
+ one: 1 小時
12
+ other: "%{count} 小時"
13
+ days:
14
+ one: 1 天
15
+ other: "%{count} 天"
16
+ weeks:
17
+ one: 1 週
18
+ other: "%{count} 週"
19
+ months:
20
+ one: 1 月
21
+ other: "%{count} 月"
22
+ years:
23
+ one: 1 年
24
+ other: "%{count} 年"
25
+ less_than_x: "小於 %{distance}"
26
+ dotiw_compact:
27
+ seconds:
28
+ one: 1秒
29
+ other: "%{count}秒"
30
+ minutes:
31
+ one: 1分鐘
32
+ other: "%{count}分鐘"
33
+ hours:
34
+ one: 1小時
35
+ other: "%{count}小時"
36
+ days:
37
+ one: 1天
38
+ other: "%{count}天"
39
+ weeks:
40
+ one: 1週
41
+ other: "%{count}週"
42
+ months:
43
+ one: 1月
44
+ other: "%{count}月"
45
+ years:
46
+ one: 1年
47
+ other: "%{count}年"
48
+ less_than_x: "小於%{distance}"
49
+ words_connector: ""
50
+ two_words_connector: ""
51
+ last_word_connector: ""
52
+ about_x_years:
53
+ one: 約1年
54
+ any: "約%{count}年"
55
+ over_x_years:
56
+ one: "大於1年"
57
+ other: "大於%{count}年"
58
+ almost_x_years:
59
+ one: 將近1年
60
+ any: "將近%{count}年"
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DOTIW
4
+ module Methods
5
+ extend self
6
+
7
+ def distance_of_time_in_words_hash(from_time, to_time, options = {})
8
+ from_time = from_time.to_time if !from_time.is_a?(Time) && from_time.respond_to?(:to_time)
9
+ to_time = to_time.to_time if !to_time.is_a?(Time) && to_time.respond_to?(:to_time)
10
+
11
+ DOTIW::TimeHash.new(nil, from_time, to_time, options).to_hash
12
+ end
13
+
14
+ def distance_of_time(seconds, options = {})
15
+ options[:include_seconds] ||= true
16
+ _display_time_in_words DOTIW::TimeHash.new(seconds, nil, nil, options_with_scope(options)).to_hash, options
17
+ end
18
+
19
+ def distance_of_time_in_words(from_time, to_time = 0, include_seconds_or_options = {}, options = {})
20
+ if include_seconds_or_options.is_a?(Hash)
21
+ options = include_seconds_or_options
22
+ else
23
+ options[:include_seconds] ||= !!include_seconds_or_options
24
+ end
25
+ return distance_of_time(from_time, options) if to_time == 0
26
+
27
+ options = options_with_scope(options)
28
+ hash = distance_of_time_in_words_hash(from_time, to_time, options)
29
+ _display_time_in_words(hash, options)
30
+ end
31
+
32
+ def time_ago_in_words(from_time, include_seconds_or_options = {})
33
+ distance_of_time_in_words(from_time, Time.current, include_seconds_or_options)
34
+ end
35
+
36
+ private
37
+
38
+ def options_with_scope(options)
39
+ options.merge!({:scope => DOTIW::DEFAULT_I18N_SCOPE_COMPACT}) if options.delete(:compact)
40
+ options
41
+ end
42
+
43
+ def _display_time_in_words(hash, options = {})
44
+ options.reverse_merge!(
45
+ include_seconds: false
46
+ ).symbolize_keys!
47
+
48
+ include_seconds = options.delete(:include_seconds)
49
+ hash.delete(:seconds) if !include_seconds && hash[:minutes]
50
+
51
+ options[:except] = Array.wrap(options[:except]).map!(&:to_sym) if options[:except]
52
+ options[:only] = Array.wrap(options[:only]).map!(&:to_sym) if options[:only]
53
+
54
+ # Remove all the values that are nil or excluded. Keep the required ones.
55
+ hash.delete_if do |key, value|
56
+ value.nil? || value.zero? ||
57
+ options[:except]&.include?(key) ||
58
+ (options[:only] && !options[:only].include?(key))
59
+ end
60
+
61
+ i18n_scope = options.delete(:scope) || DOTIW::DEFAULT_I18N_SCOPE
62
+ if hash.empty?
63
+ fractions = DOTIW::TimeHash::TIME_FRACTIONS
64
+ fractions &= options[:only] if options[:only]
65
+ fractions -= options[:except] if options[:except]
66
+
67
+ I18n.with_options locale: options[:locale], scope: i18n_scope do |locale|
68
+ # e.g. try to format 'less than 1 days', fallback to '0 days'
69
+ return locale.translate :less_than_x,
70
+ distance: locale.translate(fractions.first, count: 1),
71
+ default: locale.translate(fractions.first, count: 0)
72
+ end
73
+ end
74
+
75
+ output = []
76
+ I18n.with_options locale: options[:locale], scope: i18n_scope do |locale|
77
+ output = hash.map { |key, value| locale.t(key, count: value) }
78
+ end
79
+
80
+ options.delete(:except)
81
+ options.delete(:only)
82
+ highest_measures = options.delete(:highest_measures)
83
+ highest_measures = 1 if options.delete(:highest_measure_only)
84
+ output = output[0...highest_measures] if highest_measures
85
+
86
+ options[:words_connector] ||= I18n.translate :"#{i18n_scope}.words_connector",
87
+ default: :'support.array.words_connector',
88
+ locale: options[:locale]
89
+ options[:two_words_connector] ||= I18n.translate :"#{i18n_scope}.two_words_connector",
90
+ default: :'support.array.two_words_connector',
91
+ locale: options[:locale]
92
+ options[:last_word_connector] ||= I18n.translate :"#{i18n_scope}.last_word_connector",
93
+ default: :'support.array.last_word_connector',
94
+ locale: options[:locale]
95
+
96
+ output.to_sentence(options)
97
+ end
98
+ end
99
+ end # DOTIW
@@ -1,25 +1,25 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module DOTIW
4
4
  class TimeHash
5
- TIME_FRACTIONS = [:seconds, :minutes, :hours, :days, :weeks, :months, :years]
6
-
7
- attr_accessor :distance, :smallest, :largest, :from_time, :to_time
8
-
9
- def initialize(distance, from_time = nil, to_time = nil, options = {})
10
- self.output = ActiveSupport::OrderedHash.new
11
- self.options = options
12
- self.distance = distance
13
- self.from_time = from_time || Time.current
14
- self.to_time = to_time || (@to_time_not_given = true && self.from_time + self.distance.seconds)
15
- self.smallest, self.largest = [self.from_time, self.to_time].minmax
16
- self.to_time += 1.hour if @to_time_not_given && self.smallest.dst? && !self.largest.dst?
17
- self.to_time -= 1.hour if @to_time_not_given && !self.smallest.dst? && self.largest.dst?
18
- self.smallest, self.largest = [self.from_time, self.to_time].minmax
19
- self.distance ||= begin
5
+ TIME_FRACTIONS = %i[seconds minutes hours days weeks months years].freeze
6
+
7
+ attr_reader :distance, :smallest, :largest, :from_time, :to_time
8
+
9
+ def initialize(distance, from_time, to_time = nil, options = {})
10
+ @output = {}
11
+ @options = options
12
+ @distance = distance
13
+ @from_time = from_time || Time.current
14
+ @to_time = to_time || (@to_time_not_given = true && @from_time + distance.seconds)
15
+ @smallest, @largest = [@from_time, @to_time].minmax
16
+ @to_time += 1.hour if @to_time_not_given && smallest.dst? && !largest.dst?
17
+ @to_time -= 1.hour if @to_time_not_given && !smallest.dst? && largest.dst?
18
+ @smallest, @largest = [@from_time, @to_time].minmax
19
+ @distance ||= begin
20
20
  d = largest - smallest
21
- d -= 1.hour if self.smallest.dst? && !self.largest.dst?
22
- d += 1.hour if !self.smallest.dst? && self.largest.dst?
21
+ d -= 1.hour if smallest.dst? && !largest.dst?
22
+ d += 1.hour if !smallest.dst? && largest.dst?
23
23
  d
24
24
  end
25
25
 
@@ -30,16 +30,16 @@ module DOTIW
30
30
  output
31
31
  end
32
32
 
33
- private
34
- attr_accessor :options, :output
33
+ private
34
+
35
+ attr_reader :options, :output
35
36
 
36
37
  def build_time_hash
37
38
  if accumulate_on = options.delete(:accumulate_on)
38
39
  accumulate_on = accumulate_on.to_sym
39
- if accumulate_on == :years
40
- return build_time_hash
41
- end
42
- TIME_FRACTIONS.index(accumulate_on).downto(0) { |i| self.send("build_#{TIME_FRACTIONS[i]}") }
40
+ return build_time_hash if accumulate_on == :years
41
+
42
+ TIME_FRACTIONS.index(accumulate_on).downto(0) { |i| send("build_#{TIME_FRACTIONS[i]}") }
43
43
  else
44
44
  while distance > 0
45
45
  if distance < 1.minute
@@ -63,23 +63,23 @@ module DOTIW
63
63
 
64
64
  def build_seconds
65
65
  output[:seconds] = distance.to_i
66
- self.distance = 0
66
+ @distance = 0
67
67
  end
68
68
 
69
69
  def build_minutes
70
- output[:minutes], self.distance = distance.divmod(1.minute)
70
+ output[:minutes], @distance = distance.divmod(1.minute.to_i)
71
71
  end
72
72
 
73
73
  def build_hours
74
- output[:hours], self.distance = distance.divmod(1.hour)
74
+ output[:hours], @distance = distance.divmod(1.hour.to_i)
75
75
  end
76
76
 
77
77
  def build_days
78
- output[:days], self.distance = distance.divmod(1.day) if output[:days].nil?
78
+ output[:days], @distance = distance.divmod(1.day.to_i) unless output[:days]
79
79
  end
80
80
 
81
81
  def build_weeks
82
- output[:weeks], self.distance = distance.divmod(1.week) if output[:weeks].nil?
82
+ output[:weeks], @distance = distance.divmod(1.week.to_i) unless output[:weeks]
83
83
  end
84
84
 
85
85
  def build_months
@@ -97,7 +97,7 @@ module DOTIW
97
97
  days = largest.day - smallest.day
98
98
 
99
99
  weeks, days = days.divmod(7)
100
-
100
+
101
101
  # Will otherwise incorrectly say one more day if our range goes over a day.
102
102
  days -= 1 if largest.hour < smallest.hour
103
103
 
@@ -110,7 +110,7 @@ module DOTIW
110
110
  if weeks < 0
111
111
  # Convert the last month to a week and add to total
112
112
  months -= 1
113
- last_month = largest.advance(:months => -1)
113
+ last_month = largest.advance(months: -1)
114
114
  days_in_month = Time.days_in_month(last_month.month, last_month.year)
115
115
  weeks += days_in_month / 7
116
116
  days += days_in_month % 7
@@ -118,6 +118,12 @@ module DOTIW
118
118
  days -= 7
119
119
  weeks += 1
120
120
  end
121
+
122
+ if weeks == -1
123
+ months -= 1
124
+ weeks = 4
125
+ days -= 4
126
+ end
121
127
  end
122
128
 
123
129
  if months < 0
@@ -131,9 +137,9 @@ module DOTIW
131
137
  output[:weeks] = weeks
132
138
  output[:days] = days
133
139
 
134
- total_days, self.distance = distance.abs.divmod(1.day)
140
+ total_days, @distance = distance.abs.divmod(1.day.to_i)
135
141
 
136
- [total_days, self.distance]
142
+ [total_days, @distance]
137
143
  end
138
144
  end # TimeHash
139
145
  end # DOTIW
@@ -1,5 +1,5 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module DOTIW
4
- VERSION = "3.1.1"
4
+ VERSION = '5.2.0'
5
5
  end
@@ -1,13 +1,19 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe "A better distance_of_time_in_words" do
6
- include ActionView::Helpers::DateHelper
7
- include ActionView::Helpers::TextHelper
8
- include ActionView::Helpers::NumberHelper
5
+ describe 'A better distance_of_time_in_words' do
6
+ if defined?(ActionView)
7
+ include ActionView::Helpers::DateHelper
8
+ include ActionView::Helpers::TextHelper
9
+ include ActionView::Helpers::NumberHelper
9
10
 
10
- START_TIME = "01-08-2009".to_time
11
+ require 'action_controller'
12
+ else
13
+ include DOTIW::Methods
14
+ end
15
+
16
+ START_TIME = '01-08-2009'.to_time
11
17
 
12
18
  before do
13
19
  I18n.locale = :en
@@ -15,57 +21,57 @@ describe "A better distance_of_time_in_words" do
15
21
  allow(Time.zone).to receive(:now).and_return(START_TIME)
16
22
  end
17
23
 
18
- describe "distance of time" do
19
- fragments = [
20
- [0.5.minutes, "30 seconds"],
21
- [4.5.minutes, "4 minutes and 30 seconds"],
22
- [5.minutes.to_i, "5 minutes"],
23
- [10.minutes.to_i, "10 minutes"],
24
- [1.hour.to_i, "1 hour"],
25
- [1.hour + 30.seconds, "1 hour and 30 seconds"],
26
- [4.weeks.to_i, "4 weeks"],
27
- [4.weeks + 2.days, "4 weeks and 2 days"],
28
- [24.weeks.to_i, "5 months, 2 weeks, and 1 day"]
29
- ]
30
- fragments.each do |number, result|
24
+ describe '#distance_of_time' do
25
+ [
26
+ [0.5.minutes, "30 seconds", "30s"],
27
+ [4.5.minutes, "4 minutes and 30 seconds", "4m30s"],
28
+ [5.minutes.to_i, "5 minutes", "5m"],
29
+ [10.minutes.to_i, "10 minutes", "10m"],
30
+ [1.hour.to_i, "1 hour", "1h"],
31
+ [1.hour + 30.seconds, "1 hour and 30 seconds", "1h30s"],
32
+ [4.weeks.to_i, "4 weeks", "4w"],
33
+ [4.weeks + 2.days, "4 weeks and 2 days", "4w2d"],
34
+ [24.weeks.to_i, "5 months, 2 weeks, and 1 day", "5mo2w1d"]
35
+ ].each do |number, result, compact_result|
31
36
  it "#{number} == #{result}" do
32
37
  expect(distance_of_time(number)).to eq(result)
38
+ expect(distance_of_time(number, compact: true)).to eq(compact_result)
33
39
  end
34
40
  end
35
41
 
36
- describe "with options" do
37
- it "except:seconds should skip seconds" do
38
- expect(distance_of_time(1.2.minute, except: 'seconds')).to eq("1 minute")
39
- expect(distance_of_time(2.5.hours + 30.seconds, except: 'seconds')).to eq("2 hours and 30 minutes")
42
+ describe 'with options' do
43
+ it 'except:seconds should skip seconds' do
44
+ expect(distance_of_time(1.2.minute, except: 'seconds')).to eq('1 minute')
45
+ expect(distance_of_time(2.5.hours + 30.seconds, except: 'seconds')).to eq('2 hours and 30 minutes')
40
46
  end
41
47
 
42
- it "except:seconds has higher precedence than include_seconds:true" do
48
+ it 'except:seconds has higher precedence than include_seconds:true' do
43
49
  expect(distance_of_time(1.2.minute, include_seconds: true, except: 'seconds')).to eq('1 minute')
44
50
  end
45
51
  end
46
-
47
52
  end
48
53
 
49
- describe "hash version" do
50
- describe "giving correct numbers of" do
51
-
52
- [:years, :months, :weeks, :days, :minutes, :seconds].each do |name|
54
+ describe '#distance_of_time_in_words_hash' do
55
+ describe 'giving correct numbers of' do
56
+ %i[years months weeks days minutes seconds].each do |name|
53
57
  describe name do
54
- it "exactly" do
58
+ it 'exactly' do
55
59
  hash = distance_of_time_in_words_hash(START_TIME, START_TIME + 1.send(name))
56
60
  expect(hash[name]).to eq(1)
57
61
  end
58
62
 
59
- it "two" do
63
+ it 'two' do
60
64
  hash = distance_of_time_in_words_hash(START_TIME, START_TIME + 2.send(name))
61
65
  expect(hash[name]).to eq(2)
62
66
  end
63
67
  end
64
68
  end
65
69
 
66
- it "should be happy with lots of measurements" do
67
- hash = distance_of_time_in_words_hash(START_TIME,
68
- START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds)
70
+ it 'should be happy with lots of measurements' do
71
+ hash = distance_of_time_in_words_hash(
72
+ START_TIME,
73
+ START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds
74
+ )
69
75
  expect(hash[:years]).to eq(1)
70
76
  expect(hash[:months]).to eq(2)
71
77
  expect(hash[:weeks]).to eq(3)
@@ -77,214 +83,307 @@ describe "A better distance_of_time_in_words" do
77
83
  end
78
84
  end
79
85
 
80
- describe "real version" do
81
- it "debe hablar español" do
82
- expect(distance_of_time_in_words(START_TIME, START_TIME + 1.days, :locale => :es)).to eq("un día")
83
- expect(distance_of_time_in_words(START_TIME, START_TIME + 5.days, :locale => :es)).to eq("5 días")
86
+ describe '#time_ago_in_words' do
87
+ it 'aliases to distance_of_time_in_words' do
88
+ expect(time_ago_in_words(Time.now - 3.days - 14.minutes)).to eq('3 days and 14 minutes')
84
89
  end
90
+ end
91
+
92
+ describe '#distance_of_time_in_words' do
93
+ context 'locale' do
94
+ it 'includes known languages' do
95
+ expect(DOTIW.languages).to include :en
96
+ expect(DOTIW.languages).to include :ru
97
+ end
98
+
99
+ it 'includes all the languages in specs' do
100
+ languages = Dir[File.join(File.dirname(__FILE__), 'i18n', '*.yml')].map { |f| File.basename(f, '.yml') }
101
+ expect(DOTIW.languages.map(&:to_s).sort).to eq languages.sort
102
+ end
85
103
 
86
- it "deve parlare l'italiano" do
87
- expect(distance_of_time_in_words(START_TIME, START_TIME + 1.days, true, :locale => :it)).to eq("un giorno")
88
- expect(distance_of_time_in_words(START_TIME, START_TIME + 5.days, true, :locale => :it)).to eq("5 giorni")
104
+ DOTIW.languages.each do |lang|
105
+ context lang do
106
+ YAML.safe_load(
107
+ File.read(
108
+ File.join(
109
+ File.dirname(__FILE__), 'i18n', "#{lang}.yml"
110
+ )
111
+ )
112
+ ).each_pair do |category, fixtures|
113
+ context category do
114
+ fixtures.each_pair do |k, v|
115
+ it v do
116
+ expect(
117
+ distance_of_time_in_words(
118
+ START_TIME,
119
+ START_TIME + eval(k),
120
+ true,
121
+ locale: lang
122
+ )
123
+ ).to eq(v)
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
89
130
  end
90
131
 
91
- fragments = [
92
- [START_TIME, START_TIME + 5.days + 3.minutes, "5 days and 3 minutes"],
93
- [START_TIME, START_TIME + 1.minute, "1 minute"],
94
- [START_TIME, START_TIME + 3.years, "3 years"],
95
- [START_TIME, START_TIME + 10.years, "10 years"],
96
- [START_TIME, START_TIME + 8.months, "8 months"],
97
- [START_TIME, START_TIME + 3.hour, "3 hours"],
98
- [START_TIME, START_TIME + 13.months, "1 year and 1 month"],
132
+ [
133
+ [START_TIME, START_TIME + 5.days + 3.minutes, '5 days and 3 minutes'],
134
+ [START_TIME, START_TIME + 1.minute, '1 minute'],
135
+ [START_TIME, START_TIME + 3.years, '3 years'],
136
+ [START_TIME, START_TIME + 10.years, '10 years'],
137
+ [START_TIME, START_TIME + 8.months, '8 months'],
138
+ [START_TIME, START_TIME + 3.hour, '3 hours'],
139
+ [START_TIME, START_TIME + 13.months, '1 year and 1 month'],
99
140
  # Any numeric sequence is merely coincidental.
100
- [START_TIME, START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds, "1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, and 7 seconds"],
101
- ["2009-3-16".to_time, "2008-4-14".to_time, "11 months and 2 days"],
102
- ["2009-3-16".to_time + 1.minute, "2008-4-14".to_time, "11 months, 2 days, and 1 minute"],
103
- ["2009-4-14".to_time, "2008-3-16".to_time, "1 year, 4 weeks, and 1 day"],
104
- ["2009-2-01".to_time, "2009-3-01".to_time, "1 month"],
105
- ["2008-2-01".to_time, "2008-3-01".to_time, "1 month"]
106
- ]
107
- fragments.each do |start, finish, output|
141
+ [START_TIME, START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds, '1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, and 7 seconds'],
142
+ ['2009-3-16'.to_time, '2008-4-14'.to_time, '11 months and 2 days'],
143
+ ['2009-3-16'.to_time + 1.minute, '2008-4-14'.to_time, '11 months, 2 days, and 1 minute'],
144
+ ['2009-4-14'.to_time, '2008-3-16'.to_time, '1 year, 4 weeks, and 1 day'],
145
+ ['2009-2-01'.to_time, '2009-3-01'.to_time, '1 month'],
146
+ ['2008-2-01'.to_time, '2008-3-01'.to_time, '1 month'],
147
+ [Date.parse('31.03.2015').to_time, Time.parse('01.03.2016'), '10 months, 4 weeks, and 2 days'],
148
+ [Date.new(2014, 1, 31), Date.new(2014, 3, 1), '4 weeks and 1 day'],
149
+ ['2008-2-01'.to_time, '2008-3-01'.to_time, '1 month'],
150
+ ['2014-1-31'.to_time, '2014-3-01'.to_time, '4 weeks and 1 day'],
151
+ ['2014-1-31'.to_time, '2014-3-02'.to_time, '4 weeks and 2 days'],
152
+ ['2016-1-31'.to_time, '2016-3-01'.to_time, '4 weeks and 2 days'],
153
+ ['2016-1-31'.to_time, '2016-3-02'.to_time, '1 month']
154
+ ].each do |start, finish, output|
108
155
  it "should be #{output}" do
109
156
  expect(distance_of_time_in_words(start, finish, true)).to eq(output)
157
+ expect(distance_of_time_in_words(finish, start, true)).to eq(output)
110
158
  end
111
159
  end
112
160
 
113
- describe "accumulate on" do
114
- fragments = [
161
+ [
162
+ [Time.zone.now, Time.zone.now + 1.day - 1.minute, '23 hours and 59 minutes'],
163
+ [Time.zone.now, Time.zone.now + 15.days - 1.minute, '14 days, 23 hours, and 59 minutes'],
164
+ [Time.zone.now, Time.zone.now + 29.days - 1.minute, '28 days, 23 hours, and 59 minutes'],
165
+ [Time.zone.now, Time.zone.now + 30.days - 1.minute, '29 days, 23 hours, and 59 minutes'],
166
+ [Time.zone.now, Time.zone.now + 31.days - 1.minute, '30 days, 23 hours, and 59 minutes'],
167
+ [Time.zone.now, Time.zone.now + 32.days - 1.minute, '31 days, 23 hours, and 59 minutes'],
168
+ [Time.zone.now, Time.zone.now + 33.days - 1.minute, '32 days, 23 hours, and 59 minutes']
169
+ ].each do |start, finish, output|
170
+ it "should be #{output}" do
171
+ expect(distance_of_time_in_words(start, finish, accumulate_on: 'days')).to eq(output)
172
+ end
173
+ end
174
+
175
+ [
176
+ [Time.at(1), Time.at(100), '1 minute'],
177
+ [DateTime.now, DateTime.now + 1.minute, '1 minute'],
178
+ [Date.new(2000, 1, 2), Date.new(2000, 1, 3), '1 day'],
179
+ [Time.at(DateTime.now), DateTime.now + 1.minute, '1 minute']
180
+ ].each do |start, finish, output|
181
+ it "should be #{output}" do
182
+ expect(distance_of_time_in_words(start, finish)).to eq(output)
183
+ end
184
+ end
185
+
186
+ describe 'accumulate_on:' do
187
+ [
115
188
  [START_TIME,
116
189
  START_TIME + 10.minute,
117
190
  :seconds,
118
- "600 seconds"],
191
+ '600 seconds'],
119
192
  [START_TIME,
120
193
  START_TIME + 10.hour + 10.minute + 1.second,
121
194
  :minutes,
122
- "610 minutes and 1 second"],
195
+ '610 minutes and 1 second'],
123
196
  [START_TIME,
124
- START_TIME + 2.day + 10000.hour + 10.second,
197
+ START_TIME + 2.day + 10_000.hour + 10.second,
125
198
  :hours,
126
- "10048 hours and 10 seconds"],
199
+ '10048 hours and 10 seconds'],
127
200
  [START_TIME,
128
- START_TIME + 2.day + 10000.hour + 10.second,
201
+ START_TIME + 2.day + 10_000.hour + 10.second,
129
202
  :days,
130
- "418 days, 16 hours, and 10 seconds"],
203
+ '418 days, 16 hours, and 10 seconds'],
131
204
  [START_TIME,
132
- START_TIME + 2.day + 10000.hour + 10.second,
205
+ START_TIME + 2.day + 10_000.hour + 10.second,
133
206
  :weeks,
134
- "59 weeks, 5 days, 16 hours, and 10 seconds"],
207
+ '59 weeks, 5 days, 16 hours, and 10 seconds'],
135
208
  [START_TIME,
136
- START_TIME + 2.day + 10000.hour + 10.second,
209
+ START_TIME + 2.day + 10_000.hour + 10.second,
137
210
  :months,
138
- "13 months, 3 weeks, 1 day, 16 hours, and 10 seconds"],
139
- ["2015-1-15".to_time, "2016-3-15".to_time, :months, "14 months"]
140
-
141
- ]
142
- fragments.each do |start, finish, accumulator, output|
211
+ '13 months, 3 weeks, 1 day, 16 hours, and 10 seconds'],
212
+ ['2015-1-15'.to_time, '2016-3-15'.to_time, :months, '14 months']
213
+ ].each do |start, finish, accumulator, output|
143
214
  it "should be #{output}" do
144
- expect(distance_of_time_in_words(start, finish, true, :accumulate_on => accumulator)).to eq(output)
215
+ expect(distance_of_time_in_words(start, finish, true, accumulate_on: accumulator)).to eq(output)
145
216
  end
146
217
  end
147
218
  end # :accumulate_on
148
219
 
149
- describe "without finish time" do
220
+ describe 'without finish time' do
150
221
  # A missing finish argument should default to zero, essentially returning
151
222
  # the equivalent of distance_of_time in order to be backwards-compatible
152
223
  # with the original rails distance_of_time_in_words helper.
153
- fragments = [
154
- [5.minutes.to_i, "5 minutes"],
155
- [10.minutes.to_i, "10 minutes"],
156
- [1.hour.to_i, "1 hour"],
157
- [6.days.to_i, "6 days"],
158
- [4.weeks.to_i, "4 weeks"],
159
- [24.weeks.to_i, "5 months, 2 weeks, and 1 day"]
160
- ]
161
- fragments.each do |start, output|
224
+ [
225
+ [5.minutes.to_i, '5 minutes'],
226
+ [10.minutes.to_i, '10 minutes'],
227
+ [1.hour.to_i, '1 hour'],
228
+ [6.days.to_i, '6 days'],
229
+ [4.weeks.to_i, '4 weeks'],
230
+ [24.weeks.to_i, '5 months, 2 weeks, and 1 day']
231
+ ].each do |start, output|
162
232
  it "should be #{output}" do
163
233
  expect(distance_of_time_in_words(start)).to eq(output)
164
234
  end
165
235
  end
166
236
  end
167
-
168
237
  end
169
238
 
170
- describe "with output options" do
171
- fragments = [
239
+ describe 'with output options' do
240
+ [
172
241
  # Any numeric sequence is merely coincidental.
173
242
  [START_TIME,
174
243
  START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
175
- { :words_connector => " - " },
176
- "1 year - 2 months - 3 weeks - 4 days - 5 hours - 6 minutes, and 7 seconds"],
244
+ { words_connector: ' - ' },
245
+ '1 year - 2 months - 3 weeks - 4 days - 5 hours - 6 minutes, and 7 seconds'],
177
246
  [START_TIME,
178
247
  START_TIME + 5.minutes + 6.seconds,
179
- { :two_words_connector => " - " },
180
- "5 minutes - 6 seconds"],
248
+ { two_words_connector: ' - ' },
249
+ '5 minutes - 6 seconds'],
181
250
  [START_TIME,
182
- START_TIME + 4.hours + 5.minutes + 6.seconds,
183
- { :last_word_connector => " - " },
184
- "4 hours, 5 minutes - 6 seconds"],
251
+ START_TIME + 4.hours + 5.minutes + 6.seconds,
252
+ { last_word_connector: ' - ' },
253
+ '4 hours, 5 minutes - 6 seconds'],
185
254
  [START_TIME,
186
255
  START_TIME + 1.year + 2.months + 3.days + 4.hours + 5.minutes + 6.seconds,
187
- { :except => "minutes" },
188
- "1 year, 2 months, 3 days, 4 hours, and 6 seconds"],
256
+ { except: 'minutes' },
257
+ '1 year, 2 months, 3 days, 4 hours, and 6 seconds'],
189
258
  [START_TIME,
190
259
  START_TIME + 1.hour + 1.minute,
191
- { :except => "minutes"}, "1 hour"],
260
+ { except: 'minutes' }, '1 hour'],
192
261
  [START_TIME,
193
262
  START_TIME + 1.hour + 1.day + 1.minute,
194
- { :except => ["minutes", "hours"]},
195
- "1 day"],
263
+ { except: %w[minutes hours] },
264
+ '1 day'],
196
265
  [START_TIME,
197
266
  START_TIME + 1.hour + 1.day + 1.minute,
198
- { :only => ["minutes", "hours"]},
199
- "1 hour and 1 minute"],
200
- [START_TIME,
201
- START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
202
- { :vague => true },
203
- "about 1 year"],
204
- [START_TIME,
205
- START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
206
- { :vague => "Yes please" },
207
- "about 1 year"],
208
- [START_TIME,
209
- START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
210
- { :vague => false },
211
- "1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, and 7 seconds"],
212
- [START_TIME,
213
- START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
214
- { :vague => nil },
215
- "1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, and 7 seconds"],
267
+ { only: %w[minutes hours] },
268
+ '1 hour and 1 minute'],
216
269
  [START_TIME,
217
270
  START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
218
- { :except => "minutes" },
219
- "1 year, 2 months, 3 weeks, 4 days, 5 hours, and 7 seconds"],
271
+ { except: 'minutes' },
272
+ '1 year, 2 months, 3 weeks, 4 days, 5 hours, and 7 seconds'],
220
273
  [START_TIME,
221
- START_TIME + 1.hour + 2.minutes + 3.seconds,
222
- { :highest_measure_only => true },
223
- "1 hour"],
274
+ START_TIME + 1.hour + 2.minutes + 3.seconds,
275
+ { highest_measure_only: true },
276
+ '1 hour'],
224
277
  [START_TIME,
225
278
  START_TIME + 1.hours + 2.minutes + 3.seconds,
226
- { :highest_measures => 1 },
227
- "1 hour"],
279
+ { highest_measures: 1 },
280
+ '1 hour'],
228
281
  [START_TIME,
229
282
  START_TIME + 2.year + 3.months + 4.days + 5.hours + 6.minutes + 7.seconds,
230
- { :highest_measures => 3 },
231
- "2 years, 3 months, and 4 days"],
283
+ { highest_measures: 3 },
284
+ '2 years, 3 months, and 4 days'],
232
285
  [START_TIME,
233
286
  START_TIME + 2.year + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
234
- { :highest_measures => 2 },
235
- "2 years and 3 weeks"],
287
+ { highest_measures: 2 },
288
+ '2 years and 3 weeks'],
236
289
  [START_TIME,
237
290
  START_TIME + 4.days + 6.minutes + 7.seconds,
238
- { :highest_measures => 3 },
239
- "4 days, 6 minutes, and 7 seconds"],
291
+ { highest_measures: 3 },
292
+ '4 days, 6 minutes, and 7 seconds'],
240
293
  [START_TIME,
241
294
  START_TIME + 1.year + 2.weeks,
242
- { :highest_measures => 3 },
243
- "1 year and 2 weeks"],
295
+ { highest_measures: 3 },
296
+ '1 year and 2 weeks'],
244
297
  [START_TIME,
245
298
  START_TIME + 1.days,
246
- { :only => [:years, :months] },
247
- "less than 1 month"],
299
+ { only: %i[years months] },
300
+ 'less than 1 month'],
248
301
  [START_TIME,
249
302
  START_TIME + 5.minutes,
250
- { :except => [:hours, :minutes, :seconds] },
251
- "less than 1 day"],
303
+ { except: %i[hours minutes seconds] },
304
+ 'less than 1 day'],
252
305
  [START_TIME,
253
306
  START_TIME + 1.days,
254
- { :highest_measures => 1, :only => [:years, :months] },
255
- "less than 1 month"]
256
- ]
257
- fragments.each do |start, finish, options, output|
307
+ { highest_measures: 1, only: %i[years months] },
308
+ 'less than 1 month']
309
+ ].each do |start, finish, options, output|
258
310
  it "should be #{output}" do
259
311
  expect(distance_of_time_in_words(start, finish, true, options)).to eq(output)
260
312
  end
261
313
  end
262
314
 
263
- describe "include_seconds" do
264
- it "is ignored if only seconds have passed" do
265
- expect(distance_of_time_in_words(START_TIME, START_TIME + 1.second, false)).to eq("1 second")
315
+ if defined?(ActionView)
316
+ describe 'ActionView' do
317
+ [
318
+ [START_TIME,
319
+ START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
320
+ { vague: true },
321
+ 'about 1 year'],
322
+ [START_TIME,
323
+ START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
324
+ { vague: 'Yes please' },
325
+ 'about 1 year'],
326
+ [START_TIME,
327
+ START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
328
+ { vague: false },
329
+ '1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, and 7 seconds'],
330
+ [START_TIME,
331
+ START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
332
+ { vague: nil },
333
+ '1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, and 7 seconds']
334
+ ].each do |start, finish, options, output|
335
+ it "should be #{output}" do
336
+ expect(distance_of_time_in_words(start, finish, true, options)).to eq(output)
337
+ end
338
+ end
339
+
340
+ context 'via ActionController::Base.helpers' do
341
+ it '#distance_of_time_in_words' do
342
+ end_time = START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds
343
+ expected = '1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, and 7 seconds'
344
+ actual = ActionController::Base.helpers.distance_of_time_in_words(START_TIME, end_time, true, { vague: false })
345
+ expect(actual).to eq(expected)
346
+ end
347
+
348
+ it '#time_ago_in_words' do
349
+ expected = '3 days and 14 minutes'
350
+ actual = ActionController::Base.helpers.time_ago_in_words(Time.now - 3.days - 14.minutes)
351
+ expect(actual).to eq(expected)
352
+ end
353
+ end
354
+ end
355
+ end
356
+
357
+ describe 'include_seconds' do
358
+ it 'is ignored if only seconds have passed' do
359
+ expect(distance_of_time_in_words(START_TIME, START_TIME + 1.second, false)).to eq('1 second')
266
360
  end
267
361
 
268
- it "removes seconds in all other cases" do
269
- expect(distance_of_time_in_words(START_TIME,
270
- START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
271
- false)).to eq("1 year, 2 months, 3 weeks, 4 days, 5 hours, and 6 minutes")
362
+ it 'removes seconds in all other cases' do
363
+ expect(
364
+ distance_of_time_in_words(
365
+ START_TIME,
366
+ START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
367
+ false
368
+ )
369
+ ).to eq('1 year, 2 months, 3 weeks, 4 days, 5 hours, and 6 minutes')
272
370
  end
273
371
  end # include_seconds
274
372
  end
275
373
 
276
- describe "percentage of time" do
277
- def time_in_percent(options = {})
278
- distance_of_time_in_percent("04-12-2009".to_time, "29-01-2010".to_time, "04-12-2010".to_time, options)
279
- end
374
+ if defined?(ActionView)
375
+ describe 'percentage of time' do
376
+ def time_in_percent(options = {})
377
+ distance_of_time_in_percent('04-12-2009'.to_time, '29-01-2010'.to_time, '04-12-2010'.to_time, options)
378
+ end
280
379
 
281
- it "calculates 15%" do
282
- expect(time_in_percent).to eq("15%")
283
- end
380
+ it 'calculates 15%' do
381
+ expect(time_in_percent).to eq('15%')
382
+ end
284
383
 
285
- it "calculates 15.3%" do
286
- expect(time_in_percent(:precision => 1)).to eq("15.3%")
384
+ it 'calculates 15.3%' do
385
+ expect(time_in_percent(precision: 1)).to eq('15.3%')
386
+ end
287
387
  end
288
388
  end
289
-
290
389
  end