DSL
The Ruleur Domain-Specific Language (DSL) provides a Ruby-friendly way to define rules with readable, composable syntax.
Overview
The DSL consists of:
- Engine Definition:
Ruleur.defineblock - Rule Definition:
rule "name"block- Conditions: use
matchwithall?(),any?(),not?()builders
- Conditions: use
- Actions:
actionblock with helper methods
Engine Definition
Ruleur.define
Creates an engine with rules defined in the block.
engine = Ruleur.define do
rule 'first_rule' do
# ...
end
rule 'second_rule' do
# ...
end
endReturns:
Ruleur::Engine- Configured engine instance
Rule Definition
rule(name, options = {}, &block)
Defines a rule within the engine.
rule 'example', salience: 10, tags: [:important], no_loop: true do
conditions do
all?(conditions)
end
actions do
# action code here
end
endParameters:
name(String) - Unique rule identifieroptions(Hash) - Optional configurationsalience(Integer) - Priority (default: 0)tags(Array<Symbol>) - Tags for categorizationno_loop(Boolean) - Prevent recordursive firing
Condition Methods
Composite Conditions
all?(*conditions)
All conditions must be true (AND). Wrap them in a match block in rules:
conditions do
all?(
user(:admin?),
record(:published?),
not?(record(:archived?))
)
endany?(*conditions)
At least one condition must be true (OR). Wrap them in a match block in rules:
conditions do
any?(
user(:admin?),
user(:owner?, record),
flag(:force_access)
)
endall?(*conditions)
Same as above but can be nested within any.
conditions do
any?(
user(:admin?),
all?(
user(:moderator?),
record(:flagged?)
)
)
endany?(*conditions)
Same as above but can be nested within all.
conditions do
all?(
any?(user(:admin?), user(:moderator?)),
record(:published?)
)
endnot?(condition)
Negates a condition inside a match block.
conditions do
all?(
user(:active?),
not?(user(:banned?))
)
endReference Methods
obj(key, *methods)
References a fact from context and optionally calls methods.
obj(:user, :admin?)
obj(:order, :total)
obj(:settings, :feature, :enabled?)Shortcut Methods
Convenience methods for common references:
# user() - shorthand for obj(:user)
user(:admin?)
# record() - shorthand for obj(:record)
record(:published?)
# doc() - shorthand for obj(:document)
doc(:approved?)flag(key)
References a boolean flag from context.
flag(:approval_required)
flag(:email_sent)literal(value)
Creates a literal value reference (use literal(...) instead of lit):
user(:role).eq?(literal('admin'))
order(:total).gt?(literal(100))Action Methods
Setting Values
set(key, value)
Sets a value in the context.
actions do
set :discount, 0.15
set :approved, true
set :message, 'Order processed'
endMethod Calls
call_method(object, method_name, *args)
Calls a method on an object.
actions do
order = context[:order]
call_method(order, :apply_discount, 0.15)
call_method(order, :add_note, 'Discount applied')
endContext Access
Within action blocks, you have access to:
actions do
# Direcordt context access
user = context[:user]
order = context[:order]
# Set values
set :total, order.total * (1 - discount)
# Conditional logic
set :expedited, true if user.admin?
endComplete Example
engine = Ruleur.define do
# High priority rule
rule 'validate_user', salience: 100 do
conditions do
all?(
user(:present),
not?(user(:banned?))
)
end
actions do
set :user_valid, true
end
end
# Permission rule
rule 'allow_edit', salience: 50, tags: [:permissions] do
conditions do
any?(
user(:admin?),
all?(
user(:owner?, record),
record(:editable?),
not?(record(:locked?))
)
)
end
actions do
set :edit, true
set :reason, 'User has edit permissions'
end
end
# Workflow rule with no_loop
rule 'process_order', salience: 10, no_loop: true do
conditions do
all?(
flag(:user_valid),
flag(:update),
recordord(:ready_to_process?)
)
end
actions do
order = context[:recordord]
call_method(order, :process!)
set :processed, true
set :processed_at, Time.now
end
end
end
# Run the engine
result = engine.run(user: current_user, recordord: order)
puts result[:processed] # => trueAdvanced Patterns
Dynamic Rule Creation
def create_threshold_rule(name, threshold)
rule name do
conditions do
all?(order(:total).gt?(literal(threshold)))
end
actions { set :tier, name }
end
end
engine = Ruleur.define do
create_threshold_rule('bronze', 100)
create_threshold_rule('silver', 500)
create_threshold_rule('gold', 1000)
endNested Method Calls
rule 'premium_check' do
conditions do
all?(
user(:subscription, :tier).eq?('premium'),
user(:subscription, :active?),
user(:subscription, :expires_at).gt?(literal(Date.today))
)
end
actions do
set :premium_features, true
end
endComplex Conditionals in Actions
rule 'calculate_shipping' do
conditions do
all?(flag(:order_valid))
end
actions do
order = context[:order]
total = order.total
shipping = if total > 100
0
elsif total > 50
5
else
10
end
set :shipping_cost, shipping
set :free_shipping, shipping == 0
end
endSee Also
- DSL Basics Guide - DSL patterns and usage
- Condition - Condition types
- Operators - Available operators
- Rule - Rule structure