Skip to content

Commit 5383d41

Browse files
Simplify scope declarations (#2434)
* Simplify scope declarations More recent versions of Rails, combined with more recent versions of Ruby, enable a simpler syntax for defining scopes, replacing 5 lines of code with 1 in each case. We have several scopes to define, so let's switch to the simpler syntax. We also add a number of unit tests to make *sure* nothing changed. Signed-off-by: David A. Wheeler <[email protected]> * Update CLAUDE.md Rubocop's -A option (unsafe autocorrect) is much trouble than it's worth. It's better to let the AI fix it than to run the unsafe autocorrect. * Remove bad cop in .rubocop.yml, fix projects controller Signed-off-by: David A. Wheeler <[email protected]>
1 parent 2abfac0 commit 5383d41

18 files changed

+294
-63
lines changed

.rubocop.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ plugins:
3333

3434
AllCops:
3535
EnabledByDefault: true
36-
TargetRubyVersion: 2.5
36+
TargetRubyVersion: 3.3
3737
DisplayCopNames: true
3838
DisplayStyleGuide: true
3939
Include:
@@ -265,3 +265,14 @@ Style/YodaExpression:
265265
Enabled: false
266266
Metrics/BlockLength:
267267
Max: 36
268+
Lint/RedundantRequireStatement:
269+
Enabled: true # Let RuboCop remove truly redundant requires
270+
Rails/WhereRange:
271+
Enabled: true # Modern Rails range syntax is cleaner and equivalent
272+
273+
# The cop Performance/ArraySemiInfiniteRangeSlice
274+
# was created due to a mistake in a microbenchmark. Worse, it can raise
275+
# invalid complaints on strings. We've expressly disabled it, with this
276+
# comment, to avoid the risk of later re-enabling it.
277+
Performance/ArraySemiInfiniteRangeSlice:
278+
Enabled: false # Do NOT enable this

CLAUDE.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,23 @@ For specific files:
3939
- `mdl FILENAME.md` - Markdownlint only the file `FILENAME.md`
4040
- `rubocop FILENAME.rb` - Run rubocop on just `FILENAME.rb`
4141

42-
Don't run `rails_best_practices` to analyze individual files, as
43-
it needs the full context to do its best.
42+
Don't run `rails_best_practices` to analyze individual files.
43+
The `rails_best_practices` program needs the full context of all files
44+
to do its best.
45+
46+
**IMPORTANT**: You may ONLY use `rubocop -a` (safe autocorrect)
47+
for RuboCop corrections.
48+
49+
**NEVER use `rubocop -A` (unsafe autocorrect)** -
50+
it frequently introduces subtle bugs by making assumptions
51+
about code context that are incorrect. Examples include:
52+
53+
- Changing array methods to string methods (`.drop()` vs `[1..]`)
54+
- Changing hash syntax that breaks in complex ActiveRecord queries
55+
56+
Always use `-a` (safe) and manually review any remaining suggestions from
57+
`rubocop` without flags to determine if they're actually appropriate
58+
for the specific context.
4459

4560
### Workflow
4661

app/controllers/application_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def append_info_to_payload(payload)
101101

102102
# Combined cache control header value for CDN surrogate control
103103
BADGE_CACHE_SURROGATE_CONTROL =
104-
"max-age=#{BADGE_CACHE_MAX_AGE}, stale-if-error=#{BADGE_CACHE_STALE_AGE}"
104+
"max-age=#{BADGE_CACHE_MAX_AGE}, stale-if-error=#{BADGE_CACHE_STALE_AGE}".freeze
105105

106106
# Set default cache control - don't externally cache.
107107
# This is the safe behavior, so we make it the default.

app/controllers/projects_controller.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class ProjectsController < ApplicationController
5353
).freeze
5454

5555
# Used to validate deletion rationale.
56-
AT_LEAST_15_NON_WHITESPACE = /\A\s*(\S\s*){15}.*/.freeze
56+
AT_LEAST_15_NON_WHITESPACE = /\A\s*(\S\s*){15}.*/
5757

5858
# as= values, which redirect to alternative views
5959
ALLOWED_AS = %w[badge entry].freeze
@@ -428,7 +428,7 @@ def reminders_summary
428428

429429
# Regex pattern for validating repository rights changes.
430430
# Rights changes, if provided, must match this pattern.
431-
VALID_ADD_RIGHTS_CHANGES = /\A *[+-] *\d+ *(, *\d+)*\z/.freeze
431+
VALID_ADD_RIGHTS_CHANGES = /\A *[+-] *\d+ *(, *\d+)*\z/
432432

433433
# Number of days before a user may change repo_url.
434434
# NOTE: If you change this value, you may also need to change
@@ -589,8 +589,8 @@ def require_adequate_deletion_rationale
589589
# @return [void]
590590
# rubocop:disable Metrics/MethodLength
591591
def update_additional_rights_forced(id, new_additional_rights)
592-
command = new_additional_rights.first
593-
new_list = new_additional_rights[1..-1].split(',').map(&:to_i).uniq.sort
592+
command = new_additional_rights[0] # rubocop:disable Style/ArrayFirstLast
593+
new_list = new_additional_rights[1..].split(',').map(&:to_i).sort.uniq
594594
if command == '-'
595595
AdditionalRight.where(project_id: id, user_id: new_list).destroy_all
596596
else # '+'
@@ -769,7 +769,7 @@ def repo_data(github = nil)
769769

770770
repos.map do |repo|
771771
[repo.full_name, repo.fork, repo.homepage, repo.html_url]
772-
end.compact
772+
end
773773
end
774774
# rubocop:enable Metrics/AbcSize
775775
# rubocop:enable Style/MethodCalledOnDoEndBlock, Metrics/MethodLength

app/helpers/projects_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def tiered_percent_as_string(value)
165165
# rubocop:enable Metrics/MethodLength
166166

167167
# We sometimes insert <wbr> after sequences of these characters.
168-
WORD_BREAK_DIVIDERS = /([,_\-.]+)/.freeze
168+
WORD_BREAK_DIVIDERS = /([,_\-.]+)/
169169

170170
# This text is considered safe, so we can directly mark it as such.
171171
# rubocop:disable Rails/OutputSafety

app/helpers/sessions_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ module SessionsHelper
1010
RESET_SESSION_TIMER = 1.hour # Active sessions older than this reset timer
1111
GITHUB_PATTERN = %r{
1212
\Ahttps://github.com/([A-Za-z0-9_.-]+)/([A-Za-z0-9_.-]+)/?\Z
13-
}x.freeze
13+
}x
1414
require 'uri'
1515

1616
# Remove the "locale=value", if any, from the url_query provided

app/lib/chief.rb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
# the Detective INPUTS and OUTPUTS to determine what order to run, and
1414
# run them in parallel in an appropriate order.
1515

16-
require 'set'
17-
1816
# rubocop:disable Metrics/ClassLength
1917
class Chief
2018
# Confidence level (1..5) where automation result will *override*

app/lib/how_access_repo_files_detective.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class HowAccessRepoFilesDetective < Detective
1313
INPUTS = [:repo_url].freeze
1414
OUTPUTS = [:repo_files].freeze # Ask :repo_files.get("FILENAME") for files.
1515

16-
GITHUB_REPO = %r{https?://github.com/([\w\.-]*)/([\w\.-]*)(.git|/)?}.freeze
16+
GITHUB_REPO = %r{https?://github.com/([\w\.-]*)/([\w\.-]*)(.git|/)?}
1717
def analyze(_evidence, current)
1818
repo_url = current[:repo_url]
1919
return {} if repo_url.blank?

app/models/badge.rb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
# OpenSSF Best Practices badge contributors
55
# SPDX-License-Identifier: MIT
66

7-
require 'set'
8-
97
# rubocop: disable Metrics/ClassLength
108
class Badge
119
ACCEPTABLE_PERCENTAGES = (0..99).to_a.freeze

app/models/project.rb

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class Project < ApplicationRecord
4848

4949
# All badge levels as IDs. Useful for enumerating "all levels" as:
5050
# Project::LEVEL_IDS.each do |level| ... end
51-
LEVEL_ID_NUMBERS = (0..(COMPLETED_BADGE_LEVELS.length - 1)).freeze
51+
LEVEL_ID_NUMBERS = (0..(COMPLETED_BADGE_LEVELS.length - 1))
5252
LEVEL_IDS = LEVEL_ID_NUMBERS.map(&:to_s)
5353

5454
PROJECT_OTHER_FIELDS = %i[
@@ -65,25 +65,13 @@ class Project < ApplicationRecord
6565

6666
default_scope { order(:created_at) }
6767

68-
scope :created_since, (
69-
lambda do |time|
70-
where(Project.arel_table[:created_at].gteq(time))
71-
end
72-
)
68+
scope :created_since, ->(time) { where(created_at: time..) }
7369

74-
scope :gteq, (
75-
lambda do |floor|
76-
where(Project.arel_table[:tiered_percentage].gteq(floor.to_i))
77-
end
78-
)
70+
scope :gteq, ->(floor) { where(tiered_percentage: floor.to_i..) }
7971

8072
scope :in_progress, -> { lteq(99) }
8173

82-
scope :lteq, (
83-
lambda do |ceiling|
84-
where(Project.arel_table[:tiered_percentage].lteq(ceiling.to_i))
85-
end
86-
)
74+
scope :lteq, ->(ceiling) { where(tiered_percentage: ..ceiling.to_i) }
8775

8876
scope :passing, -> { gteq(100) }
8977

@@ -145,11 +133,7 @@ class Project < ApplicationRecord
145133
# using: { tsearch: { any_word: true } }
146134
)
147135

148-
scope :updated_since, (
149-
lambda do |time|
150-
where(Project.arel_table[:updated_at].gteq(time))
151-
end
152-
)
136+
scope :updated_since, ->(time) { where(updated_at: time..) }
153137

154138
# Record information about a project.
155139
# We'll also record previous versions of information:
@@ -189,7 +173,7 @@ class Project < ApplicationRecord
189173
# We have to allow embedded spaces, e.g., "Jupyter Notebook".
190174
VALID_LANGUAGE_LIST =
191175
%r{\A(|-| ([A-Za-z0-9!\#$%'()*+.\/\:;=?@\[\]^~ -]+
192-
(,\ ?[A-Za-z0-9!\#$%'()*+.\/\:;=?@\[\]^~ -]+)*))\Z}x.freeze
176+
(,\ ?[A-Za-z0-9!\#$%'()*+.\/\:;=?@\[\]^~ -]+)*))\Z}x
193177
validates :implementation_languages,
194178
length: { maximum: MAX_SHORT_STRING_LENGTH },
195179
format: {
@@ -552,8 +536,7 @@ def self.projects_to_remind
552536
.where('badge_percentage_0 < 100')
553537
.where('lost_passing_at IS NULL OR lost_passing_at < ?',
554538
LOST_PASSING_REMINDER.days.ago)
555-
.where('projects.updated_at < ?',
556-
LAST_UPDATED_REMINDER.days.ago)
539+
.where(projects: { updated_at: ...LAST_UPDATED_REMINDER.days.ago })
557540
.where('last_reminder_at IS NULL OR last_reminder_at < ?',
558541
LAST_SENT_REMINDER.days.ago)
559542
.joins(:user).references(:user) # Need this to check email address
@@ -592,7 +575,7 @@ def self.recently_reminded
592575
.select('projects.*, users.encrypted_email as user_encrypted_email')
593576
.joins(:user).references(:user) # Need this to check email address
594577
.where('last_reminder_at IS NOT NULL')
595-
.where('last_reminder_at >= ?', 14.days.ago)
578+
.where(last_reminder_at: 14.days.ago..)
596579
.reorder('last_reminder_at')
597580
end
598581

0 commit comments

Comments
 (0)