Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,9 @@ DATABASE_DEV_PASSWORD=postgres_password
GITHUB_ID=github_client_id
GITHUB_SECRET=github_client_secret
GOOGLE_ID=google_client_id
GOOGLE_SECRET=google_secret_key
GOOGLE_SECRET=google_secret_key
FACEBOOK_ID=facebook_app_id
FACEBOOK_SECRET=facebook_app_secret
TWITTER_ID=twitter_app_id
TWITTER_SECRET=twitter_app_secret
TZ=America/Sao_Paulo
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.1.2
3.4.5
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ruby 3.4.5
166 changes: 166 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Development Environment

### Ruby and Rails Versions
- Ruby: 3.4.5 (managed via asdf)
- Rails: 8.0.3
- Node: 22.x
- Bundler: 2.7.2

### Essential Commands

#### Development Server
```bash
bin/dev # Start Rails server with CSS watching (uses Procfile.dev)
rails server # Start Rails server only
yarn build:css --watch # Watch and compile CSS changes
```

#### Database
```bash
rails db:create # Create database
rails db:migrate # Run migrations
rails db:seed # Seed database with sample data
rails db:drop # Drop database (careful!)
rails db:reset # Drop, create, migrate, and seed
```

#### Testing
```bash
# Run all RSpec tests
bundle exec rspec

# Run specific test categories
bundle exec rspec spec/features
bundle exec rspec spec/models
bundle exec rspec spec/requests
bundle exec rspec spec/integration

# Run Rails tests
rails test test/models
rails test test/controllers
rails test test/integration
rails test test/system

# Run a single test file
bundle exec rspec path/to/spec_file.rb
rails test path/to/test_file.rb

# Run tests with specific line number
bundle exec rspec path/to/spec_file.rb:42
```

#### Linting and Code Quality
```bash
bundle exec rubocop # Run Rubocop linter
bundle exec rubocop -a # Auto-fix Rubocop offenses
bundle exec rubocop path/to/file.rb # Check specific file
```

#### API Documentation
```bash
bundle exec rake rswag # Generate Swagger/OpenAPI documentation
# Access at: http://localhost:3000/api-docs/index.html
```

#### Background Jobs
```bash
bundle exec sidekiq # Start Sidekiq worker
redis-server # Start Redis (required for Sidekiq)
```

#### Asset Compilation
```bash
yarn build:css # Build CSS with Bulma
rails assets:precompile # Precompile all assets for production
rails assets:clobber # Remove compiled assets
```

## Architecture Overview

### Core Models and Relationships

The application centers around **Member** (User) with Devise authentication:
- **Member** → has many **Posts** (blog posts)
- **Post** → has many **Comments** and **Likes**
- Complex analytics system tracking visitors, browsers, and user interactions

### Authentication & Authorization
- **Devise** handles authentication with OAuth support (GitHub, Google)
- **CanCanCan** manages authorization through `app/models/ability.rb`
- **JWT** tokens for API authentication (`app/models/json_web_token.rb`)
- API login endpoint: `POST /api/v1/auth/login`

### API Structure
- Versioned API under `app/controllers/api/v1/`
- JWT-based authentication for API endpoints
- Swagger documentation auto-generated from tests
- API responses use Jbuilder templates in `app/views/api/v1/`

### Real-time Features
- **Turbo Streams** for live updates without page refresh
- **Stimulus controllers** in `app/javascript/controllers/`
- **Action Cable** broadcasts analytics updates to dashboards
- Chart controller manages real-time analytics visualization

### Background Processing
- **Sidekiq** jobs in `app/jobs/` process analytics asynchronously
- Jobs: AddCounterJob, AddBrowserJob, AddUniqueJob, AddLengthJob
- Redis required for job queue management

### Frontend Stack
- **Turbo Rails** for SPA-like navigation
- **Stimulus** for JavaScript behavior
- **Bulma CSS** framework (compiled via cssbundling-rails)
- **Chartkick** for analytics charts
- **Pagy** for pagination

### Testing Approach
- **RSpec** for API and integration tests (`spec/`)
- **Rails Test** for models and controllers (`test/`)
- **Factory Bot** for test data generation
- **Capybara** with Selenium for system tests
- Tests generate API documentation via Rswag

### Key Technical Patterns

1. **Counter Caches**: Automatic count maintenance
- `posts_count` on Member
- `comments_count` and `likes_count` on Post

2. **FriendlyId**: SEO-friendly URLs for members
- Uses slug field on Member model

3. **Active Storage**: Avatar uploads with validation
- Image processing for variants

4. **Analytics System**:
- Visitor tracking with browser fingerprinting
- Multiple analytics models for different metrics
- MemberBrowser class implements visitor pattern

5. **Broadcasting Pattern**:
- CounterAnalytic broadcasts updates to member-specific channels
- Real-time dashboard updates via Turbo Streams

### Database Considerations
- PostgreSQL with multiple extensions enabled
- Counter caches maintain denormalized counts
- Indexes on foreign keys and frequently queried fields
- Bullet gem monitors N+1 queries in development

### Environment Variables
Required in `.env` for development:
- `DATABASE_DEV_USERNAME` - PostgreSQL username
- `DATABASE_DEV_PASSWORD` - PostgreSQL password
- `GITHUB_ID` & `GITHUB_SECRET` - GitHub OAuth
- `GOOGLE_ID` & `GOOGLE_SECRET` - Google OAuth

### Deployment
- Configured for Render.com deployment
- Dockerfile uses Ruby 3.4.5 and Node 22.x
- Production uses PostgreSQL and Redis
- Assets precompiled during Docker build
37 changes: 20 additions & 17 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,13 @@

#######################################################################

# Learn more about the chosen Ruby stack, Fullstaq Ruby, here:
# https://github.com/evilmartians/fullstaq-ruby-docker.
#
# We recommend using the highest patch level for better security and
# performance.

ARG RUBY_VERSION=3.1.2
ARG VARIANT=jemalloc-bullseye-slim
FROM quay.io/evl.ms/fullstaq-ruby:${RUBY_VERSION}-${VARIANT} as base
# Using official Ruby image for Ruby 3.4.5 support
ARG RUBY_VERSION=3.4.5
FROM ruby:${RUBY_VERSION}-slim as base

LABEL fly_launch_runtime="rails"

ARG BUNDLER_VERSION=2.3.22
ARG BUNDLER_VERSION=2.7.2

ARG RAILS_ENV=production
ENV RAILS_ENV=${RAILS_ENV}
Expand All @@ -49,43 +43,52 @@ RUN gem update --system --no-document && \

FROM base as build_deps

ARG BUILD_PACKAGES="git build-essential libpq-dev wget vim curl gzip xz-utils libsqlite3-dev"
ARG BUILD_PACKAGES="git build-essential libpq-dev wget vim curl gzip xz-utils libsqlite3-dev nodejs npm"
ENV BUILD_PACKAGES ${BUILD_PACKAGES}

# Install Node.js 22.x
RUN --mount=type=cache,id=dev-apt-cache,sharing=locked,target=/var/cache/apt \
--mount=type=cache,id=dev-apt-lib,sharing=locked,target=/var/lib/apt \
apt-get update -qq && \
apt-get install -y curl && \
curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
apt-get install --no-install-recommends -y ${BUILD_PACKAGES} \
&& rm -rf /var/lib/apt/lists /var/cache/apt/archives

#######################################################################

# install gems
# install gems and node modules

FROM build_deps as gems

COPY Gemfile* ./
RUN bundle install && rm -rf vendor/bundle/ruby/*/cache
RUN bundle install && rm -rf vendor/bundle/ruby/*/cache

# Copy package.json and install node modules for asset compilation
COPY package.json ./
RUN npm install

#######################################################################

# install deployment packages

FROM base

ARG DEPLOY_PACKAGES="postgresql-client file vim curl gzip libsqlite3-0"
ARG DEPLOY_PACKAGES="postgresql-client file vim curl gzip libsqlite3-0 nodejs"
ENV DEPLOY_PACKAGES=${DEPLOY_PACKAGES}

# Install Node.js 22.x for runtime (needed for execjs)
RUN --mount=type=cache,id=prod-apt-cache,sharing=locked,target=/var/cache/apt \
--mount=type=cache,id=prod-apt-lib,sharing=locked,target=/var/lib/apt \
apt-get update -qq && \
apt-get install -y curl && \
curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
apt-get install --no-install-recommends -y \
${DEPLOY_PACKAGES} \
&& rm -rf /var/lib/apt/lists /var/cache/apt/archives

# copy installed gems
# copy installed gems and node modules
COPY --from=gems /app /app
COPY --from=gems /usr/lib/fullstaq-ruby/versions /usr/lib/fullstaq-ruby/versions
COPY --from=gems /usr/local/bundle /usr/local/bundle

#######################################################################
Expand Down Expand Up @@ -113,4 +116,4 @@ RUN ${BUILD_COMMAND}
ENV PORT 8080
ARG SERVER_COMMAND="bin/rails fly:server"
ENV SERVER_COMMAND ${SERVER_COMMAND}
CMD ${SERVER_COMMAND}
CMD ${SERVER_COMMAND}
10 changes: 5 additions & 5 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '3.1.2'
ruby '3.4.5'

gem 'rubocop', '>= 1.0', '< 2.0'
gem 'rubocop', '>= 1.58', '< 2.0'

# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
gem 'rails', '~> 7.0.4'
gem 'rails', '~> 8.0.0'

# The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]
gem 'sprockets-rails'
Expand All @@ -27,7 +27,7 @@ gem 'rspec'
gem 'jwt'

# Use the Puma web server [https://github.com/puma/puma]
gem 'puma', '~> 5.0'
gem 'puma', '~> 6.0'

# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
gem 'importmap-rails'
Expand All @@ -42,7 +42,7 @@ gem 'stimulus-rails'
gem 'jbuilder'

# Use Redis adapter to run Action Cable in production
gem 'redis', '~> 4.0'
gem 'redis', '~> 5.0'

# Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis]
# gem "kredis"
Expand Down
Loading