Open classes in a module - ruby

I know I can execute the following to add methods to the String class
class String
def do_something
puts self.size
end
end
var = "test"
var.do_something
and this will return 4
I want to be able to have a module with a function that takes in a String, but be able to call the do_something method on this string (see below for example) - is it possible?
EDIT: Added sample code that is not working
module TestModule
class String
def do_something
puts self.size
end
end
def self.test(str)
str.do_something
end
end
This gives the error: undefined method 'do_something' for "hello":String (NoMethodError)

The way your code is written, you're defining a new class called TestModule::String. If you want to modify the built-in Ruby String class, you need to use the fully-qualified name of String (with the ""::") if you want to keep the declaration inside the module.
module TestModule
class ::String
def do_something
puts self.size
end
end
def self.test(str)
str.do_something
end
end
Adding the "::" tells Ruby that the String class that you want is not part of the TestModule.
It's probably cleaner to just declare String outside of TestModule in the same file.
If you don't want to pollute the global String class, you could just modify the specific String instance that you want to add the method to.
module TestModule
def self.test(str)
do_somethingify!(str)
str.do_something
end
def self.do_somethingify!(str)
unless str.respond_to? :do_something
str.instance_eval do
def do_something
puts size
end
end
end
end
end

Maybe this?
module TestModule
module_function
def test(str)
str.instance_eval{doSomething}
end
end
Test.test(str)
Edit Changed due to the change in the question
Just put the definition of doSomething outside out the TestModule class.
class String
def doSomething
puts size
end
end
module TestModule
module_function
def test(str)
str.doSomething
end
end

Related

Ruby: How to reference a variable defined outside of a module

How do I pass processor_pool to the method inside the module?
class Dummy
def initialize
processor_pool = Concurrent::FixedThreadPool.new(10)
#threadpool = Module.new do
extend Concurrent::Promises::FactoryMethods
def self.default_executor
return processor_pool # this cannot find the processor_pool variable
end
end
end
end
I get the same error even if I make it an instance variable like #processor_pool
Something like this (I simplified your class a bit to get rid of dependencies for the sake of example, but its structure is the same):
class Dummy
attr_reader :threadpool
def initialize
processor_pool = "It works"
#threadpool = Module.new do
define_method :default_executor do
return processor_pool
end
module_function :default_executor
end
end
end
Dummy.new.threadpool.default_executor # => "It works"

ruby pass self of caller of method into method being called automaticly

I'm trying to create a method that passes the caller as the default last argument. According to this, I only need:
class A
def initialize(object = self)
# work with object
end
end
so that in:
class B
def initialize
A.new # self is a B instance here
end
end
self will be B rather than A;
However, this doesn't seem to work. Here's some test code:
class A
def self.test test, t=self
puts t
end
end
class B
def test test,t=self
puts t
end
end
class T
def a
A.test 'hey'
end
def b
B.new.test 'hey'
end
def self.a
A.test 'hey'
end
def self.b
B.new.test'hey'
end
end
and I get:
T.new.a # => A
T.new.b # => #<B:0x000000015fef00>
T.a # => A
T.b # => #<B:0x000000015fed98>
whereas I expect it to be T or #<T:0x000000015fdf08>. Is there a way to set the default last argument to the caller?
EDIT:
class Registry
class << self
def add(component, base=self)
self.send(component).update( base.to_s.split('::').last => base)
end
end
end
The idea is pretty simple, you would use it like this
class Asset_Manager
Registry.add :utilities
end
and you access it like:
include Registry.utilities 'Debugger'
I'm trying to de-couple classes by having a middle-man management type class that takes care of inter-class communications, auto-loading of missing classes and erroring when it doesn't exist, it works but I just want to be able to use the above rather than:
class Asset_Manager
Registry.add :utilities, self
end
It just feels cleaner, that and I wanted to know if such a thing was possible.
You can't escape the explicit self. But you can hide it with some ruby magic.
class Registry
def self.add(group, klass)
puts "registering #{klass} in #{group}"
end
end
module Registrable
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def register_in(group)
Registry.add(group, self)
end
end
end
class AssetManager
include Registrable
register_in :utilities
end
# >> registering AssetManager in utilities
In short, you can't.
Ruby resolves the default arguments in the context of the receiver. That is, the object before the . in a method call. What you called the receiver should be the caller, actually.
class A
def test1(value = a)
puts a
end
def test2(value = b)
puts b
end
def a
"a"
end
end
a = A.new
a.test1 #=> a
def a.b; "b" end
a.test2 #=> b
If I were you, I would use the extended (or included) hook, where both the extending class and the extended module can be accessed. You can program what ever logic you want based on the information.
module Registry
module Utilities
def self.extended(cls)
#puts cls
::Registry.send(component).update( cls.to_s.split('::').last => cls)
end
end
end
class Asset_Manager
extend Registry::Utilities
end

Add method to standard class inside a module

I want to add new method to class String, for example. But I don't want to make this change global (keeping classes clean is good, yes?).
So, instead of this code
class String
def is_palindrome?
self == self.reverse
end
end
module MyModule
class MyClass
def filter_palindrome(str_arr)
str_arr.select { |s| s.is_palindrome? }
end
end
end
I want to have something like this:
module MyModule
class String
def is_palindrome?
self == self.reverse
end
end
class MyClass
def self.filter_palindrome(str_arr)
str_arr.select { |s| s.is_palindrome? }
end
end
end
But, of course, it's not working (undefined method 'is_palindrome?' for :String). So, is there any point in what I want? And if there is, what is the best way to achieve it?
If you are using Ruby 2.0, you can try refinements.
module MyModule
module StringAlter
refine String do
def is_palindrome?
self == self.reverse
end
end
end
end
using MyModule::StringAlter
module MyModule
class MyClass
def self.filter_palindrome(str_arr)
str_arr.select { |s| s.is_palindrome? }
end
end
end
If prior to Ruby 2.0, you cannot achieve this directly. Changes made to String will be global. However, instead of s.is_palindrome?, why not write a helper method and call it like is_palindrome?(s). Then you don't have to reopen String and you can restrict is_palindrome? to be available only in some given scope.
This is the way Python does (self), and so as to C# extension method.

Why I cannot mixin an assignment method from a module

Suppose I have a module like this:
module MyAssigments
def dummy_assignment=(value)
puts "it's not assigned #{value}"
end
end
and a class that includes it like this:
class MyClass
include MyAssigments
def x=(value)
dummy_assignment=(value)
end
end
then
o = MyClass.new
o.x="anything"
does not print anything on screen, why ?
Ruby has a syntax/paring quirk here: using an assginemnt method for self has to be explicitly qualified thus:
def x=(value)
self.dummy_assignment=value
end
without the self. part, it is assumed by ruby to be an assignment to a local variable.

Ruby module with a static method call from includer class

I need to define the constant in the module that use the method from the class that includes this module:
module B
def self.included(base)
class << base
CONST = self.find
end
end
end
class A
def self.find
"AAA"
end
include B
end
puts A::CONST
But the compiler gives the error on the 4th line.
Is there any other way to define the constant?
The more idiomatic way to achieve this in Ruby is:
module B
def self.included(klass)
klass.class_eval <<-ruby_eval
CONST = find
ruby_eval
# note that the block form of class_eval won't work
# because you can't assign a constant inside a method
end
end
class A
def self.find
"AAA"
end
include B
end
puts A::CONST
What you were doing (class << base) actually puts you into the context of A's metaclass, not A itself. The find method is on A itself, not its metaclass. The thing to keep in mind is that classes are themselves objects, and so have their own metaclasses.
To try to make it clearer:
class Human
def parent
# this method is on the Human class and available
# to all instances of Human.
end
class << self
def build
# this method is on the Human metaclass, and
# available to its instance, Human itself.
end
# the "self" here is Human's metaclass, so build
# cannot be called.
end
def self.build
# exactly the same as the above
end
build # the "self" here is Human itself, so build can
# be called
end
Not sure if that helps, but if you don't understand it, you can still use the class_eval idiom above.
In your specific case.
module B
def self.included(base)
base.const_set("CONST", base.find)
end
end
class A
def self.find
"AAA"
end
include B
end
puts A::CONST
Despite it works, it's a little bit messy. Are you sure you can't follow a different way to achieve your goal?
module B
def self.included(base)
class << base
CONST = self.find
end
end
end
class A
class << self
def self.find
"AAA"
end
end
include B
end
then the compiler error is fixed, pls try.

Resources