Skip to the content.

Advanced Features

Model Mapping

Map query results to Ruby objects instead of hashes.

Define a Model

class User
  attr_accessor :id, :name, :email, :age
  
  def initialize(attributes = {})
    attributes.each { |key, value| 
      send("#{key}=", value) if respond_to?("#{key}=") 
    }
  end
end

Use with Queries

db = QueryKit.connection

# get() with model
query = db.query('users').where('age', '>', 18)
users = db.get(query, User)  # Returns Array of User objects

# first() with model
user = db.first(query, User)  # Returns User or nil

# raw() with model
users = db.raw('SELECT * FROM users WHERE age > ?', 18, model_class: User)

Benefits

Repository Pattern

Clean data access layer with reusable queries.

Basic Repository

class User
  attr_accessor :id, :name, :email, :age
  
  def initialize(attributes = {})
    attributes.each { |key, value| 
      send("#{key}=", value) if respond_to?("#{key}=") 
    }
  end
end

class UserRepository < QueryKit::Repository
  table 'users'
  model User
end

# Use with global config
QueryKit.setup(:sqlite, database: 'app.db')
repo = UserRepository.new

# Or with explicit connection
db = QueryKit.connect(:sqlite, 'app.db')
repo = UserRepository.new(db)

Built-in Methods

# Find by ID
user = repo.find(1)                    # Returns User or nil

# Find by column
user = repo.find_by('email', 'alice@example.com')

# Get all
users = repo.all                       # Returns Array of Users

# WHERE queries
users = repo.where('age', '>', 18)
users = repo.where('age', 'BETWEEN', [20, 30])

# WHERE IN / NOT IN
users = repo.where_in('id', [1, 2, 3])
users = repo.where_not_in('status', ['banned', 'deleted'])

# Utilities
user = repo.first                      # First record
count = repo.count                     # Total count
exists = repo.exists?(1)               # Check if ID exists

# CRUD
id = repo.create(name: 'Alice', email: 'alice@test.com', age: 28)
id = repo.insert(...)                  # Alias for create

affected = repo.update(1, name: 'Alice Updated', age: 29)
affected = repo.delete(1)
affected = repo.destroy(1)             # Alias for delete

# Bulk delete
affected = repo.delete_where(status: 'inactive')

Custom Repository Methods

class UserRepository < QueryKit::Repository
  table 'users'
  model User
  
  def find_by_email(email)
    find_by('email', email)
  end
  
  def adults
    where('age', '>=', 18)
  end
  
  def active_users_in_country(country)
    execute(
      db.query(table_name)
        .where('status', 'active')
        .where('country', country)
        .order_by('name')
    )
  end
end

# Use custom methods
repo = UserRepository.new
adults = repo.adults
user = repo.find_by_email('alice@test.com')

Transactions

repo = UserRepository.new

repo.transaction do
  repo.create(name: 'Bob', email: 'bob@test.com')
  repo.update(1, status: 'active')
  # Commits on success, rolls back on error
end

Transactions

All adapters support transactions:

db = QueryKit.connection

db.transaction do
  # Insert
  user_id = db.execute_insert(
    db.insert('users').values(name: 'Alice', email: 'alice@test.com')
  )
  
  # Update
  db.execute_update(
    db.update('profiles').set(user_id: user_id).where('id', 1)
  )
  
  # Automatically commits if no errors
  # Automatically rolls back on exception
end

Architecture

QueryKit uses clean separation of concerns:

All query builders are lazy - they only build SQL strings. Execute with:

What QueryKit Does NOT Do

Intentionally excluded features:

This is by design. QueryKit is a query builder, not a full ORM.