rails_rules 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad3a7a8f20ef19b59318636327c5011cceabdbb3c8142f2903f8c38e9f22196b
4
- data.tar.gz: 0ff5347624eb8571a9bb90ab5590f1e14a7353e4ed875e7a604a9b0a94637bb7
3
+ metadata.gz: a9be68c0ccba503fc0e42a71a14673463a20af8ffd94a914fb6102b5eb13262e
4
+ data.tar.gz: ea19c7d51efcca0cdeae7cb271eba93eb9a2be96c3d990d2202ba63855f1160c
5
5
  SHA512:
6
- metadata.gz: 62b861c5be2529311b5cae394ce1657d4541e95acf6f3e38ad84854510abb2970923951477e3c805b8237733a53630a712532602cf9d45e401502ad0da4d5a06
7
- data.tar.gz: cf701583412aaed050a585b23a2c8d027b78aec6b499ebe9e037d9b629b7ddc7a12635dd624ea8d1e094892d1017742278a203147b8f8a6f0bae12e2e513478e
6
+ metadata.gz: 526d14c32a134a30ba92f58277549b3f624892a96928b4b1e9249fcfef7b703e3d05b752c62d1b123e5e578a4b25fbfb194c3c08b0de124902abc27ce48597ac
7
+ data.tar.gz: 230209de40658d6ec600028ed9b3ef14382b2439edd0ff86df0f9f140bd1980e0785b6c85738de7ee8dcae5048a9c27f88180b793e3e316576e93eb7fd285974
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright Andy Waite
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # RailsRules
2
2
 
3
- # This is a placeholder - the gem does not yet have any functionality.
3
+ This gem provides a set of [rules](https://docs.cursor.com/context/rules) files for working with AI tooling.
4
4
 
5
- This gem helps with creating and managing [rules](https://docs.cursor.com/context/rules) files for tools such as Cursor.
5
+ At this early stage, the functionality is limited to creating `.cursor/rules` files for [Cursor](https://www.cursor.com).
6
+
7
+ The rules files were originally created by [Kieran Klaassen](https://github.com/kieranklaassen) and contributed to [Jumpstart Pro Rails](jumpstartrails.com), and are reproduced with permission.
6
8
 
7
9
  ## Installation
8
10
 
data/Rakefile CHANGED
@@ -1,10 +1,3 @@
1
- # frozen_string_literal: true
1
+ require "bundler/setup"
2
2
 
3
3
  require "bundler/gem_tasks"
4
- require "minitest/test_task"
5
-
6
- Minitest::TestTask.create
7
-
8
- require "standard/rake"
9
-
10
- task default: %i[test standard]
@@ -0,0 +1,7 @@
1
+ Description:
2
+ Create rules files for AI tooling
3
+
4
+ Example:
5
+ bin/rails generate rails_rules:default
6
+
7
+ This will create set of files within `.rules/cursor`.
@@ -0,0 +1,25 @@
1
+ module RailsRules
2
+ class DefaultGenerator < Rails::Generators::Base
3
+ TARGET_PATH = ".cursor/rules"
4
+ source_root File.expand_path("templates", __dir__)
5
+ def create_rules_cursor_directory
6
+ empty_directory TARGET_PATH unless Dir.exist?(TARGET_PATH)
7
+ end
8
+
9
+ def copy_files
10
+ directory_path = "lib/generators/default/templates"
11
+ files = [
12
+ "000-cursor-rules.mdc",
13
+ "1000-rails-general.mdc",
14
+ "1001-rails-controllers.mdc",
15
+ "1002-rails-models.mdc",
16
+ "1003-rails-views.mdc",
17
+ "1004-javascript-stimulus.mdc",
18
+ "1005-service-objects.mdc",
19
+ "1006-testing.mdc",
20
+ "1007-tailwindcss.mdc"
21
+ ]
22
+ files.each { |file| copy_file file, File.join(TARGET_PATH, File.basename(file)) }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,113 @@
1
+ ---
2
+ description: Use ALWAYS when asked to CREATE A RULE or UPDATE A RULE or taught a lesson from the user that should be retained as a new rule for Cursor
3
+ globs: [".cursor/rules/*.mdc"]
4
+ ---
5
+ # Cursor Rules Format
6
+ ## Core Structure
7
+
8
+ ```mdc
9
+ ---
10
+ description: ACTION when TRIGGER to OUTCOME
11
+ globs: *.mdc
12
+ ---
13
+
14
+ # Rule Title
15
+
16
+ ## Context
17
+ - When to apply this rule
18
+ - Prerequisites or conditions
19
+
20
+ ## Requirements
21
+ - Concise, actionable items
22
+ - Each requirement must be testable
23
+
24
+ ## Examples
25
+ <example>
26
+ Good concise example with explanation
27
+ </example>
28
+
29
+ <example type="invalid">
30
+ Invalid concise example with explanation
31
+ </example>
32
+ ```
33
+
34
+ ## File Organization
35
+
36
+ ### Location
37
+ - Path: `.cursor/rules/`
38
+ - Extension: `.mdc`
39
+
40
+ ### Naming Convention
41
+ PREFIX-name.mdc where PREFIX is:
42
+ - 0XX: Core standards
43
+ - 1XX: Tool configs
44
+ - 3XX: Testing standards
45
+ - 1XXX: Language rules
46
+ - 2XXX: Framework rules
47
+ - 8XX: Workflows
48
+ - 9XX: Templates
49
+ - _name.mdc: Private rules
50
+
51
+ ### Glob Pattern Examples
52
+ Common glob patterns for different rule types:
53
+ - Core standards: .cursor/rules/*.mdc
54
+ - Language rules: src/**/*.{js,ts}
55
+ - Testing standards: **/*.test.{js,ts}
56
+ - React components: src/components/**/*.tsx
57
+ - Documentation: docs/**/*.md
58
+ - Configuration files: *.config.{js,json}
59
+ - Build artifacts: dist/**/*
60
+ - Multiple extensions: src/**/*.{js,jsx,ts,tsx}
61
+ - Multiple files: dist/**/*, docs/**/*.md
62
+
63
+ ## Required Fields
64
+
65
+ ### Frontmatter
66
+ - description: ACTION TRIGGER OUTCOME format
67
+ - globs: `glob pattern for files and folders`
68
+
69
+ ### Body
70
+ - <version>X.Y.Z</version>
71
+ - context: Usage conditions
72
+ - requirements: Actionable items
73
+ - examples: Both valid and invalid
74
+
75
+ ## Formatting Guidelines
76
+
77
+ - Use Concise Markdown primarily
78
+ - XML tags limited to:
79
+ - <example>
80
+ - <danger>
81
+ - <required>
82
+ - <rules>
83
+ - <rule>
84
+ - <critical>
85
+ - <version>
86
+ - Always indent content within XML or nested XML tags by 2 spaces
87
+ - Keep rules as short as possbile
88
+ - Use Mermaid syntax if it will be shorter or clearer than describing a complex rule
89
+ - Use Emojis where appropriate to convey meaning that will improve rule understanding by the AI Agent
90
+ - Keep examples as short as possible to clearly convey the positive or negative example
91
+
92
+ ## AI Optimization Tips
93
+
94
+ 1. Use precise, deterministic ACTION TRIGGER OUTCOME format in descriptions
95
+ 2. Provide concise positive and negative example of rule application in practice
96
+ 3. Optimize for AI context window efficiency
97
+ 4. Remove any non-essential or redundant information
98
+ 5. Use standard glob patterns without quotes (e.g., *.js, src/**/*.ts)
99
+
100
+ ## AI Context Efficiency
101
+
102
+ 1. Keep frontmatter description under 120 characters (or less) while maintaining clear intent for rule selection by AI AGent
103
+ 2. Limit examples to essential patterns only
104
+ 3. Use hierarchical structure for quick parsing
105
+ 4. Remove redundant information across sections
106
+ 5. Maintain high information density with minimal tokens
107
+ 6. Focus on machine-actionable instructions over human explanations
108
+
109
+ <critical>
110
+ - NEVER include verbose explanations or redundant context that increases AI token overhead
111
+ - Keep file as short and to the point as possible BUT NEVER at the expense of sacrificing rule impact and usefulness for the AI Agent.
112
+ - the front matter can ONLY have the fields description and globs.
113
+ </critical>
@@ -0,0 +1,87 @@
1
+ ---
2
+ description: Follow general Rails 8.0 conventions and patterns
3
+ globs: ["**/*.rb", "app/**/*.erb"]
4
+ ---
5
+
6
+ # General Rails 8.0 Conventions
7
+
8
+ ## Context
9
+
10
+ - In Ruby on Rails 8.0 application
11
+ - Using modern Rails features and patterns
12
+ - Follows Ruby style conventions
13
+
14
+ ## Requirements
15
+
16
+ - Follow Ruby style guidelines (2 spaces for indentation, snake_case for variables/methods)
17
+ - Use Service Objects for complex business logic
18
+ - Use concerns for shared functionality
19
+ - Use modules for namespacing and code organization
20
+ - Use class Module::ClassName instead of nested module/class definitions
21
+ - Add YARD documentation to methods and classes
22
+ - Use positional arguments in enums and other Rails 8.0 features
23
+ - Use credentials with Rails.application.credentials syntax
24
+ - Pass models to jobs, not IDs (they'll be serialized automatically)
25
+ - Use has_prefix_id for models with UUIDs
26
+ - Use ActiveRecord conventions for database operations
27
+ - Follow RESTful conventions for controllers
28
+ - Use Tailwind CSS for styling views
29
+ - Make all UI components responsive and support dark mode
30
+ - Use Hotwire (Turbo, Stimulus) for JavaScript functionality
31
+ - Ensure accessibility in all UI components
32
+
33
+ ## Examples
34
+
35
+ <example>
36
+ ```ruby
37
+ # Good - Using Rails 8.0 credentials
38
+ api_key = Rails.application.credentials.anthropic[:api_key]
39
+
40
+ # Good - Using Service Object
41
+
42
+ user = CreateUserService.new(user_params).run
43
+
44
+ # Good - Using positional arguments in enum
45
+
46
+ class Article < ApplicationRecord enum status: [:draft, :published, :archived] end
47
+
48
+ # Good - Class namespacing
49
+
50
+ class Api::V1::UsersController < ApplicationController
51
+
52
+ # ...
53
+
54
+ end
55
+
56
+ ````
57
+ </example>
58
+
59
+ <example type="invalid">
60
+ ```ruby
61
+ # Bad - Using outdated credentials pattern
62
+ api_key = Rails.application.secrets.anthropic_api_key
63
+
64
+ # Bad - Complex logic in controller
65
+ def create
66
+ @user = User.new(user_params)
67
+ if @user.save
68
+ # Complex business logic here
69
+ end
70
+ end
71
+
72
+ # Bad - Using hash for enum
73
+ class Article < ApplicationRecord
74
+ enum status: { draft: 0, published: 1, archived: 2 }
75
+ end
76
+
77
+ # Bad - Nested modules
78
+ module Api
79
+ module V1
80
+ class UsersController < ApplicationController
81
+ # ...
82
+ end
83
+ end
84
+ end
85
+ ````
86
+
87
+ </example>
@@ -0,0 +1,82 @@
1
+ ---
2
+ description: Follow Rails 8.0 controller standards and patterns when creating or editing controllers
3
+ globs: ["app/controllers/**/*.rb"]
4
+ ---
5
+
6
+ # Rails Controller Standards
7
+
8
+ ## Context
9
+
10
+ - In Ruby on Rails 8.0 controllers
11
+ - Controllers should follow RESTful conventions
12
+ - Comment style includes HTTP verb and path
13
+
14
+ ## Requirements
15
+
16
+ - Add controller method comments with HTTP verb and full path
17
+ - Use resource-based naming
18
+ - Use before_action for common setup
19
+ - Namespace API controllers under Api::V1
20
+ - Use respond_to for format handling (HTML/JSON)
21
+ - Use pagy for pagination: `@pagy, @resources = pagy(Resource.sort_by_params(params[:sort], sort_direction))`
22
+ - Use `status: :see_other` for redirects after DELETE
23
+ - Use `status: :unprocessable_entity` for failed creates/updates
24
+ - Return 404 for records not found with `rescue ActiveRecord::RecordNotFound`
25
+ - Use `params.expect(:resource)` instead of `params.require`
26
+ - Include authentication callbacks where needed
27
+
28
+ ## Examples
29
+
30
+ <example>
31
+ ```ruby
32
+ class CategoriesController < ApplicationController
33
+ before_action :set_category, only: [:show, :edit, :update, :destroy]
34
+
35
+ # GET /categories
36
+
37
+ def index @pagy, @categories = pagy(Category.sort_by_params(params[:sort], sort_direction)) end
38
+
39
+ # POST /categories
40
+
41
+ def create @category = Category.new(category_params)
42
+
43
+ respond_to do |format|
44
+ if @category.save
45
+ format.html { redirect_to @category, notice: "Category was successfully created." }
46
+ format.json { render :show, status: :created, location: @category }
47
+ else
48
+ format.html { render :new, status: :unprocessable_entity }
49
+ format.json { render json: @category.errors, status: :unprocessable_entity }
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ private
56
+
57
+ def set_category @category = Category.find(params.expect(:id)) rescue ActiveRecord::RecordNotFound redirect_to categories_path end
58
+
59
+ def category_params params.expect(category: [:name, :description]) end end
60
+
61
+ ````
62
+ </example>
63
+
64
+ <example type="invalid">
65
+ ```ruby
66
+ class CategoriesController < ApplicationController
67
+ def index
68
+ @categories = Category.all
69
+ end
70
+
71
+ def create
72
+ @category = Category.new(params.permit(:name, :description))
73
+ if @category.save
74
+ redirect_to @category
75
+ else
76
+ render :new
77
+ end
78
+ end
79
+ end
80
+ ````
81
+
82
+ </example>
@@ -0,0 +1,105 @@
1
+ ---
2
+ description: Follow Rails 8.0 model standards and patterns when creating or modifying models
3
+ globs: ["app/models/**/*.rb"]
4
+ ---
5
+
6
+ # Rails Model Standards
7
+
8
+ ## Context
9
+
10
+ - In Ruby on Rails 8.0 models
11
+ - Use modern Rails patterns like store_accessor, concerns, and modules
12
+ - Add YARD documentation
13
+
14
+ ## Requirements
15
+
16
+ - Add YARD documentation to all methods
17
+ - Use positional arguments in enums
18
+ - Include a separate module for specific roles, validations, or other functionality
19
+ - Define constants at the top of the file
20
+ - Use has_prefix_id for models with UUIDs
21
+ - Use strong typing with attribute declarations
22
+ - Use normalizes for attribute normalization
23
+ - Include Searchable concern for models that need search
24
+ - Use counter_cache for belongs_to relationships that need counts
25
+ - Use store_accessor for models with JSON columns
26
+ - Use acts_as_tenant for multi-tenant models
27
+ - Include validation for uploaded files with resizable_image
28
+ - Pass models to jobs, not IDs (they will be serialized)
29
+ - Use strong typing in models with `attribute` declarations
30
+
31
+ ## Examples
32
+
33
+ <example>
34
+ ```ruby
35
+ # frozen_string_literal: true
36
+
37
+ class Plan < ApplicationRecord has_prefix_id :plan
38
+
39
+ INTERVALS = [:month, :year].freeze
40
+
41
+ # Store JSON attributes in the details column
42
+
43
+ # @return [Array<String>] List of features for this plan
44
+
45
+ store_accessor :details, :features, :stripe_tax
46
+
47
+ # Define default attributes
48
+
49
+ attribute :currency, default: "usd"
50
+
51
+ # Normalize attributes before saving
52
+
53
+ normalizes :currency, with: ->(currency) { currency.downcase }
54
+
55
+ # Validations
56
+
57
+ validates :name, :amount, :interval, presence: true validates :currency, presence: true, format: {with: /\A[a-zA-Z]{3}\z/, message: "must be a 3-letter ISO currency code"} validates :interval, inclusion: INTERVALS validates :trial_period_days, numericality: {only_integer: true} validates :unit_label, presence: {if: :charge_per_unit?}
58
+
59
+ # Scopes
60
+
61
+ scope :hidden, -> { where(hidden: true) } scope :visible, -> { where(hidden: [nil, false]) } scope :monthly, -> { where(interval: :month) } scope :yearly, -> { where(interval: :year) } scope :sorted, -> { order(amount: :asc) }
62
+
63
+ # Returns a list of features for this plan
64
+
65
+ # @return [Array<String>] List of features
66
+
67
+ def features Array.wrap(super) end
68
+
69
+ # Checks if this plan has a trial period
70
+
71
+ # @return [Boolean] True if the plan has a trial
72
+
73
+ def has_trial? trial_period_days > 0 end
74
+
75
+ # Checks if this plan is a monthly plan
76
+
77
+ # @return [Boolean] True if the plan is monthly
78
+
79
+ def monthly? interval == "month" end end
80
+
81
+ ````
82
+ </example>
83
+
84
+ <example type="invalid">
85
+ ```ruby
86
+ class Plan < ApplicationRecord
87
+ def self.free
88
+ where(name: "Free").first_or_initialize
89
+ end
90
+
91
+ def features
92
+ super
93
+ end
94
+
95
+ def has_trial?
96
+ self.trial_period_days > 0
97
+ end
98
+
99
+ def self.get_all
100
+ all
101
+ end
102
+ end
103
+ ````
104
+
105
+ </example>
@@ -0,0 +1,107 @@
1
+ ---
2
+ description: Follow Rails 8.0 view standards with Tailwind CSS in ERB templates
3
+ globs: ["app/views/**/*.html.erb"]
4
+ ---
5
+
6
+ # Rails View Standards with Tailwind
7
+
8
+ ## Context
9
+
10
+ - In Ruby on Rails 8.0 views
11
+ - Using Tailwind CSS for styling
12
+ - ERB templates with responsive design
13
+ - Support for dark mode
14
+
15
+ ## Requirements
16
+
17
+ - Always use Tailwind CSS classes for styling
18
+ - Make all views responsive with breakpoints (sm:, md:, lg:, xl:, 2xl:)
19
+ - Add dark mode support (dark: prefix for Tailwind classes)
20
+ - For forms:
21
+ - Add autofocus: true on first input for new records
22
+ - Add asterisks (\*) for required fields
23
+ - Use HTML5 validation
24
+ - Use form-group, form-control classes
25
+ - For buttons use btn btn-primary, btn-small, btn-block, etc.
26
+ - Use f.button instead of f.submit
27
+ - Add proper disable_with for buttons showing loading state: disable_with: "Saving..."
28
+ - Use content_for :title for page titles
29
+ - For components like cards, alerts, navigation:
30
+ - Use corresponding Tailwind component classes
31
+ - Use Turbo Stream for real-time updates
32
+ - Use dom_id for HTML element IDs
33
+ - Use partials for reusable components
34
+ - Use proper spacing with Tailwind (mt-4, mb-4, py-2, etc.)
35
+ - Implement proper aria attributes for accessibility
36
+
37
+ ## Examples
38
+
39
+ <example>
40
+ ```erb
41
+ <% content_for :title, "Edit Profile" %>
42
+
43
+ <div class="container px-4 mx-auto my-8">
44
+ <div class="flex items-center justify-between mb-4">
45
+ <h1 class="h3">Edit Profile</h1>
46
+ </div>
47
+
48
+ <div class="p-8 bg-white dark:bg-gray-900 dark:border dark:border-gray-700 rounded shadow">
49
+ <%= form_with(model: @user, local: true) do |f| %>
50
+ <div class="form-group">
51
+ <%= f.label :name, "Name *" %>
52
+ <%= f.text_field :name, class: "form-control", autofocus: true, required: true %>
53
+ </div>
54
+
55
+ <div class="form-group">
56
+ <%= f.label :email, "Email *" %>
57
+ <%= f.email_field :email, class: "form-control", required: true %>
58
+ </div>
59
+
60
+ <div class="form-group">
61
+ <%= f.label :avatar %>
62
+ <div class="file-input-group">
63
+ <label for="avatar" class="btn btn-tertiary">
64
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-5 h-5 mr-1">
65
+ <path fill-rule="evenodd" d="M1.5 6a2.25 2.25 0 0 1 2.25-2.25h16.5A2.25 2.25 0 0 1 22.5 6v12a2.25 2.25 0 0 1-2.25 2.25H3.75A2.25 2.25 0 0 1 1.5 18V6ZM3 16.06V18c0 .414.336.75.75.75h16.5A.75.75 0 0 0 21 18v-1.94l-2.69-2.689a1.5 1.5 0 0 0-2.12 0l-.88.879.97.97a.75.75 0 1 1-1.06 1.06l-5.16-5.159a1.5 1.5 0 0 0-2.12 0L3 16.061Zm10.125-7.81a1.125 1.125 0 1 1 2.25 0 1.125 1.125 0 0 1-2.25 0Z" clip-rule="evenodd" />
66
+ </svg>
67
+ <span>Upload Avatar</span>
68
+ </label>
69
+ <%= f.file_field :avatar, id: "avatar" %>
70
+ </div>
71
+ </div>
72
+
73
+ <div class="mt-6">
74
+ <%= f.button button_text("Save Changes", disable_with: "Saving..."), class: "btn btn-primary" %>
75
+ </div>
76
+ <% end %>
77
+
78
+ </div>
79
+ </div>
80
+ ```
81
+ </example>
82
+
83
+ <example type="invalid">
84
+ ```erb
85
+ <h1>Edit Profile</h1>
86
+
87
+ <div>
88
+ <%= form_with(model: @user, local: true) do |f| %>
89
+ <div>
90
+ <%= f.label :name %>
91
+ <%= f.text_field :name %>
92
+ </div>
93
+
94
+ <div>
95
+ <%= f.label :email %>
96
+ <%= f.email_field :email %>
97
+ </div>
98
+
99
+ <div>
100
+ <%= f.submit "Save Changes" %>
101
+ </div>
102
+
103
+ <% end %>
104
+
105
+ </div>
106
+ ```
107
+ </example>
@@ -0,0 +1,90 @@
1
+ ---
2
+ description: Follow JavaScript and Stimulus controller standards when creating or modifying JS files
3
+ globs: ["app/javascript/**/*.js"]
4
+ ---
5
+
6
+ # JavaScript and Stimulus Controller Standards
7
+
8
+ ## Context
9
+
10
+ - In Ruby on Rails 8.0 JavaScript
11
+ - Using Stimulus.js for interactive components
12
+ - Using ES6 features
13
+ - Using TailwindCSS Stimulus Components
14
+
15
+ ## Requirements
16
+
17
+ - Use ES6 syntax (arrow functions, destructuring, etc.)
18
+ - Add comments at the top of Stimulus controllers explaining their purpose and usage examples
19
+ - Follow Stimulus controller naming conventions
20
+ - Use data-controller, data-action, and data-[controller]-target attributes
21
+ - Define static targets, values, and classes at the top of controller classes
22
+ - Initialize connections in the connect() lifecycle method
23
+ - Clean up resources in the disconnect() lifecycle method
24
+ - Use the tailwindcss-stimulus-components library for common components:
25
+ - Alert, Dropdown, Modal, Tabs, Popover, Toggle, Slideover
26
+ - Keep controller actions focused on a single responsibility
27
+ - Use event delegation where appropriate
28
+ - Document values properties with their types and defaults
29
+ - Avoid DOM manipulation where possible, prefer toggling classes
30
+
31
+ ## Examples
32
+
33
+ <example>
34
+ ```javascript
35
+ // Example usage:
36
+ // <div data-controller="tooltip" data-tooltip-content-value="Hello world"></div>
37
+
38
+ import { Controller } from "@hotwired/stimulus" import { autoUpdate, autoPlacement, computePosition, offset, arrow } from "@floating-ui/dom"
39
+
40
+ export default class extends Controller { // Define expected properties and their types static values = { content: String, placement: String, offset: { type: Number, default: 6 }, allowHtml: { type: Boolean, default: true } }
41
+
42
+ // Initialize on connection connect() { this.createTooltipElements() this.cleanup = autoUpdate(this.element, this.tooltip, this.updatePosition.bind(this)) this.addEvents() }
43
+
44
+ // Clean up resources on disconnect disconnect() { this.removeEvents() this.tooltip?.remove() this.cleanup?.() }
45
+
46
+ // Creates DOM elements for the tooltip createTooltipElements() { this.tooltip = document.createElement("div") this.tooltip.className = "tooltip" this.tooltip.setAttribute("role", "tooltip")
47
+
48
+ this.tooltipContent = document.createElement("div")
49
+ this.tooltipContent.className = "tooltip-content"
50
+
51
+ this.tooltipArrow = document.createElement("div")
52
+ this.tooltipArrow.className = "tooltip-arrow"
53
+
54
+ this.tooltip.appendChild(this.tooltipContent)
55
+ this.tooltip.appendChild(this.tooltipArrow)
56
+ document.body.appendChild(this.tooltip)
57
+
58
+ this.updateContent()
59
+
60
+ }
61
+
62
+ // Updates tooltip content when content value changes contentValueChanged() { this.updateContent() } }
63
+
64
+ ````
65
+ </example>
66
+
67
+ <example type="invalid">
68
+ ```javascript
69
+ import { Controller } from "@hotwired/stimulus"
70
+
71
+ export default class extends Controller {
72
+ connect() {
73
+ var self = this
74
+ var tooltip = document.createElement("div")
75
+ tooltip.className = "tooltip"
76
+ tooltip.innerHTML = this.data.get("content")
77
+ document.body.appendChild(tooltip)
78
+
79
+ this.element.addEventListener("mouseenter", function() {
80
+ tooltip.style.display = "block"
81
+ })
82
+
83
+ this.element.addEventListener("mouseleave", function() {
84
+ tooltip.style.display = "none"
85
+ })
86
+ }
87
+ }
88
+ ````
89
+
90
+ </example>
@@ -0,0 +1,129 @@
1
+ ---
2
+ description: Follow service object standards when creating or modifying service classes
3
+ globs: ["app/services/**/*.rb"]
4
+ ---
5
+
6
+ # Service Object Standards
7
+
8
+ ## Context
9
+
10
+ - In Ruby on Rails 8.0 service objects
11
+ - Used to encapsulate business logic
12
+ - Follows PORO (Plain Old Ruby Object) principles
13
+
14
+ ## Requirements
15
+
16
+ - Create service objects in app/services directory
17
+ - Name services with verb + noun format ending with "Service" (e.g., CreateUserService)
18
+ - Use class Service::ClassName instead of nested module class
19
+ - Use initialize method to accept parameters
20
+ - Include YARD documentation for all methods
21
+ - Implement a run (or call, perform, execute) method that performs the service's main action
22
+ - Return a result object or the expected return value
23
+ - Keep service objects focused on a single responsibility
24
+ - Validate parameters in initialize or a separate validate method
25
+ - Handle errors gracefully, either through exceptions or a result object
26
+ - Make services testable with Minitest
27
+ - Only modify state through explicit interfaces, not by relying on side effects
28
+
29
+ ## Examples
30
+
31
+ <example>
32
+ ```ruby
33
+ # frozen_string_literal: true
34
+
35
+ # Service for creating a new user with account
36
+
37
+ #
38
+
39
+ # @example
40
+
41
+ # service = CreateUserService.new(email: "test@example.com", name: "Test User")
42
+
43
+ # user = service.run
44
+
45
+ #
46
+
47
+ class CreateUserService
48
+
49
+ # Initialize the service with user attributes
50
+
51
+ #
52
+
53
+ # @param [Hash] attributes The user attributes
54
+
55
+ # @option attributes [String] :email User's email
56
+
57
+ # @option attributes [String] :name User's name
58
+
59
+ # @option attributes [String] :password User's password
60
+
61
+ def initialize(attributes) @attributes = attributes @account_name = attributes.delete(:account_name) || "My Account" end
62
+
63
+ # Runs the service to create a user and account
64
+
65
+ #
66
+
67
+ # @return [User] The created user
68
+
69
+ # @raise [ActiveRecord::RecordInvalid] If validation fails
70
+
71
+ def run User.transaction do create_user create_account create_account_user end
72
+
73
+ @user
74
+
75
+ end
76
+
77
+ private
78
+
79
+ # Creates a new user with the provided attributes
80
+
81
+ #
82
+
83
+ # @return [User] The new user
84
+
85
+ def create_user @user = User.create!(@attributes) end
86
+
87
+ # Creates a new account for the user
88
+
89
+ #
90
+
91
+ # @return [Account] The new account
92
+
93
+ def create_account @account = Account.create!(name: @account_name, owner: @user) end
94
+
95
+ # Creates the account user relationship
96
+
97
+ #
98
+
99
+ # @return [AccountUser] The account user relationship
100
+
101
+ def create_account_user AccountUser.create!( account: @account, user: @user, admin: true ) end end
102
+
103
+ ````
104
+ </example>
105
+
106
+ <example type="invalid">
107
+ ```ruby
108
+ module Services
109
+ module Users
110
+ class Create
111
+ def initialize(email, name, password)
112
+ @email = email
113
+ @name = name
114
+ @password = password
115
+ end
116
+
117
+ def call
118
+ user = User.new(email: @email, name: @name, password: @password)
119
+ user.save
120
+ account = Account.create(name: "My Account")
121
+ AccountUser.create(user: user, account: account)
122
+ return user
123
+ end
124
+ end
125
+ end
126
+ end
127
+ ````
128
+
129
+ </example>
@@ -0,0 +1,71 @@
1
+ ---
2
+ description: Follow testing standards when creating or modifying tests
3
+ globs: ["test/**/*.rb"]
4
+ ---
5
+
6
+ # Testing Standards
7
+
8
+ ## Context
9
+
10
+ - In Ruby on Rails 8.0 test files
11
+ - Using Minitest for testing
12
+ - Tests should be thorough and maintainable
13
+
14
+ ## Requirements
15
+
16
+ - Use Minitest for all tests
17
+ - Write tests for models, controllers, services, and other components
18
+ - Use fixtures for test data
19
+ - Organize tests into appropriate test classes
20
+ - Use descriptive test names with test\_ prefix (e.g., test_valid_user_can_login)
21
+ - Use assertions that best match what you're testing
22
+ - Keep tests focused on a single concern
23
+ - Use setup/teardown methods for common setup code
24
+ - Use test helpers for reusable test code
25
+ - Test both happy and sad paths
26
+ - Test edge cases and boundary conditions
27
+ - Avoid testing the framework itself
28
+ - Keep tests independent and idempotent
29
+ - Don't pipe test output into cat (`bin/rails test` not `bin/rails test | cat`)
30
+
31
+ ## Examples
32
+
33
+ <example>
34
+ ```ruby
35
+ require "test_helper"
36
+
37
+ class UserTest < ActiveSupport::TestCase setup do @user = users(:one) @account = accounts(:one) end
38
+
39
+ test "valid user" do assert @user.valid? end
40
+
41
+ test "invalid without email" do @user.email = nil refute @user.valid? assert_not_nil @user.errors[:email] end
42
+
43
+ test "can be assigned to account" do account_user = AccountUser.new(user: @user, account: @account) assert account_user.valid? end
44
+
45
+ test "can have admin role" do account_user = AccountUser.new(user: @user, account: @account, admin: true) assert account_user.admin? assert_includes account_user.active_roles, :admin end end
46
+
47
+ ````
48
+ </example>
49
+
50
+ <example type="invalid">
51
+ ```ruby
52
+ require "test_helper"
53
+
54
+ class UserTest < ActiveSupport::TestCase
55
+ def setup
56
+ @user = User.new(name: "Test User", email: "test@example.com")
57
+ end
58
+
59
+ def test_it_works
60
+ @user.save
61
+ assert_equal 1, User.count
62
+ User.all.each do |u|
63
+ assert u.valid?
64
+ end
65
+ @user.update(email: nil)
66
+ assert_equal false, @user.valid?
67
+ end
68
+ end
69
+ ````
70
+
71
+ </example>
@@ -0,0 +1,204 @@
1
+ ---
2
+ name: tailwind_v4
3
+ description: Guide for using Tailwind CSS v4 instead of v3.x
4
+ globs: ["**/*.{js,css,erb,rb}"]
5
+ tags:
6
+ - tailwind
7
+ - css
8
+ ---
9
+
10
+ # Tailwind CSS v4
11
+
12
+ ## Core Changes
13
+
14
+ - **CSS-first configuration**: Configuration is now done in CSS instead of JavaScript
15
+ - Use `@theme` directive in CSS instead of `tailwind.config.js`
16
+ - Example:
17
+ ```css
18
+ @import "tailwindcss";
19
+
20
+ @theme {
21
+ --font-display: "Satoshi", "sans-serif";
22
+ --breakpoint-3xl: 1920px;
23
+ --color-avocado-500: oklch(0.84 0.18 117.33);
24
+ --ease-fluid: cubic-bezier(0.3, 0, 0, 1);
25
+ }
26
+ ```
27
+ - Legacy `tailwind.config.js` files can still be imported using the `@config` directive:
28
+ ```css
29
+ @import "tailwindcss";
30
+ @config "../../tailwind.config.js";
31
+ ```
32
+ - **CSS import syntax**: Use `@import "tailwindcss"` instead of `@tailwind` directives
33
+ - Old: `@tailwind base; @tailwind components; @tailwind utilities;`
34
+ - New: `@import "tailwindcss";`
35
+
36
+ - **Package changes**:
37
+ - PostCSS plugin is now `@tailwindcss/postcss` (not `tailwindcss`)
38
+ - CLI is now `@tailwindcss/cli`
39
+ - Vite plugin is `@tailwindcss/vite`
40
+ - No need for `postcss-import` or `autoprefixer` anymore
41
+
42
+ - **Native CSS cascade layers**: Uses real CSS `@layer` instead of Tailwind's custom implementation
43
+
44
+ ## Theme Configuration
45
+
46
+ - **CSS theme variables**: All design tokens are available as CSS variables
47
+ - Namespace format: `--category-name` (e.g., `--color-blue-500`, `--font-sans`)
48
+ - Access in CSS: `var(--color-blue-500)`
49
+ - Available namespaces:
50
+ - `--color-*` : Color utilities like `bg-red-500` and `text-sky-300`
51
+ - `--font-*` : Font family utilities like `font-sans`
52
+ - `--text-*` : Font size utilities like `text-xl`
53
+ - `--font-weight-*` : Font weight utilities like `font-bold`
54
+ - `--tracking-*` : Letter spacing utilities like `tracking-wide`
55
+ - `--leading-*` : Line height utilities like `leading-tight`
56
+ - `--breakpoint-*` : Responsive breakpoint variants like `sm:*`
57
+ - `--container-*` : Container query variants like `@sm:*` and size utilities like `max-w-md`
58
+ - `--spacing-*` : Spacing and sizing utilities like `px-4` and `max-h-16`
59
+ - `--radius-*` : Border radius utilities like `rounded-sm`
60
+ - `--shadow-*` : Box shadow utilities like `shadow-md`
61
+ - `--inset-shadow-*` : Inset box shadow utilities like `inset-shadow-xs`
62
+ - `--drop-shadow-*` : Drop shadow filter utilities like `drop-shadow-md`
63
+ - `--blur-*` : Blur filter utilities like `blur-md`
64
+ - `--perspective-*` : Perspective utilities like `perspective-near`
65
+ - `--aspect-*` : Aspect ratio utilities like `aspect-video`
66
+ - `--ease-*` : Transition timing function utilities like `ease-out`
67
+ - `--animate-*` : Animation utilities like `animate-spin`
68
+
69
+
70
+ - **Simplified theme configuration**: Many utilities no longer need theme configuration
71
+ - Utilities like `grid-cols-12`, `z-40`, and `opacity-70` work without configuration
72
+ - Data attributes like `data-selected:opacity-100` don't need configuration
73
+
74
+ - **Dynamic spacing scale**: Derived from a single spacing value
75
+ - Default: `--spacing: 0.25rem`
76
+ - Every multiple of the base value is available (e.g., `mt-21` works automatically)
77
+
78
+ - **Overriding theme namespaces**:
79
+ - Override entire namespace: `--font-*: initial;`
80
+ - Override entire theme: `--*: initial;`
81
+
82
+
83
+ ## New Features
84
+
85
+ - **Container query support**: Built-in now, no plugin needed
86
+ - `@container` for container context
87
+ - `@sm:`, `@md:`, etc. for container-based breakpoints
88
+ - `@max-md:` for max-width container queries
89
+ - Combine with `@min-md:@max-xl:hidden` for ranges
90
+
91
+ - **3D transforms**:
92
+ - `transform-3d` enables 3D transforms
93
+ - `rotate-x-*`, `rotate-y-*`, `rotate-z-*` for 3D rotation
94
+ - `scale-z-*` for z-axis scaling
95
+ - `translate-z-*` for z-axis translation
96
+ - `perspective-*` utilities (`perspective-near`, `perspective-distant`, etc.)
97
+ - `perspective-origin-*` utilities
98
+ - `backface-visible` and `backface-hidden`
99
+
100
+ - **Gradient enhancements**:
101
+ - Linear gradient angles: `bg-linear-45` (renamed from `bg-gradient-*`)
102
+ - Gradient interpolation: `bg-linear-to-r/oklch`, `bg-linear-to-r/srgb`
103
+ - Conic and radial gradients: `bg-conic`, `bg-radial-[at_25%_25%]`
104
+
105
+ - **Shadow enhancements**:
106
+ - `inset-shadow-*` and `inset-ring-*` utilities
107
+ - Can be composed with regular `shadow-*` and `ring-*`
108
+
109
+ - **New CSS property utilities**:
110
+ - `field-sizing-content` for auto-resizing textareas
111
+ - `scheme-light`, `scheme-dark` for `color-scheme` property
112
+ - `font-stretch-*` utilities for variable fonts
113
+
114
+ ## New Variants
115
+
116
+ - **Composable variants**: Chain variants together
117
+ - Example: `group-has-data-potato:opacity-100`
118
+
119
+ - **New variants**:
120
+ - `starting` variant for `@starting-style` transitions
121
+ - `not-*` variant for `:not()` pseudo-class
122
+ - `inert` variant for `inert` attribute
123
+ - `nth-*` variants (`nth-3:`, `nth-last-5:`, `nth-of-type-4:`, `nth-last-of-type-6:`)
124
+ - `in-*` variant (like `group-*` but without adding `group` class)
125
+ - `open` variant now supports `:popover-open`
126
+ - `**` variant for targeting all descendants
127
+
128
+ ## Custom Extensions
129
+
130
+ - **Custom utilities**: Use `@utility` directive
131
+ ```css
132
+ @utility tab-4 {
133
+ tab-size: 4;
134
+ }
135
+ ```
136
+
137
+ - **Custom variants**: Use `@variant` directive
138
+ ```css
139
+ @variant pointer-coarse (@media (pointer: coarse));
140
+ @variant theme-midnight (&:where([data-theme="midnight"] *));
141
+ ```
142
+
143
+ - **Plugins**: Use `@plugin` directive
144
+ ```css
145
+ @plugin "@tailwindcss/typography";
146
+ ```
147
+
148
+ ## Breaking Changes
149
+
150
+ - **Removed deprecated utilities**:
151
+ - `bg-opacity-*` → Use `bg-black/50` instead
152
+ - `text-opacity-*` → Use `text-black/50` instead
153
+ - And others: `border-opacity-*`, `divide-opacity-*`, etc.
154
+
155
+ - **Renamed utilities**:
156
+ - `shadow-sm` → `shadow-xs` (and `shadow` → `shadow-sm`)
157
+ - `drop-shadow-sm` → `drop-shadow-xs` (and `drop-shadow` → `drop-shadow-sm`)
158
+ - `blur-sm` → `blur-xs` (and `blur` → `blur-sm`)
159
+ - `rounded-sm` → `rounded-xs` (and `rounded` → `rounded-sm`)
160
+ - `outline-none` → `outline-hidden` (for the old behavior)
161
+
162
+ - **Default style changes**:
163
+ - Default border color is now `currentColor` (was `gray-200`)
164
+ - Default `ring` width is now 1px (was 3px)
165
+ - Placeholder text now uses current color at 50% opacity (was `gray-400`)
166
+ - Hover styles only apply on devices that support hover (`@media (hover: hover)`)
167
+
168
+ - **Syntax changes**:
169
+ - CSS variables in arbitrary values: `bg-(--brand-color)` instead of `bg-[--brand-color]`
170
+ - Stacked variants now apply left-to-right (not right-to-left)
171
+ - Use CSS variables instead of `theme()` function
172
+
173
+ ## Advanced Configuration
174
+
175
+ - **Using a prefix**:
176
+ ```css
177
+ @import "tailwindcss" prefix(tw);
178
+ ```
179
+ - Results in classes like `tw:flex`, `tw:bg-red-500`, `tw:hover:bg-red-600`
180
+
181
+ - **Source detection**:
182
+ - Automatic by default (ignores `.gitignore` files and binary files)
183
+ - Add sources: `@source "../node_modules/@my-company/ui-lib";`
184
+ - Disable automatic detection: `@import "tailwindcss" source(none);`
185
+
186
+ - **Legacy config files**:
187
+ ```css
188
+ @import "tailwindcss";
189
+ @config "../../tailwind.config.js";
190
+ ```
191
+
192
+ - **Dark mode configuration**:
193
+ ```css
194
+ @import "tailwindcss";
195
+ @variant dark (&:where(.dark, .dark *));
196
+ ```
197
+
198
+ - **Container customization**: Extend with `@utility`
199
+ ```css
200
+ @utility container {
201
+ margin-inline: auto;
202
+ padding-inline: 2rem;
203
+ }
204
+ ```
@@ -0,0 +1,4 @@
1
+ module RailsRules
2
+ class Railtie < ::Rails::Railtie
3
+ end
4
+ end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module RailsRules
4
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
5
3
  end
data/lib/rails_rules.rb CHANGED
@@ -1,8 +1,6 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "rails_rules/version"
1
+ require "rails_rules/version"
2
+ require "rails_rules/railtie"
4
3
 
5
4
  module RailsRules
6
- class Error < StandardError; end
7
5
  # Your code goes here...
8
6
  end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :rails_rules do
3
+ # # Task goes here
4
+ # end
metadata CHANGED
@@ -1,27 +1,52 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_rules
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Waite
8
- bindir: exe
8
+ bindir: bin
9
9
  cert_chain: []
10
10
  date: 2025-05-09 00:00:00.000000000 Z
11
- dependencies: []
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '7'
12
26
  email:
13
27
  - andyw8@users.noreply.github.com
14
28
  executables: []
15
29
  extensions: []
16
30
  extra_rdoc_files: []
17
31
  files:
18
- - ".standard.yml"
19
- - LICENSE.txt
32
+ - MIT-LICENSE
20
33
  - README.md
21
34
  - Rakefile
35
+ - lib/generators/default/USAGE
36
+ - lib/generators/rails_rules/default_generator.rb
37
+ - lib/generators/rails_rules/templates/000-cursor-rules.mdc
38
+ - lib/generators/rails_rules/templates/1000-rails-general.mdc
39
+ - lib/generators/rails_rules/templates/1001-rails-controllers.mdc
40
+ - lib/generators/rails_rules/templates/1002-rails-models.mdc
41
+ - lib/generators/rails_rules/templates/1003-rails-views.mdc
42
+ - lib/generators/rails_rules/templates/1004-javascript-stimulus.mdc
43
+ - lib/generators/rails_rules/templates/1005-service-objects.mdc
44
+ - lib/generators/rails_rules/templates/1006-testing.mdc
45
+ - lib/generators/rails_rules/templates/1007-tailwindcss.mdc
22
46
  - lib/rails_rules.rb
47
+ - lib/rails_rules/railtie.rb
23
48
  - lib/rails_rules/version.rb
24
- - sig/rails_rules.rbs
49
+ - lib/tasks/rails_rules_tasks.rake
25
50
  homepage: https://github.com/andyw8/rails_rules
26
51
  licenses:
27
52
  - MIT
@@ -36,7 +61,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
36
61
  requirements:
37
62
  - - ">="
38
63
  - !ruby/object:Gem::Version
39
- version: 3.1.0
64
+ version: '0'
40
65
  required_rubygems_version: !ruby/object:Gem::Requirement
41
66
  requirements:
42
67
  - - ">="
data/.standard.yml DELETED
@@ -1,3 +0,0 @@
1
- # For available configuration options, see:
2
- # https://github.com/standardrb/standard
3
- ruby_version: 3.1
data/LICENSE.txt DELETED
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2025 Andy Waite
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
data/sig/rails_rules.rbs DELETED
@@ -1,4 +0,0 @@
1
- module RailsRules
2
- VERSION: String
3
- # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
- end