hooks 0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,12 +5,10 @@
5
5
 
6
6
  == Introduction
7
7
 
8
- Hooks lets you define hooks declaratively in your ruby class. You can add callbacks to your hook, which will be
8
+ _Hooks_ lets you define hooks declaratively in your ruby class. You can add callbacks to your hook, which will be
9
9
  run as soon as _you_ run the hook!
10
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.
11
+ It's almost like ActiveSupport::Callbacks but 76,6% less complex. Instead, it is not more than 60 lines of code, one method compilation, no +method_missing+ and no magic.
14
12
 
15
13
 
16
14
  == Example
@@ -35,7 +33,7 @@ Now you can add callbacks to your hook declaratively in your class.
35
33
  puts "Hell, yeah!"
36
34
  end
37
35
 
38
- Running the callbacks happens on instances. It will run the block and +#have_a_desert+ from above.
36
+ Running the callbacks happens on instances. It will run the block and #have_a_desert from above.
39
37
 
40
38
  cat.run_hook :after_dinner
41
39
  # => Ice cream!
@@ -73,9 +71,13 @@ The current gem requires
73
71
  * active_support 2.3.x
74
72
 
75
73
 
74
+ == Anybody using it?
75
+
76
+ * Hooks is already used in [Apotomo:http://github.com/apotonick/apotomo], a hot widget framework for Rails. Look at +lib/apotomo/widget.rb+ for examples and into +lib/apotomo/tree_node.rb+ to learn how modules-driven code might benefit from hooks.
77
+
76
78
  == Similar libraries
77
79
 
78
- * http://github.com/nakajima/aspectory/tree/master/lib/aspectory/
80
+ * http://github.com/nakajima/aspectory
79
81
  * http://github.com/auser/backcall
80
82
  * http://github.com/mmcgrana/simple_callbacks
81
83
 
data/Rakefile CHANGED
@@ -13,8 +13,8 @@ Rake::TestTask.new(:test) do |test|
13
13
  end
14
14
 
15
15
  require 'jeweler'
16
- $:.unshift File.dirname(__FILE__) # add current dir to LOAD_PATHS
17
- require 'lib/hooks'
16
+ $:.unshift File.dirname(__FILE__)+"/lib" # add current dir to LOAD_PATHS
17
+ require 'hooks'
18
18
 
19
19
  Jeweler::Tasks.new do |spec|
20
20
  spec.name = "hooks"
@@ -26,8 +26,6 @@ Jeweler::Tasks.new do |spec|
26
26
  spec.email = "apotonick@gmail.com"
27
27
 
28
28
  spec.files = FileList["[A-Z]*", File.join(*%w[{lib} ** *]).to_s]
29
-
30
- spec.add_dependency 'activesupport', '>= 2.3.0'
31
29
  end
32
30
 
33
31
  Jeweler::GemcutterTasks.new
@@ -1,4 +1,4 @@
1
- require 'active_support/core_ext/class/inheritable_attributes.rb'
1
+ require "hooks/inheritable_attribute"
2
2
 
3
3
  # Almost like ActiveSupport::Callbacks but 76,6% less complex.
4
4
  #
@@ -16,9 +16,10 @@ require 'active_support/core_ext/class/inheritable_attributes.rb'
16
16
  #
17
17
  # cat.run_hook :after_dinner
18
18
  module Hooks
19
- VERSION = "0.1"
19
+ VERSION = "0.1.1"
20
20
 
21
21
  def self.included(base)
22
+ base.extend InheritableAttribute
22
23
  base.extend ClassMethods
23
24
  end
24
25
 
@@ -30,21 +31,41 @@ module Hooks
30
31
  define_hook_writer(name, accessor_name)
31
32
  end
32
33
 
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.
34
+ # Like Hooks#run_hook but for the class. Note that +:callbacks+ must be class methods.
35
+ #
36
+ # Example:
37
+ #
38
+ # class Cat
39
+ # after_eight :grab_a_beer
40
+ #
41
+ # def self.grab_a_beer(*) # and so on...
42
+ #
43
+ # where <tt>Cat.run_hook :after_eight</tt> will call the class method +grab_a_beer+.
44
+ def run_hook(name, *args)
45
+ run_hook_for(name, self, *args)
46
+ end
47
+
48
+ def run_hook_for(name, scope, *args)
49
+ send("_#{name}_callbacks").each do |callback|
50
+ scope.send(callback, *args) and next if callback.kind_of? Symbol
51
+ callback.call(*args)
46
52
  end
47
-
53
+ end
54
+
55
+ private
56
+ def define_hook_writer(hook, accessor_name)
57
+ instance_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
58
+ def #{hook}(method=nil, &block)
59
+ callback = block_given? ? block : method
60
+ #{accessor_name} << callback
61
+ end
62
+ RUBY_EVAL
63
+ end
64
+
65
+ def setup_hook_accessors(accessor_name)
66
+ inheritable_attr(accessor_name)
67
+ send("#{accessor_name}=", []) # initialize ivar.
68
+ end
48
69
  end
49
70
 
50
71
  # Runs the callbacks (method/block) for the specified hook +name+. Additional arguments will
@@ -59,9 +80,6 @@ module Hooks
59
80
  # desert("i want ice cream!")
60
81
  # block.call("i want ice cream!")
61
82
  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
83
+ self.class.run_hook_for(name, self, *args)
66
84
  end
67
85
  end
@@ -0,0 +1,33 @@
1
+ module Hooks
2
+ module InheritableAttribute
3
+ # Creates an inheritable attribute with accessors in the singleton class. Derived classes inherit the
4
+ # attributes. This is especially helpful with arrays or hashes that are extended in the inheritance
5
+ # chain. Note that you have to initialize the inheritable attribute.
6
+ #
7
+ # Example:
8
+ #
9
+ # class Cat
10
+ # inheritable_attr :drinks
11
+ # self.drinks = ["Becks"]
12
+ #
13
+ # class Garfield < Cat
14
+ # self.drinks << "Fireman's 4"
15
+ #
16
+ # and then, later
17
+ #
18
+ # Cat.drinks #=> ["Becks"]
19
+ # Garfield.drinks #=> ["Becks", "Fireman's 4"]
20
+ def inheritable_attr(name)
21
+ instance_eval %Q{
22
+ def #{name}=(v)
23
+ @#{name} = v
24
+ end
25
+
26
+ def #{name}
27
+ return @#{name} unless superclass.respond_to?(:#{name})
28
+ @#{name} ||= superclass.#{name}.clone # only do this once.
29
+ end
30
+ }
31
+ end
32
+ end
33
+ end
@@ -31,9 +31,16 @@ class HooksTest < ActiveSupport::TestCase
31
31
  @klass.after_eight do true; end
32
32
  assert @klass._after_eight_callbacks.first.kind_of? Proc
33
33
  end
34
+
35
+ should "be inherited" do
36
+ @klass.after_eight :dine
37
+ subklass = Class.new(@klass)
38
+
39
+ assert_equal [:dine], subklass._after_eight_callbacks
40
+ end
34
41
  end
35
42
 
36
- context "Hooks.run_hook"do
43
+ context "Hooks#run_hook" do
37
44
  should "run without parameters" do
38
45
  @mum.instance_eval do
39
46
  def a; executed << :a; end
@@ -59,6 +66,32 @@ class HooksTest < ActiveSupport::TestCase
59
66
 
60
67
  assert_equal [2, 0], @mum.executed
61
68
  end
62
- end
69
+ end
70
+
71
+ context "in class context" do
72
+ should "run a callback block" do
73
+ executed = []
74
+ @klass.after_eight do
75
+ executed << :klass
76
+ end
77
+ @klass.run_hook :after_eight
78
+
79
+ assert_equal [:klass], executed
80
+ end
81
+
82
+ should "run a class methods" do
83
+ executed = []
84
+ @klass.instance_eval do
85
+ after_eight :have_dinner
86
+
87
+ def have_dinner(executed)
88
+ executed << :have_dinner
89
+ end
90
+ end
91
+ @klass.run_hook :after_eight, executed
92
+
93
+ assert_equal [:have_dinner], executed
94
+ end
95
+ end
63
96
  end
64
97
  end
@@ -0,0 +1,49 @@
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
+ extend Hooks::InheritableAttribute
8
+ end
9
+
10
+ @mum = @klass.new
11
+ @klass.inheritable_attr :drinks
12
+ end
13
+
14
+ should "provide a reader with inherited attributes, already" do
15
+ assert_equal nil, @klass.drinks
16
+ end
17
+
18
+ should "provide an attribute copy in subclasses" do
19
+ @klass.drinks = []
20
+ assert @klass.drinks.object_id != Class.new(@klass).drinks.object_id
21
+ end
22
+
23
+ should "provide a writer" do
24
+ @klass.drinks = [:cabernet]
25
+ assert_equal [:cabernet], @klass.drinks
26
+ end
27
+
28
+ should "inherit attributes" do
29
+ @klass.drinks = [:cabernet]
30
+
31
+ subklass_a = Class.new(@klass)
32
+ subklass_a.drinks << :becks
33
+
34
+ subklass_b = Class.new(@klass)
35
+
36
+ assert_equal [:cabernet], @klass.drinks
37
+ assert_equal [:cabernet, :becks], subklass_a.drinks
38
+ assert_equal [:cabernet], subklass_b.drinks
39
+ end
40
+
41
+ should "not inherit attributes if we set explicitely" do
42
+ @klass.drinks = [:cabernet]
43
+ subklass = Class.new(@klass)
44
+
45
+ subklass.drinks = [:merlot] # we only want merlot explicitely.
46
+ assert_equal [:merlot], subklass.drinks # no :cabernet, here
47
+ end
48
+ end
49
+ end
metadata CHANGED
@@ -5,7 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- version: "0.1"
8
+ - 1
9
+ version: 0.1.1
9
10
  platform: ruby
10
11
  authors:
11
12
  - Nick Sutterer
@@ -13,24 +14,10 @@ autorequire:
13
14
  bindir: bin
14
15
  cert_chain: []
15
16
 
16
- date: 2010-09-24 00:00:00 +02:00
17
+ date: 2010-10-03 00:00:00 +02:00
17
18
  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
19
+ dependencies: []
20
+
34
21
  description: Declaratively define hooks, add callbacks and run them with the options you like.
35
22
  email: apotonick@gmail.com
36
23
  executables: []
@@ -45,6 +32,8 @@ files:
45
32
  - README.rdoc
46
33
  - Rakefile
47
34
  - lib/hooks.rb
35
+ - lib/hooks/inheritable_attribute.rb
36
+ - test/inheritable_attribute_test.rb
48
37
  - test/test_helper.rb
49
38
  - test/hooks_test.rb
50
39
  has_rdoc: true
@@ -80,5 +69,6 @@ signing_key:
80
69
  specification_version: 3
81
70
  summary: Generic hooks with callbacks for Ruby.
82
71
  test_files:
72
+ - test/inheritable_attribute_test.rb
83
73
  - test/test_helper.rb
84
74
  - test/hooks_test.rb