Making functions available as instance methods - Ruby - ruby

module Example
def a (parameter)
#do something
end
def b (parameter_2)
#do something
end
end
class C
include Example
end
This is a sample Ruby code. I wish to know how to make functions a and b available to class C as instance methods. These will be tested automatically by another program.

They already are.
c = C.new
c.methods - 0.methods
# => [:a, :b]
By virtue of including the module, you've extended the functionality of C to include that of the module Example.

Related

Dynamically define a super method for an instance of a class in Ruby

Say we have a class that we cannot change,
class C
def foo
super
puts "Low!"
end
end
We'll need to dynamically define the method foo in something that we'll be able to inject into C's ancestry chain. The behavior of super must be specific to a given object, not class-wide. We'll be able to enclose that logic into an anonymous module (let's name it for now):
module M
def foo
puts "High!"
end
end
Extending an instance of C with the module:
c = C.new
c.extend(M)
c.foo
# High!
will not work since we've put the method from the module before the method we've defined in the class. Looking at our object's ancestors
c.singleton_class.ancestors
# => [#<Class:#<C:0x00005652be630b20>>, M, C, ...]
I came up with an ugly workaround, which is redefining the methods from our class in our singleton_class, i.e.
c.define_singleton_method(:foo, c.class.instance_method(:foo))
c.foo
# High!
# Low!
While this works (does it work? I've tested it for a bit and it seems to, but I'm no longer certain), I wonder whether I'm missing something obvious and there's an easier way to dynamically define a "super" method for an instance of a class.
To be clear, we want to be able to extend another instance of C with another module, i.e.
C.new.extend(Module.new do
def foo
puts "Medium!"
end
end).foo
# Medium!
# Low!
and have its output not tainted by other instances.
Now that I understand you're trying to work around an issue in some third-party code, I can suggest a more reasonable solution. In the code below, I'm thinking that B and C are defined by the gem and you don't want to change their source code, but you want to inject some code into the place where C#foo calls B#foo.
class B
def foo
puts "Highest!"
end
end
class C < B
def foo
super
puts "Low!"
end
end
module CE
def foo
super
foo_injected
end
end
C.include(CE)
module M
def foo_injected
puts "High!"
end
end
c = C.new
c.extend(M)
p c.singleton_class.ancestors
c.foo
The output is:
[#<Class:#<C:0x000055ce443366a8>>, M, C, CE, B, Object, Kernel, BasicObject]
Highest!
High!
Low!

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]

Sharing variables between Ruby classes within same module

I have module
# File 1.rb
module Search
class A
attr_accessor :results
def find_results
self.results = [somethings]
end
def do_something_with_results
b = B.new.do_something
b.response #=> something
c = C.new.use_b_response_do_something
return c.did_something
end
end
end
# File 2.rb
module Search
class B
end
class C
end
end
I have module Search with classes A, B, C
Class A does something and brings back data which needs to be shared with Classes B and C to do something with that data (refine it, send it somewhere, return true or false).
How can I share this information between the classes? I have been doing this which I think is wrong
def do_something
b = B.new.doing_something
c = C.new.something_else(b.some_attr)
end
which is not efficient
I don't see any issues in your code. You seem to be doing things as it should be done. Using methods to get and pass data around.
One thing that could make your code better is passing b to c instead.
def do_something
b = B.new.doing_something
c = C.new(b).something_else
end
But it depends on your use case...

How to share methods between ruby modules

Here is what I tried:
module A
def self.method1; "method1"; end
def method2; "method2"; end
end
module B; include A; end
B.method1 # => error
B.method2 # => error
B::method1 # => error
B::method2 # => error
I want to avoid copying and pasting equivalent code between two modules. The reason I'm using modules instead of classes here is because I don't need more than one instance of each module, as they simply hold constants (other modules, at this point).
What is the best way to solve this problem?
Plain include only gives you instance methods (method2 in your particular piece of code). If you want to share module-level methods - extract them to separate module and extend other modules with it:
module A
extend self # to be able to use A.method1
def method1
"method1"
end
end
module B
extend A
end
B.method1 # => "method1"
It is also possible get module-level methods by include, but with a little twist, using hook method:
module A
def self.included(other)
other.extend ModuleMethods # this is where the magic happens
end
def instance_method
'instance method'
end
module ModuleMethods
def module_method
'module method'
end
end
extend ModuleMethods # to be able to use A.module_method
end
module B
include A
end
B.module_method #=> "module method"
B.instance_methods #=> [:instance_method]
First of all, please note that A.method2 won't work either. You can create objects including A (or B) that will have method2:
class C
include B # (or A)
end
c = C.new
c.method2
So, for method2 it just works as you intended.
Regarding method1, it is a singleton method of the object A and there is no way to inherit it.

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