Ruby metaprogramming: how to make a modular method see a class variable

For example, I have a module and a class:

module SimpleModule
  def self.class_hello
    puts "hello from #{@@name}"
  end
end

class SimpleClass
  @@name = 'StackOverFlow'
  def self.test
    SimpleModule.class_hello
  end
end

Then I test by calling the module method from the class:

SimpleClass.test

I meet an exception:

uninitialized class variable @@ name in SimpleModule (NameError)

I know, because the module size is not the same as the class. So my question is: how can I share the SimpleClass space for the SimpleModule area?

I put metaprogramming because here is just a simple example, after which I will advance by calling a dynamic module from a dynamic class. (that’s why I don’t want to use any keyword like include or extend )

@Edit Ruby extends . :

# implementation
class Class
  def custom_extend(module_name)
    module_name.methods(false).each do |method|
      define_singleton_method(method) do |*args, &block|
        module_name.send(method, *args, &block)
      end
    end
  end
end

:

# -------------------------------------------------------------
# Demonstration
module SimpleModule
  def self.class_hello_world
    puts 'i am a simple module boss'
  end

  def self.class_hello_name
    puts "hello from #{@@name}"
  end
end

class SimpleClass
  custom_extend  SimpleModule
  @@name = 'StackOverFlow'
end

:

SimpleClass.class_hello_world  # work
SimpleClass.class_hello_name   # not work
+4
3

. include, extend, append_features module_function. custom_include .

: @7stud , .

class Class
  def custom_extend(module_name)
    module_name.instance_methods(false).each do |method|
      define_singleton_method(method) do |*args, &block|
        module_name.instance_method(method).bind(self).call(*args, &block)
      end
    end
  end
end

module SimpleModule
  def class_hello
    puts "hello from #{@name}"
  end
end

class SimpleClass
  @name = 'class'
  custom_extend SimpleModule

  def self.test
    class_hello
  end
end

SimpleClass.test
#=> hello from class

:

module SimpleModule
  def class_hello
    puts "hello from #{@name}"
  end
end

class SimpleClass
  @name = 'StackOverFlow'
  extend SimpleModule

  def self.test
    class_hello
  end
end

SimpleClass.class_hello

. (?)

SimpleClass SimpleModule . , NameError. - name.

:

module SimpleModule
  def self.class_hello(name='')
    puts "hello from #{name}"
  end
end

class SimpleClass
  @@name = 'StackOverFlow'
  def self.test
    SimpleModule.class_hello(@@name)
  end
end

klass:

module SimpleModule
  def self.class_hello(calling_class=self)
    calling_class.class_eval{
      puts "hello from #{@name}"
    }
  end
end

class SimpleClass
  @name = 'StackOverFlow'
  def self.test
    SimpleModule.class_hello(self)
  end
end

SimpleClass.test

binding:

module SimpleModule
  def self.class_hello(b)
    puts "hello from #{b.eval('@@name')}"
  end
end

class SimpleClass
  @@name = 'StackOverFlow'
  def self.test
    SimpleModule.class_hello(binding)
  end
end

SimpleClass.test

my_ruby_extend SimpleModule

, my_ruby_extend. , .

, Ruby, SimpleClass SimpleModule . SimpleModule, SimpleClass.

+4

.

ruby ​​ extend() :

module SimpleModule
  def self.class_hello_world
    puts 'i am a simple module boss'
  end

  def self.class_hello_name
    puts "hello from #{@@name}"
  end
end

class SimpleClass
  custom_extend  SimpleModule

, :

module Dog
  def self.greet
    puts "hello"
  end
end

class Cat
  extend Dog
end

Cat.greet

--output:--
`<main>': undefined method `greet' for Cat:Class (NoMethodError)

extend() :

module Dog
  def greet
    puts "hello"
  end
end

class Cat
  extend Dog
end

Cat.greet

--output:--
hello

, extend() , (, , self)), Cattonton ( , Cat). ruby, include() extend() ( , self). :

  • , . def self.method_name
  • , . def some_method

include() extend() # 2.

@@variables, , , @@variables , - . , .. @variables, def:

def my_extend(some_module)
  singleton_class.include some_module
end

module Dog
  def greet
    puts @greeting
  end

  private
  def sayhi
    puts "hi"
  end
end

class Cat
  @greeting = "hello"
  my_extend Dog
end

Cat.greet
#Cat.sayhi  #=>`<main>': private method `sayhi' called for Cat:Class (NoMethodError) 

Cat.class_eval {sayhi}  #Change self to the Cat class so the implicit
                        #self variable that calls sayhi is equal to Cat

--output:--
hello
hi

my_include include.:)

my_include():

class Class

  def my_include(module_)
    #For public and protected methods:
    module_.instance_methods(include_super=false).each do |meth_name|
      meth = module_.instance_method(meth_name)
      define_method(meth_name) do 
        meth.bind(self).call
      end
    end

    #For private methods:
    module_.private_instance_methods(include_super=false).each do |meth_name|
      meth = module_.instance_method(meth_name)
      define_method(meth_name) do
        meth.bind(self).call
      end
      private :"#{meth_name}"
    end

  end
end

module Dog
  def greet
    puts "hello"
  end

  def go
    puts "run, run run"
  end

  private
  def sayhi
    puts "hi"
  end

end

class Cat
  my_include Dog
end

c = Cat.new
c.greet
c.go
c.sayhi 

--output:--
hello
run, run run
 #=>`<main>': private method `sayhi' called for #<Cat:0x007fc014136f60> (NoMethodError)

my_extend():

class Class

  def my_include(module_)
    #For public and protected methods:
    module_.instance_methods(include_super=false).each do |meth_name|
      meth = module_.instance_method(meth_name)
      define_method(meth_name) do 
        meth.bind(self).call
      end
    end

    #For private methods:
    module_.private_instance_methods(include_super=false).each do |meth_name|
      meth = module_.instance_method(meth_name)
      define_method(meth_name) do
        meth.bind(self).call
      end
      private :"#{meth_name}"
    end

  end

  def my_extend(module_)
    singleton_class.my_include module_
  end

end


module Dog
  def greet
    puts @greeting
  end

  private
  def sayhi
    puts "hi"
  end
end

class Cat
  @greeting = "hello"
  my_extend Dog
end

Cat.greet
#Cat.sayhi  #=>private method `sayhi' called for Cat:Class (NoMethodError)
Cat.class_eval {sayhi}

--output:--
hello
hi
+4

, .

module SimpleModule
  def self.class_hello
    puts "hello from #{@@name}"
  end
end

class SimpleClass
  @@name = 'StackOverFlow'
end

SimpleModule.class_hello
  # NameError: uninitialized class variable @@name in SimpleModule

?

, class_hello , &&name. class_hello. [Module # class_variable_get] (https://ruby-doc.org/core-2.2.1/Module.html#method-i-class_variable_get ), .

module SimpleModule
  def self.class_hello(klass)
    puts "hello from #{ klass.class_variable_get(:@@name) }"
  end
end

SimpleModule.class_hello(SimpleClass)
  # hello from StackOverFlow
0

Source: https://habr.com/ru/post/1668803/


All Articles