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 +4 -4
- data/MIT-LICENSE +20 -0
- data/README.md +4 -2
- data/Rakefile +1 -8
- data/lib/generators/default/USAGE +7 -0
- data/lib/generators/rails_rules/default_generator.rb +25 -0
- data/lib/generators/rails_rules/templates/000-cursor-rules.mdc +113 -0
- data/lib/generators/rails_rules/templates/1000-rails-general.mdc +87 -0
- data/lib/generators/rails_rules/templates/1001-rails-controllers.mdc +82 -0
- data/lib/generators/rails_rules/templates/1002-rails-models.mdc +105 -0
- data/lib/generators/rails_rules/templates/1003-rails-views.mdc +107 -0
- data/lib/generators/rails_rules/templates/1004-javascript-stimulus.mdc +90 -0
- data/lib/generators/rails_rules/templates/1005-service-objects.mdc +129 -0
- data/lib/generators/rails_rules/templates/1006-testing.mdc +71 -0
- data/lib/generators/rails_rules/templates/1007-tailwindcss.mdc +204 -0
- data/lib/rails_rules/railtie.rb +4 -0
- data/lib/rails_rules/version.rb +1 -3
- data/lib/rails_rules.rb +2 -4
- data/lib/tasks/rails_rules_tasks.rake +4 -0
- metadata +32 -7
- data/.standard.yml +0 -3
- data/LICENSE.txt +0 -21
- data/sig/rails_rules.rbs +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9be68c0ccba503fc0e42a71a14673463a20af8ffd94a914fb6102b5eb13262e
|
4
|
+
data.tar.gz: ea19c7d51efcca0cdeae7cb271eba93eb9a2be96c3d990d2202ba63855f1160c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
3
|
+
This gem provides a set of [rules](https://docs.cursor.com/context/rules) files for working with AI tooling.
|
4
4
|
|
5
|
-
|
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
@@ -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
|
+
```
|
data/lib/rails_rules/version.rb
CHANGED
data/lib/rails_rules.rb
CHANGED
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.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Waite
|
8
|
-
bindir:
|
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
|
-
-
|
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
|
-
-
|
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:
|
64
|
+
version: '0'
|
40
65
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
66
|
requirements:
|
42
67
|
- - ">="
|
data/.standard.yml
DELETED
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