hooks 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # A sample Gemfile
2
+ source :gemcutter
3
+
4
+ gem "activesupport", "~>2.3"
5
+
6
+ group :test do
7
+ gem "shoulda"
8
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,12 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activesupport (2.3.9)
5
+ shoulda (2.11.3)
6
+
7
+ PLATFORMS
8
+ ruby
9
+
10
+ DEPENDENCIES
11
+ activesupport (~> 2.3)
12
+ shoulda
data/README.rdoc ADDED
@@ -0,0 +1,87 @@
1
+ = Hooks
2
+
3
+ <em>Generic hooks with callbacks for Ruby.</em>
4
+
5
+
6
+ == Introduction
7
+
8
+ Hooks lets you define hooks declaratively in your ruby class. You can add callbacks to your hook, which will be
9
+ run as soon as _you_ run the hook!
10
+
11
+ It's almost like ActiveSupport::Callbacks but 76,6% less complex.
12
+
13
+ Instead, it is not more than 80 lines of code, one method compilation, no +method_missing+ and no magic.
14
+
15
+
16
+ == Example
17
+
18
+ Let's take... a cat.
19
+
20
+ class Cat
21
+ include Hooks
22
+
23
+ define_hook :after_dinner
24
+
25
+ Now you can add callbacks to your hook declaratively in your class.
26
+
27
+ after_dinner do
28
+ puts "Ice cream!"
29
+ end
30
+
31
+ after_dinner :have_a_desert # => refers to Cat#have_a_desert
32
+
33
+
34
+ def have_a_desert
35
+ puts "Hell, yeah!"
36
+ end
37
+
38
+ Running the callbacks happens on instances. It will run the block and +#have_a_desert+ from above.
39
+
40
+ cat.run_hook :after_dinner
41
+ # => Ice cream!
42
+ Hell, yeah!
43
+
44
+
45
+ == Options
46
+
47
+ You're free to pass any number of arguments to #run_callback.
48
+
49
+ cat.run_hook :before_dinner, cat, Time.now
50
+
51
+ The callbacks should be ready for receiving parameters.
52
+
53
+ before_dinner :wash_pawns
54
+ before_dinner do |who, when|
55
+ ...
56
+ end
57
+
58
+ def wash_pawns(who, when)
59
+
60
+
61
+ Not sure why a cat should have ice cream for dinner. Beside that, I was tempted naming this gem _hooker_.
62
+
63
+
64
+ == Installation
65
+
66
+ gem install hooks
67
+
68
+
69
+ == Dependencies
70
+
71
+ The current gem requires
72
+
73
+ * active_support 2.3.x
74
+
75
+
76
+ == Similar libraries
77
+
78
+ * http://github.com/nakajima/aspectory/tree/master/lib/aspectory/
79
+ * http://github.com/auser/backcall
80
+ * http://github.com/mmcgrana/simple_callbacks
81
+
82
+
83
+ == License
84
+
85
+ Copyright (c) 2010, Nick Sutterer
86
+
87
+ Released under the MIT License.
data/Rakefile ADDED
@@ -0,0 +1,33 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the hooks plugin.'
9
+ Rake::TestTask.new(:test) do |test|
10
+ test.libs << 'test'
11
+ test.test_files = FileList['test/*_test.rb']
12
+ test.verbose = true
13
+ end
14
+
15
+ require 'jeweler'
16
+ $:.unshift File.dirname(__FILE__) # add current dir to LOAD_PATHS
17
+ require 'lib/hooks'
18
+
19
+ Jeweler::Tasks.new do |spec|
20
+ spec.name = "hooks"
21
+ spec.version = ::Hooks::VERSION
22
+ spec.summary = %{Generic hooks with callbacks for Ruby. }
23
+ spec.description = "Declaratively define hooks, add callbacks and run them with the options you like."
24
+ spec.homepage = "http://nicksda.apotomo.de/category/hooks"
25
+ spec.authors = ["Nick Sutterer"]
26
+ spec.email = "apotonick@gmail.com"
27
+
28
+ spec.files = FileList["[A-Z]*", File.join(*%w[{lib} ** *]).to_s]
29
+
30
+ spec.add_dependency 'activesupport', '>= 2.3.0'
31
+ end
32
+
33
+ Jeweler::GemcutterTasks.new
data/lib/hooks.rb ADDED
@@ -0,0 +1,67 @@
1
+ require 'active_support/core_ext/class/inheritable_attributes.rb'
2
+
3
+ # Almost like ActiveSupport::Callbacks but 76,6% less complex.
4
+ #
5
+ # Example:
6
+ #
7
+ # class CatWidget < Apotomo::Widget
8
+ # define_hook :after_dinner
9
+ #
10
+ # Now you can add callbacks to your hook declaratively in your class.
11
+ #
12
+ # after_dinner do puts "Ice cream!" end
13
+ # after_dinner :have_a_desert # => refers to CatWidget#have_a_desert
14
+ #
15
+ # Running the callbacks happens on instances. It will run the block and #have_a_desert from above.
16
+ #
17
+ # cat.run_hook :after_dinner
18
+ module Hooks
19
+ VERSION = "0.1"
20
+
21
+ def self.included(base)
22
+ base.extend ClassMethods
23
+ end
24
+
25
+ module ClassMethods
26
+ def define_hook(name)
27
+ accessor_name = "_#{name}_callbacks"
28
+
29
+ setup_hook_accessors(accessor_name)
30
+ define_hook_writer(name, accessor_name)
31
+ end
32
+
33
+ private
34
+ def define_hook_writer(hook, accessor_name)
35
+ instance_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
36
+ def #{hook}(method=nil, &block)
37
+ callback = block_given? ? block : method
38
+ #{accessor_name} << callback
39
+ end
40
+ RUBY_EVAL
41
+ end
42
+
43
+ def setup_hook_accessors(accessor_name)
44
+ class_inheritable_array(accessor_name, :instance_writer => false)
45
+ send("#{accessor_name}=", []) # initialize ivar.
46
+ end
47
+
48
+ end
49
+
50
+ # Runs the callbacks (method/block) for the specified hook +name+. Additional arguments will
51
+ # be passed to the callback.
52
+ #
53
+ # Example:
54
+ #
55
+ # cat.run_hook :after_dinner, "i want ice cream!"
56
+ #
57
+ # will invoke the callbacks like
58
+ #
59
+ # desert("i want ice cream!")
60
+ # block.call("i want ice cream!")
61
+ def run_hook(name, *args)
62
+ self.class.send("_#{name}_callbacks").each do |callback|
63
+ send(callback, *args) and next if callback.kind_of? Symbol
64
+ callback.call(*args)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,64 @@
1
+ require 'test_helper'
2
+
3
+ class HooksTest < ActiveSupport::TestCase
4
+ context "Hooks.define_hook" do
5
+ setup do
6
+ @klass = Class.new(Object) do
7
+ include Hooks
8
+
9
+ def executed
10
+ @executed ||= [];
11
+ end
12
+ end
13
+
14
+ @mum = @klass.new
15
+ @mum.class.define_hook :after_eight
16
+ end
17
+
18
+ should "provide accessors to the stored callbacks" do
19
+ assert_equal [], @klass._after_eight_callbacks
20
+ @klass._after_eight_callbacks << :dine
21
+ assert_equal [:dine], @klass._after_eight_callbacks
22
+ end
23
+
24
+ context "creates a public writer for the hook that" do
25
+ should "accepts method names" do
26
+ @klass.after_eight :dine
27
+ assert_equal [:dine], @klass._after_eight_callbacks
28
+ end
29
+
30
+ should "accepts blocks" do
31
+ @klass.after_eight do true; end
32
+ assert @klass._after_eight_callbacks.first.kind_of? Proc
33
+ end
34
+ end
35
+
36
+ context "Hooks.run_hook"do
37
+ should "run without parameters" do
38
+ @mum.instance_eval do
39
+ def a; executed << :a; end
40
+ def b; executed << :b; end
41
+
42
+ self.class.after_eight :b
43
+ self.class.after_eight :a
44
+ end
45
+
46
+ @mum.run_hook(:after_eight)
47
+
48
+ assert_equal [:b, :a], @mum.executed
49
+ end
50
+
51
+ should "accept arbitrary parameters" do
52
+ @mum.instance_eval do
53
+ def a(me, arg); executed << arg+1; end
54
+ end
55
+ @mum.class.after_eight :a
56
+ @mum.class.after_eight lambda { |me, arg| me.executed << arg-1 }
57
+
58
+ @mum.run_hook(:after_eight, @mum, 1)
59
+
60
+ assert_equal [2, 0], @mum.executed
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+
3
+ # wycats says...
4
+ require 'bundler'
5
+ Bundler.setup
6
+ require 'test/unit'
7
+ require 'shoulda'
8
+ require 'active_support/test_case'
9
+ require 'hooks'
10
+
11
+ $:.unshift File.dirname(__FILE__) # add current dir to LOAD_PATHS
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hooks
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ version: "0.1"
9
+ platform: ruby
10
+ authors:
11
+ - Nick Sutterer
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+
16
+ date: 2010-09-24 00:00:00 +02:00
17
+ default_executable:
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: activesupport
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 2
29
+ - 3
30
+ - 0
31
+ version: 2.3.0
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ description: Declaratively define hooks, add callbacks and run them with the options you like.
35
+ email: apotonick@gmail.com
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files:
41
+ - README.rdoc
42
+ files:
43
+ - Gemfile
44
+ - Gemfile.lock
45
+ - README.rdoc
46
+ - Rakefile
47
+ - lib/hooks.rb
48
+ - test/test_helper.rb
49
+ - test/hooks_test.rb
50
+ has_rdoc: true
51
+ homepage: http://nicksda.apotomo.de/category/hooks
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options:
56
+ - --charset=UTF-8
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ requirements: []
76
+
77
+ rubyforge_project:
78
+ rubygems_version: 1.3.7
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: Generic hooks with callbacks for Ruby.
82
+ test_files:
83
+ - test/test_helper.rb
84
+ - test/hooks_test.rb