rails-diff-time 0.1.4 → 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: 908929488b5de5bb6d8dcdfdf0f16b7081d02b3e2873e3969884a64c7400d674
4
- data.tar.gz: bbf8fc3135e4e1afcacd7299e6f43775337f5798dacb712505d5020b02a14a48
3
+ metadata.gz: f500ef502d22be1eb93fe920650122bdd370b5799788e00180235a48afa17ce1
4
+ data.tar.gz: 0b954d61dcfbc5c431a8ea9a85ea996bac0a71470dc6db8dafb69e77d4c88813
5
5
  SHA512:
6
- metadata.gz: 40e116660189676a4a8636e9d7ec9375ba7f715ec9e72cdb4bd192d824b26b8a4eae5ffade00a493f232425c801e8ac8d405d0713ce318031cedaec794208464
7
- data.tar.gz: 82e04671eba7400273228f7ed916397da2a52fb6559160d10ac1eb5bdae1ca5d1c07944860be28405fa437eaf1ebc4c1ff563c0d19c31eee6f29ae7afb395ce6
6
+ metadata.gz: b515308f6f0ace59434dd0ee7be048696fbdddfe196d02198fb7cfd8d8ca7625195d5867dd3b3e287bf1bdc40f01bd92d6667b1295ff9e0c06edba1cdfc9b0e4
7
+ data.tar.gz: ee2d795c5307826898c38ca91421d106db62e4d0592a9c4ceebbf9ef34a1edb06970e2ab246b5c8e15ea1df60c5303d442f213766bfc8b28f9b4464eee1d3910
data/.rubocop.yml CHANGED
@@ -7,6 +7,7 @@ Style/StringLiterals:
7
7
 
8
8
  Style/StringLiteralsInInterpolation:
9
9
  EnforcedStyle: double_quotes
10
+ AutoCorrect: false
10
11
 
11
12
  # メトリクス系のルールをオプトアウト
12
13
  Metrics/ModuleLength:
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # Rails::Diff::Time
1
+ # rails-diff-time
2
2
 
3
- A Rails helper gem to display time differences in a human-readable format.
3
+ A Rails helper gem to display time differences in a human-readable format with automatic updates.
4
4
 
5
5
  ## Installation
6
6
 
@@ -18,6 +18,10 @@ Or install it yourself as:
18
18
 
19
19
  $ gem install rails-diff-time
20
20
 
21
+ ## Setup
22
+
23
+ No setup required! Just install the gem and start using it. The auto-update JavaScript will be automatically included when you use `auto_update: true` for the first time on a page.
24
+
21
25
  ## Usage
22
26
 
23
27
  In your Rails views, you can use the `diff_time` helper to display time differences:
@@ -29,15 +33,34 @@ In your Rails views, you can use the `diff_time` helper to display time differen
29
33
  <%= diff_time(Time.now - 2.hours) %>
30
34
  # Output: <span>2 hours ago</span>
31
35
 
32
- <%= diff_time(Time.now + 1.year + 2.months, "div", class: "time-diff") %>
36
+ <%= diff_time(Time.now + 1.year + 2.months, "div", { class: "time-diff" }) %>
33
37
  # Output: <div class="time-diff">1 year 2 months later</div>
34
38
  ```
35
39
 
40
+ ### Auto-update Feature
41
+
42
+ Enable automatic updates (every 1 minute) by passing the `auto_update: true` option:
43
+
44
+ ```erb
45
+ <%= diff_time(user.created_at, auto_update: true) %>
46
+ # The display will automatically update every minute
47
+
48
+ <%= diff_time(post.published_at, "span", { class: "timestamp" }, auto_update: true) %>
49
+ # With custom element and attributes
50
+ ```
51
+
52
+ **Note:** The necessary JavaScript code will be automatically included on the page the first time you call `diff_time` with `auto_update: true`. No manual setup required!
53
+
36
54
  ### Parameters
37
55
 
56
+ ```ruby
57
+ diff_time(certain_time, element_name = "span", attributes = {}, auto_update: false)
58
+ ```
59
+
38
60
  - `certain_time` (required): The time to compare with the current time
39
61
  - `element_name` (optional, default: "span"): The HTML element to wrap the output
40
62
  - `attributes` (optional, default: {}): HTML attributes to add to the element
63
+ - `auto_update` (optional, default: false): Enable automatic updates every minute (JavaScript automatically included on first use)
41
64
 
42
65
  ## Examples
43
66
 
@@ -49,9 +72,63 @@ In your Rails views, you can use the `diff_time` helper to display time differen
49
72
  <%= diff_time(event.starts_at, "p") %>
50
73
 
51
74
  # With CSS classes
52
- <%= diff_time(post.published_at, "span", class: "text-muted", id: "post-time") %>
75
+ <%= diff_time(post.published_at, "span", { class: "text-muted", id: "post-time" }) %>
76
+
77
+ # With auto-update enabled (JavaScript automatically included)
78
+ <%= diff_time(meeting.scheduled_at, "span", { class: "meeting-time" }, auto_update: true) %>
79
+
80
+ # Live timestamps for comments
81
+ <% @comments.each do |comment| %>
82
+ <div class="comment">
83
+ <p><%= comment.body %></p>
84
+ <small>Posted <%= diff_time(comment.created_at, auto_update: true) %></small>
85
+ </div>
86
+ <% end %>
53
87
  ```
54
88
 
89
+ ### Complete Example
90
+
91
+ **app/views/posts/show.html.erb:**
92
+ ```erb
93
+ <article>
94
+ <h1><%= @post.title %></h1>
95
+ <p class="meta">
96
+ Published <%= diff_time(@post.published_at, auto_update: true) %>
97
+ </p>
98
+ <div><%= @post.content %></div>
99
+ </article>
100
+ ```
101
+
102
+ That's it! No need to modify layout files or configure JavaScript imports.
103
+
104
+ ## How It Works
105
+
106
+ The auto-update feature works by:
107
+
108
+ 1. Storing the original timestamp in a `data-certain-time` attribute (ISO 8601 format)
109
+ 2. Automatically including JavaScript code on first use of `auto_update: true`
110
+ 3. Running a JavaScript timer that recalculates the time difference every 60 seconds
111
+ 4. Updating the text content of elements client-side
112
+
113
+ This means:
114
+ - No additional server requests are made
115
+ - The display stays accurate without page reloads
116
+ - Works with Turbolinks/Turbo for SPA-like experiences
117
+ - **No JavaScript imports, asset pipeline configuration, or layout modifications needed** - everything is automatic!
118
+
119
+ ## Browser Support
120
+
121
+ The JavaScript functionality requires:
122
+ - Modern browsers with ES6+ support
123
+ - Native `Date` object support
124
+ - `setInterval` support
125
+
126
+ Compatible with:
127
+ - Chrome/Edge (latest)
128
+ - Firefox (latest)
129
+ - Safari (latest)
130
+ - Mobile browsers (iOS Safari, Chrome Mobile)
131
+
55
132
  ## Development
56
133
 
57
134
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -64,5 +141,4 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/dhq_bo
64
141
 
65
142
  ## License
66
143
 
67
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
68
-
144
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,8 +1,14 @@
1
1
  <%
2
- _element_name ||= locals[:element_name] || "span"
3
- _diff_time = locals[:diff_time]
4
- _attributes ||= locals[:attributes] || {}
2
+ element_name ||= locals[:element_name] || "span"
3
+ diff_time = locals[:diff_time]
4
+ attributes ||= locals[:attributes] || {}
5
+ certain_time = locals[:certain_time]
6
+ auto_update = locals[:auto_update]
7
+
8
+ if auto_update
9
+ attributes[:data] ||= {}
10
+ attributes[:data][:diff_time_target] = "display"
11
+ attributes[:data][:certain_time] = certain_time.iso8601
12
+ end
5
13
  %>
6
- <% if _diff_time %>
7
- <%= content_tag(_element_name.to_sym, _diff_time, _attributes) %>
8
- <% end %>
14
+ <%= content_tag(element_name.to_sym, diff_time, attributes) %>
@@ -2,34 +2,229 @@
2
2
 
3
3
  module RailsDiffTime
4
4
  # Helper methods for displaying time differences in a human-readable format.
5
- #
6
- # This module provides methods to format time differences as relative strings
7
- # (e.g., "2 hours ago", "3 days later") and integrates with Rails views.
8
- # The helpers can be used directly in templates or through the provided
9
- # render helper method.
10
- #
11
- # @example Usage in a Rails view
12
- # <%= diff_time(user.created_at) %>
13
- # <%= diff_time(event.start_time, "div", class: "timestamp") %>
14
5
  module Helpers
15
- def diff_time(certain_time, element_name = "span", attributes = {})
6
+ def diff_time(certain_time, element_name = "span", attributes = {}, auto_update: false)
16
7
  return "" if certain_time.nil?
17
8
 
18
- render "rails-diff-time/diff_time", locals: {
9
+ result = render "rails-diff-time/diff_time", locals: {
19
10
  diff_time: diff_time_str(certain_time),
20
11
  element_name: element_name,
21
- attributes: attributes
12
+ attributes: attributes,
13
+ certain_time: certain_time,
14
+ auto_update: auto_update
22
15
  }
16
+
17
+ # If auto_update is enabled and the script has not been included yet
18
+ if auto_update && !@_diff_time_script_included
19
+ @_diff_time_script_included = true
20
+ result += diff_time_script_tag
21
+ end
22
+
23
+ result
23
24
  end
24
25
 
25
26
  private
26
27
 
28
+ def diff_time_script_tag
29
+ javascript_tag do
30
+ <<~JAVASCRIPT.html_safe
31
+ (function() {
32
+ 'use strict';
33
+
34
+ class DiffTimeUpdater {
35
+ constructor() {
36
+ this.updateInterval = 60000;
37
+ this.elements = [];
38
+ this.timerId = null;
39
+ }
40
+
41
+ init() {
42
+ this.findElements();
43
+ if (this.elements.length > 0) {
44
+ this.startAutoUpdate();
45
+ }
46
+ }
47
+
48
+ findElements() {
49
+ this.elements = Array.from(
50
+ document.querySelectorAll('[data-diff-time-target="display"]')
51
+ );
52
+ }
53
+
54
+ startAutoUpdate() {
55
+ if (this.timerId) clearInterval(this.timerId);
56
+ this.timerId = setInterval(() => this.updateAll(), this.updateInterval);
57
+ }
58
+
59
+ updateAll() {
60
+ this.elements.forEach(element => this.updateElement(element));
61
+ }
62
+
63
+ updateElement(element) {
64
+ const certainTimeStr = element.dataset.certainTime;
65
+ if (!certainTimeStr) return;
66
+ const certainTime = new Date(certainTimeStr);
67
+ const diffTimeStr = this.calculateDiffTime(certainTime);
68
+ element.textContent = diffTimeStr;
69
+ }
70
+
71
+ calculateDiffTime(certainTime) {
72
+ const now = new Date();
73
+ const diff = certainTime - now;
74
+ const differenceInSeconds = Math.abs(diff) / 1000;
75
+
76
+ if (differenceInSeconds <= 5) return "now";
77
+
78
+ const isLater = diff > 0;
79
+
80
+ if (differenceInSeconds >= this.yearInSeconds()) {
81
+ return this.formatYears(differenceInSeconds, isLater);
82
+ } else if (differenceInSeconds >= this.monthInSeconds()) {
83
+ return this.formatMonths(differenceInSeconds, isLater);
84
+ } else if (differenceInSeconds >= this.weekInSeconds()) {
85
+ return this.formatWeeks(differenceInSeconds, isLater);
86
+ } else if (differenceInSeconds >= this.dayInSeconds()) {
87
+ return this.formatDays(differenceInSeconds, isLater);
88
+ } else if (differenceInSeconds >= this.hourInSeconds()) {
89
+ return this.formatHours(differenceInSeconds, isLater);
90
+ } else if (differenceInSeconds >= this.minuteInSeconds()) {
91
+ return this.formatMinutes(differenceInSeconds, isLater);
92
+ } else {
93
+ return this.formatSeconds(differenceInSeconds, isLater);
94
+ }
95
+ }
96
+
97
+ formatYears(seconds, isLater) {
98
+ const years = Math.floor(seconds / this.yearInSeconds());
99
+ let remaining = seconds - (years * this.yearInSeconds());
100
+ let result = this.timeStr(years, "year");
101
+
102
+ if (remaining >= this.monthInSeconds()) {
103
+ const months = Math.floor(remaining / this.monthInSeconds());
104
+ remaining -= months * this.monthInSeconds();
105
+ result += ` ${this.timeStr(months, "month")}`;
106
+
107
+ if (remaining >= this.dayInSeconds()) {
108
+ const days = Math.floor(remaining / this.dayInSeconds());
109
+ result += ` ${this.timeStr(days, "day")}`;
110
+ }
111
+ }
112
+ return `${result} ${this.agoOrLater(isLater)}`;
113
+ }
114
+
115
+ formatMonths(seconds, isLater) {
116
+ const months = Math.floor(seconds / this.monthInSeconds());
117
+ const remaining = seconds - (months * this.monthInSeconds());
118
+ let result = this.timeStr(months, "month");
119
+
120
+ if (remaining >= this.dayInSeconds()) {
121
+ const days = Math.floor(remaining / this.dayInSeconds());
122
+ result += ` ${this.timeStr(days, "day")}`;
123
+ }
124
+ return `${result} ${this.agoOrLater(isLater)}`;
125
+ }
126
+
127
+ formatWeeks(seconds, isLater) {
128
+ const weeks = Math.floor(seconds / this.weekInSeconds());
129
+ return `${this.timeStr(weeks, "week")} ${this.agoOrLater(isLater)}`;
130
+ }
131
+
132
+ formatDays(seconds, isLater) {
133
+ const days = Math.floor(seconds / this.dayInSeconds());
134
+ return `${this.timeStr(days, "day")} ${this.agoOrLater(isLater)}`;
135
+ }
136
+
137
+ formatHours(seconds, isLater) {
138
+ const hours = Math.floor(seconds / this.hourInSeconds());
139
+ const remaining = seconds - (hours * this.hourInSeconds());
140
+ let result = this.timeStr(hours, "hour");
141
+
142
+ if (remaining >= this.minuteInSeconds()) {
143
+ const minutes = Math.floor(remaining / this.minuteInSeconds());
144
+ result += ` ${this.timeStr(minutes, "minute")}`;
145
+ }
146
+ return `${result} ${this.agoOrLater(isLater)}`;
147
+ }
148
+
149
+ formatMinutes(seconds, isLater) {
150
+ const minutes = Math.floor(seconds / this.minuteInSeconds());
151
+ const remaining = seconds - (minutes * this.minuteInSeconds());
152
+ let result = this.timeStr(minutes, "minute");
153
+
154
+ if (remaining > 0) {
155
+ const secs = Math.floor(remaining);
156
+ result += ` ${this.timeStr(secs, "second")}`;
157
+ }
158
+ return `${result} ${this.agoOrLater(isLater)}`;
159
+ }
160
+
161
+ formatSeconds(seconds, isLater) {
162
+ const secs = Math.floor(seconds);
163
+ return `${this.timeStr(secs, "second")} ${this.agoOrLater(isLater)}`;
164
+ }
165
+
166
+ yearInSeconds() { return 365.25 * 24 * 3600; }
167
+ monthInSeconds() { return 30 * 24 * 3600; }
168
+ weekInSeconds() { return 7 * 24 * 3600; }
169
+ dayInSeconds() { return 24 * 3600; }
170
+ hourInSeconds() { return 3600; }
171
+ minuteInSeconds() { return 60; }
172
+
173
+ agoOrLater(isLater) { return isLater ? "later" : "ago"; }
174
+
175
+ timeStr(count, singular) {
176
+ const plural = singular + "s";
177
+ return `${count} ${count === 1 ? singular : plural}`;
178
+ }
179
+
180
+ destroy() {
181
+ if (this.timerId) {
182
+ clearInterval(this.timerId);
183
+ this.timerId = null;
184
+ }
185
+ this.elements = [];
186
+ }
187
+ }
188
+
189
+ let updater = null;
190
+
191
+ function initUpdater() {
192
+ if (updater) updater.destroy();
193
+ updater = new DiffTimeUpdater();
194
+ updater.init();
195
+ }
196
+
197
+ if (document.readyState === 'loading') {
198
+ document.addEventListener('DOMContentLoaded', initUpdater);
199
+ } else {
200
+ initUpdater();
201
+ }
202
+
203
+ document.addEventListener('turbolinks:load', initUpdater);
204
+ document.addEventListener('turbo:load', initUpdater);
205
+
206
+ document.addEventListener('turbolinks:before-cache', function() {
207
+ if (updater) updater.destroy();
208
+ });
209
+
210
+ document.addEventListener('turbo:before-cache', function() {
211
+ if (updater) updater.destroy();
212
+ });
213
+
214
+ window.RailsDiffTime = {
215
+ updater: updater,
216
+ DiffTimeUpdater: DiffTimeUpdater
217
+ };
218
+ })();
219
+ JAVASCRIPT
220
+ end
221
+ end
222
+
27
223
  def diff_time_str(certain_time)
28
224
  now = ::Time.now
29
225
  diff = certain_time - now
30
226
  difference_in_seconds = diff.abs
31
227
 
32
- # Display "now" if within 5 seconds
33
228
  return "now" if difference_in_seconds <= 5
34
229
 
35
230
  case difference_in_seconds
@@ -120,13 +315,12 @@ module RailsDiffTime
120
315
  "#{time_str(seconds, "second")} #{ago_or_later(diff)}"
121
316
  end
122
317
 
123
- # Time unit constant methods
124
318
  def year_in_seconds
125
- 365.25 * 24 * 3600 # Considering leap years
319
+ 365.25 * 24 * 3600
126
320
  end
127
321
 
128
322
  def month_in_seconds
129
- 30 * 24 * 3600 # Average 30 days
323
+ 30 * 24 * 3600
130
324
  end
131
325
 
132
326
  def week_in_seconds
@@ -145,7 +339,6 @@ module RailsDiffTime
145
339
  60
146
340
  end
147
341
 
148
- # Threshold methods
149
342
  def year_threshold
150
343
  year_in_seconds
151
344
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsDiffTime
4
- VERSION = "0.1.4"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-diff-time
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - dhq_boiler
@@ -10,8 +10,10 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: A Rails gem that provides helper methods to display time differences
13
- in a human-readable format (e.g., '2 hours ago', '3 days later'). Integrates seamlessly
14
- with Rails views and supports various time units from seconds to years.
13
+ in a human-readable format (e.g., '2 hours ago', '3 days later'). Features include
14
+ auto-updating timestamps every minute without page reload, no JavaScript imports
15
+ required, and seamless integration with Rails views. Supports various time units
16
+ from seconds to years.
15
17
  email:
16
18
  - dhq_boiler@live.jp
17
19
  executables: []
@@ -44,7 +46,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
44
46
  requirements:
45
47
  - - ">="
46
48
  - !ruby/object:Gem::Version
47
- version: 3.4.5
49
+ version: 3.4.7
48
50
  required_rubygems_version: !ruby/object:Gem::Requirement
49
51
  requirements:
50
52
  - - ">="
@@ -53,5 +55,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
53
55
  requirements: []
54
56
  rubygems_version: 3.6.9
55
57
  specification_version: 4
56
- summary: Rails helper for displaying human-readable time differences
58
+ summary: Rails helper for displaying human-readable time differences with auto-update
57
59
  test_files: []