@el-j/magic-helix-plugins 4.0.0-beta.2 → 4.0.0-beta.3
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.
- package/dist/architecture/codeowners.md +123 -0
- package/dist/architecture/monorepo.md +146 -0
- package/dist/architecture/nx.md +122 -0
- package/dist/architecture/turborepo.md +114 -0
- package/dist/ci/github-actions.md +268 -0
- package/dist/ci/gitlab-ci.md +330 -0
- package/dist/containers/docker-multistage.md +120 -0
- package/dist/containers/kubernetes-deploy.md +210 -0
- package/dist/cpp/index.cjs +79 -0
- package/dist/cpp/index.mjs +209 -0
- package/dist/csharp/index.cjs +2 -2
- package/dist/csharp/{index.js → index.mjs} +17 -11
- package/dist/csharp/templates/framework-aspnetcore.md +205 -0
- package/dist/csharp/templates/framework-blazor.md +271 -0
- package/dist/csharp/templates/lang-csharp.md +162 -0
- package/dist/devops/docker-compose.md +111 -0
- package/dist/devops/docker-dockerfile.md +94 -0
- package/dist/devops/github-actions.md +160 -0
- package/dist/devops/gitlab-ci.md +210 -0
- package/dist/generic/lang-typescript.md +57 -0
- package/dist/generic/state-redux.md +21 -0
- package/dist/generic/state-rxjs.md +6 -0
- package/dist/generic/style-mui.md +23 -0
- package/dist/generic/style-tailwind.md +76 -0
- package/dist/generic/test-cypress.md +21 -0
- package/dist/generic/test-jest.md +20 -0
- package/dist/generic/test-playwright.md +21 -0
- package/dist/generic/test-vitest.md +131 -0
- package/dist/go/index.cjs +3 -3
- package/dist/go/{index.js → index.mjs} +18 -15
- package/dist/go/templates/lang-go.md +571 -0
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +24 -0
- package/dist/java/index.cjs +2 -2
- package/dist/java/{index.js → index.mjs} +25 -19
- package/dist/java/templates/build-gradle.md +102 -0
- package/dist/java/templates/build-maven.md +86 -0
- package/dist/java/templates/framework-spring-boot.md +179 -0
- package/dist/java/templates/lang-java.md +78 -0
- package/dist/java/templates/lang-kotlin.md +88 -0
- package/dist/meta/magic-helix-meta.md +213 -0
- package/dist/meta/meta-debug.md +459 -0
- package/dist/meta/meta-implement.md +450 -0
- package/dist/meta/meta-roadmap.md +265 -0
- package/dist/nodejs/templates/angular-core.md +19 -0
- package/dist/nodejs/templates/lang-typescript.md +57 -0
- package/dist/nodejs/templates/nestjs-core.md +7 -0
- package/dist/nodejs/templates/react-core.md +677 -0
- package/dist/nodejs/templates/react-zustand.md +7 -0
- package/dist/nodejs/templates/state-redux.md +21 -0
- package/dist/nodejs/templates/state-rxjs.md +6 -0
- package/dist/nodejs/templates/style-primevue.md +6 -0
- package/dist/nodejs/templates/style-quasar.md +22 -0
- package/dist/nodejs/templates/style-tailwind.md +76 -0
- package/dist/nodejs/templates/test-cypress.md +21 -0
- package/dist/nodejs/templates/test-jest.md +20 -0
- package/dist/nodejs/templates/test-playwright.md +21 -0
- package/dist/nodejs/templates/test-vitest.md +131 -0
- package/dist/nodejs/templates/vue-core.md +108 -0
- package/dist/nodejs/templates/vue-pinia.md +5 -0
- package/dist/patterns/architecture/clean-architecture.md +469 -0
- package/dist/patterns/architecture/dependency-injection.md +517 -0
- package/dist/patterns/architecture/domain-driven-design.md +621 -0
- package/dist/patterns/architecture/layered-architecture.md +382 -0
- package/dist/patterns/architecture/repository-pattern.md +408 -0
- package/dist/patterns/domain-expertise/nextjs-rules.md +115 -0
- package/dist/patterns/domain-expertise/react-patterns.md +181 -0
- package/dist/patterns/domain-expertise/server-components.md +212 -0
- package/dist/patterns/domain-expertise/shadcn-ui.md +52 -0
- package/dist/patterns/domain-expertise/tailwind-patterns.md +52 -0
- package/dist/patterns/environment/container-awareness.md +17 -0
- package/dist/patterns/environment/ide-features.md +17 -0
- package/dist/patterns/environment/os-commands.md +17 -0
- package/dist/patterns/organization/heading-hierarchy.md +103 -0
- package/dist/patterns/organization/sequential-workflows.md +102 -0
- package/dist/patterns/organization/xml-rule-groups.md +64 -0
- package/dist/patterns/reasoning/agent-loop.md +151 -0
- package/dist/patterns/reasoning/confirmation-gates.md +141 -0
- package/dist/patterns/reasoning/dependency-analysis.md +132 -0
- package/dist/patterns/reasoning/one-tool-per-iteration.md +152 -0
- package/dist/patterns/reasoning/preview-before-action.md +194 -0
- package/dist/patterns/reasoning/reflection-checkpoints.md +166 -0
- package/dist/patterns/reasoning/result-verification.md +157 -0
- package/dist/patterns/reasoning/subtask-breakdown.md +131 -0
- package/dist/patterns/reasoning/thinking-tags.md +100 -0
- package/dist/patterns/role-definition/capability-declarations.md +72 -0
- package/dist/patterns/role-definition/expert-identity.md +45 -0
- package/dist/patterns/role-definition/scope-boundaries.md +61 -0
- package/dist/patterns/safety/code-safety-rules.md +17 -0
- package/dist/patterns/safety/credential-handling.md +17 -0
- package/dist/patterns/safety/destructive-warnings.md +17 -0
- package/dist/patterns/safety/refusal-messages.md +17 -0
- package/dist/patterns/tone/adaptive-tone.md +17 -0
- package/dist/patterns/tone/concise-communication.md +17 -0
- package/dist/patterns/tone/forbidden-phrases.md +17 -0
- package/dist/patterns/tool-guidelines/function-schemas.md +143 -0
- package/dist/patterns/tool-guidelines/parameter-examples.md +137 -0
- package/dist/patterns/tool-guidelines/usage-policies.md +105 -0
- package/dist/php/index.cjs +2 -2
- package/dist/php/{index.js → index.mjs} +12 -6
- package/dist/php/templates/framework-laravel.md +112 -0
- package/dist/php/templates/lang-php.md +94 -0
- package/dist/python/index.cjs +4 -4
- package/dist/python/{index.js → index.mjs} +10 -7
- package/dist/python/templates/lang-python.md +508 -0
- package/dist/ruby/index.cjs +2 -2
- package/dist/ruby/{index.js → index.mjs} +16 -10
- package/dist/ruby/templates/framework-rails.md +309 -0
- package/dist/ruby/templates/framework-sinatra.md +227 -0
- package/dist/ruby/templates/lang-ruby.md +216 -0
- package/dist/rust/index.cjs +3 -3
- package/dist/rust/{index.js → index.mjs} +24 -18
- package/dist/rust/templates/lang-rust.md +89 -0
- package/dist/swift/index.cjs +32 -0
- package/dist/swift/index.mjs +112 -0
- package/dist/swift/templates/framework-vapor.md +352 -0
- package/dist/swift/templates/lang-swift.md +291 -0
- package/package.json +31 -21
- package/dist/index.js +0 -20
- /package/dist/nodejs/{index.js → index.mjs} +0 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# Ruby on Rails Framework Instructions
|
|
2
|
+
|
|
3
|
+
## Project Structure
|
|
4
|
+
```
|
|
5
|
+
app/
|
|
6
|
+
├── models/ # Database models
|
|
7
|
+
├── controllers/ # Request handlers
|
|
8
|
+
├── views/ # Templates
|
|
9
|
+
├── jobs/ # Background jobs
|
|
10
|
+
├── mailers/ # Email templates
|
|
11
|
+
└── channels/ # WebSocket channels
|
|
12
|
+
config/
|
|
13
|
+
├── routes.rb # URL routing
|
|
14
|
+
├── database.yml # Database config
|
|
15
|
+
└── application.rb # App config
|
|
16
|
+
db/
|
|
17
|
+
├── migrate/ # Database migrations
|
|
18
|
+
└── schema.rb # Current schema
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## MVC Pattern
|
|
22
|
+
|
|
23
|
+
### Model
|
|
24
|
+
```ruby
|
|
25
|
+
class User < ApplicationRecord
|
|
26
|
+
has_many :posts
|
|
27
|
+
validates :email, presence: true, uniqueness: true
|
|
28
|
+
validates :name, presence: true
|
|
29
|
+
|
|
30
|
+
scope :active, -> { where(active: true) }
|
|
31
|
+
|
|
32
|
+
def full_name
|
|
33
|
+
"#{first_name} #{last_name}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Controller
|
|
39
|
+
```ruby
|
|
40
|
+
class UsersController < ApplicationController
|
|
41
|
+
before_action :set_user, only: [:show, :update, :destroy]
|
|
42
|
+
before_action :authenticate_user!
|
|
43
|
+
|
|
44
|
+
def index
|
|
45
|
+
@users = User.active.page(params[:page])
|
|
46
|
+
render json: @users
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def create
|
|
50
|
+
@user = User.new(user_params)
|
|
51
|
+
|
|
52
|
+
if @user.save
|
|
53
|
+
render json: @user, status: :created
|
|
54
|
+
else
|
|
55
|
+
render json: @user.errors, status: :unprocessable_entity
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def set_user
|
|
62
|
+
@user = User.find(params[:id])
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def user_params
|
|
66
|
+
params.require(:user).permit(:name, :email)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### View (ERB)
|
|
72
|
+
```erb
|
|
73
|
+
<h1>Users</h1>
|
|
74
|
+
|
|
75
|
+
<% @users.each do |user| %>
|
|
76
|
+
<div class="user">
|
|
77
|
+
<h2><%= user.name %></h2>
|
|
78
|
+
<p><%= user.email %></p>
|
|
79
|
+
<%= link_to 'Show', user_path(user) %>
|
|
80
|
+
</div>
|
|
81
|
+
<% end %>
|
|
82
|
+
|
|
83
|
+
<%= paginate @users %>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Routing
|
|
87
|
+
|
|
88
|
+
```ruby
|
|
89
|
+
# config/routes.rb
|
|
90
|
+
Rails.application.routes.draw do
|
|
91
|
+
root 'home#index'
|
|
92
|
+
|
|
93
|
+
resources :users do
|
|
94
|
+
resources :posts
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
namespace :api do
|
|
98
|
+
namespace :v1 do
|
|
99
|
+
resources :users, only: [:index, :show, :create]
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
get '/health', to: 'health#index'
|
|
104
|
+
end
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Database Migrations
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
rails generate migration CreateUsers name:string email:string
|
|
111
|
+
rails db:migrate
|
|
112
|
+
rails db:rollback
|
|
113
|
+
rails db:migrate:status
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
class CreateUsers < ActiveRecord::Migration[7.0]
|
|
118
|
+
def change
|
|
119
|
+
create_table :users do |t|
|
|
120
|
+
t.string :name, null: false
|
|
121
|
+
t.string :email, null: false
|
|
122
|
+
t.boolean :active, default: true
|
|
123
|
+
|
|
124
|
+
t.timestamps
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
add_index :users, :email, unique: true
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Active Record Queries
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
# Find
|
|
136
|
+
User.find(1)
|
|
137
|
+
User.find_by(email: 'user@example.com')
|
|
138
|
+
|
|
139
|
+
# Where
|
|
140
|
+
User.where(active: true)
|
|
141
|
+
User.where('created_at > ?', 1.week.ago)
|
|
142
|
+
|
|
143
|
+
# Joins
|
|
144
|
+
User.joins(:posts).where(posts: { published: true })
|
|
145
|
+
|
|
146
|
+
# Eager loading (avoid N+1)
|
|
147
|
+
User.includes(:posts).all
|
|
148
|
+
|
|
149
|
+
# Aggregations
|
|
150
|
+
User.count
|
|
151
|
+
User.average(:age)
|
|
152
|
+
User.group(:country).count
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Background Jobs (Sidekiq)
|
|
156
|
+
|
|
157
|
+
```ruby
|
|
158
|
+
class UserMailerJob < ApplicationJob
|
|
159
|
+
queue_as :default
|
|
160
|
+
|
|
161
|
+
def perform(user_id)
|
|
162
|
+
user = User.find(user_id)
|
|
163
|
+
UserMailer.welcome_email(user).deliver_now
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Enqueue job
|
|
168
|
+
UserMailerJob.perform_later(user.id)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## API Development
|
|
172
|
+
|
|
173
|
+
### JSON API
|
|
174
|
+
```ruby
|
|
175
|
+
class Api::V1::UsersController < ApplicationController
|
|
176
|
+
def index
|
|
177
|
+
users = User.active
|
|
178
|
+
render json: users, each_serializer: UserSerializer
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
class UserSerializer < ActiveModel::Serializer
|
|
183
|
+
attributes :id, :name, :email, :created_at
|
|
184
|
+
|
|
185
|
+
has_many :posts
|
|
186
|
+
end
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Jbuilder
|
|
190
|
+
```ruby
|
|
191
|
+
# app/views/users/index.json.jbuilder
|
|
192
|
+
json.array! @users do |user|
|
|
193
|
+
json.id user.id
|
|
194
|
+
json.name user.name
|
|
195
|
+
json.email user.email
|
|
196
|
+
json.posts user.posts, :id, :title
|
|
197
|
+
end
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Testing with RSpec
|
|
201
|
+
|
|
202
|
+
```ruby
|
|
203
|
+
# spec/requests/users_spec.rb
|
|
204
|
+
require 'rails_helper'
|
|
205
|
+
|
|
206
|
+
RSpec.describe 'Users API', type: :request do
|
|
207
|
+
describe 'GET /api/v1/users' do
|
|
208
|
+
it 'returns all active users' do
|
|
209
|
+
create_list(:user, 3, active: true)
|
|
210
|
+
|
|
211
|
+
get '/api/v1/users'
|
|
212
|
+
|
|
213
|
+
expect(response).to have_http_status(:ok)
|
|
214
|
+
expect(JSON.parse(response.body).size).to eq(3)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# spec/models/user_spec.rb
|
|
220
|
+
require 'rails_helper'
|
|
221
|
+
|
|
222
|
+
RSpec.describe User, type: :model do
|
|
223
|
+
it { should validate_presence_of(:email) }
|
|
224
|
+
it { should have_many(:posts) }
|
|
225
|
+
|
|
226
|
+
describe '.active' do
|
|
227
|
+
it 'returns only active users' do
|
|
228
|
+
active_user = create(:user, active: true)
|
|
229
|
+
create(:user, active: false)
|
|
230
|
+
|
|
231
|
+
expect(User.active).to eq([active_user])
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Configuration
|
|
238
|
+
|
|
239
|
+
### Database (config/database.yml)
|
|
240
|
+
```yaml
|
|
241
|
+
production:
|
|
242
|
+
adapter: postgresql
|
|
243
|
+
url: <%= ENV['DATABASE_URL'] %>
|
|
244
|
+
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Credentials
|
|
248
|
+
```bash
|
|
249
|
+
rails credentials:edit
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
```yaml
|
|
253
|
+
# config/credentials.yml.enc
|
|
254
|
+
secret_key_base: xxx
|
|
255
|
+
aws:
|
|
256
|
+
access_key_id: xxx
|
|
257
|
+
secret_access_key: xxx
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
```ruby
|
|
261
|
+
# Access in code
|
|
262
|
+
Rails.application.credentials.aws[:access_key_id]
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Docker Production Setup
|
|
266
|
+
|
|
267
|
+
```dockerfile
|
|
268
|
+
FROM ruby:3.2-alpine
|
|
269
|
+
WORKDIR /app
|
|
270
|
+
|
|
271
|
+
RUN apk add --no-cache postgresql-client tzdata
|
|
272
|
+
|
|
273
|
+
COPY --from=builder /usr/local/bundle /usr/local/bundle
|
|
274
|
+
COPY . .
|
|
275
|
+
|
|
276
|
+
RUN adduser -D -u 1000 rails && chown -R rails /app
|
|
277
|
+
USER rails
|
|
278
|
+
|
|
279
|
+
ENV RAILS_ENV=production
|
|
280
|
+
ENV RAILS_SERVE_STATIC_FILES=true
|
|
281
|
+
ENV RAILS_LOG_TO_STDOUT=true
|
|
282
|
+
|
|
283
|
+
EXPOSE 3000
|
|
284
|
+
|
|
285
|
+
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Performance Best Practices
|
|
289
|
+
|
|
290
|
+
- Use database indexes strategically
|
|
291
|
+
- Eager load associations with `includes`
|
|
292
|
+
- Use `pluck` for simple data extraction
|
|
293
|
+
- Cache expensive operations
|
|
294
|
+
- Use fragment caching for views
|
|
295
|
+
- Optimize database queries (N+1 detection)
|
|
296
|
+
- Use background jobs for slow operations
|
|
297
|
+
- Enable YJIT in Ruby 3.x
|
|
298
|
+
- Use ActionCable for real-time features
|
|
299
|
+
|
|
300
|
+
## Security Best Practices
|
|
301
|
+
|
|
302
|
+
- Use strong parameters
|
|
303
|
+
- Enable CSRF protection (default)
|
|
304
|
+
- Use `has_secure_password` for authentication
|
|
305
|
+
- Set secure headers
|
|
306
|
+
- Use SSL in production
|
|
307
|
+
- Sanitize user input
|
|
308
|
+
- Use parameterized queries (ActiveRecord default)
|
|
309
|
+
- Keep dependencies updated
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Sinatra Framework Instructions
|
|
2
|
+
|
|
3
|
+
## Minimal Web Framework
|
|
4
|
+
|
|
5
|
+
Sinatra is a lightweight Ruby web framework for quick web applications and APIs.
|
|
6
|
+
|
|
7
|
+
## Basic Application
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
require 'sinatra'
|
|
11
|
+
require 'json'
|
|
12
|
+
|
|
13
|
+
# Simple route
|
|
14
|
+
get '/' do
|
|
15
|
+
'Hello World!'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Route with parameters
|
|
19
|
+
get '/users/:id' do
|
|
20
|
+
user = User.find(params[:id])
|
|
21
|
+
user.to_json
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# POST request
|
|
25
|
+
post '/users' do
|
|
26
|
+
request.body.rewind
|
|
27
|
+
data = JSON.parse(request.body.read)
|
|
28
|
+
user = User.create(data)
|
|
29
|
+
status 201
|
|
30
|
+
user.to_json
|
|
31
|
+
end
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Modular Application
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
# app.rb
|
|
38
|
+
require 'sinatra/base'
|
|
39
|
+
|
|
40
|
+
class MyApp < Sinatra::Base
|
|
41
|
+
configure do
|
|
42
|
+
set :show_exceptions, false
|
|
43
|
+
set :raise_errors, true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
before do
|
|
47
|
+
content_type :json
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
get '/api/users' do
|
|
51
|
+
User.all.to_json
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
error 404 do
|
|
55
|
+
{ error: 'Not Found' }.to_json
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
run! if app_file == $0
|
|
59
|
+
end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Configuration
|
|
63
|
+
|
|
64
|
+
### config.ru
|
|
65
|
+
```ruby
|
|
66
|
+
require './app'
|
|
67
|
+
|
|
68
|
+
use Rack::Logger
|
|
69
|
+
use Rack::Session::Cookie, secret: ENV['SESSION_SECRET']
|
|
70
|
+
|
|
71
|
+
run MyApp
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Middleware
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
class MyApp < Sinatra::Base
|
|
78
|
+
use Rack::Auth::Basic do |username, password|
|
|
79
|
+
username == 'admin' && password == 'secret'
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
use Rack::Cors do
|
|
83
|
+
allow do
|
|
84
|
+
origins '*'
|
|
85
|
+
resource '*', headers: :any, methods: [:get, :post]
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Helpers
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
class MyApp < Sinatra::Base
|
|
95
|
+
helpers do
|
|
96
|
+
def authenticated?
|
|
97
|
+
session[:user_id].present?
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def current_user
|
|
101
|
+
@current_user ||= User.find(session[:user_id]) if authenticated?
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def require_authentication!
|
|
105
|
+
halt 401, { error: 'Unauthorized' }.to_json unless authenticated?
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
before '/api/*' do
|
|
110
|
+
require_authentication!
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Templates (ERB)
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
get '/users/:id' do
|
|
119
|
+
@user = User.find(params[:id])
|
|
120
|
+
erb :user
|
|
121
|
+
end
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```erb
|
|
125
|
+
<!-- views/user.erb -->
|
|
126
|
+
<h1><%= @user.name %></h1>
|
|
127
|
+
<p><%= @user.email %></p>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Testing with RSpec
|
|
131
|
+
|
|
132
|
+
```ruby
|
|
133
|
+
# spec/app_spec.rb
|
|
134
|
+
require 'rack/test'
|
|
135
|
+
require './app'
|
|
136
|
+
|
|
137
|
+
RSpec.describe 'MyApp' do
|
|
138
|
+
include Rack::Test::Methods
|
|
139
|
+
|
|
140
|
+
def app
|
|
141
|
+
MyApp
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
describe 'GET /api/users' do
|
|
145
|
+
it 'returns all users' do
|
|
146
|
+
create_list(:user, 3)
|
|
147
|
+
|
|
148
|
+
get '/api/users'
|
|
149
|
+
|
|
150
|
+
expect(last_response.status).to eq(200)
|
|
151
|
+
expect(JSON.parse(last_response.body).size).to eq(3)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Docker Setup
|
|
158
|
+
|
|
159
|
+
```dockerfile
|
|
160
|
+
FROM ruby:3.2-alpine
|
|
161
|
+
WORKDIR /app
|
|
162
|
+
|
|
163
|
+
RUN apk add --no-cache build-base
|
|
164
|
+
|
|
165
|
+
COPY Gemfile Gemfile.lock ./
|
|
166
|
+
RUN bundle install --without development test
|
|
167
|
+
|
|
168
|
+
COPY . .
|
|
169
|
+
|
|
170
|
+
RUN adduser -D -u 1000 sinatra && chown -R sinatra /app
|
|
171
|
+
USER sinatra
|
|
172
|
+
|
|
173
|
+
EXPOSE 4567
|
|
174
|
+
|
|
175
|
+
CMD ["ruby", "app.rb", "-o", "0.0.0.0"]
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Running the Application
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
# Development
|
|
182
|
+
ruby app.rb
|
|
183
|
+
|
|
184
|
+
# With Puma
|
|
185
|
+
bundle exec puma config.ru
|
|
186
|
+
|
|
187
|
+
# Production (Puma)
|
|
188
|
+
bundle exec puma -C config/puma.rb
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Common Extensions
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
# Gemfile
|
|
195
|
+
gem 'sinatra'
|
|
196
|
+
gem 'sinatra-contrib' # Reloader, JSON helpers
|
|
197
|
+
gem 'puma' # Web server
|
|
198
|
+
gem 'activerecord' # Database ORM
|
|
199
|
+
gem 'rack-cors' # CORS support
|
|
200
|
+
gem 'rack-protection' # Security
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Best Practices
|
|
204
|
+
|
|
205
|
+
- Use modular style for larger apps
|
|
206
|
+
- Implement proper error handling
|
|
207
|
+
- Use middleware for cross-cutting concerns
|
|
208
|
+
- Keep routes organized
|
|
209
|
+
- Use helpers for common logic
|
|
210
|
+
- Implement authentication/authorization
|
|
211
|
+
- Use environment variables for config
|
|
212
|
+
- Add logging
|
|
213
|
+
- Implement health checks
|
|
214
|
+
|
|
215
|
+
## When to Use Sinatra
|
|
216
|
+
|
|
217
|
+
✅ **Good for:**
|
|
218
|
+
- Simple APIs
|
|
219
|
+
- Microservices
|
|
220
|
+
- Prototypes
|
|
221
|
+
- Lightweight web services
|
|
222
|
+
|
|
223
|
+
❌ **Use Rails instead for:**
|
|
224
|
+
- Large applications
|
|
225
|
+
- Complex business logic
|
|
226
|
+
- Need for convention over configuration
|
|
227
|
+
- Built-in admin interfaces
|