in_time_scope 0.1.5 → 0.1.7

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.
@@ -21,8 +21,6 @@ module InTimeScope
21
21
  # @option end_at [Boolean] :null Whether the column allows NULL values
22
22
  # (auto-detected from schema if not specified)
23
23
  #
24
- # @param prefix [Boolean] If true, creates +<scope_name>_in_time+ instead of +in_time_<scope_name>+
25
- #
26
24
  # @raise [ColumnNotFoundError] When a specified column doesn't exist (at class load time)
27
25
  # @raise [ConfigurationError] When both columns are nil, or when using start-only/end-only
28
26
  # pattern with a nullable column (at scope call time)
@@ -56,7 +54,7 @@ module InTimeScope
56
54
  # in_time_scope start_at: { column: nil }, end_at: { null: false }
57
55
  # # Also creates: Model.latest_in_time(:foreign_key), Model.earliest_in_time(:foreign_key)
58
56
  #
59
- def in_time_scope(scope_name = :in_time, start_at: {}, end_at: {}, prefix: false)
57
+ def in_time_scope(scope_name = :in_time, start_at: {}, end_at: {})
60
58
  table_column_hash = columns_hash
61
59
  time_column_prefix = scope_name == :in_time ? "" : "#{scope_name}_"
62
60
 
@@ -66,10 +64,8 @@ module InTimeScope
66
64
  start_at_null = fetch_null_option(start_at, start_at_column, table_column_hash)
67
65
  end_at_null = fetch_null_option(end_at, end_at_column, table_column_hash)
68
66
 
69
- scope_method_name = method_name(scope_name, prefix)
70
-
71
67
  define_scope_methods(
72
- scope_method_name,
68
+ scope_name == :in_time ? "" : "_#{scope_name}",
73
69
  start_at_column: start_at_column,
74
70
  start_at_null: start_at_null,
75
71
  end_at_column: end_at_column,
@@ -97,96 +93,97 @@ module InTimeScope
97
93
  column_info.null
98
94
  end
99
95
 
100
- # Generates the method name for the scope
101
- #
102
- # @param scope_name [Symbol] The scope name
103
- # @param prefix [Boolean] Whether to use prefix style
104
- # @return [Symbol] The generated method name
105
- # @api private
106
- def method_name(scope_name, prefix)
107
- return :in_time if scope_name == :in_time
108
-
109
- prefix ? "#{scope_name}_in_time" : "in_time_#{scope_name}"
110
- end
111
-
112
96
  # Defines the appropriate scope methods based on configuration
113
97
  #
114
- # @param scope_method_name [Symbol] The name of the scope method to create
98
+ # @param suffix [String] The suffix for method names ("" or "_#{scope_name}")
115
99
  # @param start_at_column [Symbol, nil] Start column name
116
100
  # @param start_at_null [Boolean, nil] Whether start column allows NULL
117
101
  # @param end_at_column [Symbol, nil] End column name
118
102
  # @param end_at_null [Boolean, nil] Whether end column allows NULL
119
103
  # @return [void]
120
104
  # @api private
121
- def define_scope_methods(scope_method_name, start_at_column:, start_at_null:, end_at_column:, end_at_null:)
105
+ def define_scope_methods(suffix, start_at_column:, start_at_null:, end_at_column:, end_at_null:)
122
106
  # Define class-level scope and instance method
123
107
  if start_at_column.nil? && end_at_column.nil?
124
- define_error_scope_and_method(scope_method_name,
108
+ define_error_scope_and_method(suffix,
125
109
  "At least one of start_at or end_at must be specified")
126
110
  elsif end_at_column.nil?
127
111
  # Start-only pattern (history tracking) - requires non-nullable column
128
112
  if start_at_null
129
- define_error_scope_and_method(scope_method_name,
113
+ define_error_scope_and_method(suffix,
130
114
  "Start-only pattern requires non-nullable column. " \
131
115
  "Set `start_at: { null: false }` or add an end_at column")
132
116
  else
133
- define_start_only_scope(scope_method_name, start_at_column)
134
- define_instance_method(scope_method_name, start_at_column, start_at_null, end_at_column, end_at_null)
117
+ define_start_only_scope(suffix, start_at_column)
118
+ define_instance_method(suffix, start_at_column, start_at_null, end_at_column, end_at_null)
119
+ define_latest_one_scope(suffix, start_at_column)
120
+ define_earliest_one_scope(suffix, start_at_column)
121
+ define_before_scope(suffix, start_at_column, start_at_null)
122
+ define_after_scope(suffix, end_at_column, end_at_null)
123
+ define_out_of_time_scope(suffix)
135
124
  end
136
125
  elsif start_at_column.nil?
137
126
  # End-only pattern (expiration) - requires non-nullable column
138
127
  if end_at_null
139
- define_error_scope_and_method(scope_method_name,
128
+ define_error_scope_and_method(suffix,
140
129
  "End-only pattern requires non-nullable column. " \
141
130
  "Set `end_at: { null: false }` or add a start_at column")
142
131
  else
143
- define_end_only_scope(scope_method_name, end_at_column)
144
- define_instance_method(scope_method_name, start_at_column, start_at_null, end_at_column, end_at_null)
132
+ define_end_only_scope(suffix, end_at_column)
133
+ define_instance_method(suffix, start_at_column, start_at_null, end_at_column, end_at_null)
134
+ define_latest_one_scope(suffix, end_at_column)
135
+ define_earliest_one_scope(suffix, end_at_column)
136
+ define_before_scope(suffix, start_at_column, start_at_null)
137
+ define_after_scope(suffix, end_at_column, end_at_null)
138
+ define_out_of_time_scope(suffix)
145
139
  end
146
140
  else
147
141
  # Both start and end
148
- define_full_scope(scope_method_name, start_at_column, start_at_null, end_at_column, end_at_null)
149
- define_instance_method(scope_method_name, start_at_column, start_at_null, end_at_column, end_at_null)
142
+ define_full_scope(suffix, start_at_column, start_at_null, end_at_column, end_at_null)
143
+ define_instance_method(suffix, start_at_column, start_at_null, end_at_column, end_at_null)
144
+ define_before_scope(suffix, start_at_column, start_at_null)
145
+ define_after_scope(suffix, end_at_column, end_at_null)
146
+ define_out_of_time_scope(suffix)
150
147
  end
151
148
  end
152
149
 
153
150
  # Defines a scope and instance method that raise ConfigurationError
154
151
  #
155
- # @param scope_method_name [Symbol] The name of the scope method
152
+ # @param suffix [String] The suffix for method names
156
153
  # @param message [String] The error message
157
154
  # @return [void]
158
155
  # @api private
159
- def define_error_scope_and_method(scope_method_name, message)
160
- err_message = message
161
-
162
- scope scope_method_name, ->(_time = Time.current) {
163
- raise InTimeScope::ConfigurationError, err_message
164
- }
165
-
166
- define_method("#{scope_method_name}?") do |_time = Time.current|
167
- raise InTimeScope::ConfigurationError, err_message
156
+ def define_error_scope_and_method(suffix, message)
157
+ method_names = [
158
+ :"in_time#{suffix}",
159
+ :"before_in_time#{suffix}",
160
+ :"after_in_time#{suffix}",
161
+ :"out_of_time#{suffix}"
162
+ ]
163
+
164
+ method_names.each do |method_name|
165
+ scope method_name, ->(_time = Time.current) {
166
+ raise InTimeScope::ConfigurationError, message
167
+ }
168
+
169
+ define_method("#{method_name}?") do |_time = Time.current|
170
+ raise InTimeScope::ConfigurationError, message
171
+ end
168
172
  end
169
173
  end
170
174
 
171
175
  # Defines a start-only scope (for history tracking pattern)
172
176
  #
173
- # @param scope_method_name [Symbol] The name of the scope method
177
+ # @param suffix [String] The suffix for method names
174
178
  # @param column [Symbol] The start column name
175
179
  # @return [void]
176
180
  # @api private
177
- def define_start_only_scope(scope_method_name, column)
178
- col = column
179
-
181
+ def define_start_only_scope(suffix, column)
180
182
  # Simple scope - WHERE only, no ORDER BY
181
183
  # Users can add .order(start_at: :desc) externally if needed
182
- scope scope_method_name, ->(time = Time.current) {
183
- where(col => ..time)
184
+ scope :"in_time#{suffix}", ->(time = Time.current) {
185
+ where(column => ..time)
184
186
  }
185
-
186
- # Efficient scope for has_one + includes using NOT EXISTS subquery
187
- # Usage: has_one :current_price, -> { latest_in_time(:user_id) }, class_name: 'Price'
188
- define_latest_one_scope(scope_method_name, column)
189
- define_earliest_one_scope(scope_method_name, column)
190
187
  end
191
188
 
192
189
  # Defines the latest_in_time scope using NOT EXISTS subquery
@@ -194,7 +191,13 @@ module InTimeScope
194
191
  # This scope efficiently finds the latest record per foreign key,
195
192
  # suitable for use with has_one associations and includes.
196
193
  #
197
- # @param scope_method_name [Symbol] The base scope method name
194
+ # @note When multiple records share the same timestamp for a given foreign key,
195
+ # all of them will be returned. This is safe for +has_one+ associations
196
+ # (ActiveRecord picks one), but callers using this as a standalone scope
197
+ # should be aware that the result may contain multiple records per foreign key
198
+ # in the case of timestamp ties.
199
+ #
200
+ # @param suffix [String] The suffix for method names
198
201
  # @param column [Symbol] The timestamp column name
199
202
  # @return [void]
200
203
  #
@@ -202,25 +205,39 @@ module InTimeScope
202
205
  # has_one :current_price, -> { latest_in_time(:user_id) }, class_name: 'Price'
203
206
  #
204
207
  # @api private
205
- def define_latest_one_scope(scope_method_name, column)
206
- latest_method_name = scope_method_name == :in_time ? :latest_in_time : :"latest_#{scope_method_name}"
207
- col = column
208
-
208
+ def define_latest_one_scope(suffix, column)
209
209
  # NOT EXISTS approach: select records where no later record exists for the same foreign key
210
- scope latest_method_name, ->(foreign_key, time = Time.current) {
210
+ scope :"latest_in_time#{suffix}", ->(foreign_key, time = Time.current) {
211
211
  p2 = arel_table.alias("p2")
212
212
 
213
213
  subquery = Arel::SelectManager.new(arel_table)
214
214
  .from(p2)
215
215
  .project(Arel.sql("1"))
216
216
  .where(p2[foreign_key].eq(arel_table[foreign_key]))
217
- .where(p2[col].lteq(time))
218
- .where(p2[col].gt(arel_table[col]))
217
+ .where(p2[column].lteq(time))
218
+ .where(p2[column].gt(arel_table[column]))
219
219
  .where(p2[:id].not_eq(arel_table[:id]))
220
220
 
221
+ # Propagate simple equality conditions from the current scope into the NOT EXISTS
222
+ # subquery. This ensures that chained scopes like `.approved.latest_in_time(:user_id)`
223
+ # only consider approved records when searching for "a newer record exists",
224
+ # preventing a newer rejected record from shadowing the latest approved one.
225
+ where_values_hash.each do |col, val|
226
+ next if col.to_s == column.to_s # time column already handled above
227
+ next if col.to_s == foreign_key.to_s # foreign key already handled above
228
+
229
+ subquery = if val.nil?
230
+ subquery.where(p2[col].eq(nil))
231
+ elsif val.is_a?(Array)
232
+ subquery.where(p2[col].in(val))
233
+ else
234
+ subquery.where(p2[col].eq(val))
235
+ end
236
+ end
237
+
221
238
  not_exists = Arel::Nodes::Not.new(Arel::Nodes::Exists.new(subquery.ast))
222
239
 
223
- where(col => ..time).where(not_exists)
240
+ where(column => ..time).where(not_exists)
224
241
  }
225
242
  end
226
243
 
@@ -229,7 +246,13 @@ module InTimeScope
229
246
  # This scope efficiently finds the earliest record per foreign key,
230
247
  # suitable for use with has_one associations and includes.
231
248
  #
232
- # @param scope_method_name [Symbol] The base scope method name
249
+ # @note When multiple records share the same timestamp for a given foreign key,
250
+ # all of them will be returned. This is safe for +has_one+ associations
251
+ # (ActiveRecord picks one), but callers using this as a standalone scope
252
+ # should be aware that the result may contain multiple records per foreign key
253
+ # in the case of timestamp ties.
254
+ #
255
+ # @param suffix [String] The suffix for method names
233
256
  # @param column [Symbol] The timestamp column name
234
257
  # @return [void]
235
258
  #
@@ -237,70 +260,73 @@ module InTimeScope
237
260
  # has_one :first_price, -> { earliest_in_time(:user_id) }, class_name: 'Price'
238
261
  #
239
262
  # @api private
240
- def define_earliest_one_scope(scope_method_name, column)
241
- earliest_method_name = scope_method_name == :in_time ? :earliest_in_time : :"earliest_#{scope_method_name}"
242
- col = column
243
-
263
+ def define_earliest_one_scope(suffix, column)
244
264
  # NOT EXISTS approach: select records where no earlier record exists for the same foreign key
245
- scope earliest_method_name, ->(foreign_key, time = Time.current) {
265
+ scope :"earliest_in_time#{suffix}", ->(foreign_key, time = Time.current) {
246
266
  p2 = arel_table.alias("p2")
247
267
 
248
268
  subquery = Arel::SelectManager.new(arel_table)
249
269
  .from(p2)
250
270
  .project(Arel.sql("1"))
251
271
  .where(p2[foreign_key].eq(arel_table[foreign_key]))
252
- .where(p2[col].lteq(time))
253
- .where(p2[col].lt(arel_table[col]))
272
+ .where(p2[column].lteq(time))
273
+ .where(p2[column].lt(arel_table[column]))
254
274
  .where(p2[:id].not_eq(arel_table[:id]))
255
275
 
276
+ # Propagate simple equality conditions from the current scope into the NOT EXISTS
277
+ # subquery (same reasoning as define_latest_one_scope).
278
+ where_values_hash.each do |col, val|
279
+ next if col.to_s == column.to_s
280
+ next if col.to_s == foreign_key.to_s
281
+
282
+ subquery = if val.nil?
283
+ subquery.where(p2[col].eq(nil))
284
+ elsif val.is_a?(Array)
285
+ subquery.where(p2[col].in(val))
286
+ else
287
+ subquery.where(p2[col].eq(val))
288
+ end
289
+ end
290
+
256
291
  not_exists = Arel::Nodes::Not.new(Arel::Nodes::Exists.new(subquery.ast))
257
292
 
258
- where(col => ..time).where(not_exists)
293
+ where(column => ..time).where(not_exists)
259
294
  }
260
295
  end
261
296
 
262
297
  # Defines an end-only scope (for expiration pattern)
263
298
  #
264
- # @param scope_method_name [Symbol] The name of the scope method
299
+ # @param suffix [String] The suffix for method names
265
300
  # @param column [Symbol] The end column name
266
301
  # @return [void]
267
302
  # @api private
268
- def define_end_only_scope(scope_method_name, column)
269
- col = column
270
-
271
- scope scope_method_name, ->(time = Time.current) {
272
- where.not(col => ..time)
303
+ def define_end_only_scope(suffix, column)
304
+ scope :"in_time#{suffix}", ->(time = Time.current) {
305
+ where.not(column => ..time)
273
306
  }
274
-
275
- # Efficient scope for has_one + includes using NOT EXISTS subquery
276
- define_latest_one_scope(scope_method_name, column)
277
- define_earliest_one_scope(scope_method_name, column)
278
307
  end
279
308
 
280
309
  # Defines a full scope with both start and end columns
281
310
  #
282
- # @param scope_method_name [Symbol] The name of the scope method
311
+ # @param suffix [String] The suffix for method names
283
312
  # @param start_column [Symbol] The start column name
284
313
  # @param start_null [Boolean] Whether start column allows NULL
285
314
  # @param end_column [Symbol] The end column name
286
315
  # @param end_null [Boolean] Whether end column allows NULL
287
316
  # @return [void]
288
317
  # @api private
289
- def define_full_scope(scope_method_name, start_column, start_null, end_column, end_null)
290
- s_col = start_column
291
- e_col = end_column
292
-
293
- scope scope_method_name, ->(time = Time.current) {
318
+ def define_full_scope(suffix, start_column, start_null, end_column, end_null)
319
+ scope :"in_time#{suffix}", ->(time = Time.current) {
294
320
  start_scope = if start_null
295
- where(s_col => nil).or(where(s_col => ..time))
321
+ where(start_column => nil).or(where(start_column => ..time))
296
322
  else
297
- where(s_col => ..time)
323
+ where(start_column => ..time)
298
324
  end
299
325
 
300
326
  end_scope = if end_null
301
- where(e_col => nil).or(where.not(e_col => ..time))
327
+ where(end_column => nil).or(where.not(end_column => ..time))
302
328
  else
303
- where.not(e_col => ..time)
329
+ where.not(end_column => ..time)
304
330
  end
305
331
 
306
332
  start_scope.merge(end_scope)
@@ -313,15 +339,15 @@ module InTimeScope
313
339
 
314
340
  # Defines the instance method to check if a record is within the time window
315
341
  #
316
- # @param scope_method_name [Symbol] The name of the scope method
342
+ # @param suffix [String] The suffix for method names
317
343
  # @param start_column [Symbol, nil] The start column name
318
344
  # @param start_null [Boolean, nil] Whether start column allows NULL
319
345
  # @param end_column [Symbol, nil] The end column name
320
346
  # @param end_null [Boolean, nil] Whether end column allows NULL
321
347
  # @return [void]
322
348
  # @api private
323
- def define_instance_method(scope_method_name, start_column, start_null, end_column, end_null)
324
- define_method("#{scope_method_name}?") do |time = Time.current|
349
+ def define_instance_method(suffix, start_column, start_null, end_column, end_null)
350
+ define_method("in_time#{suffix}?") do |time = Time.current|
325
351
  start_ok = if start_column.nil?
326
352
  true
327
353
  elsif start_null
@@ -341,5 +367,71 @@ module InTimeScope
341
367
  start_ok && end_ok
342
368
  end
343
369
  end
370
+
371
+ # Defines before_in_time scope (records not yet started: start_at > time)
372
+ #
373
+ # @param suffix [String] The suffix for method names
374
+ # @param start_column [Symbol, nil] The start column name
375
+ # @param start_null [Boolean, nil] Whether start column allows NULL
376
+ # @return [void]
377
+ # @api private
378
+ def define_before_scope(suffix, start_column, start_null)
379
+ # No start column means always started (never before)
380
+ # start_at > time means not yet started
381
+ # NULL start_at is treated as "already started" (not before)
382
+ scope :"before_in_time#{suffix}", ->(time = Time.current) {
383
+ start_column.nil? ? none : where.not(start_column => ..time)
384
+ }
385
+
386
+ define_method("before_in_time#{suffix}?") do |time = Time.current|
387
+ return false if start_column.nil?
388
+
389
+ val = send(start_column)
390
+ return false if val.nil? && start_null
391
+
392
+ val > time
393
+ end
394
+ end
395
+
396
+ # Defines after_in_time scope (records already ended: end_at <= time)
397
+ #
398
+ # @param suffix [String] The suffix for method names
399
+ # @param end_column [Symbol, nil] The end column name
400
+ # @param end_null [Boolean, nil] Whether end column allows NULL
401
+ # @return [void]
402
+ # @api private
403
+ def define_after_scope(suffix, end_column, end_null)
404
+ # No end column means never ends (never after)
405
+ # end_at <= time means already ended
406
+ # NULL end_at is treated as "never ends" (not after)
407
+ scope :"after_in_time#{suffix}", ->(time = Time.current) {
408
+ end_column.nil? ? none : where(end_column => ..time)
409
+ }
410
+
411
+ define_method("after_in_time#{suffix}?") do |time = Time.current|
412
+ return false if end_column.nil?
413
+
414
+ val = send(end_column)
415
+ return false if val.nil? && end_null
416
+
417
+ val <= time
418
+ end
419
+ end
420
+
421
+ # Defines out_of_time scope (records outside time window: before OR after)
422
+ #
423
+ # @param suffix [String] The suffix for method names
424
+ # @return [void]
425
+ # @api private
426
+ def define_out_of_time_scope(suffix)
427
+ # out_of_time = before_in_time OR after_in_time
428
+ scope :"out_of_time#{suffix}", ->(time = Time.current) {
429
+ send(:"before_in_time#{suffix}", time).or(send(:"after_in_time#{suffix}", time))
430
+ }
431
+
432
+ define_method("out_of_time#{suffix}?") do |time = Time.current|
433
+ send("before_in_time#{suffix}?", time) || send("after_in_time#{suffix}?", time)
434
+ end
435
+ end
344
436
  end
345
437
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module InTimeScope
4
4
  # The current version of the InTimeScope gem
5
- VERSION = "0.1.5"
5
+ VERSION = "0.1.7"
6
6
  end
data/rulesync.jsonc ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/dyoshikawa/rulesync/refs/heads/main/config-schema.json",
3
+ "targets": ["claudecode"],
4
+ "features": ["rules", "commands"],
5
+ "baseDirs": ["."]
6
+ }
@@ -36,15 +36,13 @@ module InTimeScope
36
36
  # @param scope_name [Symbol] The name of the scope (default: :in_time)
37
37
  # @param start_at [Hash] Configuration for the start column
38
38
  # @param end_at [Hash] Configuration for the end column
39
- # @param prefix [Boolean] If true, creates prefix-style method names
40
39
  # @return [void]
41
40
  # @raise [ColumnNotFoundError] When a specified column doesn't exist
42
41
  # @raise [ConfigurationError] When the configuration is invalid (at scope call time)
43
42
  def in_time_scope: (
44
43
  ?Symbol scope_name,
45
44
  ?start_at: start_at_config,
46
- ?end_at: end_at_config,
47
- ?prefix: bool
45
+ ?end_at: end_at_config
48
46
  ) -> void
49
47
 
50
48
  private
@@ -52,30 +50,42 @@ module InTimeScope
52
50
  # Private implementation methods
53
51
  # These use ActiveRecord internals and are typed as untyped for flexibility
54
52
  def fetch_null_option: (untyped config, untyped column, untyped table_column_hash) -> untyped
55
- def method_name: (Symbol scope_name, bool prefix) -> (Symbol | String)
56
- def define_scope_methods: (untyped scope_method_name, start_at_column: untyped, start_at_null: untyped, end_at_column: untyped, end_at_null: untyped) -> void
57
- def define_error_scope_and_method: (untyped scope_method_name, String message) -> void
58
- def define_start_only_scope: (untyped scope_method_name, Symbol column) -> void
59
- def define_latest_one_scope: (untyped scope_method_name, Symbol column) -> void
60
- def define_earliest_one_scope: (untyped scope_method_name, Symbol column) -> void
61
- def define_end_only_scope: (untyped scope_method_name, Symbol column) -> void
62
- def define_full_scope: (untyped scope_method_name, Symbol start_column, untyped start_null, Symbol end_column, untyped end_null) -> void
63
- def define_instance_method: (untyped scope_method_name, Symbol? start_column, untyped start_null, Symbol? end_column, untyped end_null) -> void
53
+ def define_scope_methods: (String suffix, start_at_column: untyped, start_at_null: untyped, end_at_column: untyped, end_at_null: untyped) -> void
54
+ def define_error_scope_and_method: (String suffix, String message) -> void
55
+ def define_start_only_scope: (String suffix, Symbol column) -> void
56
+ def define_latest_one_scope: (String suffix, Symbol column) -> void
57
+ def define_earliest_one_scope: (String suffix, Symbol column) -> void
58
+ def define_end_only_scope: (String suffix, Symbol column) -> void
59
+ def define_full_scope: (String suffix, Symbol start_column, untyped start_null, Symbol end_column, untyped end_null) -> void
60
+ def define_instance_method: (String suffix, Symbol? start_column, untyped start_null, Symbol? end_column, untyped end_null) -> void
61
+ def define_before_scope: (String suffix, Symbol? start_column, untyped start_null) -> void
62
+ def define_after_scope: (String suffix, Symbol? end_column, untyped end_null) -> void
63
+ def define_out_of_time_scope: (String suffix) -> void
64
64
  end
65
65
  end
66
66
 
67
67
  # Generated scope methods (dynamically defined)
68
68
  # When you call `in_time_scope` on a model, it creates these methods:
69
69
  #
70
- # Class methods:
70
+ # Class methods (primary - records in time window):
71
71
  # Model.in_time(time = Time.current) -> ActiveRecord::Relation
72
72
  # Model.in_time_<name>(time = Time.current) -> ActiveRecord::Relation (for named scopes)
73
73
  # Model.latest_in_time(foreign_key, time = Time.current) -> ActiveRecord::Relation (start-only/end-only)
74
74
  # Model.earliest_in_time(foreign_key, time = Time.current) -> ActiveRecord::Relation (start-only/end-only)
75
75
  #
76
- # Instance methods:
76
+ # Class methods (inverse - records outside time window):
77
+ # Model.before_in_time(time = Time.current) -> ActiveRecord::Relation (not yet started)
78
+ # Model.after_in_time(time = Time.current) -> ActiveRecord::Relation (already ended)
79
+ # Model.out_of_time(time = Time.current) -> ActiveRecord::Relation (before OR after)
80
+ #
81
+ # Instance methods (primary):
77
82
  # model.in_time?(time = Time.current) -> bool
78
83
  # model.in_time_<name>?(time = Time.current) -> bool (for named scopes)
84
+ #
85
+ # Instance methods (inverse):
86
+ # model.before_in_time?(time = Time.current) -> bool
87
+ # model.after_in_time?(time = Time.current) -> bool
88
+ # model.out_of_time?(time = Time.current) -> bool
79
89
 
80
90
  # Extend ActiveRecord::Base to include InTimeScope
81
91
  class ActiveRecord::Base
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: in_time_scope
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - kyohah
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: '0'
18
+ version: '6.1'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: '0'
25
+ version: '6.1'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: irb
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -121,6 +121,20 @@ dependencies:
121
121
  - - ">="
122
122
  - !ruby/object:Gem::Version
123
123
  version: '0'
124
+ - !ruby/object:Gem::Dependency
125
+ name: timecop
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ type: :development
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
124
138
  - !ruby/object:Gem::Dependency
125
139
  name: yard
126
140
  requirement: !ruby/object:Gem::Requirement
@@ -143,17 +157,40 @@ extensions: []
143
157
  extra_rdoc_files: []
144
158
  files:
145
159
  - ".rubocop.yml"
160
+ - ".rulesync/commands/translate-readme.md"
161
+ - ".rulesync/rules/project.md"
146
162
  - CHANGELOG.md
147
- - CLAUDE.md
148
163
  - CODE_OF_CONDUCT.md
149
164
  - LICENSE.txt
150
165
  - README.md
151
166
  - Rakefile
152
167
  - Steepfile
168
+ - docs/book.toml
169
+ - docs/de/SUMMARY.md
170
+ - docs/de/index.md
171
+ - docs/de/point-system.md
172
+ - docs/de/user-name-history.md
173
+ - docs/fr/SUMMARY.md
174
+ - docs/fr/index.md
175
+ - docs/fr/point-system.md
176
+ - docs/fr/user-name-history.md
177
+ - docs/ja/SUMMARY.md
178
+ - docs/ja/index.md
179
+ - docs/ja/point-system.md
180
+ - docs/ja/user-name-history.md
181
+ - docs/src/SUMMARY.md
182
+ - docs/src/index.md
183
+ - docs/src/point-system.md
184
+ - docs/src/user-name-history.md
185
+ - docs/zh/SUMMARY.md
186
+ - docs/zh/index.md
187
+ - docs/zh/point-system.md
188
+ - docs/zh/user-name-history.md
153
189
  - lib/in_time_scope.rb
154
190
  - lib/in_time_scope/class_methods.rb
155
191
  - lib/in_time_scope/version.rb
156
192
  - rbs_collection.yaml
193
+ - rulesync.jsonc
157
194
  - sig/in_time_scope.rbs
158
195
  homepage: https://github.com/kyohah/in_time_scope
159
196
  licenses: