ActionScope
ActionScope provides a comprehensive set of dynamic scopes for ActiveRecord models, including basic column filtering, text matching, range queries, association scopes, multi-column search, and sorting capabilities. Automatically generates scopes based on model attributes and associations.
Features
-
Basic Scopes: Automatic
by_column_name
scopes for all model attributes - Text Matching: Fuzzy search scopes for string/text columns
- Range Queries: Greater than, less than, and between scopes for numeric/date columns
- Association Scopes: Automatic scopes for model associations
- Multi-Column Search: Search across multiple columns with a single query
- Sorting: Dynamic sorting scopes for all columns
- Type-Aware: Automatically detects column types and generates appropriate scopes
- Rails 7+ Compatible: Works with modern Rails versions
Installation
Add this line to your application's Gemfile:
gem 'action_scope'
And then execute:
bundle install
Or install it yourself as:
gem install action_scope
Usage
Basic Setup
Include ActionScope in your ActiveRecord models and call action_scope
:
class User < ApplicationRecord
include ActionScope
action_scope
# Your model code here...
end
Example Model
Let's say you have a User model with the following structure:
# db/migrate/xxx_create_users.rb
class CreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.text :bio
t.integer :age
t.decimal :salary, precision: 10, scale: 2
t.datetime :last_login_at
t.date :birth_date
t.boolean :active
t.references :company, null: false, foreign_key: true
t.timestamps
end
end
end
# app/models/user.rb
class User < ApplicationRecord
include ActionScope
belongs_to :company
has_many :posts
action_scope
end
# app/models/company.rb
class Company < ApplicationRecord
include ActionScope
has_many :users
action_scope
end
Generated Scopes
ActionScope automatically generates the following types of scopes:
1. Basic Scopes (by_column_name
)
For every column in your model:
# Filter by exact values
User.by_name("John Doe")
User.by_email("john@example.com")
User.by_age(30)
User.by_active(true)
# Chain multiple filters
User.by_name("John").by_active(true).by_age(30)
2. Text Matching Scopes (by_column_name_match
)
For string and text columns:
# Fuzzy search (uses LIKE %query%)
User.by_name_match("john") # Finds "John Doe", "Johnny", etc.
User.by_email_match("gmail") # Finds emails containing "gmail"
User.by_bio_match("developer") # Searches bio text
3. Range Scopes
For numeric, date, and datetime columns:
# Greater than / Less than
User.by_age_gt(25) # age > 25
User.by_age_gte(25) # age >= 25
User.by_age_lt(65) # age < 65
User.by_age_lte(65) # age <= 65
# Date ranges
User.by_birth_date_gte(Date.new(1990, 1, 1))
User.by_last_login_at_lt(1.week.ago)
# Salary ranges
User.by_salary_gte(50000)
User.by_salary_lte(100000)
# Chain range conditions
User.by_age_gte(25).by_age_lte(65) # Between 25 and 65
4. Association Scopes
For belongs_to and has_many associations:
# Filter by association ID
User.by_company(1) # Users belonging to company with ID 1
User.by_company(company) # Users belonging to company object
# Works with company model too
Company.by_user(user) # Companies associated with user
5. Multi-Column Search
Search across multiple columns simultaneously:
# Searches name, email, bio, and id columns
User.by_search_query("john")
# Finds users where any searchable column contains "john"
User.by_search_query("developer") # Matches name, email, or bio
6. Sorting Scopes
For all columns:
# Sort ascending (default)
User.sort_by_name
User.sort_by_age
User.sort_by_created_at
# Sort descending
User.sort_by_name('desc')
User.sort_by_salary('desc')
# Generic sorting method
User.sorted_by(:name, 'asc')
User.sorted_by(:age, 'desc')
Advanced Usage
Selective Scope Generation
Generate scopes only for specific columns:
class User < ApplicationRecord
include ActionScope
# Only generate scopes for name and email
action_scope :name, :email
end
Excluding Columns
Exclude certain columns from scope generation:
class User < ApplicationRecord
include ActionScope
# Generate scopes for all columns except password_digest
action_scope except: [:password_digest, :secret_token]
end
Custom Configuration
You can also provide a block for custom configuration:
class User < ApplicationRecord
include ActionScope
action_scope do |model|
# Custom logic here
puts "Generating scopes for #{model.name}"
end
end
Real-World Examples
Building Complex Queries
# Find active adult users from a specific company with recent activity
User.by_active(true)
.by_age_gte(18)
.by_company(company)
.by_last_login_at_gte(1.month.ago)
.sort_by_name
# Search for users and sort by salary
User.by_search_query("engineer")
.by_salary_gte(70000)
.sort_by_salary('desc')
# Filter by date ranges
User.by_birth_date_gte(Date.new(1980, 1, 1))
.by_birth_date_lte(Date.new(2000, 12, 31))
.by_active(true)
Scope Introspection
ActionScope provides methods to inspect generated scopes:
action_scope_options
- Complete Scope Overview
The action_scope_options
method (aliased as options_for_search
) returns a comprehensive hash of all available scopes with their types and options:
User.action_scope_options
# Returns a hash like this:
{
# Basic scopes with column types
:by_name => nil,
:by_email => nil,
:by_bio => nil,
:by_age => nil,
:by_salary => nil,
:by_last_login_at => nil,
:by_birth_date => nil,
:by_active => nil,
:by_company_id => nil,
# Text matching scopes for string/text columns
:by_name_match => nil,
:by_email_match => nil,
:by_bio_match => nil,
# Multi-column search with searchable columns
:by_search_query => [
"id", "name", "email", "bio", "age", "salary",
"last_login_at", "birth_date", "active", "company_id"
],
# Range scopes for numeric/date columns
:by_age_gte => :integer,
:by_age_lte => :integer,
:by_age_gt => :integer,
:by_age_lt => :integer,
:by_salary_gte => :decimal,
:by_salary_lte => :decimal,
:by_salary_gt => :decimal,
:by_salary_lt => :decimal,
:by_last_login_at_gte => :datetime,
:by_last_login_at_lte => :datetime,
:by_last_login_at_gt => :datetime,
:by_last_login_at_lt => :datetime,
:by_birth_date_gte => :date,
:by_birth_date_lte => :date,
:by_birth_date_gt => :date,
:by_birth_date_lt => :date,
# Association scopes
:by_company => "id",
# Sorting scopes
:sort_by_name => ["asc", "desc"],
:sort_by_email => ["asc", "desc"],
:sort_by_age => ["asc", "desc"],
:sort_by_salary => ["asc", "desc"],
:sort_by_created_at => ["asc", "desc"]
}
Individual Introspection Methods
You can also access specific scope types individually:
# Basic scopes with their column types
User.basic_scopes_with_types
# Text matching scopes
User.matchable_scopes_with_types
# Range scopes with their column types
User.rangeable_scopes_scopes_with_types
# Sorting scopes with available directions
User.sortable_scopes_with_directions
# Association scopes with their primary keys
User.association_scopes_with_primary_keys
# Multi-search scope with searchable column names
User.multi_searchable_scopes_with_column_names
This introspection is particularly useful for building dynamic UIs, API documentation, or debugging scope availability.