Rails重构动态行为的最佳实践

时间:2009-06-22 15:22:21

标签: ruby-on-rails ruby model-view-controller refactoring dry

我已经开发了偶然的国家/地区选择下拉列表,我想将此行为分解为地址模型,这样我就不必复制动态行为(在视图和控制器之间或多或少分开)每次我希望用户能够输入完整的地址。

基本上我正试图让我的脚趾更深入DRY。但我不确定在哪里嵌入这些行为。我是否使用模型或帮助程序来构建必要的表单?最重要的是:我在哪里以及如何调用动态行为来更新状态列表?我是否需要地址控制器,或者是否可以在模型中完成?

换句话说,我现在在视图中得到的是:

  # _refine.html.erb
      <tr>
        <td>
        <%= label_tag :dest_country, 'Country: ' %></td><td>
          <%= select_tag :dest_country, 
            options_for_select(Carmen::country_names 
                      << 'Select a country', 
                :selected => 'Select a country'), 
            {:include_blank => true,
            :id => 'country_select',                              
            :style => 'width: 180px',
            :onchange => remote_function(
              :url => {:action => 'update_state_select'},
              :with => "'country='+value")} %>
        </td>      
      </tr>
      <tr>
          <div id="state_select_div">
            <td><%= label_tag :dest_state, 'State: &nbsp&nbsp' %></td>
            <td><%= select_tag :dest_state, 
                        options_for_select(Carmen::states('US').collect{
                                |s| [s[0],s[0]]} << ['Select a state'], 
                                :selected => 'Select a state'), 
                                {:style => 'width: 180px'} %></td>
          </div>      
      </tr>

更新方法在控制器中:

# search_controller.rb

def update_state_select
  # puts "Attempting to update states"
  states = []
  q = Carmen::states(Carmen::country_code(params[:country]))
  states = q unless q.nil? 
  render :update do |page|
    page.replace_html("state_select_div",
    :partial => "state_select",
    :locals => {:states => states }
  )
 end
end

最后,我有一个部分用正确的名称或空白文本字段输入:

# _state_select.html.erb
<% unless states.empty? or states.nil? %>
      <%= label_tag :dest_state, 'Select a state'  %>
 <br/> <%= select_tag :dest_state, 
                     options_for_select(states.collect{|s| [s[0],s[0]]}  
                         << ['Select a state'], 
                       :selected => 'Select a state'), 
                       {:style => 'width: 180px'} %>
 <% else %>
   <%= label_tag :dest_state, 'Please enter state/province' %><br />
   <%= text_field_tag :dest_state %>
<% end %>

现在,我想做的是能够通过模型(例如,Person has_one:地址)关联地址,并在表单中创建新人,能够使用像

 <%= label_tag :name, 'What's your name?' %>
 <%= text_field_tag :name %>
 <%= label_tag :address, 'Where do you live?' %>
 <%= address_fields_tag :address %>

这可以生成适当的下拉菜单,动态耦合在一起,其结果可以通过Person.address.country和Person.address.state访问。

提前致谢!

3 个答案:

答案 0 :(得分:2)

首先让我确保我正确理解代码;当用户更改国家/地区选择框时,向服务器发出AJAX请求,然后服务器发送回更新状态框的脚本以及正确的条目?

如果是这样,我会采用完全不同的策略。使用在dest_country更改时触发的回调。让这个回调对例如AJAX GET请求做/countries/{COUNTRY}/states.json(或.xml)。当然,您需要为此设置路由和控制器。使用返回的值填充dest_state

更新: 至于分解用户界面代码,请尝试使用partials和helpers。 http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials

答案 1 :(得分:1)

数据属于模型,演示文稿属于模板(和帮助程序)。 这就是说我会开始从演示文稿中分离数据并删除无用的代码。

states.collect{|s| [s[0],s[0]]}

AFAIK,不需要执行此操作。您可以将其简化为

states.collect{|s| s[0]}

states.collect(&:first)

但最重要的是,您可以在Carmen类中提供自定义方法,以便以正确的格式获取数据。

使用以下代码可以简化以下代码:prompt =&gt; “选择状态”选项,这样您既不需要附加新数组项,也不需要手动选择名为“选择状态”的项目。

options_for_select(Carmen::states('US').collect{
                                |s| [s[0],s[0]]} << ['Select a state'], 
                                :selected => 'Select a state'),

最后,您可能希望将复杂的帮助器封装在对象中,以便更容易使用Test :: Unit套件进行测试。查看this screencast

谈论控制器,改变

# puts "Attempting to update states"
states = []
q = Carmen::states(Carmen::country_code(params[:country]))
states = q unless q.nil? 

# puts "Attempting to update states"
states = Carmen::states(Carmen::country_code(params[:country])) || []

当你只能打一个方法时,一定不要调用两个方法。变化

<% unless states.empty? or states.nil? %>

<% unless states.blank? %>

答案 2 :(得分:0)

我使用Carmen和jQuery写了一篇关于此的博文。代码是可重用的,因为它在我的地址视图中处于部分状态。我在表单中使用 fields_for“address”标记。您可以在此处阅读完整条目:http://eric.lubow.org/2009/ruby/rails/country-state-select-using-carmen-and-jquery/