blockenspiel 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{History.txt → History.rdoc} +7 -0
- data/{ImplementingDSLblocks.txt → ImplementingDSLblocks.rdoc} +4 -4
- data/{README.txt → README.rdoc} +8 -7
- data/Rakefile +144 -15
- data/ext/blockenspiel/BlockenspielUnmixerService.java +140 -0
- data/ext/blockenspiel/extconf.rb +2 -0
- data/ext/blockenspiel/unmixer.c +86 -0
- data/lib/blockenspiel/impl.rb +670 -0
- data/lib/blockenspiel/version.rb +42 -0
- data/lib/blockenspiel.rb +7 -636
- data/tests/tc_basic.rb +2 -1
- data/tests/tc_behaviors.rb +4 -3
- data/tests/tc_dsl_methods.rb +2 -1
- data/tests/tc_dynamic.rb +2 -1
- data/tests/tc_mixins.rb +2 -1
- metadata +27 -43
- data/Manifest.txt +0 -11
@@ -1,3 +1,10 @@
|
|
1
|
+
=== 0.2.0 / 2009-04-15
|
2
|
+
|
3
|
+
* Now compatible with Ruby 1.9.
|
4
|
+
* Now compatible with JRuby 1.2.
|
5
|
+
* No longer requires the mixology gem.
|
6
|
+
* Building no longer requires hoe.
|
7
|
+
|
1
8
|
=== 0.1.1 / 2008-11-06
|
2
9
|
|
3
10
|
* Added ability to pass the block as the first parameter in
|
@@ -669,9 +669,9 @@ What we would really like is a way to add methods to just one object temporarily
|
|
669
669
|
s1.foo # prints "foo called"
|
670
670
|
s2.foo # NameError: s2 is unchanged
|
671
671
|
|
672
|
-
Unfortunately, there is no way to remove the module from the object. Ruby has no "unextend" capability. This omission led Why to implement it himself as a Ruby language extension
|
672
|
+
Unfortunately, there is no way to remove the module from the object. Ruby has no "unextend" capability. This omission led Why to implement it himself as a Ruby language extension called {Mixico}[http://github.com/why/mixico/tree/master]. The name comes from the library's ability to add and remove "mixins" at will. A similar library exists as a gem called {Mixology}[http://www.somethingnimble.com/bliki/mixology]. The two libraries use different APIs but perform the same basic function. For the discussion below, I will assume Mixico is installed. However, the library I describe in the next section uses a custom implementation that is compatible with MRI 1.9 and JRuby.
|
673
673
|
|
674
|
-
Using
|
674
|
+
Using Mixico, we can now write the +draw+ method like this:
|
675
675
|
|
676
676
|
def draw(&block)
|
677
677
|
clear!
|
@@ -758,7 +758,7 @@ As we have seen, the mixin idea seems like it may be a compelling solution, part
|
|
758
758
|
|
759
759
|
*Implementation*:
|
760
760
|
|
761
|
-
* Install a mixin library such as mixico or mixology.
|
761
|
+
* Install a mixin library such as mixico or mixology (or roll your own if necessary).
|
762
762
|
* Define the DSL methods in a module.
|
763
763
|
* Mix the module into the block's context before invoking the block, and remove it afterwards.
|
764
764
|
* Carefully handle any issues involving nested blocks and multithreading while remaining unobtrusive.
|
@@ -873,7 +873,7 @@ This requires dynamic generation of the proxy class. We could implement it using
|
|
873
873
|
def add_route(path, options = {})
|
874
874
|
# ...
|
875
875
|
|
876
|
-
You can install blockenspiel as a gem
|
876
|
+
You can install blockenspiel as a gem. It is compatible with MRI 1.8.7 or later, MRI 1.9.1 or later, and JRuby 1.2 or later.
|
877
877
|
|
878
878
|
gem install blockenspiel
|
879
879
|
|
data/{README.txt → README.rdoc}
RENAMED
@@ -256,7 +256,7 @@ Here's an example:
|
|
256
256
|
That API is in itself a DSL block, and yes, Blockenspiel uses itself to
|
257
257
|
implement this feature.
|
258
258
|
|
259
|
-
By default Blockenspiel uses mixins, which usually exhibit safe and
|
259
|
+
By default Blockenspiel uses mixins, which usually exhibit fairly safe and
|
260
260
|
non-surprising behavior. However, there are a few cases when you might
|
261
261
|
want the <tt>instance_eval</tt> behavior anyway. RSpec is a good example of
|
262
262
|
such a case, since the DSL is being used to construct objects, so it makes
|
@@ -284,9 +284,8 @@ concurrently.
|
|
284
284
|
|
285
285
|
=== Requirements
|
286
286
|
|
287
|
-
* Ruby 1.8.
|
287
|
+
* Ruby 1.8.7 or later, or JRuby 1.2 or later. Ruby 1.9 compatible.
|
288
288
|
* Rubygems
|
289
|
-
* mixology gem.
|
290
289
|
|
291
290
|
=== Installation
|
292
291
|
|
@@ -296,8 +295,6 @@ concurrently.
|
|
296
295
|
|
297
296
|
* Implementing wildcard DSL methods using <tt>method_missing</tt> doesn't
|
298
297
|
work. I haven't yet figured out the right semantics for this case.
|
299
|
-
* Doesn't yet work in Ruby 1.9 because the mixology gem is not compatible at this point.
|
300
|
-
* JRuby status not yet known.
|
301
298
|
|
302
299
|
=== Development and support
|
303
300
|
|
@@ -311,16 +308,20 @@ Contact the author at dazuma at gmail dot com.
|
|
311
308
|
|
312
309
|
=== Author / Credits
|
313
310
|
|
314
|
-
Blockenspiel is written by Daniel Azuma (http://www.daniel-azuma.com).
|
311
|
+
Blockenspiel is written by Daniel Azuma (http://www.daniel-azuma.com/).
|
315
312
|
|
316
313
|
The mixin implementation is based on a concept by Why The Lucky Stiff.
|
317
314
|
See his 6 October 2008 blog posting,
|
318
315
|
<em>{Mixing Our Way Out Of Instance Eval?}[http://hackety.org/2008/10/06/mixingOurWayOutOfInstanceEval.html]</em>
|
319
316
|
for further discussion.
|
320
317
|
|
318
|
+
The unmixer code is based on {Mixology}[http://rubyforge.org/projects/mixology],
|
319
|
+
by Patrick Farley, anonymous z, Dan Manges, and Clint Bishop.
|
320
|
+
The code has been stripped down and modified to support MRI 1.9 and JRuby 1.2.
|
321
|
+
|
321
322
|
=== License
|
322
323
|
|
323
|
-
Copyright 2008 Daniel Azuma.
|
324
|
+
Copyright 2008-2009 Daniel Azuma.
|
324
325
|
|
325
326
|
All rights reserved.
|
326
327
|
|
data/Rakefile
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# Blockenspiel Rakefile
|
4
4
|
#
|
5
5
|
# -----------------------------------------------------------------------------
|
6
|
-
# Copyright 2008 Daniel Azuma
|
6
|
+
# Copyright 2008-2009 Daniel Azuma
|
7
7
|
#
|
8
8
|
# All rights reserved.
|
9
9
|
#
|
@@ -32,21 +32,150 @@
|
|
32
32
|
# POSSIBILITY OF SUCH DAMAGE.
|
33
33
|
# -----------------------------------------------------------------------------
|
34
34
|
|
35
|
+
require 'rake'
|
36
|
+
require 'rake/clean'
|
37
|
+
require 'rake/gempackagetask'
|
38
|
+
require 'rake/testtask'
|
39
|
+
require 'rake/rdoctask'
|
40
|
+
require 'rdoc/generator/darkfish'
|
35
41
|
|
36
|
-
require
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
42
|
+
require File.expand_path("#{File.dirname(__FILE__)}/lib/blockenspiel/version")
|
43
|
+
|
44
|
+
|
45
|
+
# Configuration
|
46
|
+
extra_rdoc_files_ = ['README.rdoc', 'History.rdoc', 'ImplementingDSLblocks.rdoc']
|
47
|
+
|
48
|
+
|
49
|
+
# Default task
|
50
|
+
task :default => [:clean, :compile, :rdoc, :test]
|
51
|
+
|
52
|
+
|
53
|
+
# Clean task
|
54
|
+
CLEAN.include(['ext/blockenspiel/Makefile', '**/unmixer.bundle', 'ext/blockenspiel/unmixer.o', '**/blockenspiel_unmixer.jar', 'ext/blockenspiel/BlockenspielUnmixerService.class', 'idslb_markdown.txt', 'doc', 'pkg'])
|
55
|
+
|
56
|
+
|
57
|
+
# Test task
|
58
|
+
Rake::TestTask.new('test') do |task_|
|
59
|
+
task_.pattern = 'tests/tc_*.rb'
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# RDoc task
|
64
|
+
Rake::RDocTask.new do |task_|
|
65
|
+
task_.main = 'README.rdoc'
|
66
|
+
task_.rdoc_files.include(*extra_rdoc_files_)
|
67
|
+
task_.rdoc_files.include('lib/blockenspiel/*.rb')
|
68
|
+
task_.rdoc_dir = 'doc'
|
69
|
+
task_.title = "Blockenspiel #{Blockenspiel::VERSION_STRING} documentation"
|
70
|
+
task_.options << '-f' << 'darkfish'
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
# Gem task
|
75
|
+
gemspec_ = Gem::Specification.new do |s_|
|
76
|
+
s_.name = 'blockenspiel'
|
77
|
+
s_.summary = 'Blockenspiel is a helper library designed to make it easy to implement DSL blocks.'
|
78
|
+
s_.version = Blockenspiel::VERSION_STRING
|
79
|
+
s_.author = 'Daniel Azuma'
|
80
|
+
s_.email = 'dazuma@gmail.com'
|
81
|
+
s_.description = 'Blockenspiel is a helper library designed to make it easy to implement DSL blocks. It is designed to be comprehensive and robust, supporting most common usage patterns, and working correctly in the presence of nested blocks and multithreading.'
|
82
|
+
s_.homepage = 'http://virtuoso.rubyforge.org/blockenspiel'
|
83
|
+
s_.rubyforge_project = 'virtuoso'
|
84
|
+
s_.required_ruby_version = '>= 1.8.6'
|
85
|
+
s_.files = FileList['ext/**/*.{c,rb,java}', 'lib/**/*.{rb,jar}', 'tests/**/*.rb', '*.rdoc', 'Rakefile'].to_a
|
86
|
+
s_.extra_rdoc_files = extra_rdoc_files_
|
87
|
+
s_.has_rdoc = true
|
88
|
+
s_.test_files = FileList['tests/tc_*.rb']
|
89
|
+
if RUBY_PLATFORM =~ /java/
|
90
|
+
s_.platform = 'jruby'
|
91
|
+
s_.files += ['lib/blockenspiel_unmixer.jar']
|
92
|
+
else
|
93
|
+
s_.platform = Gem::Platform::RUBY
|
94
|
+
s_.extensions = ['ext/blockenspiel/extconf.rb']
|
95
|
+
end
|
96
|
+
end
|
97
|
+
task :package => [:compile]
|
98
|
+
Rake::GemPackageTask.new(gemspec_) do |task_|
|
99
|
+
task_.need_zip = false
|
100
|
+
task_.need_tar = false
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
# General build task
|
105
|
+
task :compile => RUBY_PLATFORM =~ /java/ ? [:compile_java] : [:compile_c]
|
106
|
+
|
107
|
+
|
108
|
+
# Build tasks for MRI
|
109
|
+
dlext_ = Config::CONFIG['DLEXT']
|
110
|
+
|
111
|
+
desc 'Builds the extension'
|
112
|
+
task :compile_c => ["lib/blockenspiel/unmixer.#{dlext_}"]
|
113
|
+
|
114
|
+
file 'ext/blockenspiel/Makefile' => ['ext/blockenspiel/extconf.rb'] do
|
115
|
+
Dir.chdir('ext/blockenspiel') do
|
116
|
+
ruby 'extconf.rb'
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
file "ext/blockenspiel/unmixer.#{dlext_}" => ['ext/blockenspiel/Makefile', 'ext/blockenspiel/unmixer.c'] do
|
121
|
+
Dir.chdir('ext/blockenspiel') do
|
122
|
+
sh 'make'
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
file "lib/blockenspiel/unmixer.#{dlext_}" => ["ext/blockenspiel/unmixer.#{dlext_}"] do
|
127
|
+
cp "ext/blockenspiel/unmixer.#{dlext_}", 'lib/blockenspiel'
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
# Build tasks for JRuby
|
132
|
+
desc "Compiles the JRuby extension"
|
133
|
+
task :compile_java do
|
134
|
+
Dir.chdir('ext/blockenspiel') do
|
135
|
+
sh 'javac -source 1.5 -target 1.5 -classpath $JRUBY_HOME/lib/jruby.jar BlockenspielUnmixerService.java'
|
136
|
+
sh 'jar cf blockenspiel_unmixer.jar BlockenspielUnmixerService.class'
|
137
|
+
cp 'blockenspiel_unmixer.jar', '../../lib/blockenspiel_unmixer.jar'
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
# Publish RDocs
|
143
|
+
desc 'Publishes RDocs to RubyForge'
|
144
|
+
task :publish_rdoc => [:rerdoc] do
|
145
|
+
config_ = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
|
146
|
+
username_ = config_['username']
|
147
|
+
sh "rsync -av --delete doc/ #{username_}@rubyforge.org:/var/www/gforge-projects/virtuoso/blockenspiel"
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
# Publish gem
|
152
|
+
task :publish_gem do |t_|
|
153
|
+
v_ = ENV["VERSION"]
|
154
|
+
abort "Must supply VERSION=x.y.z" unless v_
|
155
|
+
if v_ != Blockenspiel::VERSION_STRING
|
156
|
+
abort "Versions don't match: #{v_} vs #{Blockenspiel::VERSION_STRING}"
|
157
|
+
end
|
158
|
+
mri_pkg_ = "pkg/blockenspiel-#{v_}.gem"
|
159
|
+
jruby_pkg_ = "pkg/blockenspiel-#{v_}-java.gem"
|
160
|
+
if !File.file?(mri_pkg_) || !File.readable?(mri_pkg_)
|
161
|
+
abort "You haven't built #{mri_pkg_} yet. Try rake package"
|
162
|
+
end
|
163
|
+
if !File.file?(jruby_pkg_) || !File.readable?(jruby_pkg_)
|
164
|
+
abort "You haven't built #{jruby_pkg_} yet. Try jrake package"
|
165
|
+
end
|
166
|
+
release_notes_ = File.read("README.rdoc").split(/^(==.*)/)[2].strip
|
167
|
+
release_changes_ = File.read("History.rdoc").split(/^(===.*)/)[1..2].join.strip
|
168
|
+
|
169
|
+
require 'rubyforge'
|
170
|
+
rf_ = RubyForge.new.configure
|
171
|
+
puts "Logging in to RubyForge"
|
172
|
+
rf_.login
|
173
|
+
config_ = rf_.userconfig
|
174
|
+
config_["release_notes"] = release_notes_
|
175
|
+
config_["release_changes"] = release_changes_
|
176
|
+
config_["preformatted"] = true
|
177
|
+
puts "Releasing blockenspiel #{v_}"
|
178
|
+
rf.add_release('virtuoso', 'blockenspiel', v_, mri_pkg_, jruby_pkg_)
|
50
179
|
end
|
51
180
|
|
52
181
|
|
@@ -0,0 +1,140 @@
|
|
1
|
+
/*
|
2
|
+
-----------------------------------------------------------------------------
|
3
|
+
|
4
|
+
Blockenspiel unmixer (JRuby implementation)
|
5
|
+
|
6
|
+
-----------------------------------------------------------------------------
|
7
|
+
Copyright 2008-2009 Daniel Azuma
|
8
|
+
|
9
|
+
All rights reserved.
|
10
|
+
|
11
|
+
Redistribution and use in source and binary forms, with or without
|
12
|
+
modification, are permitted provided that the following conditions are met:
|
13
|
+
|
14
|
+
* Redistributions of source code must retain the above copyright notice,
|
15
|
+
this list of conditions and the following disclaimer.
|
16
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
17
|
+
this list of conditions and the following disclaimer in the documentation
|
18
|
+
and/or other materials provided with the distribution.
|
19
|
+
* Neither the name of the copyright holder, nor the names of any other
|
20
|
+
contributors to this software, may be used to endorse or promote products
|
21
|
+
derived from this software without specific prior written permission.
|
22
|
+
|
23
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
24
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
25
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
26
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
27
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
28
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
29
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
30
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
31
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
32
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
33
|
+
POSSIBILITY OF SUCH DAMAGE.
|
34
|
+
-----------------------------------------------------------------------------
|
35
|
+
*/
|
36
|
+
|
37
|
+
|
38
|
+
/*
|
39
|
+
This implementation based on Mixology,
|
40
|
+
written by Patrick Farley, anonymous z, Dan Manges, and Clint Bishop.
|
41
|
+
http://rubyforge.org/projects/mixology
|
42
|
+
http://github.com/dan-manges/mixology/tree/master
|
43
|
+
|
44
|
+
It has been stripped down and modified for JRuby 1.2 compatibility.
|
45
|
+
*/
|
46
|
+
|
47
|
+
import java.io.IOException;
|
48
|
+
import java.lang.reflect.InvocationTargetException;
|
49
|
+
import java.lang.reflect.Method;
|
50
|
+
import java.util.Iterator;
|
51
|
+
import java.util.List;
|
52
|
+
import java.util.ArrayList;
|
53
|
+
|
54
|
+
import org.jruby.Ruby;
|
55
|
+
import org.jruby.RubyArray;
|
56
|
+
import org.jruby.RubyClass;
|
57
|
+
import org.jruby.RubyModule;
|
58
|
+
import org.jruby.anno.JRubyMethod;
|
59
|
+
import org.jruby.IncludedModuleWrapper;
|
60
|
+
import org.jruby.runtime.Block;
|
61
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
62
|
+
import org.jruby.exceptions.RaiseException;
|
63
|
+
import org.jruby.runtime.load.BasicLibraryService;
|
64
|
+
|
65
|
+
|
66
|
+
public class BlockenspielUnmixerService implements BasicLibraryService
|
67
|
+
{
|
68
|
+
|
69
|
+
public boolean basicLoad(final Ruby runtime) throws IOException
|
70
|
+
{
|
71
|
+
RubyModule blockenspielModule = runtime.getOrCreateModule("Blockenspiel");
|
72
|
+
RubyModule unmixerModule = runtime.defineModuleUnder("Unmixer", blockenspielModule);
|
73
|
+
unmixerModule.getSingletonClass().defineAnnotatedMethods(BlockenspielUnmixerService.class);
|
74
|
+
return true;
|
75
|
+
}
|
76
|
+
|
77
|
+
|
78
|
+
@JRubyMethod(name = "unmix", required = 2)
|
79
|
+
public synchronized static IRubyObject unmix(IRubyObject self, IRubyObject receiver, IRubyObject module, Block block)
|
80
|
+
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
|
81
|
+
{
|
82
|
+
RubyModule actualModule = (RubyModule)module;
|
83
|
+
RubyClass klass = receiver.getMetaClass();
|
84
|
+
while (klass != receiver.getMetaClass().getRealClass())
|
85
|
+
{
|
86
|
+
RubyClass superClass = klass.getSuperClass();
|
87
|
+
if (superClass != null && superClass.getNonIncludedClass() == actualModule)
|
88
|
+
{
|
89
|
+
if (actualModule.getSuperClass() != null &&
|
90
|
+
actualModule.getSuperClass() instanceof IncludedModuleWrapper)
|
91
|
+
{
|
92
|
+
removeNestedModule(superClass, actualModule);
|
93
|
+
}
|
94
|
+
setSuperClass(klass, superClass.getSuperClass());
|
95
|
+
invalidateCacheDescendants(klass);
|
96
|
+
}
|
97
|
+
klass = superClass;
|
98
|
+
}
|
99
|
+
return receiver;
|
100
|
+
}
|
101
|
+
|
102
|
+
|
103
|
+
protected synchronized static void removeNestedModule(RubyClass klass, RubyModule module)
|
104
|
+
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
|
105
|
+
{
|
106
|
+
if ((klass.getSuperClass() instanceof IncludedModuleWrapper) &&
|
107
|
+
((IncludedModuleWrapper)klass.getSuperClass()).getNonIncludedClass() ==
|
108
|
+
((IncludedModuleWrapper)module.getSuperClass()).getNonIncludedClass())
|
109
|
+
{
|
110
|
+
if (module.getSuperClass().getSuperClass() != null &&
|
111
|
+
module.getSuperClass() instanceof IncludedModuleWrapper)
|
112
|
+
{
|
113
|
+
removeNestedModule(klass.getSuperClass(), module.getSuperClass());
|
114
|
+
}
|
115
|
+
setSuperClass(klass, klass.getSuperClass().getSuperClass());
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
|
120
|
+
protected synchronized static void setSuperClass(RubyModule klass, RubyModule superClass)
|
121
|
+
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
|
122
|
+
{
|
123
|
+
Method method = RubyModule.class.getDeclaredMethod("setSuperClass",
|
124
|
+
new Class[] {RubyClass.class} );
|
125
|
+
method.setAccessible(true);
|
126
|
+
Object[] superClassArg = new Object[] { superClass };
|
127
|
+
method.invoke(klass, superClassArg);
|
128
|
+
}
|
129
|
+
|
130
|
+
|
131
|
+
protected synchronized static void invalidateCacheDescendants(RubyModule klass)
|
132
|
+
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
|
133
|
+
{
|
134
|
+
Method method = RubyModule.class.getDeclaredMethod("invalidateCacheDescendants", new Class[0]);
|
135
|
+
method.setAccessible(true);
|
136
|
+
method.invoke(klass, new Object[0]);
|
137
|
+
}
|
138
|
+
|
139
|
+
}
|
140
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
/*
|
2
|
+
-----------------------------------------------------------------------------
|
3
|
+
|
4
|
+
Blockenspiel unmixer (MRI implementation)
|
5
|
+
|
6
|
+
-----------------------------------------------------------------------------
|
7
|
+
Copyright 2008-2009 Daniel Azuma
|
8
|
+
|
9
|
+
All rights reserved.
|
10
|
+
|
11
|
+
Redistribution and use in source and binary forms, with or without
|
12
|
+
modification, are permitted provided that the following conditions are met:
|
13
|
+
|
14
|
+
* Redistributions of source code must retain the above copyright notice,
|
15
|
+
this list of conditions and the following disclaimer.
|
16
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
17
|
+
this list of conditions and the following disclaimer in the documentation
|
18
|
+
and/or other materials provided with the distribution.
|
19
|
+
* Neither the name of the copyright holder, nor the names of any other
|
20
|
+
contributors to this software, may be used to endorse or promote products
|
21
|
+
derived from this software without specific prior written permission.
|
22
|
+
|
23
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
24
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
25
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
26
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
27
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
28
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
29
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
30
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
31
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
32
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
33
|
+
POSSIBILITY OF SUCH DAMAGE.
|
34
|
+
-----------------------------------------------------------------------------
|
35
|
+
*/
|
36
|
+
|
37
|
+
|
38
|
+
/*
|
39
|
+
This implementation based on Mixology,
|
40
|
+
written by Patrick Farley, anonymous z, Dan Manges, and Clint Bishop.
|
41
|
+
http://rubyforge.org/projects/mixology
|
42
|
+
http://github.com/dan-manges/mixology/tree/master
|
43
|
+
|
44
|
+
It has been stripped down and modified for compatibility with Ruby 1.9.
|
45
|
+
*/
|
46
|
+
|
47
|
+
|
48
|
+
#include "ruby.h"
|
49
|
+
|
50
|
+
|
51
|
+
#ifndef RCLASS_SUPER
|
52
|
+
#define RCLASS_SUPER(c) (RCLASS(c)->super)
|
53
|
+
#endif
|
54
|
+
|
55
|
+
|
56
|
+
static void remove_nested_module(VALUE klass, VALUE module) {
|
57
|
+
if (RBASIC(RCLASS_SUPER(klass))->klass == RBASIC(RCLASS_SUPER(module))->klass) {
|
58
|
+
if (RCLASS_SUPER(RCLASS_SUPER(module)) && BUILTIN_TYPE(RCLASS_SUPER(module)) == T_ICLASS) {
|
59
|
+
remove_nested_module(RCLASS_SUPER(klass), RCLASS_SUPER(module));
|
60
|
+
}
|
61
|
+
RCLASS_SUPER(klass) = RCLASS_SUPER(RCLASS_SUPER(klass));
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
|
66
|
+
static VALUE do_unmix(VALUE self, VALUE receiver, VALUE module) {
|
67
|
+
VALUE klass = RBASIC(receiver)->klass;
|
68
|
+
while (klass != rb_class_real(klass)) {
|
69
|
+
VALUE super = RCLASS_SUPER(klass);
|
70
|
+
if (BUILTIN_TYPE(super) == T_ICLASS && RBASIC(super)->klass == module) {
|
71
|
+
if (RCLASS_SUPER(module) && BUILTIN_TYPE(RCLASS_SUPER(module)) == T_ICLASS) {
|
72
|
+
remove_nested_module(super, module);
|
73
|
+
}
|
74
|
+
RCLASS_SUPER(klass) = RCLASS_SUPER(super);
|
75
|
+
rb_clear_cache();
|
76
|
+
}
|
77
|
+
klass = super;
|
78
|
+
}
|
79
|
+
return receiver;
|
80
|
+
}
|
81
|
+
|
82
|
+
|
83
|
+
void Init_unmixer() {
|
84
|
+
VALUE container = rb_singleton_class(rb_define_module_under(rb_define_module("Blockenspiel"), "Unmixer"));
|
85
|
+
rb_define_method(container, "unmix", do_unmix, 2);
|
86
|
+
}
|