/action_state

Rails scopes and predicates defined with the same DSL.

Primary LanguageRubyMIT LicenseMIT

action_state

ActionState provides a simple DSL for defining Rails model states allowing you to query the state as an ActiveRecord scope on the class and a predicate on the instance.

For example, the following state definition defines a class scope Article.published and an instance predicate article.published?.

class Article < ApplicationRecord
  state(:published) { where(published_at: ..Time.current) }
  ...
end

✨ Contributing ✨

If you’re interested in contributing to this project, you can book a pairing session with me and we can work through it together and I can hopefully share some context with you. Otherwise, please feel free top open a PR / issue / description. ❤️

Usage

ActionState supports a small subset of ActiveRecord queries for the predicate definition, and delegates the scope definition to ActiveRecord.

It's not meant to comprehensively support every possible ActiveRecord query; rather it supports a few features that tend to lend themselves well to predicate definitions.

where

The where method checks for inclusion in an Enumerable, coverage by a Range, and equality with other types of value.

Inclusion in an Enumerable

state(:crafter) { where(role: ["designer", "developer"]) }

Covered by a Range

state(:negative) { where(stars: 1..4 }
state(:indifferent) { where(stars: 5..6) }
state(:positive) { where(stars: 7..9) }

state(:recently_published) { where(published_at: 1.week.ago..) }

Equality

state(:featured) { where(featured: true) }

where.not

The counterpart to where is where.not which checks for exclusion from an Enumerable or Range, and inequality with other types of value.

state(:deleted) { where.not(deleted_at: nil) }

excluding

The excluding method excludes specific instances of a model.

state(:normal) { excluding(special_post) }

Passing arguments

States can also be defined to accept arguments.

state(:before) { |whenever| where(created_at: ..whenever) }
state(:after) { |whenever| where(created_at: whenever..) }

Composing states

You can chain query methods together to form more complex queries.

state(:can_edit) { where(role: "admin").where.not(disabled: true) }

You can also compose multiple states together.

state(:published) { where(published: true) }
state(:featured) { published.where(featured: true) }

Installation

Add this line to your application's Gemfile:

gem "action_state"

And then execute:

$ bundle

Or install it yourself as:

$ gem install action_state

Finally, include ActionState in your model class or ApplicationRecord:

class ApplicationRecord < ActiveRecord::Base
  include ActionState
  ...
end

License

The gem is available as open source under the terms of the MIT License.