From 721c8a435502833e8881738ae2d0a1f180c03726 Mon Sep 17 00:00:00 2001 From: kares Date: Fri, 20 Apr 2012 20:24:43 +0200 Subject: [PATCH] some oop into access impls AccessBase as a base class for different implementations change DefaultAccess and RubyAccess to use AccessBase (but keep backwards compatibility with the "module" way) --- README.rdoc | 2 +- lib/rhino/ruby.rb | 33 +++++++++--------- lib/rhino/ruby/access.rb | 54 ++++++++++++++++++++++++++++++ lib/rhino/ruby/attribute_access.rb | 36 ++++++-------------- lib/rhino/ruby/default_access.rb | 36 ++++++-------------- spec/rhino/access_spec.rb | 27 ++++++++++++--- 6 files changed, 115 insertions(+), 73 deletions(-) diff --git a/README.rdoc b/README.rdoc index 1125b39..d8eea40 100644 --- a/README.rdoc +++ b/README.rdoc @@ -143,7 +143,7 @@ that attempts to mirror only attributes as properties as close as possible: If you happen to come up with your own access strategy, just set it directly : - Rhino::Ruby::Scriptable.access = FooApp::BarAccess + Rhino::Ruby::Scriptable.access = FooApp::BarAccess.instance === Safe by default diff --git a/lib/rhino/ruby.rb b/lib/rhino/ruby.rb index 8beca4d..f25742d 100644 --- a/lib/rhino/ruby.rb +++ b/lib/rhino/ruby.rb @@ -38,24 +38,27 @@ module Rhino @@access = nil def self.access=(access) - @@access = access.is_a?(Module) ? access : begin - if access # Scriptable.access = :attribute - name = access.to_s.chomp('_access') - name = name[0, 1].capitalize << name[1..-1] - name = :"#{name}Access" - if Ruby.const_defined?(name) - Ruby.const_get(name) # e.g. Rhino::Ruby::AttributeAccess - else - const_get(name) # e.g. Rhino::Ruby::Scriptable::FooAccess - end - else # nil, false - access + @@access = ( access.respond_to?(:get) && access.respond_to?(:put) ) ? access : + begin + access = + if access && ! access.is_a?(Class) # Scriptable.access = :attribute + name = access.to_s.chomp('_access') + name = name[0, 1].capitalize << name[1..-1] + name = :"#{name}Access" + if Ruby.const_defined?(name) + Ruby.const_get(name) # e.g. Rhino::Ruby::AttributeAccess + else + const_get(name) # e.g. Rhino::Ruby::Scriptable::FooAccess + end + else # nil, false, Class + access + end + access.is_a?(Class) ? access.new : access end - end end def self.access - @@access ||= Ruby::DefaultAccess + @@access ||= Ruby::DefaultAccess.new end private @@ -87,7 +90,7 @@ module Rhino def unwrap @ruby end - + # abstract String Scriptable#getClassName(); def getClassName @ruby.class.to_s # to_s handles 'nameless' classes as well diff --git a/lib/rhino/ruby/access.rb b/lib/rhino/ruby/access.rb index ad44abe..05528bc 100644 --- a/lib/rhino/ruby/access.rb +++ b/lib/rhino/ruby/access.rb @@ -4,5 +4,59 @@ module Rhino autoload :DefaultAccess, "rhino/ruby/default_access" autoload :AttributeAccess, "rhino/ruby/attribute_access" + class AccessBase + + def has(object, name, scope) + # try [](name) method : + if object.respond_to?(:'[]') && object.method(:'[]').arity == 1 + return true unless object[name].nil? + end + yield + end + + def get(object, name, scope) + # try [](name) method : + if object.respond_to?(:'[]') && object.method(:'[]').arity == 1 + unless (value = object[name]).nil? + return Rhino.to_javascript(value, scope) + end + end + yield + end + + def put(object, name, value) + # try []=(name, value) method : + if object.respond_to?(:'[]=') && object.method(:'[]=').arity == 2 + return object[name] = Rhino.to_ruby(value) + end + yield + end + + end + + module DeprecatedAccess + + def has(object, name, scope, &block) + Rhino.warn "[DEPRECATION] `#{self.name}.has` is deprecated, please sub-class #{self.name} instead." + instance.has(object, name, scope, &block) + end + + def get(object, name, scope, &block) + Rhino.warn "[DEPRECATION] `#{self.name}.get` is deprecated, please sub-class #{self.name} instead." + instance.get(object, name, scope, &block) + end + + def put(object, name, value, &block) + Rhino.warn "[DEPRECATION] `#{self.name}.put` is deprecated, please sub-class #{self.name} instead." + instance.put(object, name, value, &block) + end + + private + def instance + @instance ||= self.new + end + + end + end end \ No newline at end of file diff --git a/lib/rhino/ruby/attribute_access.rb b/lib/rhino/ruby/attribute_access.rb index ad55112..927ae91 100644 --- a/lib/rhino/ruby/attribute_access.rb +++ b/lib/rhino/ruby/attribute_access.rb @@ -1,57 +1,41 @@ module Rhino module Ruby - module AttributeAccess + class AttributeAccess < AccessBase - def self.has(object, name, scope) + def has(object, name, scope) if object.respond_to?(name.to_s) || object.respond_to?(:"#{name}=") # might have a writer but no reader return true end - # try [](name) method : - if object.respond_to?(:'[]') && object.method(:'[]').arity == 1 - return true if object[name] - end - yield + super end - def self.get(object, name, scope) + def get(object, name, scope) name_sym = name.to_s.to_sym if object.respond_to?(name_sym) method = object.method(name_sym) if method.arity == 0 && # check if it is an attr_reader ( object.respond_to?(:"#{name}=") || object.instance_variables.find { |var| var.to_sym == :"@#{name}" } ) - begin - return Rhino.to_javascript(method.call, scope) - rescue => e - raise Rhino::Ruby.wrap_error(e) - end + return Rhino.to_javascript(method.call, scope) else return Function.wrap(method.unbind) end elsif object.respond_to?(:"#{name}=") return nil # it does have the property but is non readable end - # try [](name) method : - if object.respond_to?(:'[]') && object.method(:'[]').arity == 1 - if value = object[name] - return Rhino.to_javascript(value, scope) - end - end - yield + super end - def self.put(object, name, value) + def put(object, name, value) if object.respond_to?(set_name = :"#{name}=") return object.send(set_name, Rhino.to_ruby(value)) end - # try []=(name, value) method : - if object.respond_to?(:'[]=') && object.method(:'[]=').arity == 2 - return object[name] = Rhino.to_ruby(value) - end - yield + super end + extend DeprecatedAccess # backward compatibility for a while + end end end \ No newline at end of file diff --git a/lib/rhino/ruby/default_access.rb b/lib/rhino/ruby/default_access.rb index 344d012..579b7d1 100644 --- a/lib/rhino/ruby/default_access.rb +++ b/lib/rhino/ruby/default_access.rb @@ -1,54 +1,38 @@ module Rhino module Ruby - module DefaultAccess + class DefaultAccess < AccessBase - def self.has(object, name, scope) + def has(object, name, scope) if object.respond_to?(name.to_s) || object.respond_to?("#{name}=") return true end - # try [](name) method : - if object.respond_to?(:'[]') && object.method(:'[]').arity == 1 - return true if object[name] - end - yield + super end - def self.get(object, name, scope) + def get(object, name, scope) if object.respond_to?(name_s = name.to_s) method = object.method(name_s) if method.arity == 0 - begin - return Rhino.to_javascript(method.call, scope) - rescue => e - raise Rhino::Ruby.wrap_error(e) - end + return Rhino.to_javascript(method.call, scope) else return Function.wrap(method.unbind) end elsif object.respond_to?("#{name}=") return nil end - # try [](name) method : - if object.respond_to?(:'[]') && object.method(:'[]').arity == 1 - if value = object[name] - return Rhino.to_javascript(value, scope) - end - end - yield + super end - def self.put(object, name, value) + def put(object, name, value) if object.respond_to?(set_name = "#{name}=") return object.send(set_name, Rhino.to_ruby(value)) end - # try []=(name, value) method : - if object.respond_to?(:'[]=') && object.method(:'[]=').arity == 2 - return object[name] = Rhino.to_ruby(value) - end - yield + super end + extend DeprecatedAccess # backward compatibility for a while + end end end \ No newline at end of file diff --git a/spec/rhino/access_spec.rb b/spec/rhino/access_spec.rb index f0a23f0..b553f55 100644 --- a/spec/rhino/access_spec.rb +++ b/spec/rhino/access_spec.rb @@ -3,7 +3,7 @@ require File.expand_path('../spec_helper', File.dirname(__FILE__)) describe Rhino::Ruby::AttributeAccess do before(:all) do - Rhino::Ruby::Scriptable.access = Rhino::Ruby::AttributeAccess + Rhino::Ruby::Scriptable.access = Rhino::Ruby::AttributeAccess.new end after(:all) do @@ -68,8 +68,8 @@ describe Rhino::Ruby::AttributeAccess do it "might set access as a symbol" do prev_access = Rhino::Ruby::Scriptable.access - attr_access = Rhino::Ruby::AttributeAccess - module FooAccess; end + module FooAccess; end # backward compatibility + class Foo2Access; end begin @@ -77,19 +77,24 @@ describe Rhino::Ruby::AttributeAccess do lambda { Rhino::Ruby::Scriptable.access = :attribute }.should_not raise_error - Rhino::Ruby::Scriptable.access.should == attr_access + Rhino::Ruby::Scriptable.access.should be_a Rhino::Ruby::AttributeAccess Rhino::Ruby::Scriptable.access = nil lambda { Rhino::Ruby::Scriptable.access = :attribute_access }.should_not raise_error - Rhino::Ruby::Scriptable.access.should == attr_access + Rhino::Ruby::Scriptable.access.should be_a Rhino::Ruby::AttributeAccess lambda { Rhino::Ruby::Scriptable.access = :foo }.should_not raise_error Rhino::Ruby::Scriptable.access.should == FooAccess + lambda { + Rhino::Ruby::Scriptable.access = :foo2 + }.should_not raise_error + Rhino::Ruby::Scriptable.access.should be_a Foo2Access + lambda { Rhino::Ruby::Scriptable.access = :bar }.should raise_error @@ -99,4 +104,16 @@ describe Rhino::Ruby::AttributeAccess do end end + it "is backward compatibile with the 'module' way" do + Rhino::Ruby::AttributeAccess.respond_to?(:has).should be true + Rhino::Ruby::AttributeAccess.respond_to?(:get).should be true + Rhino::Ruby::AttributeAccess.respond_to?(:put).should be true + + Rhino::Ruby::Scriptable.access = Rhino::Ruby::AttributeAccess + + rb_object = Rhino::Ruby::Object.wrap Meh.new + rb_object.get('theMethod0', nil).should be_a(Rhino::Ruby::Function) + rb_object.has('non-existent-method', nil).should be false + end + end