-
-
Notifications
You must be signed in to change notification settings - Fork 237
0.36.5 #2067
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
#1980) * Update exporting code to stream points data to file in batches to reduce memory usage * Update changelog
* Add a plan to use MapLibre GL JS for the frontend map rendering, replacing Leaflet * Implement phase 1 * Phases 1-3 + part of 4 * Fix e2e tests * Phase 6 * Implement fog of war * Phase 7 * Next step: fix specs, phase 7 done * Use our own map tiles * Extract v2 map logic to separate manager classes * Update settings panel on v2 map * Update v2 e2e tests structure * Reimplement location search in maps v2 * Update speed routes * Implement visits and places creation in v2 * Fix last failing test * Implement visits merging * Fix a routes e2e test and simplify the routes layer styling. * Extract js to modules from maps_v2_controller.js * Implement area creation * Fix spec problem * Fix some e2e tests * Implement live mode in v2 map * Update icons and panel * Extract some styles * Remove unused file * Start adding dark theme to popups on MapLibre maps * Make popups respect dark theme * Move v2 maps to maplibre namespace * Update v2 references to maplibre * Put place, area and visit info into side panel * Update API to use safe settings config method * Fix specs * Fix method name to config in SafeSettings and update usages accordingly * Add missing public files * Add handling for real time points * Fix remembering enabled/disabled layers of the v2 map * Fix lots of e2e tests * Add settings to select map version * Use maps/v2 as main path for MapLibre maps * Update routing * Update live mode
* 0.36.2 (#2007) * fix: move foreman to global gems to fix startup crash (#1971) * Update exporting code to stream points data to file in batches to red… (#1980) * Update exporting code to stream points data to file in batches to reduce memory usage * Update changelog * Update changelog * Feature/maplibre frontend (#1953) * Add a plan to use MapLibre GL JS for the frontend map rendering, replacing Leaflet * Implement phase 1 * Phases 1-3 + part of 4 * Fix e2e tests * Phase 6 * Implement fog of war * Phase 7 * Next step: fix specs, phase 7 done * Use our own map tiles * Extract v2 map logic to separate manager classes * Update settings panel on v2 map * Update v2 e2e tests structure * Reimplement location search in maps v2 * Update speed routes * Implement visits and places creation in v2 * Fix last failing test * Implement visits merging * Fix a routes e2e test and simplify the routes layer styling. * Extract js to modules from maps_v2_controller.js * Implement area creation * Fix spec problem * Fix some e2e tests * Implement live mode in v2 map * Update icons and panel * Extract some styles * Remove unused file * Start adding dark theme to popups on MapLibre maps * Make popups respect dark theme * Move v2 maps to maplibre namespace * Update v2 references to maplibre * Put place, area and visit info into side panel * Update API to use safe settings config method * Fix specs * Fix method name to config in SafeSettings and update usages accordingly * Add missing public files * Add handling for real time points * Fix remembering enabled/disabled layers of the v2 map * Fix lots of e2e tests * Add settings to select map version * Use maps/v2 as main path for MapLibre maps * Update routing * Update live mode * Update maplibre controller * Update changelog * Remove some console.log statements --------- Co-authored-by: Robin Tuszik <[email protected]> * Remove esbuild scripts from package.json * Remove sideEffects field from package.json * Raw data archivation * Add tests * Fix tests * Fix tests * Update ExceptionReporter * Add schedule to run raw data archival job monthly * Change file structure for raw data archival feature * Update changelog and version for raw data archival feature --------- Co-authored-by: Robin Tuszik <[email protected]>
* Consider MIN_MINUTES_SPENT_IN_CITY during stats calculation * Remove raw data from visited cities api endpoint
* Use user timezone to show dates on maps * Limit timestamps to valid range to prevent database errors when users enter pre-epoch dates. * Limit timestamps to valid range to prevent database errors when users enter pre-epoch dates. * Fix tests failing due to new index on stats table * Fix failing specs
* Fix kml kmz import issues * Refactor KML importer to improve readability and maintainability
#2027) * Implement moving points in map v2 and fix route rendering logic to match map v1. * Fix route spec
* Add verification step to raw data archival process * Add actual verification of raw data archives after creation, and only clear raw_data for verified archives. * Fix failing specs
Bumps [sentry-rails](https://github.com/getsentry/sentry-ruby) from 6.0.0 to 6.1.0. - [Release notes](https://github.com/getsentry/sentry-ruby/releases) - [Changelog](https://github.com/getsentry/sentry-ruby/blob/master/CHANGELOG.md) - [Commits](getsentry/sentry-ruby@6.0.0...6.1.0) --- updated-dependencies: - dependency-name: sentry-rails dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [turbo-rails](https://github.com/hotwired/turbo-rails) from 2.0.17 to 2.0.20. - [Release notes](https://github.com/hotwired/turbo-rails/releases) - [Commits](hotwired/turbo-rails@v2.0.17...v2.0.20) --- updated-dependencies: - dependency-name: turbo-rails dependency-version: 2.0.20 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Evgenii Burmakin <[email protected]>
Bumps [webmock](https://github.com/bblimke/webmock) from 3.25.1 to 3.26.1. - [Release notes](https://github.com/bblimke/webmock/releases) - [Changelog](https://github.com/bblimke/webmock/blob/master/CHANGELOG.md) - [Commits](bblimke/webmock@v3.25.1...v3.26.1) --- updated-dependencies: - dependency-name: webmock dependency-version: 3.26.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Evgenii Burmakin <[email protected]>
Bumps [brakeman](https://github.com/presidentbeef/brakeman) from 7.1.0 to 7.1.1. - [Release notes](https://github.com/presidentbeef/brakeman/releases) - [Changelog](https://github.com/presidentbeef/brakeman/blob/main/CHANGES.md) - [Commits](presidentbeef/brakeman@v7.1.0...v7.1.1) --- updated-dependencies: - dependency-name: brakeman dependency-version: 7.1.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [redis](https://github.com/redis/redis-rb) from 5.4.0 to 5.4.1. - [Changelog](https://github.com/redis/redis-rb/blob/master/CHANGELOG.md) - [Commits](redis/redis-rb@v5.4.0...v5.4.1) --- updated-dependencies: - dependency-name: redis dependency-version: 5.4.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Put import deletion into background job * Update changelog
* fix: use constant weight for maplibre heatmap layer * fix null type, update heatmap styling * improve heatmap styling * fix typo
#2065) * Fix stats calculation to recursively reduce H3 resolution when too many hexagons are generated * Update CHANGELOG.md
* Validate trip start and end dates * Update changelog
📝 WalkthroughWalkthroughPatch release bump to 0.36.5. Adds Year‑End Digests (DB, model, services, jobs, mailer, UI, routes, tests), moves import deletion to a background job with status broadcasting and Turbo updates, adds trip start/end validation (server + client + tests), and applies several map, stats, cache, and index changes across the app. Changes
Sequence DiagramsequenceDiagram
autonumber
participant User
participant Browser
participant Controller as ImportsController
participant Job as Imports::DestroyJob
participant Service as Imports::Destroy
participant Channel as ImportsChannel
participant DB as Database
User->>Browser: Click "Delete" on import
Browser->>Controller: DELETE /imports/:id
Controller->>DB: import.deleting! (persist status)
Controller->>Job: Imports::DestroyJob.perform_later(import.id)
Controller-->>Browser: Redirect / turbo_stream → render "Deleting..." row
alt Job executes
Job->>DB: load import by id
Job->>Channel: broadcast status update (deleting)
Job->>Service: run delete workflow (Imports::Destroy)
Service->>DB: `@import.points.destroy_all` (trigger callbacks)
Service->>DB: `@import.destroy`
Service->>Service: Stats::BulkCalculator recalculation
Job->>Channel: broadcast deletion complete
Channel-->>Browser: TurboStream updates (remove/replace row)
else Import not found
Job-->>Job: log warning and exit
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (4)
app/services/stats/hexagon_calculator.rb (1)
56-57: Good refactor: In-place recursion optimization.Calling
calculate_hexagons(lower_resolution)on the current instance instead of creating a newStats::HexagonCalculatorreduces object allocation overhead while maintaining the same behavior and state.spec/services/stats/hexagon_calculator_spec.rb (1)
65-96: Good test coverage for recursive hexagon reduction.The test correctly simulates the recursive behavior when hexagon count exceeds
MAX_HEXAGONSby stubbing multiple calls and verifying the final result is within bounds.Minor style improvements suggested by static analysis
The following are optional nitpicks from RuboCop that could improve code clarity:
- Line 72: Omit unused block arguments or prefix with
_:- allow_any_instance_of(described_class).to receive(:calculate_h3_indexes) do |instance, points, resolution| + allow_any_instance_of(described_class).to receive(:calculate_h3_indexes) do |_instance, _points, _resolution|
- Line 76: Use literal
{}instead ofHash.new:- Hash.new.tap do |hash| + {}.tap do |hash|app/views/imports/destroy.turbo_stream.erb (1)
6-11: Verify: Intentional removal of import name link?The index view (line 61) renders
<%= link_to import.name, import, ... %>, but this template shows plain text. If this prevents users from navigating to a deleting import, it's good UX. Otherwise, it's an inconsistency.app/jobs/imports/destroy_job.rb (1)
10-10: Optional: Consider removing redundant status update.The controller already calls
@import.deleting!(line 81 of imports_controller.rb) before enqueueing this job, so line 10 sets the same status again. While defensive programming in background jobs can be valuable, this redundancy may be unnecessary since the controller guarantees the status is already set.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
Gemfile.lockis excluded by!**/*.lock
📒 Files selected for processing (21)
.app_versionCHANGELOG.mdapp/controllers/imports_controller.rbapp/javascript/controllers/datetime_controller.jsapp/javascript/controllers/imports_controller.jsapp/javascript/controllers/maps/maplibre/layer_manager.jsapp/javascript/controllers/maps/maplibre/settings_manager.jsapp/javascript/maps_maplibre/layers/fog_layer.jsapp/javascript/maps_maplibre/layers/heatmap_layer.jsapp/jobs/imports/destroy_job.rbapp/models/import.rbapp/models/trip.rbapp/services/imports/destroy.rbapp/services/stats/hexagon_calculator.rbapp/views/imports/destroy.turbo_stream.erbapp/views/imports/index.html.erbdb/migrate/20251208210410_add_composite_index_to_stats.rbe2e/v2/map/layers/advanced.spec.jse2e/v2/trips.spec.jsspec/models/trip_spec.rbspec/services/stats/hexagon_calculator_spec.rb
🧰 Additional context used
📓 Path-based instructions (3)
spec/**/*.rb
📄 CodeRabbit inference engine (CLAUDE.md)
spec/**/*.rb: Use RSpec as the test framework
Use WebMock for external HTTP request mocking in tests
Files:
spec/models/trip_spec.rbspec/services/stats/hexagon_calculator_spec.rb
db/migrate/**/*.rb
📄 CodeRabbit inference engine (CLAUDE.md)
Use Strong Migrations practices for safe database schema changes
Files:
db/migrate/20251208210410_add_composite_index_to_stats.rb
app/jobs/**/*.rb
📄 CodeRabbit inference engine (CLAUDE.md)
Implement long-running or asynchronous work as Sidekiq background jobs
Files:
app/jobs/imports/destroy_job.rb
🧠 Learnings (2)
📚 Learning: 2025-09-29T21:15:25.496Z
Learnt from: CR
Repo: Freika/dawarich PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-29T21:15:25.496Z
Learning: Applies to db/migrate/**/*.rb : Use Strong Migrations practices for safe database schema changes
Applied to files:
db/migrate/20251208210410_add_composite_index_to_stats.rb
📚 Learning: 2025-09-29T21:15:25.496Z
Learnt from: CR
Repo: Freika/dawarich PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-29T21:15:25.496Z
Learning: Applies to app/jobs/**/*.rb : Implement long-running or asynchronous work as Sidekiq background jobs
Applied to files:
app/jobs/imports/destroy_job.rb
🧬 Code graph analysis (5)
spec/services/stats/hexagon_calculator_spec.rb (1)
app/services/stats/hexagon_calculator.rb (2)
points(77-87)calculate_hexagons(40-67)
e2e/v2/trips.spec.js (2)
e2e/helpers/navigation.js (1)
closeOnboardingModal(9-16)app/javascript/controllers/add_visit_controller.js (1)
submitButton(313-313)
app/jobs/imports/destroy_job.rb (1)
app/services/imports/destroy.rb (1)
call(11-22)
app/javascript/maps_maplibre/layers/fog_layer.js (1)
app/javascript/controllers/maps/maplibre_controller.js (1)
data(375-375)
app/services/imports/destroy.rb (2)
app/controllers/map/leaflet_controller.rb (1)
points_count(71-73)app/controllers/notifications_controller.rb (1)
destroy_all(22-25)
🪛 RuboCop (1.82.0)
spec/services/stats/hexagon_calculator_spec.rb
[warning] 72-72: Unused block argument - instance. You can omit all the arguments if you don't care about them.
(Lint/UnusedBlockArgument)
[warning] 72-72: Unused block argument - points. You can omit all the arguments if you don't care about them.
(Lint/UnusedBlockArgument)
[warning] 72-72: Unused block argument - resolution. You can omit all the arguments if you don't care about them.
(Lint/UnusedBlockArgument)
[convention] 76-76: Use hash literal {} instead of Hash.new.
(Style/EmptyLiteral)
db/migrate/20251208210410_add_composite_index_to_stats.rb
[convention] 9-9: execute is not reversible.
(Rails/ReversibleMigration)
[convention] 29-29: execute is not reversible.
(Rails/ReversibleMigration)
🔇 Additional comments (22)
.app_version (1)
1-1: LGTM! Version bump to 0.36.5.The version increment is appropriate for this patch release.
app/services/imports/destroy.rb (2)
12-12: Good practice: Capturing count before deletion.Storing
points_countbefore the transaction ensures the count is preserved for logging after the points are destroyed.
19-19: Helpful addition: Logging import deletion.The info log provides useful tracking for import deletion operations.
app/models/trip.rb (2)
12-12: LGTM! Date validation added.The custom validator ensures trip end dates are after start dates at the model level, complementing frontend validation.
52-57: Clear and correct validation logic.The validation correctly:
- Returns early when dates are blank (avoiding errors during initialization)
- Enforces that
started_atmust be beforeended_at(strict ordering)- Provides a clear error message on the
ended_atfieldspec/models/trip_spec.rb (1)
11-36: Comprehensive test coverage for date validation.The test suite thoroughly covers all validation scenarios:
- Valid case: started_at before ended_at
- Invalid case: started_at after ended_at
- Invalid case: started_at equals ended_at (boundary)
- Valid case: blank dates during initialization
All assertions correctly verify the expected error messages from the model validator.
app/javascript/maps_maplibre/layers/fog_layer.js (3)
15-15: Good addition: Retaining original data for updates.Storing the original data in
this.dataenables re-rendering the fog layer when settings change without requiring a full data reload.
19-19: Correct data storage in add() and update().Both methods now store the incoming data, ensuring
this.datais available for subsequent operations and settings updates.Also applies to: 29-29
84-84: Improved fog clearing with fully opaque fill.Using
'rgba(0, 0, 0, 1)'withdestination-outcomposite operation ensures complete fog removal in the cleared circles.app/javascript/controllers/maps/maplibre/settings_manager.js (1)
247-249: Good defensive check: Ensuring fog layer has data before update.Adding the
fogLayer.datacheck prevents callingupdate()on a fog layer that hasn't received data yet, avoiding potential errors when settings are applied before fog data loads.app/javascript/controllers/maps/maplibre/layer_manager.js (1)
269-280: LGTM!The fog layer now correctly derives
clearRadiusfromthis.settings.fogOfWarRadiuswith a sensible fallback to 1000. This aligns with the PR objective to fix fog of war radius settings being respected in map v2.CHANGELOG.md (1)
7-20: LGTM!The changelog accurately documents the key changes in this release: background processing for import deletion, fixes for negative points count, stats updates, trip date validation, and fog of war settings. The entries are clear and reference relevant issue numbers.
app/javascript/controllers/imports_controller.js (1)
26-46: LGTM!The updated channel handling logic is well-structured:
- Early return on missing row prevents errors
- Delete action cleanly removes the row from DOM
- Points count uses
Intl.NumberFormatfor proper localization- Guards ensure updates only occur when data is present
This aligns well with the background deletion workflow introduced via
Imports::DestroyJob.e2e/v2/trips.spec.js (1)
1-100: Good test coverage for trip date validation.The tests appropriately cover:
- Blocking submission when start date > end date
- Allowing valid date ranges
- Validation when updating dates after initial valid entry
The tests align well with the model-level validation added in
app/models/trip.rband provide good end-to-end coverage.app/javascript/maps_maplibre/layers/heatmap_layer.js (1)
23-80: LGTM! Simplified heatmap configuration with improved visibility.The changes provide a more predictable heatmap rendering:
- Fixed weight of 1 removes per-feature variability
- Stepped intensity values (0.01 → 0.1 → 0.3) provide controlled visibility at different zoom levels
- Transparent baseline in the color ramp prevents obscuring the map at low densities
- Exponential radius growth provides better cluster visibility when zoomed in
The radius reaching 160px at zoom 20 may be intentional for high-zoom scenarios. If clusters appear too large, consider reducing the final value.
app/javascript/controllers/datetime_controller.js (2)
14-35: Good client-side validation implementation.The validation setup properly prevents form submission with invalid date ranges and provides user feedback via
reportValidity(). The integration with the existingupdateCoordinatesflow ensures invalid ranges don't trigger unnecessary API calls.One minor observation: both
changeandblurlisteners callvalidateDates(). For datetime-local inputs,changetypically fires when the value changes and focus leaves, soblurmay be partially redundant. However, this redundancy is harmless and ensures validation runs in edge cases.
38-62: Validation logic handles partial input gracefully.Returning
truewhen either date is invalid (lines 47-49) is a good design choice — it allows users to fill in dates incrementally without triggering premature validation errors. The actual date comparison validation only runs when both dates are parseable.app/models/import.rb (1)
20-20: LGTM! Enum addition aligns with background deletion workflow.The new
deleting: 4status enables proper UI state management during asynchronous import destruction.app/views/imports/index.html.erb (1)
75-83: LGTM! UI correctly reflects async deletion state.The conditional rendering prevents users from triggering duplicate deletions and provides clear feedback during background processing.
app/controllers/imports_controller.rb (1)
84-87: LGTM! Response handling supports both HTML and Turbo Stream requests.The dual format support ensures proper user feedback whether JavaScript is enabled or not.
app/jobs/imports/destroy_job.rb (2)
6-18: Job structure follows guidelines, but ensure error handling for resilience.The background job correctly implements asynchronous deletion following coding guidelines for Sidekiq jobs. However, as noted in the controller review, add error handling beyond
RecordNotFoundto prevent imports from being stuck indeletingstatus if the destruction fails.Based on coding guidelines: "Implement long-running or asynchronous work as Sidekiq background jobs"
22-45: LGTM! Broadcast helpers enable multi-session/multi-tab updates.The broadcast methods correctly notify all connected clients (e.g., multiple tabs) about import status changes, complementing the controller's Turbo Stream response.
| def destroy | ||
| Imports::Destroy.new(current_user, @import).call | ||
| @import.deleting! | ||
| Imports::DestroyJob.perform_later(@import.id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error recovery to prevent stuck "deleting" status.
If Imports::DestroyJob fails (e.g., database errors, service exceptions), the import will remain in deleting status indefinitely with no way to retry or recover. Consider adding:
- A retry mechanism in the job (Sidekiq's built-in retry with exponential backoff)
- A failure callback to revert status to
failedor log for manual intervention - A timeout or staleness check for imports stuck in
deletingstate
🔎 Example: Add error recovery in the job
In app/jobs/imports/destroy_job.rb, wrap the destruction in error handling:
def perform(import_id)
import = Import.find_by(id: import_id)
return unless import
import.deleting!
broadcast_status_update(import)
Imports::Destroy.new(import.user, import).call
broadcast_deletion_complete(import)
rescue ActiveRecord::RecordNotFound
Rails.logger.warn "Import #{import_id} not found, may have already been deleted"
rescue StandardError => e
Rails.logger.error "Failed to delete import #{import_id}: #{e.message}"
import&.failed! if import&.persisted?
raise # Let Sidekiq retry
end🤖 Prompt for AI Agents
In app/controllers/imports_controller.rb around lines 80 to 82, calling
Imports::DestroyJob.perform_later(@import.id) leaves the Import in "deleting" if
the job fails; update the job (app/jobs/imports/destroy_job.rb) to add error
recovery: wrap perform in begin/rescue (or use ActiveJob's retry_on for
transient errors), log failures with the exception, set import.failed! (or
another terminal status) when deletion cannot proceed, re-raise the exception to
allow retries for transient errors, and ensure RecordNotFound is handled
gracefully; additionally consider adding a periodic staleness check (rake task
or scheduled job) to mark imports stuck in "deleting" as failed after a timeout.
|
|
||
| ActiveRecord::Base.transaction do | ||
| @import.points.delete_all | ||
| @import.points.destroy_all |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if Point model has any destroy callbacks that justify using destroy_all
rg -n -A 3 -B 3 'after_destroy|before_destroy' --type=rb -g '**/point.rb'Repository: Freika/dawarich
Length of output: 85
🏁 Script executed:
# First, let's locate and examine the imports/destroy.rb file to understand context
fd -n 'destroy.rb' | grep -E 'imports.*destroy'Repository: Freika/dawarich
Length of output: 289
🏁 Script executed:
# Find the Point model file
fd -n 'point.rb' -type fRepository: Freika/dawarich
Length of output: 289
🏁 Script executed:
#!/bin/bash
# Check if Point model has any destroy callbacks - corrected rg syntax
rg -n -A 3 -B 3 'after_destroy|before_destroy' -t rubyRepository: Freika/dawarich
Length of output: 1834
🏁 Script executed:
# Find the Point model and imports/destroy files
fd 'destroy.rb'Repository: Freika/dawarich
Length of output: 135
🏁 Script executed:
# Find point model
fd 'point\.rb' -type fRepository: Freika/dawarich
Length of output: 229
🏁 Script executed:
#!/bin/bash
# Find Point model - try different patterns
find . -name '*point*.rb' -type f | grep -i modelRepository: Freika/dawarich
Length of output: 198
🏁 Script executed:
# Examine imports/destroy.rb to see context
cat -n app/services/imports/destroy.rbRepository: Freika/dawarich
Length of output: 671
🏁 Script executed:
# Search for Point model more broadly
fd pointRepository: Freika/dawarich
Length of output: 5197
🏁 Script executed:
#!/bin/bash
# Examine the Point model file
cat -n app/models/point.rbRepository: Freika/dawarich
Length of output: 4342
🏁 Script executed:
#!/bin/bash
# Search specifically in Point model for any callbacks
rg -n 'after_destroy|before_destroy' app/models/point.rbRepository: Freika/dawarich
Length of output: 41
🏁 Script executed:
#!/bin/bash
# Check for included concerns in Point model that might have callbacks
rg -n 'include|extend' app/models/point.rbRepository: Freika/dawarich
Length of output: 127
🏁 Script executed:
#!/bin/bash
# Check the Archivable concern for destroy callbacks
rg -n 'after_destroy|before_destroy' app/models/concerns/archivable.rbRepository: Freika/dawarich
Length of output: 41
🏁 Script executed:
#!/bin/bash
# Check all included concerns
cat app/models/concerns/archivable.rbRepository: Freika/dawarich
Length of output: 2167
🏁 Script executed:
#!/bin/bash
# Check Nearable and Distanceable for callbacks
rg -n 'after_destroy|before_destroy' app/models/concerns/ -A 2 -B 2Repository: Freika/dawarich
Length of output: 357
🏁 Script executed:
#!/bin/bash
# Double-check by searching for any destroy_all usage patterns and understanding the Import model
cat app/models/import.rb | head -50Repository: Freika/dawarich
Length of output: 1530
Replace destroy_all with delete_all for better performance on large imports.
The Point model has no destroy callbacks, making the instantiation of each record during destroy_all unnecessary overhead. For imports with thousands of points, use delete_all instead. If point deletion must be cascaded through dependent relationships, rely on the existing dependent: :destroy in the Import model rather than explicitly calling destroy_all.
🤖 Prompt for AI Agents
In app/services/imports/destroy.rb at line 15, calling
@import.points.destroy_all iterates and instantiates each Point (slow for large
imports) even though Point has no destroy callbacks; replace this call with
@import.points.delete_all to perform a single SQL delete for better performance,
ensuring you only do this when no callbacks or dependent cascade logic are
required (rely on the Import model's dependent: :destroy for cascade behavior
where appropriate).
| <td data-points-count> | ||
| <%= number_with_delimiter @import.processed %> | ||
| </td> | ||
| <td data-status-display>deleting</td> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Use @import.status instead of hardcoded string for consistency.
Hardcoding "deleting" creates a maintenance burden and diverges from the index view (line 72: <%= import.status %>).
🔎 Proposed fix
- <td data-status-display>deleting</td>
+ <td data-status-display><%= @import.status %></td>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <td data-status-display>deleting</td> | |
| <td data-status-display><%= @import.status %></td> |
🤖 Prompt for AI Agents
In app/views/imports/destroy.turbo_stream.erb around line 17, the cell currently
hardcodes "deleting"; replace that literal with the model's status so it matches
the index view — use ERB to render the import's status (e.g. <%= @import.status
%>) inside the <td data-status-display> element and ensure proper
escaping/formatting to keep behavior consistent.
| def change | ||
| # Add composite index for the most common stats lookup pattern: | ||
| # Stat.find_or_initialize_by(year:, month:, user:) | ||
| # This query is called on EVERY stats calculation | ||
| # | ||
| # Using algorithm: :concurrently to avoid locking the table during index creation | ||
| # This is crucial for production deployments with existing data | ||
| total_duplicates = execute(<<-SQL.squish).first['count'].to_i | ||
| SELECT COUNT(*) as count | ||
| FROM stats s1 | ||
| WHERE EXISTS ( | ||
| SELECT 1 FROM stats s2 | ||
| WHERE s2.user_id = s1.user_id | ||
| AND s2.year = s1.year | ||
| AND s2.month = s1.month | ||
| AND s2.id > s1.id | ||
| ) | ||
| SQL | ||
|
|
||
| if total_duplicates.positive? | ||
| Rails.logger.info( | ||
| "Found #{total_duplicates} duplicate stats records. Starting cleanup in batches of #{BATCH_SIZE}..." | ||
| ) | ||
| end | ||
|
|
||
| deleted_count = 0 | ||
| loop do | ||
| batch_deleted = execute(<<-SQL.squish).cmd_tuples | ||
| DELETE FROM stats s1 | ||
| WHERE EXISTS ( | ||
| SELECT 1 FROM stats s2 | ||
| WHERE s2.user_id = s1.user_id | ||
| AND s2.year = s1.year | ||
| AND s2.month = s1.month | ||
| AND s2.id > s1.id | ||
| ) | ||
| LIMIT #{BATCH_SIZE} | ||
| SQL | ||
|
|
||
| break if batch_deleted.zero? | ||
|
|
||
| deleted_count += batch_deleted | ||
| Rails.logger.info("Cleaned up #{deleted_count}/#{total_duplicates} duplicate stats records") | ||
| end | ||
|
|
||
| Rails.logger.info("Completed cleanup: removed #{deleted_count} duplicate stats records") if deleted_count.positive? | ||
|
|
||
| add_index :stats, %i[user_id year month], | ||
| name: 'index_stats_on_user_id_year_month', | ||
| unique: true, | ||
| algorithm: :concurrently | ||
| algorithm: :concurrently, | ||
| if_not_exists: true | ||
|
|
||
| BulkStatsCalculatingJob.perform_later | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Migration is not reversible and has PostgreSQL compatibility issues.
Two issues:
-
Non-reversible operations in
changemethod: Theexecutestatements andperform_latercall are not reversible. If a rollback is needed, this migration will fail. Per Strong Migrations practices, consider usingup/downmethods instead. -
PostgreSQL doesn't support
LIMITinDELETEstatements directly: TheDELETE ... LIMITsyntax at line 38 is not valid PostgreSQL. Use a subquery withctidoridinstead.
🔎 Proposed fix for PostgreSQL-compatible batch deletion
loop do
- batch_deleted = execute(<<-SQL.squish).cmd_tuples
- DELETE FROM stats s1
- WHERE EXISTS (
- SELECT 1 FROM stats s2
- WHERE s2.user_id = s1.user_id
- AND s2.year = s1.year
- AND s2.month = s1.month
- AND s2.id > s1.id
- )
- LIMIT #{BATCH_SIZE}
+ batch_deleted = execute(<<-SQL.squish).cmd_tuples
+ DELETE FROM stats
+ WHERE id IN (
+ SELECT s1.id FROM stats s1
+ WHERE EXISTS (
+ SELECT 1 FROM stats s2
+ WHERE s2.user_id = s1.user_id
+ AND s2.year = s1.year
+ AND s2.month = s1.month
+ AND s2.id > s1.id
+ )
+ LIMIT #{BATCH_SIZE}
+ )
SQL
break if batch_deleted.zero?Based on coding guidelines, use Strong Migrations practices for safe database schema changes.
🧰 Tools
🪛 RuboCop (1.82.0)
[convention] 8-56: Assignment Branch Condition size for change is too high. [<4, 20, 3> 20.62/17]
(Metrics/AbcSize)
[convention] 8-56: Method has too many lines. [40/10]
(Metrics/MethodLength)
[convention] 9-9: execute is not reversible.
(Rails/ReversibleMigration)
[convention] 29-29: execute is not reversible.
(Rails/ReversibleMigration)
| // Verify no errors in console | ||
| const consoleErrors = [] | ||
| page.on('console', msg => { | ||
| if (msg.type() === 'error') consoleErrors.push(msg.text()) | ||
| }) | ||
| await page.waitForTimeout(500) | ||
| expect(consoleErrors.filter(e => e.includes('fog_layer'))).toHaveLength(0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Console error listener registered too late to capture errors.
The page.on('console', ...) listener is registered after clicking "Apply Settings" and waiting. Any console errors that occur during applyButton.click() or the first waitForTimeout(500) won't be captured because the listener doesn't exist yet.
🔎 Proposed fix: register the listener before the action
+ // Setup console error listener before clicking apply
+ const consoleErrors = []
+ page.on('console', msg => {
+ if (msg.type() === 'error') consoleErrors.push(msg.text())
+ })
+
// Click Apply Settings button
const applyButton = page.locator('button:has-text("Apply Settings")')
await applyButton.click()
await page.waitForTimeout(500)
- // Verify no errors in console
- const consoleErrors = []
- page.on('console', msg => {
- if (msg.type() === 'error') consoleErrors.push(msg.text())
- })
- await page.waitForTimeout(500)
expect(consoleErrors.filter(e => e.includes('fog_layer'))).toHaveLength(0)Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In e2e/v2/map/layers/advanced.spec.js around lines 78 to 84, the console error
listener is registered after the action so it misses errors triggered by
applyButton.click(); move the page.on('console', ...) registration to before any
UI interactions (before clicking "Apply Settings" and before the initial
waitForTimeout) so it captures errors during the click and subsequent
operations, then keep the rest of the assertions unchanged.
| // Verify no JavaScript errors occurred | ||
| const consoleErrors = [] | ||
| page.on('console', msg => { | ||
| if (msg.type() === 'error') consoleErrors.push(msg.text()) | ||
| }) | ||
| await page.waitForTimeout(500) | ||
| expect(consoleErrors.filter(e => e.includes('undefined') || e.includes('fog'))).toHaveLength(0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same timing issue with console listener.
Same issue as the previous test — the console error listener is registered after the action completes. Move the listener registration before applyButton.click() to capture any errors that occur during settings application.
🤖 Prompt for AI Agents
In e2e/v2/map/layers/advanced.spec.js around lines 106 to 112, the console error
listener is being added after the action that can produce console errors, so
some errors are missed; move the page.on('console', ...) registration to before
await applyButton.click() (i.e., register the listener before triggering the
settings application) so it captures errors emitted during the click, and
optionally clean up the listener after the assertion (page.off or a reference to
the handler) to avoid cross-test leakage.
… compression for responses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
config/application.rb
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-29T21:15:25.496Z
Learnt from: CR
Repo: Freika/dawarich PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-29T21:15:25.496Z
Learning: Applies to app/jobs/**/*.rb : Implement long-running or asynchronous work as Sidekiq background jobs
Applied to files:
config/application.rb
📚 Learning: 2025-09-29T21:15:25.496Z
Learnt from: CR
Repo: Freika/dawarich PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-29T21:15:25.496Z
Learning: Applies to spec/{spec_helper,rails_helper}.rb : Enable code coverage reporting with SimpleCov in the test setup
Applied to files:
config/application.rb
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-and-push-docker
🔇 Additional comments (1)
config/application.rb (1)
40-40: LGTM: Explicit string conversion improves clarity.The explicit
.to_sconversion makes the type expectation clear and prevents potential issues with Pathname coercion in ActionMailer preview paths.
| config.action_mailer.preview_paths << "#{Rails.root.join('spec/mailers/previews')}" | ||
| config.action_mailer.preview_paths << Rails.root.join('spec/mailers/previews').to_s | ||
|
|
||
| config.middleware.use Rack::Deflater |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, let's check the repository structure
git ls-files | head -20Repository: Freika/dawarich
Length of output: 450
🏁 Script executed:
# Locate config/application.rb
find . -name "application.rb" -type fRepository: Freika/dawarich
Length of output: 82
🏁 Script executed:
# Read the application.rb file around line 42
cat -n config/application.rb | sed -n '1,60p'Repository: Freika/dawarich
Length of output: 1822
🏁 Script executed:
# Check if there are other middleware configurations or compression-related gems
rg -i "middleware|deflater|compression" config/ --type ruby -B 2 -A 2Repository: Freika/dawarich
Length of output: 2238
🏁 Script executed:
# Check Gemfile for rack and compression-related dependencies
cat Gemfile 2>/dev/null || echo "No Gemfile found"Repository: Freika/dawarich
Length of output: 2254
🏁 Script executed:
# Check environment-specific middleware configurations
find config/environments -name "*.rb" -type fRepository: Freika/dawarich
Length of output: 186
🏁 Script executed:
# Check if there are any compression-related settings in environment files
rg -i "deflater|compression|gzip" config/environments/ --type rubyRepository: Freika/dawarich
Length of output: 41
Good performance optimization, but verify deployment compatibility.
Adding Rack::Deflater enables gzip compression of HTTP responses. The middleware order is correct—placed at the end of the stack to compress the final output. Rack::Deflater automatically detects existing Content-Encoding headers, so it prevents double compression when a reverse proxy already handles it.
Verify that your deployment environment (reverse proxy, load balancer, CDN) is compatible with this approach. If your infrastructure already compresses responses, ensure Rack::Deflater doesn't override that setup or add unnecessary overhead.
🤖 Prompt for AI Agents
In config/application.rb around line 42, the addition of config.middleware.use
Rack::Deflater may conflict with upstream compression; guard its registration
with an explicit deployment-controlled check (e.g. wrap the middleware
registration in a conditional that reads an ENV flag like ENABLE_RACK_DEFLATER
or a Rails config value), add a clear comment referencing that the flag should
be toggled based on whether the reverse proxy/CDN already compresses responses,
and ensure the default value reflects your most common deployment (disabled if
upstream handles compression).
* Fix family layer toggle in Map v2 settings for non-selfhosted env * Invalidate cache * Remove comments * Remove comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (4)
app/javascript/controllers/maps/maplibre/settings_manager.js (1)
62-63: Consider extracting the capitalization logic for readability.The dynamic property name construction is correct, but the string manipulation could be extracted into a helper function for improved readability and reusability.
🔎 Proposed refactor
+ // Helper to capitalize first letter + capitalizeFirst(str) { + return str.charAt(0).toUpperCase() + str.slice(1) + } + syncToggleStates() { const controller = this.controller // Sync layer toggles const toggleMap = { pointsToggle: 'pointsVisible', routesToggle: 'routesVisible', heatmapToggle: 'heatmapEnabled', visitsToggle: 'visitsEnabled', photosToggle: 'photosEnabled', areasToggle: 'areasEnabled', placesToggle: 'placesEnabled', fogToggle: 'fogEnabled', scratchToggle: 'scratchEnabled', familyToggle: 'familyEnabled', speedColoredToggle: 'speedColoredRoutesEnabled' } Object.entries(toggleMap).forEach(([targetName, settingKey]) => { const target = `${targetName}Target` - const hasTarget = `has${targetName.charAt(0).toUpperCase()}${targetName.slice(1)}Target` + const hasTarget = `has${this.capitalizeFirst(targetName)}Target` if (controller[hasTarget]) { controller[target].checked = this.settings[settingKey] } })app/services/stats/calculate_month.rb (1)
44-47: Consider moving cache invalidation outside the transaction block.Placing
Cache::InvalidateUserCaches.new(user.id).callinside the transaction means:
- If cache deletion fails (e.g., Redis connection error), the entire transaction rolls back, preventing the stat from being saved.
- If the transaction rolls back for other reasons after cache invalidation, you may have prematurely invalidated caches.
Cache invalidation is typically safer as a post-commit hook or outside the transaction:
🔎 Suggested refactor
def update_month_stats(year, month) Stat.transaction do stat = Stat.find_or_initialize_by(year:, month:, user:) distance_by_day = stat.distance_by_day stat.assign_attributes( daily_distance: distance_by_day, distance: distance(distance_by_day), toponyms: toponyms, h3_hex_ids: calculate_h3_hex_ids ) stat.save! - - Cache::InvalidateUserCaches.new(user.id).call end + + Cache::InvalidateUserCaches.new(user.id).call endspec/services/stats/calculate_month_spec.rb (1)
205-224: LGTM!Good test coverage for cache invalidation behavior:
- Verifies cache invalidation occurs after successful stat updates
- Confirms no invalidation when user has no points (early return path)
Minor note: The "does not invalidate caches when there are no points" test (lines 216-223) is nested inside the "when there are points" context but creates a new user without points. This works correctly but could be clearer if placed under the "when there are no points" context at lines 13-27.
app/services/cache/invalidate_user_caches.rb (1)
19-31: Consider making invalidation methods private.The individual invalidation methods (
invalidate_countries_visited,invalidate_cities_visited,invalidate_points_geocoded_stats) are currently public but appear to be implementation details. Theprivatekeyword on line 31 only applies toattr_reader :user_id.🔎 Proposed fix to move methods below private
def call invalidate_countries_visited invalidate_cities_visited invalidate_points_geocoded_stats end + private + + attr_reader :user_id + def invalidate_countries_visited Rails.cache.delete("dawarich/user_#{user_id}_countries_visited") end def invalidate_cities_visited Rails.cache.delete("dawarich/user_#{user_id}_cities_visited") end def invalidate_points_geocoded_stats Rails.cache.delete("dawarich/user_#{user_id}_points_geocoded_stats") end - - private - - attr_reader :user_id end
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
CHANGELOG.mdapp/javascript/controllers/maps/maplibre/settings_manager.jsapp/jobs/points/nightly_reverse_geocoding_job.rbapp/services/cache/clean.rbapp/services/cache/invalidate_user_caches.rbapp/services/stats/calculate_month.rbspec/jobs/imports/destroy_job_spec.rbspec/jobs/points/nightly_reverse_geocoding_job_spec.rbspec/requests/imports_spec.rbspec/services/cache/clean_spec.rbspec/services/cache/invalidate_user_caches_spec.rbspec/services/stats/calculate_month_spec.rb
🚧 Files skipped from review as they are similar to previous changes (1)
- CHANGELOG.md
🧰 Additional context used
📓 Path-based instructions (2)
spec/**/*.rb
📄 CodeRabbit inference engine (CLAUDE.md)
spec/**/*.rb: Use RSpec as the test framework
Use WebMock for external HTTP request mocking in tests
Files:
spec/services/cache/clean_spec.rbspec/jobs/imports/destroy_job_spec.rbspec/services/stats/calculate_month_spec.rbspec/jobs/points/nightly_reverse_geocoding_job_spec.rbspec/requests/imports_spec.rbspec/services/cache/invalidate_user_caches_spec.rb
app/jobs/**/*.rb
📄 CodeRabbit inference engine (CLAUDE.md)
Implement long-running or asynchronous work as Sidekiq background jobs
Files:
app/jobs/points/nightly_reverse_geocoding_job.rb
🧠 Learnings (2)
📚 Learning: 2025-09-29T21:15:25.496Z
Learnt from: CR
Repo: Freika/dawarich PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-29T21:15:25.496Z
Learning: Applies to spec/factories/**/*.rb : Define test data with FactoryBot factories
Applied to files:
spec/services/cache/clean_spec.rbspec/jobs/points/nightly_reverse_geocoding_job_spec.rb
📚 Learning: 2025-09-29T21:15:25.496Z
Learnt from: CR
Repo: Freika/dawarich PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-29T21:15:25.496Z
Learning: Applies to app/controllers/shared/stats_controller.rb : When serving public stats, check `public_accessible?`, enforce expiration, and restrict data exposure
Applied to files:
spec/services/stats/calculate_month_spec.rbapp/services/stats/calculate_month.rb
🧬 Code graph analysis (6)
app/javascript/controllers/maps/maplibre/settings_manager.js (1)
app/javascript/controllers/maps_controller.js (2)
controller(1038-1038)controller(1551-1551)
spec/services/stats/calculate_month_spec.rb (2)
app/services/cache/invalidate_user_caches.rb (1)
call(13-17)app/services/stats/calculate_month.rb (1)
call(10-20)
app/jobs/points/nightly_reverse_geocoding_job.rb (2)
app/services/cache/clean.rb (2)
call(3-44)call(5-13)app/services/cache/invalidate_user_caches.rb (1)
call(13-17)
spec/services/cache/invalidate_user_caches_spec.rb (1)
app/services/cache/invalidate_user_caches.rb (4)
call(13-17)invalidate_countries_visited(19-21)invalidate_cities_visited(23-25)invalidate_points_geocoded_stats(27-29)
app/services/stats/calculate_month.rb (1)
app/services/cache/invalidate_user_caches.rb (1)
call(13-17)
app/services/cache/invalidate_user_caches.rb (2)
app/services/stats/calculate_month.rb (3)
initialize(3-92)initialize(4-8)call(10-20)app/services/cache/clean.rb (2)
call(3-44)call(5-13)
🔇 Additional comments (10)
app/javascript/controllers/maps/maplibre/settings_manager.js (1)
248-250: Good defensive check to prevent updating without data.The addition of the
fogLayer.datacheck prevents attempting to redraw the fog layer when no data is available, which could lead to errors. This aligns well with the FogLayer's public data property mentioned in the context.app/services/cache/clean.rb (1)
37-42: LGTM!The cache key rename from
countries/citiestocountries_visited/cities_visitedaligns with the newCache::InvalidateUserCachesservice (as seen inapp/services/cache/invalidate_user_caches.rblines 18-24), ensuring consistency across the cache invalidation layer.app/jobs/points/nightly_reverse_geocoding_job.rb (1)
6-19: LGTM!Good implementation using a
Setto collect unique user IDs, ensuring each user's cache is invalidated exactly once regardless of how many points they have. This aligns with the coding guidelines for background jobs.Note that cache invalidation occurs immediately after points are queued for reverse geocoding (via
async_reverse_geocode), not after geocoding completes. This seems appropriate for signaling that data is about to change.spec/services/cache/clean_spec.rb (1)
15-18: LGTM!The test cache keys are correctly updated to match the production code changes in
app/services/cache/clean.rb, ensuring consistent naming (_countries_visited,_cities_visited).spec/requests/imports_spec.rb (1)
223-230: LGTM!The test correctly validates the new asynchronous import deletion flow:
- Verifies
Imports::DestroyJobis enqueued with the correct import ID- Confirms the import transitions to
deletingstate- Maintains the redirect expectation
This aligns with the background job pattern in the coding guidelines.
spec/services/cache/invalidate_user_caches_spec.rb (1)
1-57: LGTM!Comprehensive test coverage for
Cache::InvalidateUserCaches:
- Tests the composite
#callmethod validating all three caches are invalidated- Tests each individual invalidation method independently
- Cache key patterns correctly match the service implementation in
app/services/cache/invalidate_user_caches.rb- Uses FactoryBot as per coding guidelines
spec/jobs/points/nightly_reverse_geocoding_job_spec.rb (2)
64-73: LGTM!Good addition of multi-user test scenario with
user2and a third point. This ensures the job correctly handles points belonging to different users.
101-141: LGTM!Comprehensive test coverage for the per-user cache invalidation behavior:
- Verifies cache service instantiation for each unique user (lines 102-110)
- Confirms
#callis invoked for each user's cache service (lines 112-125)- Validates that the same user's cache is only invalidated once despite having multiple points (lines 127-141) - this correctly tests the
Setdeduplication logic in the jobspec/jobs/imports/destroy_job_spec.rb (1)
1-14: Well-structured test suite for the background job.The test file provides comprehensive coverage including:
- Happy path with status transitions and broadcasts
- Early return for non-existent imports
- Race condition handling with graceful error recovery
- Error propagation for broadcast and service failures
- User isolation verification
Good use of
instance_double,hash_including, and proper RSpec patterns.Also applies to: 16-89, 91-117, 119-136, 138-146, 166-190
app/services/cache/invalidate_user_caches.rb (1)
1-17: Clean service design with consistent cache key patterns.The cache keys align with those used in
Cache::Clean(seeapp/services/cache/clean.rb), ensuring consistency across the codebase. The service provides a focused, single-responsibility approach to user-specific cache invalidation.
|
|
||
| // Show/hide family members list based on initial toggle state | ||
| if (controller.hasFamilyToggleTarget && controller.hasFamilyMembersListTarget) { | ||
| if (controller.hasFamilyToggleTarget && controller.hasFamilyMembersListTarget && controller.familyToggleTarget) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
cat -n app/javascript/controllers/maps/maplibre/settings_manager.js | sed -n '70,90p'Repository: Freika/dawarich
Length of output: 1211
🏁 Script executed:
rg -nP 'hasFamilyToggleTarget|familyToggleTarget' app/javascript/controllers/maps/maplibre/settings_manager.js -B2 -A2Repository: Freika/dawarich
Length of output: 383
🏁 Script executed:
rg -nP 'has\w+Target.*&&.*\.\w+Target.*&&.*\.\w+Target' --type=jsRepository: Freika/dawarich
Length of output: 240
🏁 Script executed:
rg -nP 'has\w+Target.*&&.*\.\w+Target' app/javascript/controllers/ --type=js | head -20Repository: Freika/dawarich
Length of output: 1315
Remove the redundant controller.familyToggleTarget check.
Line 79 checks hasFamilyToggleTarget && hasFamilyMembersListTarget && familyToggleTarget, but similar patterns elsewhere (lines 69, 74, 133) use only the has[Target]Target checks. In the Stimulus framework, when hasFamilyToggleTarget is true, familyToggleTarget is guaranteed to exist. The third condition is redundant and inconsistent with the codebase pattern. Simplify to: if (controller.hasFamilyToggleTarget && controller.hasFamilyMembersListTarget).
🤖 Prompt for AI Agents
In app/javascript/controllers/maps/maplibre/settings_manager.js around line 79,
the if condition redundantly checks controller.familyToggleTarget in addition to
controller.hasFamilyToggleTarget and controller.hasFamilyMembersListTarget;
remove the redundant controller.familyToggleTarget check so the condition reads
only controller.hasFamilyToggleTarget && controller.hasFamilyMembersListTarget
to match the codebase pattern and Stimulus guarantees.
| it 'has already set status to deleting before service is called' do | ||
| expect do | ||
| described_class.perform_now(import.id) | ||
| rescue StandardError | ||
| StandardError | ||
| end.to change { import.reload.status }.to('deleting') | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the no-op expression in rescue block.
Line 161 references StandardError as an expression after rescue, which does nothing. The intent appears to be suppressing the error to continue the expectation check.
🔎 Proposed fix
it 'has already set status to deleting before service is called' do
expect do
described_class.perform_now(import.id)
rescue StandardError
- StandardError
+ nil
end.to change { import.reload.status }.to('deleting')
end📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| it 'has already set status to deleting before service is called' do | |
| expect do | |
| described_class.perform_now(import.id) | |
| rescue StandardError | |
| StandardError | |
| end.to change { import.reload.status }.to('deleting') | |
| end | |
| it 'has already set status to deleting before service is called' do | |
| expect do | |
| described_class.perform_now(import.id) | |
| rescue StandardError | |
| nil | |
| end.to change { import.reload.status }.to('deleting') | |
| end |
🤖 Prompt for AI Agents
In spec/jobs/imports/destroy_job_spec.rb around lines 157 to 163, the rescue
block currently contains a no-op expression `StandardError` which does nothing;
replace it with a proper rescue that suppresses the error (e.g., `rescue
StandardError => _` or `rescue StandardError; nil`) so the exception is
swallowed and the expectation can continue to assert that the import status
changes to 'deleting'.
#2078) * Add new indicies to improve performance and remove unused ones to optimize database. * Remove comments * Update map search suggestions panel styling
* Add yearly digest * Rename YearlyDigests to Users::Digests * Minor changes * Update yearly digest layout and styles * Add flags and chart to email * Update colors * Fix layout of stats in yearly digest view * Remove cron job for yearly digest scheduling * Update CHANGELOG.md * Update digest email setting handling * Allow sharing digest for 1 week or 1 month * Change Digests Distance to Bigint * Fix settings page
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
lib/tasks/points_raw_data.rake (1)
59-59: Inconsistent task argument syntax.Line 59 still uses the old
[:user_id]syntax, while other tasks (lines 6, 30, 145, 180) were updated to the modern%i[user_id]syntax. This inconsistency should be addressed for uniformity.🔎 Proposed fix for consistency
- task :restore_all, [:user_id] => :environment do |_t, args| + task :restore_all, %i[user_id] => :environment do |_t, args|
♻️ Duplicate comments (1)
app/controllers/imports_controller.rb (1)
81-88: Verify error recovery for background deletion.The import transitions to
deletingstatus before enqueueing the background job. IfImports::DestroyJobfails, the import may remain stuck in thedeletingstate indefinitely. This concern has been previously raised and should be addressed with proper error handling in the job.
🧹 Nitpick comments (21)
lib/tasks/points_raw_data.rake (1)
116-118: Consider breaking the long line for better readability.Line 116 exceeds the 120-character limit (135 characters). While functional, breaking the SQL string across lines would improve readability and satisfy RuboCop conventions.
🔎 Suggested formatting
total_blob_size = ActiveStorage::Blob - .joins('INNER JOIN active_storage_attachments ON active_storage_attachments.blob_id = active_storage_blobs.id') + .joins( + 'INNER JOIN active_storage_attachments ' \ + 'ON active_storage_attachments.blob_id = active_storage_blobs.id' + ) .where("active_storage_attachments.record_type = 'Points::RawDataArchive'") .sum(:byte_size)CHANGELOG.md (1)
13-19: Consider consolidating duplicate "Changed" sections.There are two separate "## Changed" sections (lines 13 and 17). For clarity and consistency with the Keep a Changelog format, consider merging them into a single section.
🔎 Suggested consolidation
## Added - In the beginning of the year users will receive a year-end digest email with stats about their tracking activity during the past year. Users can opt out of receiving these emails in User Settings -> Notifications. Emails won't be sent if no email is configured in the SMTP settings or if user has no points tracked during the year. ## Changed - Added and removed some indexes to improve the app performance based on the production usage data. - -## Changed - - Deleting an import will now be processed in the background to prevent request timeouts for large imports.db/migrate/20251227000001_create_digests.rb (1)
33-36: Consider adding indexes concurrently for zero-downtime deployment.While adding indexes on a new empty table is safe during initial creation, for production deployments where users are upgrading, it's safer to add indexes concurrently to avoid locking the table.
Based on coding guidelines for Strong Migrations practices.
🔎 Safer index creation approach
class CreateDigests < ActiveRecord::Migration[8.0] + disable_ddl_transaction! + def change create_table :digests do |t| t.references :user, null: false, foreign_key: true t.integer :year, null: false t.integer :period_type, null: false, default: 0 # enum: monthly: 0, yearly: 1 # Aggregated data t.bigint :distance, null: false, default: 0 # Total distance in meters t.jsonb :toponyms, default: {} # Countries/cities data t.jsonb :monthly_distances, default: {} # {1: meters, 2: meters, ...} t.jsonb :time_spent_by_location, default: {} # Top locations by time # First-time visits (calculated from historical data) t.jsonb :first_time_visits, default: {} # {countries: [], cities: []} # Comparisons t.jsonb :year_over_year, default: {} # {distance_change_percent: 15, ...} t.jsonb :all_time_stats, default: {} # {total_countries: 50, ...} # Sharing (like Stat model) t.jsonb :sharing_settings, default: {} t.uuid :sharing_uuid # Email tracking t.datetime :sent_at t.timestamps end - add_index :digests, %i[user_id year period_type], unique: true - add_index :digests, :sharing_uuid, unique: true - add_index :digests, :year - add_index :digests, :period_type + add_index :digests, %i[user_id year period_type], unique: true, algorithm: :concurrently + add_index :digests, :sharing_uuid, unique: true, algorithm: :concurrently + add_index :digests, :year, algorithm: :concurrently + add_index :digests, :period_type, algorithm: :concurrently end endspec/mailers/previews/users/digests_mailer_preview.rb (1)
4-9: Consider adding defensive guards for better developer experience.The preview assumes both a user and digest exist in the development database. While this is typical for mailer previews, you could improve the developer experience by handling edge cases more gracefully.
🔎 Suggested improvement
def year_end_digest - user = User.first + user = User.first || User.create!( + email: '[email protected]', + password: 'password123', + status: :active + ) digest = user.digests.yearly.last || Users::Digest.last + + unless digest + # Create a sample digest if none exist + digest = Users::Digest.create!( + user: user, + year: Time.current.year, + digest_type: :yearly, + data: { total_distance: 1000, total_points: 5000 } + ) + end Users::DigestsMailer.with(user: user, digest: digest).year_end_digest endThis ensures the preview always works, even with a fresh database.
app/views/users/digests_mailer/year_end_digest.text.erb (1)
4-4: Consider making the greeting configurable.The hardcoded greeting "Hi, this is Evgenii from Dawarich!" may not scale if the application is multi-tenant, changes ownership, or if different senders are needed for different contexts. Consider extracting this to a configuration setting or environment variable.
spec/services/users/digests/first_time_visits_calculator_spec.rb (1)
15-17: Address RuboCop indentation warnings.RuboCop has flagged several indentation inconsistencies in the
toponymsarray literals. Consider adjusting the indentation to align with the project's style guide.As per coding guidelines, formatting issues flagged by static analysis tools should be addressed.
Also applies to: 18-20, 35-37, 42-44, 45-47, 62-64, 68-70, 74-77
spec/services/users/digests/year_over_year_calculator_spec.rb (1)
25-27: Address RuboCop indentation warnings.RuboCop has flagged several indentation inconsistencies in the
toponymsarray literals. Consider adjusting the indentation to align with the project's style guide.As per coding guidelines, formatting issues flagged by static analysis tools should be addressed.
Also applies to: 28-30, 36-38, 39-41, 95-98, 102-104
spec/jobs/users/digests/calculating_job_spec.rb (1)
29-29: Address line length warning.RuboCop has flagged this line as exceeding the 120-character limit (121 characters). Consider breaking the line for better readability.
As per coding guidelines, formatting issues flagged by static analysis tools should be addressed.
🔎 Proposed fix
- allow_any_instance_of(Users::Digests::CalculateYear).to receive(:call).and_raise(StandardError.new('Test error')) + allow_any_instance_of(Users::Digests::CalculateYear) + .to receive(:call) + .and_raise(StandardError.new('Test error'))spec/requests/users/digests_spec.rb (1)
44-50: Remove unused variable assignment.The
digestvariable is assigned but never referenced. The test only needs the side effect (creating the record in the database).🔎 Proposed fix
it 'displays existing digests' do - digest = create(:users_digest, user:, year: 2024) + create(:users_digest, user:, year: 2024) get users_digests_url expect(response.body).to include('2024') endspec/services/users/safe_settings_spec.rb (1)
140-144: Consider adding test coverage fordigest_emails_enabled?predicate.The spec tests individual settings but doesn't explicitly verify the
digest_emails_enabled?method behavior, including its nil-handling logic (defaulting totruewhennil).🔎 Suggested test additions
# Add to the 'with default values' context around line 144: expect(safe_settings.digest_emails_enabled?).to be true # Consider adding a separate context for nil handling: context 'when digest_emails_enabled is nil' do let(:settings) { { 'digest_emails_enabled' => nil } } it 'defaults to true' do expect(safe_settings.digest_emails_enabled?).to be true end end context 'when digest_emails_enabled is false' do let(:settings) { { 'digest_emails_enabled' => false } } it 'returns false' do expect(safe_settings.digest_emails_enabled?).to be false end endspec/jobs/users/digests/year_end_scheduling_job_spec.rb (1)
20-26: Consider using the enum value instead of magic number.Line 22 uses
update_column(:status, 0)with a magic number. While the comment explains the intent, using the enum mapping would be more maintainable.🔎 Proposed fix
before do # Force inactive status after any after_commit callbacks - inactive_user.update_column(:status, 0) # inactive + inactive_user.update_column(:status, User.statuses[:inactive]) create(:stat, user: active_user, year: previous_year, month: 1)app/jobs/users/digests/calculating_job.rb (1)
17-22: Consider truncating stacktrace in user-facing notification.Including the full stacktrace in the notification content may be overwhelming for end users. Consider logging the full stacktrace and showing only the error message to users.
🔎 Proposed fix
Notifications::Create.new( user:, kind: :error, title: 'Year-End Digest calculation failed', - content: "#{error.message}, stacktrace: #{error.backtrace.join("\n")}" + content: "An error occurred while generating your digest: #{error.message}" ).call + + Rails.logger.error("[CalculatingJob] Digest failed for user #{user_id}: #{error.message}\n#{error.backtrace.join("\n")}")app/helpers/users/digests_helper.rb (1)
10-20: Code duplication with model method.The
distance_comparison_textlogic duplicates the same method inUsers::Digestmodel (lines 134-146 per relevant code snippets). The model already provides this functionality.🔎 Proposed refactor to use model method
def distance_comparison_text(distance_meters) - distance_km = distance_meters.to_f / 1000 - - if distance_km >= Users::Digest::MOON_DISTANCE_KM - percentage = ((distance_km / Users::Digest::MOON_DISTANCE_KM) * 100).round(1) - "That's #{percentage}% of the distance to the Moon!" - else - percentage = ((distance_km / Users::Digest::EARTH_CIRCUMFERENCE_KM) * 100).round(1) - "That's #{percentage}% of Earth's circumference!" - end + # Delegate to the digest model for consistency + digest = Users::Digest.new(distance: distance_meters) + digest.distance_comparison_text endAlternatively, if you have a digest object in the view, call
@digest.distance_comparison_textdirectly instead of using the helper.app/views/users/digests/public_year.html.erb (1)
81-95: Move distance conversion logic to controller or helper.The view contains business logic for converting monthly distances. This calculation should be performed in the controller or a dedicated helper method to keep views focused on presentation.
🔎 Proposed refactor
In the controller or helper, prepare the data:
# In controller or helper def monthly_distance_chart_data(digest, unit) digest.monthly_distances.sort.map do |month, distance_meters| [ Date::ABBR_MONTHNAMES[month.to_i], Users::Digest.convert_distance(distance_meters.to_i, unit).round ] end endThen in the view:
<%= column_chart( monthly_distance_chart_data(@digest, @distance_unit), height: '200px', suffix: " #{@distance_unit}", # ... rest of options ) %>spec/factories/users/digests.rb (1)
127-127: Optional: Consider using %w for word array.Line 127 could use
%w[Berlin Paris Madrid Barcelona]instead of['Berlin', 'Paris', 'Madrid', 'Barcelona']for consistency with Ruby style conventions, though the current format is perfectly acceptable.app/services/users/digests/calculate_year.rb (2)
42-73: Complex but necessary data aggregation; minor style suggestion.The nested iteration over toponyms is inherently complex due to the data structure. The logic correctly handles:
- Non-array toponyms (skip)
- Non-hash toponym entries (skip)
- Countries without cities (still tracked)
- Deduplication via Set
One minor style improvement per RuboCop: use
country.blank?instead ofunless country.present?.🔎 Minor style fix
- next unless country.present? + next if country.blank?
90-120: Time-spent calculation is correct but has long lines.The logic correctly aggregates time by country and city, returning the top 10 for each. Lines 117-118 exceed 120 characters.
🔎 Break long lines for readability
{ - 'countries' => country_time.sort_by { |_, v| -v }.first(10).map { |name, minutes| { 'name' => name, 'minutes' => minutes } }, - 'cities' => city_time.sort_by { |_, v| -v }.first(10).map { |name, minutes| { 'name' => name, 'minutes' => minutes } } + 'countries' => country_time.sort_by { |_, v| -v } + .first(10) + .map { |name, minutes| { 'name' => name, 'minutes' => minutes } }, + 'cities' => city_time.sort_by { |_, v| -v } + .first(10) + .map { |name, minutes| { 'name' => name, 'minutes' => minutes } } }app/views/users/digests/show.html.erb (1)
10-13: Share buttons use inlineonclickhandlers.The share buttons use
onclick="sharing_modal.showModal()"which works but is slightly inconsistent with the Stimulus pattern used elsewhere. Consider using a Stimulus action for consistency, though this is optional since the native dialog API is being leveraged directly.Also applies to: 214-216
app/controllers/users/digests_controller.rb (1)
35-39: Redundant exception handling withfind_by!.Using
find_by!and then rescuingRecordNotFoundis redundant. Either usefind_by(returnsnil) with a nil check, or usefind_by!without the rescue block and let Rails handle the 404. The current pattern works but is unconventional.🔎 Cleaner approach using find_by with nil check
def set_digest - @digest = current_user.digests.yearly.find_by!(year: params[:year]) - rescue ActiveRecord::RecordNotFound - redirect_to users_digests_path, alert: 'Digest not found' + @digest = current_user.digests.yearly.find_by(year: params[:year]) + redirect_to users_digests_path, alert: 'Digest not found' unless @digest endapp/models/users/digest.rb (1)
91-97: First-time visit accessors could return nil from hash access.The methods access
first_time_visits['countries']andfirst_time_visits['cities']. Iffirst_time_visitsitself isnil(not just an empty hash), this will raiseNoMethodError. Given the schema default is{}, this should be safe, but defensive access would be more robust.🔎 More defensive accessor pattern
def first_time_countries - first_time_visits['countries'] || [] + first_time_visits&.dig('countries') || [] end def first_time_cities - first_time_visits['cities'] || [] + first_time_visits&.dig('cities') || [] endapp/services/countries_and_cities.rb (1)
31-38: Inconsistent access pattern: method vs. hash notation.Line 32 uses
pluck(:timestamp)(method-based), while line 34 usescity_points.first[:city](hash-based). This inconsistency could cause issues depending on the data structure of the elements incity_points.Consider updating line 32 for consistency:
🔎 Proposed change for consistent hash-based access
- timestamps = city_points.pluck(:timestamp) + timestamps = city_points.map { |point| point[:timestamp] }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
app/assets/svg/icons/lucide/outline/calendar-plus-2.svgis excluded by!**/*.svgapp/assets/svg/icons/lucide/outline/mail.svgis excluded by!**/*.svg
📒 Files selected for processing (53)
CHANGELOG.mdapp/assets/builds/tailwind.cssapp/controllers/exports_controller.rbapp/controllers/imports_controller.rbapp/controllers/settings_controller.rbapp/controllers/shared/digests_controller.rbapp/controllers/users/digests_controller.rbapp/helpers/users/digests_helper.rbapp/jobs/users/digests/calculating_job.rbapp/jobs/users/digests/email_sending_job.rbapp/jobs/users/digests/year_end_scheduling_job.rbapp/mailers/users/digests_mailer.rbapp/models/stat.rbapp/models/user.rbapp/models/users/digest.rbapp/serializers/stats_serializer.rbapp/services/countries_and_cities.rbapp/services/reverse_geocoding/places/fetch_data.rbapp/services/users/digests/calculate_year.rbapp/services/users/digests/first_time_visits_calculator.rbapp/services/users/digests/year_over_year_calculator.rbapp/services/users/safe_settings.rbapp/views/map/maplibre/_settings_panel.html.erbapp/views/settings/index.html.erbapp/views/shared/_sharing_modal.html.erbapp/views/stats/index.html.erbapp/views/users/digests/index.html.erbapp/views/users/digests/public_year.html.erbapp/views/users/digests/show.html.erbapp/views/users/digests_mailer/year_end_digest.html.erbapp/views/users/digests_mailer/year_end_digest.text.erbconfig/routes.rbconfig/sidekiq.ymldb/migrate/20251227000001_create_digests.rbdb/migrate/20251227223614_change_digests_distance_to_bigint.rbdb/migrate/20251228000000_remove_unused_indexes.rbdb/migrate/20251228100000_add_performance_indexes.rbdb/schema.rbdb/seeds.rblib/tasks/points_raw_data.rakelib/tasks/webmanifest.rakespec/factories/users/digests.rbspec/jobs/users/digests/calculating_job_spec.rbspec/jobs/users/digests/email_sending_job_spec.rbspec/jobs/users/digests/year_end_scheduling_job_spec.rbspec/mailers/previews/users/digests_mailer_preview.rbspec/models/users/digest_spec.rbspec/requests/shared/digests_spec.rbspec/requests/users/digests_spec.rbspec/services/users/digests/calculate_year_spec.rbspec/services/users/digests/first_time_visits_calculator_spec.rbspec/services/users/digests/year_over_year_calculator_spec.rbspec/services/users/safe_settings_spec.rb
✅ Files skipped from review due to trivial changes (1)
- db/seeds.rb
🧰 Additional context used
📓 Path-based instructions (4)
spec/**/*.rb
📄 CodeRabbit inference engine (CLAUDE.md)
spec/**/*.rb: Use RSpec as the test framework
Use WebMock for external HTTP request mocking in tests
Files:
spec/jobs/users/digests/year_end_scheduling_job_spec.rbspec/jobs/users/digests/calculating_job_spec.rbspec/requests/users/digests_spec.rbspec/services/users/digests/first_time_visits_calculator_spec.rbspec/factories/users/digests.rbspec/mailers/previews/users/digests_mailer_preview.rbspec/requests/shared/digests_spec.rbspec/services/users/digests/calculate_year_spec.rbspec/services/users/digests/year_over_year_calculator_spec.rbspec/models/users/digest_spec.rbspec/services/users/safe_settings_spec.rbspec/jobs/users/digests/email_sending_job_spec.rb
db/migrate/**/*.rb
📄 CodeRabbit inference engine (CLAUDE.md)
Use Strong Migrations practices for safe database schema changes
Files:
db/migrate/20251227223614_change_digests_distance_to_bigint.rbdb/migrate/20251227000001_create_digests.rbdb/migrate/20251228000000_remove_unused_indexes.rbdb/migrate/20251228100000_add_performance_indexes.rb
app/jobs/**/*.rb
📄 CodeRabbit inference engine (CLAUDE.md)
Implement long-running or asynchronous work as Sidekiq background jobs
Files:
app/jobs/users/digests/email_sending_job.rbapp/jobs/users/digests/year_end_scheduling_job.rbapp/jobs/users/digests/calculating_job.rb
spec/factories/**/*.rb
📄 CodeRabbit inference engine (CLAUDE.md)
Define test data with FactoryBot factories
Files:
spec/factories/users/digests.rb
🧠 Learnings (6)
📚 Learning: 2025-09-29T21:15:25.496Z
Learnt from: CR
Repo: Freika/dawarich PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-29T21:15:25.496Z
Learning: Applies to app/jobs/**/*.rb : Implement long-running or asynchronous work as Sidekiq background jobs
Applied to files:
spec/jobs/users/digests/year_end_scheduling_job_spec.rbspec/jobs/users/digests/calculating_job_spec.rbapp/jobs/users/digests/email_sending_job.rbapp/controllers/imports_controller.rbapp/jobs/users/digests/year_end_scheduling_job.rbapp/jobs/users/digests/calculating_job.rbspec/jobs/users/digests/email_sending_job_spec.rb
📚 Learning: 2025-09-29T21:15:25.496Z
Learnt from: CR
Repo: Freika/dawarich PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-29T21:15:25.496Z
Learning: Applies to db/migrate/**/*.rb : Use Strong Migrations practices for safe database schema changes
Applied to files:
db/migrate/20251227223614_change_digests_distance_to_bigint.rbdb/migrate/20251227000001_create_digests.rbdb/migrate/20251228000000_remove_unused_indexes.rbdb/migrate/20251228100000_add_performance_indexes.rbspec/services/users/safe_settings_spec.rbdb/schema.rb
📚 Learning: 2025-09-29T21:15:25.496Z
Learnt from: CR
Repo: Freika/dawarich PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-29T21:15:25.496Z
Learning: Applies to app/controllers/shared/stats_controller.rb : When serving public stats, check `public_accessible?`, enforce expiration, and restrict data exposure
Applied to files:
app/serializers/stats_serializer.rbconfig/routes.rbapp/views/stats/index.html.erbapp/models/stat.rbapp/views/shared/_sharing_modal.html.erbapp/models/users/digest.rbapp/controllers/shared/digests_controller.rbapp/controllers/users/digests_controller.rbapp/services/users/digests/first_time_visits_calculator.rb
📚 Learning: 2025-09-29T21:15:25.496Z
Learnt from: CR
Repo: Freika/dawarich PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-29T21:15:25.496Z
Learning: Applies to spec/factories/**/*.rb : Define test data with FactoryBot factories
Applied to files:
spec/services/users/digests/first_time_visits_calculator_spec.rbspec/factories/users/digests.rb
📚 Learning: 2025-09-29T21:15:25.496Z
Learnt from: CR
Repo: Freika/dawarich PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-29T21:15:25.496Z
Learning: Applies to app/controllers/api/**/*.rb : Protect API endpoints with Bearer API keys, except explicitly public-share endpoints which must verify UUID access
Applied to files:
config/routes.rbspec/requests/shared/digests_spec.rbapp/controllers/shared/digests_controller.rb
📚 Learning: 2025-09-29T21:15:25.496Z
Learnt from: CR
Repo: Freika/dawarich PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-29T21:15:25.496Z
Learning: Add appropriate database indexes for geographic queries to ensure performance
Applied to files:
db/migrate/20251228100000_add_performance_indexes.rbdb/schema.rb
🧬 Code graph analysis (23)
spec/jobs/users/digests/year_end_scheduling_job_spec.rb (2)
app/controllers/users/digests_controller.rb (1)
create(20-31)app/models/users/digest.rb (1)
previous_year(119-121)
spec/jobs/users/digests/calculating_job_spec.rb (2)
app/controllers/users/digests_controller.rb (1)
create(20-31)db/migrate/20251227000001_create_digests.rb (2)
change(3-38)change(4-37)
spec/requests/users/digests_spec.rb (2)
app/controllers/users/digests_controller.rb (1)
create(20-31)app/models/user.rb (1)
include(3-203)
app/serializers/stats_serializer.rb (1)
app/queries/stats_query.rb (1)
points_stats(8-18)
app/jobs/users/digests/email_sending_job.rb (4)
app/jobs/users/digests/year_end_scheduling_job.rb (2)
queue_as(3-20)perform(6-19)app/mailers/users/digests_mailer.rb (1)
year_end_digest(7-16)app/models/user.rb (1)
safe_settings(42-44)app/services/users/safe_settings.rb (1)
digest_emails_enabled?(144-149)
spec/services/users/digests/first_time_visits_calculator_spec.rb (3)
app/services/users/digests/calculate_year.rb (1)
call(11-28)app/services/users/digests/first_time_visits_calculator.rb (1)
call(11-16)app/services/users/digests/year_over_year_calculator.rb (1)
call(11-20)
app/models/user.rb (1)
app/queries/stats_query.rb (1)
points_stats(8-18)
spec/factories/users/digests.rb (3)
app/services/stats/calculate_month.rb (1)
toponyms(65-74)app/models/stat.rb (1)
enable_sharing!(69-89)app/models/users/digest.rb (1)
enable_sharing!(48-67)
spec/mailers/previews/users/digests_mailer_preview.rb (2)
app/mailers/users/digests_mailer.rb (1)
year_end_digest(7-16)app/services/tracks/track_builder.rb (1)
user(147-149)
spec/requests/shared/digests_spec.rb (1)
app/models/users/digest.rb (1)
sharing_enabled?(20-22)
spec/services/users/digests/calculate_year_spec.rb (4)
app/services/users/digests/first_time_visits_calculator.rb (1)
call(11-16)app/services/users/digests/year_over_year_calculator.rb (1)
call(11-20)db/migrate/20251227000001_create_digests.rb (2)
change(3-38)change(4-37)app/services/stats/calculate_month.rb (1)
toponyms(65-74)
app/helpers/users/digests_helper.rb (2)
app/models/concerns/distance_convertible.rb (1)
convert_distance(41-52)app/models/users/digest.rb (2)
distance_comparison_text(139-147)distance_km(135-137)
app/jobs/users/digests/year_end_scheduling_job.rb (2)
app/jobs/users/digests/calculating_job.rb (2)
queue_as(3-26)perform(6-10)app/jobs/users/digests/email_sending_job.rb (2)
queue_as(3-31)perform(6-20)
app/services/users/safe_settings.rb (1)
app/serializers/api/user_serializer.rb (1)
settings(28-47)
spec/services/users/digests/year_over_year_calculator_spec.rb (3)
app/services/users/digests/calculate_year.rb (1)
call(11-28)app/services/users/digests/first_time_visits_calculator.rb (1)
call(11-16)app/services/users/digests/year_over_year_calculator.rb (1)
call(11-20)
app/controllers/shared/digests_controller.rb (3)
app/models/users/digest.rb (3)
public_accessible?(40-42)enable_sharing!(48-67)disable_sharing!(69-77)app/models/user.rb (1)
safe_settings(42-44)app/services/users/safe_settings.rb (1)
distance_unit(120-122)
db/migrate/20251228100000_add_performance_indexes.rb (1)
db/migrate/20251228000000_remove_unused_indexes.rb (2)
disable_ddl_transaction!(3-19)change(6-18)
app/controllers/users/digests_controller.rb (4)
app/controllers/shared/digests_controller.rb (2)
helper(3-55)show(10-24)app/mailers/users/digests_mailer.rb (1)
helper(3-17)app/models/user.rb (1)
safe_settings(42-44)app/services/users/safe_settings.rb (1)
distance_unit(120-122)
app/services/users/digests/first_time_visits_calculator.rb (2)
app/services/users/digests/calculate_year.rb (3)
initialize(5-137)initialize(6-9)call(11-28)app/models/users/digest.rb (2)
first_time_countries(91-93)first_time_cities(95-97)
spec/services/users/safe_settings_spec.rb (3)
app/serializers/api/user_serializer.rb (1)
settings(28-47)app/models/user.rb (1)
safe_settings(42-44)app/services/users/safe_settings.rb (3)
maps(116-118)visits_suggestions_enabled?(124-126)enabled_map_layers(136-138)
app/jobs/users/digests/calculating_job.rb (3)
app/jobs/users/digests/email_sending_job.rb (2)
queue_as(3-31)perform(6-20)app/jobs/users/digests/year_end_scheduling_job.rb (2)
queue_as(3-20)perform(6-19)app/services/users/digests/calculate_year.rb (1)
call(11-28)
spec/jobs/users/digests/email_sending_job_spec.rb (1)
db/migrate/20251227000001_create_digests.rb (2)
change(3-38)change(4-37)
app/services/countries_and_cities.rb (1)
app/serializers/api/photo_serializer.rb (1)
city(49-51)
🪛 markdownlint-cli2 (0.18.1)
CHANGELOG.md
17-17: Multiple headings with the same content
(MD024, no-duplicate-heading)
🪛 RuboCop (1.82.0)
spec/jobs/users/digests/calculating_job_spec.rb
[convention] 29-29: Line is too long. [121/120]
(Layout/LineLength)
spec/requests/users/digests_spec.rb
[warning] 45-45: Useless assignment to variable - digest.
(Lint/UselessAssignment)
spec/services/users/digests/first_time_visits_calculator_spec.rb
[convention] 16-16: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 17-17: Indent the right bracket the same as the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 19-19: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 20-20: Indent the right bracket the same as the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 36-36: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 37-37: Indent the right bracket the same as the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 43-43: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 44-44: Indent the right bracket the same as the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 46-46: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 47-47: Indent the right bracket the same as the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 63-63: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 64-64: Indent the right bracket the same as the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 69-69: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 70-70: Indent the right bracket the same as the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 75-75: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 77-77: Indent the right bracket the same as the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
spec/factories/users/digests.rb
[convention] 127-127: Use %w or %W for an array of words.
(Style/WordArray)
db/migrate/20251227000001_create_digests.rb
[convention] 4-37: Assignment Branch Condition size for change is too high. [<1, 19, 0> 19.03/17]
(Metrics/AbcSize)
[convention] 4-37: Method has too many lines. [20/10]
(Metrics/MethodLength)
spec/services/users/digests/calculate_year_spec.rb
[convention] 25-28: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 29-29: Indent the right bracket the same as the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 34-36: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 37-37: Indent the right bracket the same as the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 111-111: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 112-112: Indent the right bracket the same as the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 117-117: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 119-119: Indent the right bracket the same as the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
db/migrate/20251228000000_remove_unused_indexes.rb
[convention] 6-18: Method has too many lines. [11/10]
(Metrics/MethodLength)
lib/tasks/points_raw_data.rake
[convention] 116-116: Line is too long. [135/120]
(Layout/LineLength)
app/models/users/digest.rb
[convention] 3-154: Class has too many lines. [114/100]
(Metrics/ClassLength)
[convention] 48-67: Cyclomatic complexity for enable_sharing! is too high. [8/7]
(Metrics/CyclomaticComplexity)
[convention] 48-67: Method has too many lines. [16/10]
(Metrics/MethodLength)
spec/services/users/digests/year_over_year_calculator_spec.rb
[convention] 26-26: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 27-27: Indent the right bracket the same as the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 29-29: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 30-30: Indent the right bracket the same as the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 37-37: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 38-38: Indent the right bracket the same as the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 40-40: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 41-41: Indent the right bracket the same as the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 96-96: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 98-98: Indent the right bracket the same as the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 103-103: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
[convention] 104-104: Indent the right bracket the same as the first position after the preceding left parenthesis.
(Layout/FirstArrayElementIndentation)
app/controllers/shared/digests_controller.rb
[convention] 26-54: Assignment Branch Condition size for update is too high. [<3, 19, 6> 20.15/17]
(Metrics/AbcSize)
[convention] 26-54: Method has too many lines. [23/10]
(Metrics/MethodLength)
spec/models/users/digest_spec.rb
[convention] 257-257: Use 2 spaces for indentation in a hash, relative to the first position after the preceding left parenthesis.
(Layout/FirstHashElementIndentation)
[convention] 259-259: Indent the right brace the same as the first position after the preceding left parenthesis.
(Layout/FirstHashElementIndentation)
[convention] 270-270: Use 2 spaces for indentation in a hash, relative to the first position after the preceding left parenthesis.
(Layout/FirstHashElementIndentation)
[convention] 273-273: Indent the right brace the same as the first position after the preceding left parenthesis.
(Layout/FirstHashElementIndentation)
[convention] 284-284: Use 2 spaces for indentation in a hash, relative to the first position after the preceding left parenthesis.
(Layout/FirstHashElementIndentation)
[convention] 287-287: Indent the right brace the same as the first position after the preceding left parenthesis.
(Layout/FirstHashElementIndentation)
[convention] 298-298: Use 2 spaces for indentation in a hash, relative to the first position after the preceding left parenthesis.
(Layout/FirstHashElementIndentation)
[convention] 301-301: Indent the right brace the same as the first position after the preceding left parenthesis.
(Layout/FirstHashElementIndentation)
[convention] 330-330: Use 2 spaces for indentation in a hash, relative to the first position after the preceding left parenthesis.
(Layout/FirstHashElementIndentation)
[convention] 333-333: Indent the right brace the same as the first position after the preceding left parenthesis.
(Layout/FirstHashElementIndentation)
[convention] 344-344: Use 2 spaces for indentation in a hash, relative to the first position after the preceding left parenthesis.
(Layout/FirstHashElementIndentation)
[convention] 347-347: Indent the right brace the same as the first position after the preceding left parenthesis.
(Layout/FirstHashElementIndentation)
app/services/users/digests/year_over_year_calculator.rb
[convention] 65-76: Assignment Branch Condition size for count_cities is too high. [<4, 15, 8> 17.46/17]
(Metrics/AbcSize)
[convention] 65-76: Cyclomatic complexity for count_cities is too high. [9/7]
(Metrics/CyclomaticComplexity)
[convention] 65-76: Perceived complexity for count_cities is too high. [9/8]
(Metrics/PerceivedComplexity)
db/migrate/20251228100000_add_performance_indexes.rb
[convention] 6-29: Method has too many lines. [15/10]
(Metrics/MethodLength)
app/services/users/digests/first_time_visits_calculator.rb
[convention] 63-74: Cyclomatic complexity for extract_cities is too high. [9/7]
(Metrics/CyclomaticComplexity)
[convention] 63-74: Perceived complexity for extract_cities is too high. [9/8]
(Metrics/PerceivedComplexity)
app/services/users/digests/calculate_year.rb
[convention] 11-28: Method has too many lines. [13/10]
(Metrics/MethodLength)
[convention] 42-73: Assignment Branch Condition size for aggregate_toponyms is too high. [<14, 26, 13> 32.26/17]
(Metrics/AbcSize)
[convention] 42-73: Cyclomatic complexity for aggregate_toponyms is too high. [13/7]
(Metrics/CyclomaticComplexity)
[convention] 42-73: Method has too many lines. [24/10]
(Metrics/MethodLength)
[convention] 42-73: Perceived complexity for aggregate_toponyms is too high. [14/8]
(Metrics/PerceivedComplexity)
[convention] 53-53: Use if country.blank? instead of unless country.present?.
(Rails/Blank)
[convention] 90-120: Assignment Branch Condition size for calculate_time_spent is too high. [<17, 29, 13> 36.04/17]
(Metrics/AbcSize)
[convention] 90-120: Cyclomatic complexity for calculate_time_spent is too high. [14/7]
(Metrics/CyclomaticComplexity)
[convention] 90-120: Method has too many lines. [22/10]
(Metrics/MethodLength)
[convention] 90-120: Perceived complexity for calculate_time_spent is too high. [14/8]
(Metrics/PerceivedComplexity)
[convention] 117-117: Line is too long. [135/120]
(Layout/LineLength)
[convention] 118-118: Line is too long. [128/120]
(Layout/LineLength)
db/schema.rb
[convention] 13-13: Use underscores(_) as thousands separator and separate every 3 digits with them.
(Style/NumericLiterals)
[convention] 83-83: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 84-84: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 85-85: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 86-86: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 87-87: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 88-88: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 89-89: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 90-90: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 91-91: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 92-92: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 93-93: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 94-94: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 95-95: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 96-96: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 97-97: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 98-98: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 99-99: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 99-99: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 100-100: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 100-100: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 101-101: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 101-101: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 101-101: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 101-101: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 101-101: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 102-102: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 102-102: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 103-103: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 103-103: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 261-261: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 261-261: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 261-261: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 261-261: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 262-262: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 262-262: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 262-262: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 262-262: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 263-263: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 263-263: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 263-263: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 263-263: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 263-263: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 263-263: Line is too long. [147/120]
(Layout/LineLength)
[convention] 264-264: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 264-264: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 264-264: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 264-264: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 264-264: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 265-265: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 265-265: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 265-265: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 265-265: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 265-265: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 266-266: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 266-266: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 266-266: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 266-266: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 390-390: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 390-390: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 391-391: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 391-391: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 392-392: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 392-392: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 392-392: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 392-392: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 393-393: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 393-393: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 394-394: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 394-394: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 419-419: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 419-419: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
| def perform(user_id, year) | ||
| Users::Digests::CalculateYear.new(user_id, year).call | ||
| rescue StandardError => e | ||
| create_digest_failed_notification(user_id, e) | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Swallowing errors prevents Sidekiq retries.
The rescue StandardError block catches the error and creates a notification, but doesn't re-raise. This prevents Sidekiq's automatic retry mechanism from attempting the job again for transient failures (e.g., network issues, temporary database unavailability).
Consider whether retries are desired. If so, re-raise after notification:
🔎 Proposed fix to allow retries
def perform(user_id, year)
Users::Digests::CalculateYear.new(user_id, year).call
rescue StandardError => e
create_digest_failed_notification(user_id, e)
+ raise # Allow Sidekiq to retry
endIf retries are intentionally disabled (to avoid duplicate notifications), consider using discard_on or retry_on with specific exception types for more granular control.
🤖 Prompt for AI Agents
In app/jobs/users/digests/calculating_job.rb around lines 6 to 10, the rescue
block swallows exceptions which prevents Sidekiq retries; update the handler to
notify on failure and then re-raise the exception to allow Sidekiq to retry (or
alternatively, replace the blanket rescue with Sidekiq's discard_on/retry_on for
specific exception classes if you intentionally want to disable retries),
ensuring the notification still runs before the re-raise or handled by the
configured Sidekiq error handling strategy.
| autocomplete="off" /> | ||
| <!-- Search Results --> | ||
| <div class="absolute z-50 w-full mt-1 bg-base-100 rounded-lg shadow-lg border border-base-300 hidden max-h-full overflow-y-auto" | ||
| <div class="absolute z-50 w-full mt-1 bg-base-100 rounded-lg shadow-lg border border-base-300 hidden max-height:400px; overflow-y-auto" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix invalid CSS syntax in class attribute.
The max-height:400px; syntax is invalid inside the class attribute. CSS properties must be placed in a style attribute, not in class. This will prevent the search results container from having the intended height constraint.
🔎 Proposed fixes
Solution 1 (recommended): Use the style attribute
- <div class="absolute z-50 w-full mt-1 bg-base-100 rounded-lg shadow-lg border border-base-300 hidden max-height:400px; overflow-y-auto"
+ <div class="absolute z-50 w-full mt-1 bg-base-100 rounded-lg shadow-lg border border-base-300 hidden overflow-y-auto"
+ style="max-height: 400px;"
data-maps--maplibre-target="searchResults">Solution 2: Use Tailwind utility class (more consistent with the rest of the codebase)
- <div class="absolute z-50 w-full mt-1 bg-base-100 rounded-lg shadow-lg border border-base-300 hidden max-height:400px; overflow-y-auto"
+ <div class="absolute z-50 w-full mt-1 bg-base-100 rounded-lg shadow-lg border border-base-300 hidden max-h-[400px] overflow-y-auto"
data-maps--maplibre-target="searchResults">🤖 Prompt for AI Agents
In app/views/map/maplibre/_settings_panel.html.erb around line 75, the class
attribute contains invalid CSS syntax `max-height:400px;`; remove that property
from the class list and either add it to a style attribute (e.g.,
style="max-height:400px;") or replace it with a Tailwind utility (e.g., add a
max-h-* or max-h-[400px] class), keeping the rest of the classes intact and
removing the stray semicolon.
| <div class="w-full my-5"> | ||
| <div class="flex justify-between items-center mb-6"> | ||
| <h1 class="text-3xl font-bold">Statistics</h1> | ||
| <% if Date.today >= Date.new(2025, 12, 31) %> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider timezone for date comparison.
Date.today uses the server's timezone, which may differ from the user's timezone. This could cause the link to appear at different local times for users around the world. Consider using Time.current.to_date (respects Rails timezone config) or a user-specific timezone if available.
🔎 Proposed fix using Rails timezone
- <% if Date.today >= Date.new(2025, 12, 31) %>
+ <% if Time.current.to_date >= Date.new(2025, 12, 31) %>
<%= link_to users_digests_path, class: 'btn btn-outline btn-sm' do %>
<%= icon 'earth' %> Year-End Digests
<% end %>
<% end %>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <% if Date.today >= Date.new(2025, 12, 31) %> | |
| <% if Time.current.to_date >= Date.new(2025, 12, 31) %> | |
| <%= link_to users_digests_path, class: 'btn btn-outline btn-sm' do %> | |
| <%= icon 'earth' %> Year-End Digests | |
| <% end %> | |
| <% end %> |
🤖 Prompt for AI Agents
In app/views/stats/index.html.erb around line 6, the code uses Date.today which
relies on server timezone; replace it with Time.current.to_date to respect Rails
timezone config, or if you have a user-specific timezone available convert the
current time to that zone (e.g., user.time_zone) and call to_date, so the date
comparison reflects the configured/user timezone.
| <% if Date.today >= Date.new(2025, 12, 31) %> | ||
| <%= link_to users_digests_path, class: 'btn btn-outline btn-sm' do %> | ||
| <%= icon 'earth' %> Year-End Digests | ||
| <% end %> | ||
| <% end %> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use dynamic year for recurring Year-End Digests feature.
The hardcoded year 2025 means the link will appear on December 31, 2025, and remain visible indefinitely. For a recurring yearly digest feature, this should dynamically reference the current year. After 2025, users would expect to generate digests for 2026, 2027, etc., but this condition will always be true.
🔎 Proposed fix using dynamic year
- <% if Date.today >= Date.new(2025, 12, 31) %>
+ <% if Date.today >= Date.new(Date.today.year, 12, 31) %>
<%= link_to users_digests_path, class: 'btn btn-outline btn-sm' do %>
<%= icon 'earth' %> Year-End Digests
<% end %>
<% end %>Alternatively, if digests should only be accessible after the year ends:
- <% if Date.today >= Date.new(2025, 12, 31) %>
+ <% if Date.today.month == 12 && Date.today.day >= 31 %>
<%= link_to users_digests_path, class: 'btn btn-outline btn-sm' do %>
<%= icon 'earth' %> Year-End Digests
<% end %>
<% end %>Or if year-end digests should be available year-round after they're first generated:
- <% if Date.today >= Date.new(2025, 12, 31) %>
+ <% if Date.today > Date.new(Date.today.year, 1, 1) %>
<%= link_to users_digests_path, class: 'btn btn-outline btn-sm' do %>
<%= icon 'earth' %> Year-End Digests
<% end %>
<% end %>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <% if Date.today >= Date.new(2025, 12, 31) %> | |
| <%= link_to users_digests_path, class: 'btn btn-outline btn-sm' do %> | |
| <%= icon 'earth' %> Year-End Digests | |
| <% end %> | |
| <% end %> | |
| <% if Date.today >= Date.new(Date.today.year, 12, 31) %> | |
| <%= link_to users_digests_path, class: 'btn btn-outline btn-sm' do %> | |
| <%= icon 'earth' %> Year-End Digests | |
| <% end %> | |
| <% end %> |
🤖 Prompt for AI Agents
In app/views/stats/index.html.erb around lines 6 to 10, the condition uses a
hardcoded Date.new(2025, 12, 31) so the Year-End Digests link will behave
incorrectly after 2025; change the condition to use the current year dynamically
(e.g., build the year-end date from Date.current.year) so the link appears
relative to each calendar year's December 31st, and verify the logic matches
desired behavior (only on/after year-end or always after first generation).
|
|
||
| <div class="content"> | ||
| <p> | ||
| Hi, this is Evgenii from Dawarich! Pretty wild journey last yeah, huh? Let's take a look back at all the places you explored in <strong><%= @digest.year %></strong>. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix typo in the email content.
Line 171 contains "last yeah" which should be "last year":
🔎 Proposed fix
- Hi, this is Evgenii from Dawarich! Pretty wild journey last yeah, huh? Let's take a look back at all the places you explored in <strong><%= @digest.year %></strong>.
+ Hi, this is Evgenii from Dawarich! Pretty wild journey last year, huh? Let's take a look back at all the places you explored in <strong><%= @digest.year %></strong>.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Hi, this is Evgenii from Dawarich! Pretty wild journey last yeah, huh? Let's take a look back at all the places you explored in <strong><%= @digest.year %></strong>. | |
| Hi, this is Evgenii from Dawarich! Pretty wild journey last year, huh? Let's take a look back at all the places you explored in <strong><%= @digest.year %></strong>. |
🤖 Prompt for AI Agents
In app/views/users/digests_mailer/year_end_digest.html.erb around line 171, the
email body contains a typo "last yeah" — replace it with "last year" so the
sentence reads correctly ("Pretty wild journey last year, huh?") and ensure
spacing/punctuation remains unchanged.
| <h2 class="text-xl font-semibold mb-2 flex items-center justify-center gap-2"> | ||
| <%= icon 'earth' %>No Year-End Digests Yet | ||
| </h2> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: Add space between icon and heading text.
The icon and text run together without a gap.
🔎 Proposed fix
<h2 class="text-xl font-semibold mb-2 flex items-center justify-center gap-2">
- <%= icon 'earth' %>No Year-End Digests Yet
+ <%= icon 'earth' %> No Year-End Digests Yet
</h2>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <h2 class="text-xl font-semibold mb-2 flex items-center justify-center gap-2"> | |
| <%= icon 'earth' %>No Year-End Digests Yet | |
| </h2> | |
| <h2 class="text-xl font-semibold mb-2 flex items-center justify-center gap-2"> | |
| <%= icon 'earth' %> No Year-End Digests Yet | |
| </h2> |
🤖 Prompt for AI Agents
In app/views/users/digests/index.html.erb around lines 30–32 the icon and
heading text run together; add a small gap between them by applying a right
margin to the icon (e.g., render the icon inside a span or pass a class that
adds margin such as mr-2) or insert a single non-breaking space after the icon
so the heading reads "icon + space + No Year-End Digests Yet".
| </span> | ||
| <span class="text-sm"><%= country['cities']&.length || 0 %> cities</span> | ||
| </div> | ||
| <progress class="progress progress-primary w-full" value="<%= 100 - (index * 15) %>" max="100"></progress> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Progress calculation uses magic numbers and could produce negative values.
The formula 100 - (index * 15) uses magic numbers (100, 15) and will produce negative values if there are more than ~6 countries. Consider using a more robust calculation or capping at 0.
🔎 Proposed fix
-<progress class="progress progress-primary w-full" value="<%= 100 - (index * 15) %>" max="100"></progress>
+<% progress_value = [100 - (index * 15), 0].max %>
+<progress class="progress progress-primary w-full" value="<%= progress_value %>" max="100"></progress>Or better, calculate progress based on actual data (e.g., time spent or visit count):
<% max_cities = @digest.toponyms.map { |c| c['cities']&.length || 0 }.max %>
<% progress = max_cities > 0 ? ((country['cities']&.length || 0).to_f / max_cities * 100).round : 0 %>
<progress class="progress progress-primary w-full" value="<%= progress %>" max="100"></progress>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <progress class="progress progress-primary w-full" value="<%= 100 - (index * 15) %>" max="100"></progress> | |
| <% progress_value = [100 - (index * 15), 0].max %> | |
| <progress class="progress progress-primary w-full" value="<%= progress_value %>" max="100"></progress> |
🤖 Prompt for AI Agents
In app/views/users/digests/public_year.html.erb around line 139, the progress
value is computed with a magic-number formula `100 - (index * 15)` which can
produce negative results and is brittle; replace it with a data-driven, bounded
calculation: compute a meaningful denominator (e.g., maximum cities or max visit
count across countries), calculate the country's ratio as a float, convert to a
percentage and round, and then clamp the result between 0 and 100 while handling
nils/defaults so the progress value never goes below 0 or above 100.
* Add RailsPulse * Add RailsPulse monitoring tool with basic HTTP authentication
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
Gemfile (1)
39-39: Consider pinning the rails_pulse gem version.While the gem is correctly positioned, adding a version constraint (e.g.,
gem 'rails_pulse', '~> 1.0') would prevent unexpected breaking changes from automatic updates.db/migrate/20251228163703_install_rails_pulse_tables.rb (1)
1-23: Migration approach looks safe for new table creation.The pattern of loading an external schema file is appropriate for Rails Pulse integration. Since this creates new tables without touching existing data, there are no Strong Migrations concerns.
However, consider these optional style improvements:
🔎 Optional style improvements
+# frozen_string_literal: true + # Generated from Rails Pulse schema - automatically loads current schema definition class InstallRailsPulseTables < ActiveRecord::Migration[8.0] def change + schema_file = Rails.root.join("db/rails_pulse_schema.rb") - # Load and execute the Rails Pulse schema directly - # This ensures the migration is always in sync with the schema file - schema_file = File.join(::Rails.root.to_s, "db/rails_pulse_schema.rb") - - if File.exist?(schema_file) + raise "Rails Pulse schema file not found at db/rails_pulse_schema.rb" unless File.exist?(schema_file) + - say "Loading Rails Pulse schema from db/rails_pulse_schema.rb" + say 'Loading Rails Pulse schema from db/rails_pulse_schema.rb' - - # Load the schema file to define RailsPulse::Schema - load schema_file + load schema_file + RailsPulse::Schema.call(connection) - - # Execute the schema in the context of this migration - RailsPulse::Schema.call(connection) - - say "Rails Pulse tables created successfully" - say "The schema file db/rails_pulse_schema.rb remains as your single source of truth" - else - raise "Rails Pulse schema file not found at db/rails_pulse_schema.rb" - end + say 'Rails Pulse tables created successfully' + say 'The schema file db/rails_pulse_schema.rb remains as your single source of truth' end endconfig/schedule.yml (1)
53-61: RailsPulse scheduled jobs look correctly configured.The cron schedules are appropriate (hourly summaries at :05, daily cleanup at 01:00).
Note: Both jobs use the
defaultqueue. If RailsPulse operations become resource-intensive, consider using a dedicated queue (the PR adds adigestsqueue in sidekiq.yml) to isolate performance monitoring workload.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
Gemfile.lockis excluded by!**/*.lock
📒 Files selected for processing (8)
Gemfileconfig/initializers/rails_pulse.rbconfig/routes.rbconfig/schedule.ymldb/migrate/20251228163703_install_rails_pulse_tables.rbdb/rails_pulse_migrate/.keepdb/rails_pulse_schema.rbdb/schema.rb
🧰 Additional context used
📓 Path-based instructions (1)
db/migrate/**/*.rb
📄 CodeRabbit inference engine (CLAUDE.md)
Use Strong Migrations practices for safe database schema changes
Files:
db/migrate/20251228163703_install_rails_pulse_tables.rb
🧠 Learnings (5)
📚 Learning: 2025-09-29T21:15:25.496Z
Learnt from: CR
Repo: Freika/dawarich PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-29T21:15:25.496Z
Learning: Applies to app/jobs/**/*.rb : Implement long-running or asynchronous work as Sidekiq background jobs
Applied to files:
config/schedule.ymlconfig/routes.rb
📚 Learning: 2025-09-29T21:15:25.496Z
Learnt from: CR
Repo: Freika/dawarich PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-29T21:15:25.496Z
Learning: Applies to db/migrate/**/*.rb : Use Strong Migrations practices for safe database schema changes
Applied to files:
db/schema.rbdb/rails_pulse_schema.rbdb/migrate/20251228163703_install_rails_pulse_tables.rb
📚 Learning: 2025-09-29T21:15:25.496Z
Learnt from: CR
Repo: Freika/dawarich PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-29T21:15:25.496Z
Learning: Add appropriate database indexes for geographic queries to ensure performance
Applied to files:
db/schema.rb
📚 Learning: 2025-09-29T21:15:25.496Z
Learnt from: CR
Repo: Freika/dawarich PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-29T21:15:25.496Z
Learning: Applies to app/controllers/shared/stats_controller.rb : When serving public stats, check `public_accessible?`, enforce expiration, and restrict data exposure
Applied to files:
config/routes.rb
📚 Learning: 2025-09-29T21:15:25.496Z
Learnt from: CR
Repo: Freika/dawarich PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-29T21:15:25.496Z
Learning: Applies to app/controllers/api/**/*.rb : Protect API endpoints with Bearer API keys, except explicitly public-share endpoints which must verify UUID access
Applied to files:
config/routes.rb
🪛 RuboCop (1.82.0)
db/schema.rb
[convention] 13-13: Use underscores(_) as thousands separator and separate every 3 digits with them.
(Style/NumericLiterals)
[convention] 83-83: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 84-84: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 85-85: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 86-86: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 87-87: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 88-88: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 89-89: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 90-90: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 91-91: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 92-92: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 93-93: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 94-94: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 95-95: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 96-96: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 97-97: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 98-98: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 99-99: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 99-99: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 100-100: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 100-100: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 101-101: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 101-101: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 101-101: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 101-101: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 101-101: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 102-102: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 102-102: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 103-103: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 103-103: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 261-261: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 261-261: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 261-261: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 261-261: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 262-262: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 262-262: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 262-262: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 262-262: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 263-263: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 263-263: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 263-263: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 263-263: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 263-263: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 263-263: Line is too long. [147/120]
(Layout/LineLength)
[convention] 264-264: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 264-264: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 264-264: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 264-264: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 264-264: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 265-265: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 265-265: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 265-265: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 265-265: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 265-265: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 266-266: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 266-266: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 266-266: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 266-266: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 288-288: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 289-289: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 289-289: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 290-290: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 290-290: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 291-291: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 291-291: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 292-292: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 292-292: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 293-293: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 293-293: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 294-294: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 294-294: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 295-295: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 295-295: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 296-296: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 296-296: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 297-297: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 298-298: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 299-299: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 299-299: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 299-299: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 299-299: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 300-300: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 300-300: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 301-301: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 301-301: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 301-301: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 301-301: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 301-301: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 302-302: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 302-302: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 303-303: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 303-303: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 304-304: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 304-304: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 304-304: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 304-304: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 304-304: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 305-305: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 305-305: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 305-305: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 305-305: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 306-306: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 306-306: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 307-307: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 307-307: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 310-310: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 311-311: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 311-311: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 311-311: Line is too long. [136/120]
(Layout/LineLength)
[convention] 312-312: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 312-312: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 313-313: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 313-313: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 314-314: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 314-314: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 315-315: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 315-315: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 316-316: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 316-316: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 317-317: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 317-317: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 318-318: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 318-318: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 319-319: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 319-319: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 320-320: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 320-320: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 321-321: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 321-321: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 322-322: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 323-323: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 324-324: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 324-324: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 327-327: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 328-328: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 328-328: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 329-329: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 329-329: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 330-330: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 330-330: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 331-331: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 331-331: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 332-332: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 332-332: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 333-333: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 333-333: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 334-334: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 334-334: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 335-335: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 335-335: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 336-336: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 337-337: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 338-338: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 338-338: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 338-338: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 338-338: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 339-339: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 339-339: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 340-340: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 340-340: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 341-341: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 341-341: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 342-342: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 342-342: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 342-342: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 342-342: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 343-343: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 343-343: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 346-346: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 347-347: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 347-347: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 348-348: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 348-348: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 349-349: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 349-349: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 350-350: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 351-351: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 352-352: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 352-352: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 352-352: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 352-352: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 355-355: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 356-356: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 356-356: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 357-357: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 357-357: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 358-358: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 358-358: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 359-359: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 360-360: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 360-360: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 361-361: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 361-361: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 362-362: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 362-362: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 363-363: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 363-363: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 364-364: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 364-364: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 365-365: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 365-365: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 366-366: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 366-366: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 367-367: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 367-367: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 368-368: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 368-368: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 369-369: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 369-369: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 370-370: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 370-370: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 371-371: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 371-371: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 372-372: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 372-372: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 373-373: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 373-373: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 374-374: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 374-374: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 375-375: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 375-375: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 376-376: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 377-377: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 378-378: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 378-378: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 379-379: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 379-379: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 379-379: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 379-379: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 380-380: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 380-380: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 380-380: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 380-380: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 380-380: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 380-380: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 380-380: Line is too long. [133/120]
(Layout/LineLength)
[convention] 381-381: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 381-381: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 381-381: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 381-381: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 486-486: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 486-486: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 487-487: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 487-487: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 488-488: Use %w or %W for an array of words.
(Style/WordArray)
[convention] 488-488: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 488-488: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 488-488: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 489-489: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 489-489: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 490-490: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 490-490: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 515-515: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 515-515: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 528-528: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 528-528: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 528-528: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 529-529: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 529-529: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 529-529: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 530-530: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 530-530: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 530-530: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
db/rails_pulse_schema.rb
[convention] 1-1: Missing frozen string literal comment.
(Style/FrozenStringLiteralComment)
[convention] 7-7: Use %i or %I for an array of symbols.
(Style/SymbolArray)
[convention] 7-7: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 7-7: Line is too long. [137/120]
(Layout/LineLength)
[convention] 7-7: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 9-9: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 9-9: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 12-12: Do not write to stdout. Use Rails's logger if you want to log.
(Rails/Output)
[convention] 13-13: Do not write to stdout. Use Rails's logger if you want to log.
(Rails/Output)
[convention] 19-19: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 20-20: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 21-21: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 25-25: Use %i or %I for an array of symbols.
(Style/SymbolArray)
[convention] 25-25: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 25-25: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 25-25: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 25-25: Line is too long. [129/120]
(Layout/LineLength)
[convention] 28-28: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 28-28: Line is too long. [135/120]
(Layout/LineLength)
[convention] 29-29: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 30-30: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 31-31: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 32-32: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 33-33: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 34-34: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 35-35: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 36-36: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 37-37: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 38-38: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 42-42: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 42-42: Line is too long. [140/120]
(Layout/LineLength)
[convention] 45-45: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 46-46: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 47-47: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 48-48: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 49-49: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 50-50: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 51-51: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 52-52: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 56-56: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 57-57: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 57-57: Line is too long. [125/120]
(Layout/LineLength)
[convention] 58-58: Use %i or %I for an array of symbols.
(Style/SymbolArray)
[convention] 58-58: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 58-58: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 58-58: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 58-58: Line is too long. [137/120]
(Layout/LineLength)
[convention] 61-61: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 62-62: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 62-62: Line is too long. [130/120]
(Layout/LineLength)
[convention] 63-63: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 64-64: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 65-65: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 66-66: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 67-67: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 68-68: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 72-72: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 73-73: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 74-74: Use %i or %I for an array of symbols.
(Style/SymbolArray)
[convention] 74-74: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 74-74: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 74-74: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 74-74: Line is too long. [131/120]
(Layout/LineLength)
[convention] 75-75: Use %i or %I for an array of symbols.
(Style/SymbolArray)
[convention] 75-75: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 75-75: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 75-75: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 75-75: Line is too long. [142/120]
(Layout/LineLength)
[convention] 76-76: Use %i or %I for an array of symbols.
(Style/SymbolArray)
[convention] 76-76: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 76-76: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 76-76: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 76-76: Line is too long. [152/120]
(Layout/LineLength)
[convention] 80-80: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 81-81: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 82-82: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 85-85: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 90-90: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 91-91: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 92-92: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 93-93: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 94-94: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 95-95: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 96-96: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 97-97: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 98-98: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 101-101: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 102-102: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 103-103: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 104-104: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 105-105: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 106-106: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 112-112: Use %i or %I for an array of symbols.
(Style/SymbolArray)
[convention] 112-112: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 112-112: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 113-114: Align the arguments of a method call if they span more than one line.
(Layout/ArgumentAlignment)
[convention] 114-114: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 115-115: Use %i or %I for an array of symbols.
(Style/SymbolArray)
[convention] 115-115: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 115-115: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 115-115: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 115-115: Line is too long. [125/120]
(Layout/LineLength)
[convention] 116-116: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 119-119: Use %i or %I for an array of symbols.
(Style/SymbolArray)
[convention] 119-119: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 119-119: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 119-119: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 120-120: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 122-122: Use %i or %I for an array of symbols.
(Style/SymbolArray)
[convention] 122-122: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 122-122: Do not use space inside array brackets.
(Layout/SpaceInsideArrayLiteralBrackets)
[convention] 122-122: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 123-123: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 125-125: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 125-125: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 127-127: Do not write to stdout. Use Rails's logger if you want to log.
(Rails/Output)
[convention] 131-131: Favor modifier if usage when having a single-line body. Another good alternative is the usage of control flow &&/||.
(Style/IfUnlessModifier)
db/migrate/20251228163703_install_rails_pulse_tables.rb
[convention] 1-1: Missing frozen string literal comment.
(Style/FrozenStringLiteralComment)
[convention] 6-6: Prefer Rails.root.join('path/to').to_s.
(Rails/FilePath)
[convention] 6-6: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 8-8: Use a guard clause (raise "Rails Pulse schema file not found at db/rails_pulse_schema.rb" unless File.exist?(schema_file)) instead of wrapping the code inside a conditional expression.
(Style/GuardClause)
[convention] 9-9: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 17-17: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 18-18: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 20-20: Prefer single-quoted strings when you don't need string interpolation or special symbols.
(Style/StringLiterals)
[convention] 23-23: Final newline missing.
(Layout/TrailingEmptyLines)
config/initializers/rails_pulse.rb
[convention] 1-1: Missing frozen string literal comment.
(Style/FrozenStringLiteralComment)
🔇 Additional comments (5)
config/routes.rb (2)
29-29: RailsPulse engine mounting looks correct.The engine is mounted at a standard path, consistent with other engine mounts in this file.
102-111: Digest routes follow existing patterns correctly.The route structure mirrors the existing stats sharing implementation (lines 94-100), maintaining consistency. Year constraints and UUID-based public sharing are appropriate.
All authentication requirements are properly implemented:
users/digestsactions are protected withauthenticate_user!filtershared/digests#showvalidates UUID access viapublic_accessible?check without requiring authenticationshared/digests#updaterequires authentication withauthenticate_active_user!filterThe implementation follows the same proven pattern as the stats sharing endpoints.
db/rails_pulse_schema.rb (2)
5-129: RailsPulse schema design is well-structured with appropriate indexes.The schema provides comprehensive coverage for performance monitoring:
- Proper unique constraints (request_uuid, routes, queries)
- Foreign key relationships ensure referential integrity
- Composite indexes support aggregation queries efficiently
- Polymorphic association in summaries is appropriate for flexible summarization
The CI-specific logging (lines 9-14, 125-128) helps debug table creation issues and can remain as is.
Based on learnings: The index strategy follows best practices for geographic and time-series queries.
131-133: Conditional schema execution looks correct.The guard clause ensures the schema is only applied when RailsPulse::ApplicationRecord is defined, preventing errors during migration runs.
db/schema.rb (1)
13-13: Schema changes reflect safe database migrations.The auto-generated schema correctly captures:
- New
digeststable with proper constraints and indexes- RailsPulse tables with foreign key relationships
- Performance indexes on existing tables (users.api_key, users.status, points optimizations)
All changes create new tables or add indexes without modifying existing data, which is safe from a Strong Migrations perspective.
Based on learnings: The new indexes on points (lines 261, 265-266) will improve geographic query performance for city-based filtering and visit timestamp lookups.
Also applies to: 83-104, 288-382, 486-490, 515-530
| RailsPulse.configure do |config| | ||
| # ==================================================================================================== | ||
| # GLOBAL CONFIGURATION | ||
| # ==================================================================================================== | ||
|
|
||
| # Enable or disable Rails Pulse | ||
| config.enabled = true | ||
|
|
||
| # ==================================================================================================== | ||
| # THRESHOLDS | ||
| # ==================================================================================================== | ||
| # These thresholds are used to determine if a route, request, or query is slow, very slow, or critical. | ||
| # Values are in milliseconds (ms). Adjust these based on your application's performance requirements. | ||
|
|
||
| # Thresholds for an individual route | ||
| config.route_thresholds = { | ||
| slow: 500, | ||
| very_slow: 1500, | ||
| critical: 3000 | ||
| } | ||
|
|
||
| # Thresholds for an individual request | ||
| config.request_thresholds = { | ||
| slow: 700, | ||
| very_slow: 2000, | ||
| critical: 4000 | ||
| } | ||
|
|
||
| # Thresholds for an individual database query | ||
| config.query_thresholds = { | ||
| slow: 100, | ||
| very_slow: 500, | ||
| critical: 1000 | ||
| } | ||
|
|
||
| # ==================================================================================================== | ||
| # FILTERING | ||
| # ==================================================================================================== | ||
|
|
||
| # Asset Tracking Configuration | ||
| # By default, Rails Pulse ignores asset requests (images, CSS, JS files) to focus on application performance. | ||
| # Set track_assets to true if you want to monitor asset delivery performance. | ||
| config.track_assets = false | ||
|
|
||
| # Custom asset patterns to ignore (in addition to the built-in defaults) | ||
| # Only applies when track_assets is false. Add patterns for app-specific asset paths. | ||
| config.custom_asset_patterns = [ | ||
| # Example: ignore specific asset directories | ||
| # %r{^/uploads/}, | ||
| # %r{^/media/}, | ||
| # "/special-assets/" | ||
| ] | ||
|
|
||
| # Rails Pulse Mount Path (optional) | ||
| # If Rails Pulse is mounted at a custom path, specify it here to prevent | ||
| # Rails Pulse from tracking its own requests. Leave as nil for default '/rails_pulse'. | ||
| # Examples: | ||
| # config.mount_path = "/admin/monitoring" | ||
| config.mount_path = nil | ||
|
|
||
| # Manual route filtering | ||
| # Specify additional routes, requests, or queries to ignore from performance tracking. | ||
| # Each array can include strings (exact matches) or regular expressions. | ||
| # | ||
| # Examples: | ||
| # config.ignored_routes = ["/health_check", %r{^/admin}] | ||
| # config.ignored_requests = ["GET /status", %r{POST /api/v1/.*}] | ||
| # config.ignored_queries = ["SELECT 1", %r{FROM \"schema_migrations\"}] | ||
|
|
||
| config.ignored_routes = [] | ||
| config.ignored_requests = [] | ||
| config.ignored_queries = [] | ||
|
|
||
| # ==================================================================================================== | ||
| # TAGGING | ||
| # ==================================================================================================== | ||
| # Define custom tags for categorizing routes, requests, and queries. | ||
| # You can add any custom tags you want for filtering and organization. | ||
| # | ||
| # Tag names should be in present tense and describe the current state or category. | ||
| # Examples of good tag names: | ||
| # - "critical" (for high-priority endpoints) | ||
| # - "experimental" (for routes under development) | ||
| # - "deprecated" (for routes being phased out) | ||
| # - "external" (for third-party API calls) | ||
| # - "background" (for async job-related operations) | ||
| # - "admin" (for administrative routes) | ||
| # - "public" (for public-facing routes) | ||
| # | ||
| # Example configuration: | ||
| # config.tags = ["ignored", "critical", "experimental", "deprecated", "external", "admin"] | ||
|
|
||
| config.tags = %w[ignored critical experimental] | ||
|
|
||
| # ==================================================================================================== | ||
| # DATABASE CONFIGURATION | ||
| # ==================================================================================================== | ||
| # Configure Rails Pulse to use a separate database for performance monitoring data. | ||
| # This is optional but recommended for production applications to isolate performance | ||
| # data from your main application database. | ||
| # | ||
| # Uncomment and configure one of the following patterns: | ||
|
|
||
| # Option 1: Separate single database for Rails Pulse | ||
| # config.connects_to = { | ||
| # database: { writing: :rails_pulse, reading: :rails_pulse } | ||
| # } | ||
|
|
||
| # Option 2: Primary/replica configuration for Rails Pulse | ||
| # config.connects_to = { | ||
| # database: { writing: :rails_pulse_primary, reading: :rails_pulse_replica } | ||
| # } | ||
|
|
||
| # Don't forget to add the database configuration to config/database.yml: | ||
| # | ||
| # production: | ||
| # # ... your main database config ... | ||
| # rails_pulse: | ||
| # adapter: postgresql # or mysql2, sqlite3 | ||
| # database: myapp_rails_pulse_production | ||
| # username: rails_pulse_user | ||
| # password: <%= Rails.application.credentials.dig(:rails_pulse, :database_password) %> | ||
| # host: localhost | ||
| # pool: 5 | ||
|
|
||
| # ==================================================================================================== | ||
| # AUTHENTICATION | ||
| # ==================================================================================================== | ||
| # Configure authentication to secure access to the Rails Pulse dashboard. | ||
| # Authentication is ENABLED BY DEFAULT in production environments for security. | ||
| # | ||
| # If no authentication method is configured, Rails Pulse will use HTTP Basic Auth | ||
| # with credentials from RAILS_PULSE_USERNAME (default: 'admin') and RAILS_PULSE_PASSWORD | ||
| # environment variables. Set RAILS_PULSE_PASSWORD to enable this fallback. | ||
| # | ||
| # Uncomment and configure one of the following patterns based on your authentication system: | ||
|
|
||
| # Enable/disable authentication (enabled by default in production) | ||
| config.authentication_enabled = true | ||
|
|
||
| # Where to redirect unauthorized users | ||
| config.authentication_redirect_path = '/' | ||
|
|
||
| # Custom authentication method - choose one of the examples below: | ||
|
|
||
| # Example 1: Devise with admin role check | ||
| # config.authentication_method = proc { | ||
| # redirect_to main_app.root_path, alert: 'Access denied' unless user_signed_in? && current_user.admin? | ||
| # } | ||
|
|
||
| # Example 2: Custom session-based authentication | ||
| # config.authentication_method = proc { | ||
| # unless session[:user_id] && User.find_by(id: session[:user_id])&.admin? | ||
| # redirect_to main_app.login_path, alert: "Please log in as an admin" | ||
| # end | ||
| # } | ||
|
|
||
| # Example 3: Warden authentication | ||
| # config.authentication_method = proc { | ||
| # warden.authenticate!(:scope => :admin) | ||
| # } | ||
|
|
||
| # Example 4: Basic HTTP authentication | ||
| config.authentication_method = proc { | ||
| authenticate_or_request_with_http_basic do |username, password| | ||
| username == ENV['RAILS_PULSE_USERNAME'] && password == ENV['RAILS_PULSE_PASSWORD'] | ||
| end | ||
| } | ||
|
|
||
| # Example 5: Custom authorization check | ||
| # config.authentication_method = proc { | ||
| # current_user = User.find_by(id: session[:user_id]) | ||
| # unless current_user&.can_access_rails_pulse? | ||
| # render plain: "Forbidden", status: :forbidden | ||
| # end | ||
| # } | ||
|
|
||
| # ==================================================================================================== | ||
| # DATA CLEANUP | ||
| # ==================================================================================================== | ||
| # Configure automatic cleanup of old performance data to manage database size. | ||
| # Rails Pulse provides two cleanup mechanisms that work together: | ||
| # | ||
| # 1. Time-based cleanup: Delete records older than the retention period | ||
| # 2. Count-based cleanup: Keep only the specified number of records per table | ||
| # | ||
| # Cleanup order respects foreign key constraints: | ||
| # operations → requests → queries/routes | ||
|
|
||
| # Enable or disable automatic data cleanup | ||
| config.archiving_enabled = true | ||
|
|
||
| # Time-based retention - delete records older than this period | ||
| config.full_retention_period = 2.weeks | ||
|
|
||
| # Count-based retention - maximum records to keep per table | ||
| # After time-based cleanup, if tables still exceed these limits, | ||
| # the oldest remaining records will be deleted to stay under the limit | ||
| config.max_table_records = { | ||
| rails_pulse_requests: 10_000, # HTTP requests (moderate volume) | ||
| rails_pulse_operations: 50_000, # Operations within requests (high volume) | ||
| rails_pulse_routes: 1000, # Unique routes (low volume) | ||
| rails_pulse_queries: 500 # Normalized SQL queries (low volume) | ||
| } | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for documentation about RAILS_PULSE environment variables
echo "=== Searching for RailsPulse documentation files ==="
find . -type f \( -name "*.md" -o -name "*.txt" -o -name "*.rdoc" \) | head -20
echo -e "\n=== Checking for mentions of RAILS_PULSE environment variables ==="
rg "RAILS_PULSE_(USERNAME|PASSWORD)" --type md --type txt 2>/dev/null | head -20
echo -e "\n=== Searching for .env example files ==="
fd -e example -e sample | grep -E "(env|config)" | head -20
echo -e "\n=== Checking if .env.example exists ==="
fd "\.env" . | head -20
echo -e "\n=== Searching for authentication documentation in code comments ==="
rg -B2 -A2 "RAILS_PULSE_PASSWORD" . 2>/dev/null | head -40Repository: Freika/dawarich
Length of output: 1747
🏁 Script executed:
#!/bin/bash
# Check the actual behavior when ENV vars are missing
echo "=== Examining the initializer to understand ENV var handling ==="
sed -n '160,175p' config/initializers/rails_pulse.rb
echo -e "\n=== Checking if there are any defaults set elsewhere ==="
rg "RAILS_PULSE_USERNAME|RAILS_PULSE_PASSWORD" . -t rb --type-list 2>/dev/null | grep -v "config/initializers"
echo -e "\n=== Looking for setup or installation documentation ==="
find . -type f -name "*.md" | xargs grep -l "setup\|install\|authentication" 2>/dev/null | head -10Repository: Freika/dawarich
Length of output: 1068
Add deployment documentation for RailsPulse authentication environment variables.
The configuration lacks documentation in project setup guides. No mention of RAILS_PULSE_USERNAME and RAILS_PULSE_PASSWORD exists in README.md, DEVELOPMENT.md, or deployment docs, yet these variables are required for dashboard access. Additionally, the initializer comment at line 146 is misleading—it mentions "default: 'admin'" but the code implements no actual default; if RAILS_PULSE_PASSWORD is not set, authentication will fail with a 401 prompt.
Add setup guidance (e.g., in DEVELOPMENT.md or a .env.example) documenting:
- Both environment variables are required when
authentication_enabled = true - Example values for development environments
- Production deployment instructions for securely setting credentials
🧰 Tools
🪛 RuboCop (1.82.0)
[convention] 1-1: Missing frozen string literal comment.
(Style/FrozenStringLiteralComment)
🤖 Prompt for AI Agents
In config/initializers/rails_pulse.rb around line 146 and in project docs, the
initializer mentions a "default: 'admin'" for RAILS_PULSE_USERNAME but no
default is implemented and there is no project documentation for
RAILS_PULSE_USERNAME/RAILS_PULSE_PASSWORD; add deployment/setup docs and fix the
comment: update README.md or DEVELOPMENT.md to state that when
config.authentication_enabled = true both RAILS_PULSE_USERNAME and
RAILS_PULSE_PASSWORD are required, show example .env entries (e.g.
RAILS_PULSE_USERNAME=admin, RAILS_PULSE_PASSWORD=changeme) for local/dev, add a
.env.example entry, provide production guidance to set credentials securely
(Rails encrypted credentials / secrets manager / environment variables via the
deploy system), and either remove or correct the misleading "default: 'admin'"
comment in the initializer (or implement a safe default fallback) so the code
and docs match.
Summary by CodeRabbit
New Features
Improvements
Chores
✏️ Tip: You can customize this high-level summary in your review settings.