有关Ruby mixin模块的规范有问题

时间:2017-02-22 00:19:41

标签: ruby-on-rails ruby rspec module

我对mixin模块没有很好的经验。那么,如果我的问题看起来有点天真,请原谅我。

我正在创建一些模块来将项目与Spotify之类的音乐服务集成,后者拥有REST API。所有这些模块都包含我创建的另一个名为APIClientBuilder的mixin模块,它提供了一个用于创建API端点的小型DSL。

LIB /整合/ api_client_builder.rb

require 'rest-client'

module APIClientBuilder

  attr_accessor :api_client, :endpoint, :url, :param

  def api_client(api_name)
  end

  def fetch_client(api_name)
  end

  def api_endpoint(endpoint_name)
  end

  def fetch_endpoint(api_name,endpoint_name)
  end 

  def method=(meth)
  end   

  def url=(endpoint_url) 
  end

  def param(param_name,param_value)
  end

  def call(api_name,api_endpoint,token,*extra_params)
  end

end

LIB /整合/ spotify.rb

require_relative 'api_client_builder'

module SpotifyIntegration

  include APIClientBuilder

  def base_url
    'https://api.spotify.com/v1'
  end

  def random_state_string
    (0..10).map { (65 + rand(26)).chr }.join
  end

  api_client('spotify') do |apic|

    apic.api_endpoint('request_authorization') do |ep|
      ep.method = :get
      ep.url = "https://accounts.spotify.com/authorize"
      ep.param("client_id",SPOTIFY_KEY)
      ep.param("response_type","code")
      ep.param("redirect_uri","http://localhost:3000")
    end

    apic.api_endpoint('my_playlists') do |ep|
      ep.method = :get
      ep.url = "#{base_url}/me/playlists"
    end

  end

end

我的想法是在我的控制器中有这样的东西:

应用/控制器/ API / V1 / users_controller.rb

require 'integrations/spotify.rb'

class UsersController < ApplicationController

  include SpotifyIntegration

end

然后可以访问SpotifyIntegration中的方法,并通过它访问APIClientBuilder中的方法。

碰巧我用一个非常简单的测试编写了以下spec文件:

规格/ LIB /整合/ spotify_integration_spec.rb

require 'rails_helper'

require 'integrations/spotify'

class SpotifyClientTester
  include SpotifyIntegration
end

RSpec.describe SpotifyIntegration do

  context 'Auxiliary methods' do

    it 'Two calls to random_state_string shall generate two different strings' do
      obj = SpotifyClientTester.new
      s1 = obj.random_state_string
      s2 = obj.random_state_string
      expect(s1).not_to eq(s2)
    end

  end

end

但是当我运行它时,我得到了

  

SpotifyIntegration的未定义局部变量或方法base_url:模块(NameError)

我不确定我错过了什么。也许我应该使用extend代替include。我总是对此感到困惑。

有人能把我放在正确的道路上吗?我整整一个下午一直在打这个错误。

2 个答案:

答案 0 :(得分:3)

你滥用mixins。对于经典继承不适合为对象添加一组特征的情况,请使用mixins。

例如:

module Commentable
  extend ActiveSupport::Concern

  included do
    has_many :comments, as: :commentable
  end 
  # ...
end

class Video < ApplicationRecord
  include Commentable
end

class Hotel < ApplicationRecord
  include Commentable
end

正如您在本示例中所见,您extend一个模块,其中包含其他模块和类include模块。使用经典继承来添加共享行为最好是尴尬,因为这两个类是苹果和梨。

在您的特定情况下,您应该使用经典继承,而不是将API客户端混合到控制器中。相反,你的控制器应该将它作为一个独特的对象来调用。

class APIClient
  # Implement shared behavior for a REST api client
end

class SpotifyClient < APIClient
  # ...
end 

class FoosController < ApplicationController
  def index
    client = SpotifyClient.new
    @foos = client.get_something
  end
end

为什么不将API客户端混合到控制器或模型中?由于单一责任原则以及使用较小部件进行有限数量的事情比创建上帝类更为可取。

答案 1 :(得分:1)

如果要在模块APIClientBuilder的类级别使用此处定义的方法,则需要扩展SpotifyIntegration

module SpotifyIntegration

  extend APIClientBuilder

此外,base_url也必须是类方法,def self.base_url

相关问题