in_time_scope 0.1.4 → 0.1.6
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 +4 -4
- data/.rubocop.yml +2 -2
- data/.rulesync/commands/translate-readme.md +46 -0
- data/{CLAUDE.md → .rulesync/rules/project.md} +23 -7
- data/README.md +104 -223
- data/Steepfile +25 -0
- data/docs/book.toml +14 -0
- data/docs/de/SUMMARY.md +5 -0
- data/docs/de/index.md +192 -0
- data/docs/de/point-system.md +295 -0
- data/docs/de/user-name-history.md +164 -0
- data/docs/fr/SUMMARY.md +5 -0
- data/docs/fr/index.md +192 -0
- data/docs/fr/point-system.md +295 -0
- data/docs/fr/user-name-history.md +164 -0
- data/docs/ja/SUMMARY.md +5 -0
- data/docs/ja/index.md +192 -0
- data/docs/ja/point-system.md +295 -0
- data/docs/ja/user-name-history.md +164 -0
- data/docs/src/SUMMARY.md +5 -0
- data/docs/src/index.md +194 -0
- data/docs/src/point-system.md +295 -0
- data/docs/src/user-name-history.md +164 -0
- data/docs/zh/SUMMARY.md +5 -0
- data/docs/zh/index.md +192 -0
- data/docs/zh/point-system.md +295 -0
- data/docs/zh/user-name-history.md +164 -0
- data/lib/in_time_scope/class_methods.rb +393 -0
- data/lib/in_time_scope/version.rb +2 -1
- data/lib/in_time_scope.rb +115 -213
- data/rbs_collection.yaml +16 -0
- data/rulesync.jsonc +6 -0
- data/sig/in_time_scope.rbs +90 -1
- metadata +75 -7
data/lib/in_time_scope.rb
CHANGED
|
@@ -2,227 +2,129 @@
|
|
|
2
2
|
|
|
3
3
|
require "active_record"
|
|
4
4
|
require_relative "in_time_scope/version"
|
|
5
|
-
|
|
5
|
+
require_relative "in_time_scope/class_methods"
|
|
6
|
+
|
|
7
|
+
# InTimeScope provides time-window scopes for ActiveRecord models.
|
|
8
|
+
#
|
|
9
|
+
# It allows you to easily query records that fall within specific time periods,
|
|
10
|
+
# with support for nullable columns, custom column names, and multiple scopes per model.
|
|
11
|
+
#
|
|
12
|
+
# InTimeScope is automatically included in ActiveRecord::Base, so you can use
|
|
13
|
+
# +in_time_scope+ directly in your models without explicit include.
|
|
14
|
+
#
|
|
15
|
+
# == Basic usage
|
|
16
|
+
#
|
|
17
|
+
# class Event < ActiveRecord::Base
|
|
18
|
+
# in_time_scope
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# Event.in_time # Records active at current time
|
|
22
|
+
# Event.in_time(some_time) # Records active at specific time
|
|
23
|
+
# event.in_time? # Check if record is active now
|
|
24
|
+
#
|
|
25
|
+
# == Patterns
|
|
26
|
+
#
|
|
27
|
+
# === Full pattern (both start and end)
|
|
28
|
+
#
|
|
29
|
+
# Default pattern with both +start_at+ and +end_at+ columns.
|
|
30
|
+
# Supports nullable columns (NULL means "no limit").
|
|
31
|
+
#
|
|
32
|
+
# class Event < ActiveRecord::Base
|
|
33
|
+
# in_time_scope # Uses start_at and end_at columns
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
# === Start-only pattern (history tracking)
|
|
37
|
+
#
|
|
38
|
+
# For versioned records where each row is valid from +start_at+ until the next row.
|
|
39
|
+
# Requires non-nullable column.
|
|
40
|
+
#
|
|
41
|
+
# class Price < ActiveRecord::Base
|
|
42
|
+
# in_time_scope start_at: { null: false }, end_at: { column: nil }
|
|
43
|
+
# end
|
|
44
|
+
#
|
|
45
|
+
# # Additional scopes created:
|
|
46
|
+
# Price.latest_in_time(:user_id) # Latest record per user
|
|
47
|
+
# Price.earliest_in_time(:user_id) # Earliest record per user
|
|
48
|
+
#
|
|
49
|
+
# === End-only pattern (expiration)
|
|
50
|
+
#
|
|
51
|
+
# For records that are always active until they expire.
|
|
52
|
+
# Requires non-nullable column.
|
|
53
|
+
#
|
|
54
|
+
# class Coupon < ActiveRecord::Base
|
|
55
|
+
# in_time_scope start_at: { column: nil }, end_at: { null: false }
|
|
56
|
+
# end
|
|
57
|
+
#
|
|
58
|
+
# == Using with has_one associations
|
|
59
|
+
#
|
|
60
|
+
# The +latest_in_time+ and +earliest_in_time+ scopes are optimized for
|
|
61
|
+
# +has_one+ associations with +includes+, using NOT EXISTS subqueries.
|
|
62
|
+
#
|
|
63
|
+
# class Price < ActiveRecord::Base
|
|
64
|
+
# belongs_to :user
|
|
65
|
+
# in_time_scope start_at: { null: false }, end_at: { column: nil }
|
|
66
|
+
# end
|
|
67
|
+
#
|
|
68
|
+
# class User < ActiveRecord::Base
|
|
69
|
+
# has_many :prices
|
|
70
|
+
#
|
|
71
|
+
# # Efficient: uses NOT EXISTS subquery
|
|
72
|
+
# has_one :current_price,
|
|
73
|
+
# -> { latest_in_time(:user_id) },
|
|
74
|
+
# class_name: "Price"
|
|
75
|
+
#
|
|
76
|
+
# has_one :first_price,
|
|
77
|
+
# -> { earliest_in_time(:user_id) },
|
|
78
|
+
# class_name: "Price"
|
|
79
|
+
# end
|
|
80
|
+
#
|
|
81
|
+
# # Works efficiently with includes
|
|
82
|
+
# User.includes(:current_price).each do |user|
|
|
83
|
+
# puts user.current_price&.amount
|
|
84
|
+
# end
|
|
85
|
+
#
|
|
86
|
+
# == Named scopes
|
|
87
|
+
#
|
|
88
|
+
# Define multiple time windows per model using named scopes.
|
|
89
|
+
#
|
|
90
|
+
# class Article < ActiveRecord::Base
|
|
91
|
+
# in_time_scope :published # Uses published_start_at, published_end_at
|
|
92
|
+
# in_time_scope :featured # Uses featured_start_at, featured_end_at
|
|
93
|
+
# end
|
|
94
|
+
#
|
|
95
|
+
# Article.in_time_published
|
|
96
|
+
# Article.in_time_featured
|
|
97
|
+
# article.in_time_published?
|
|
98
|
+
#
|
|
99
|
+
# == Custom columns
|
|
100
|
+
#
|
|
101
|
+
# class Event < ActiveRecord::Base
|
|
102
|
+
# in_time_scope start_at: { column: :available_at },
|
|
103
|
+
# end_at: { column: :expired_at }
|
|
104
|
+
# end
|
|
105
|
+
#
|
|
106
|
+
# == Error handling
|
|
107
|
+
#
|
|
108
|
+
# - ColumnNotFoundError: Raised at class load time if column doesn't exist
|
|
109
|
+
# - ConfigurationError: Raised at scope call time for invalid configurations
|
|
110
|
+
#
|
|
111
|
+
# @see ClassMethods#in_time_scope
|
|
6
112
|
module InTimeScope
|
|
113
|
+
# Base error class for InTimeScope errors
|
|
7
114
|
class Error < StandardError; end
|
|
115
|
+
|
|
116
|
+
# Raised when a specified column does not exist on the table
|
|
117
|
+
# @note This error is raised at class load time
|
|
8
118
|
class ColumnNotFoundError < Error; end
|
|
119
|
+
|
|
120
|
+
# Raised when the scope configuration is invalid
|
|
121
|
+
# @note This error is raised when the scope or instance method is called
|
|
9
122
|
class ConfigurationError < Error; end
|
|
10
123
|
|
|
124
|
+
# @api private
|
|
11
125
|
def self.included(model)
|
|
12
126
|
model.extend ClassMethods
|
|
13
127
|
end
|
|
14
|
-
|
|
15
|
-
module ClassMethods
|
|
16
|
-
def in_time_scope(scope_name = :in_time, start_at: {}, end_at: {}, prefix: false)
|
|
17
|
-
table_column_hash = columns_hash
|
|
18
|
-
time_column_prefix = scope_name == :in_time ? "" : "#{scope_name}_"
|
|
19
|
-
|
|
20
|
-
start_at_column = start_at.fetch(:column, :"#{time_column_prefix}start_at")
|
|
21
|
-
end_at_column = end_at.fetch(:column, :"#{time_column_prefix}end_at")
|
|
22
|
-
|
|
23
|
-
start_at_null = fetch_null_option(start_at, start_at_column, table_column_hash)
|
|
24
|
-
end_at_null = fetch_null_option(end_at, end_at_column, table_column_hash)
|
|
25
|
-
|
|
26
|
-
scope_method_name = method_name(scope_name, prefix)
|
|
27
|
-
|
|
28
|
-
define_scope_methods(scope_method_name, start_at_column:, start_at_null:, end_at_column:, end_at_null:)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
private
|
|
32
|
-
|
|
33
|
-
def fetch_null_option(config, column, table_column_hash)
|
|
34
|
-
return nil if column.nil?
|
|
35
|
-
return config[:null] if config.key?(:null)
|
|
36
|
-
|
|
37
|
-
column_info = table_column_hash[column.to_s]
|
|
38
|
-
raise ColumnNotFoundError, "Column '#{column}' does not exist on table '#{table_name}'" if column_info.nil?
|
|
39
|
-
|
|
40
|
-
column_info.null
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def method_name(scope_name, prefix)
|
|
44
|
-
return :in_time if scope_name == :in_time
|
|
45
|
-
|
|
46
|
-
prefix ? "#{scope_name}_in_time" : "in_time_#{scope_name}"
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def define_scope_methods(scope_method_name, start_at_column:, start_at_null:, end_at_column:, end_at_null:)
|
|
50
|
-
# Define class-level scope
|
|
51
|
-
if start_at_column.nil? && end_at_column.nil?
|
|
52
|
-
# Both disabled - return all
|
|
53
|
-
raise ConfigurationError, "At least one of start_at or end_at must be specified"
|
|
54
|
-
elsif end_at_column.nil?
|
|
55
|
-
# Start-only pattern (history tracking)
|
|
56
|
-
define_start_only_scope(scope_method_name, start_at_column, start_at_null)
|
|
57
|
-
elsif start_at_column.nil?
|
|
58
|
-
# End-only pattern (expiration)
|
|
59
|
-
define_end_only_scope(scope_method_name, end_at_column, end_at_null)
|
|
60
|
-
else
|
|
61
|
-
# Both start and end
|
|
62
|
-
define_full_scope(scope_method_name, start_at_column, start_at_null, end_at_column, end_at_null)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Define instance method
|
|
66
|
-
define_instance_method(scope_method_name, start_at_column, start_at_null, end_at_column, end_at_null)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def define_start_only_scope(scope_method_name, start_column, start_null)
|
|
70
|
-
# Simple scope - WHERE only, no ORDER BY
|
|
71
|
-
# Users can add .order(start_at: :desc) externally if needed
|
|
72
|
-
if start_null
|
|
73
|
-
scope scope_method_name, ->(time = Time.current) {
|
|
74
|
-
where(arel_table[start_column].eq(nil).or(arel_table[start_column].lteq(time)))
|
|
75
|
-
}
|
|
76
|
-
else
|
|
77
|
-
scope scope_method_name, ->(time = Time.current) {
|
|
78
|
-
where(arel_table[start_column].lteq(time))
|
|
79
|
-
}
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# Efficient scope for has_one + includes using NOT EXISTS subquery
|
|
83
|
-
# Usage: has_one :current_price, -> { latest_in_time(:user_id) }, class_name: 'Price'
|
|
84
|
-
define_latest_one_scope(scope_method_name, start_column, start_null)
|
|
85
|
-
define_earliest_one_scope(scope_method_name, start_column, start_null)
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def define_latest_one_scope(scope_method_name, start_column, start_null)
|
|
89
|
-
latest_method_name = scope_method_name == :in_time ? :latest_in_time : :"latest_#{scope_method_name}"
|
|
90
|
-
tbl = table_name
|
|
91
|
-
col = start_column
|
|
92
|
-
|
|
93
|
-
# NOT EXISTS approach: select records where no later record exists for the same foreign key
|
|
94
|
-
# SELECT * FROM prices p1 WHERE start_at <= ? AND NOT EXISTS (
|
|
95
|
-
# SELECT 1 FROM prices p2 WHERE p2.user_id = p1.user_id
|
|
96
|
-
# AND p2.start_at <= ? AND p2.start_at > p1.start_at
|
|
97
|
-
# )
|
|
98
|
-
scope latest_method_name, ->(foreign_key, time = Time.current) {
|
|
99
|
-
fk = foreign_key
|
|
100
|
-
|
|
101
|
-
not_exists_sql = if start_null
|
|
102
|
-
<<~SQL.squish
|
|
103
|
-
NOT EXISTS (
|
|
104
|
-
SELECT 1 FROM #{tbl} p2
|
|
105
|
-
WHERE p2.#{fk} = #{tbl}.#{fk}
|
|
106
|
-
AND (p2.#{col} IS NULL OR p2.#{col} <= ?)
|
|
107
|
-
AND (p2.#{col} IS NULL OR p2.#{col} > #{tbl}.#{col} OR #{tbl}.#{col} IS NULL)
|
|
108
|
-
AND p2.id != #{tbl}.id
|
|
109
|
-
)
|
|
110
|
-
SQL
|
|
111
|
-
else
|
|
112
|
-
<<~SQL.squish
|
|
113
|
-
NOT EXISTS (
|
|
114
|
-
SELECT 1 FROM #{tbl} p2
|
|
115
|
-
WHERE p2.#{fk} = #{tbl}.#{fk}
|
|
116
|
-
AND p2.#{col} <= ?
|
|
117
|
-
AND p2.#{col} > #{tbl}.#{col}
|
|
118
|
-
)
|
|
119
|
-
SQL
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
base_condition = if start_null
|
|
123
|
-
where(arel_table[col].eq(nil).or(arel_table[col].lteq(time)))
|
|
124
|
-
else
|
|
125
|
-
where(arel_table[col].lteq(time))
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
base_condition.where(not_exists_sql, time)
|
|
129
|
-
}
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def define_earliest_one_scope(scope_method_name, start_column, start_null)
|
|
133
|
-
earliest_method_name = scope_method_name == :in_time ? :earliest_in_time : :"earliest_#{scope_method_name}"
|
|
134
|
-
tbl = table_name
|
|
135
|
-
col = start_column
|
|
136
|
-
scope earliest_method_name, ->(foreign_key, time = Time.current) {
|
|
137
|
-
fk = foreign_key
|
|
138
|
-
not_exists_sql = if start_null
|
|
139
|
-
<<~SQL.squish
|
|
140
|
-
NOT EXISTS (
|
|
141
|
-
SELECT 1 FROM #{tbl} p2
|
|
142
|
-
WHERE p2.#{fk} = #{tbl}.#{fk}
|
|
143
|
-
AND (p2.#{col} IS NULL OR p2.#{col} <= ?)
|
|
144
|
-
AND (p2.#{col} IS NULL OR p2.#{col} < #{tbl}.#{col} OR #{tbl}.#{col} IS NULL)
|
|
145
|
-
AND p2.id != #{tbl}.id
|
|
146
|
-
)
|
|
147
|
-
SQL
|
|
148
|
-
else
|
|
149
|
-
<<~SQL.squish
|
|
150
|
-
NOT EXISTS (
|
|
151
|
-
SELECT 1 FROM #{tbl} p2
|
|
152
|
-
WHERE p2.#{fk} = #{tbl}.#{fk}
|
|
153
|
-
AND p2.#{col} <= ?
|
|
154
|
-
AND p2.#{col} < #{tbl}.#{col}
|
|
155
|
-
)
|
|
156
|
-
SQL
|
|
157
|
-
end
|
|
158
|
-
base_condition = if start_null
|
|
159
|
-
where(arel_table[col].eq(nil).or(arel_table[col].lteq(time)))
|
|
160
|
-
else
|
|
161
|
-
where(arel_table[col].lteq(time))
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
base_condition.where(not_exists_sql, time)
|
|
165
|
-
}
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def define_end_only_scope(scope_method_name, end_column, end_null)
|
|
169
|
-
if end_null
|
|
170
|
-
scope scope_method_name, ->(time = Time.current) {
|
|
171
|
-
where(arel_table[end_column].eq(nil).or(arel_table[end_column].gt(time)))
|
|
172
|
-
}
|
|
173
|
-
else
|
|
174
|
-
scope scope_method_name, ->(time = Time.current) {
|
|
175
|
-
where(arel_table[end_column].gt(time))
|
|
176
|
-
}
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
define_latest_one_scope(scope_method_name, end_column, end_null)
|
|
180
|
-
define_earliest_one_scope(scope_method_name, end_column, end_null)
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
def define_full_scope(scope_method_name, start_column, start_null, end_column, end_null)
|
|
184
|
-
scope scope_method_name, ->(time = Time.current) {
|
|
185
|
-
start_condition = if start_null
|
|
186
|
-
arel_table[start_column].eq(nil).or(arel_table[start_column].lteq(time))
|
|
187
|
-
else
|
|
188
|
-
arel_table[start_column].lteq(time)
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
end_condition = if end_null
|
|
192
|
-
arel_table[end_column].eq(nil).or(arel_table[end_column].gt(time))
|
|
193
|
-
else
|
|
194
|
-
arel_table[end_column].gt(time)
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
where(start_condition).where(end_condition)
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
define_latest_one_scope(scope_method_name, start_column, start_null)
|
|
201
|
-
define_earliest_one_scope(scope_method_name, start_column, start_null)
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
def define_instance_method(scope_method_name, start_column, start_null, end_column, end_null)
|
|
205
|
-
define_method("#{scope_method_name}?") do |time = Time.current|
|
|
206
|
-
start_ok = if start_column.nil?
|
|
207
|
-
true
|
|
208
|
-
elsif start_null
|
|
209
|
-
send(start_column).nil? || send(start_column) <= time
|
|
210
|
-
else
|
|
211
|
-
send(start_column) <= time
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
end_ok = if end_column.nil?
|
|
215
|
-
true
|
|
216
|
-
elsif end_null
|
|
217
|
-
send(end_column).nil? || send(end_column) > time
|
|
218
|
-
else
|
|
219
|
-
send(end_column) > time
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
start_ok && end_ok
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
end
|
|
226
128
|
end
|
|
227
129
|
|
|
228
130
|
ActiveSupport.on_load(:active_record) do
|
data/rbs_collection.yaml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# RBS collection configuration
|
|
2
|
+
# Run `rbs collection install` to download external gem signatures
|
|
3
|
+
|
|
4
|
+
sources:
|
|
5
|
+
- type: git
|
|
6
|
+
name: ruby/gem_rbs_collection
|
|
7
|
+
remote: https://github.com/ruby/gem_rbs_collection.git
|
|
8
|
+
revision: main
|
|
9
|
+
repo_dir: gems
|
|
10
|
+
|
|
11
|
+
path: .gem_rbs_collection
|
|
12
|
+
|
|
13
|
+
gems:
|
|
14
|
+
- name: activerecord
|
|
15
|
+
- name: activesupport
|
|
16
|
+
- name: activemodel
|
data/rulesync.jsonc
ADDED
data/sig/in_time_scope.rbs
CHANGED
|
@@ -1,4 +1,93 @@
|
|
|
1
|
+
# Type definitions for InTimeScope gem
|
|
2
|
+
|
|
1
3
|
module InTimeScope
|
|
2
4
|
VERSION: String
|
|
3
|
-
|
|
5
|
+
|
|
6
|
+
# Base error class for InTimeScope errors
|
|
7
|
+
class Error < StandardError
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Raised when a specified column does not exist on the table
|
|
11
|
+
class ColumnNotFoundError < Error
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Raised when the scope configuration is invalid
|
|
15
|
+
class ConfigurationError < Error
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.included: (Class model) -> void
|
|
19
|
+
|
|
20
|
+
# Configuration options for start_at column
|
|
21
|
+
type start_at_config = {
|
|
22
|
+
?column: Symbol?,
|
|
23
|
+
?null: bool
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# Configuration options for end_at column
|
|
27
|
+
type end_at_config = {
|
|
28
|
+
?column: Symbol?,
|
|
29
|
+
?null: bool
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Class methods added to ActiveRecord models when InTimeScope is included
|
|
33
|
+
module ClassMethods
|
|
34
|
+
# Defines time-window scopes for the model
|
|
35
|
+
#
|
|
36
|
+
# @param scope_name [Symbol] The name of the scope (default: :in_time)
|
|
37
|
+
# @param start_at [Hash] Configuration for the start column
|
|
38
|
+
# @param end_at [Hash] Configuration for the end column
|
|
39
|
+
# @return [void]
|
|
40
|
+
# @raise [ColumnNotFoundError] When a specified column doesn't exist
|
|
41
|
+
# @raise [ConfigurationError] When the configuration is invalid (at scope call time)
|
|
42
|
+
def in_time_scope: (
|
|
43
|
+
?Symbol scope_name,
|
|
44
|
+
?start_at: start_at_config,
|
|
45
|
+
?end_at: end_at_config
|
|
46
|
+
) -> void
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
# Private implementation methods
|
|
51
|
+
# These use ActiveRecord internals and are typed as untyped for flexibility
|
|
52
|
+
def fetch_null_option: (untyped config, untyped column, untyped table_column_hash) -> untyped
|
|
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
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Generated scope methods (dynamically defined)
|
|
68
|
+
# When you call `in_time_scope` on a model, it creates these methods:
|
|
69
|
+
#
|
|
70
|
+
# Class methods (primary - records in time window):
|
|
71
|
+
# Model.in_time(time = Time.current) -> ActiveRecord::Relation
|
|
72
|
+
# Model.in_time_<name>(time = Time.current) -> ActiveRecord::Relation (for named scopes)
|
|
73
|
+
# Model.latest_in_time(foreign_key, time = Time.current) -> ActiveRecord::Relation (start-only/end-only)
|
|
74
|
+
# Model.earliest_in_time(foreign_key, time = Time.current) -> ActiveRecord::Relation (start-only/end-only)
|
|
75
|
+
#
|
|
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):
|
|
82
|
+
# model.in_time?(time = Time.current) -> bool
|
|
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
|
|
89
|
+
|
|
90
|
+
# Extend ActiveRecord::Base to include InTimeScope
|
|
91
|
+
class ActiveRecord::Base
|
|
92
|
+
extend InTimeScope::ClassMethods
|
|
4
93
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: in_time_scope
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- kyohah
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: activerecord
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '
|
|
18
|
+
version: '0'
|
|
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: '
|
|
25
|
+
version: '0'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: irb
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -51,6 +51,20 @@ dependencies:
|
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: '13.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: rbs
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
54
68
|
- !ruby/object:Gem::Dependency
|
|
55
69
|
name: rspec
|
|
56
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -93,6 +107,34 @@ dependencies:
|
|
|
93
107
|
- - ">="
|
|
94
108
|
- !ruby/object:Gem::Version
|
|
95
109
|
version: '0'
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: steep
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - ">="
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '0'
|
|
117
|
+
type: :development
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ">="
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '0'
|
|
124
|
+
- !ruby/object:Gem::Dependency
|
|
125
|
+
name: yard
|
|
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'
|
|
96
138
|
description: InTimeScope provides time-window scopes for ActiveRecord models.
|
|
97
139
|
email:
|
|
98
140
|
- 3257272+kyohah@users.noreply.github.com
|
|
@@ -101,14 +143,40 @@ extensions: []
|
|
|
101
143
|
extra_rdoc_files: []
|
|
102
144
|
files:
|
|
103
145
|
- ".rubocop.yml"
|
|
146
|
+
- ".rulesync/commands/translate-readme.md"
|
|
147
|
+
- ".rulesync/rules/project.md"
|
|
104
148
|
- CHANGELOG.md
|
|
105
|
-
- CLAUDE.md
|
|
106
149
|
- CODE_OF_CONDUCT.md
|
|
107
150
|
- LICENSE.txt
|
|
108
151
|
- README.md
|
|
109
152
|
- Rakefile
|
|
153
|
+
- Steepfile
|
|
154
|
+
- docs/book.toml
|
|
155
|
+
- docs/de/SUMMARY.md
|
|
156
|
+
- docs/de/index.md
|
|
157
|
+
- docs/de/point-system.md
|
|
158
|
+
- docs/de/user-name-history.md
|
|
159
|
+
- docs/fr/SUMMARY.md
|
|
160
|
+
- docs/fr/index.md
|
|
161
|
+
- docs/fr/point-system.md
|
|
162
|
+
- docs/fr/user-name-history.md
|
|
163
|
+
- docs/ja/SUMMARY.md
|
|
164
|
+
- docs/ja/index.md
|
|
165
|
+
- docs/ja/point-system.md
|
|
166
|
+
- docs/ja/user-name-history.md
|
|
167
|
+
- docs/src/SUMMARY.md
|
|
168
|
+
- docs/src/index.md
|
|
169
|
+
- docs/src/point-system.md
|
|
170
|
+
- docs/src/user-name-history.md
|
|
171
|
+
- docs/zh/SUMMARY.md
|
|
172
|
+
- docs/zh/index.md
|
|
173
|
+
- docs/zh/point-system.md
|
|
174
|
+
- docs/zh/user-name-history.md
|
|
110
175
|
- lib/in_time_scope.rb
|
|
176
|
+
- lib/in_time_scope/class_methods.rb
|
|
111
177
|
- lib/in_time_scope/version.rb
|
|
178
|
+
- rbs_collection.yaml
|
|
179
|
+
- rulesync.jsonc
|
|
112
180
|
- sig/in_time_scope.rbs
|
|
113
181
|
homepage: https://github.com/kyohah/in_time_scope
|
|
114
182
|
licenses:
|
|
@@ -126,14 +194,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
126
194
|
requirements:
|
|
127
195
|
- - ">="
|
|
128
196
|
- !ruby/object:Gem::Version
|
|
129
|
-
version: 3.
|
|
197
|
+
version: 3.0.0
|
|
130
198
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
199
|
requirements:
|
|
132
200
|
- - ">="
|
|
133
201
|
- !ruby/object:Gem::Version
|
|
134
202
|
version: '0'
|
|
135
203
|
requirements: []
|
|
136
|
-
rubygems_version:
|
|
204
|
+
rubygems_version: 4.0.3
|
|
137
205
|
specification_version: 4
|
|
138
206
|
summary: Add time-window scopes to ActiveRecord models
|
|
139
207
|
test_files: []
|