Skip to content

Conversation

shrestha-bishal
Copy link

@shrestha-bishal shrestha-bishal commented Aug 1, 2025

Before:
Screenshot from 2025-08-01 21-48-05
Screenshot from 2025-08-01 22-42-19

After:
Screenshot from 2025-08-01 22-42-42
Screenshot from 2025-08-01 23-48-55
Screenshot from 2025-08-02 00-08-07
Kazam_screenshot_00004
Kazam_screenshot_00000
Kazam_screenshot_00006
Kazam_screenshot_00005

Summary by CodeRabbit

  • New Features

    • Natural sorting for tags across the app, providing intuitive ordering (e.g., Tag2 comes before Tag10) in tag lists and selection menus.
  • Bug Fixes

    • Consistent, alphabetical ordering for rule condition filters and action executors, preventing unpredictable list order.
    • Category and tag option lists in rule actions now display in alphabetical order for easier selection.
    • Tag selection fields in transaction forms and details use natural sorting for clearer, predictable option ordering.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @shrestha-bishal, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

I've updated the application to sort tags alphabetically across various views, improving the user experience by presenting tags in a consistent and easily navigable order. This change affects how tags are displayed on the main tags page and within transaction forms where tags can be selected. Additionally, I've made a minor adjustment to the Docker development environment's port configuration.

Highlights

  • Custom Alphanumeric Tag Sorting: I've implemented a custom alphanumeric sorting logic for tags, ensuring they are displayed alphabetically, handling both text and numerical parts correctly.
  • Tags Index Page Sorting: This new sorting is applied to the tags listed on the TagsController index page, providing a consistent order for all tags.
  • Refactored Tag Sorting Logic: I've refactored the tag sorting logic into a reusable set_tags private method within TransactionsController to ensure consistent sorting for tag selection in transaction forms and views.
  • Updated Transaction Forms: The transaction forms (_form.html.erb and show.html.erb) have been updated to utilize the pre-sorted @tags instance variable for tag selection dropdowns, ensuring users always see tags in the desired order.
  • Docker Port Mapping Adjustment: I've adjusted the Docker development environment port mapping from 3000:3000 to 3001:3000 in .devcontainer/docker-compose.yml.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments or fill out our survey to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements a natural sort order for tags, which is a great usability improvement. My review focuses on two main points: first, the new sorting logic is duplicated and has performance implications that can be addressed by moving it to the Tag model. Second, there's an unrelated port change in the Docker configuration that might be unintentional. Addressing these points will improve the maintainability and performance of the code.

@jjmata jjmata requested a review from Copilot August 10, 2025 07:08
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements natural sorting for tags throughout the application, replacing the previous alphabetical sorting with a more user-friendly approach that properly handles tags containing numbers.

  • Adds a new sorted_naturally method to the Tag model that sorts tags by separating alphabetic and numeric components
  • Updates all tag display locations to use natural sorting instead of alphabetical sorting
  • Removes unnecessary blank line in the tags controller

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
app/models/tag.rb Adds sorted_naturally method with natural sorting logic
app/views/transactions/show.html.erb Updates tag selection to use natural sorting
app/views/transactions/_form.html.erb Updates tag selection to use natural sorting
app/controllers/tags_controller.rb Updates tag index to use natural sorting and removes blank line

@jjmata
Copy link
Collaborator

jjmata commented Aug 10, 2025

Fixed a silly linter error so tests pass now, want to act on any of the Copilot comments @shrestha-bishal ? A couple of them are interesting ones!

@shrestha-bishal
Copy link
Author

Fixed a silly linter error so tests pass now, want to act on any of the Copilot comments @shrestha-bishal ? A couple of them are interesting ones!

Sure ill have a look into it. Thanks

@shrestha-bishal
Copy link
Author

Fixed a silly linter error so tests pass now, want to act on any of the Copilot comments @shrestha-bishal ? A couple of them are interesting ones!

@jjmata fixed it and all checks passed. good to merge. Thanks

@shrestha-bishal
Copy link
Author

shrestha-bishal commented Aug 11, 2025

The new commit also fixes the sorting issue in rule creation window on rule category and the value options.
After
Kazam_screenshot_00004
Kazam_screenshot_00000
Kazam_screenshot_00006
Kazam_screenshot_00005

@jjmata
Copy link
Collaborator

jjmata commented Aug 11, 2025

Want to give this one a look @Oaluiser / add a review?

@jjmata
Copy link
Collaborator

jjmata commented Aug 24, 2025

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Aug 24, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Aug 24, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Introduces natural sorting for tags and applies deterministic ordering across tags, categories, rule filters, and executors. Updates controllers and views to use the new tag sorting. Adds SQL and Ruby implementations for natural sort in Tag. Ensures options and registries return consistently ordered arrays.

Changes

Cohort / File(s) Summary of Changes
Tag natural sorting
app/models/tag.rb
Added SQL-backed scope sorted_naturally_db for natural order by name and trailing digits; added Ruby method self.sorted_naturally implementing tokenized natural sort.
UI: use natural tag order
app/controllers/tags_controller.rb, app/views/transactions/_form.html.erb, app/views/transactions/show.html.erb
Replaced .alphabetically with .sorted_naturally for tag lists in controller index and transaction form/show selects; only ordering changed.
Rules: deterministic option ordering
app/models/rule/action_executor/set_transaction_category.rb, app/models/rule/action_executor/set_transaction_tags.rb, app/models/rule/registry/transaction_resource.rb
Categories and tags options now ordered by name before pluck; registry now sorts condition filters and action executors by case-insensitive labels for deterministic order.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant VC as View/Controller
  participant Tag as Tag Model
  participant Rule as Rule Registry

  rect rgba(220,235,255,0.3)
    note over VC,Tag: Tags index / transaction selects
    User->>VC: Request tags / open form
    VC->>Tag: Current.family.tags.sorted_naturally
    Tag-->>VC: Array of tags in natural order
    VC-->>User: Rendered list/select with natural ordering
  end

  rect rgba(235,255,220,0.3)
    note over VC,Rule: Rule config options
    User->>VC: Open rule editor
    VC->>Rule: condition_filters / action_executors
    Rule-->>VC: Arrays sorted by label (case-insensitive)
    VC-->>User: Deterministically ordered UI options
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I lined up tags like carrots in a row,
Numbers and names in a natural flow.
Filters now march with tidy grace,
Executors follow, each in place.
I thump my paw—order restored! 🥕
Neat little lists I can’t ignore.

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/models/rule/action_executor/set_transaction_category.rb (1)

10-26: Guard against nil category to avoid NoMethodError on category.id

If value is nil/blank or the category ID doesn’t exist, category will be nil and category.id will raise. A no-op early return keeps rule execution safe.

   def execute(transaction_scope, value: nil, ignore_attribute_locks: false)
-    category = family.categories.find_by_id(value)
+    return if value.blank?
+    category = family.categories.find_by(id: value)
+    return unless category

Minor readability tweak (optional):

-    scope = transaction_scope
-
-    unless ignore_attribute_locks
-      scope = scope.enrichable(:category_id)
-    end
+    scope = ignore_attribute_locks ? transaction_scope : transaction_scope.enrichable(:category_id)
♻️ Duplicate comments (1)
app/models/tag.rb (1)

11-18: Fix SQL: empty-string cast to INTEGER will raise and breaks tags with no digits

For names without digits, REGEXP_REPLACE(name, '\D+', '') returns an empty string; casting '' to INTEGER raises in PostgreSQL. Also, add a case-insensitive prefix key to stabilize ordering. Replace the cast with NULLIF/COALESCE to avoid runtime errors.

Apply this diff:

   scope :sorted_naturally_db, -> {
     order(
       Arel.sql(
-        "REGEXP_REPLACE(name, '\\d+$', '') ASC, " \
-        "CAST(REGEXP_REPLACE(name, '\\D+', '') AS INTEGER) ASC"
+        "LOWER(REGEXP_REPLACE(name, '\\d+$', '')) ASC, " \
+        "COALESCE(NULLIF(REGEXP_REPLACE(name, '\\D+', ''), '')::INTEGER, 0) ASC, " \
+        "LOWER(name) ASC"
       )
     )
   }

Notes:

  • This still won’t perfectly “natural sort” multi-number segments (e.g., foo12bar3 vs foo12bar10), but it’s deterministic, safe, and performant for most lists. If you need true multi-token natural sorting at the DB layer, we can discuss a regexp_split_to_array-based approach or a function-based index.
🧹 Nitpick comments (4)
app/models/rule/action_executor/set_transaction_tags.rb (1)

7-7: Consider natural sort for rules to match the rest of the UI

Elsewhere tags are shown with natural ordering; rules UI currently uses pure alphabetical. If you want a consistent experience, switch to the same scope.

-    family.tags.order(:name).pluck(:name, :id)
+    family.tags.sorted_naturally_db.pluck(:name, :id)
app/models/rule/action_executor/set_transaction_category.rb (1)

6-8: Deterministic, alphabetical options for categories — good move

Ordering by name before pluck aligns the UI with the rest of the PR’s deterministic ordering goal.

If you want case-insensitive stability across DB collations, consider ordering by LOWER(name):

-    family.categories.order(:name).pluck(:name, :id)
+    family.categories.order(Arel.sql('LOWER(name) ASC'), :name).pluck(:name, :id)

Optionally, a composite index on (family_id, name) will keep this snappy at scale.

app/models/rule/registry/transaction_resource.rb (2)

6-12: Sorting condition filters for deterministic UI — thumbs up

This removes order drift between environments. To be extra defensive if any label could be nil, coerce to string:

-    ].sort_by { |filter| filter.label.downcase }
+    ].sort_by { |filter| filter.label.to_s.downcase }

22-28: Executor list ordering is now stable; consider DRYing the sort key

Both places sort by label.downcase. Extracting a tiny helper keeps them in sync.

Example:

 class Rule::Registry::TransactionResource < Rule::Registry
+  LABEL_KEY = ->(o) { o.label.to_s.downcase }
   def condition_filters
-    [ ... ].sort_by { |filter| filter.label.downcase }
+    [ ... ].sort_by(&LABEL_KEY)
   end

   def action_executors
     enabled_executors = [ ... ]
     if ai_enabled?
       enabled_executors << Rule::ActionExecutor::AutoCategorize.new(rule)
       enabled_executors << Rule::ActionExecutor::AutoDetectMerchants.new(rule)
     end
-    enabled_executors.sort_by { |executor| executor.label.downcase }
+    enabled_executors.sort_by(&LABEL_KEY)
   end

Optional: add an assertion in tests that the returned labels are already sorted to prevent regressions.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d04c874 and 8fae3e9.

📒 Files selected for processing (7)
  • app/controllers/tags_controller.rb (1 hunks)
  • app/models/rule/action_executor/set_transaction_category.rb (1 hunks)
  • app/models/rule/action_executor/set_transaction_tags.rb (1 hunks)
  • app/models/rule/registry/transaction_resource.rb (2 hunks)
  • app/models/tag.rb (2 hunks)
  • app/views/transactions/_form.html.erb (1 hunks)
  • app/views/transactions/show.html.erb (1 hunks)

def index
@tags = Current.family.tags.alphabetically

@tags = Current.family.tags.sorted_naturally
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use the scope to keep it relational (no eager loading)
Switch to the DB scope so this remains chainable and avoids loading into Ruby.

-    @tags = Current.family.tags.sorted_naturally
+    @tags = Current.family.tags.sorted_naturally_db
📝 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.

Suggested change
@tags = Current.family.tags.sorted_naturally
@tags = Current.family.tags.sorted_naturally_db
🤖 Prompt for AI Agents
In app/controllers/tags_controller.rb around line 5, the current call loads Tag
records into Ruby (non-relational) via a method that eagerly materializes
results; replace it with a chainable ActiveRecord scope so the result stays an
ActiveRecord::Relation. Use the DB scope version of the natural sort by either
merging the Tag.sorted_naturally scope into Current.family.tags
(Current.family.tags.merge(Tag.sorted_naturally)) or by applying
Tag.sorted_naturally.where(family: Current.family), and ensure you do not call
any loading methods like to_a, load, or similar.

Comment on lines +36 to +40
def self.sorted_naturally
all.to_a.sort_by do |tag|
tag.name.to_s.scan(/\d+|\D+/).map { |s| s =~ /\d+/ ? s.to_i : s.downcase }
end
end
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Make natural sort chainable and consistent (avoid loading entire table and ambiguity between scope vs class method)

self.sorted_naturally returns an Array (loads all records) and is a class method, not a scope. Calling Current.family.tags.sorted_naturally relies on Rails’ method_missing scoping semantics and returns a Ruby array, which:

  • Breaks chainability (can’t paginate, further filter, etc.).
  • Loads all matching rows into memory.
  • Diverges from the DB-level variant.

Prefer a scope for use in relations and keep the Ruby method only as an explicit fallback.

Two options:

Option A (recommended): consolidate on DB scope and remove/rename the Ruby method.

- def self.sorted_naturally
-   all.to_a.sort_by do |tag|
-     tag.name.to_s.scan(/\d+|\D+/).map { |s| s =~ /\d+/ ? s.to_i : s.downcase }
-   end
- end
+ # Optional: keep a clearly named Ruby-only helper if ever needed explicitly
+ def self.sorted_naturally_ruby(collection = all)
+   collection.to_a.sort_by do |tag|
+     tag.name.to_s.scan(/\d+|\D+/).map { |s| s =~ /\d+/ ? s.to_i : s.downcase }
+   end
+ end

Option B: keep both, but add a scope alias for consistent call sites:

+ scope :sorted_naturally, -> { sorted_naturally_db }

If you’d like, I can follow through with a repo-wide rename to use sorted_naturally_db (or the alias) wherever relations are expected.

📝 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.

Suggested change
def self.sorted_naturally
all.to_a.sort_by do |tag|
tag.name.to_s.scan(/\d+|\D+/).map { |s| s =~ /\d+/ ? s.to_i : s.downcase }
end
end
# Optional: keep a clearly named Ruby-only helper if ever needed explicitly
def self.sorted_naturally_ruby(collection = all)
collection.to_a.sort_by do |tag|
tag.name.to_s.scan(/\d+|\D+/).map { |s| s =~ /\d+/ ? s.to_i : s.downcase }
end
end

<%= f.fields_for :entryable do |ef| %>
<%= ef.select :tag_ids,
Current.family.tags.alphabetically.pluck(:name, :id),
Current.family.tags.sorted_naturally.pluck(:name, :id),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Prefer DB-level sort for options to avoid loading all records and to keep consistent ordering

Using the scope keeps pluck on the database and avoids Ruby-level sorting overhead.

-                    Current.family.tags.sorted_naturally.pluck(:name, :id),
+                    Current.family.tags.sorted_naturally_db.pluck(:name, :id),
📝 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.

Suggested change
Current.family.tags.sorted_naturally.pluck(:name, :id),
Current.family.tags.sorted_naturally_db.pluck(:name, :id),


<%= ef.select :tag_ids,
Current.family.tags.alphabetically.pluck(:name, :id),
Current.family.tags.sorted_naturally.pluck(:name, :id),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Align with DB scope for consistent, efficient natural ordering

Same reasoning as in _form: keep sorting in SQL and let pluck run on the DB.

-                    Current.family.tags.sorted_naturally.pluck(:name, :id),
+                    Current.family.tags.sorted_naturally_db.pluck(:name, :id),
📝 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.

Suggested change
Current.family.tags.sorted_naturally.pluck(:name, :id),
Current.family.tags.sorted_naturally_db.pluck(:name, :id),
🤖 Prompt for AI Agents
app/views/transactions/show.html.erb around line 80: the view currently calls
sorted_naturally in Ruby before pluck, causing sorting to happen in memory;
change this to use an ActiveRecord/SQL ordering so the DB performs the natural
sort and then pluck runs on the DB. Replace the in-memory sort with the model
scope or an order(...) that implements natural ordering (or ensure
sorted_naturally is an ActiveRecord scope) so you call
.order(...)/.sorted_naturally_scope.pluck(:name, :id) instead of sorting the
array after loading.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants