How to detect a BasicObject Proxy? - ruby

I am using a BasicObject Proxy and I need to detect whether I have passed an actual object OR such a proxy. Problem is that methods such as is_a? or class are not defined
module ControllerProxyable
extend ActiveSupport::Concern
included do
attr_reader :controller
delegate :current_user, to: :controller
end
def controller_proxy(controller)
# is_a? actually is NOT defined for a BasicObject causes the following to crash
#controller = if controller.is_a?(ControllerProxy)
controller
else
ControllerProxy.new(controller)
end
end
end
class ControllerProxy < BasicObject
def initialize(controller = nil)
#controller = controller
end
def some_proxy_method
end
# def respond_to and respond_to_missing not relevant here
end
This is an example of how I am using it :
class Foo
include ControllerProxyable
def initialize(controller: nil)
controller_proxy(controller)
end
def bar
bar ||= Bar.new(controller: controller)
end
end
class Bar
include ControllerProxyable
def initialize(controller: nil)
controller_proxy(controller)
end
end
The following therefore doesn't work
Foo.new(controller: nil).bar.some_proxy_method
How can I define is_a? for a Proxy (or actually identifying I am using a proxy) ?

Problem is that methods such as is_a? or class are not defined
The obvious solution to the problem "some method is not defined", is to define the method:
class ControllerProxy
def class; ControllerProxy end
def is_a?(mod)
self.class < mod
end
end
But! This defeats the whole purpose of a proxy, which is to be indistinguishable from the real thing. A better way would be IMO:
class ControllerProxy
def class; Controller end
def is_a?(mod)
Controller < mod
end
end

I actually found an answer for RUby 2 here. My question almost feels like a duplicate but in my case I was talking about an extension of the basicObject class not patching the BasicObject class itself
For such a use case this becomes :
def controller_proxy(controller)
# Note that is_a? is not defined for a proxy
#controller = if Kernel.instance_method(:class).bind(controller).call <= ServiceControllerProxy
controller
else
ServiceControllerProxy.new(controller)
end
end

Related

ruby private class method helper

Hi I am trying to create a helper for mass defining ruby methods as private class methods. In general one can define a method as a private class method by using private_class_method key work. But I would like to create a helper in the following style:
class Person
define_private_class_methods do
def method_one
end
def method_two
end
end
end
The way I planned to dynamically define this is in the following way, which is not at all working:
class Object
def self.define_private_class_methods &block
instance_eval do
private
&block
end
end
end
any ideas where I might be going wrong?
$ cat /tmp/a.rb
class Object
def self.define_private_class_methods &cb
existing = methods(false)
instance_eval &cb
(methods(false) - existing).each { |m| singleton_class.send :private, m }
end
end
class Person
define_private_class_methods do
def method_one
puts "¡Yay!"
end
end
end
Person.send(:method_one)
Person.public_send(:method_one)
$ ruby /tmp/a.rb
¡Yay!
/tmp/a.rb:18:in `public_send': private method `method_one'
called for Person:Class (NoMethodError)
Did you mean? method
from /tmp/a.rb:18:in `<main>'
Please note, that it’s hard to understand, what you are trying to achieve and possibly there is better, cleaner and more robust way to achieve this functionality.
Similar, yet different (and semantically more correct IMHO) to #mudasobwa's answer:
class Class
def define_private_class_methods(&definition)
class_methods_prior = methods
singleton_class.class_eval(&definition)
(methods - class_methods_prior).each do |method_name|
private_class_method method_name
end
end
end
class Person
define_private_class_methods do
def method_one
1
end
end
end
Person.method_one # !> NoMethodError: private method `method_one' called for Person:Class
Person.send :method_one # => 1
Note: It will not change the accessibility of a class method that you are currently overwriting.
You could define the methods in an anonymous module by passing the block to Module.new, make each instance method in the module private and extend your class with the module:
class Class
def define_private_class_methods(&block)
mod = Module.new(&block)
mod.instance_methods.each { |m| mod.send(:private, m) }
extend(mod)
end
end
This has the desired result:
class Person
define_private_class_methods do
def method_one
123
end
end
end
Person.send(:method_one)
#=> 123
Person.method_one
#=> private method `method_one' called for Person:Class (NoMethodError)
... and as a bonus, it also gives you a super method: (probably of little use)
class Person
def self.method_one
super * 2
end
end
Person.method_one
#=> 456
Of course, you don't have to use extend, you could just as well define the methods manually:
class Class
def define_private_class_methods(&block)
mod = Module.new(&block)
mod.instance_methods.each do |m|
define_singleton_method(m, mod.instance_method(m))
private_class_method(m)
end
end
end
The essential component is the anonymous module, so you have a (temporary) container to define the methods in.

How to call super in an initialize method when both class inheritance and Module include is being used?

How does the look-path decide where to call "super" if I have a module included along with class inheritance. My hunch is that by default it will use the initialize method in the module. Is this correct? And if so, how do I explicitly tell the code to use the initialize method in the inherited class instead?
Posted below is an example:
I want the Employee class to inherit initialize from Other and not Subject.
module Subject
def initialize
#observers = []
end
end
class Other
def initialize
#other_stuff = []
end
end
class Employee < Other
include Subject
attr_reader :name
def initialize(name)
super()
end
end
My hunch is that by default it will use the initialize method in the module.
Correct. If a class includes a module then the methods of that module will be replace inherited methods of the same name.
And if so, how do I explicitly tell the code to use the initialize method in the inherited class instead?
You're probably best off refactoring so that you don't have this problem.
However, there are several ways you could make Other's initialize method get called instead of Subject's.
How about something like this:
module Subject
def initialize
puts "subject initialize"
#observers = []
end
end
class Other
def initialize
puts "other initialize"
#other_stuff = []
end
end
class Employee < Other
alias_method :other_initialize, :initialize
include Subject
attr_reader :name
def initialize(name)
other_initialize
end
end
Employee.new('test')
If you run this, you'll see that Other's initialize method is called. Writing code like this is not a good idea, however.

Using a mixin method inside an instance method

Why does this not work:
class Myclass
include HTTParty
def dosomething
base_uri("some_url")
end
end
The base_uri method is a class method of HTTParty. It works fine if I call it from my class, outside of any instance methods, or from a class method, but when try to call it from an instance method I get "NoMethodError: undefined method `base_uri' for #"
Why? Shouldn't there be some way to refer to the HTTParty class from within my instance method so I can call that HTTParty class method?
I could change it to a class method, but then every instance of my class would have the same value for base_uri.
Why doesn't it work? Because that's not how Ruby works. Similarly, this doesn't work:
class Foo
def self.utility_method; ...; end
def inst_method
utility_method # Error! This instance has no method named "utility_method"
end
end
You could work around this by just doing:
class MyClass
include HTTParty
def dosomething
HTTParty.base_uri("some_url")
end
end
Let's look deeper at how method lookup works with modules. First, some code:
module M
def self.m1; end
def m2; end
end
class Foo
include M
end
p Foo.methods - Object.methods #=> []
p Foo.new.methods - Object.methods #=> [:m2]
class Bar
extend M
end
p Bar.methods - Object.methods #=> [:m2]
p Bar.new.methods - Object.methods #=> []
class Jim; end
j = Jim.new
j.extend M
p j.methods - Object.methods #=> [:m2]
As we see, you can use extend to cause an object (a class or instance) to use the 'instance' methods of a module for the object itself (instead of instances), but you cannot cause 'class methods' of the module to be inherited by anything. The closest you can get is this idiom:
module M2
module ClassMethods
def m1; end # Define as an instance method of this sub-module!
end
extend ClassMethods # Make all methods on the submodule also my own
def self.included(k)
k.extend(ClassMethods) # When included in a class, extend that class with
end # my special class methods
def m2; end
end
class Foo
include M2
end
p Foo.methods - Object.methods #=> [:m1]
p Foo.new.methods - Object.methods #=> [:m2]
If the HTTParty module used the above pattern, and so made the base_uri method available on your MyClass, then you could do this:
class MyClass
include HTTParty
def dosomething
self.class.base_uri("some_url")
end
end
...but that's more work than just directly referencing the module owning the method.
Finally, because this might help you, here's a diagram I made some years ago. (It's missing some core objects from Ruby 1.9, like BasicObject, but is otherwise still applicable. Click for a PDF version. Note #3 from the diagram is particularly applicable.)
(source: phrogz.net)
class Myclass
include HTTParty
def dosomething
Myclass.base_uri("some_url")
end
end

How do you delegate class methods?

I would like to make a class which delegates all instance and class methods to another class. My aim is to be able to give a class which is set in the app options a name which appears to fit in with the code around it.
This is what I have so far:
require 'delegate'
class Page < DelegateClass(PageAdapter)
# Empty
end
However, this only seems to delegate instance methods, not class methods.
You could define your own delegation scheme:
class Page < DelegateClass(PageAdapter)
##delegate_class=PageAdaptor
def self.delegate_class_method(*names)
names.each do |name|
define_method(name) do |*args|
##delegate_class.__send__(name, *args)
end
end
end
delegate_class_method ##delegate_class.singleton_methods
end
You don't want delegation, you want direct subclassing:
class Page < PageAdapter
# Empty
end
Proof:
class Foo
def self.cats?
"YES"
end
def armadillos?
"MAYBE"
end
end
class Bar < Foo; end
p Bar.new.armadillos?
#=> "MAYBE"
p Bar.cats?
#=> "YES"

Missing constant and "const_missing" inside "class << self" definition

I'm greatly confused by Ruby's behavior when defining const_missing and other class methods inside a class << self definition as opposed to using the def self.foo syntax.
I was trying to do something like this:
class Foo
class << self
def foo
puts MISSING
end
def const_missing(name)
puts "#{name} missing"
end
end
end
Foo.foo
I mostly use the the class << self syntax to define class methods. However, it did not work as expected. const_missing is never called. The above results in a NameError.
Defining both methods like this works as expected:
def self.foo
puts MISSING
end
def self.const_missing(name)
puts "#{name} missing"
end
I thought that the class << self syntax is just another way to define class methods, but completely equivalent to def self.foo? I've tested the above with MRI 1.8.7, 1.9.2 and JRuby 1.5.6. So obviously I'm missing something here?
Any hint is greatly appreciated.
Thanks, Martin
class << self is not a shortcut to define class methods. This syntax (I don't know the exact naming) opens the eigenclass from a object (in your case, a class). With that you can define methods to the object (not instance methods). But when you call a constant into the eigenclass, you are calling a constant from the eigenclass, not from the class. In this case you have to define a class method on the eigenclass to the const_missing, two ways to do that:
class Test
class << self
def foo
p MISSING
end
# First way: (syntax sugar for the second way)
def self.const_missing(name)
name
end
# Second way:
class << self # eigenclass of the eigenclass of the class
def const_missing(name)
name
end
end
end
end
Test.foo #=> :MISSING

Resources