callbacks 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +5 -0
- data/Manifest.txt +29 -0
- data/README.txt +65 -0
- data/Rakefile +25 -0
- data/Version.txt +1 -0
- data/bin/callbacks +8 -0
- data/ext/metaid.rb +18 -0
- data/lib/callback.rb +43 -0
- data/lib/callbacks.rb +27 -0
- data/lib/classmethods.rb +78 -0
- data/lib/instancemethods.rb +38 -0
- data/spec/callbacks_spec.rb +8 -0
- data/spec/spec_helper.rb +17 -0
- data/tasks/ann.rake +81 -0
- data/tasks/bones.rake +21 -0
- data/tasks/gem.rake +121 -0
- data/tasks/manifest.rake +49 -0
- data/tasks/notes.rake +28 -0
- data/tasks/post_load.rake +39 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +57 -0
- data/tasks/setup.rb +266 -0
- data/tasks/spec.rake +55 -0
- data/tasks/svn.rake +48 -0
- data/tasks/test.rake +38 -0
- data/test/profile.rb +16 -0
- data/test/test_callbacks.rb +168 -0
- data/test/test_helper.rb +4 -0
- data/test/tester_class.rb +23 -0
- metadata +86 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
Version.txt
|
6
|
+
bin/callbacks
|
7
|
+
ext/metaid.rb
|
8
|
+
lib/callback.rb
|
9
|
+
lib/callbacks.rb
|
10
|
+
lib/classmethods.rb
|
11
|
+
lib/instancemethods.rb
|
12
|
+
spec/callbacks_spec.rb
|
13
|
+
spec/spec_helper.rb
|
14
|
+
tasks/ann.rake
|
15
|
+
tasks/bones.rake
|
16
|
+
tasks/gem.rake
|
17
|
+
tasks/manifest.rake
|
18
|
+
tasks/notes.rake
|
19
|
+
tasks/post_load.rake
|
20
|
+
tasks/rdoc.rake
|
21
|
+
tasks/rubyforge.rake
|
22
|
+
tasks/setup.rb
|
23
|
+
tasks/spec.rake
|
24
|
+
tasks/svn.rake
|
25
|
+
tasks/test.rake
|
26
|
+
test/profile.rb
|
27
|
+
test/test_callbacks.rb
|
28
|
+
test/test_helper.rb
|
29
|
+
test/tester_class.rb
|
data/README.txt
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
callbacks
|
2
|
+
by Leon Bogaert
|
3
|
+
http://github.com/LeonB/callbacks
|
4
|
+
http://callbacks.rubyforge.com
|
5
|
+
http://www.vanutsteen.nl
|
6
|
+
|
7
|
+
== DESCRIPTION:
|
8
|
+
|
9
|
+
This package makes it simple to add callbacks a.k.a. "hooks" to ruby classes
|
10
|
+
|
11
|
+
== FEATURES/PROBLEMS:
|
12
|
+
|
13
|
+
* It now only supports adding callbacks to classes
|
14
|
+
* Different callbacks for instances comes with version 0.2
|
15
|
+
* I haven't decided yet on the API, maybe I'll swap "callback_methods" with "hooks"
|
16
|
+
|
17
|
+
== SYNOPSIS:
|
18
|
+
|
19
|
+
require 'callbacks'
|
20
|
+
|
21
|
+
Object.send(:include, Callbacks)
|
22
|
+
Object.add_callback_methods :inspect
|
23
|
+
Object.before_inspect do
|
24
|
+
print "Going to inspect #{self.class}\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
o = Object.new
|
28
|
+
o.inspect #will print "Going to inspect Object"
|
29
|
+
|
30
|
+
== REQUIREMENTS:
|
31
|
+
|
32
|
+
* ruby 1.8 (might work with ruby1.9)
|
33
|
+
|
34
|
+
== INSTALL:
|
35
|
+
|
36
|
+
* gem install callbacks
|
37
|
+
|
38
|
+
== THANKS:
|
39
|
+
* RDoc [http://www.ruby-doc.org/core/classes/RDoc.html]
|
40
|
+
* Bones [http://codeforpeople.rubyforge.org/bones]
|
41
|
+
|
42
|
+
== LICENSE:
|
43
|
+
|
44
|
+
(The MIT License)
|
45
|
+
|
46
|
+
Copyright (c) 2008
|
47
|
+
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
49
|
+
a copy of this software and associated documentation files (the
|
50
|
+
'Software'), to deal in the Software without restriction, including
|
51
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
52
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
53
|
+
permit persons to whom the Software is furnished to do so, subject to
|
54
|
+
the following conditions:
|
55
|
+
|
56
|
+
The above copyright notice and this permission notice shall be
|
57
|
+
included in all copies or substantial portions of the Software.
|
58
|
+
|
59
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
60
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
61
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
62
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
63
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
64
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
65
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Look in the tasks/setup.rb file for the various options that can be
|
2
|
+
# configured in this Rakefile. The .rake files in the tasks directory
|
3
|
+
# are where the options are used.
|
4
|
+
|
5
|
+
load 'tasks/setup.rb'
|
6
|
+
|
7
|
+
ensure_in_path 'lib'
|
8
|
+
require 'callbacks'
|
9
|
+
|
10
|
+
task :default => 'spec:run'
|
11
|
+
|
12
|
+
PROJ.name = 'callbacks'
|
13
|
+
PROJ.authors = 'Leon Bogaert'
|
14
|
+
PROJ.email = 'leon@vanutsteen.nl'
|
15
|
+
PROJ.url = 'www.vanutsteen.nl'
|
16
|
+
PROJ.rubyforge.name = 'callbacks'
|
17
|
+
PROJ.version = File.read('Version.txt').strip
|
18
|
+
PROJ.exclude = %w(.git pkg/ nbproject/ doc/ ^test[0-9]*.rb$ website/ )
|
19
|
+
|
20
|
+
PROJ.rdoc.exclude = %w(Version.txt)
|
21
|
+
PROJ.rdoc.remote_dir = 'docs/'
|
22
|
+
|
23
|
+
PROJ.spec.opts << '--color'
|
24
|
+
|
25
|
+
# EOF
|
data/Version.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/bin/callbacks
ADDED
data/ext/metaid.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#The well know file made by why (http://redhanded.hobix.com/inspect/seeingMetaclassesClearly.html)
|
2
|
+
class Object
|
3
|
+
# The hidden singleton lurks behind everyone
|
4
|
+
def metaclass; class << self; self; end; end
|
5
|
+
def meta_eval &blk
|
6
|
+
metaclass.instance_eval(&blk)
|
7
|
+
end
|
8
|
+
|
9
|
+
# Adds methods to a metaclass
|
10
|
+
def meta_def name, &blk
|
11
|
+
meta_eval { define_method name, &blk }
|
12
|
+
end
|
13
|
+
|
14
|
+
# Defines an instance method within a class
|
15
|
+
def class_def name, &blk
|
16
|
+
class_eval { define_method name, &blk }
|
17
|
+
end
|
18
|
+
end
|
data/lib/callback.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
class Callback
|
2
|
+
attr_accessor :method, :input
|
3
|
+
attr_writer :proc
|
4
|
+
ACCEPTED = [Symbol, Proc, String, Method]
|
5
|
+
|
6
|
+
def initialize(method, input, &block)
|
7
|
+
#self.method = method
|
8
|
+
self.input = input
|
9
|
+
self.input = block if block_given?
|
10
|
+
|
11
|
+
if not ACCEPTED.include? self.input.class
|
12
|
+
raise "Callbacks must be a symbol denoting the method to call, a string to be evaluated, a block to be invoked, or an object responding to the callback method. #{self.input} is none of those."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def proc
|
17
|
+
@proc ||= self.generate_proc
|
18
|
+
end
|
19
|
+
|
20
|
+
def block
|
21
|
+
@block ||= self.generate_block
|
22
|
+
end
|
23
|
+
|
24
|
+
def generate_proc
|
25
|
+
case self.input
|
26
|
+
when String
|
27
|
+
Proc.new { eval(self.input) }
|
28
|
+
#else if self.input.respond_to?(self.method)
|
29
|
+
# Proc.new { callback.send(method, self) }
|
30
|
+
# end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def generate_block
|
35
|
+
case self.input
|
36
|
+
when Symbol
|
37
|
+
symbol = self.input.to_sym
|
38
|
+
Proc.new { self.send(symbol) }
|
39
|
+
when Proc, Method
|
40
|
+
self.input
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/callbacks.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#Idea stolen from rails/activerecord
|
2
|
+
module Callbacks
|
3
|
+
require 'observer'
|
4
|
+
require 'ext/metaid.rb'
|
5
|
+
require 'instancemethods.rb'
|
6
|
+
require 'classmethods.rb'
|
7
|
+
require 'callback.rb'
|
8
|
+
|
9
|
+
# CALLBACKS = [
|
10
|
+
# 'song_halfway',
|
11
|
+
# 'song_end',
|
12
|
+
# 'boot',
|
13
|
+
# 'shutdown',
|
14
|
+
# ]
|
15
|
+
|
16
|
+
CALLBACKS = []
|
17
|
+
|
18
|
+
#Sets all things right
|
19
|
+
def self.included(base) #:nodoc:
|
20
|
+
base.extend Observable
|
21
|
+
base.extend Callbacks::ClassMethods
|
22
|
+
base.send(:include, Callbacks::InstanceMethods)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
#TODO: add aliases like add_hook(), callbacks(), et cetera
|
27
|
+
#TODO: feature to add hooks/callbacks to instances instead of only classes
|
data/lib/classmethods.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
module Callbacks
|
2
|
+
module ClassMethods
|
3
|
+
|
4
|
+
def callback_methods
|
5
|
+
#Use one @, else it gets stored in the module and then it will be avaiable in every class that uses callback
|
6
|
+
@callback_methods ||= []
|
7
|
+
end
|
8
|
+
|
9
|
+
def callback_actions
|
10
|
+
#Use one @, else it gets stored in the module and then it will be avaiable in every class that uses callback
|
11
|
+
@callback_actions ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_callback_methods(*callbacks)
|
15
|
+
callbacks.each do |callback|
|
16
|
+
self.callback_methods << callback.to_sym
|
17
|
+
self.build_callback_method(callback)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def remove_callback_method(method)
|
22
|
+
self.meta_eval do
|
23
|
+
remove_method("before_#{method}")
|
24
|
+
remove_method("after_#{method}")
|
25
|
+
end
|
26
|
+
|
27
|
+
remove_method("#{method}_with_callbacks")
|
28
|
+
remove_method(method) #This is the method_with_callbacks
|
29
|
+
alias_method(method, "#{method}_without_callbacks")
|
30
|
+
self.callback_methods.delete(method.to_sym)
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_callback_method(method)
|
34
|
+
|
35
|
+
#instance_eval do
|
36
|
+
instance_eval <<-"end_eval"
|
37
|
+
|
38
|
+
def before_#{method}(*callbacks, &block)
|
39
|
+
add_callback_action :before, :#{method}, *callbacks, &block
|
40
|
+
end
|
41
|
+
|
42
|
+
def after_#{method}(*callbacks, &block)
|
43
|
+
add_callback_action :after, :#{method}, *callbacks, &block
|
44
|
+
end
|
45
|
+
|
46
|
+
end_eval
|
47
|
+
#end
|
48
|
+
|
49
|
+
class_eval <<-"end_eval"
|
50
|
+
def #{method}_with_callbacks
|
51
|
+
trigger_callback_actions(:#{method}, :before)
|
52
|
+
retval = self.send("#{method}_without_callbacks")
|
53
|
+
trigger_callback_actions(:#{method}, :after)
|
54
|
+
return retval
|
55
|
+
end
|
56
|
+
end_eval
|
57
|
+
|
58
|
+
send :alias_method, "#{method}_without_callbacks", method
|
59
|
+
send :alias_method, method, "#{method}_with_callbacks"
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_callback_action(type, method, *code, &block)
|
63
|
+
ca = self.callback_actions
|
64
|
+
|
65
|
+
ca[method] = {} if ca[method].nil?
|
66
|
+
ca[method][type] = [] if ca[method][type].nil?
|
67
|
+
|
68
|
+
if block_given?
|
69
|
+
ca[method][type] << Callback.new(method, nil, &block)
|
70
|
+
else
|
71
|
+
code.each do |c|
|
72
|
+
ca[method][type] << Callback.new(method, c)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Callbacks
|
2
|
+
module InstanceMethods
|
3
|
+
|
4
|
+
def callback_actions(show_classvars = true)
|
5
|
+
return self.class_callback_actions
|
6
|
+
#return self.instance_callbacks if show_classvars == false
|
7
|
+
#return self.class_callback_actions + self.instance_callback_actions if show_classvars == true
|
8
|
+
end
|
9
|
+
|
10
|
+
#Really don't know what this does :p But it works!
|
11
|
+
#I'm really, really bad at metaprogramming
|
12
|
+
def callback_actions_for(method, type)
|
13
|
+
begin
|
14
|
+
return self.callback_actions[method] if type.nil?
|
15
|
+
return self.callback_actions[method][type] ||= []
|
16
|
+
rescue NoMethodError
|
17
|
+
return []
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def class_callback_actions
|
22
|
+
self.class.callback_actions
|
23
|
+
end
|
24
|
+
|
25
|
+
def trigger_callback_actions(method, type)
|
26
|
+
|
27
|
+
self.callback_actions_for(method, type).each do |callback|
|
28
|
+
if callback.block
|
29
|
+
return instance_eval(&callback.block)
|
30
|
+
else
|
31
|
+
callback.proc.call
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
#Should I return something?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# $Id$
|
2
|
+
|
3
|
+
require File.expand_path(
|
4
|
+
File.join(File.dirname(__FILE__), %w[.. lib callbacks]))
|
5
|
+
|
6
|
+
Spec::Runner.configure do |config|
|
7
|
+
# == Mock Framework
|
8
|
+
#
|
9
|
+
# RSpec uses it's own mocking framework by default. If you prefer to
|
10
|
+
# use mocha, flexmock or RR, uncomment the appropriate line:
|
11
|
+
#
|
12
|
+
# config.mock_with :mocha
|
13
|
+
# config.mock_with :flexmock
|
14
|
+
# config.mock_with :rr
|
15
|
+
end
|
16
|
+
|
17
|
+
# EOF
|
data/tasks/ann.rake
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# $Id$
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'bones/smtp_tls'
|
5
|
+
rescue LoadError
|
6
|
+
require 'net/smtp'
|
7
|
+
end
|
8
|
+
require 'time'
|
9
|
+
|
10
|
+
namespace :ann do
|
11
|
+
|
12
|
+
# A prerequisites task that all other tasks depend upon
|
13
|
+
task :prereqs
|
14
|
+
|
15
|
+
file PROJ.ann.file do
|
16
|
+
ann = PROJ.ann
|
17
|
+
puts "Generating #{ann.file}"
|
18
|
+
File.open(ann.file,'w') do |fd|
|
19
|
+
fd.puts("#{PROJ.name} version #{PROJ.version}")
|
20
|
+
fd.puts(" by #{Array(PROJ.authors).first}") if PROJ.authors
|
21
|
+
fd.puts(" #{PROJ.url}") if PROJ.url.valid?
|
22
|
+
fd.puts(" (the \"#{PROJ.release_name}\" release)") if PROJ.release_name
|
23
|
+
fd.puts
|
24
|
+
fd.puts("== DESCRIPTION")
|
25
|
+
fd.puts
|
26
|
+
fd.puts(PROJ.description)
|
27
|
+
fd.puts
|
28
|
+
fd.puts(PROJ.changes.sub(%r/^.*$/, '== CHANGES'))
|
29
|
+
fd.puts
|
30
|
+
ann.paragraphs.each do |p|
|
31
|
+
fd.puts "== #{p.upcase}"
|
32
|
+
fd.puts
|
33
|
+
fd.puts paragraphs_of(PROJ.readme_file, p).join("\n\n")
|
34
|
+
fd.puts
|
35
|
+
end
|
36
|
+
fd.puts ann.text if ann.text
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "Create an announcement file"
|
41
|
+
task :announcement => ['ann:prereqs', PROJ.ann.file]
|
42
|
+
|
43
|
+
desc "Send an email announcement"
|
44
|
+
task :email => ['ann:prereqs', PROJ.ann.file] do
|
45
|
+
ann = PROJ.ann
|
46
|
+
from = ann.email[:from] || PROJ.email
|
47
|
+
to = Array(ann.email[:to])
|
48
|
+
|
49
|
+
### build a mail header for RFC 822
|
50
|
+
rfc822msg = "From: #{from}\n"
|
51
|
+
rfc822msg << "To: #{to.join(',')}\n"
|
52
|
+
rfc822msg << "Subject: [ANN] #{PROJ.name} #{PROJ.version}"
|
53
|
+
rfc822msg << " (#{PROJ.release_name})" if PROJ.release_name
|
54
|
+
rfc822msg << "\n"
|
55
|
+
rfc822msg << "Date: #{Time.new.rfc822}\n"
|
56
|
+
rfc822msg << "Message-Id: "
|
57
|
+
rfc822msg << "<#{"%.8f" % Time.now.to_f}@#{ann.email[:domain]}>\n\n"
|
58
|
+
rfc822msg << File.read(ann.file)
|
59
|
+
|
60
|
+
params = [:server, :port, :domain, :acct, :passwd, :authtype].map do |key|
|
61
|
+
ann.email[key]
|
62
|
+
end
|
63
|
+
|
64
|
+
params[3] = PROJ.email if params[3].nil?
|
65
|
+
|
66
|
+
if params[4].nil?
|
67
|
+
STDOUT.write "Please enter your e-mail password (#{params[3]}): "
|
68
|
+
params[4] = STDIN.gets.chomp
|
69
|
+
end
|
70
|
+
|
71
|
+
### send email
|
72
|
+
Net::SMTP.start(*params) {|smtp| smtp.sendmail(rfc822msg, from, to)}
|
73
|
+
end
|
74
|
+
end # namespace :ann
|
75
|
+
|
76
|
+
desc 'Alias to ann:announcement'
|
77
|
+
task :ann => 'ann:announcement'
|
78
|
+
|
79
|
+
CLOBBER << PROJ.ann.file
|
80
|
+
|
81
|
+
# EOF
|