Skip to content

Instantly share code, notes, and snippets.

@jcoglan
Created January 7, 2009 00:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcoglan/44084 to your computer and use it in GitHub Desktop.
Save jcoglan/44084 to your computer and use it in GitHub Desktop.
# Stripped-down version of Consent's expression language
# http://blog.jcoglan.com/2009/01/07/writing-your-own-expression-language-in-ruby/
# http://blog.jcoglan.com/2009/01/16/and-now-the-rules/
require 'observer'
module Consent
class Expression
def initialize(controller, params = {})
@controller, @params = controller.to_s, params.dup
end
def +(expression)
self.class.const_get(:Group).new + self + expression
end
def *(expression)
@format = expression.instance_eval { @controller }
@action = nil
self
end
def /(expression)
expression.nesting = @controller
expression
end
def nesting=(name)
@controller = "#{ name }/#{ @controller }"
end
def verb=(verb)
@verb = verb.to_s
end
def method_missing(name, params = {})
@format = name.to_s if @action
@action ||= name.to_s
@params.update(params)
self
end
def inspect
source = @controller.dup
source << ".#{ @action }" if @action
source << "(#{ @params.map { |k,v| ":#{k} => #{v.inspect}" } * ', ' })" unless @params.empty?
source << "#{ @action ? '.' : '*' }#{ @format }" if @format
source = "#{ @verb }(#{ source })" if @verb
source
end
class Group
include Enumerable
def initialize
@exprs = []
end
def each(&block)
@exprs.each(&block)
end
def +(expression)
expression.is_a?(Enumerable) ?
expression.each { |exp| @exprs << exp } :
@exprs << expression
self
end
def verb=(verb)
each { |exp| exp.verb = verb }
end
def inspect
collect { |exp| exp.inspect } * ' + '
end
end
module Generator
def method_missing(name, params = {})
Expression.new(name, params)
end
end
end
class Rule
def initialize(expression, block)
@expression, @predicate = expression, block
@expression.add_observer(self)
end
def update(message)
@invalid = true if message == :destroyed
end
def inspect
string = @expression.inspect
string << "#invalid" if @invalid
string
end
class Expression < Consent::Expression
include Observable
attr_reader :block
def initialize(env, name, params = {})
@env = env
super(name, params)
end
def rule!(block)
return if block.nil?
@block = block
@env.rules << Rule.new(self, block)
end
def *(expression)
expression.destroy!
rule!(expression.block)
super
end
def destroy!
changed(true)
notify_observers(:destroyed)
end
def method_missing(name, params = {}, &block)
rule!(block)
super(name, params)
end
class Group < Consent::Expression::Group
attr_reader :block
def +(expression)
rule!(expression.block)
super
end
def rule!(block)
return if block.nil?
@block = block
each { |exp| exp.rule!(block) }
end
end
end
module Generator
def self.included(host)
host.module_eval do
def rules
@rules ||= []
end
end
end
def method_missing(name, params = {}, &block)
expression = Rule::Expression.new(self, name, params)
expression.rule!(block)
expression
end
%w(get post put head delete).each do |verb|
module_eval <<-EOS
def #{verb}(*exprs, &block)
group = exprs.inject { |grp, exp| grp + exp }
group.verb = :#{verb}
group.rule!(block)
group
end
EOS
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment