摘要:
如何自定义respond_to
为ActiveModel对象生成的路径?
更新:我正在寻找钩子,方法覆盖或配置更改来实现此目的,而不是解决方法。 (解决方法很简单但不优雅。)
上下文&实施例:
这是一个例子来说明。我有一个模型Contract
,它有很多字段:
class Contract < ActiveRecord::Base
# cumbersome, too much for a UI form
end
为了使UI代码更易于使用,我有一个更简单的类,SimpleContract
:
class SimpleContract
include ActiveModel::Model
# ...
def contract_attributes
# convert SimpleContract attributes to Contract attributes
end
def save
Contract.new(contract_attributes).save
end
end
这很好用,但我的控制器出了问题......
class ContractsController < ApplicationController
# ...
def create
@contract = SimpleContract.new(contract_params)
flash[:notice] = "Created Contract." if @contract.save
respond_with(@contract)
end
# ...
end
问题是respond_with
指向simple_contract_url
,但我希望它指向contract_url
。最好的方法是什么? (请注意,我正在使用ActiveModel。)
(注意:我正在使用Rails 4 Beta,但这不是我的问题的核心。我认为Rails 3的一个好答案也可以。)
补充工具栏:如果这种在轻量级ActiveModel类中包装模型的方法对您来说似乎不明智,请在评论中告诉我。就个人而言,我喜欢它,因为它使我原来的模型变得简单。 “包装器”模型处理一些UI细节,这些细节被有意简化并给出合理的默认值。
答案 0 :(得分:1)
首先,这是一个有效的答案:
class SimpleContract
include ActiveModel::Model
def self.model_name
ActiveModel::Name.new(self, nil, "Contract")
end
end
我将此答案从kinopyo's answer调整为Change input name of model。
现在,为什么呢。 respond_to
的调用堆栈有些参与。
# Start with `respond_with` in `ActionController`. Here is part of it:
def respond_with(*resources, &block)
# ...
(options.delete(:responder) || self.class.responder).call(self, resources, options)
end
# That takes us to `call` in `ActionController:Responder`:
def self.call(*args)
new(*args).respond
end
# Now, to `respond` (still in `ActionController:Responder`):
def respond
method = "to_#{format}"
respond_to?(method) ? send(method) : to_format
end
# Then to `to_html` (still in `ActionController:Responder`):
def to_html
default_render
rescue ActionView::MissingTemplate => e
navigation_behavior(e)
end
# Then to `default_render`:
def default_render
if @default_response
@default_response.call(options)
else
controller.default_render(options)
end
end
就目前而言,这是我的目标。我实际上并没有找到构建URL的位置。我知道它基于model_name
发生,但我还没有找到它发生的代码行。
答案 1 :(得分:0)
我不确定我是否完全理解你的问题,但你可以做这样的事吗?
class SimpleContract
include ActiveModel::Model
attr_accessor :contract
# ...
def contract_attributes
# convert SimpleContract attributes to Contract attributes
end
def save
self.contract = Contract.new(contract_attributes)
contract.save
end
end
-
class ContractsController < ApplicationController
# ...
def create
@simple_contract = SimpleContract.new(contract_params)
flash[:notice] = "Created Contract." if @simple_contract.save
respond_with(@simple_contract.contract)
end
# ...
end
我可能会偏离基地。希望至少能为你触发一个想法。