2013-08-31 13 views
25

Module#refine metodo prende una classe e un blocco e restituisce un modulo di raffinatezza, così ho pensato che potrei definire:Un modo migliore per trasformare una classe di rubini in un modulo rispetto ai raffinamenti?

class Class 
    def include_refined(klass) 
    _refinement = Module.new do 
     include refine(klass) { 
     yield if block_given? 
     } 
    end 
    self.send :include, _refinement 
    end 
end 

e il seguente test viene superato

class Base 
    def foo 
    "foo" 
    end 
end 

class Receiver 
    include_refined(Base) { 
    def foo 
     "refined " + super 
    end 
    } 
end 

describe Receiver do 
    it { should respond_to(:foo) } 
    its(:foo) { should eq("refined foo") } 
end 

Quindi, utilizzando i parametri, posso girare una classe in un modulo, raffina il suo comportamento al volo e includilo in altre classi.

  • C'è un modo più semplice per trasformare una classe in un modulo in Ruby (ad esempio in rubino < 2)?
  • Nel C-implementazione di rb_mod_refine vediamo

    refinement = rb_module_new(); 
    RCLASS_SET_SUPER(refinement, klass); 
    

    E 'solo l'impostazione della superclasse di raffinatezza a klass che copia l'implementazione della classe all'interno del modulo raffinatezza?

  • Sono a conoscenza del fatto che l'ereditarietà multipla è eseguita tramite moduli, ma cosa penserebbe la comunità sopra Class#include_refined? Sarebbe ragionevole estrapolare questo aspetto dai raffinamenti? "Localmente" l'applicazione di patch all'interno di una classe invece di utilizzare "usa" per attivare i perfezionamenti?
+1

Sembra una soluzione insolita. Esistono casi di utilizzo pratico in Rails? –

+1

@BillyChan Immagina di voler utilizzare tutte le azioni definite in alcune classi di controller definite in alcune gemme (o Rails :: Engine) nel mio controller, che naturalmente ereditano dal mio 'ApplicationController'. Prendi [Tolk :: LocalesController] (https://github.com/tolk/tolk/blob/master/app/controllers/tolk/locales_controller.rb) per esempio. –

+0

Sono confuso sul perché vorresti farlo. Perché non definirlo come un modulo, e se hai bisogno di istanziare qualcosa con _solo quei metodi? Puoi creare una classe che non fa altro che includere i moduli. – prater

risposta

1

Andrea, grazie per le informazioni nel commento. Scusa la mia mancanza di conoscenza per capire che questo è veramente necessario anche se sembra fattibile secondo la tua ricerca.

Non penso che abbiamo bisogno di andare così in basso per fare qualcosa in Rails.

Se ho intenzione di fare simili su Engine, proverò le seguenti idee, dal facile al difficile.

  1. In routes.rb, montare l'intero motore nel percorso corretto.

    Ho paura che questo utilizzo più comune non può soddisfare le vostre necessità

  2. In routes.rb, Personalizza percorso del motore per i controller specifici in via di applicazione.

    Devace, come un motore, può fare facilmente. Ma so che non tutti i motori potrebbero fare questo.

  3. In routes.rb, reindirizzare insieme specifico o intero di rotte per le rotte del motore

  4. In azione dell'applicazione, reindirizzare all'azione di motore specifico in azione dell'applicazione.

    Questo dovrebbe essere adattato abbastanza per un'azione specifica

    class FoosController < ApplicationController 
        def foo 
        redirect_to some_engine_path if params[:foo] == 'bar' 
        end 
    
  5. Eredita controllo del motore - per una serie di azioni, e se tutto di cui sopra non può andare bene

    * classi del motore sono disponibile in tutte le applicazioni, è possibile ereditare un controller da questi, invece del normale ApplicationController.

    # class FoosController < ApplicationController 
    class FoosController < BarEngine::BarsController 
    

    * Dal controller di più del motore eredita da ApplicationController, questa eredità permette ancora di utilizzare le proprie cose da ApplicationController, nessun effetto negativo a tutti.

  6. Se quanto sopra non può fare, posso provare a servire un locale personalizzato o dal mio repository github.

In conclusione, quanto sopra dovrebbe essere in grado di risolvere la maggior parte dei casi, e io stesso preferisco 5 #, quando possibile e necessario.

+2

grazie per la risposta ma non intendevo davvero iniziare una discussione sui binari. A proposito, preferirei anche 5. Come fai notare che il "maggior" controller dei motori (vedi Devise) eredita da "ApplicationController" ma "Tolk :: ApplicationController" (che cito sopra) non lo fa. –

3

Sono davvero contento della portata "privata" di Ruby 2.1 (e successive) a livello di classe. Il mio esempio sopra può essere riformulato come:

# spec/modulify_spec.rb 
module Modulify 
    refine(Class) do 
    def include_refined(klass) 
     _refined = Module.new do 
     include refine(klass) { yield if block_given? } 
     end 
     include _refined 
    end 
    end 
end 

class A 
    def a 
    "I am an 'a'" 
    end 
end 

class B 
    using Modulify 

    include_refined(A) do 
    def a 
     super + " and not a 'b'" 
    end 
    end 

    def b 
    "I cannot say: " + a 
    end 
end 

RSpec.describe B do 
    it "can use refined methods from A" do 
    expect(subject.b).to eq "I cannot say: I am an 'a' and not a 'b'" 
    end 
end 

e si adatta come soluzione per il problema originale.