Better way to turn a ruby class into a module than using refinements?

后端 未结 2 1053
春和景丽
春和景丽 2021-01-31 09:39

Module#refine method takes a class and a block and returns a refinement module, so I thought I could define:

class Class
  def include_refined(klass)
    _refine         


        
相关标签:
2条回答
  • 2021-01-31 10:18

    Andrea, thank you for the info in comment. Excuse my lack of knowledge to understand this is really necessary though it sounds doable as per your research.

    I don't think we need to go so low level to do something in Rails.

    If I'm going to do similar on Engine, I will try the following ideas, from easy to hard.

    1. In routes.rb, mount the whole engine in right route.

      I'm afraid this most common usage can't fit your need

    2. In routes.rb, Customize engine's route for specific controllers in application route.

      Devise, as an engine, can do easily. But I know not every engine could do this.

    3. In routes.rb, redirect specific or whole set of routes to engine's routes

    4. In your application's action, redirect to specific engine's action in application's action.

      This should be customized enough for specific action

      class FoosController < ApplicationController
        def foo
          redirect_to some_engine_path if params[:foo] == 'bar'
        end
      
    5. Inherit the engine's controller - for a set of actions, and if all above can't fit

      *The engine's classes are available in all application, you can inherit a controller from them, instead of normal ApplicationController.

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

      *Since most engine's controller inherit from ApplicationController, this inheritance still allows you to use your own things from ApplicationController, no bad effect at all.

    6. If all above can't do, I can try to serve a customized locally or from my github repo.

    In conclusion, the above should be able to solve most of cases, and I myself prefer #5 when possible and needed.

    0 讨论(0)
  • 2021-01-31 10:29

    I am happy indeed with Ruby 2.1 (and later) class-level "private" scope of refinements. My example above can be rephrased as:

    # 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
    

    and suits as solution for the original problem.

    0 讨论(0)
提交回复
热议问题