RoR 5.1.4。我的应用程序由MongoDB via Mongoid支持,许多模型具有类似的处理方式。我正在尝试使其尽可能地受数据驱动,这意味着查询Mongoid
的模型和Rails的路线,由于急切的加载和尚未完成的路线,在初始化器中都无法轻松地完成这两项设定。模式信息(控制器/模型关系,模型字段,等)存储在单个位置,并且使 发生并且可以从类和实例环境访问PITA,但是那是背景。
大多数控制器支持两种上传操作,一种通过表单文件规范进行通过,另一种通过请求内容主体通过通过。我分别对它们使用webupload
和upload
动作,由于它们总是相同的(差异是通过检查上述模式信息来确定的),因此我想抽象这两种动作方法,及其相关的之前/之后/ ..关系定义,放在一个单独的模块中。
控制器的关注点无法实现OOTB,因为没有在继承中声明回调( eg ,:before_action
和朋友),而且我无法弄清楚哪个模块我需要include
来解决这个问题。
助手之所以退出,是因为它们已不被包含在控制器中(您需要采取额外的步骤来获取它们),并且无论如何它们主要是用于视图。
那么编写/放置/包括专门用于控制器,模型和测试的模块的模式是什么?那些功能具有适当的方法继承吗? (例如, ,:before_action
用于控制器,:validates
用于模型,依此类推。)
RoR是功能和挂钩的丰富宝库,但是我发现很难对它应用抽象和DRY模式,这仅仅是因为它如此如此丰富。
感谢您的帮助或指示!
[EDIT]有人建议我加入一些代码。因此,这里是我为解决控制器问题而做的尝试的简短摘录。
module UploadAssistant
extend ActiveSupport::Concern
#
# Code to execute when something does a `include ControllerAssistant`.
#
included do
#
# Make the application's local storage module more easily
# accessible, too.
#
unless (self.const_defined?('LocalStore'))
self.const_set('LocalStore', ::PerceptSys::LocalStore)
end
def set_title_uploading
title.base = 'Uploading records'
title.add(model_info.friendly)
end # def set_base_title
#+
# Supply these routes to the controllers so they needn't define
# them.
#
#
# GET /<model>/upload(.:format)
#
def webupload
end # def webupload
#
# POST /<model>/upload(.:format)
#
def upload
title.add('Results')
@uploads = {
:success => {},
:failure => {},
}
errors = 0
@upload_records.each do |record|
#
# Stuff happens here.
#
end
successes = @uploads[:success].count
failures = @uploads[:failure].count
respond_to do |format|
format.html {
render(:upload,
:status => :ok,
:template => 'application/upload.html')
}
format.json {
render(:json => @uploads)
}
end
end
def upload_file_params
if (params[:file])
params.require(:file).require(:upload)
colname = model_info.collection
file_id = params[:file][:upload]
#
# Get the file contents.
#
end
@upload_records = params.delete(model_info.collection.to_sym)
end # def upload_file_params
def upload_params
@upload_records = params.require(model_info.collection.to_sym)
end # def upload_params
def set_file_upload
file_id = params.require(:file).require(:upload)
#
# Read/decompress the file.
#
data = JSON.parse(data)
params[model_info.collection] = data[model_info.collection]
end # def set_file_upload
end # included do
#+
# Insert here any class methods we want added to our including class
# or module.
#
class_methods do
#
# Stuff relating specifically to bulk uploading.
#
before_action(:set_title_uploading,
:only => [
:upload,
:webupload,
])
before_action(:set_file_upload,
:only => [
:upload,
])
before_action(:upload_params,
:only => [
:upload,
])
end # class_methods do
end # module ControllerAssistant
# Local Variables:
# mode: ruby
# eval: (fci-mode t)
# End:
答案 0 :(得分:3)
您已经完全误解了关于ActiveSupport::Concern
以及模块mixin如何工作的所有内容。
让我们开始使用合成来分离问题。例如:
module LocalStorage
extend ActiveSupport::Concern
class_methods do
# use a memoized helper instead of a constant
# as its easier to stub for testing
def local_storage
@local_storage ||= ::PerceptSys::LocalStore
end
end
end
将其提取为一个单独的关注点是有意义的,因为它具有可重用的行为。
然后我们可以提出一个Uploadable
问题:
module Uploadable
# we compose modules by extending
extend ActiveSupport::Concern
extend LocalStorage
# put instance methods in the module body
# GET /<model>/upload(.:format)
def webupload
# ...
end
#
# POST /<model>/upload(.:format)
#
def upload
# ...
end
# don't abuse use a callback for this - just use a straight
# method that returns a value and preferably does not have side effects
def upload_params
# ...
end
# ...
# use "included" to hook in the class definition
# self here is the singleton class instance
# so this is where you put callbacks, attr_accessor etc
# which would normally go in the class defintion
included do
before_action(:set_title_uploading,
:only => [
:upload,
:webupload,
])
before_action(:set_file_upload,
:only => [
:upload,
])
end
# just use class_methods for actual class_methods!
class_methods do
# for example to derive the name of a model from the controller name
def resource_class_name
controller_name.singularize
end
def resource_class
@resource_class ||= resource_class_name.classify.constantize
end
end
end