[Sass] Add some callback infrastructure.

This commit is contained in:
Nathan Weizenbaum 2010-01-16 18:37:14 -08:00
parent 6248ca3721
commit 4987a5cb0d
4 changed files with 142 additions and 0 deletions

50
lib/sass/callbacks.rb Normal file
View File

@ -0,0 +1,50 @@
# A lightweight infrastructure for defining and running callbacks.
# Callbacks are defined using \{#define\_callback\} at the class level,
# and called using `run_#{name}` at the instance level.
#
# Clients can add callbacks by calling the generated `on_#{name}` method,
# and passing in a block that's run when the callback is activated.
#
# @example Define a callback
# class Munger
# extend Sass::Callbacks
# define_callback :string_munged
#
# def munge(str)
# res = str.gsub(/[a-z]/, '\1\1')
# run_string_munged str, res
# res
# end
# end
#
# @example Use a callback
# m = Munger.new
# m.on_string_munged {|str, res| puts "#{str} was munged into #{res}!"}
# m.munge "bar" #=> bar was munged into bbaarr!
module Sass::Callbacks
protected
# Define a callback with the given name.
# This will define an `on_#{name}` method
# that registers a block,
# and a `run_#{name}` method that runs that block
# (optionall with some arguments).
#
# @param name [Symbol] The name of the callback
# @return [void]
def define_callback(name)
class_eval <<RUBY
def on_#{name}(&block)
@_sass_callbacks ||= {}
(@_sass_callbacks[#{name.inspect}] ||= []) << block
end
def run_#{name}(*args)
return unless @_sass_callbacks
return unless @_sass_callbacks[#{name.inspect}]
@_sass_callbacks[#{name.inspect}].each {|c| c[*args]}
end
private :run_#{name}
RUBY
end
end

View File

@ -1,6 +1,7 @@
require 'fileutils'
require 'sass'
require 'sass/callbacks'
module Sass
# This module handles the compilation of Sass files.
@ -14,6 +15,7 @@ module Sass
# Other frameworks must enable it explicitly; see {Sass::Plugin::Rack}.
module Plugin
include Haml::Util
include Sass::Callbacks
extend self
@options = {

61
test/sass/callbacks_test.rb Executable file
View File

@ -0,0 +1,61 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../test_helper'
require 'sass/callbacks'
class CallerBack
extend Sass::Callbacks
define_callback :foo
define_callback :bar
def do_foo
run_foo
end
def do_bar
run_bar 12
end
end
module ClassLevelCallerBack
extend Sass::Callbacks
define_callback :foo
extend self
def do_foo
run_foo
end
end
class SassCallbacksTest < Test::Unit::TestCase
def test_simple_callback
cb = CallerBack.new
there = false
cb.on_foo {there = true}
cb.do_foo
assert there, "Expected callback to be called."
end
def test_multiple_callbacks
cb = CallerBack.new
str = ""
cb.on_foo {str += "first"}
cb.on_foo {str += " second"}
cb.do_foo
assert_equal "first second", str
end
def test_callback_with_arg
cb = CallerBack.new
val = nil
cb.on_bar {|a| val = a}
cb.do_bar
assert_equal 12, val
end
def test_class_level_callback
there = false
ClassLevelCallerBack.on_foo {there = true}
ClassLevelCallerBack.do_foo
assert there, "Expected callback to be called."
end
end

29
yard/callbacks.rb Normal file
View File

@ -0,0 +1,29 @@
class CallbacksHandler < YARD::Handlers::Ruby::Legacy::Base
handles /\Adefine_callback(\s|\()/
def process
callback_name = tokval(statement.tokens[2])
attr_index = statement.comments.each_with_index {|c, i| break i if c[0] == ?@}
if attr_index.is_a?(Fixnum)
docstring = statement.comments[0...attr_index]
attrs = statement.comments[attr_index..-1]
else
docstring = statement.comments
attrs = []
end
yieldparams = ""
attrs.reject! do |a|
next unless a =~ /^@yield *(\[.*?\])/
yieldparams = $1
true
end
o = register(MethodObject.new(namespace, "on_#{callback_name}", scope))
o.docstring = docstring + [
"@return [void]",
"@yield #{yieldparams} When the callback is run"
] + attrs
o.signature = true
end
end