object2module 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,18 @@
1
+ # Object2module
2
+
3
+ - converts a Class (or the Singleton of an Object) to a Module
4
+ - Includes gen\_extend and gen\_include methods: generalizations
5
+ of Object#extend and Module#include that work with Objects and
6
+ Classes as well as Modules
7
+
8
+ How it works:
9
+
10
+ - First creates an IClass for the Class in question and sets the
11
+ T\_MODULE flag
12
+ - Recursively converts superclasses of the Class to IClasses
13
+ creating a modulified version of the Class's inheritance chain
14
+ - gen\_include/gen\_extend automatically call #to\_module on the
15
+ Class/Object before inclusion/extension.
16
+
17
+
18
+
data/Rakefile ADDED
@@ -0,0 +1,84 @@
1
+ require 'rake/clean'
2
+
3
+ OBJECT2MODULE_VERSION = "0.1.0"
4
+
5
+ $dlext = Config::CONFIG['DLEXT']
6
+
7
+ CLEAN.include("ext/*.#{$dlext}", "ext/*.log", "ext/*.o", "ext/*~", "ext/*#*", "ext/*.obj", "ext/*.def", "ext/*.pdb")
8
+ CLOBBER.include("**/*.#{$dlext}", "**/*~", "**/*#*", "**/*.log", "**/*.o", "doc/**")
9
+
10
+ $make_program = if RUBY_PLATFORM =~ /win/
11
+ "nmake"
12
+ else
13
+ "make"
14
+ end
15
+
16
+ task :default => [:build]
17
+
18
+ desc "Build Object2module"
19
+ task :build => :clean do
20
+ chdir("./ext/") do
21
+ ruby "extconf.rb"
22
+ sh "#{$make_program}"
23
+ cp "cobject2module.#{$dlext}", "../lib" , :verbose => true
24
+
25
+ if RUBY_PLATFORM =~ /mswin/
26
+ if RUBY_VERSION =~ /1.9/
27
+ File.rename("../lib/cobject2module.#{$dlext}",
28
+ "../lib/cobject2module.19.#{$dlext}")
29
+ else
30
+ File.rename("../lib/cobject2module.#{$dlext}",
31
+ "../lib/cobject2module.18.#{$dlext}")
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ require 'rake/gempackagetask'
38
+ specification = Gem::Specification.new do |s|
39
+ s.name = "object2module"
40
+ s.summary = "object2module enables ruby classes and objects to be used as modules"
41
+ s.version = OBJECT2MODULE_VERSION
42
+ s.date = Time.now.strftime '%Y-%m-%d'
43
+ s.author = "John Mair (banisterfiend)"
44
+ s.email = 'jrmair@gmail.com'
45
+ s.description = s.summary
46
+ s.require_path = 'lib'
47
+ s.homepage = "http://banisterfiend.wordpress.com"
48
+ s.has_rdoc = true
49
+ s.extra_rdoc_files = ["README.rdoc", "ext/object2module.c"]
50
+ s.rdoc_options << '--main' << 'README.rdoc'
51
+ s.files = ["Rakefile", "lib/object2module.rb", "README.rdoc"] +
52
+ FileList["ext/*.c", "ext/*.h", "ext/*.rb", "test/*.rb"].to_a
53
+
54
+ if RUBY_PLATFORM =~ /mswin/
55
+ s.platform = Gem::Platform::CURRENT
56
+ s.files += ["lib/cobject2module.18.so", "lib/cobject2module.19.so"]
57
+
58
+ else
59
+ s.platform = Gem::Platform::RUBY
60
+ s.extensions = ["ext/extconf.rb"]
61
+ end
62
+ end
63
+
64
+ # gem, rdoc, and test tasks below
65
+
66
+ Rake::GemPackageTask.new(specification) do |package|
67
+ package.need_zip = false
68
+ package.need_tar = false
69
+ end
70
+
71
+ require 'rake/rdoctask'
72
+ Rake::RDocTask.new do |rd|
73
+ rd.main = "README.rdoc"
74
+ rd.rdoc_files.include("README.rdoc", "ext/*.c")
75
+ end
76
+
77
+ require 'rake/testtask'
78
+ Rake::TestTask.new do |t|
79
+ t.test_files = FileList['test/test*.rb']
80
+ t.verbose = true
81
+ end
82
+
83
+
84
+
data/ext/compat.h ADDED
@@ -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
data/ext/extconf.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+
3
+ # 1.9 compatibility
4
+ $CPPFLAGS += " -DRUBY_19" if RUBY_VERSION =~ /1.9/
5
+
6
+ create_makefile('cobject2module')
@@ -0,0 +1,215 @@
1
+ /* object2module.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
+ #ifdef RUBY_19
10
+ # include <ruby/st.h>
11
+ #else
12
+ # include <st.h>
13
+ #endif
14
+
15
+ /* class creation. from class.c in 1.9.1 */
16
+ #ifdef RUBY_19
17
+ static VALUE
18
+ class_alloc(VALUE flags, VALUE klass)
19
+ {
20
+ rb_classext_t *ext = ALLOC(rb_classext_t);
21
+ NEWOBJ(obj, struct RClass);
22
+ OBJSETUP(obj, klass, flags);
23
+ obj->ptr = ext;
24
+ RCLASS_IV_TBL(obj) = 0;
25
+ RCLASS_M_TBL(obj) = 0;
26
+ RCLASS_SUPER(obj) = 0;
27
+ RCLASS_IV_INDEX_TBL(obj) = 0;
28
+ return (VALUE)obj;
29
+ }
30
+ #endif
31
+
32
+ /* a modified version of include_class_new from class.c */
33
+ static VALUE
34
+ j_class_new(VALUE module, VALUE sup) {
35
+
36
+ #ifdef RUBY_19
37
+ VALUE klass = class_alloc(T_ICLASS, rb_cClass);
38
+ #else
39
+ NEWOBJ(klass, struct RClass);
40
+ OBJSETUP(klass, rb_cClass, T_ICLASS);
41
+ #endif
42
+
43
+ if (BUILTIN_TYPE(module) == T_ICLASS) {
44
+ module = KLASS_OF(module);
45
+ }
46
+
47
+ if (!RCLASS_IV_TBL(module)) {
48
+
49
+ RCLASS_IV_TBL(module) = (struct st_table *)st_init_numtable();
50
+ }
51
+
52
+ /* assign iv_tbl, m_tbl and super */
53
+ RCLASS_IV_TBL(klass) = RCLASS_IV_TBL(module);
54
+ RCLASS_SUPER(klass) = sup;
55
+ if(TYPE(module) != T_OBJECT) {
56
+
57
+ RCLASS_M_TBL(klass) = RCLASS_M_TBL(module);
58
+ }
59
+ else {
60
+ RCLASS_M_TBL(klass) = RCLASS_M_TBL(CLASS_OF(module));
61
+ }
62
+
63
+ /* */
64
+
65
+ if (TYPE(module) == T_ICLASS) {
66
+ KLASS_OF(klass) = KLASS_OF(module);
67
+ }
68
+ else {
69
+ KLASS_OF(klass) = module;
70
+ }
71
+
72
+
73
+ if(TYPE(module) != T_OBJECT) {
74
+ OBJ_INFECT(klass, module);
75
+ OBJ_INFECT(klass, sup);
76
+ }
77
+ return (VALUE)klass;
78
+ }
79
+
80
+ static VALUE
81
+ rb_to_module(VALUE self) {
82
+ VALUE rclass, chain_start, jcur, klass;
83
+
84
+ switch(BUILTIN_TYPE(self)) {
85
+ case T_MODULE:
86
+ return self;
87
+ case T_CLASS:
88
+ klass = self;
89
+ break;
90
+ case T_OBJECT:
91
+ default:
92
+ klass = rb_singleton_class(self);
93
+ }
94
+
95
+ chain_start = j_class_new(klass, rb_cObject);
96
+
97
+ KLASS_OF(chain_start) = rb_cModule;
98
+ RBASIC(chain_start)->flags = T_MODULE;
99
+
100
+ jcur = chain_start;
101
+ for(rclass = RCLASS_SUPER(klass); rclass != rb_cObject;
102
+ rclass = RCLASS_SUPER(rclass)) {
103
+
104
+ RCLASS_SUPER(jcur) = j_class_new(rclass, rb_cObject);
105
+ jcur = RCLASS_SUPER(jcur);
106
+ }
107
+
108
+ RCLASS_SUPER(jcur) = (VALUE)NULL;
109
+
110
+ return chain_start;
111
+ }
112
+
113
+ static VALUE
114
+ rb_reset_tbls(VALUE self) {
115
+ RCLASS_IV_TBL(self) = (struct st_table *) 0;
116
+ RCLASS_M_TBL(self) = (struct st_table *) st_init_numtable();
117
+
118
+ return Qnil;
119
+ }
120
+
121
+ /*
122
+ * call-seq:
123
+ * obj.gen_extend(other, ...) => obj
124
+ *
125
+ * Adds to _obj_ the instance methods from each object given as a
126
+ * parameter.
127
+ *
128
+ * class C
129
+ * def hello
130
+ * "Hello from C.\n"
131
+ * end
132
+ * end
133
+ *
134
+ * class Klass
135
+ * def hello
136
+ * "Hello from Klass.\n"
137
+ * end
138
+ * end
139
+ *
140
+ * k = Klass.new
141
+ * k.hello #=> "Hello from Klass.\n"
142
+ * k.gen_extend(C) #=> #<Klass:0x401b3bc8>
143
+ * k.hello #=> "Hello from C.\n"
144
+ */
145
+
146
+ static VALUE
147
+ rb_gen_extend(int argc, VALUE * argv, VALUE self) {
148
+ int i;
149
+
150
+ if (argc == 0) rb_raise(rb_eArgError, "wrong number of arguments (0 for 1)");
151
+
152
+ rb_singleton_class(self);
153
+
154
+ for(i = 0; i < argc; i++) {
155
+ VALUE mod = rb_to_module(argv[i]);
156
+ rb_funcall(mod, rb_intern("extend_object"), 1, self);
157
+ rb_funcall(mod, rb_intern("extended"), 1, self);
158
+
159
+ /* only redirect if argv[i] is not a module */
160
+ if(argv[i] != mod) rb_reset_tbls(mod);
161
+ }
162
+
163
+ return self;
164
+ }
165
+
166
+ /*
167
+ * call-seq:
168
+ * gen_include(other, ...) => self
169
+ *
170
+ * Adds to the implied receiver the instance methods from each object given as a
171
+ * parameter.
172
+ *
173
+ * class C
174
+ * def hello
175
+ * "Hello from C.\n"
176
+ * end
177
+ * end
178
+ *
179
+ * class Klass
180
+ * gen_include(C)
181
+ * end
182
+ *
183
+ * k = Klass.new
184
+ * k.hello #=> "Hello from C.\n"
185
+ */
186
+
187
+ static VALUE
188
+ rb_gen_include(int argc, VALUE * argv, VALUE self) {
189
+ int i;
190
+
191
+ if (argc == 0) rb_raise(rb_eArgError, "wrong number of arguments (0 for 1)");
192
+
193
+ for(i = 0; i < argc; i++) {
194
+ VALUE mod = rb_to_module(argv[i]);
195
+ rb_funcall(mod, rb_intern("append_features"), 1, self);
196
+ rb_funcall(mod, rb_intern("included"), 1, self);
197
+
198
+ if(argv[i] != mod) rb_reset_tbls(mod);
199
+ }
200
+
201
+ return self;
202
+ }
203
+
204
+
205
+ void Init_cobject2module() {
206
+
207
+ /* too dangerous as may result in double free. */
208
+ rb_define_method(rb_cObject, "to_module", rb_to_module , 0);
209
+
210
+ /* these methods are fine */
211
+ rb_define_method(rb_cObject, "gen_extend", rb_gen_extend, -1);
212
+ rb_define_method(rb_cModule, "gen_include", rb_gen_include, -1);
213
+ rb_define_method(rb_cModule, "reset_tbls", rb_reset_tbls, 0);
214
+ }
215
+
@@ -0,0 +1,11 @@
1
+ /* object2module.h */
2
+
3
+ #ifndef GUARD_OBJECT2MODULE_H
4
+ #define GUARD_OBJECT2MODULE_H
5
+
6
+ VALUE rb_gen_include(int argc, VALUE * argv, VALUE self);
7
+ VALUE rb_gen_extend(int argc, VALUE * argv, VALUE self);
8
+ VALUE rb_to_module(VALUE self);
9
+ VALUE rb_reset_tbls(VALUE self);
10
+
11
+ #endif
@@ -0,0 +1,13 @@
1
+ require 'rbconfig'
2
+
3
+ direc = File.dirname(__FILE__)
4
+ dlext = Config::CONFIG['DLEXT']
5
+ begin
6
+ if RUBY_VERSION && RUBY_VERSION =~ /1.9/
7
+ require "#{direc}/cobject2module.19.#{dlext}"
8
+ else
9
+ require "#{direc}/cobject2module.18.#{dlext}"
10
+ end
11
+ rescue LoadError => e
12
+ require "#{direc}/cobject2module.#{dlext}"
13
+ end
@@ -0,0 +1,145 @@
1
+ require 'test/unit'
2
+ require 'object2module'
3
+
4
+ module M
5
+ def m
6
+ "m"
7
+ end
8
+ end
9
+
10
+ class A
11
+ include M
12
+
13
+ def a
14
+ "a"
15
+ end
16
+ end
17
+
18
+ class B < A
19
+ def b
20
+ "b"
21
+ end
22
+ end
23
+
24
+ class C < B
25
+ def c
26
+ "c"
27
+ end
28
+ end
29
+
30
+ # stand-alone class
31
+ class K
32
+ def k
33
+ "k"
34
+ end
35
+ end
36
+
37
+ # another stand-alone class
38
+ class J
39
+ def j
40
+ "j"
41
+ end
42
+ end
43
+
44
+ class Object2ModuleTest < Test::Unit::TestCase
45
+ def test_class_to_module
46
+ assert_instance_of(Module, C.to_module)
47
+ end
48
+
49
+ def test_class_heirarchy
50
+ c = C.to_module
51
+
52
+ h = c.ancestors
53
+ assert_equal(B, h[1])
54
+ assert_equal(A, h[2])
55
+ assert_equal(M, h[3])
56
+ end
57
+
58
+ def test_class_extend
59
+ o = Object.new
60
+ assert_equal(o, o.gen_extend(C))
61
+ end
62
+
63
+ def test_class_extended_methods
64
+ h = C.to_module
65
+ o = Object.new
66
+ o.extend(h)
67
+ assert_equal("a", o.a)
68
+ assert_equal("b", o.b)
69
+ assert_equal("c", o.c)
70
+ assert_equal("m", o.m)
71
+ end
72
+
73
+ def test_object_to_module
74
+ o = C.new
75
+ assert_instance_of(Module, o.to_module)
76
+ end
77
+
78
+ def test_object_heirarchy
79
+ o = C.new
80
+ h = o.to_module.ancestors
81
+ assert_equal(C, h[1])
82
+ assert_equal(B, h[2])
83
+ assert_equal(A, h[3])
84
+ assert_equal(M, h[4])
85
+ end
86
+
87
+ def test_object_extend
88
+ h = C.to_module
89
+ o = Object.new
90
+ assert_equal(o, o.extend(h))
91
+ end
92
+
93
+ def test_object_extended_methods
94
+ o = C.new
95
+ h = o.to_module
96
+ l = Object.new
97
+ l.extend(h)
98
+ assert_equal("a", l.a)
99
+ assert_equal("b", l.b)
100
+ assert_equal("c", l.c)
101
+ assert_equal("m", l.m)
102
+ end
103
+
104
+ def test_gen_extend
105
+ o = Object.new
106
+ o.gen_extend(C)
107
+ assert_equal("a", o.a)
108
+ assert_equal("b", o.b)
109
+ assert_equal("c", o.c)
110
+ assert_equal("m", o.m)
111
+ end
112
+
113
+ def test_gen_include
114
+ k = Class.new
115
+ k.gen_include(C)
116
+ o = k.new
117
+ assert_equal("a", o.a)
118
+ assert_equal("b", o.b)
119
+ assert_equal("c", o.c)
120
+ assert_equal("m", o.m)
121
+ end
122
+
123
+ def test_gen_extend_multi
124
+ o = Object.new
125
+ o.gen_extend(C, K, J)
126
+ assert_equal("a", o.a)
127
+ assert_equal("b", o.b)
128
+ assert_equal("c", o.c)
129
+ assert_equal("m", o.m)
130
+ assert_equal("k", o.k)
131
+ assert_equal("j", o.j)
132
+ end
133
+
134
+ def test_gen_include_multi
135
+ k = Class.new
136
+ k.gen_include(C, K, J)
137
+ o = k.new
138
+ assert_equal("a", o.a)
139
+ assert_equal("b", o.b)
140
+ assert_equal("c", o.c)
141
+ assert_equal("m", o.m)
142
+ assert_equal("k", o.k)
143
+ assert_equal("j", o.j)
144
+ end
145
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: object2module
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - John Mair (banisterfiend)
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-09-25 00:00:00 +12:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: object2module enables ruby classes and objects to be used as modules
17
+ email: jrmair@gmail.com
18
+ executables: []
19
+
20
+ extensions:
21
+ - ext/extconf.rb
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ - ext/object2module.c
25
+ files:
26
+ - Rakefile
27
+ - lib/object2module.rb
28
+ - README.rdoc
29
+ - ext/object2module.c
30
+ - ext/object2module.h
31
+ - ext/compat.h
32
+ - ext/extconf.rb
33
+ - test/test_object2module.rb
34
+ has_rdoc: true
35
+ homepage: http://banisterfiend.wordpress.com
36
+ post_install_message:
37
+ rdoc_options:
38
+ - --main
39
+ - README.rdoc
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project:
57
+ rubygems_version: 1.2.0
58
+ signing_key:
59
+ specification_version: 2
60
+ summary: object2module enables ruby classes and objects to be used as modules
61
+ test_files: []
62
+