methodmissing-callback 1.0.0
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.
- data/README +42 -0
- data/Rakefile +69 -0
- data/bench/bench.rb +14 -0
- data/bench/pure_ruby_cb.rb +17 -0
- data/bench/raggi_bench.rb +23 -0
- data/callback.gemspec +26 -0
- data/ext/callback/callback.c +95 -0
- data/ext/callback/extconf.rb +5 -0
- data/lib/callback.rb +1 -0
- metadata +64 -0
data/README
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
Simple native Callback object for Ruby MRI
|
2
|
+
(c) 2009 Lourens Naudé (methodmissing), James Tucker (raggi) and coderrr
|
3
|
+
|
4
|
+
http://github.com/methodmissing/callback
|
5
|
+
|
6
|
+
This library works with Ruby 1.8 and 1.9 and is a more efficient
|
7
|
+
implementation of the following Ruby code :
|
8
|
+
|
9
|
+
class RubyCallback
|
10
|
+
def initialize(object = nil, method = :call, &b)
|
11
|
+
@object, @method = object, method
|
12
|
+
@object ||= b
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(*args)
|
16
|
+
@object.__send__(@method, *args)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module Kernel
|
21
|
+
private
|
22
|
+
def RubyCallback(object = nil, method = :call, &b)
|
23
|
+
RubyCallback.new(object, method, &b)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
Concept, ideas and API design James's - any pointers for better GC integration much appreciated.
|
28
|
+
|
29
|
+
Examples :
|
30
|
+
|
31
|
+
'hai'.callback(:gsub).call('h', 'b') #=> 'bai'
|
32
|
+
Callback( 'bai', :to_s ).call #=> 'hai'
|
33
|
+
Callback{ 'hai' }.call #=> 'hai'
|
34
|
+
Callback( 'bai', :gsub ).call( 'b', 'h' ) #=> 'hai'
|
35
|
+
|
36
|
+
To run the test suite:
|
37
|
+
|
38
|
+
rake
|
39
|
+
|
40
|
+
To run the benchmarks:
|
41
|
+
|
42
|
+
rake bench
|
data/Rakefile
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/clean'
|
4
|
+
|
5
|
+
CB_ROOT = 'ext/callback'
|
6
|
+
|
7
|
+
desc 'Default: test'
|
8
|
+
task :default => :test
|
9
|
+
|
10
|
+
desc 'Run callback tests.'
|
11
|
+
Rake::TestTask.new(:test) do |t|
|
12
|
+
t.libs = [CB_ROOT]
|
13
|
+
t.pattern = 'test/test_*.rb'
|
14
|
+
t.verbose = true
|
15
|
+
end
|
16
|
+
task :test => :build
|
17
|
+
|
18
|
+
namespace :build do
|
19
|
+
file "#{CB_ROOT}/callback.c"
|
20
|
+
file "#{CB_ROOT}/extconf.rb"
|
21
|
+
file "#{CB_ROOT}/Makefile" => %W(#{CB_ROOT}/callback.c #{CB_ROOT}/extconf.rb) do
|
22
|
+
Dir.chdir(CB_ROOT) do
|
23
|
+
ruby 'extconf.rb'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "generate makefile"
|
28
|
+
task :makefile => %W(#{CB_ROOT}/Makefile #{CB_ROOT}/callback.c)
|
29
|
+
|
30
|
+
dlext = Config::CONFIG['DLEXT']
|
31
|
+
file "#{CB_ROOT}/callback.#{dlext}" => %W(#{CB_ROOT}/Makefile #{CB_ROOT}/callback.c) do
|
32
|
+
Dir.chdir(CB_ROOT) do
|
33
|
+
sh 'make' # TODO - is there a config for which make somewhere?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "compile callback extension"
|
38
|
+
task :compile => "#{CB_ROOT}/callback.#{dlext}"
|
39
|
+
|
40
|
+
task :clean do
|
41
|
+
Dir.chdir(CB_ROOT) do
|
42
|
+
sh 'make clean'
|
43
|
+
end if File.exists?("#{CB_ROOT}/Makefile")
|
44
|
+
end
|
45
|
+
|
46
|
+
CLEAN.include("#{CB_ROOT}/Makefile")
|
47
|
+
CLEAN.include("#{CB_ROOT}/callback.#{dlext}")
|
48
|
+
end
|
49
|
+
|
50
|
+
task :clean => %w(build:clean)
|
51
|
+
|
52
|
+
desc "compile"
|
53
|
+
task :build => %w(build:compile)
|
54
|
+
|
55
|
+
task :install do |t|
|
56
|
+
Dir.chdir(CB_ROOT) do
|
57
|
+
sh 'sudo make install'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
desc "clean build install"
|
62
|
+
task :setup => %w(clean build install)
|
63
|
+
|
64
|
+
desc "run benchmarks"
|
65
|
+
task :bench do |t|
|
66
|
+
ruby "bench/bench.rb"
|
67
|
+
ruby "bench/raggi_bench.rb"
|
68
|
+
end
|
69
|
+
task :bench => :build
|
data/bench/bench.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "benchmark"
|
2
|
+
$:.unshift "."
|
3
|
+
require File.dirname(__FILE__) + "/../ext/callback/callback"
|
4
|
+
require File.dirname(__FILE__) + "/pure_ruby_cb"
|
5
|
+
|
6
|
+
TESTS = 10_000
|
7
|
+
Benchmark.bmbm do |results|
|
8
|
+
results.report("Callback{ 'hai' }.call") { TESTS.times { Callback{ 'hai' }.call } }
|
9
|
+
results.report("RubyCallback{ 'hai' }.call") { TESTS.times { RubyCallback{ 'hai' }.call } }
|
10
|
+
results.report("Callback( 'bai', :to_s ).call") { TESTS.times { Callback( 'bai', :to_s ).call } }
|
11
|
+
results.report("RubyCallback( 'bai', :to_s ).call") { TESTS.times { RubyCallback( 'bai', :to_s ).call } }
|
12
|
+
results.report("Callback( 'bai', :gsub ).call( 'b', 'h' )") { TESTS.times { Callback( 'bai', :gsub ).call( 'b', 'h' ) } }
|
13
|
+
results.report("RubyCallback( 'bai', :gsub ).call( 'b', 'h' )") { TESTS.times { RubyCallback( 'bai', :gsub).call( 'b', 'h' ) } }
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class RubyCallback
|
2
|
+
def initialize(object = nil, method = :call, &b)
|
3
|
+
@object, @method = object, method
|
4
|
+
@object ||= b
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(*args)
|
8
|
+
@object.__send__(@method, *args)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Kernel
|
13
|
+
private
|
14
|
+
def RubyCallback(object = nil, method = :call, &b)
|
15
|
+
RubyCallback.new(object, method, &b)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "benchmark"
|
2
|
+
$:.unshift "."
|
3
|
+
require File.dirname(__FILE__) + "/../ext/callback/callback"
|
4
|
+
require File.dirname(__FILE__) + "/pure_ruby_cb"
|
5
|
+
|
6
|
+
@s = 'foo'
|
7
|
+
def me; @s.to_s; end
|
8
|
+
ld = lambda { @s.to_s }
|
9
|
+
cb = Callback &ld
|
10
|
+
c2 = Callback( @s, :to_s )
|
11
|
+
rb = RubyCallback { @s.to_s }
|
12
|
+
farm = @s.callback(:to_s)
|
13
|
+
|
14
|
+
tests = 100_000
|
15
|
+
puts "# Calling"
|
16
|
+
Benchmark.bmbm do |results|
|
17
|
+
results.report("methback:") { tests.times { me } }
|
18
|
+
results.report("lambdabk:") { tests.times { ld.call } }
|
19
|
+
results.report("callback:") { tests.times { cb.call } }
|
20
|
+
results.report("callbak2:") { tests.times { c2.call } }
|
21
|
+
results.report("rubyback:") { tests.times { rb.call } }
|
22
|
+
results.report("farm:") { tests.times { farm.call } }
|
23
|
+
end
|
data/callback.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "callback"
|
3
|
+
s.version = "1.0.0"
|
4
|
+
s.date = "2009-08-22"
|
5
|
+
s.summary = "Native MRI callback"
|
6
|
+
s.email = "lourens@methodmissing.com"
|
7
|
+
s.homepage = "http://github.com/methodmissing/callback"
|
8
|
+
s.description = "Simple native Callback object for Ruby MRI (1.8.{6,7} and 1.9.2)"
|
9
|
+
s.has_rdoc = true
|
10
|
+
s.authors = ["Lourens Naudé (methodmissing)","James Tucker (raggi)"]
|
11
|
+
s.platform = Gem::Platform::RUBY
|
12
|
+
s.files = %w[
|
13
|
+
README
|
14
|
+
Rakefile
|
15
|
+
bench/bench.rb
|
16
|
+
bench/pure_ruby_cb.rb
|
17
|
+
bench/raggi_bench.rb
|
18
|
+
ext/callback/extconf.rb
|
19
|
+
ext/callback/callback.c
|
20
|
+
lib/callback.rb
|
21
|
+
callback.gemspec
|
22
|
+
] + Dir.glob('test/*')
|
23
|
+
s.rdoc_options = ["--main", "README"]
|
24
|
+
s.extra_rdoc_files = ["README"]
|
25
|
+
s.extensions << "ext/callback/extconf.rb"
|
26
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
|
3
|
+
typedef struct {
|
4
|
+
VALUE object;
|
5
|
+
ID method;
|
6
|
+
} RCallback;
|
7
|
+
|
8
|
+
#define GetCallbackStruct(obj) (Check_Type(obj, T_DATA), (RCallback*)DATA_PTR(obj))
|
9
|
+
|
10
|
+
VALUE rb_cCallback;
|
11
|
+
static ID id_call;
|
12
|
+
|
13
|
+
static void mark_callback(RCallback* cb)
|
14
|
+
{
|
15
|
+
rb_gc_mark(cb->object);
|
16
|
+
}
|
17
|
+
|
18
|
+
static void free_callback(RCallback* cb)
|
19
|
+
{
|
20
|
+
xfree(cb);
|
21
|
+
}
|
22
|
+
|
23
|
+
static VALUE callback_alloc _((VALUE));
|
24
|
+
static VALUE
|
25
|
+
callback_alloc( VALUE klass )
|
26
|
+
{
|
27
|
+
VALUE cb;
|
28
|
+
RCallback* cbs;
|
29
|
+
cb = Data_Make_Struct(klass, RCallback, mark_callback, free_callback, cbs);
|
30
|
+
cbs->object = Qnil;
|
31
|
+
cbs->method = 0;
|
32
|
+
|
33
|
+
return cb;
|
34
|
+
}
|
35
|
+
|
36
|
+
static VALUE
|
37
|
+
rb_callback_new()
|
38
|
+
{
|
39
|
+
return callback_alloc(rb_cCallback);
|
40
|
+
}
|
41
|
+
|
42
|
+
static VALUE rb_callback_initialize( int argc, VALUE *argv, VALUE cb )
|
43
|
+
{
|
44
|
+
VALUE object;
|
45
|
+
VALUE method;
|
46
|
+
RCallback* cbs = GetCallbackStruct(cb);
|
47
|
+
|
48
|
+
if (rb_block_given_p()) {
|
49
|
+
if (argc == 1) rb_raise(rb_eArgError, "wrong number of arguments");
|
50
|
+
cbs->object = rb_block_proc();
|
51
|
+
cbs->method = id_call;
|
52
|
+
}else {
|
53
|
+
rb_scan_args(argc, argv, "02", &object, &method);
|
54
|
+
cbs->object = object;
|
55
|
+
cbs->method = rb_to_id(method);
|
56
|
+
}
|
57
|
+
|
58
|
+
OBJ_FREEZE(cb);
|
59
|
+
return cb;
|
60
|
+
}
|
61
|
+
|
62
|
+
static VALUE rb_callback_call( VALUE cb, VALUE args )
|
63
|
+
{
|
64
|
+
RCallback* cbs = GetCallbackStruct(cb);
|
65
|
+
return rb_apply(cbs->object, cbs->method, args);
|
66
|
+
}
|
67
|
+
|
68
|
+
static VALUE rb_f_callback( int argc, VALUE *argv )
|
69
|
+
{
|
70
|
+
return rb_callback_initialize( argc, argv, rb_callback_new() );
|
71
|
+
}
|
72
|
+
|
73
|
+
static VALUE
|
74
|
+
rb_obj_callback( VALUE obj, VALUE mid)
|
75
|
+
{
|
76
|
+
VALUE args[2];
|
77
|
+
args[0] = obj;
|
78
|
+
args[1] = mid;
|
79
|
+
return rb_f_callback( 2, (VALUE *)args );
|
80
|
+
}
|
81
|
+
|
82
|
+
void
|
83
|
+
Init_callback()
|
84
|
+
{
|
85
|
+
id_call = rb_intern("call");
|
86
|
+
|
87
|
+
rb_cCallback = rb_define_class("Callback", rb_cObject);
|
88
|
+
rb_define_alloc_func(rb_cCallback, callback_alloc);
|
89
|
+
|
90
|
+
rb_define_method(rb_cCallback,"initialize", rb_callback_initialize, -1);
|
91
|
+
rb_define_method(rb_cCallback,"call", rb_callback_call, -2);
|
92
|
+
|
93
|
+
rb_define_global_function("Callback", rb_f_callback, -1);
|
94
|
+
rb_define_method(rb_mKernel, "callback", rb_obj_callback, 1);
|
95
|
+
}
|
data/lib/callback.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# stubbed
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: methodmissing-callback
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- "Lourens Naud\xC3\xA9 (methodmissing)"
|
8
|
+
- James Tucker (raggi)
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2009-08-22 00:00:00 -07:00
|
14
|
+
default_executable:
|
15
|
+
dependencies: []
|
16
|
+
|
17
|
+
description: Simple native Callback object for Ruby MRI (1.8.{6,7} and 1.9.2)
|
18
|
+
email: lourens@methodmissing.com
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions:
|
22
|
+
- ext/callback/extconf.rb
|
23
|
+
extra_rdoc_files:
|
24
|
+
- README
|
25
|
+
files:
|
26
|
+
- README
|
27
|
+
- Rakefile
|
28
|
+
- bench/bench.rb
|
29
|
+
- bench/pure_ruby_cb.rb
|
30
|
+
- bench/raggi_bench.rb
|
31
|
+
- ext/callback/extconf.rb
|
32
|
+
- ext/callback/callback.c
|
33
|
+
- lib/callback.rb
|
34
|
+
- callback.gemspec
|
35
|
+
has_rdoc: true
|
36
|
+
homepage: http://github.com/methodmissing/callback
|
37
|
+
licenses:
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options:
|
40
|
+
- --main
|
41
|
+
- README
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: "0"
|
49
|
+
version:
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
requirements: []
|
57
|
+
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 1.3.5
|
60
|
+
signing_key:
|
61
|
+
specification_version: 2
|
62
|
+
summary: Native MRI callback
|
63
|
+
test_files: []
|
64
|
+
|