gen_eval 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ /* Copyright (c) 2007 Scott Lembcke
2
+ *
3
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ * of this software and associated documentation files (the "Software"), to deal
5
+ * in the Software without restriction, including without limitation the rights
6
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ * copies of the Software, and to permit persons to whom the Software is
8
+ * furnished to do so, subject to the following conditions:
9
+ *
10
+ * The above copyright notice and this permission notice shall be included in
11
+ * all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ * SOFTWARE.
20
+ */
data/README.rdoc ADDED
@@ -0,0 +1,24 @@
1
+ gen_eval is a homebrew version of instance_eval that eliminates the most annoying aspect of instance_eval.
2
+
3
+ For example:
4
+
5
+ @x = 20
6
+ @y = 30
7
+ my_image.instance_eval {
8
+ circle @x, @y, 20
9
+ }
10
+
11
+ => error @x not initialized
12
+
13
+ In the above code you meant to access the local @x yet instance_eval looks up @x in the receiver (my_image).
14
+
15
+ gen_eval, on the other hand, works as you'd expect, it looks up @x in the caller-context yet still invokes methods in the receiver-context.
16
+
17
+ This means we can now do things like this:
18
+
19
+ @x = 20
20
+ @y = 30
21
+ image.gen_eval {
22
+ pixel @x, @y
23
+ circle @x, @y, 20
24
+ }
data/Rakefile ADDED
@@ -0,0 +1,77 @@
1
+ require 'rake/clean'
2
+ #require 'rake/extensiontask'
3
+ require 'rake/gempackagetask'
4
+ require 'rake/rdoctask'
5
+ require 'lib/gen_eval/version'
6
+
7
+ dlext = Config::CONFIG['DLEXT']
8
+ direc = File.dirname(__FILE__)
9
+
10
+ CLEAN.include("ext/**/*.#{dlext}", "ext/**/.log", "ext/**/.o", "ext/**/*~", "ext/**/*#*", "ext/**/.obj", "ext/**/.def", "ext/**/.pdb")
11
+ CLOBBER.include("**/*.#{dlext}", "**/*~", "**/*#*", "**/*.log", "**/*.o", "doc/**")
12
+
13
+ spec = Gem::Specification.new do |s|
14
+ s.name = "gen_eval"
15
+ s.summary = "a strange new breed of instance_eval"
16
+ s.description = s.summary
17
+ s.version = GenEval::VERSION
18
+ s.author = "John Mair (banisterfiend)"
19
+ s.email = 'jrmair@gmail.com'
20
+ s.date = Time.now.strftime '%Y-%m-%d'
21
+ s.require_path = 'lib'
22
+ s.homepage = "http://banisterfiend.wordpress.com"
23
+ s.platform = Gem::Platform::RUBY #'i386-mswin32'
24
+ s.extensions = FileList["ext/**/extconf.rb"]
25
+ s.has_rdoc = true
26
+ s.add_dependency("object2module",">=0.3.0")
27
+ s.files = ["Rakefile", "README.rdoc", "LICENSE", "lib/gen_eval.rb"] + FileList["ext/**/extconf.rb", "ext/**/*.h", "ext/**/*.c", "lib/gen_eval/version.rb"].to_a
28
+ end
29
+
30
+ Rake::GemPackageTask.new(spec) do |pkg|
31
+ pkg.need_zip = false
32
+ pkg.need_tar = false
33
+ end
34
+
35
+ ext_direc = "#{direc}/ext/gen_eval"
36
+
37
+ file "#{ext_direc}/Makefile" => "#{ext_direc}/extconf.rb" do
38
+ chdir(ext_direc) do
39
+ sh 'ruby extconf.rb'
40
+ end
41
+ end
42
+
43
+ directory "#{direc}/lib/1.9"
44
+ directory "#{direc}/lib/1.8"
45
+
46
+ file "#{ext_direc}/gen_eval.#{dlext}" => ["#{ext_direc}/Makefile", "#{direc}/lib/1.9", "#{direc}/lib/1.8"] do
47
+ chdir(ext_direc) do
48
+ sh 'make'
49
+
50
+ if RUBY_VERSION =~ /1.9/
51
+ cp "gen_eval.#{dlext}", "#[direc}/lib/1.9/"
52
+ puts "built gen_eval.#{dlext} and copied to #{direc}/lib/1.9"
53
+ elsif RUBY_VERSION =~ /1.8/
54
+ cp "gen_eval.#{dlext}", "#[direc}/lib/1.8/"
55
+ puts "built gen_eval.#{dlext} and copied to #{direc}/lib/1.8"
56
+ else
57
+ raise "Error, Rakefile only supports Ruby 1.8 and 1.9"
58
+ end
59
+ end
60
+ end
61
+
62
+ task :compile => "#{ext_direc}/gen_eval.#{dlext}"
63
+
64
+
65
+
66
+
67
+ # Rake::ExtensionTask.new('mult', spec) do |ext|
68
+ # ext.config_script = 'extconf.rb'
69
+ # ext.cross_compile = true
70
+ # ext.cross_platform = 'i386-mswin32'
71
+ # end
72
+
73
+ Rake::RDocTask.new do |rd|
74
+ rd.main = "README.rdoc"
75
+ rd.rdoc_files.include("README.rdoc", "lib/gen_eval.rb")
76
+ end
77
+
@@ -0,0 +1,18 @@
1
+ /* contains basic macros to facilitate ruby 1.8 and ruby 1.9 compatibility */
2
+
3
+ #ifndef GUARD_COMPAT_H
4
+ #define GUARD_COMPAT_H
5
+
6
+ #include <ruby.h>
7
+
8
+ /* macros for backwards compatibility with 1.8 */
9
+ #ifndef RUBY_19
10
+ # define RCLASS_M_TBL(c) (RCLASS(c)->m_tbl)
11
+ # define RCLASS_SUPER(c) (RCLASS(c)->super)
12
+ # define RCLASS_IV_TBL(c) (RCLASS(c)->iv_tbl)
13
+ #endif
14
+
15
+ /* a useful macro. cannot use ordinary CLASS_OF as it does not return an lvalue */
16
+ #define KLASS_OF(c) (RBASIC(c)->klass)
17
+
18
+ #endif
@@ -0,0 +1,9 @@
1
+ require 'mkmf'
2
+
3
+ # 1.9 compatibility
4
+ $CFLAGS += " -DRUBY_19" if RUBY_VERSION =~ /1.9/
5
+
6
+ # let's use c99
7
+ $CFLAGS += " -std=c99"
8
+
9
+ create_makefile('gen_eval')
@@ -0,0 +1,192 @@
1
+ /* gen_eval.c */
2
+ /* (C) John Mair 2009
3
+ * This program is distributed under the terms of the MIT License
4
+ * */
5
+
6
+ #include <ruby.h>
7
+ #include "compat.h"
8
+
9
+ VALUE
10
+ retrieve_hidden_self(VALUE duped_context)
11
+ {
12
+ VALUE thread_id, unique_name, hidden_self;
13
+
14
+ /* retrieve hidden self (if it exists) */
15
+ thread_id = rb_funcall(rb_obj_id(rb_thread_current()), rb_intern("to_s"), 0);
16
+ unique_name = rb_str_plus(rb_str_new2("__hidden_self__"), thread_id);
17
+ hidden_self = rb_ivar_get(duped_context, rb_to_id(unique_name));
18
+
19
+ return hidden_self;
20
+ }
21
+
22
+ VALUE
23
+ remove_hidden_self(VALUE duped_context)
24
+ {
25
+ /* retrieve hidden self (if it exists) */
26
+ VALUE thread_id, unique_name;
27
+
28
+ thread_id = rb_funcall(rb_obj_id(rb_thread_current()), rb_intern("to_s"), 0);
29
+ unique_name = rb_str_plus(rb_str_new2("__hidden_self__"), thread_id);
30
+
31
+ rb_obj_remove_instance_variable(duped_context, unique_name);
32
+
33
+ return Qnil;
34
+ }
35
+
36
+ VALUE
37
+ set_hidden_self(VALUE duped_context, VALUE hidden_self)
38
+ {
39
+ VALUE thread_id, unique_name;
40
+
41
+ /* generate a unique (thread safe) name for the hidden self */
42
+ thread_id = rb_funcall(rb_obj_id(rb_thread_current()), rb_intern("to_s"), 0);
43
+ unique_name = rb_str_plus(rb_str_new2("__hidden_self__"), thread_id);
44
+
45
+ /* store self in hidden var in duped context */
46
+ rb_ivar_set(duped_context, rb_to_id(unique_name), hidden_self);
47
+
48
+ return Qnil;
49
+ }
50
+
51
+ static void
52
+ save_m_tbl(VALUE klass)
53
+ {
54
+ rb_iv_set(klass, "__saved_m_tbl__", (VALUE)RCLASS_M_TBL(klass));
55
+ }
56
+
57
+ static void
58
+ restore_m_tbl(VALUE klass)
59
+ {
60
+ RCLASS_M_TBL(klass) = (struct st_table *)rb_iv_get(klass, "__saved_m_tbl__");
61
+ }
62
+
63
+ /* we do not want any ROBJECT_EMBED objects, so ensure we have > 3 ivars */
64
+ static void
65
+ force_iv_tbl(VALUE obj)
66
+ {
67
+ rb_iv_set(obj, "__force_iv_tbl_1__", Qtrue);
68
+ rb_iv_set(obj, "__force_iv_tbl_2__", Qtrue);
69
+ rb_iv_set(obj, "__force_iv_tbl_3__", Qtrue);
70
+ rb_iv_set(obj, "__force_iv_tbl_4__", Qtrue);
71
+ }
72
+
73
+ #ifdef RUBY_19
74
+ static void
75
+ redirect_iv_for_object(VALUE obj, VALUE dest)
76
+ {
77
+ if(TYPE(obj) != T_OBJECT)
78
+ rb_raise(rb_eArgError, "must provide a T_OBJECT");
79
+
80
+ /* ensure ivars are stored on the heap and not embedded */
81
+ force_iv_tbl(obj);
82
+
83
+ if (!(RBASIC(dest)->flags & ROBJECT_EMBED) && ROBJECT_IVPTR(dest)) {
84
+ rb_raise(rb_eArgError, "im sorry gen_eval does not yet work with this type of ROBJECT");
85
+ }
86
+ if (RBASIC(obj)->flags & ROBJECT_EMBED) {
87
+ rb_raise(rb_eArgError, "im sorry gen_eval does not yet work with R_OBJECT_EMBED types");
88
+ }
89
+ else {
90
+ ROBJECT(dest)->as.heap.ivptr = ROBJECT(obj)->as.heap.ivptr;
91
+ ROBJECT(dest)->as.heap.numiv = ROBJECT(obj)->as.heap.numiv;
92
+ ROBJECT(dest)->as.heap.iv_index_tbl = ROBJECT(obj)->as.heap.iv_index_tbl;
93
+ RBASIC(dest)->flags &= ~ROBJECT_EMBED;
94
+ }
95
+ }
96
+
97
+ static void
98
+ release_iv_for_object(VALUE obj)
99
+ {
100
+ if(TYPE(obj) != T_OBJECT)
101
+ rb_raise(rb_eArgError, "must provide a T_OBJECT");
102
+
103
+ ROBJECT(obj)->as.heap.ivptr = (void *) 0;
104
+ ROBJECT(obj)->as.heap.numiv = 0;
105
+ ROBJECT(obj)->as.heap.iv_index_tbl = (void *) 0;
106
+ RBASIC(obj)->flags &= ~ROBJECT_EMBED;
107
+ }
108
+ #endif
109
+ /** end of Ruby 1.9 funcs **/
110
+
111
+ VALUE
112
+ rb_mirror_object(VALUE context)
113
+ {
114
+ VALUE duped_context;
115
+
116
+ #ifdef RUBY_19
117
+ if(TYPE(context) == T_OBJECT)
118
+ duped_context = rb_funcall(rb_cObject, rb_intern("new"), 0);
119
+ else
120
+ duped_context = rb_funcall(rb_cClass, rb_intern("new"), 0);
121
+
122
+ #else
123
+ duped_context = rb_funcall(rb_cClass, rb_intern("new"), 0);
124
+ #endif
125
+
126
+ /* the duped_context shares the context's iv_tbl.
127
+ 2 cases: (1) external iv_tbl, (2) local iv_tbl
128
+
129
+ NOTE: we do not need to save original iv_tbl before replacing it, a brand new Class
130
+ instance does not yet have an iv_tbl (the pointer is set to 0)
131
+ */
132
+ if(FL_TEST(context, FL_EXIVAR))
133
+ RCLASS_IV_TBL(duped_context) = (struct st_table *) rb_generic_ivar_table(context);
134
+ else {
135
+ #ifdef RUBY_19
136
+ if(TYPE(context) == T_OBJECT)
137
+ redirect_iv_for_object(context, duped_context);
138
+ else {
139
+ save_m_tbl(duped_context);
140
+ RCLASS_M_TBL(duped_context) = (struct st_table *) RCLASS_M_TBL(context);
141
+ RCLASS_IV_TBL(duped_context) = (struct st_table *) RCLASS_IV_TBL(context);
142
+ }
143
+ #else
144
+ save_m_tbl(duped_context);
145
+ RCLASS_M_TBL(duped_context) = (struct st_table *) RCLASS_M_TBL(context);
146
+ RCLASS_IV_TBL(duped_context) = (struct st_table *) RCLASS_IV_TBL(context);
147
+ #endif
148
+
149
+
150
+ }
151
+
152
+ /* ensure singleton exists */
153
+ rb_singleton_class(context);
154
+
155
+ /* set up the class hierarchy for our dup_context */
156
+ KLASS_OF(duped_context) = rb_singleton_class_clone(context);
157
+
158
+ return duped_context;
159
+ }
160
+
161
+ VALUE
162
+ rb_unmirror_object(VALUE duped_context)
163
+ {
164
+ #ifdef RUBY_19
165
+ if(TYPE(duped_context) == T_OBJECT)
166
+ release_iv_for_object(duped_context);
167
+ else {
168
+ RCLASS_IV_TBL(duped_context) = (struct st_table *) 0;
169
+ restore_m_tbl(duped_context);
170
+ }
171
+ #else
172
+ RCLASS_IV_TBL(duped_context) = (struct st_table *) 0;
173
+ restore_m_tbl(duped_context);
174
+ #endif
175
+
176
+ return Qnil;
177
+ }
178
+
179
+ void
180
+ Init_gen_eval()
181
+ {
182
+ rb_define_method(rb_cObject, "__mirror__", rb_mirror_object, 0);
183
+ rb_define_method(rb_cObject, "__unmirror__", rb_unmirror_object, 0);
184
+ rb_define_method(rb_cObject, "__set_hidden_self__", set_hidden_self, 1);
185
+ rb_define_method(rb_cObject, "__retrieve_hidden_self__", retrieve_hidden_self, 0);
186
+ rb_define_method(rb_cObject, "__remove_hidden_self__", retrieve_hidden_self, 0);
187
+ }
188
+
189
+
190
+
191
+
192
+
@@ -0,0 +1,23 @@
1
+ /* gen_eval.h */
2
+
3
+ #ifndef GUARD_GEN_EVAL_H
4
+ #define GUARD_GEN_EVAL_H
5
+
6
+ #include <ruby.h>
7
+
8
+ VALUE retrieve_hidden_self(VALUE duped_context);
9
+ void set_hidden_self(VALUE duped_context, VALUE hidden_self);
10
+ VALUE remove_hidden_self(VALUE duped_context);
11
+ VALUE rb_mirror_object(VALUE context);
12
+ VALUE rb_unmirror_object(VALUE duped_context);
13
+
14
+
15
+
16
+ /* change self to hidden self if __hidden_self__ defined */
17
+ #define ADJUST_SELF(X) \
18
+ do { \
19
+ if(!NIL_P(retrieve_hidden_self((X)))) \
20
+ (X) = retrieve_hidden_self((X)); \
21
+ } while(0)
22
+
23
+ #endif
@@ -0,0 +1,3 @@
1
+ module GenEval
2
+ VERSION = "0.3.0"
3
+ end
data/lib/gen_eval.rb ADDED
@@ -0,0 +1,103 @@
1
+ # (C) John Mair 2009, under the MIT licence
2
+
3
+ require 'rbconfig'
4
+ require 'object2module'
5
+
6
+ direc = File.dirname(__FILE__)
7
+ dlext = Config::CONFIG['DLEXT']
8
+
9
+ begin
10
+ if RUBY_VERSION && RUBY_VERSION =~ /1.9/
11
+ require "#{direc}/1.9/gen_eval.#{dlext}"
12
+ else
13
+ require "#{direc}/1.8/gen_eval.#{dlext}"
14
+ end
15
+ rescue LoadError => e
16
+ require "#{direc}/gen_eval.#{dlext}"
17
+ end
18
+ require "#{direc}/gen_eval/version"
19
+
20
+ class Proc #:nodoc:
21
+ def __context__
22
+ eval('self', self.binding)
23
+ end
24
+ end
25
+
26
+
27
+ class Object
28
+
29
+ # call-seq:
30
+ # obj.gen_eval {| | block } => obj
31
+ #
32
+ # Works similarly to instance_eval except instance variables are
33
+ # not looked up in _obj_. All instance methods of +Klass+ are
34
+ # available but mutators must be wrapped in a +capture+ block.
35
+ #
36
+ # class Klass
37
+ # attr_reader :age
38
+ #
39
+ # def hello(name)
40
+ # "hello #{name}"
41
+ # end
42
+ #
43
+ # def set_age(age)
44
+ # capture {
45
+ # @age = age
46
+ # }
47
+ # end
48
+ # end
49
+ # k = Klass.new
50
+ # @name = "John"
51
+ # k.gen_eval { hello(@name) } #=> "hello John"
52
+ #
53
+ # k.gen_eval { set_age(21) }
54
+ # k.age #=> 21
55
+ def gen_eval(*objs, &block)
56
+ raise "block needed" if !block
57
+
58
+ objs = [self] if objs.empty?
59
+
60
+ raise "cannot gen_eval on Object" if objs.include?(Object)
61
+
62
+ mirror_context = block.__context__.__mirror__
63
+ mirror_context.gen_extend(*objs)
64
+ mirror_context.__set_hidden_self__(self)
65
+
66
+ begin
67
+ m = mirror_context.is_a?(Module) ? :module_eval : :instance_eval
68
+ result = mirror_context.send(m, &block)
69
+ ensure
70
+ mirror_context.__remove_hidden_self__
71
+ mirror_context.__unmirror__
72
+ end
73
+
74
+ result
75
+ end
76
+
77
+ alias_method :gen_eval_with, :gen_eval
78
+
79
+ # call-seq:
80
+ # capture {| | block } => result
81
+ #
82
+ # Used in conjunction with +gen_eval+. Causes code in block
83
+ # to be +instance_eval+'d with respect to the receiver of
84
+ # the +gen_eval+.
85
+ def capture(&block)
86
+ raise "block needed" if !block
87
+
88
+ hidden_self = self.__retrieve_hidden_self__
89
+
90
+ if hidden_self
91
+ result = hidden_self.instance_eval(&block)
92
+ else
93
+ result = yield
94
+ end
95
+
96
+ result
97
+ end
98
+
99
+ end
100
+
101
+
102
+
103
+
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gen_eval
3
+ version: !ruby/object:Gem::Version
4
+ hash: 19
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 3
9
+ - 0
10
+ version: 0.3.0
11
+ platform: ruby
12
+ authors:
13
+ - John Mair (banisterfiend)
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-07-31 00:00:00 +12:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: object2module
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 19
30
+ segments:
31
+ - 0
32
+ - 3
33
+ - 0
34
+ version: 0.3.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ description: a strange new breed of instance_eval
38
+ email: jrmair@gmail.com
39
+ executables: []
40
+
41
+ extensions:
42
+ - ext/gen_eval/extconf.rb
43
+ extra_rdoc_files: []
44
+
45
+ files:
46
+ - Rakefile
47
+ - README.rdoc
48
+ - LICENSE
49
+ - lib/gen_eval.rb
50
+ - ext/gen_eval/extconf.rb
51
+ - ext/gen_eval/compat.h
52
+ - ext/gen_eval/gen_eval.h
53
+ - ext/gen_eval/gen_eval.c
54
+ - lib/gen_eval/version.rb
55
+ has_rdoc: true
56
+ homepage: http://banisterfiend.wordpress.com
57
+ licenses: []
58
+
59
+ post_install_message:
60
+ rdoc_options: []
61
+
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ hash: 3
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ hash: 3
79
+ segments:
80
+ - 0
81
+ version: "0"
82
+ requirements: []
83
+
84
+ rubyforge_project:
85
+ rubygems_version: 1.3.7
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: a strange new breed of instance_eval
89
+ test_files: []
90
+