RuboCop Integration
Axn provides a custom RuboCop cop to help enforce proper result handling when calling Actions.
What It Does
The Axn/UncheckedResult cop detects when you call another Action from within an Action but don't properly handle the result. This helps prevent silent failures and ensures consistent error handling patterns.
⚠️ Warning: This cop uses static analysis and cannot distinguish between actual Axn classes and other classes that happen to have a
callmethod. If you're using legacy services or other service patterns alongside Axn, you may encounter false positives. Use RuboCop disable comments for intentional violations.💡 Tip: If you're using the Actions namespace (see Rails Integration), you can configure the cop to only check
Actions::*classes, eliminating false positives from other service objects.
Setup
1. Add to Your .rubocop.yml
require:
- axn/rubocop
Axn/UncheckedResult:
Enabled: true
Severity: warning2. Verify Installation
bundle exec rubocop --show-cops | grep AxnYou should see Axn/UncheckedResult in the output.
Basic Usage
✅ Good - Using call!
class OuterAction
include Axn
def call
InnerAction.call!(param: "value") # Exceptions bubble up
end
end✅ Good - Checking the result
class OuterAction
include Axn
def call
result = InnerAction.call(param: "value")
return result unless result.ok?
# Process successful result...
end
end❌ Bad - Ignoring the result
class OuterAction
include Axn
def call
InnerAction.call(param: "value") # Will trigger offense
# This continues even if InnerAction fails
end
endConfiguration
The cop supports flexible configuration:
Axn/UncheckedResult:
Enabled: true
CheckNested: true # Check nested Axn calls (default: true)
CheckNonNested: true # Check non-nested Axn calls (default: true)
ActionsNamespace: "Actions" # Only check Actions::* classes (optional)
Severity: warning # or errorActionsNamespace Configuration
When using the Actions namespace, you can configure the cop to only check calls on Actions::* classes:
Axn/UncheckedResult:
ActionsNamespace: "Actions"This eliminates false positives from other service objects while still catching unchecked Axn action calls:
class OuterAction
include Axn
def call
SomeService.call(param: "value") # Won't trigger cop
Actions::InnerAction.call(param: "value") # Will trigger cop
end
endFor detailed configuration options, usage patterns, and troubleshooting, see the technical documentation.
Axn/AmbientContextBypass
A second, opt-in cop that flags reading Current.<attr> / ::Current.<attr> directly inside an Axn and steers you toward declaring the dependency explicitly with expects :<attr>, on: :ambient_context. Reaching into Current inside an action hides a real dependency — the class's expects declarations no longer describe everything it needs, callers can't tell what ambient state is required, and tests have to set up CurrentAttributes instead of just passing a value.
# ❌ bad — the dependency on the current company is invisible in the contract
class ChargeCard
include Axn
def call = do_thing(Current.company)
end
# ✅ good — declared, validated, sensitive-filtered, and trivially testable
class ChargeCard
include Axn
expects :company, on: :ambient_context
def call = do_thing(company)
endIt fires only on reads inside a class/module that include Axn (the on: :ambient_context fix exists nowhere else, so a Current read in a controller, model, or plain job is left alone). Assignments (Current.company = c), calls with arguments (Current.foo(bar)), and unrelated receivers (Time.current) are ignored, as are the CurrentAttributes lifecycle methods (reset, instance, …).
Axn ships no default config that enables it, so turn it on explicitly:
require:
- axn/rubocop
Axn/AmbientContextBypass:
Enabled: true
Severity: warning