callbacks 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ == 0.0.1 / 2008-05-10
2
+
3
+ * First release
4
+ * API not yet stable
5
+ * Only supports adding hooks to classes not instances. Instances are planned for 0.2
@@ -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
@@ -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.
@@ -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
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(
4
+ File.join(File.dirname(__FILE__), '..', 'lib', 'callbacks'))
5
+
6
+ # Put your code here
7
+
8
+ # EOF
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,8 @@
1
+ # $Id$
2
+
3
+ require File.join(File.dirname(__FILE__), %w[spec_helper])
4
+
5
+ describe Callbacks do
6
+ end
7
+
8
+ # EOF
@@ -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
@@ -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