Revision: 3915 http://trac.macosforge.org/projects/ruby/changeset/3915 Author: eloy.de.enige@gmail.com Date: 2010-04-07 12:45:53 -0700 (Wed, 07 Apr 2010) Log Message: ----------- Once any framework is loaded, send KVO notifications from attr writers. That is, loaded through Kernel#framework. Modified Paths: -------------- MacRuby/trunk/compiler.cpp MacRuby/trunk/compiler.h MacRuby/trunk/objc.h MacRuby/trunk/objc.m MacRuby/trunk/spec/macruby/core/kvc_spec.rb MacRuby/trunk/spec/macruby/core/kvo_spec.rb Added Paths: ----------- MacRuby/trunk/spec/macruby/fixtures/kvo_wrapper.rb Modified: MacRuby/trunk/compiler.cpp =================================================================== --- MacRuby/trunk/compiler.cpp 2010-04-07 07:34:31 UTC (rev 3914) +++ MacRuby/trunk/compiler.cpp 2010-04-07 19:45:53 UTC (rev 3915) @@ -100,6 +100,7 @@ prepareIvarSlotFunc = NULL; getIvarFunc = NULL; setIvarFunc = NULL; + setKVOIvarFunc = NULL; definedFunc = NULL; undefFunc = NULL; aliasFunc = NULL; @@ -5988,7 +5989,7 @@ RoxorCompiler::compile_write_attr(ID name) { Function *f = cast<Function>(module->getOrInsertFunction("", - RubyObjTy, RubyObjTy, PtrTy, RubyObjTy, NULL)); + RubyObjTy, RubyObjTy, PtrTy, RubyObjTy, NULL)); Function::arg_iterator arg = f->arg_begin(); current_self = arg++; @@ -5997,14 +5998,19 @@ bb = BasicBlock::Create(context, "EntryBlock", f); - // This disables ivar slot generation. - // TODO: make it work - const bool old_current_instance_method = current_instance_method; - current_instance_method = false; + std::vector<Value *> params; + params.push_back(current_self); + params.push_back(compile_id(name)); + params.push_back(new_val); - Value *val = compile_ivar_assignment(name, new_val); + if (setKVOIvarFunc == NULL) { + setKVOIvarFunc = + cast<Function>(module->getOrInsertFunction("rb_vm_set_kvo_ivar", + RubyObjTy, RubyObjTy, RubyObjTy, RubyObjTy, NULL)); + } - current_instance_method = old_current_instance_method; + Value *val = CallInst::Create(setKVOIvarFunc, + params.begin(), params.end(), "", bb); ReturnInst::Create(context, val, bb); Modified: MacRuby/trunk/compiler.h =================================================================== --- MacRuby/trunk/compiler.h 2010-04-07 07:34:31 UTC (rev 3914) +++ MacRuby/trunk/compiler.h 2010-04-07 19:45:53 UTC (rev 3915) @@ -151,6 +151,7 @@ Function *prepareIvarSlotFunc; Function *getIvarFunc; Function *setIvarFunc; + Function *setKVOIvarFunc; Function *definedFunc; Function *undefFunc; Function *aliasFunc; Modified: MacRuby/trunk/objc.h =================================================================== --- MacRuby/trunk/objc.h 2010-04-07 07:34:31 UTC (rev 3914) +++ MacRuby/trunk/objc.h 2010-04-07 19:45:53 UTC (rev 3915) @@ -19,6 +19,7 @@ bs_element_method_t *bs_method, char *buf, size_t buflen); void rb_objc_define_kvo_setter(VALUE klass, ID mid); +VALUE rb_vm_set_kvo_ivar(VALUE obj, ID name, VALUE val); static inline IMP rb_objc_install_method(Class klass, SEL sel, IMP imp) Modified: MacRuby/trunk/objc.m =================================================================== --- MacRuby/trunk/objc.m 2010-04-07 07:34:31 UTC (rev 3914) +++ MacRuby/trunk/objc.m 2010-04-07 19:45:53 UTC (rev 3915) @@ -338,6 +338,8 @@ #endif } +static bool enable_kvo_notifications = false; + VALUE rb_require_framework(VALUE recv, SEL sel, int argc, VALUE *argv) { @@ -450,6 +452,8 @@ reload_class_constants(); reload_protocols(); + enable_kvo_notifications = true; + return Qtrue; } @@ -505,6 +509,21 @@ } VALUE +rb_vm_set_kvo_ivar(VALUE obj, ID name, VALUE val) +{ + NSString *key = NULL; + if (enable_kvo_notifications) { + key = [(NSString *)rb_id2str(name) substringFromIndex:1]; // skip '@' prefix + [(id)obj willChangeValueForKey:key]; + } + rb_ivar_set(obj, name, val); + if (enable_kvo_notifications) { + [(id)obj didChangeValueForKey:key]; + } + return val; +} + +VALUE rb_mod_objc_ib_outlet(VALUE recv, SEL sel, int argc, VALUE *argv) { int i; Modified: MacRuby/trunk/spec/macruby/core/kvc_spec.rb =================================================================== --- MacRuby/trunk/spec/macruby/core/kvc_spec.rb 2010-04-07 07:34:31 UTC (rev 3914) +++ MacRuby/trunk/spec/macruby/core/kvc_spec.rb 2010-04-07 19:45:53 UTC (rev 3915) @@ -221,3 +221,15 @@ manipulateUnorderedCollection(w) end end + +class Wrapper + attr_accessor :whatever +end + +describe "Module::attr_writer" do + it "defines an Objective-C style setter method" do + w = Wrapper.new + w.setWhatever(42) + w.whatever.should == 42 + end +end \ No newline at end of file Modified: MacRuby/trunk/spec/macruby/core/kvo_spec.rb =================================================================== --- MacRuby/trunk/spec/macruby/core/kvo_spec.rb 2010-04-07 07:34:31 UTC (rev 3914) +++ MacRuby/trunk/spec/macruby/core/kvo_spec.rb 2010-04-07 19:45:53 UTC (rev 3915) @@ -1,33 +1,6 @@ -require File.dirname(__FILE__) + "/../spec_helper" +require File.expand_path("../../spec_helper", __FILE__) +require File.join(FIXTURES, "kvo_wrapper") -class Wrapper - attr_accessor :whatever - - def initialize(value) - super() - @wrapped = value - @whatever= 'like, whatever' - end - - def wrappedValue - @wrapped - end -end - -class FancyWrapper < NSValue - attr_accessor :whatever - - def initialize(value) - super() - @wrapped = value - @whatever= 'like, whatever' - end - - def wrappedValue - @wrapped - end -end - describe "An Object being observed through NSKeyValueObservation" do it "retains the values for its instance variables" do # @@ -86,3 +59,40 @@ lambda { w.inspect }.should_not raise_error end end + +describe "An accessor defined with Module::attr_writer" do + it "does not send any KVO notifications unless a framework has been loaded" do + expected_output = "Manfred is very happy with himself" + + example = %{delegate = Object.new + def delegate.wrapperWillChangeValueForKey(key) + raise "did not expect a KVO notification!" + end + w = Wrapper.new + w.kvoDelegate = delegate + w.whatever = "#{expected_output}" + puts w.whatever}.gsub("\n", ";") + + output = ruby_exe(example, :options => "-r #{File.join(FIXTURES, "kvo_wrapper.rb")}") + output.chomp.should == expected_output + end + + it "sends KVO notifications, around assigning the new value, once a framework has been loaded" do + @w = Wrapper.new + @w.kvoDelegate = self + @w.whatever = "oh come on" + (@ranBefore && @ranAfter).should == true + end + + def wrapperWillChangeValueForKey(key) + key.should == "whatever" + @w.whatever.should == "like, whatever" + @ranBefore = true + end + + def wrapperDidChangeValueForKey(key) + key.should == "whatever" + @w.whatever.should == "oh come on" + @ranAfter = true + end +end \ No newline at end of file Added: MacRuby/trunk/spec/macruby/fixtures/kvo_wrapper.rb =================================================================== --- MacRuby/trunk/spec/macruby/fixtures/kvo_wrapper.rb (rev 0) +++ MacRuby/trunk/spec/macruby/fixtures/kvo_wrapper.rb 2010-04-07 19:45:53 UTC (rev 3915) @@ -0,0 +1,43 @@ +class Wrapper + attr_accessor :whatever + + def initialize(value = nil) + super() + @wrapped = value + @whatever= 'like, whatever' + end + + def wrappedValue + @wrapped + end +end + +class FancyWrapper < NSValue + attr_accessor :whatever + + def initialize(value) + super() + @wrapped = value + @whatever= 'like, whatever' + end + + def wrappedValue + @wrapped + end +end + +class Wrapper + attr_accessor :whatever + + def kvoDelegate=(delegate) + @kvoDelegate = delegate + end + + def willChangeValueForKey(key) + @kvoDelegate.wrapperWillChangeValueForKey(key) if @kvoDelegate + end + + def didChangeValueForKey(key) + @kvoDelegate.wrapperDidChangeValueForKey(key) if @kvoDelegate + end +end \ No newline at end of file
participants (1)
-
source_changes@macosforge.org