programing

루비 모듈에서 인스턴스 메소드를 포함하지 않고 호출할 수 있습니까?

lovejava 2023. 6. 3. 07:50

루비 모듈에서 인스턴스 메소드를 포함하지 않고 호출할 수 있습니까?

배경:

여러 인스턴스 메서드를 선언하는 모듈이 있습니다.

module UsefulThings
  def get_file; ...
  def delete_file; ...

  def format_text(x); ...
end

그리고 저는 이 방법들 중 몇 가지를 클래스 내에서 부르고 싶습니다.루비에서 일반적으로 이 작업을 수행하는 방법은 다음과 같습니다.

class UsefulWorker
  include UsefulThings

  def do_work
    format_text("abc")
    ...
  end
end

문제

include UsefulThings에서 모든 방법을 가져옵니다.UsefulThings이 경우에 나는 단지 원할 뿐입니다.format_text으로 그고분명원않습다니지하히리를 원하지 않습니다.get_file그리고.delete_file.

이에 대한 몇 가지 가능한 솔루션을 확인할 수 있습니다.

  1. 어떻게든 메소드를 모듈에 포함시키지 않고 직접 호출합니다.
    • 이 작업이 어떻게 수행될 수 있는지 모르겠습니다. (따라서 이 질문)
  2. 든 어게든포다니함합떻다를 합니다.Usefulthings그리고 그것의 방법 중 일부만 가지고 옵니다.
    • 또한 이 작업이 수행될 수 있는지 여부를 모르겠습니다.
  3. 클래스를 . 를 포함합니다.UsefulThings그 안에서, 그 다음에 위임합니다.format_text그 대리인 인스턴스까지
    • 이것은 효과가 있겠지만 익명의 프록시 클래스는 해킹입니다.우웩.
  4. 모듈을 두 개 이상의 작은 모듈로 분할합니다.
    • 이것도 효과가 있을 것이고, 아마도 제가 생각할 수 있는 최고의 솔루션일 것입니다. 하지만 결국 수십 개의 모듈이 확산될 것이기 때문에 피하고 싶습니다. 이를 관리하는 것은 부담스러울 것입니다.

하나의 모듈에 관련 없는 기능이 많은 이유는 무엇입니까?그건…ApplicationHelper우리 팀이 다른 곳에 속할 만큼 구체적이지 않은 것을 위한 쓰레기 매립지로 결정한 레일 앱에서.대부분 모든 곳에서 사용되는 독립형 유틸리티 방법입니다.다른 도우미들로 나눌 수도 있지만 30명이 있을 겁니다 각각 한 가지 방법으로...으로 보입니다.

(기존 모듈을 변경하거나 새 모듈을 만들지 않고) 일회용 단일 통화를 수행하는 가장 빠른 방법은 다음과 같습니다.

Class.new.extend(UsefulThings).get_file

모듈의 메서드가 모듈 기능으로 전환되면 다음과 같이 선언된 것처럼 간단히 모드에서 호출할 수 있습니다.

module Mods
  def self.foo
     puts "Mods.foo(self)"
  end
end

아래의 module_function 접근 방식은 모든 Mod를 포함하는 클래스를 중단하지 않도록 합니다.

module Mods
  def foo
    puts "Mods.foo"
  end
end

class Includer
  include Mods
end

Includer.new.foo

Mods.module_eval do
  module_function(:foo)
  public :foo
end

Includer.new.foo # this would break without public :foo above

class Thing
  def bar
    Mods.foo
  end
end

Thing.new.bar  

그런데, 애초에 관련이 없는 함수들의 집합이 같은 모듈 안에 모두 포함되어 있는 이유가 궁금합니다.

다음과 같은 경우 여전히 작업을 포함하도록 편집됨public :foo다음으로 호출됩니다.module_function :foo

모듈을 "소유"한 경우 이를 수행하는 또 다른 방법은 다음과 같습니다.module_function.

module UsefulThings
  def a
    puts "aaay"
  end
  module_function :a

  def b
    puts "beee"
  end
end

def test
  UsefulThings.a
  UsefulThings.b # Fails!  Not a module method
end

test

다른 클래스에 모듈을 포함하지 않고 이러한 메서드를 호출하려면 모듈 메서드로 정의해야 합니다.

module UsefulThings
  def self.get_file; ...
  def self.delete_file; ...

  def self.format_text(x); ...
end

그리고 나서 그들에게 전화할 수 있습니다.

UsefulThings.format_text("xxx")

또는

UsefulThings::format_text("xxx")

하지만 어쨌든 관련된 방법만 한 모듈이나 한 클래스에 넣는 것이 좋습니다.모듈의 메소드를 하나만 포함하고 싶은 문제가 있다면 코드 냄새가 나쁘고 관련 없는 메소드를 함께 넣는 것은 좋은 루비 스타일이 아닙니다.

모듈을 포함하지 않고(그리고 중간 개체를 만들지 않고) 모듈 인스턴스 메서드를 호출하려면:

class UsefulWorker
  def do_work
    UsefulThings.instance_method(:format_text).bind(self).call("abc")
    ...
  end
end

10년이 지난 지금도 누군가가 필요한지 모르겠지만, 나는 그것을 고유 클래스를 사용하여 해결했습니다.

module UsefulThings
  def useful_thing_1
    "thing_1"
  end

  class << self
    include UsefulThings
  end
end

class A
  include UsefulThings
end

class B
  extend UsefulThings
end

UsefulThings.useful_thing_1 # => "thing_1"
A.new.useful_thing_1 # => "thing_1"
B.useful_thing_1 # => "thing_1"

먼저 모듈을 필요한 유용한 것들로 나누는 것을 추천합니다.그러나 언제든지 호출에 대해 클래스를 확장할 수 있습니다.

module UsefulThings
  def a
    puts "aaay"
  end
  def b
    puts "beee"
  end
end

def test
  ob = Class.new.send(:include, UsefulThings).new
  ob.a
end

test

A. 항상 "정격화된" 독립 실행형 방식(UsusefulThings.get_file)으로 호출한 다음 다른 사람이 지적한 대로 정적으로 만듭니다.

module UsefulThings
  def self.get_file; ...
  def self.delete_file; ...

  def self.format_text(x); ...

  # Or.. make all of the "static"
  class << self
     def write_file; ...
     def commit_file; ...
  end

end

B. 일회성 독립 실행뿐 아니라 동일한 경우에도 mixin 접근 방식을 유지하려면 mixin과 함께 자체 확장되는 단일 라이너 모듈을 사용할 수 있습니다.

module UsefulThingsMixin
  def get_file; ...
  def delete_file; ...

  def format_text(x); ...
end

module UsefulThings
  extend UsefulThingsMixin
end

따라서 두 가지 모두 작동합니다.

  UsefulThings.get_file()       # one off

   class MyUser
      include UsefulThingsMixin  
      def f
         format_text             # all useful things available directly
      end
   end 

은 IMHO보다 .module_function모든 방법에 대해 - 모든 방법이 필요할 경우에 대비합니다.

질문을 이해한 바로는 모듈의 인스턴스 메소드 중 일부를 클래스에 혼합하려고 합니다.

먼저 모듈 #include가 작동하는 방식을 고려해 보겠습니다.모듈이 있다고 가정합니다.UsefulThings에는 두 인스턴스메소드가 되어 있습니다.

module UsefulThings
  def add1
    self + 1
  end
  def add3
    self + 3
  end
end

UsefulThings.instance_methods
  #=> [:add1, :add3]

그리고.Fixnum include 모듈s 당해모듈:

class Fixnum
  def add2
    puts "cat"
  end
  def add3
    puts "dog"
  end
  include UsefulThings
end

다음과 같은 것을 확인할 수:

Fixnum.instance_methods.select { |m| m.to_s.start_with? "add" }
  #=> [:add2, :add3, :add1] 
1.add1
2 
1.add2
cat
1.add3
dog

당신은 기대하고 있었습니까?UsefulThings#add3 무시하는Fixnum#add3, 도록하록1.add3할 것입니다.4고려 사항:

Fixnum.ancestors
  #=> [Fixnum, UsefulThings, Integer, Numeric, Comparable,
  #    Object, Kernel, BasicObject] 

이 있을 때include모듈은 클래스의 슈퍼 클래스가 됩니다.그래서, 상속이 어떻게 돌아가는지 때문에,add3를 들어Fixnum유할것의 원인이 될 입니다.Fixnum#add3 호됨출, 환됨반 반환dog.

이제 방법을 해 보겠습니다.:add2UsefulThings:

module UsefulThings
  def add1
    self + 1
  end
  def add2
    self + 2
  end
  def add3
    self + 3
  end
end

우리는 이제 소망합니다Fixnuminclude 방법들 직오방들법▁the▁methods.add1그리고.add3그렇게 해서 위와 같은 결과가 나올 것으로 예상됩니다.

위와 같이 다음을 실행한다고 가정합니다.

class Fixnum
  def add2
    puts "cat"
  end
  def add3
    puts "dog"
  end
  include UsefulThings
end

결과는 어떻습니까? 않는 방법은 하지않 방법입니다.:add2되었습니다.Fixnum,:add1로, 추되었고설, 서한이유로명위에가위설이,유,:add3추가되지 않았습니다.그래서 우리가 해야 할 일은undef :add2간단한 도우미 방법으로 이를 수행할 수 있습니다.

module Helpers
  def self.include_some(mod, klass, *args)
    klass.send(:include, mod)
    (mod.instance_methods - args - klass.instance_methods).each do |m|
      klass.send(:undef_method, m)
    end
  end
end

다음과 같이 호출합니다.

class Fixnum
  def add2
    puts "cat"
  end
  def add3
    puts "dog"
  end
  Helpers.include_some(UsefulThings, self, :add1, :add3)
end

그러면:

Fixnum.instance_methods.select { |m| m.to_s.start_with? "add" }
  #=> [:add2, :add3, :add1] 
1.add1
2 
1.add2
cat
1.add3
dog

그게 우리가 원하는 결과야

거의 9년 만에 일반적인 솔루션을 소개합니다.

module CreateModuleFunctions
  def self.included(base)
    base.instance_methods.each do |method|
      base.module_eval do
        module_function(method)
        public(method)
      end
    end
  end
end

RSpec.describe CreateModuleFunctions do
  context "when included into a Module" do
    it "makes the Module's methods invokable via the Module" do
      module ModuleIncluded
        def instance_method_1;end
        def instance_method_2;end

        include CreateModuleFunctions
      end

      expect { ModuleIncluded.instance_method_1 }.to_not raise_error
    end
  end
end

적용해야 하는 불행한 트릭은 메소드가 정의된 후 모듈을 포함하는 것입니다.또는 컨텍스트가 다음과 같이 정의된 후에 포함할 수도 있습니다.ModuleIncluded.send(:include, CreateModuleFunctions).

또는 reflection_utils 보석을 통해 사용할 수 있습니다.

spec.add_dependency "reflection_utils", ">= 0.3.0"

require 'reflection_utils'
include ReflectionUtils::CreateModuleFunctions

오늘 루비를 공부하다가 흥미를 느꼈을 때 이 오래된 질문이 떠오릅니다.

모듈이 있다고 가정합니다.

module MyModule
  def say
    'I say'
  end

  def cheer
    'I cheer'
  end
end 

그럼 수업과 함께, so call.Animal참을 수 있어요cheerMyModule로부터의 메소드는 다음과 같습니다.

class Animal
  define_method(:happy, MyModule.method(:cheer))
end

이를 바인딩되지 않은 메서드라고 하므로 호출 가능한 개체를 가져와 다른 위치에 바인딩할 수 있습니다.

이때부터 다음과 같은 방법을 일반적으로 사용할 수 있습니다.

my_dog = Animal.new

my_dog.happy # => "I cheer"

저도 오늘 새로운 것을 배웠기 때문에 이것이 도움이 되길 바랍니다.

자세한 내용은 다음을 참조하십시오.irbMethod 개체를 살펴봅니다.

언급URL : https://stackoverflow.com/questions/322470/can-i-invoke-an-instance-method-on-a-ruby-module-without-including-it