How to detect my anonymous modules in ancestors of some class - ruby

During long run I prepend foreign classes with my anonymous modules (that are being created on the fly.)
At some point, given an instance of arbitrary class, I need to list my anonymous modules, prepended to it. Unfortunately, Module#new, unlike Class#new, does not accept a parent parameter (in other words, they cannot all have a same common dummy parent, that I could use to sieve my modules out of all ancestors.)
I could define a specific my_litmus_dummy_method on each of my anonymous modules and check for it while ancestors.select, but this is ugly.
I could include DummyLitmusModule in each of my anonymous modules and check for it being an ancestor while ancestors.select, but this is ugly either.
So, my question is: is there any elegant way to mark my anonymous modules so that later on I could easily sieve them out of all modules list?
UPD Code example
def patch_class klazz
klazz.send :prepend, Module.new { define_method(:my_method) {} }
end
...
# load list of classes from somewhere and patch them
classes = my_parse_yaml('config.yml')
classes.each do |c|
patch_class c
end
...
# somewhere in other place
instance.patches # ⇐ I want to implement this
At this point I need^W want to know, what methods were patched. I kinda reimplement alias_method_chain from Rails and I want to report the “usages” to my interface user.

What about patching Module#prepend?
class Module
attr_reader :mods_prepended
alias :old_prepend :prepend
def prepend(*mods)
#mods_prepended =|| {}
(#mods_prepended[self] =|| []).concat(mods)
end
old_prepend(*mods)
end
class C
def patches
Module.mods_prepended[self.class]
end
end
Then the desired information is given by:
C.new.patches

Store your anonymous modules somewhere and then you will be able to find them:
patches = [Module.new] # some other module
c = Class.new
m = Module.new
c.prepend m
patches << m
instance = c.new
used_patches = patches.select{|p| instance.is_a? p}

Related

How to get a class name given that self could be a class or an instance?

I have a module that provides logging functionality. Some classes extend it, others include it. As part of the logging, I pass in a class name.
If I do this:
global_logger.call(level, self, msg)
It could log either:
WARN -- Some::Class: some msg
OR
WARN -- #<Some::OtherClass:0x00007fdc04907710>: some msg
based on if the module was extended or included. I can call self.class instead, but then the the other one turns into Class.
Is there a way to get a class name(without the #<...:0x00007fdc04907710>), given that you don't know if self is a class or an instance?
There are a number of things you could do, but one of the easiest is probably to see if the passed object is a class before trying to extract other information about it. For example:
Object.kind_of? Class #=> true
Object.new.kind_of? Class #=> false
You can then decide what methods are appropriate to call based on whether or not it's a class. For example:
p Object.new.class.name #=> "Object"
P Object.name #=> "Object"
We know that, for a given module M, you can determine if a class C has included M by writing
C.included_modules.include?(M)
To determine if a class C has extended M you may execute
C.singleton_class.included_modules.include?(M)
because extending a module to a class is equivalent to including the same module to the class' singleton class.
See Module#included_modules and Object#singleton_class.
Here is an example.
module M
def m; end
end
class C
extend M
end
a = C.singleton_class.included_modules
#=> [M, Kernel]
a.include?(M)
#=> true

How to inspect a module

How can I find out the constructs (methods, constants, etc.) defined in a module?
Let's say there's require 'observer', and I would like to see all that are defined within 'observer'. How can I?
Short answer: You can't, not with absolute certainty.
Long answer: This a product of how Ruby is a very dynamic language at its core and imposes almost no constraints on what a require statement might do. Any number of modules and classes can be created by a library, and there's no requirement for these to be grouped together neatly.
Even if you go through the trouble of taking a snapshot of all defined classes and methods before you require it, and then another one after to find out what's been added there's no assurance you've captured them all. Some classes might be loaded or defined well after the require statement is finished.
The best way to find out is to read through the source. There you'll see all the different modules and classes that could be defined even if they're not triggered by your code.
Reflection tools like methods help to a degree, but it's also highly misleading. Methods can be defined at a later point in time, you may need to exercise the code more thoroughly for them to show up.
If you use pry, simply do ls ModuleName
ls shows all the available methods and instance variables of a certain module or class or instance. More about pry: http://pryrepl.org/
Or you can do
ModuleName.instance_methods to get instance_methods
ModuleName.instance_variables to get instance_variables
As another answer state, it's almost impossible (still doable in a brittle way) to get a full picture of what you require by just an arbitrary require
imo, this kind of implementation itself is brittle and prone to error, unless its your own module and you have full control of the API. But still not a good idea.
If I do not understand the question wrong.
You can do like this, to read the property of Module.
Example:
1st Version:
require "nokogiri"
p Nokogiri.methods # It will print for you all methods in that Module,
# also available methods from ruby.
2nd Version
x = require "nokogiri"
p x.methods #available methods there private, public, protected methods and etc...
p x.private_methods # prints bunch of private methods, [:Nokogiri]
You want to execute Observer methods from outside the module you wish to examine. Here is an example of what you might do.
module Observer
def self.show_all(mod)
puts "owner of methods = #{mod.methods(false)}"
puts "owner of instance methods = #{mod.instance_methods(false)}"
puts "instance variables = #{mod.instance_variables}"
puts "ancestors = #{mod.ancestors}"
puts "included modules = #{mod.included_modules}"
end
end
module A
module B
include Comparable
#a = 1
def self.b; end
def c; end
module C
end
end
end
Observer.show_all(A::B)
# owner of methods = [:b]
# owner of instance methods = [:c]
# instance variables = [:#a]
# ancestors = [A::B, Comparable]
# included modules = [Comparable]
class D; end
class E<D; end
class F<E
include Comparable
#a = 1
def self.e; end
def f; end
end
Observer.show_all(F)
# owner of methods = [:e]
# owner of instance methods = [:f]
# instance variables = [:#a]
# ancestors = [F, Comparable, E, D, Object, Kernel, BasicObject]
# included modules = [Comparable, Kernel]

Ruby - gain class and instance methods from a module which includes another module?

Apologies for the title, suggestions to make it clearer are welcome.
I have created a module (we'll denote this by M) which, when included inside a class, will cause it to obtain new class methods and instance methods (Apologies if my terminology is incorrect). This is achieved by = class F including the A::ModuleInstanceMethods module in the code below.
Now that I've done that, I am trying to create a new module (we'll call this new module M') which includes the module M, such that when M' is included in a class, the class should gain the appropriate class and instance methods as per class F. This is where I get stuck. Examples of such a class is G in the code below.
I'd also like classes which include module M'' (module M'' will includes M') to have the same functionality. An example will be class H in the code below. The same should go for classes which include M''' (which itself includes M''), classes which M'''' (which itself includes M'''), and so on. It's pretty similar to an inheritance hierarchy.
If my textual explanation is confusing, do read the code below. In particular, I'd like to resolve the failures caused by calling G.class_method_one and H.class_method_one, but I lack the knowledge to do so.
I know it is possible to just extend the A::ModuleClassMethods module in the classes that I'm interested in, but I wish to avoid doing this. The same could also be achieved by manually adding the portion of the self.included function in A::ModuleInstanceMethods with the base.instance_of? Class, but if possible I'd like to do it programatically instead of copying and pasting the same code in many different sites.
module A
module ModuleClassMethods
def class_method_one
2
end
end
module ModuleInstanceMethods
def instance_method_one
3
end
def self.included(base)
if base.instance_of? Class
base.extend(A::ModuleClassMethods)
elsif base.instance_of? Module
# Intended functionality:
# When modules that `include` A::ModuleInstanceMethods are themselves
# included in a class (such as module `A::D` included in class `F`),
# class `F` will get the functions defined in the A::ModuleClassMethods
# module as class level methods
end
end
end
module D
include A::ModuleInstanceMethods
end
module E
include D
end
end
class F
include A::ModuleInstanceMethods
end
class G
include A::D
end
class H
include A::E
end
F.class_method_one # 2
F.new.instance_method_one # 3
G.new.instance_method_one # 3
# below statement fails
# G.class_method_one
H.new.instance_method_one # 3
# below statement fails
# H.class_method_one
Thank you.
I've seem to have figured it out. This solution makes use of module_eval. For modules, it adds a self.included function which calls A::ModuleInstanceMethods.included. I wouldn't mind learning about more elegant solutions.
module A
module ModuleClassMethods
def class_method_one
2
end
end
module ModuleInstanceMethods
def instance_method_one
3
end
def self.included(base)
if base.instance_of? Class
base.extend(A::ModuleClassMethods)
elsif base.instance_of? Module
base.module_eval {
def self.included(base)
A::ModuleInstanceMethods.included(base)
end
}
end
end
end
end

Metaprogramming Ruby: defining inheritance in class_eval

I would like to expand the functionality of some class using class_eval. I would like to force the class to inherit some methods from some other class.
I.e.:
SomeClass.class_eval do
# force inheritence from some other class
end
What's the best way to achieve it?
If overriding existing functionality is a hard requirement here, you need to have those existing methods defined in a module that's also included.
class SomeClass
include DefaultBehaviour
end
module DefaultBehaviour
def run
puts "ran default"
end
end
module AlternateBehaviour
def run
puts "ran alternate"
end
end
SomeClass.class_eval {
include AlternateBehaviour
}
SomeClass.new.run #=> "ran alternate"
The reason for this is because of ruby's method lookup path.
It starts off as SomeClass -> Object.
When you include AlternateBehaviour, it becomes SomeClass -> AlternateBehaviour -> Object. So methods defined directly on SomeClass still take precedence.
However, if those methods are defined on DefaultBehaviour, the lookup path becomes SomeClass -> AlternateBehaviour -> DefaultBehaviour -> Object, so your alternate method takes priority. Whichever module was included most recently is the highest priority.
In the case where you do not have control of the original class, you can do instead:
module AlternateBehaviour
def self.included(base)
base.send(:remove_method, :run)
end
def run
puts "ran alternate"
end
end
Though at this point, one starts to wonder whether you might be better off by just doing
SomeClass.class_eval {
def run
"ran alternate"
end
end
Try using include and extend, both explained here. They only work with modules; you just can't modify/add superclasses of a class in Ruby after it has already been created.
Only one problem: you can't override already existing methods in a class for the explained in the third comment to this post.
Also see this topic for more information.

Writing Ruby Libraries - hiding methods from outside the module

I'm writing a Ruby library which has a module with a bunch of classes inside it. Many of these classes need to be usable and modifiable by calling scripts, but I don't want (some of) the initializers to be visible/callable:
module MyLib
class Control
def initialize
# They can use this
end
def do_stuff
Helper.new('things')
end
end
class Helper
# Shouldn't be visible
def initialize(what)
#what = what
end
def shout
#what
end
end
end
c = MyLib::Control.new
h = c.do_stuff
p h.shout
# => "things"
# ^ All of this is desired
# v This is undesirable
p MyLib::Helper.new('!')
# => <MyLib::Helper #what='!'>
If it's a simple thing, then I'd also appreciate the generated RDoc not even include the .new method for the Helper class either. Any ideas?
Thanks for reading!
My original answer was completely wrong, as #Matthew pointed out. But there are other workarounds. For instance, you can assign an anonymous class to a class variable on Control, and still define methods as normal by using class_eval:
module MyLib
class Control
def initialize
end
def do_stuff
##helper.new('things')
end
##helper = Class.new
##helper.class_eval do
def initialize(what)
#what = what
end
def shout
#what
end
end
end
end
The snippet
c = MyLib::Control.new
h = c.do_stuff
p h.shout
still writes "things", but now there's no way to access ##helper except through the class variable. If someone really wants to access it my reopening the Control class or using class_eval, there's nothing to stop them, but that's just something you have to deal with in a dynamic language.
I chose to assign the anonymous class to a class variable so that it would only be created once; but if you don't care about redefining the anonymous class many times, there's no reason it couldn't be an instance variable.
Ruby has access control.

Resources