Vraag Kan ik een instantiemethode op een Ruby-module aanroepen zonder deze op te nemen?


Achtergrond:

Ik heb een module die een aantal exemplarische methoden declareert

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

  def format_text(x); ...
end

En ik wil sommige van deze methoden vanuit een klas noemen. Hoe je dit normaal in Ruby doet is als volgt:

class UsefulWorker
  include UsefulThings

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

Probleem

include UsefulThings brengt binnen alle van de methoden van UsefulThings. In dit geval wil ik alleen format_text en expliciet niet willen get_file en delete_file.

Ik zie verschillende mogelijke oplossingen hiervoor:

  1. Op de een of andere manier roept u de methode rechtstreeks op de module op zonder deze ergens op te nemen
    • Ik weet niet hoe / als dit kan worden gedaan. (Vandaar deze vraag)
  2. Een of andere manier bevatten Usefulthings en breng slechts enkele van zijn methoden binnen
    • Ik weet ook niet hoe / of dit kan worden gedaan
  3. Maak een proxyklasse, opnemen UsefulThings in dat, delegeer dan format_text naar die proxy-instantie
    • Dit zou werken, maar anonieme proxy-klassen zijn een hack. Bah.
  4. Splits de module op in 2 of meer kleinere modules
    • Dit zou ook werken, en is waarschijnlijk de beste oplossing die ik kan bedenken, maar ik zou het liever vermijden, omdat ik zou eindigen met een wildgroei van tientallen en tientallen modules - het zou lastig zijn om dit te beheren

Waarom zijn er veel niet-gerelateerde functies in een enkele module? Haar ApplicationHelper van een rails-app, die ons team de facto heeft besloten als de stortplaats voor iets dat niet specifiek genoeg is om ergens anders te horen. Meestal zelfstandige hulpprogramma's die overal worden gebruikt. Ik zou het in afzonderlijke helpers kunnen verdelen, maar er zouden er 30 zijn, allemaal met 1 methode elk ... dit lijkt onproductief


153
2017-11-26 23:03


oorsprong


antwoorden:


Als een methode op een module wordt omgezet in een modulefunctie, kunt u deze eenvoudigweg uit Mods noemen alsof deze was gedeclareerd als

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

De module-functiebenadering hieronder vermijdt het breken van alle klassen die alle Mods bevatten.

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  

Ik ben echter nieuwsgierig waarom een ​​reeks niet-gerelateerde functies allemaal in dezelfde module zit?

bewerkt om te laten zien dat inclusief nog steeds werk als public :foo is genoemd naar module_function :foo


121
2017-09-11 14:03



Ik denk dat de kortste weg om één enkele oproep weg te gooien (zonder bestaande modules te wijzigen of nieuwe aan te maken) de volgende zou zijn:

Class.new.extend(UsefulThings).get_file

121
2017-11-26 23:23



Een andere manier om het te doen als u de module "bezit" is om te gebruiken 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

73
2017-11-26 23:18



Als u deze methoden wilt aanroepen zonder de module in een andere klasse op te nemen, moet u ze definiëren als modulemethoden:

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

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

en dan kun je ze bellen

UsefulThings.format_text("xxx")

of

UsefulThings::format_text("xxx")

Maar goed, ik zou aanraden dat je gewoon gerelateerde methoden in één module of in één klas zet. Als u een probleem hebt dat u slechts één methode uit de module wilt opnemen, klinkt dit als een slechte codegeur en is het niet goed voor Ruby-stijl om niet-verwante methoden samen te stellen.


16
2017-10-02 03:50



Om een ​​moduleinstellingsmethode aan te roepen zonder de module op te nemen (en zonder intermediaire objecten te maken):

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

13
2017-11-26 23:16



Ten eerste zou ik aanraden om de module op te splitsen in de nuttige dingen die je nodig hebt. Maar je kunt altijd een klasse maken die die voor je aanroep verlengt:

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

4
2018-02-06 21:21



A. Als u ze altijd op een "gekwalificeerde", op zichzelf staande manier (UsefulThings.get_file) wilt noemen, maak ze dan gewoon statisch zoals anderen hebben opgemerkt,

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. Als je in dezelfde gevallen de mixin-benadering nog steeds wilt behouden, evenals de eenmalige, zelfstandige aanroep, kun je een one-liner-module hebben die strekt zelf met de mixin:

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

  def format_text(x); ...
end

module UsefulThings
  extend UsefulThingsMixin
end

Dus beide werken dan:

  UsefulThings.get_file()       # one off

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

IMHO het is schoner dan module_function voor elke afzonderlijke methode - als ze allemaal willen.


4
2017-08-13 07:27