supplies 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9e11cbc6df13eb326ec0edb3173745f8587491ed655c118452d703f01ac6f63f
4
+ data.tar.gz: 62e11a3e89ff59487d48966ce4b7025e2b2e19ef501ed41d92330daa17e5e377
5
+ SHA512:
6
+ metadata.gz: c63d3e472513b2d286c9cc38849e39dafc25612889a5db4d7c468334d1f0eb9594e1415cc0b657ac97cde91c76059a0fda24de490890f64710b3811060050078
7
+ data.tar.gz: f3b9d9524a0e3d44fc0fea7e98eb637e3302b429d278b9486a4a218b12440b94aee0d4000eae4daa61aa9dec9e1669bddb2b7f5e55494192d8326b51b9a5448c
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Launch Supply
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/README.md ADDED
@@ -0,0 +1,271 @@
1
+ # Supplies
2
+
3
+ Shared Ruby patterns, linting configuration, and AI workflows for Launch Supply Rails development.
4
+
5
+ ## What's in This Repo
6
+
7
+ Supplies serves two purposes with **different distribution paths**:
8
+
9
+ | Purpose | Distribution | What it includes |
10
+ |---------|--------------|------------------|
11
+ | **Ruby code** | Public gem on RubyGems | `Supplies::Functional`, RuboCop configs, generators |
12
+ | **AI workflows** | Local clone only | CLAUDE.md, commands, CLI |
13
+
14
+ ## Quick Start
15
+
16
+ ### For Ruby Code (Public Gem)
17
+
18
+ ```ruby
19
+ # Gemfile
20
+ gem 'supplies'
21
+ ```
22
+
23
+ ```bash
24
+ bundle install
25
+ rails g supplies:install # Sets up RuboCop config
26
+ ```
27
+
28
+ ### For AI Workflows (Local Clone)
29
+
30
+ ```bash
31
+ # One-time setup per developer machine
32
+ cd ~/Source/launch-supply
33
+ git clone git@github.com:launch-supply/supplies.git
34
+
35
+ # Add to ~/.zshrc or ~/.bashrc
36
+ export SUPPLIES_ROOT="$HOME/Source/launch-supply/supplies"
37
+ export PATH="$SUPPLIES_ROOT/bin:$PATH"
38
+
39
+ # Reload shell
40
+ source ~/.zshrc
41
+
42
+ # Initialize the hub (once per workspace)
43
+ cd ~/Source/launch-supply
44
+ supplies hub init
45
+
46
+ # Initialize each project
47
+ cd mission-control
48
+ supplies project init
49
+ ```
50
+
51
+ ## Terminology
52
+
53
+ | Term | Definition |
54
+ |------|------------|
55
+ | **Supply Hub** | Directory with shared CLAUDE.md symlink. All projects under it inherit shared workflows. |
56
+ | **Supplied Project** | A project initialized with `supplies project init`. Has its own CLAUDE.md with variables. |
57
+ | **Supply Root** | Local clone of this repo. Set via `$SUPPLIES_ROOT`. |
58
+
59
+ ## Directory Structure
60
+
61
+ ```
62
+ $SUPPLIES_ROOT/
63
+ ├── lib/ # Published to RubyGems
64
+ │ ├── supplies.rb
65
+ │ └── supplies/
66
+ │ ├── functional.rb
67
+ │ └── version.rb
68
+ ├── config/rubocop/ # Published to RubyGems
69
+ │ ├── base.yml
70
+ │ ├── rails.yml
71
+ │ └── ...
72
+ ├── ai/ # Local clone only (NOT in gem)
73
+ │ ├── CLAUDE.md # Shared AI workflow base
74
+ │ └── commands/ # Shared command templates
75
+ └── bin/
76
+ └── supplies # CLI (local clone only)
77
+
78
+ Supply Hub (~/Source/launch-supply/)
79
+ ├── CLAUDE.md → $SUPPLIES_ROOT/ai/CLAUDE.md
80
+ ├── mission-control/
81
+ │ ├── CLAUDE.md # Project-specific
82
+ │ └── .claude/commands/ # Symlinks to shared
83
+ └── batch-table/
84
+ ├── CLAUDE.md
85
+ └── .claude/commands/
86
+ ```
87
+
88
+ ## CLI Commands
89
+
90
+ ```bash
91
+ # Hub Commands (run from the hub directory)
92
+ supplies hub init # Create symlink to shared CLAUDE.md
93
+ supplies hub status # Show hub health and projects
94
+
95
+ # Project Commands (run from within a project)
96
+ supplies project init # Initialize project with CLAUDE.md and command symlinks
97
+ supplies project link # Add symlinks for new commands after updating supplies
98
+ supplies project status # Show project setup status
99
+ supplies project eject # Convert symlinked command to local file for customization
100
+
101
+ # Global Options
102
+ --dry-run # Preview without making changes
103
+ --quiet # Suppress non-essential output
104
+ ```
105
+
106
+ ## How Variable Substitution Works
107
+
108
+ The base CLAUDE.md (in supplies) uses placeholder variables. Each project's CLAUDE.md defines the values:
109
+
110
+ **Base (supplies):**
111
+ ```markdown
112
+ Create branch: `feature/PROJECT_PREFIX-123-description`
113
+ ```
114
+
115
+ **Project (mission-control):**
116
+ ```markdown
117
+ ## Project Variables
118
+ - **PROJECT_PREFIX**: MC
119
+ ```
120
+
121
+ **No preprocessing required.** Claude Code loads both files, sees `PROJECT_PREFIX` in the instructions and `MC` as the defined value, and performs contextual substitution.
122
+
123
+ ## Workflow Examples
124
+
125
+ ### Setting Up a New Developer Machine
126
+
127
+ ```bash
128
+ # Clone supplies
129
+ cd ~/Source/launch-supply
130
+ git clone git@github.com:launch-supply/supplies.git
131
+
132
+ # Add to shell profile
133
+ export SUPPLIES_ROOT="$HOME/Source/launch-supply/supplies"
134
+ export PATH="$SUPPLIES_ROOT/bin:$PATH"
135
+
136
+ # Initialize hub
137
+ supplies hub init
138
+
139
+ # Initialize each project
140
+ cd mission-control && supplies project init
141
+ cd ../batch-table && supplies project init
142
+ ```
143
+
144
+ ### Starting a New Rails Project
145
+
146
+ ```bash
147
+ cd ~/Source/launch-supply
148
+ rails new my-app
149
+ cd my-app
150
+
151
+ # Add gem (Ruby code)
152
+ bundle add supplies
153
+ rails g supplies:install
154
+
155
+ # Add AI workflows
156
+ supplies project init
157
+ ```
158
+
159
+ ### After Updating Supplies
160
+
161
+ ```bash
162
+ # Pull latest changes
163
+ cd $SUPPLIES_ROOT
164
+ git pull
165
+
166
+ # Existing symlinks auto-update (CLAUDE.md, existing commands)
167
+ # Only need to run this if NEW commands were added:
168
+ cd ~/Source/launch-supply/mission-control
169
+ supplies project link
170
+ ```
171
+
172
+ ### Ejecting a Command for Customization
173
+
174
+ ```bash
175
+ # Convert symlink to local file
176
+ supplies project eject implement-issue
177
+
178
+ # Now you can customize .claude/commands/implement-issue.md
179
+ ```
180
+
181
+ ---
182
+
183
+ ## Gem Features
184
+
185
+ ### RuboCop Configuration
186
+
187
+ The generator creates `.rubocop.yml` that inherits from the gem:
188
+
189
+ ```yaml
190
+ inherit_gem:
191
+ supplies:
192
+ - config/rubocop/base.yml
193
+ - config/rubocop/rails.yml
194
+ - config/rubocop/rspec.yml
195
+ - config/rubocop/performance.yml
196
+ ```
197
+
198
+ Updates to RuboCop rules propagate automatically when you update the gem.
199
+
200
+ ### Supplies::Functional
201
+
202
+ Service object pattern support:
203
+
204
+ ```ruby
205
+ class SendsWelcomeEmail
206
+ include Supplies::Functional
207
+
208
+ def initialize(user:)
209
+ @user = user
210
+ end
211
+
212
+ def call
213
+ UserMailer.welcome(@user).deliver_now
214
+ end
215
+ end
216
+
217
+ # Usage
218
+ SendsWelcomeEmail.(user: user)
219
+
220
+ # Currying
221
+ send_email = SendsWelcomeEmail.curry(template: :welcome)
222
+ users.each { |user| send_email.(user: user) }
223
+ ```
224
+
225
+ ---
226
+
227
+ ## AI Workflow Features
228
+
229
+ ### Shared CLAUDE.md
230
+
231
+ The base CLAUDE.md at `ai/CLAUDE.md` provides:
232
+
233
+ - TDD workflow (red-green-refactor)
234
+ - Linear integration patterns
235
+ - Git conventions (branch naming, commit messages)
236
+ - Code style guidelines
237
+ - PR creation workflow
238
+
239
+ ### Shared Commands
240
+
241
+ Commands in `ai/commands/` include:
242
+
243
+ | Command | Purpose |
244
+ |---------|---------|
245
+ | `implement-issue` | Full issue implementation with TDD |
246
+ | `fix-ci-failure` | Diagnose and fix failing CI checks |
247
+ | `handle-pr-feedback` | Address review comments |
248
+ | `process-issues` | Start parallel issue processing |
249
+ | `orchestrate-issues` | Coordinate multiple sub-agents |
250
+ | `rebase-pr` | Rebase PRs with merge conflicts |
251
+
252
+ ---
253
+
254
+ ## Requirements
255
+
256
+ - Ruby 3.2+
257
+ - Rails 7.1+ (for generator)
258
+ - `linctl` CLI for Linear integration (optional)
259
+
260
+ ## Development
261
+
262
+ ```bash
263
+ cd supplies
264
+ bundle install
265
+ bundle exec rspec
266
+ bundle exec rubocop
267
+ ```
268
+
269
+ ## License
270
+
271
+ MIT License
@@ -0,0 +1,264 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # LsStandards Base RuboCop Configuration
4
+ # Core Ruby style rules for all projects
5
+
6
+ AllCops:
7
+ NewCops: enable
8
+ DisplayCopNames: true
9
+ TargetRubyVersion: 3.2
10
+ SuggestExtensions: false
11
+ Exclude:
12
+ - "bin/**/*"
13
+ - "db/schema.rb"
14
+ - "db/migrate/**/*"
15
+ - "config/importmap.rb"
16
+ - "node_modules/**/*"
17
+ - "tmp/**/*"
18
+ - "vendor/**/*"
19
+
20
+ # Layout Cops
21
+ Layout/ParameterAlignment:
22
+ EnforcedStyle: with_fixed_indentation
23
+
24
+ Layout/ArgumentAlignment:
25
+ EnforcedStyle: with_fixed_indentation
26
+
27
+ Layout/EmptyLinesAroundAttributeAccessor:
28
+ Enabled: true
29
+
30
+ Layout/SpaceAroundMethodCallOperator:
31
+ Enabled: true
32
+
33
+ Layout/MultilineMethodCallIndentation:
34
+ EnforcedStyle: indented
35
+
36
+ Layout/MultilineOperationIndentation:
37
+ EnforcedStyle: indented
38
+
39
+ Layout/FirstArrayElementIndentation:
40
+ EnforcedStyle: consistent
41
+
42
+ Layout/FirstHashElementIndentation:
43
+ EnforcedStyle: consistent
44
+
45
+ Layout/HashAlignment:
46
+ EnforcedLastArgumentHashStyle: ignore_explicit
47
+
48
+ Layout/FirstArrayElementLineBreak:
49
+ Enabled: true
50
+
51
+ Layout/FirstHashElementLineBreak:
52
+ Enabled: true
53
+
54
+ Layout/FirstMethodArgumentLineBreak:
55
+ Enabled: false
56
+
57
+ Layout/FirstMethodParameterLineBreak:
58
+ Enabled: true
59
+
60
+ Layout/MultilineAssignmentLayout:
61
+ Enabled: true
62
+
63
+ # Lint Cops
64
+ Lint/RaiseException:
65
+ Enabled: true
66
+
67
+ Lint/StructNewOverride:
68
+ Enabled: true
69
+
70
+ Lint/DeprecatedOpenSSLConstant:
71
+ Enabled: true
72
+
73
+ Lint/MixedRegexpCaptureTypes:
74
+ Enabled: true
75
+
76
+ Lint/AssignmentInCondition:
77
+ Enabled: false
78
+
79
+ Lint/EmptyBlock:
80
+ Enabled: false
81
+
82
+ Lint/MissingCopEnableDirective:
83
+ Exclude:
84
+ - "spec/fixtures/**/*"
85
+
86
+ # Metrics Cops - All disabled per team agreement
87
+ Metrics/AbcSize:
88
+ Enabled: false
89
+
90
+ Metrics/BlockLength:
91
+ Enabled: false
92
+
93
+ Metrics/ClassLength:
94
+ Enabled: false
95
+
96
+ Metrics/MethodLength:
97
+ Enabled: false
98
+
99
+ Metrics/ModuleLength:
100
+ Enabled: false
101
+
102
+ Metrics/ParameterLists:
103
+ Enabled: false
104
+
105
+ Metrics/CyclomaticComplexity:
106
+ Enabled: false
107
+
108
+ Metrics/PerceivedComplexity:
109
+ Enabled: false
110
+
111
+ # Naming Cops
112
+ Naming/PredicatePrefix:
113
+ Enabled: false
114
+
115
+ Naming/AccessorMethodName:
116
+ Enabled: false
117
+
118
+ Naming/VariableNumber:
119
+ Enabled: false
120
+
121
+ Naming/FileName:
122
+ Exclude:
123
+ - "spec/views/**/*_spec.rb"
124
+
125
+ # Style Cops
126
+ Style/Documentation:
127
+ Enabled: false
128
+
129
+ Style/BlockDelimiters:
130
+ EnforcedStyle: semantic
131
+ AllowBracesOnProceduralOneLiners: true
132
+ FunctionalMethods:
133
+ - let
134
+ - let!
135
+ - subject
136
+ - watch
137
+ - find
138
+ Exclude:
139
+ - "spec/factories/*.rb"
140
+
141
+ Style/FormatString:
142
+ EnforcedStyle: percent
143
+
144
+ Style/FormatStringToken:
145
+ Enabled: false
146
+
147
+ Style/DoubleNegation:
148
+ Enabled: false
149
+
150
+ Style/EmptyMethod:
151
+ EnforcedStyle: expanded
152
+
153
+ Style/Alias:
154
+ EnforcedStyle: prefer_alias_method
155
+
156
+ Style/NumericPredicate:
157
+ EnforcedStyle: comparison
158
+
159
+ Style/RegexpLiteral:
160
+ Enabled: false
161
+
162
+ Style/ModuleFunction:
163
+ EnforcedStyle: extend_self
164
+
165
+ Style/TrailingCommaInArguments:
166
+ EnforcedStyleForMultiline: comma
167
+
168
+ Style/TrailingCommaInArrayLiteral:
169
+ EnforcedStyleForMultiline: comma
170
+
171
+ Style/TrailingCommaInHashLiteral:
172
+ EnforcedStyleForMultiline: comma
173
+
174
+ Style/MultilineBlockChain:
175
+ Enabled: false
176
+
177
+ Style/ExponentialNotation:
178
+ Enabled: true
179
+ EnforcedStyle: scientific
180
+
181
+ Style/GuardClause:
182
+ MinBodyLength: 3
183
+
184
+ Style/IfUnlessModifier:
185
+ Enabled: false
186
+
187
+ Style/InlineComment:
188
+ Enabled: true
189
+ Exclude:
190
+ - "config/importmap.rb"
191
+ - "Gemfile"
192
+
193
+ Style/Lambda:
194
+ EnforcedStyle: literal
195
+
196
+ Style/LambdaCall:
197
+ EnforcedStyle: braces
198
+
199
+ Style/AutoResourceCleanup:
200
+ Enabled: true
201
+
202
+ Style/CollectionMethods:
203
+ Enabled: true
204
+
205
+ Style/Copyright:
206
+ Enabled: false
207
+
208
+ Style/DocumentationMethod:
209
+ Enabled: false
210
+ Exclude:
211
+ - "spec/**/*"
212
+ - "test/**/*"
213
+
214
+ Style/MethodCallWithArgsParentheses:
215
+ Enabled: false
216
+
217
+ Style/MethodCalledOnDoEndBlock:
218
+ Enabled: true
219
+ Exclude:
220
+ - "spec/**/*"
221
+
222
+ Style/MissingElse:
223
+ Enabled: false
224
+
225
+ Style/OptionHash:
226
+ Enabled: false
227
+
228
+ Style/ReturnNil:
229
+ Enabled: true
230
+
231
+ Style/Send:
232
+ Enabled: true
233
+ Exclude:
234
+ - "spec/**/*"
235
+
236
+ Style/StringMethods:
237
+ Enabled: false
238
+
239
+ Style/SignalException:
240
+ EnforcedStyle: semantic
241
+
242
+ Style/SingleLineBlockParams:
243
+ Enabled: false
244
+
245
+ Style/OpenStructUse:
246
+ Enabled: false
247
+
248
+ Style/HashEachMethods:
249
+ Enabled: true
250
+
251
+ Style/HashTransformKeys:
252
+ Enabled: true
253
+
254
+ Style/HashTransformValues:
255
+ Enabled: true
256
+
257
+ Style/RedundantRegexpCharacterClass:
258
+ Enabled: true
259
+
260
+ Style/RedundantRegexpEscape:
261
+ Enabled: true
262
+
263
+ Style/SlicingWithRange:
264
+ Enabled: true
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # LsStandards Performance RuboCop Configuration
4
+ # Performance-focused rules for optimized Ruby code
5
+
6
+ inherit_from:
7
+ - base.yml
8
+
9
+ # Performance Cops
10
+ Performance:
11
+ Enabled: true
12
+
13
+ Performance/AncestorsInclude:
14
+ Enabled: true
15
+
16
+ Performance/BigDecimalWithNumericArgument:
17
+ Enabled: true
18
+
19
+ Performance/BlockGivenWithExplicitBlock:
20
+ Enabled: true
21
+
22
+ Performance/CaseWhenSplat:
23
+ Enabled: true
24
+
25
+ Performance/Casecmp:
26
+ Enabled: true
27
+
28
+ Performance/ChainArrayAllocation:
29
+ Enabled: true
30
+
31
+ Performance/CollectionLiteralInLoop:
32
+ Enabled: true
33
+ MinSize: 2
34
+
35
+ Performance/CompareWithBlock:
36
+ Enabled: true
37
+
38
+ Performance/ConcurrentMonotonicTime:
39
+ Enabled: true
40
+
41
+ Performance/ConstantRegexp:
42
+ Enabled: true
43
+
44
+ Performance/Count:
45
+ Enabled: true
46
+
47
+ Performance/DeletePrefix:
48
+ Enabled: true
49
+
50
+ Performance/DeleteSuffix:
51
+ Enabled: true
52
+
53
+ Performance/Detect:
54
+ Enabled: true
55
+
56
+ Performance/DoubleStartEndWith:
57
+ Enabled: true
58
+
59
+ Performance/EndWith:
60
+ Enabled: true
61
+
62
+ Performance/FixedSize:
63
+ Enabled: true
64
+
65
+ Performance/FlatMap:
66
+ Enabled: true
67
+
68
+ Performance/InefficientHashSearch:
69
+ Enabled: true
70
+
71
+ Performance/IoReadlines:
72
+ Enabled: true
73
+
74
+ Performance/MapCompact:
75
+ Enabled: true
76
+
77
+ Performance/MapMethodChain:
78
+ Enabled: true
79
+
80
+ Performance/MethodObjectAsBlock:
81
+ Enabled: true
82
+
83
+ Performance/OpenStruct:
84
+ Enabled: true
85
+
86
+ Performance/RangeInclude:
87
+ Enabled: true
88
+
89
+ Performance/RedundantBlockCall:
90
+ Enabled: true
91
+
92
+ Performance/RedundantEqualityComparisonBlock:
93
+ Enabled: true
94
+
95
+ Performance/RedundantMatch:
96
+ Enabled: true
97
+
98
+ Performance/RedundantMerge:
99
+ Enabled: true
100
+
101
+ Performance/RedundantSortBlock:
102
+ Enabled: true
103
+
104
+ Performance/RedundantSplitRegexpArgument:
105
+ Enabled: true
106
+
107
+ Performance/RedundantStringChars:
108
+ Enabled: true
109
+
110
+ Performance/RegexpMatch:
111
+ Enabled: true
112
+
113
+ Performance/ReverseEach:
114
+ Enabled: true
115
+
116
+ Performance/ReverseFirst:
117
+ Enabled: true
118
+
119
+ Performance/SelectMap:
120
+ Enabled: true
121
+
122
+ Performance/Size:
123
+ Enabled: true
124
+
125
+ Performance/SortReverse:
126
+ Enabled: true
127
+
128
+ Performance/Squeeze:
129
+ Enabled: true
130
+
131
+ Performance/StartWith:
132
+ Enabled: true
133
+
134
+ Performance/StringIdentifierArgument:
135
+ Enabled: true
136
+
137
+ Performance/StringInclude:
138
+ Enabled: true
139
+
140
+ Performance/StringReplacement:
141
+ Enabled: true
142
+
143
+ Performance/Sum:
144
+ Enabled: true
145
+
146
+ Performance/TimesMap:
147
+ Enabled: true
148
+
149
+ Performance/UnfreezeString:
150
+ Enabled: true
151
+
152
+ Performance/UriDefaultParser:
153
+ Enabled: true
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # LsStandards Rails RuboCop Configuration
4
+ # Rails-specific rules for web applications
5
+
6
+ require:
7
+ - rubocop-rails
8
+
9
+ inherit_from:
10
+ - base.yml
11
+
12
+ # Rails Cops
13
+ Rails/FindEach:
14
+ Enabled: false
15
+
16
+ Rails/SkipsModelValidations:
17
+ Enabled: false
18
+
19
+ Rails/RedundantActiveRecordAllMethod:
20
+ Enabled: false
21
+
22
+ Rails/FilePath:
23
+ Enabled: false
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # LsStandards RSpec RuboCop Configuration
4
+ # Testing-specific rules for RSpec test suites
5
+
6
+ inherit_from:
7
+ - base.yml
8
+
9
+ # RSpec Cops
10
+ RSpec/ExampleLength:
11
+ Enabled: false
12
+
13
+ RSpec/MessageSpies:
14
+ Enabled: false
15
+
16
+ RSpec/NestedGroups:
17
+ Enabled: false
18
+
19
+ RSpec/MultipleExpectations:
20
+ Enabled: false
21
+
22
+ RSpec/MultipleMemoizedHelpers:
23
+ Enabled: false
24
+
25
+ RSpec/ContextWording:
26
+ Enabled: false
27
+
28
+ RSpec/EmptyExampleGroup:
29
+ Enabled: false
30
+
31
+ RSpec/LetSetup:
32
+ Enabled: false
33
+
34
+ RSpec/InstanceVariable:
35
+ Enabled: false
36
+
37
+ RSpec/MessageChain:
38
+ Enabled: false
39
+
40
+ RSpec/DescribedClass:
41
+ Enabled: false
42
+
43
+ RSpec/VerifiedDoubles:
44
+ IgnoreNameless: true
45
+ IgnoreSymbolicNames: true
46
+
47
+ RSpec/DescribeClass:
48
+ Enabled: false
49
+
50
+ RSpec/RepeatedExample:
51
+ Exclude:
52
+ - "spec/integration/**/*"
53
+
54
+ RSpec/RepeatedDescription:
55
+ Exclude:
56
+ - "spec/integration/**/*"
57
+
58
+ RSpec/ScatteredSetup:
59
+ Exclude:
60
+ - "spec/integration/**/*"
61
+
62
+ RSpec/StubbedMock:
63
+ Enabled: false
64
+
65
+ RSpec/NoExpectationExample:
66
+ Enabled: false
67
+
68
+ RSpec/SpecFilePathFormat:
69
+ Enabled: false
70
+
71
+ RSpec/SpecFilePathSuffix:
72
+ Enabled: false
73
+
74
+ # Capybara Cops
75
+ Capybara/SpecificActions:
76
+ Enabled: false
77
+
78
+ Capybara/ClickLinkOrButtonStyle:
79
+ Enabled: false
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+
5
+ module Supplies
6
+ module Generators
7
+ # Installs Supplies RuboCop configuration into a Rails application.
8
+ #
9
+ # This generator:
10
+ # - Adds RuboCop gems to Gemfile (development/test group)
11
+ # - Creates `.rubocop.yml` that inherits from the gem's configurations
12
+ #
13
+ # RuboCop rules auto-update when the gem is updated.
14
+ #
15
+ # AI workflows (CLAUDE.md, commands) are managed separately via the
16
+ # `supplies` CLI from a local clone of the supplies repo.
17
+ #
18
+ # @example Running the generator
19
+ # rails g supplies:install
20
+ #
21
+ class InstallGenerator < Rails::Generators::Base
22
+ source_root File.expand_path('templates', __dir__)
23
+
24
+ desc 'Installs Supplies RuboCop configuration'
25
+
26
+ RUBOCOP_GEMS = [
27
+ "gem 'rubocop', '~> 1.60', require: false",
28
+ "gem 'rubocop-capybara', '~> 2.20', require: false",
29
+ "gem 'rubocop-factory_bot', '~> 2.25', require: false",
30
+ "gem 'rubocop-md', '~> 2.0', require: false",
31
+ "gem 'rubocop-performance', '~> 1.20', require: false",
32
+ "gem 'rubocop-rails', '~> 2.23', require: false",
33
+ "gem 'rubocop-rspec', '~> 3.8', require: false",
34
+ ].freeze
35
+
36
+ # Adds RuboCop gems to the Gemfile in the development/test group.
37
+ #
38
+ # @return [void]
39
+ def add_rubocop_gems
40
+ gemfile_content = File.read('Gemfile')
41
+
42
+ # Check if gems are already present
43
+ if gemfile_content.include?("gem 'rubocop'") || gemfile_content.include?('gem "rubocop"')
44
+ say 'RuboCop gems already in Gemfile, skipping...', :yellow
45
+ return
46
+ end
47
+
48
+ gem_entries = RUBOCOP_GEMS.map { |g| " #{g}" }.join("\n")
49
+ append_to_file 'Gemfile', <<~GEMS
50
+
51
+ # RuboCop linting (added by supplies:install)
52
+ group :development, :test do
53
+ #{gem_entries}
54
+ end
55
+ GEMS
56
+
57
+ say 'Added RuboCop gems to Gemfile', :green
58
+ end
59
+
60
+ # Creates the .rubocop.yml configuration file.
61
+ #
62
+ # @return [void]
63
+ def create_rubocop_config
64
+ template 'rubocop.yml.tt', '.rubocop.yml'
65
+ end
66
+
67
+ # Displays post-install instructions.
68
+ #
69
+ # @return [void]
70
+ def show_post_install_message
71
+ say ''
72
+ say '=' * 60, :green
73
+ say 'Supplies RuboCop configuration installed!', :green
74
+ say '=' * 60, :green
75
+ say ''
76
+ say 'Files created/modified:'
77
+ say ' - Gemfile (RuboCop gems added to development/test group)'
78
+ say ' - .rubocop.yml (inherits from gem - auto-updates)'
79
+ say ''
80
+ say 'Next steps:', :yellow
81
+ say " 1. Run 'bundle install' to install RuboCop gems"
82
+ say " 2. Run 'bundle exec rubocop' to check your code"
83
+ say " 3. Run 'bundle exec rubocop -a' to auto-fix issues"
84
+ say ''
85
+ say 'For AI workflows (CLAUDE.md, commands), use the supplies CLI:', :yellow
86
+ say ' supplies project init'
87
+ say ''
88
+ end
89
+
90
+ private
91
+
92
+ # Returns the current Ruby version (major.minor).
93
+ #
94
+ # @return [String] the Ruby version
95
+ def ruby_version
96
+ RUBY_VERSION.split('.').take(2).join('.')
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # RuboCop configuration
4
+ # Generated by supplies gem - inherits from gem, auto-updates
5
+ #
6
+ # To customize, add your overrides below.
7
+ # See: https://docs.rubocop.org/rubocop/configuration.html
8
+
9
+ inherit_gem:
10
+ supplies:
11
+ - config/rubocop/base.yml
12
+ - config/rubocop/rails.yml
13
+ - config/rubocop/rspec.yml
14
+ - config/rubocop/performance.yml
15
+
16
+ AllCops:
17
+ TargetRubyVersion: <%= ruby_version %>
18
+ NewCops: enable
19
+
20
+ # Add your project-specific overrides below:
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Supplies
4
+ # Functional module for service objects that provides a consistent `.call`
5
+ # class method interface and support for currying.
6
+ #
7
+ # This module enables the service object pattern where business logic is
8
+ # encapsulated in classes with a single public `#call` method. Including
9
+ # this module provides:
10
+ #
11
+ # - A class-level `.call` method that instantiates and invokes the service
12
+ # - A `.curry` method for partial application of arguments
13
+ #
14
+ # @example Basic usage
15
+ # class SendsWelcomeEmail
16
+ # include Supplies::Functional
17
+ #
18
+ # def initialize(user:)
19
+ # @user = user
20
+ # end
21
+ #
22
+ # def call
23
+ # UserMailer.welcome(@user).deliver_now
24
+ # end
25
+ # end
26
+ #
27
+ # # Invoke with class method
28
+ # SendsWelcomeEmail.call(user: user)
29
+ #
30
+ # @example Using curry for partial application
31
+ # class SendsNotification
32
+ # include Supplies::Functional
33
+ #
34
+ # def initialize(user:, message:)
35
+ # @user = user
36
+ # @message = message
37
+ # end
38
+ #
39
+ # def call
40
+ # NotificationService.send(@user, @message)
41
+ # end
42
+ # end
43
+ #
44
+ # # Create a curried version with preset message
45
+ # notify_welcome = SendsNotification.curry(message: "Welcome!")
46
+ # users.each { |user| notify_welcome.call(user: user) }
47
+ #
48
+ # @example With block support
49
+ # class ProcessesItems
50
+ # include Supplies::Functional
51
+ #
52
+ # def initialize(items:)
53
+ # @items = items
54
+ # end
55
+ #
56
+ # def call(&block)
57
+ # @items.each { |item| block.call(item) }
58
+ # end
59
+ # end
60
+ #
61
+ # ProcessesItems.call(items: items) { |item| puts item }
62
+ #
63
+ # @see ADR-0001 for the full architectural decision record
64
+ module Functional
65
+ # Hook called when this module is included in a class.
66
+ # Extends the including class with ClassMethods.
67
+ #
68
+ # @param base [Class] the class including this module
69
+ def self.included(base)
70
+ base.extend(ClassMethods)
71
+ end
72
+
73
+ # Class methods added to any class that includes Functional.
74
+ module ClassMethods
75
+ # Instantiates the service and calls it with the provided arguments.
76
+ #
77
+ # This method supports both positional and keyword arguments, as well
78
+ # as blocks that are passed through to the instance's `#call` method.
79
+ #
80
+ # @param args [Array] positional arguments passed to initialize
81
+ # @param kwargs [Hash] keyword arguments passed to initialize
82
+ # @param block [Proc] optional block passed to the instance's call method
83
+ # @return [Object] the return value of the instance's `#call` method
84
+ #
85
+ # @example
86
+ # SendsEmail.call(user: user, template: :welcome)
87
+ #
88
+ # @example With hash as first argument (merged with kwargs)
89
+ # base_args = { user: user }
90
+ # SendsEmail.call(base_args, template: :welcome)
91
+ def call(*args, **kwargs, &)
92
+ if args.first.is_a?(Hash)
93
+ # Merge hash with kwargs (kwargs take precedence)
94
+ kwargs = args.shift.merge(kwargs)
95
+ end
96
+ new(*args, **kwargs).(&)
97
+ end
98
+
99
+ # Creates a lambda that calls the service with preset arguments.
100
+ #
101
+ # This enables partial application, where some arguments are provided
102
+ # upfront and the rest are supplied later when the lambda is called.
103
+ #
104
+ # @param args [Hash] keyword arguments to preset
105
+ # @return [Proc] a lambda that accepts remaining arguments and calls the service
106
+ #
107
+ # @example
108
+ # # Preset the admin_user, supply user later
109
+ # send_reset = SendsPasswordResetEmail.curry(admin_user: admin)
110
+ # users.each { |user| send_reset.call(user: user) }
111
+ def curry(**args)
112
+ ->(**next_args) { call(**args, **next_args) }
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/railtie'
4
+
5
+ module Supplies
6
+ # Railtie for integrating Supplies with Rails applications.
7
+ #
8
+ # This railtie ensures proper initialization of the gem within
9
+ # the Rails boot process and makes generators available.
10
+ class Railtie < Rails::Railtie
11
+ generators do
12
+ require 'generators/supplies/install_generator'
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Supplies
4
+ VERSION = '0.1.0'
5
+ end
data/lib/supplies.rb ADDED
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'supplies/version'
4
+ require_relative 'supplies/functional'
5
+ require_relative 'supplies/railtie' if defined?(Rails::Railtie)
6
+
7
+ # Supplies provides shared coding conventions, linting configuration,
8
+ # and AI tool context files for Rails development.
9
+ #
10
+ # @example Installation
11
+ # # Add to Gemfile
12
+ # gem "supplies", group: [:development, :test]
13
+ #
14
+ # # Run the install generator
15
+ # rails g supplies:install
16
+ #
17
+ module Supplies
18
+ class Error < StandardError; end
19
+
20
+ class << self
21
+ # Returns the root path of the gem installation.
22
+ #
23
+ # @return [Pathname] the gem's root directory path
24
+ # @example
25
+ # Supplies.root #=> #<Pathname:/path/to/supplies>
26
+ def root
27
+ Pathname.new(__dir__).parent
28
+ end
29
+
30
+ # Returns the path to the RuboCop configuration directory.
31
+ #
32
+ # @return [Pathname] path to config/rubocop within the gem
33
+ # @example
34
+ # Supplies.rubocop_config_path #=> #<Pathname:/path/to/supplies/config/rubocop>
35
+ def rubocop_config_path
36
+ root.join('config', 'rubocop')
37
+ end
38
+ end
39
+ end
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: supplies
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Launch Supply
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: railties
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '7.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rubocop
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.60'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.60'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rubocop-capybara
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '2.20'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '2.20'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rubocop-factory_bot
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '2.25'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '2.25'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rubocop-md
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '2.0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '2.0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: rubocop-performance
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.20'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '1.20'
96
+ - !ruby/object:Gem::Dependency
97
+ name: rubocop-rails
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '2.23'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '2.23'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rubocop-rspec
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '3.8'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '3.8'
124
+ description: A collection of tools used by Launch Supply for Ruby developmentand Rubocop
125
+ configs for consistent Rails development practices.
126
+ email:
127
+ - dev@launchsupply.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - LICENSE.txt
133
+ - README.md
134
+ - config/rubocop/base.yml
135
+ - config/rubocop/performance.yml
136
+ - config/rubocop/rails.yml
137
+ - config/rubocop/rspec.yml
138
+ - lib/generators/supplies/install_generator.rb
139
+ - lib/generators/supplies/templates/rubocop.yml.tt
140
+ - lib/supplies.rb
141
+ - lib/supplies/functional.rb
142
+ - lib/supplies/railtie.rb
143
+ - lib/supplies/version.rb
144
+ homepage: https://www.launchsupply.com/
145
+ licenses:
146
+ - MIT
147
+ metadata:
148
+ homepage_uri: https://www.launchsupply.com/
149
+ source_code_uri: https://www.launchsupply.com/
150
+ changelog_uri: https://www.launchsupply.com//blob/main/CHANGELOG.md
151
+ rubygems_mfa_required: 'true'
152
+ rdoc_options: []
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: 3.2.0
160
+ required_rubygems_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ requirements: []
166
+ rubygems_version: 4.0.0
167
+ specification_version: 4
168
+ summary: Shared Ruby patterns and linting for Rails development
169
+ test_files: []