Skip to content

Instantly share code, notes, and snippets.

@kronos
Created April 10, 2010 13:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kronos/362029 to your computer and use it in GitHub Desktop.
Save kronos/362029 to your computer and use it in GitHub Desktop.
diff --git a/kernel/common/module.rb b/kernel/common/module.rb
index 5f98a67..f4b661a 100644
--- a/kernel/common/module.rb
+++ b/kernel/common/module.rb
@@ -69,6 +69,13 @@ class Module
class_variable_defined? verify_class_variable_name(name)
end
+ def remove_class_variable(name)
+ Ruby.primitive :module_cvar_remove
+
+ remove_class_variable verify_class_variable_name(name)
+ end
+ private :remove_class_variable
+
def __class_variables__
Ruby.primitive :module_class_variables
diff --git a/spec/ruby/core/module/class_variable_set_spec.rb b/spec/ruby/core/module/class_variable_set_spec.rb
index 3864935..39700a8 100644
--- a/spec/ruby/core/module/class_variable_set_spec.rb
+++ b/spec/ruby/core/module/class_variable_set_spec.rb
@@ -13,7 +13,7 @@ describe "Module#class_variable_set" do
end
it "sets the value of a class variable with the given name defined in an included module" do
- c = Class.new { include ModuleSpecs::MVars }
+ c = Class.new { include ModuleSpecs::MVars.dup }
c.send(:class_variable_set, "@@mvar", :new_mvar).should == :new_mvar
c.send(:class_variable_get, "@@mvar").should == :new_mvar
end
diff --git a/spec/ruby/core/module/fixtures/classes.rb b/spec/ruby/core/module/fixtures/classes.rb
index 36d541d..4fd0e35 100644
--- a/spec/ruby/core/module/fixtures/classes.rb
+++ b/spec/ruby/core/module/fixtures/classes.rb
@@ -7,6 +7,9 @@ module ModuleSpecs
class SubclassSpec
end
+ class RemoveClassVariable
+ end
+
module LookupModInMod
INCS = :ethereal
end
diff --git a/spec/ruby/core/module/remove_class_variable_spec.rb b/spec/ruby/core/module/remove_class_variable_spec.rb
index 47ee321..95678ac 100644
--- a/spec/ruby/core/module/remove_class_variable_spec.rb
+++ b/spec/ruby/core/module/remove_class_variable_spec.rb
@@ -1,8 +1,45 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)
-ruby_version_is "1.9" do
- describe "Module#remove_class_variable" do
- it "needs to be reviewed for spec completeness"
+describe "Module#remove_class_variable" do
+ it "removes class variable" do
+ m = ModuleSpecs::MVars.dup
+ m.send(:remove_class_variable, :@@mvar)
+ m.class_variable_defined?(:@@mvar).should == false
+ end
+
+ it "returns the value of removing class variable" do
+ m = ModuleSpecs::MVars.dup
+ m.send(:remove_class_variable, :@@mvar).should == :mvar
+ end
+
+ it "raises a NameError when removing class variable declared in included module" do
+ c = ModuleSpecs::RemoveClassVariable.new { include ModuleSpecs::MVars.dup }
+ lambda { c.send(:remove_class_variable, :@@mvar) }.should raise_error(NameError)
+ end
+
+ it "dunno how to name this block" do
+ c = ModuleSpecs::RemoveClassVariable.new { @@mvar = :mvar }
+ lambda { c.send(:remove_class_variable, :@@mvar) }.should raise_error(NameError)
+ end
+
+ it "raises a NameError when passed a symbol with one leading @" do
+ lambda { ModuleSpecs::MVars.send(:remove_class_variable, :@mvar) }.should raise_error(NameError)
+ end
+
+ it "raises a NameError when passed a symbol with no leading @" do
+ lambda { ModuleSpecs::MVars.send(:remove_class_variable, :mvar) }.should raise_error(NameError)
+ end
+
+ it "raises a NameError when a Fixnum for an uninitialized class variable is given" do
+ lambda { ModuleSpecs::MVars.send(:remove_class_variable, :@@nonexisting_class_variable) }.should raise_error(NameError)
+ end
+
+ ruby_version_is "" ... "1.9" do
+ it "is private" do
+ lambda { ModuleSpecs::MVars.dup.remove_class_variable(:@@mvar) }.should raise_error(NoMethodError)
+ end
end
end
diff --git a/vm/builtin/module.cpp b/vm/builtin/module.cpp
index de5fb54..ae04f76 100644
--- a/vm/builtin/module.cpp
+++ b/vm/builtin/module.cpp
@@ -311,7 +311,60 @@ namespace rubinius {
mod_to_query->set_table_ivar(state, name, value);
return value;
+ }
+
+ Object* Module::cvar_remove(STATE, Symbol* name) {
+ if(!name->is_cvar_p(state)->true_p()) return Primitives::failure();
+
+ Module* mod = this;
+ Module* mod_to_query;
+ if(MetaClass* mc = try_as<MetaClass>(mod)) {
+ mod_to_query = as<Module>(mc->attached_instance());
+ } else if(IncludedModule* im = try_as<IncludedModule>(mod)) {
+ mod_to_query = im->module();
+ } else {
+ mod_to_query = mod;
+ }
+
+ if(mod_to_query->table_ivar_defined(state, name)->true_p()) {
+ Object* value = mod_to_query->get_table_ivar(state, name);
+ mod_to_query->del_table_ivar(state, name);
+ return value;
+ }
+
+ std::stringstream ss;
+ mod = this;
+ if(MetaClass* mc = try_as<MetaClass>(mod)) {
+ mod = as<Module>(mc->attached_instance());
+ }
+
+ if (this->cvar_defined(state, name) == Qtrue) {
+ ss << "cannot remove ";
+ ss << name->c_str(state);
+ ss << " for ";
+ } else {
+ ss << "uninitialized class variable ";
+ ss << name->c_str(state);
+ ss << " in module ";
+ }
+
+ if(mod->name()->nil_p()) {
+ if(kind_of<Class>(mod)) {
+ ss << "#<Class>";
+ } else {
+ ss << "#<Module>";
+ }
+ } else {
+ ss << mod->name()->c_str(state);
+ }
+
+ RubyException::raise(
+ Exception::make_exception(state,
+ as<Class>(G(object)->get_const(state, "NameError")),
+ ss.str().c_str()));
+
+ return NULL;
}
void Module::Info::show(STATE, Object* self, int level) {
diff --git a/vm/builtin/module.hpp b/vm/builtin/module.hpp
index a6cba64..11913f6 100644
--- a/vm/builtin/module.hpp
+++ b/vm/builtin/module.hpp
@@ -61,6 +61,9 @@ namespace rubinius {
// Ruby.primitive :module_cvar_get_or_set
Object* cvar_get_or_set(STATE, Symbol* name, Object* val);
+ // Ruby.primitive :module_cvar_remove
+ Object* cvar_remove(STATE, Symbol* name);
+
void setup(STATE);
void setup(STATE, const char* name, Module* under = NULL);
void setup(STATE, Symbol* name, Module* under = NULL);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment