Ruby on rails 如何为尚未存在的方法添加别名?(红宝石装饰)
这听起来很疯狂,但我正在尝试构建一个基于装饰的通用系统,它将允许装饰类使用属性执行各种疯狂的操作。我们的目标是能够在高层定义属性,修饰一个ORM类(例如ActiveRecord,尽管我们的主要案例实际上有点不同),并在应用程序中的不同位置使用这些修饰来自动执行一些动态操作“魔术”是我们的应用程序所需要的。例如,我们将使用这些属性自动生成表单和视图,将复杂的表单哈希转换为更平坦的结构,等等 为了适应到目前为止我们已经确定的两个用例,我有一个可混合的模块和一个装饰器(使用Draper,Rails form magic仍然有效,尽管我不一定嫁给Draper),它们看起来或多或少像这样(显然省略了很多细节):Ruby on rails 如何为尚未存在的方法添加别名?(红宝石装饰),ruby-on-rails,ruby,decorator,Ruby On Rails,Ruby,Decorator,这听起来很疯狂,但我正在尝试构建一个基于装饰的通用系统,它将允许装饰类使用属性执行各种疯狂的操作。我们的目标是能够在高层定义属性,修饰一个ORM类(例如ActiveRecord,尽管我们的主要案例实际上有点不同),并在应用程序中的不同位置使用这些修饰来自动执行一些动态操作“魔术”是我们的应用程序所需要的。例如,我们将使用这些属性自动生成表单和视图,将复杂的表单哈希转换为更平坦的结构,等等 为了适应到目前为止我们已经确定的两个用例,我有一个可混合的模块和一个装饰器(使用Draper,Rails f
类装饰
然后,特定实例的实际装饰器执行以下操作:
class FooDecorator < DecoratorThing
decorates Foo
attribute :field, multiple: true, serialize: true
attribute :field2, field: :delegation_field
attribute :field3 do |field|
field.subtype ...
field.subtype ...
end
end
class-FooDecorator
这样做的目的是允许Foo#field获取一个数组并在内部将其序列化为字符串,然后将其发送到装饰对象获取它的任何位置。Foo#field2只会按原样将数据传递给delegation#field。Foo#field3将获取复杂的数据散列,并将其委托给子类型字段
后两种情况是痛苦的,但我有他们的原型工作。第一个是问题,因为上面的alias\u方法
stuff-由于attribute方法是在decorator上运行的,所以我尝试别名的方法实际上还不存在。直到调用FooDecorator.new(一些\u foo\u实例)
之后,其他实例方法才可用
我认为我的选择仅限于以下几点,但我希望有更好的选择:
- 放弃装潢,接受这整件事必须是一场混战
- 放弃mixin,改为需要修饰,遍历修饰的对象,而不是对方法进行别名
- 放弃字段包装,要求属性始终具有唯一的名称并委托给字段(在上面的示例中,
将变成属性:字段…
)属性:字段包装,多个:true,序列化:true,字段:“field”
- 放弃序列化数据,让用户负责正确定义/重写方法来处理他们定义的数据
第四种选择可能是最明智的,但是我已经对能够序列化做了很多假设,所以如果其他人知道一种很好的方法来实现这一点,那就太棒了。我想我有一个适合这个问题的答案 基本上,我的解决方案是在与活动记录模型关联的现有列的顶部添加一个代理列 此代理列覆盖原始列的访问器方法。除了设置和获取原始列的值之外,基本代理功能没有做很多工作 实际代码相当长,因此我为它创建了一个要点 代理列对象可以子类化/重写,以提供所需的任何类型的功能。例如,在您的代码中,
AttributeDefinition
将子类ProxyColumn
。AttributeDefinition
将充当不同属性和值之间的代理。它可以根据您认为合适的任何条件,在装饰中包装/展开属性值
在这个阶段,它没有做的一件事是提供一种覆盖如何/添加什么方法的方法。然而,所有这些都可以根据您的需要轻松地进行更改。实际上,我是根据你的添加翻译方法方法得出这一部分的
这是定义列的基本用法:
class MyModel
# Add the concern to get the functionality
include DataCollectionElements # concern
# create proxy column and set various options
proxy_column :my_col_1
proxy_column :my_col_2, :alias => [:col1, col2, col3]
proxy_column :my_col_3, :column => :actual_col_name
# define the proxy object using class/string/symbol
proxy_column :my_col_4, :proxy => MyProxy # or 'MyProxy' or :my_proxy
# define the proxy object using a proc/lambda
proxy_column :my_col_5, :proxy => ->(model, code, options) { MyProxy.new(model, code, options) }
proxy_column :my_col_6, :proxy => proc {|model, code, options| MyProxy.new(model, code, options) }
# define the proxy object using a block
proxy_column :my_col_7 do |model, code, options|
MyProxy.new(model, code, options)
end
end
你看了吗?我可能弄错了,但我不相信这会对方法别名魔法起作用。我们根据属性定义创建别名,属性定义实际上发生在包含的块之后。问题是,属性(…)
在decorator上被调用,但decorator不应用于类;它适用于一个实例。i、 例如,当定义装饰器时,被别名化的方法将永远不存在。当然,我可能是错的——这种特殊情况远远超出了我的舒适区:)你是对的。另外,ActiveSupport::Concern
包含在decoratorhing
中,而不是foodcorator
中。您有权访问Draper::Decorator吗?看起来最简单的工作可能是“钩住”到foodcorator.new
(您可以重新定义它)并执行方法别名
class MyModel
# Add the concern to get the functionality
include DataCollectionElements # concern
# create proxy column and set various options
proxy_column :my_col_1
proxy_column :my_col_2, :alias => [:col1, col2, col3]
proxy_column :my_col_3, :column => :actual_col_name
# define the proxy object using class/string/symbol
proxy_column :my_col_4, :proxy => MyProxy # or 'MyProxy' or :my_proxy
# define the proxy object using a proc/lambda
proxy_column :my_col_5, :proxy => ->(model, code, options) { MyProxy.new(model, code, options) }
proxy_column :my_col_6, :proxy => proc {|model, code, options| MyProxy.new(model, code, options) }
# define the proxy object using a block
proxy_column :my_col_7 do |model, code, options|
MyProxy.new(model, code, options)
end
end