5

所以我有一些数据是从控制器中的另一个 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)

如何使用有效数据?示例控制器中的方法?在我的验证中。

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

还是我需要将此添加到此视图中?

(/views/awizard/_step1.html.erb)

<div class="field">
  <%= f.label 'Step1' %><br />
  #This is the step I want to validate
</div>

4

4 回答 4

2

我可能误解了这个问题,因为我的答案很简单。然而,这是一个不求助于元编程的解决方案,而是向导(它创建的类而不是对象)是一个单例/常量的事实。

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 对应用程序是全局的,@wizard 实例将依赖于全局状态,因此封装得不好。但是来自另一个站点的数据在您的应用程序范围内是全局的。因此,拥有数据的向导蜂并没有不匹配。相反,它可以被视为一个特征。

一个例子。奇才魔法只在满月时有效。应用程序 SkyReport 发送数据:

:full_moon => true

如果他们需要继续其权力的第 2 步,它会影响第 1 阶段中的所有巫师。因此依赖全局状态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 并且从问题的呈现方式来看,来自其他站点的数据非常不可知!

这里的问题是我们对应用程序、它的要求和底层逻辑了解不够,无法决定什么是好或不好......

于 2013-01-16T13:16:56.903 回答
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
于 2013-01-16T10:06:57.443 回答
0

所以我尝试编辑并添加到@charlysisto 问题,因为这最接近答案,但它不起作用所以这是我使用的解决方案,正如建议的答案是将数据从控制器发送到模型(虽然使用视图调用控制器方法遗漏的答案)这是我的解决方案

模型- 模型/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

查看- 向导/_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
于 2013-01-17T00:22:56.230 回答
0

您可以将来自控制器的数据作为参数传递给模型中的验证方法。

在你的模型/awizard.rb

def valid_for_step_one?(some_external_data)
  #validation logic here
end

这样在您的控制器中,您可以调用:

model.valid_for_step_one?(data_from_controller)

如果您可以描述您从控制器获得的数据,那也是很好的。它与模型向导有什么关系?

因为另一种选择是将外部数据设置为模型的属性。然后您可以从那里在您的验证功能中使用它。

于 2013-01-16T08:50:34.710 回答