dup_eval 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +47 -0
- data/dup_eval.gemspec +17 -0
- data/lib/dup_eval.rb +85 -0
- data/test/dup_eval_test.rb +89 -0
- metadata +65 -0
data/README.markdown
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
Dup\_eval
|
2
|
+
========
|
3
|
+
|
4
|
+
This is an alternative to \_why's mix\_eval C extension. It provides identical functionality to mix\_eval as well as the following extra
|
5
|
+
functionality:
|
6
|
+
* Ability to mix in Objects/Classes (using Object2module)
|
7
|
+
* Thread-safety
|
8
|
+
|
9
|
+
Dup\_eval is based on coderrr's idea for dupping the binding of a block (http://coderrr.wordpress.com/2008/11/14/making-mixico-thread-safe/)
|
10
|
+
|
11
|
+
NOTE:
|
12
|
+
Dup\_eval is still in proof of concept stage, use at own risk!
|
13
|
+
|
14
|
+
Example use:
|
15
|
+
===========
|
16
|
+
|
17
|
+
#create our object
|
18
|
+
o = Object.new
|
19
|
+
|
20
|
+
#give it a method
|
21
|
+
class << o
|
22
|
+
def hello; print "Hello! "; end
|
23
|
+
end
|
24
|
+
|
25
|
+
#create a method in the current binding
|
26
|
+
def goodbye; puts "Goodbye!"; end
|
27
|
+
|
28
|
+
o.dup_eval { hello; goodbye } #=> "Hello! Goodbye!"
|
29
|
+
|
30
|
+
From above, both the methods of the object itself and the binding of the block are available to the block.
|
31
|
+
|
32
|
+
#we can also choose which objects we want to eval the block with respect to (we can have more than one)
|
33
|
+
o1 = Object.new
|
34
|
+
class << o1; ...define methods here... end
|
35
|
+
|
36
|
+
o2 = Object.new
|
37
|
+
class << o2; ...define methods here... end
|
38
|
+
|
39
|
+
o3 = Object.new
|
40
|
+
class << o3; ...define methods here... end
|
41
|
+
|
42
|
+
#create a method in the current binding
|
43
|
+
def goodbye; puts "Goodbye!"; end
|
44
|
+
|
45
|
+
o1.dup_eval_with(o1, o2, o3) { o1_method; o2_method; o3_method; goodbye }
|
46
|
+
|
47
|
+
As shown above we can have the block access methods in many objects (as many as we want). The objects may also be either Objects, Classes, or Modules.
|
data/dup_eval.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "dup_eval"
|
3
|
+
s.version = "0.1.0"
|
4
|
+
s.summary = "souped up version of instance_eval in the vein of mix_eval"
|
5
|
+
s.email = "jrmair@gmail.com"
|
6
|
+
s.homepage = "http://banisterfiend.wordpress.com"
|
7
|
+
s.description = "see http://github.com/why/mixico"
|
8
|
+
s.has_rdoc = false
|
9
|
+
s.authors = ["banister", "coderrr"]
|
10
|
+
s.files = [
|
11
|
+
"README.markdown",
|
12
|
+
"dup_eval.gemspec",
|
13
|
+
"lib/dup_eval.rb"
|
14
|
+
]
|
15
|
+
s.test_files = ["test/dup_eval_test.rb"]
|
16
|
+
s.add_dependency("RubyInline", [">= 3.6.7"])
|
17
|
+
end
|
data/lib/dup_eval.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'inline'
|
3
|
+
require 'object2module'
|
4
|
+
|
5
|
+
class Object
|
6
|
+
inline do |builder|
|
7
|
+
builder.prefix %{
|
8
|
+
#define KLASS_OF(o) RCLASS(RBASIC(o)->klass)
|
9
|
+
}
|
10
|
+
|
11
|
+
builder.c %{
|
12
|
+
void redirect_tbls(VALUE obj) {
|
13
|
+
unsigned long orig_iv_tbl, orig_m_tbl;
|
14
|
+
orig_iv_tbl = (unsigned long)ROBJECT(self)->iv_tbl;
|
15
|
+
orig_m_tbl = (unsigned long)KLASS_OF(self)->m_tbl;
|
16
|
+
ROBJECT(self)->iv_tbl = ROBJECT(obj)->iv_tbl;
|
17
|
+
KLASS_OF(self)->m_tbl = KLASS_OF(obj)->m_tbl;
|
18
|
+
rb_iv_set(self, "__orig_m_tbl__", orig_m_tbl);
|
19
|
+
rb_iv_set(self, "__orig_iv_tbl__", orig_iv_tbl);
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
#restore needed, or else GC will crash
|
24
|
+
builder.c %{
|
25
|
+
void restore_tbls() {
|
26
|
+
KLASS_OF(self)->m_tbl = (struct st_table *)rb_iv_get(self, "__orig_m_tbl__");
|
27
|
+
ROBJECT(self)->iv_tbl = (struct st_table *)rb_iv_get(self, "__orig_iv_tbl__");
|
28
|
+
}
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
class Class
|
35
|
+
inline do |builder|
|
36
|
+
builder.c %{
|
37
|
+
void redirect_tbls(VALUE class) {
|
38
|
+
unsigned long orig_iv_tbl, orig_m_tbl;
|
39
|
+
orig_iv_tbl = (unsigned long)RCLASS(self)->iv_tbl;
|
40
|
+
orig_m_tbl = (unsigned long)RCLASS(self)->m_tbl;
|
41
|
+
RCLASS(self)->iv_tbl = RCLASS(class)->iv_tbl;
|
42
|
+
RCLASS(self)->m_tbl = RCLASS(class)->m_tbl;
|
43
|
+
rb_iv_set(self, "__orig_iv_tbl__", orig_iv_tbl);
|
44
|
+
rb_iv_set(self, "__orig_m_tbl__", orig_m_tbl);
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
#restore needed, or else GC will crash
|
49
|
+
builder.c %{
|
50
|
+
void restore_tbls() {
|
51
|
+
RCLASS(self)->m_tbl = (struct st_table *)rb_iv_get(self, "__orig_m_tbl__");
|
52
|
+
RCLASS(self)->iv_tbl = (struct st_table *)rb_iv_get(self, "__orig_iv_tbl__");
|
53
|
+
}
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Proc
|
59
|
+
def _context
|
60
|
+
eval('self', self)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class Object
|
65
|
+
def dup_eval(*mods, &blk)
|
66
|
+
#default value is self
|
67
|
+
mods = self if mods.empty?
|
68
|
+
|
69
|
+
duped_context = blk._context.dup
|
70
|
+
#make sure the singleton class is in existence
|
71
|
+
class << duped_context; self; end
|
72
|
+
|
73
|
+
duped_context.redirect_tbls(blk._context)
|
74
|
+
|
75
|
+
duped_context.gen_extend(*mods)
|
76
|
+
begin
|
77
|
+
m = duped_context.is_a?(Module) ? :class_eval : :instance_eval
|
78
|
+
duped_context.send(m, &blk)
|
79
|
+
ensure
|
80
|
+
duped_context.restore_tbls
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
alias_method :dup_eval_with, :dup_eval
|
85
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + "/../lib/dup_eval"
|
4
|
+
|
5
|
+
class Dup_EvalTest < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
@p = Proc.new do
|
8
|
+
assert_equal :bink, bink
|
9
|
+
assert_equal :local_method, local_method
|
10
|
+
end
|
11
|
+
|
12
|
+
@p2 = Proc.new do
|
13
|
+
assert_equal :bink, bink
|
14
|
+
assert_equal :bunk, bunk
|
15
|
+
assert_equal :local_method, local_method
|
16
|
+
end
|
17
|
+
|
18
|
+
@m = Module.new do
|
19
|
+
def bink
|
20
|
+
:bink
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
@o = Object.new
|
25
|
+
class << @o
|
26
|
+
def bink
|
27
|
+
:bink
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
@o2 = Object.new
|
32
|
+
class <<@o2
|
33
|
+
def bunk
|
34
|
+
:bunk
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
@c = Class.new
|
39
|
+
@c.class_eval do
|
40
|
+
def bink
|
41
|
+
:bink
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
#a method in the local binding
|
47
|
+
#used for testing
|
48
|
+
def local_method
|
49
|
+
:local_method
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_mixing_in_self_when_object
|
53
|
+
o = Object.new
|
54
|
+
class << o
|
55
|
+
def bink
|
56
|
+
:bink
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
o.dup_eval(&@p)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_mixing_in_self_when_class
|
64
|
+
c = Class.new
|
65
|
+
c.class_eval do
|
66
|
+
def bink
|
67
|
+
:bink
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
c.dup_eval(&@p)
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_mixing_in_module
|
75
|
+
Module.new.dup_eval_with(@m, &@p)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_mixing_in_object
|
79
|
+
Module.new.dup_eval_with(@o, &@p)
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_mixing_in_class
|
83
|
+
Module.new.dup_eval_with(@c, &@p)
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_mixing_in_multi_objects
|
87
|
+
Module.new.dup_eval_with(@o, @o2, &@p2)
|
88
|
+
end
|
89
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dup_eval
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- banister
|
8
|
+
- coderrr
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2008-12-17 00:00:00 +13:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: RubyInline
|
18
|
+
type: :runtime
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 3.6.7
|
25
|
+
version:
|
26
|
+
description: see http://github.com/why/mixico
|
27
|
+
email: jrmair@gmail.com
|
28
|
+
executables: []
|
29
|
+
|
30
|
+
extensions: []
|
31
|
+
|
32
|
+
extra_rdoc_files: []
|
33
|
+
|
34
|
+
files:
|
35
|
+
- README.markdown
|
36
|
+
- dup_eval.gemspec
|
37
|
+
- lib/dup_eval.rb
|
38
|
+
has_rdoc: false
|
39
|
+
homepage: http://banisterfiend.wordpress.com
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
50
|
+
version:
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
requirements: []
|
58
|
+
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.2.0
|
61
|
+
signing_key:
|
62
|
+
specification_version: 2
|
63
|
+
summary: souped up version of instance_eval in the vein of mix_eval
|
64
|
+
test_files:
|
65
|
+
- test/dup_eval_test.rb
|