This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
minfraud-api-ruby is MaxMind's official Ruby client library for the minFraud web services:
- minFraud Score, Insights, and Factors: Fraud detection and risk scoring endpoints
- minFraud Report Transaction API: Report fraudulent or legitimate transactions
The library provides a request builder pattern through components that are assembled into assessments, then sent to the service endpoints. Responses are returned as strongly-typed model objects.
Key Technologies:
- Ruby 3.2+ (uses frozen string literals and modern Ruby features)
- HTTP gem for web service client functionality and persistent connections
- ConnectionPool for thread-safe connection management
- RSpec for testing
- RuboCop with multiple plugins for code quality
lib/minfraud/
├── components/ # Request components (Account, Device, Email, etc.)
│ └── report/ # Report Transaction components
├── model/ # Response models (Score, Insights, Factors, etc.)
├── http_service/ # HTTP response wrapper
├── assessments.rb # Main request builder for Score/Insights/Factors
├── report.rb # Report Transaction API
├── resolver.rb # Maps component parameters to component objects
├── enum.rb # Enum helper for validated attributes
├── validates.rb # Input validation methods
├── errors.rb # Custom exceptions
└── version.rb # Version constant
Requests are built using component objects that represent different aspects of a transaction:
assessment = Minfraud::Assessments.new(
device: { ip_address: '1.2.3.4' },
event: { type: :purchase },
billing: { country: 'US' },
)Component Architecture:
- All components extend
Minfraud::Components::Base - Components use
attr_accessorfor attributes - Components implement
#to_jsonto convert to API request format - The
Resolvermodule maps hash parameters to component objects - Components with enum attributes include
Minfraud::Enummodule
Resolver Pattern:
The Minfraud::Resolver module handles converting hashes to component objects:
- Maintains a
MAPPINGconstant from keys to component classes - The
#assignmethod creates components from hashes or uses existing component objects - Raises
RequestFormatErrorfor unknown keys - Used by
Minfraud::Assessmentsin initialization
Components with restricted value sets use the Enum module:
class Event < Base
include ::Minfraud::Enum
enum_accessor :type, %i[
account_creation
purchase
payout
recurring_purchase
referral
account_login
]
endKey Points:
enum_accessorcreates getter/setter methods with validation- Values are stored as symbols internally
- Raises
NotEnumValueErrorif invalid value assigned - The class gets a
{attribute}_valuesmethod returning permitted values
Components can optionally include Minfraud::Validates for input validation:
class Device < Base
include ::Minfraud::Validates
def ip_address=(ip_address)
validate_ip('ip_address', ip_address) if Minfraud.enable_validation
@ip_address = ip_address
end
endKey Points:
- Validation is disabled by default
- Enable with
Minfraud.configure { |c| c.enable_validation = true } - Validation happens in setters before assignment
- Validates types, formats, lengths, ranges, etc.
- Raises
InvalidInputErrorfor invalid values
Response models follow a clear hierarchy:
Abstract → Score → Insights → Factors
Abstractprovides#getmethod for safe hash accessScorehas basic risk scoring fieldsInsightsextendsScorewith detailed fraud data (addresses, phones, device, etc.)FactorsextendsInsightswith subscores and risk reasons
Model Composition: Models compose smaller model objects representing nested data:
class Insights < Score
attr_reader :billing_address
attr_reader :credit_card
attr_reader :device
attr_reader :email
def initialize(record, locales)
super
@billing_address = Minfraud::Model::BillingAddress.new(get('billing_address'))
@credit_card = Minfraud::Model::CreditCard.new(get('credit_card'))
# ...
end
endThe library uses ConnectionPool for thread-safe persistent HTTP connections:
@connection_pool = ConnectionPool.new(size: 5) do
HTTP.basic_auth(user: @account_id, pass: @license_key)
.persistent("https://#{host}")
endKey Points:
- Pool is created in
Minfraud.configure - Assessments and Reports use
Minfraud.connection_pool.with { |client| ... } - Enables persistent connections without manual management
- Safe for multi-threaded use
- Must call
Minfraud.configurebefore using from multiple threads
# Install dependencies
bundle install
# Run all tests
bundle exec rake spec
# Run tests and RuboCop
bundle exec rake # default task
# Run RuboCop only
bundle exec rake rubocop
# Run specific test file
bundle exec rspec spec/assessments_spec.rb
# Run specific test
bundle exec rspec spec/assessments_spec.rb:10Tests use RSpec and are organized by functionality:
spec/assessments_spec.rb- Main assessment request builder testsspec/report_spec.rb- Report Transaction API testsspec/components/*_spec.rb- Component testsspec/model/*_spec.rb- Response model testsspec/enum_spec.rb- Enum validation testsspec/http_spec.rb- HTTP integration tests
Tests use RSpec with test data hashes:
describe Minfraud::Model::Score do
let(:raw) do
{
'disposition' => { 'action' => 'accept' },
'funds_remaining' => 10.01,
'id' => '27d26476-e2bc-11e4-92b8-962e705b4af5',
'risk_score' => 0.01,
}
end
let(:model) { described_class.new(raw, ['en']) }
it 'has risk_score' do
expect(model.risk_score).to eq(0.01)
end
endWhen adding new fields:
- Add field to test data hash
- Add expectation to verify the field is accessible
- Test nil handling if field is optional
- Test validation if applicable
For input components (request data):
-
Add an attr_accessor:
# A description of the field. # # @return [String, nil] attr_accessor :new_field
-
Add validation if needed:
def new_field=(value) validate_string('new_field', 255, value) if Minfraud.enable_validation @new_field = value end
-
Update tests to include the new field
For output models (response data):
-
Add an attr_reader (models are immutable):
# A description of the field. # # @return [Type, nil] attr_reader :new_field
-
Initialize in constructor:
def initialize(record, locales) super @new_field = get('new_field') end
-
For nested objects, create a model class:
@new_field = Minfraud::Model::NewField.new(get('new_field'))
-
Update tests with test data and expectations
When adding new values to an enum attribute:
-
Add to the enum_accessor array:
enum_accessor :type, %i[ existing_value new_value ]
-
Update CHANGELOG.md with the new value
-
Add test to verify the new value is accepted
When creating a new component class:
-
Extend Base and include Enum/Validates if needed:
class NewComponent < Base include ::Minfraud::Enum include ::Minfraud::Validates end
-
Add to Resolver::MAPPING in lib/minfraud/resolver.rb:
MAPPING = { new_component: ::Minfraud::Components::NewComponent, # ... }.freeze
-
Add attr_accessor to Assessments or Report
-
Require the file in lib/minfraud.rb
-
Add tests for the component
Always update CHANGELOG.md for user-facing changes.
Important: Do not add a date to changelog entries until release time.
- If there's an existing version entry without a date (e.g.,
v2.9.0), add your changes there - If creating a new version entry, don't include a date - it will be added at release time
- Use past tense for descriptions
- Use bullet points starting with
*
## v2.10.0
* Added the `new_field` attribute to `Minfraud::Components::Device`. This
allows you to provide...
* Added the `/output/path` to the Insights response. This is available
as the `field_name` attribute on `Minfraud::Model::Insights`.A new component doesn't work when passed to Assessments.
Solution: Make sure you:
- Added the component to
Resolver::MAPPING - Required the file in lib/minfraud.rb
- Added the attr_accessor to Assessments or Report
Getting connection errors or state issues in multi-threaded code.
Solution:
- Always call
Minfraud.configurebefore spawning threads - Never share
Minfraud::AssessmentsorMinfraud::Reportobjects across threads - Create new Assessment/Report instances per thread
Validation doesn't catch invalid inputs.
Solution:
- Validation is disabled by default
- Enable with
Minfraud.configure { |c| c.enable_validation = true } - Make sure the component setter calls the appropriate
validate_*method
A valid enum value raises NotEnumValueError.
Solution:
- Enum values must be symbols (
:valuenot"value") - Check the enum_accessor definition includes the value
- Values are case-sensitive
- RuboCop enforced with plugins: performance, rake, rspec, thread_safety
- Frozen string literals (
# frozen_string_literal: true) in all files - Target Ruby 3.2+
- Max line length: 150 characters
- Hash alignment: table style (aligned rockets and colons)
- Trailing commas allowed in arrays, hashes, and arguments
- Use
if !conditioninstead ofunless condition(NegatedIf disabled) - Metrics cops disabled - AbcSize, ClassLength, MethodLength, etc.
Key RuboCop configurations:
- Guard clauses not enforced
- Conditional assignment not enforced
- Format string token checks disabled
- Multiple assertions allowed in RSpec tests
- Extra spacing with force equal sign alignment enabled
bundle install# Run tests and linting
bundle exec rake
# Or run separately
bundle exec rake spec
bundle exec rake rubocopbundle exec rspec spec/assessments_spec.rbMinfraud.configure do |c|
c.account_id = 12345
c.license_key = 'your_license_key'
c.host = 'sandbox.maxmind.com'
end- Ruby 3.2+ required
- Target compatibility should match current supported Ruby versions
The library supports three assessment endpoints:
assessment.score- Basic risk score (Score model)assessment.insights- Score + detailed fraud data (Insights model)assessment.factors- Insights + subscores + risk reasons (Factors model)
Report Transaction API:
reporter.report_transaction- Report transaction outcomes