Contributing

Thanks for contributing! Here's how to get started:

  1. Open an issue to discuss the proposed change
  2. Create a branch from main
  3. Implement the change with tests
  4. Make sure tests and linting pass
  5. Open a pull request targeting main

Setup

git clone git@github.com:fluence-eu/activejob-later.git
cd activejob-later
bundle install

Requires Ruby 3.3+.

Development

Running tests and linting

bundle exec guardian-ci all             # full catalog (lint + security + codequality + docs + tests)
bundle exec guardian-ci lint            # rubocop only
bundle exec guardian-ci tests           # minitest only
bundle exec guardian-ci docs --report   # yardstick threshold check
bundle exec rubocop -a                  # auto-fix linting (no guardian-ci equivalent)
bundle exec guardian-ci docs --build    # generate YARD HTML in doc/
bundle exec appraisal rake test         # minitest against every activejob version in Appraisals

Commit Convention

This project follows Conventional Commits.

Format

<type>(<scope>): <description>

[body]

[footer(s)]

Types

Type Description
feat New feature
fix Bug fix
docs Documentation only changes
style Formatting changes (whitespace, commas, etc.) — no logic
refactor Code change that neither fixes a bug nor adds a feature
perf Performance improvement
test Adding or updating tests
build Changes to build system or dependencies
ci CI/CD configuration changes
chore Other changes that don't modify source or tests
revert Revert a previous commit

Description

  • Imperative mood ("add", not "adds" or "added")
  • No capital letter at the start
  • No period at the end
  • Maximum 72 characters

Body (optional)

  • Use bullet points with - for multiple items
  • Explain why, not what (the diff speaks for itself)

Breaking Changes

Non-backward-compatible changes must be flagged with:

  • A ! after the type/scope: feat(proxy)!: change job argument format
  • And/or a BREAKING CHANGE: footer in the body

Pull Request Convention

PR Title

  • Same format as commits: <type>(<scope>): <description>
  • Under 72 characters

Rules

  • One topic per PR — don't mix unrelated changes
  • All tests must pass (bundle exec rake test)
  • New code must include tests
  • Never commit secrets or credentials

Architecture

lib/activejob-later.rb                   # Gem entry point (requires active_job + the extension)
lib/active_job/perform_later.rb          # Namespace, base_job configuration, job resolution
lib/active_job/perform_later/
  version.rb                             # Semantic version (bumped by release workflow)
  mixin.rb                               # Object#perform_later — returns the enqueueing proxy
  proxy.rb                               # BasicObject proxy capturing the next method call
  macro.rb                               # Module#perform_later_job — declares dedicated job classes
  job.rb                                 # Shared JobBehavior + generic fallback job (active_job load hook)
  • Mixin#perform_later returns a Proxy; the next method call on it is enqueued instead of invoked
  • Macro#perform_later_job declares per-method (optionally class/instance scoped) or class-wide job classes
  • Job resolution walks the target's ancestry for the conventional job constant, then falls back to the generic job
  • base_job configures the parent class of every generated job (String resolved lazily)

Tests

  • Framework: minitest (test/**/*_test.rb)
  • All new code must be tested
  • Coverage enforced at 95% minimum (SimpleCov + Undercover)
  • YARD-style doc comments on all public methods

Code Conventions

  • frozen_string_literal: true on every Ruby file
  • Strings: double quotes
  • RuboCop: no rule disabling without justification
  • Version in lib/active_job/perform_later/version.rb — do not modify unless for an explicit release
  • YARD-style doc comments on all public methods

CI

GitHub Actions:

  • main.yml — runs on every PR: minitest across the supported Rubies
  • docs-deploy.yml — builds the YARD site and publishes to GitHub Pages on main
  • docs-measure.yml, docs-pr.yml — yardstick baseline on main + sticky coverage-delta comment on PRs
  • rubocop-measure.yml, rubocop-pr.yml — RuboCop offense baseline + sticky offense-delta comment on PRs
  • coverage-measure.yml, coverage-pr.yml — coverage baseline + undercover regression check
  • codequality-measure.yml, codequality-pr.yml — reek/flog/flay advisory deltas
  • claude-review-pr.yml — Claude review on every PR
  • prepare-release.yml — workflow_dispatch stamps a dated version (YYYY.MM.DD.HHMMSS) and opens an auto-merged release PR
  • release.yml — on release PR merge, creates the tag + GitHub Release with the .gem attached
  • publish.yml — on Release published, pushes the gem to GitHub Packages
  • publish-rubygems.yml — on Release published, pushes the gem to rubygems.org through trusted publishing

AI-Assisted Contributions

When using AI tools (Claude, Copilot, etc.) to generate commits or PRs:

  • Classify first — determine if the change is critical, major, minor, or trivial
  • Focus on the "why" — the diff already shows the "what"
  • Skip boilerplate — don't add empty sections or placeholder text

The person opening the PR takes full responsibility for the code. Before submitting:

  • [ ] I have read and understood every line of code in this PR
  • [ ] I can explain why each change was made
  • [ ] I have tested the changes locally

Do not add AI co-author lines (Co-Authored-By), "Generated with" footers, or any AI attribution in commits or PR descriptions.