Your First Rule
Let's build a simple permission system to understand how Ruleur works.
The Scenario
We're building a document management system where:
- Admins can do anything
- Regular users can only update their own draft documents
Step 1: Define Domain Objects
First, let's create simple test objects:
ruby
require 'ruleur'
class Document
attr_reader :author_id, :status
def initialize(author_id:, status:)
@author_id = author_id
@status = status
end
def draft?
status == 'draft'
end
end
class User
attr_reader :id, :role
def initialize(id:, role:)
@id = id
@role = role
end
def admin?
role == 'admin'
end
endStep 2: Create Rules
You define rules that set values when conditions are met:
ruby
engine = Ruleur.define do
rule 'admin_update' do
conditions do
any?(user(:admin?))
end
actions do
set :update, true
end
end
rule 'author_draft_update' do
conditions do
all?(
record(:draft?),
eq?(record_value(:author_id), user_value(:id))
)
end
actions do
set :update, true
end
end
endDSL Helpers Explained
user(:admin?)- checks ifuser.admin?returns truthyrecord(:draft?)- checks ifrecord.draft?returns truthyset :update, true- grants update permissionsalience- priority (higher fires first)
Step 3: Run the Engine
ruby
admin = User.new(id: 1, role: 'admin')
doc = Document.new(author_id: 2, status: 'published')
ctx = engine.run(user: admin, record: doc)
ctx[:update] # => true (admin_update fired)
author = User.new(id: 2, role: 'user')
draft = Document.new(author_id: 2, status: 'draft')
ctx = engine.run(user: author, record: draft)
ctx[:update] # => true (author_draft_update fired)
other_user = User.new(id: 3, role: 'user')
ctx = engine.run(user: other_user, record: draft)
ctx[:update] # => nil (no rule matched)Step 4: Add More Complex Logic
ruby
engine = Ruleur.define do
rule 'admin_update' do
match { any?(user(:admin?)) }
actions { set :update, true }
end
rule 'author_draft_update' do
conditions do
all?(
record(:draft?),
eq?(record_value(:author_id), user_value(:id))
)
end
actions do
set :update, true
end
end
rule 'published_requires_admin' do
conditions do
all?(
not?(record(:draft?)),
user(:admin?)
)
end
actions do
set :update, true
end
end
endComplete Example
ruby
require 'ruleur'
class Document
attr_reader :author_id, :status
def initialize(author_id:, status:)
@author_id = author_id
@status = status
end
def draft? = status == 'draft'
end
class User
attr_reader :id, :role
def initialize(id:, role:)
@id = id
@role = role
end
def admin? = role == 'admin'
end
engine = Ruleur.define do
rule 'admin_update' do
conditions do
any?(user(:admin?))
end
actions do
set :update, true
end
end
rule 'author_draft_update' do
conditions do
all?(
record(:draft?),
eq?(record_value(:author_id), user_value(:id))
)
end
actions do
set :update, true
end
end
end
admin = User.new(id: 1, role: 'admin')
author = User.new(id: 2, role: 'user')
doc = Document.new(author_id: 2, status: 'draft')
puts engine.run(user: admin, record: doc)[:update] # => true
puts engine.run(user: author, record: doc)[:update] # => true
puts engine.run(user: User.new(id: 3, role: 'user'), record: doc)[:update] # => nilUnderstanding Rule Execution
When you run engine.run():
- Context is created with the provided facts (user, record)
- Eligible rules are identified - rules whose conditions match
- Rules fire in priority order (salience: highest first)
- Actions set context values - only explicit
setcalls modify the context
Debugging with Trace
Enable tracing to see which rules fire:
ruby
engine = Ruleur::Engine.new(rules: engine.rules, trace: true)
ctx = engine.run(user: admin, record: doc)
# Output:
# [Ruleur] Firing: admin_update (salience=10)
# [Ruleur] Facts changed: updateWhat's Next?
Now you understand the basics! Explore:
- Guide: DSL Basics - Learn all DSL helpers and operators
- Guide: YAML Rules - Store rules in YAML files
- Guide: Validation - Validate rules before execution
- Examples: Permission Rules - More complex permission scenarios
Try It Yourself
Experiment with:
- Adding more rules (e.g., editors can update)
- Using different operators (
gt,lt,includes) - Chaining conditions with
allandany - Creating rules that depend on other rule outcomes