@re-shell/cli
Version:
Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja
1,292 lines (1,078 loc) • 29.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.grapeTemplate = void 0;
exports.grapeTemplate = {
id: 'grape',
name: 'grape',
displayName: 'Grape Framework',
description: 'REST-like API framework for Ruby with advanced routing, parameter validation, and automatic documentation',
language: 'ruby',
framework: 'grape',
version: '2.0.0',
tags: ['ruby', 'grape', 'api', 'rest', 'microservices', 'json', 'swagger'],
port: 9292,
dependencies: {},
features: ['authentication', 'database', 'validation', 'logging', 'documentation', 'testing'],
files: {
// Gemfile
'Gemfile': `source 'https://rubygems.org'
ruby '3.3.0'
# API framework
gem 'grape', '~> 2.0'
gem 'grape-entity', '~> 1.0'
gem 'grape-swagger', '~> 2.0'
gem 'grape-swagger-entity', '~> 0.5'
# Web server
gem 'puma', '~> 6.4'
gem 'rack', '~> 3.0'
# Database
gem 'activerecord', '~> 7.1.2'
gem 'pg', '~> 1.5'
gem 'rake', '~> 13.1'
# Authentication
gem 'bcrypt', '~> 3.1.19'
gem 'jwt', '~> 2.7'
gem 'rack-jwt', '~> 0.4'
# JSON
gem 'json', '~> 2.7'
gem 'multi_json', '~> 1.15'
gem 'oj', '~> 3.16'
# Validation
gem 'dry-validation', '~> 1.10'
gem 'dry-types', '~> 1.7'
# Environment
gem 'dotenv', '~> 2.8'
# Logging
gem 'grape_logging', '~> 1.8'
gem 'logger', '~> 1.6'
# CORS
gem 'rack-cors', '~> 2.0'
# Rate limiting
gem 'rack-attack', '~> 6.7'
# Redis
gem 'redis', '~> 5.0'
gem 'hiredis', '~> 0.6'
gem 'connection_pool', '~> 2.4'
# Background jobs
gem 'sidekiq', '~> 7.2'
# Pagination
gem 'grape-kaminari', '~> 0.4'
gem 'kaminari', '~> 1.2'
# Caching
gem 'grape-cache', '~> 0.1'
gem 'redis-rack-cache', '~> 2.2'
# Health checks
gem 'health_check', '~> 3.1'
# Error tracking
gem 'grape-sentry', '~> 0.4'
group :development do
gem 'rerun', '~> 0.14'
gem 'grape-reload', '~> 0.1'
end
group :test do
gem 'rspec', '~> 3.12'
gem 'rack-test', '~> 2.1'
gem 'factory_bot', '~> 6.4'
gem 'faker', '~> 3.2'
gem 'database_cleaner-active_record', '~> 2.1'
gem 'simplecov', '~> 0.22', require: false
gem 'shoulda-matchers', '~> 5.3'
gem 'timecop', '~> 0.9'
gem 'webmock', '~> 3.19'
gem 'vcr', '~> 6.2'
end
group :development, :test do
gem 'pry', '~> 0.14'
gem 'rubocop', '~> 1.59', require: false
gem 'rubocop-rspec', '~> 2.25', require: false
gem 'rubocop-grape', '~> 0.1', require: false
end
`,
// Ruby version
'.ruby-version': `3.3.0
`,
// Rakefile
'Rakefile': `require 'bundler/setup'
require 'rake'
require 'active_record'
# Load all rake tasks
Dir.glob('lib/tasks/*.rake').each { |r| import r }
# Default task
task default: :spec
# Load environment
task :environment do
require_relative 'config/environment'
end
# Database tasks
namespace :db do
desc "Create the database"
task create: :environment do
ActiveRecord::Base.establish_connection(DATABASE_CONFIG.merge('database' => 'postgres'))
ActiveRecord::Base.connection.create_database(DATABASE_CONFIG['database'])
puts "Database created."
end
desc "Drop the database"
task drop: :environment do
ActiveRecord::Base.establish_connection(DATABASE_CONFIG.merge('database' => 'postgres'))
ActiveRecord::Base.connection.drop_database(DATABASE_CONFIG['database'])
puts "Database dropped."
end
desc "Migrate the database"
task migrate: :environment do
ActiveRecord::MigrationContext.new('db/migrate').migrate
Rake::Task["db:schema:dump"].invoke
puts "Database migrated."
end
desc "Rollback the database"
task rollback: :environment do
ActiveRecord::MigrationContext.new('db/migrate').rollback
Rake::Task["db:schema:dump"].invoke
puts "Database rolled back."
end
namespace :schema do
desc "Create a db/schema.rb file"
task dump: :environment do
require 'active_record/schema_dumper'
File.open('db/schema.rb', 'w:utf-8') do |file|
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
end
end
end
desc "Reset the database"
task reset: [:drop, :create, :migrate]
end
# Test tasks
begin
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)
rescue LoadError
# RSpec not available in production
end
`,
// config.ru
'config.ru': `require_relative 'config/environment'
# Middleware stack
use Rack::Cors do
allow do
origins '*'
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head],
expose: ['X-Total-Count', 'X-Page', 'X-Per-Page']
end
end
use Rack::Attack
use ActiveRecord::ConnectionAdapters::ConnectionManagement
# Health check endpoint
map '/health' do
run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] }
end
# API endpoints
map '/api' do
run API::Root
end
# Swagger documentation
map '/swagger' do
run Rack::Cascade.new([
API::SwaggerUI,
API::Root
])
end
# Sidekiq Web UI (development only)
if ENV['RACK_ENV'] == 'development'
require 'sidekiq/web'
map '/sidekiq' do
run Sidekiq::Web
end
end
`,
// config/environment.rb
'config/environment.rb': `ENV['RACK_ENV'] ||= 'development'
require 'bundler/setup'
Bundler.require(:default, ENV['RACK_ENV'])
# Load environment variables
Dotenv.load(".env.\${ENV['RACK_ENV']}", '.env')
# Require all Ruby files
Dir[File.expand_path('../lib/**/*.rb', __dir__)].sort.each { |f| require f }
Dir[File.expand_path('../app/**/*.rb', __dir__)].sort.each { |f| require f }
# Configure database
DATABASE_CONFIG = YAML.load_file('config/database.yml')[ENV['RACK_ENV']]
ActiveRecord::Base.establish_connection(DATABASE_CONFIG)
ActiveRecord::Base.logger = Logger.new(STDOUT) if ENV['RACK_ENV'] == 'development'
# Configure Redis
REDIS_CONFIG = {
url: ENV['REDIS_URL'] || 'redis://localhost:6379/0',
driver: :hiredis
}
$redis = ConnectionPool::Wrapper.new(size: 10, timeout: 3) do
Redis.new(REDIS_CONFIG)
end
# Configure Sidekiq
Sidekiq.configure_server do |config|
config.redis = REDIS_CONFIG
end
Sidekiq.configure_client do |config|
config.redis = REDIS_CONFIG
end
# Configure Rack::Attack
Rack::Attack.cache.store = ActiveSupport::Cache::RedisStore.new(REDIS_CONFIG[:url])
# Throttle configuration
Rack::Attack.throttle('api/ip', limit: 300, period: 5.minutes) do |req|
req.ip if req.path.start_with?('/api')
end
Rack::Attack.throttle('api/aggressive', limit: 5, period: 1.minute) do |req|
req.ip if req.path.start_with?('/api') && req.post?
end
# Configure OJ for JSON parsing
Oj.default_options = {
mode: :compat,
time_format: :ruby,
use_to_json: true
}
MultiJson.use :oj
`,
// config/database.yml
'config/database.yml': `default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("DATABASE_POOL") { 5 } %>
timeout: 5000
development:
<<: *default
database: <%= ENV['DATABASE_NAME'] || 'myapp_development' %>
username: <%= ENV['DATABASE_USER'] || 'postgres' %>
password: <%= ENV['DATABASE_PASSWORD'] %>
host: <%= ENV['DATABASE_HOST'] || 'localhost' %>
port: <%= ENV['DATABASE_PORT'] || 5432 %>
test:
<<: *default
database: <%= ENV['DATABASE_NAME'] || 'myapp_test' %>
username: <%= ENV['DATABASE_USER'] || 'postgres' %>
password: <%= ENV['DATABASE_PASSWORD'] %>
host: <%= ENV['DATABASE_HOST'] || 'localhost' %>
port: <%= ENV['DATABASE_PORT'] || 5432 %>
production:
<<: *default
database: <%= ENV['DATABASE_NAME'] %>
username: <%= ENV['DATABASE_USER'] %>
password: <%= ENV['DATABASE_PASSWORD'] %>
host: <%= ENV['DATABASE_HOST'] %>
port: <%= ENV['DATABASE_PORT'] || 5432 %>
`,
// app/api/root.rb
'app/api/root.rb': `module API
class Root < Grape::API
format :json
prefix :v1
# Global exception handling
rescue_from :all do |e|
if ENV['RACK_ENV'] == 'development'
error!({ error: e.message, backtrace: e.backtrace }, 500)
else
error!({ error: 'Internal Server Error' }, 500)
end
end
rescue_from ActiveRecord::RecordNotFound do |e|
error!({ error: 'Record not found' }, 404)
end
rescue_from Grape::Exceptions::ValidationErrors do |e|
error!({ error: e.full_messages }, 400)
end
# Helpers
helpers AuthHelpers
helpers PaginationHelpers
# Mount APIs
mount API::Auth
mount API::Users
mount API::Products
mount API::Orders
mount API::Health
# Swagger documentation
add_swagger_documentation(
api_version: 'v1',
hide_documentation_path: true,
mount_path: '/swagger_doc',
info: {
title: 'My API',
description: 'API documentation for My Application',
contact_name: 'API Support',
contact_email: 'api@example.com',
license: 'MIT',
license_url: 'https://opensource.org/licenses/MIT'
}
)
end
# Swagger UI for development
class SwaggerUI < Grape::API
format :html
get '/' do
content_type 'text/html'
<<-HTML
<!DOCTYPE html>
<html>
<head>
<title>API Documentation</title>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.18.3/swagger-ui.css">
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.18.3/swagger-ui-bundle.js"></script>
<script>
window.onload = function() {
SwaggerUIBundle({
url: "/api/v1/swagger_doc",
dom_id: '#swagger-ui',
presets: [SwaggerUIBundle.presets.apis],
layout: "BaseLayout"
});
}
</script>
</body>
</html>
HTML
end
end
end
`,
// app/api/auth.rb
'app/api/auth.rb': `module API
class Auth < Grape::API
resource :auth do
desc 'User login' do
detail 'Authenticate user and return JWT token'
tags ['Authentication']
end
params do
requires :email, type: String, desc: 'User email'
requires :password, type: String, desc: 'User password'
end
post :login do
user = User.find_by(email: params[:email])
if user&.authenticate(params[:password])
token = JWT.encode(
{
user_id: user.id,
email: user.email,
exp: 24.hours.from_now.to_i
},
ENV['JWT_SECRET'] || 'secret',
'HS256'
)
{ token: token, user: UserEntity.new(user) }
else
error!({ error: 'Invalid credentials' }, 401)
end
end
desc 'User registration' do
detail 'Create a new user account'
tags ['Authentication']
end
params do
requires :email, type: String, desc: 'User email'
requires :password, type: String, desc: 'User password'
requires :password_confirmation, type: String, desc: 'Password confirmation'
optional :name, type: String, desc: 'User name'
end
post :register do
user = User.new(declared(params))
if user.save
token = JWT.encode(
{
user_id: user.id,
email: user.email,
exp: 24.hours.from_now.to_i
},
ENV['JWT_SECRET'] || 'secret',
'HS256'
)
{ token: token, user: UserEntity.new(user) }
else
error!({ errors: user.errors.full_messages }, 422)
end
end
desc 'Refresh token' do
detail 'Get a new JWT token'
tags ['Authentication']
end
get :refresh do
authenticate!
token = JWT.encode(
{
user_id: current_user.id,
email: current_user.email,
exp: 24.hours.from_now.to_i
},
ENV['JWT_SECRET'] || 'secret',
'HS256'
)
{ token: token }
end
desc 'User profile' do
detail 'Get current user profile'
tags ['Authentication']
end
get :profile do
authenticate!
UserEntity.new(current_user)
end
end
end
end
`,
// app/api/users.rb
'app/api/users.rb': `module API
class Users < Grape::API
before { authenticate! }
resource :users do
desc 'List all users' do
detail 'Get paginated list of users'
tags ['Users']
paginate per_page: 20, max_per_page: 100
end
params do
optional :search, type: String, desc: 'Search by name or email'
optional :role, type: String, values: User::ROLES, desc: 'Filter by role'
use :pagination
end
get do
users = User.all
users = users.search(params[:search]) if params[:search]
users = users.where(role: params[:role]) if params[:role]
paginate(users, UserEntity)
end
desc 'Get a user' do
detail 'Get user by ID'
tags ['Users']
end
params do
requires :id, type: Integer, desc: 'User ID'
end
get ':id' do
user = User.find(params[:id])
UserEntity.new(user)
end
desc 'Create a user' do
detail 'Create a new user (admin only)'
tags ['Users']
end
params do
requires :email, type: String, desc: 'User email'
requires :password, type: String, desc: 'User password'
optional :name, type: String, desc: 'User name'
optional :role, type: String, values: User::ROLES, desc: 'User role'
end
post do
authorize_admin!
user = User.new(declared(params))
if user.save
UserEntity.new(user)
else
error!({ errors: user.errors.full_messages }, 422)
end
end
desc 'Update a user' do
detail 'Update user details'
tags ['Users']
end
params do
requires :id, type: Integer, desc: 'User ID'
optional :name, type: String, desc: 'User name'
optional :email, type: String, desc: 'User email'
optional :role, type: String, values: User::ROLES, desc: 'User role'
end
patch ':id' do
user = User.find(params[:id])
authorize_user!(user)
if user.update(declared(params, include_missing: false))
UserEntity.new(user)
else
error!({ errors: user.errors.full_messages }, 422)
end
end
desc 'Delete a user' do
detail 'Delete a user (admin only)'
tags ['Users']
end
params do
requires :id, type: Integer, desc: 'User ID'
end
delete ':id' do
authorize_admin!
user = User.find(params[:id])
user.destroy
status 204
end
end
end
end
`,
// app/helpers/auth_helpers.rb
'app/helpers/auth_helpers.rb': `module AuthHelpers
def authenticate!
error!('Unauthorized', 401) unless current_user
end
def current_user
return @current_user if defined?(@current_user)
token = headers['Authorization']&.split(' ')&.last
return @current_user = nil unless token
begin
payload = JWT.decode(
token,
ENV['JWT_SECRET'] || 'secret',
true,
algorithm: 'HS256'
).first
@current_user = User.find_by(id: payload['user_id'])
rescue JWT::DecodeError
@current_user = nil
end
end
def authorize_admin!
error!('Forbidden', 403) unless current_user&.admin?
end
def authorize_user!(user)
error!('Forbidden', 403) unless current_user&.admin? || current_user == user
end
end
`,
// app/helpers/pagination_helpers.rb
'app/helpers/pagination_helpers.rb': `module PaginationHelpers
extend Grape::API::Helpers
params :pagination do
optional :page, type: Integer, default: 1, desc: 'Page number'
optional :per_page, type: Integer, default: 20, desc: 'Items per page'
end
def paginate(collection, entity_class)
collection = collection.page(params[:page]).per(params[:per_page])
header 'X-Total-Count', collection.total_count.to_s
header 'X-Page', collection.current_page.to_s
header 'X-Per-Page', collection.limit_value.to_s
header 'X-Total-Pages', collection.total_pages.to_s
present collection, with: entity_class
end
end
`,
// app/models/user.rb
'app/models/user.rb': `class User < ActiveRecord::Base
has_secure_password
ROLES = %w[user admin moderator].freeze
# Associations
has_many :orders, dependent: :destroy
# Validations
validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }
validates :password, length: { minimum: 6 }, if: :password_required?
validates :role, inclusion: { in: ROLES }
# Scopes
scope :search, ->(query) { where('name ILIKE ? OR email ILIKE ?', "%\#{query}%", "%\#{query}%") }
scope :admins, -> { where(role: 'admin') }
scope :active, -> { where(active: true) }
# Callbacks
before_validation :normalize_email
before_create :set_default_role
# Methods
def admin?
role == 'admin'
end
def moderator?
role == 'moderator'
end
private
def normalize_email
self.email = email&.downcase&.strip
end
def set_default_role
self.role ||= 'user'
end
def password_required?
new_record? || password.present?
end
end
`,
// app/entities/user_entity.rb
'app/entities/user_entity.rb': `class UserEntity < Grape::Entity
expose :id
expose :email
expose :name
expose :role
expose :active
expose :created_at
expose :updated_at
# Conditional exposures
expose :admin?, as: :is_admin, if: lambda { |user, options| options[:current_user]&.admin? }
expose :orders_count, if: lambda { |user, options| options[:include_stats] } do |user|
user.orders.count
end
end
`,
// db/migrate/001_create_users.rb
'db/migrate/001_create_users.rb': `class CreateUsers < ActiveRecord::Migration[7.1]
def change
create_table :users do |t|
t.string :email, null: false
t.string :password_digest, null: false
t.string :name
t.string :role, default: 'user'
t.boolean :active, default: true
t.timestamps
end
add_index :users, :email, unique: true
add_index :users, :role
add_index :users, :active
end
end
`,
// spec/spec_helper.rb
'spec/spec_helper.rb': `ENV['RACK_ENV'] = 'test'
require_relative '../config/environment'
require 'rspec'
require 'rack/test'
require 'factory_bot'
require 'faker'
require 'database_cleaner/active_record'
# Configure RSpec
RSpec.configure do |config|
config.include Rack::Test::Methods
config.include FactoryBot::Syntax::Methods
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
FactoryBot.find_definitions
end
config.around(:each) do |example|
DatabaseCleaner.cleaning do
example.run
end
end
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.shared_context_metadata_behavior = :apply_to_host_groups
config.filter_run_when_matching :focus
config.example_status_persistence_file_path = "spec/examples.txt"
config.disable_monkey_patching!
config.warnings = true
if config.files_to_run.one?
config.default_formatter = "doc"
end
config.order = :random
Kernel.srand config.seed
end
# Helper method for API testing
def app
API::Root
end
# Helper for authentication
def auth_headers(user)
token = JWT.encode(
{ user_id: user.id, email: user.email, exp: 1.hour.from_now.to_i },
ENV['JWT_SECRET'] || 'secret',
'HS256'
)
{ 'Authorization' => "Bearer \#{token}" }
end
`,
// spec/api/auth_spec.rb
'spec/api/auth_spec.rb': `require 'spec_helper'
RSpec.describe API::Auth do
describe 'POST /api/v1/auth/login' do
let!(:user) { create(:user, email: 'test@example.com', password: 'password123') }
context 'with valid credentials' do
it 'returns JWT token' do
post '/api/v1/auth/login', email: 'test@example.com', password: 'password123'
expect(last_response.status).to eq(201)
expect(JSON.parse(last_response.body)).to have_key('token')
expect(JSON.parse(last_response.body)).to have_key('user')
end
end
context 'with invalid credentials' do
it 'returns 401' do
post '/api/v1/auth/login', email: 'test@example.com', password: 'wrong'
expect(last_response.status).to eq(401)
expect(JSON.parse(last_response.body)).to have_key('error')
end
end
end
describe 'POST /api/v1/auth/register' do
context 'with valid data' do
it 'creates user and returns token' do
post '/api/v1/auth/register', {
email: 'new@example.com',
password: 'password123',
password_confirmation: 'password123',
name: 'New User'
}
expect(last_response.status).to eq(201)
expect(JSON.parse(last_response.body)).to have_key('token')
expect(User.find_by(email: 'new@example.com')).to be_present
end
end
context 'with invalid data' do
it 'returns validation errors' do
post '/api/v1/auth/register', {
email: 'invalid',
password: 'short',
password_confirmation: 'different'
}
expect(last_response.status).to eq(422)
expect(JSON.parse(last_response.body)).to have_key('errors')
end
end
end
describe 'GET /api/v1/auth/profile' do
let(:user) { create(:user) }
context 'with valid token' do
it 'returns user profile' do
header 'Authorization', "Bearer \#{auth_headers(user)['Authorization']}"
get '/api/v1/auth/profile'
expect(last_response.status).to eq(200)
expect(JSON.parse(last_response.body)['email']).to eq(user.email)
end
end
context 'without token' do
it 'returns 401' do
get '/api/v1/auth/profile'
expect(last_response.status).to eq(401)
end
end
end
end
`,
// spec/factories/users.rb
'spec/factories/users.rb': `FactoryBot.define do
factory :user do
email { Faker::Internet.email }
password { 'password123' }
name { Faker::Name.name }
role { 'user' }
active { true }
trait :admin do
role { 'admin' }
end
trait :moderator do
role { 'moderator' }
end
trait :inactive do
active { false }
end
end
end
`,
// .env.example
'.env.example': `# Application
RACK_ENV=development
PORT=9292
# Database
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USER=postgres
DATABASE_PASSWORD=
DATABASE_NAME=myapp_development
# Redis
REDIS_URL=redis://localhost:6379/0
# JWT
JWT_SECRET=your-secret-key-here
# Logging
LOG_LEVEL=debug
# External APIs
SENTRY_DSN=
# Sidekiq
SIDEKIQ_CONCURRENCY=10
`,
// Dockerfile
'Dockerfile': `FROM ruby:3.3.0-alpine
# Install dependencies
RUN apk add --no-cache \\
build-base \\
postgresql-dev \\
tzdata \\
git
# Set working directory
WORKDIR /app
# Install gems
COPY Gemfile Gemfile.lock ./
RUN bundle config set --local deployment 'true' && \\
bundle config set --local without 'development test' && \\
bundle install --jobs 4 --retry 3
# Copy application
COPY . .
# Create non-root user
RUN addgroup -g 1000 -S app && \\
adduser -u 1000 -S app -G app && \\
chown -R app:app /app
USER app
# Expose port
EXPOSE 9292
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \\
CMD curl -f http://localhost:9292/health || exit 1
# Run the application
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
`,
// docker-compose.yml
'docker-compose.yml': `version: '3.8'
services:
app:
build: .
ports:
- "9292:9292"
environment:
- RACK_ENV=development
- DATABASE_HOST=db
- DATABASE_USER=postgres
- DATABASE_PASSWORD=postgres
- REDIS_URL=redis://redis:6379/0
depends_on:
- db
- redis
volumes:
- .:/app
- bundle:/usr/local/bundle
command: bundle exec rerun 'rackup -p 9292 -o 0.0.0.0'
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=myapp_development
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
sidekiq:
build: .
depends_on:
- db
- redis
environment:
- RACK_ENV=development
- DATABASE_HOST=db
- DATABASE_USER=postgres
- DATABASE_PASSWORD=postgres
- REDIS_URL=redis://redis:6379/0
volumes:
- .:/app
- bundle:/usr/local/bundle
command: bundle exec sidekiq
volumes:
postgres_data:
redis_data:
bundle:
`,
// README.md
'README.md': `# Grape API Application
A RESTful API built with Grape framework for Ruby.
## Features
- 🍇 Grape framework for API development
- 🔐 JWT authentication
- 📝 Swagger API documentation
- 🗄️ PostgreSQL with ActiveRecord
- 🔄 Background jobs with Sidekiq
- ✅ RSpec testing suite
- 🐳 Docker support
- 📊 Request rate limiting
- 🌐 CORS support
- 📄 Pagination with headers
## Prerequisites
- Ruby 3.3.0
- PostgreSQL 14+
- Redis 6+
- Docker (optional)
## Installation
1. Clone the repository:
\`\`\`bash
git clone <repository-url>
cd <project-directory>
\`\`\`
2. Install dependencies:
\`\`\`bash
bundle install
\`\`\`
3. Setup environment:
\`\`\`bash
cp .env.example .env
# Edit .env with your configuration
\`\`\`
4. Setup database:
\`\`\`bash
bundle exec rake db:create
bundle exec rake db:migrate
\`\`\`
## Running the Application
### Development
\`\`\`bash
# Run with auto-reload
bundle exec rerun 'rackup -p 9292'
# Or without auto-reload
bundle exec rackup -p 9292
\`\`\`
### Production
\`\`\`bash
bundle exec puma -C config/puma.rb
\`\`\`
### Docker
\`\`\`bash
docker-compose up
\`\`\`
## API Documentation
When running in development, Swagger documentation is available at:
http://localhost:9292/swagger
## Testing
\`\`\`bash
# Run all tests
bundle exec rspec
# Run with coverage
COVERAGE=true bundle exec rspec
# Run specific test
bundle exec rspec spec/api/users_spec.rb
\`\`\`
## API Endpoints
### Authentication
- \`POST /api/v1/auth/login\` - User login
- \`POST /api/v1/auth/register\` - User registration
- \`GET /api/v1/auth/refresh\` - Refresh JWT token
- \`GET /api/v1/auth/profile\` - Get current user profile
### Users
- \`GET /api/v1/users\` - List users (paginated)
- \`GET /api/v1/users/:id\` - Get user details
- \`POST /api/v1/users\` - Create user (admin only)
- \`PATCH /api/v1/users/:id\` - Update user
- \`DELETE /api/v1/users/:id\` - Delete user (admin only)
## Background Jobs
Run Sidekiq worker:
\`\`\`bash
bundle exec sidekiq
\`\`\`
## Code Quality
\`\`\`bash
# Run RuboCop
bundle exec rubocop
# Auto-correct offenses
bundle exec rubocop -A
\`\`\`
## Deployment
1. Set production environment variables
2. Precompile assets (if any)
3. Run database migrations
4. Start the application with Puma
## Contributing
1. Fork the repository
2. Create your feature branch
3. Write tests for your changes
4. Ensure all tests pass
5. Submit a pull request
## License
MIT License
`,
// config/puma.rb
'config/puma.rb': `# Puma configuration file
# Threads
threads_count = ENV.fetch("RAILS_MAX_THREADS", 5)
threads threads_count, threads_count
# Port
port ENV.fetch("PORT", 9292)
# Environment
environment ENV.fetch("RACK_ENV", "development")
# Workers (in production)
workers ENV.fetch("WEB_CONCURRENCY", 2) if ENV["RACK_ENV"] == "production"
# Preload app for performance
preload_app! if ENV["RACK_ENV"] == "production"
# Allow puma to be restarted by \`rails restart\` command
plugin :tmp_restart
on_worker_boot do
# Reconnect to database
ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
# Reconnect to Redis
$redis.client.reconnect if defined?($redis)
end
`,
// .rubocop.yml
'.rubocop.yml': `require:
- rubocop-rspec
- rubocop-grape
AllCops:
NewCops: enable
TargetRubyVersion: 3.3
Exclude:
- 'db/schema.rb'
- 'vendor/**/*'
- 'bin/**/*'
Style/Documentation:
Enabled: false
Style/FrozenStringLiteralComment:
Enabled: false
Metrics/BlockLength:
Exclude:
- 'spec/**/*'
- 'app/api/**/*'
Metrics/MethodLength:
Max: 20
Metrics/ClassLength:
Max: 200
Layout/LineLength:
Max: 120
RSpec/ExampleLength:
Max: 20
RSpec/MultipleExpectations:
Max: 5
`,
// .gitignore
'.gitignore': `# Ruby
*.gem
*.rbc
/.config
/coverage/
/InstalledFiles
/pkg/
/spec/reports/
/spec/examples.txt
/test/tmp/
/test/version_tmp/
/tmp/
# Bundler
/.bundle/
/vendor/bundle
/lib/bundler/man/
# Environment
.env
.env.*
!.env.example
# Database
/db/*.sqlite3
/db/*.sqlite3-journal
/db/*.sqlite3-*
# Logs
/log/*
!/log/.keep
*.log
# OS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# IDE
.idea/
.vscode/
*.swp
*.swo
*~
# Testing
/coverage/
.rspec_status
# Docker
.dockerignore
docker-compose.override.yml
`
}
};