所以我有一些数据从控制器中的另一个rails应用程序中拉出来让我们称之为ExampleController,我希望在允许向导进入下一步之前将其验证为在我的模型中,我不能完全弄清楚我应该怎么做(我知道直接从控制器获取这些数据到模型中违反了MVC我正在寻找从控制器获取数据的最佳解决方法)。数据必须来自控制器,因为获取它的方法包含在ApplicationController中,但如果这更容易,我可以在Awizard控制器中执行此操作。 (我也不能使用宝石)
请提供一些问题的建议,而不是解释为什么这不是正确的做事方式,我已经意识到已经但不能以另一种方式做到这一点。
示例控制器
这应该代替呈现数据,然后检查它在其他地方是否为空白?
class ExampleController < ApplicationController
def valid_data?
data = #data could be nil or not
if data.blank?
return false
else
return true
end
end
我的模型 - (models / awizard.rb)
如何使用valid_data?来自示例控制器的方法?在我的验证中。
class AWizard
include ActiveModel::Validations
include ActiveModel::Conversion
include ActiveModel::Dirty
include ActiveModel::Naming
#This class is used to manage the wizard steps using ActiveModel (not ActiveRecord)
attr_accessor :id
attr_writer :current_step #used to write to current step
define_attribute_methods [:current_step] #used for marking change
validate :first_step_data, :if => lambda { |o| o.current_step == "step1" };
def first_step_data
#What should i put here to check the valid_data? from the examplecontroller
end
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def current_step
@current_step || steps.first
end
def steps
%w[step1 step2 step3] #make list of steps (partials)
end
def next_step
current_step_will_change! #mark changed when moving stepped
self.current_step = steps[steps.index(current_step)+1] unless last_step?
end
def previous_step
current_step_will_change! #mark changed when moving stepped
self.current_step = steps[steps.index(current_step)-1] unless first_step?
end
def first_step?
current_step == steps.first
end
def last_step?
current_step == steps.last
end
def all_valid?
steps.all? do |step|
self.current_step = step
valid?
end
end
def step(val)
current_step_will_change!
self.current_step = steps[val]
end
def persisted?
self.id == 1
end
end
或者我是否需要将此添加到此视图中?
(适用/视图/ awizard / _step1.html.erb)
<div class="field">
<%= f.label 'Step1' %><br />
#This is the step I want to validate
</div>
答案 0 :(得分:2)
我可能误解了这个问题,因为我的答案很简单。然而,这里的解决方案并不依赖于元编程,而是指向Wizard(类不是它创建的对象)是单例/常量。
class ExampleController < ApplicationController
def valid_data?
data = #data could be nil or not
result = data.blank?
Awizard.valid_data= result
result
end
end
class Wizard
cattr_accessor :valid_data
def valid_data?
self.class.valid_data
end
end
如果在使用向导传递step_one之前必须调用当前的ExampleController#valid_data。
(由@Valery Kvon提出)
论证是向导是应用程序的全局,并且@wizard实例将依赖于全局状态,因此被严重封装。但是,来自其他网站的数据在您的应用范围内是gloabl。因此,与持有数据的人没有不匹配。相反,它可以被视为一个特征。
一个例子。奇才魔法只在满月时有效。 应用程序SkyReport发送数据:
:full_moon => true
如果他们需要继续执行第二步,它会影响第一阶段的所有向导。因此,依赖于Wizard.valid_data?
的全局状态正是我们想要的......
然而如果每个向导都有来自Gandalf应用程序的个人消息,那么我们将要强制调用Gandalf的数据,但之后解决方案更简单:
# in example_controller.rb
before_filter :set_wizard_data, :only => [:create, :update]
....
def set_wizard_data
@wizard = Wizard.find params[:id]
@wizard.valid_data= valid_data
end
但这再次暗示Gandalf.app知道(某些)@wizard以及问题是如何呈现的,来自其他网站的数据是非常不可知的!
这里的问题是我们对应用程序及其要求和基本逻辑的了解不足以决定什么是好的......
答案 1 :(得分:1)
与模型共享控制器级数据的唯一方法是通过外部访问器。 使用元编程,您可以欺骗将其传递给模型实例。
def valid_data?
data = #data could be nil or not
result = data.blank? ? false : true
instance_eval <<-EOV
def AWizard.new(*args)
super(*args).tap {|aw| aw.external_valid = #{result}}
end
EOV
result
end
class AWizard
attr_accessor :external_valid
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
validate :first_step_data, :if => lambda { |o| o.current_step == "step1" };
def first_step_data
# :external_valid would be true or false according to a valid_data?. Nil would be if valid_data? has not been called
if external_valid == false
errors.add ...
end
end
end
答案 2 :(得分:0)
您可以将控制器中的数据作为参数传递给模型中的验证方法。
在你的models / awizard.rb
中def valid_for_step_one?(some_external_data)
#validation logic here
end
因此,在您的控制器中,您可以致电:
model.valid_for_step_one?(data_from_controller)
如果您可以从控制器中获取数据的描述,那也很好。它与模型awizard有什么关系?
因为另一个选项是将外部数据设置为模型的属性。然后你可以在那里的验证函数中使用它。
答案 3 :(得分:0)
所以我尝试编辑并添加到@charlysisto问题,因为它最接近答案但是它不起作用所以这里是我使用的解决方案,因为建议答案是将数据从控制器发送到模型(虽然使用视图调用控制器方法的答案遗漏了)这是我的解决方案
模型 - models / awizard.rb
class Awizard
include ActiveModel::Validations
cattr_accessor :valid_data
validate :data_validation :if => lambda { |o| o.current_step == "step1" }
def data_validation
if self.valid_data == false || self.valid_data.blank?
errors.add(:valid_data, "not found")
end
end
#Other wizard stuff
end
查看 - awizard / _step1.html.erb
<div class="field">
<% f.label "valid data? %>
<% @_controller.valid_data %> #Call controller method to send data to model
</div>
<强>控制器强>
class AwizardController < ApplicationController
def valid_data
data = #data from elsewhere
if !data.blank?
Awizard.valid_data = true
else
Awizard.valid_data = false
end
end