Skip to content

Repository files navigation

IpAttribute

ActiveRecord IP address attributes stored as integers. Auto-converts columns between human-readable IP strings and integer storage. Supports IPv4 and IPv6 per RFC 791 and RFC 4291.

Installation

gem"ip_attribute"

Storage Strategies

The gem auto-detects which strategy to use based on column naming.

Strategy A: dual column (recommended for new projects)

_ipv4 (bigint) + _ipv6 (decimal 39,0). Unambiguous family. Native integer indexing for IPv4. ::ffff:x.x.x.x normalizes to IPv4 automatically.

classSession < ActiveRecord::Base# has client_ipv4 (bigint) + client_ipv6 (decimal) columnsincludeIpAttribute::ActiveRecordIntegrationendsession.client_ip="::1"session.client_ip.to_s# => "::1" (correct — stored in _ipv6)session.client_ip.ipv6?# => true

Strategy B: single column + optional family

_ip (decimal 39,0) with optional _ip_family (smallint). If the family column exists, perfect round-trip. If missing, falls back to range inference (v0.1.0 compatible).

classUser < ActiveRecord::Base# has login_ip (decimal) + login_ip_family (smallint, optional)includeIpAttribute::ActiveRecordIntegrationenduser.login_ip="127.0.0.1"user.login_ip.to_s# => "127.0.0.1"user.login_ip_display# => "127.0.0.1" (string shortcut)

Without _ip_family, integer 1 maps to IPv4 0.0.0.1 (not IPv6 ::1). Add the family column for disambiguation.

Subnet Queries

User.where_ip(:login_ip,"192.168.0.0/24")Session.where_ip(:client_ipv4,"10.0.0.0/8")

ActiveModel Type (no database required)

classRequestincludeActiveModel::ModelincludeActiveModel::Attributesattribute:client_ip,IpAttribute::Type.newendreq=Request.new(client_ip: "10.0.0.1")req.client_ip# => #<IPAddr: IPv4:10.0.0.1/...>

With Rails, :ip_address is auto-registered via Railtie:

attribute:login_ip,:ip_address

Standalone Converter

IpAttribute::Converter.to_integer("192.168.0.1")# => 3232235521IpAttribute::Converter.to_ipaddr(2130706433)# => #<IPAddr: IPv4:127.0.0.1/...>

IPv4-mapped normalization

IpAttribute::Converter.to_integer("::ffff:10.0.0.1",normalize_mapped: true)# => 167772161 (IPv4, not IPv6)

Opt-in Refinements

require"ip_attribute/core_ext"usingIpAttribute::CoreExt"192.168.0.1".to_ip# => 3232235521

Lexically scoped — no global monkey-patching.

Migration Generators

# Dual column (recommended) + indexes
rails generate ip_attribute:column sessions client --dual
# Single column + indexes
rails generate ip_attribute:column users login_ip
# Single column + family (perfect round-trip) + indexes
rails generate ip_attribute:column users login_ip --family
# Add indexes to existing columns (production-safe)
rails generate ip_attribute:index users login_ip
rails generate ip_attribute:index sessions client --dual

All migrations use disable_ddl_transaction! and detect PostgreSQL at runtime for algorithm: :concurrently.

Generated Indexes

All generators create partial indexes (WHERE col IS NOT NULL) for space efficiency on nullable columns. The --family flag adds per-family conditional indexes for IPv4/IPv6 filtering.

For time-scoped queries (rate limiting, audit logs), add a composite index manually:

# Web server: "requests from this IP in the last hour"add_index:requests,[:client_ip,:created_at],order: {created_at: :desc}# Dual column variantadd_index:requests,[:client_ipv4,:created_at],where: "client_ipv4 IS NOT NULL",order: {created_at: :desc}

Column Types

No SQL database has a native 128-bit integer. bigint is 64 bits (max 2^63-1) — sufficient for IPv4 but not IPv6. decimal(39,0) is the only portable type for the full range.

StrategyColumnTypeRange
Dualpre_ipv4bigint0 – 2^32-1
Dualpre_ipv6decimal(39,0)0 – 2^128-1
Singlecol_ipdecimal(39,0)0 – 2^128-1 (both)
Singlecol_familytinyint4 or 6

Family Enum (RFC protocol versions)

ValueMeaningRFC
4IPv4791
6IPv64291
NULLUnknown

Uses IP protocol version numbers (4/6), not OS-specific Socket::AF_* constants (which vary: AF_INET6 is 10 on Linux, 30 on macOS, 23 on Windows).

The family column is optional in Strategy B. When absent, family is inferred from the integer range (lossy for the [0, 2^32-1] overlap). When present, perfect round-trip for all addresses including ::1 vs 0.0.0.1.

Requirements

  • Ruby >= 3.4.0
  • ActiveRecord >= 7.2 (for AR integration)

Tested: Ruby 3.4 + 4.0 × ActiveRecord 7.2 + 8.1.

See doc/rfc-ip-addressing.md for the full RFC reference.

License

MIT — see LICENSE.

About

Automagically store attributes ending with _ip as an integer in the DB

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages