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

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"

Related

How to define an original name scope in module/class with Ruby

How to define an original name scope in module/class with Ruby
I want to implement class like the following:
module SomeModule
extend OriginalNameScope
scope(:some) do
def method1
puts 1
end
def method2
puts 2
end
end
end
class SomeClass
include SomeModule
end
c = SomeClass.new
# I want to call methods like the following:
c.some_method1
c.some_method2
How to implement the OriginalNameScope module? I found out to get the method definitions in this method, but I don't know how to redefine methods with a prefix scope.
module OriginalNameScope
def scope(name, &method_definition)
puts method_definition.class
# => Proc
end
end
This is actually just a combination of some simple standard Ruby metaprogramming patterns and idioms:
module OriginalNameScope
def scope(name)
singleton_class.prepend(Module.new do
define_method(:method_added) do |meth|
if name && !#__recursion_guard__
#__recursion_guard__ = meth
method = instance_method(meth)
undef_method(meth)
define_method(:"#{name}_#{meth}") do |*args, &block|
method.bind(self).(*args, &block)
end
end
#__recursion_guard__ = nil
super(meth)
end
end)
yield
end
end
I just slapped this together, there's probably a lot that can be improved (e.g. use Refinements) and simplified.

How to pass a method to instance_eval?

I want to call instance_eval on this class:
class A
attr_reader :att
end
passing this method b:
class B
def b(*args)
att
end
end
but this is happening:
a = A.new
bb = B.new
a.instance_eval(&bb.method(:b)) # NameError: undefined local variable or method `att' for #<B:0x007fb39ad0d568>
When b is a block it works, but b as a method isn't working. How can I make it work?
It's not clear exactly what you goal is. You can easily share methods between classes by defining them in a module and including the module in each class
module ABCommon
def a
'a'
end
end
class A
include ABCommon
end
Anything = Hash
class B < Anything
include ABCommon
def b(*args)
a
end
def run
puts b
end
end
This answer does not use a real method as asked, but I didn't need to return a Proc or change A. This is a DSL, def_b should have a meaningful name to the domain, like configure, and it is more likely to be defined in a module or base class.
class B
class << self
def def_b(&block)
(#b_blocks ||= []) << block
end
def run
return if #b_blocks.nil?
a = A.new
#b_blocks.each { |block| a.instance_eval(&block) }
end
end
def_b do
a
end
end
And it accepts multiple definitions. It could be made accept only a single definition like this:
class B
class << self
def def_b(&block)
raise "b defined twice!" unless #b_block.nil?
#b_block = block
end
def run
A.new.instance_eval(&#b_block) unless #b_block.nil?
end
end
def_b do
a
end
end

Dynamic class creation, problem with function definition inside block: function body does not see local scope

I'm writing a function to dynamically create classes. I run into a problem with the *vars variable (below), where inside the block passed to Class::new, the "def initialize" method cannot see the value of *vars (and thus Ruby reports a unbound variable error on *vars).
What to do?
Thanks!
class MyParentClass
def do_something_with(*args)
end
def do_something_else_with(*vars)
end
end
def create_class(class_name,*vars)
new_class = Class::new(MyParentClass) do
def initialize(*args)
super
do_something_with(args)
do_something_else_with(vars)
end
end
Object::const_set(class_name.intern,new_class)
end
# Ruby: Error: *vars is unbound variable
# My notes: *vars is in scope inside the do..end block passed to Class::new, but cannot be seen inside def initialize (why?) . And, how to fix this?
I am not sure what are you trying to achieve with this contraption, but after some changes it works (well, depending on your definition of "to work"):
class MyParentClass
def do_something_with(*args)
puts "something #{args.inspect}"
end
def do_something_else_with(*vars)
puts "else #{vars.inspect}"
end
end
def create_class(class_name,*vars)
new_class = Class::new(MyParentClass) do
define_method :initialize do |*args|
super()
do_something_with(*args)
do_something_else_with(*vars)
end
end
Object::const_set(class_name.intern,new_class)
end
create_class :MyClass, 1, :foo, :bar
MyClass.new(2, :baz)
The trick to make vars visible is to define the constructor using a closure.

Open classes in a module

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

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