我正在编写Ruby Gem,我
配置before_save :set_status, if: 'payload[:current_user_id] && payload[:current_user_id] != 1'
def set_status
status = 2
end
before_save :set_status, if: 'payload[:current_user_id] && payload[:current_user_id] != 1'
def set_status
status = 2
end
模块
发出API请求的第二个模块如下所示:
Connection
我使用Faraday
的{{1}}类看起来像这样:
module Example
module Connection
private
def connection
Faraday.new(url: 'http://localhost:3000/api') do |conn|
conn.request :url_encoded # form-encode POST params
conn.response :logger # log requests to STDOUT
conn.adapter Faraday.default_adapter # make requests with Net::HTTP
conn.use Faraday::Response::ParseJson
conn.use FaradayMiddleware::RaiseHttpException
end
end
end
end
所以,您在module Example
module Request
include Connection
def get(uri)
connection.get(uri).body
end
def post(url, attributes)
response = connection.post(url) do |request|
request.body = attributes.to_json
end
end
def self.extended(base)
base.include(InstanceMethods)
end
module InstanceMethods
include Connection
def put(url, attributes)
response = connection.put(url) do |request|
request.body = attributes.to_json
end
end
end
end
end
类方法中看到我调用了Cusomer
方法,因为我在Request
中扩展了module Example
class Customer
extend Request
attr_accessor :id, :name, :age
def initialize(attrs)
attrs.each do |key, value|
instance_variable_set("@#{key}", value)
end
end
def self.all
customers = get('v1/customer')
customers.map { |cust| new cust }
end
def save
params = {
id: self.id,
age: self.age
name: self.name,
}
put("v1/customers/#{self.id}", params)
end
end
end
。然后我在Customer#all
模块中使用self.extended方法在Request#get
类中使Request
可用,所以我有一个问题就是使用这样的mixins这个好方法,或者你有什么建议吗?
答案 0 :(得分:5)
但是,mixins是一种继承形式(你可以看一下#ancestors
)。我会挑战你说你不应该在这里使用继承,因为Customer
与Connection
没有“is-a”关系。我建议你改用合成(例如传入Connection/Request
),因为在这种情况下对我来说更有意义并且具有更强的封装。
编写mixins的一个指导原则是让所有内容都以“-able”结尾,因此您将拥有Enumerable
,Sortable
,Runnable
,Callable
等。从这个意义上说,mixins是一般的扩展,提供某种依赖于非常特定的接口的帮助(例如Enumerable
依赖于实现#each
的类。)
您也可以使用mixins来解决问题。例如,我们过去在后台作业中使用了mixins,因此我们可以添加日志记录,而无需触及类的源代码。在这种情况下,如果一个新工作想要记录,那么他们只是混合了与框架耦合的关注点,并将正确注入。
我的一般经验法则是,如果您不需要,请不要使用它们。在大多数情况下,他们会使代码理解得更复杂
编辑:添加一个组合示例。为了维护上面的接口,你需要有某种全局连接状态,所以它可能没有意义。这是使用合成
的替代方案class CustomerConnection
# CustomerConnection is composed of a Connection and retains isolation
# of responsibilities. It also uses constructor injection (e.g. takes
# its dependencies in the constructor) which means easy testing.
def initialize(connection)
@connection = connection
end
def all_customers
@connection.get('v1/customers').map { |res| Customer.new(res) }
end
end
connection = Connection.new
CustomerConnection.new(connection).all_customers