我对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
。我总是对此感到困惑。
有人能把我放在正确的道路上吗?我整整一个下午一直在打这个错误。
答案 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