2017-06-06 65 views
0

我有两个控制器在导轨上有不同的认证方案, 但它们几乎完全相同。在导轨中封装控制器逻辑

什么是最好的方式在轨道封装 控制器的逻辑在另一个类或助手?

样品:

def ControllerA < BasicAuthController  
    def create 
    blablacode 
    end 
end 

def ControllerB < TokenAuthController 
    def create 
    blablacode 
    end 
end 

请告诉我正确的方式做到这一点?用代码创建一个模型? 创建一个帮手?其他?

+0

嗯轨道的方法是定义在相应的辅助文件夹中找到的辅助方法,反正你实际上可以使一个类或模块代表了你想做的事,如果你去的模块路径,你可以在它混控制器 – niceman

+3

这是一个广泛的问题。这取决于。帮手方法?模块?遗产?依赖注入?抽象逻辑变成宝石? ...你至少可以展示*你想要清理的代码是什么? –

回答

0

简短的回答,我选择创建一个助手

从在回答所有建议

  • 创建模块: 似乎是正确的,但它并没有觉得不对劲有外 的app逻辑目录。这不是一个外部模块或库,但 东西与我的应用程序的逻辑非常相关。

  • 在一个控制器集成diferents认证: 是一个很好的建议,但我不得不改变我的应用程序的所有逻辑。

  • 创建一个帮手: 在我看来,更好的解决方案,我有一个助手的代码, 是应用程序目录中,非常接近从另一个逻辑。

0

我做这样的事情:

#app/services/my_app/services/authentication.rb 
class MyApp::Services::Authentication 

    class < self 

    def call(params={}) 
     new(params).call 
    end 

    end # Class Methods 

    #============================================================================================== 
    # Instance Methods 
    #============================================================================================== 

    def initialize(params) 
     @params = params 
    end 

    def call 
     ... do a lot of clever stuff 
     ... end by returning true or false 
    end 

    private 

    def params() @params end 

end 

然后:

class FooController < ApplicationController 
    before_action :authenticate 

    def authenticate 
    redirect_to 'some_path' unless MyApp::Services::Authenticate.call(with: 'some_params') 
    end 

end 
+0

'app/services/my_app/services' ?? !! –

+0

是的。我已经看到了一些关于这样的事情的讨论。我不介意愚蠢的目录结构。我喜欢(我认为)是MyApp :: Services :: Authentication'命名空间的清晰度。 (可能是因为我在一堆应用程序中使用了大量的东西,而且我喜欢过多的命名空间的安全带和吊带安全性。)但是,我认识到有些人对我们的目录结构比我更挑剔。给每个人自己。 – jvillian

0

你为什么不启用单一控制器两种方案?特别是如果唯一的区别是认证。您可以使用两个app/controllers/concerns来封装两种身份验证方法,而对于只管理其管理的任何资源的单个控制器,可以使用include Auth1和​​。

否则,服务是封装控制器逻辑的最佳方法。

在您的app文件夹中创建一个名为services的文件夹并在此处编写PORO类。假设你在你的应用中有几个地方你想通过使Stripe支付东西。

# app/services/stripe_service.rb 
module StripeService 
    def customer(args) 
    ... 
    end 

    def pay(amount, customer) 
    ... 
    end 

    def reverse(stripe_txn_id) 
    ... 
    end 
end 
# controller 
StripeService.customer(data) 
=> <#Stripe::Customer> 

或者如果你只需要做一件事。

# app/services/some_thing.rb 
module SomeThing 
    def call 
    # do stuff 
    end 
end 
# controller 
SomeThing.call 
=> # w/e 

如果您需要一个具有多个职责的对象,您可以创建一个类。

class ReportingService 
    def initialize(args) 
    ... 
    end 

    def query 
    ... 
    end 

    def data 
    ... 
    end 

    def to_json 
    ... 
    end 
end 

https://blog.engineyard.com/2014/keeping-your-rails-controllers-dry-with-services

+0

除非我错了,否则这些方法将成为实例方法。在这种情况下,您需要执行类似“SomeThing.new.call”的操作。或者可能不是? – jvillian

+0

你是对的,我的坏! – fbelanger

0

最简单的事情就是让一个模块,然后include入其它控制器:

module ControllerMixin 
    def create 
    blablacode 
    end 
end 

剩下的问题,不过,在这里把这个代码,这样它适用于Rails自动加载器,因为它需要在控制器之前加载。这样做将是对模块写在lib/目录中的文件,然后添加到自动加载路径的一种方式(见auto-loading-lib-files-in-rails-4