Rails 5:从控制器中抽象出动作
Rails 5: Abstracting actions out of controllers
RoR 5.1.4。我的应用程序由 MongoDB via Mongoid 支持,许多模型都有类似的处理。我试图让它尽可能地成为数据驱动的,这意味着查询 Mongoid
模型和 Rails 路线,由于急切的加载和路线,这两者都不能在初始化程序中轻松完成尚未设置。架构信息(controller/model 关系、模型字段、 等 存储在单个位置,并使 that 发生并成为可从 class 访问,实例环境已成为 PITA。但是,那是背景。
大多数控制器支持两种上传操作,一种是via 表单文件规范,另一种是via 请求内容主体。我分别对这些使用 webupload
和 upload
操作,并且由于它们的处理方式始终相同(差异是通过检查上述模式信息确定的),我想抽象出这两种操作方法,及其相关的 before/after/.. 关系定义,放入单独的模块中。
控制器问题不会在 OOTB 时执行,因为回调(例如、:before_action
和朋友)没有在继承中声明,我可以'不知道我需要 include
关注什么模块才能得到它们。
Helpers 已被弃用,因为它们已被控制器包含所弃用(您需要采取额外的步骤来获取它们),而且它们主要用于视图。
那么 writing/placing/including 专门用于控制器、模型和测试的模块的模式是什么?那些功能的方法有适当的继承吗? (例如,:before_action
控制器,:validates
模型等。)
RoR 是一个丰富的功能和钩子宝库,但我发现很难将抽象和 DRY 模式应用于它只是因为它 太丰富了。
感谢任何帮助或指点!
[编辑] 有人建议我包含一些代码。所以这里是我尝试通过控制器问题进行此操作的简短摘录。
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:
你完全误解了关于 ActiveSupport::Concern
的几乎所有内容以及模块 mixins 的工作原理。
让我们从使用组合来分离关注点开始。例如:
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
RoR 5.1.4。我的应用程序由 MongoDB via Mongoid 支持,许多模型都有类似的处理。我试图让它尽可能地成为数据驱动的,这意味着查询 Mongoid
模型和 Rails 路线,由于急切的加载和路线,这两者都不能在初始化程序中轻松完成尚未设置。架构信息(controller/model 关系、模型字段、 等 存储在单个位置,并使 that 发生并成为可从 class 访问,实例环境已成为 PITA。但是,那是背景。
大多数控制器支持两种上传操作,一种是via 表单文件规范,另一种是via 请求内容主体。我分别对这些使用 webupload
和 upload
操作,并且由于它们的处理方式始终相同(差异是通过检查上述模式信息确定的),我想抽象出这两种操作方法,及其相关的 before/after/.. 关系定义,放入单独的模块中。
控制器问题不会在 OOTB 时执行,因为回调(例如、:before_action
和朋友)没有在继承中声明,我可以'不知道我需要 include
关注什么模块才能得到它们。
Helpers 已被弃用,因为它们已被控制器包含所弃用(您需要采取额外的步骤来获取它们),而且它们主要用于视图。
那么 writing/placing/including 专门用于控制器、模型和测试的模块的模式是什么?那些功能的方法有适当的继承吗? (例如,:before_action
控制器,:validates
模型等。)
RoR 是一个丰富的功能和钩子宝库,但我发现很难将抽象和 DRY 模式应用于它只是因为它 太丰富了。
感谢任何帮助或指点!
[编辑] 有人建议我包含一些代码。所以这里是我尝试通过控制器问题进行此操作的简短摘录。
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:
你完全误解了关于 ActiveSupport::Concern
的几乎所有内容以及模块 mixins 的工作原理。
让我们从使用组合来分离关注点开始。例如:
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