3.1.5 - 2025-10-13
- TAG: v3.1.5
- COVERAGE: 93.58% -- 437/467 lines in 14 files
- BRANCH COVERAGE: 81.00% -- 81/100 branches in 14 files
- 92.39% documented
Added
- Adapter support for Hanami and ROM
- Complete YARD documentation
- kettle-dev for easier maintenance & dev tooling
The one where omniauth-identity gains a rom
adapter. Since rom
is the default DB adapter for Hanami, this also makes it hanami-ready.
class Identity
include OmniAuth::Identity::Models::Rom
# Configure the ROM container and relation using the DSL (no `self.`):
rom_container -> { MyDatabase.rom } # accepts a proc or a container object
rom_relation_name :identities # optional, defaults to :identities
owner_relation_name :owners # optional, for loading associated owner
# Uses OmniAuth::Identity::Model.auth_key to set the auth key (defaults to :email)
auth_key :email
# Optional: override the password digest field name (defaults to :password_digest)
password_field :password_digest
end
Find a complete example after the funding info.
Support & Funding Info
I am a full-time FLOSS maintainer. If you find my work valuable I ask that you become a sponsor. Every dollar helps!
As an example I'll use the code straight from the specs.
Test Harness
ROM_DB = if RUBY_ENGINE == "jruby"
require "jdbc/sqlite3"
require "sequel"
Sequel.connect("jdbc:sqlite::memory:rom")
else
require "sqlite3"
require "sequel"
Sequel.connect("sqlite::memory:rom")
end
require "rom"
require "rom-sql"
# Define the ROM relations
class RomTestIdentities < ROM::Relation[:sql]
schema(:rom_test_identities) do
attribute :id, ROM::SQL::Types::Serial
attribute :email, ROM::SQL::Types::String
attribute :login, ROM::SQL::Types::String
attribute :password_digest, ROM::SQL::Types::String
attribute :pwd_hash, ROM::SQL::Types::String
attribute :owner_id, ROM::SQL::Types::Integer
end
end
class RomTestOwners < ROM::Relation[:sql]
schema(:rom_test_owners) do
attribute :id, ROM::SQL::Types::Serial
attribute :name, ROM::SQL::Types::String
end
end
# Set up ROM container
ROM_CONFIG = ROM::Configuration.new(:sql, ROM_DB)
ROM_CONFIG.register_relation(RomTestIdentities)
ROM_CONFIG.register_relation(RomTestOwners)
ROM_CONTAINER = ROM.container(ROM_CONFIG)
class RomTestIdentity
include OmniAuth::Identity::Models::Rom
# Configure via the new DSL (no `self.`): accepts a value or a proc
rom_container -> { ROM_CONTAINER }
rom_relation_name :rom_test_identities
# Use the standard Model.auth_key API to configure the auth key
auth_key :email
password_field :password_digest
# Provide a simple reader for the :login attribute for specs that use a
# non-standard auth key (e.g. `auth_key :login`). The ROM adapter stores the
# underlying tuple in @identity_data, so delegate to it here.
def login
@identity_data && @identity_data[:login]
end
end
Test
RSpec.describe(OmniAuth::Identity::Models::Rom, :sqlite3) do
before(:all) do
# Create the tables
ROM_DB.create_table(:rom_test_identities) do
primary_key :id
String :email, null: false
String :password_digest, null: false
# Add the login column to support tests that use a non-standard auth_key
String :login
Integer :owner_id
end
ROM_DB.create_table(:rom_test_owners) do
primary_key :id
String :name, null: false
end
end
before do
# Clear the tables before each test
ROM_DB[:rom_test_identities].delete
ROM_DB[:rom_test_owners].delete
end
describe "model", type: :model do
subject(:model_klass) { RomTestIdentity }
describe "::authenticate" do
it "authenticates with correct password" do
# Insert test data
password_digest = BCrypt::Password.create("password")
identity_data = {email: "test@example.com", password_digest: password_digest}
ROM_CONTAINER.relations[:rom_test_identities].insert(identity_data)
authenticated = model_klass.authenticate({email: "test@example.com"}, "password")
expect(authenticated).to(be_a(RomTestIdentity))
expect(authenticated.email).to(eq("test@example.com"))
end
it "authenticates with custom auth_key (login) when auth_key is set to :login" do
original = model_klass.auth_key
model_klass.auth_key(:login)
begin
# Insert test data using login field
password_digest = BCrypt::Password.create("password")
identity_data = {login: "bob", email: "bob@example.com", password_digest: password_digest}
ROM_CONTAINER.relations[:rom_test_identities].insert(identity_data)
authenticated = model_klass.authenticate({login: "bob"}, "password")
expect(authenticated).to(be_a(RomTestIdentity))
expect(authenticated.login).to(eq("bob"))
ensure
model_klass.auth_key(original)
end
end
it "returns false with incorrect password" do
# Insert test data
password_digest = BCrypt::Password.create("password")
identity_data = {email: "test@example.com", password_digest: password_digest}
ROM_CONTAINER.relations[:rom_test_identities].insert(identity_data)
authenticated = model_klass.authenticate({email: "test@example.com"}, "wrong")
expect(authenticated).to(be(false))
end
it "returns false for non-existent user" do
authenticated = model_klass.authenticate({email: "nonexistent@example.com"}, "password")
expect(authenticated).to(be(false))
end
end
end
end
Photo by Ryunosuke Kikuno on Unsplash
Top comments (0)