Commit c5e6187faf2bd455ad3560fc9e29f154decd1661
Committed by
Paulo Meireles
1 parent
3d1e7d49
Exists in
master
and in
23 other branches
[Mezuro] Add native metric configuration.
Showing
13 changed files
with
158 additions
and
142 deletions
Show diff stats
plugins/mezuro/controllers/myprofile/mezuro_plugin_metric_configuration_controller.rb
| ... | ... | @@ -7,11 +7,27 @@ class MezuroPluginMetricConfigurationController < MezuroPluginMyprofileControlle |
| 7 | 7 | @base_tools = Kalibro::BaseTool.all |
| 8 | 8 | end |
| 9 | 9 | |
| 10 | - def new_metric_configuration | |
| 10 | + def new_native | |
| 11 | 11 | @configuration_content = profile.articles.find(params[:id]) |
| 12 | - @metric = Kalibro::BaseTool.find_by_name(params[:base_tool]).metric params[:metric_name] | |
| 12 | + @base_tool_name = params[:base_tool_name] | |
| 13 | + @metric = Kalibro::BaseTool.find_by_name(@base_tool_name).metric params[:metric_name] | |
| 14 | + @reading_group_names_and_ids = reading_group_names_and_ids | |
| 13 | 15 | end |
| 14 | - | |
| 16 | + | |
| 17 | + def create_native | |
| 18 | + metric_configuration = Kalibro::MetricConfiguration.new(params[:metric_configuration]) | |
| 19 | + metric_configuration.save | |
| 20 | + | |
| 21 | + if metric_configuration_has_errors? metric_configuration | |
| 22 | + redirect_to_error_page metric_configuration.errors[0].message | |
| 23 | + else | |
| 24 | + id = params[:id] | |
| 25 | + metric_name = params[:metric_configuration][:metric][:name] | |
| 26 | + redirect_to "/myprofile/#{profile.identifier}/plugin/mezuro/metric_configuration/edit_native?id=#{id}&metric_name=#{metric_name.gsub(/\s/, '+')}" | |
| 27 | + end | |
| 28 | + end | |
| 29 | + | |
| 30 | +=begin | |
| 15 | 31 | def new_compound_metric_configuration |
| 16 | 32 | @configuration_content = profile.articles.find(params[:id]) |
| 17 | 33 | @metric_configurations = @configuration_content.metric_configurations |
| ... | ... | @@ -33,17 +49,6 @@ class MezuroPluginMetricConfigurationController < MezuroPluginMyprofileControlle |
| 33 | 49 | @metric = @metric_configuration.metric |
| 34 | 50 | end |
| 35 | 51 | |
| 36 | - def create_metric_configuration | |
| 37 | - id = params[:id] | |
| 38 | - metric_name = params[:metric_configuration][:metric][:name] | |
| 39 | - metric_configuration = Kalibro::MetricConfiguration.new(params[:metric_configuration]) | |
| 40 | - metric_configuration.save | |
| 41 | - if metric_configuration_has_errors? metric_configuration | |
| 42 | - redirect_to_error_page metric_configuration.errors[0].message | |
| 43 | - else | |
| 44 | - redirect_to "/myprofile/#{profile.identifier}/plugin/mezuro/metric_configuration/edit_metric_configuration?id=#{id}&metric_name=#{metric_name.gsub(/\s/, '+')}" | |
| 45 | - end | |
| 46 | - end | |
| 47 | 52 | |
| 48 | 53 | def create_compound_metric_configuration |
| 49 | 54 | id = params[:id] |
| ... | ... | @@ -92,11 +97,20 @@ class MezuroPluginMetricConfigurationController < MezuroPluginMyprofileControlle |
| 92 | 97 | redirect_to "/#{profile.identifier}/#{configuration_content.slug}" |
| 93 | 98 | end |
| 94 | 99 | end |
| 100 | +=end | |
| 95 | 101 | |
| 96 | 102 | private |
| 97 | 103 | |
| 104 | + def reading_group_names_and_ids | |
| 105 | + array = Kalibro::ReadingGroup.all.map { |reading_group| [reading_group.name, reading_group.id] } | |
| 106 | + array.sort { |x,y| x.first.downcase <=> y.first.downcase } | |
| 107 | + end | |
| 108 | + | |
| 109 | + def metric_configuration_has_errors? metric_configuration | |
| 110 | + not metric_configuration.errors.empty? | |
| 111 | + end | |
| 112 | + | |
| 98 | 113 | def configuration_content_has_errors? |
| 99 | 114 | not @configuration_content.errors[:base].nil? |
| 100 | 115 | end |
| 101 | - | |
| 102 | 116 | end | ... | ... |
plugins/mezuro/controllers/myprofile/mezuro_plugin_myprofile_controller.rb
| ... | ... | @@ -18,11 +18,7 @@ class MezuroPluginMyprofileController < ProfileController #MyprofileController? |
| 18 | 18 | redirect_to "/myprofile/#{profile.identifier}/plugin/mezuro/error_page?message=#{message}" |
| 19 | 19 | end |
| 20 | 20 | |
| 21 | - def metric_configuration_has_errors? metric_configuration | |
| 22 | - not metric_configuration.errors.empty? | |
| 23 | - end | |
| 24 | - | |
| 25 | - def process_error_message message | |
| 21 | + def process_error_message message #FIXME | |
| 26 | 22 | if message =~ /bla/ |
| 27 | 23 | message |
| 28 | 24 | else | ... | ... |
plugins/mezuro/lib/kalibro/metric.rb
plugins/mezuro/lib/mezuro_plugin/helpers/content_viewer_helper.rb
| ... | ... | @@ -52,6 +52,11 @@ class MezuroPlugin::Helpers::ContentViewerHelper |
| 52 | 52 | MezuroPluginModuleResultController.helpers.distance_of_time_in_words(0, seconds, include_seconds = true) |
| 53 | 53 | end |
| 54 | 54 | |
| 55 | + def self.aggregation_options | |
| 56 | + [["Average","AVERAGE"], ["Median", "MEDIAN"], ["Maximum", "MAXIMUM"], ["Minimum", "MINIMUM"], | |
| 57 | + ["Count", "COUNT"], ["Standard Deviation", "STANDARD_DEVIATION"]] | |
| 58 | + end | |
| 59 | + | |
| 55 | 60 | private |
| 56 | 61 | |
| 57 | 62 | def self.discretize_array(array) | ... | ... |
plugins/mezuro/test/fixtures/metric_fixtures.rb
| ... | ... | @@ -13,7 +13,7 @@ class MetricFixtures |
| 13 | 13 | end |
| 14 | 14 | |
| 15 | 15 | def self.total_cof_hash |
| 16 | - {:name => 'Total Coupling Factor', :compound => "false", :scope => 'APPLICATION', :origin => 'Analizo', :language => ['JAVA']} | |
| 16 | + {:name => 'Total Coupling Factor', :compound => "false", :scope => 'APPLICATION', :language => ['JAVA']} | |
| 17 | 17 | end |
| 18 | 18 | |
| 19 | 19 | def self.amloc |
| ... | ... | @@ -21,7 +21,7 @@ class MetricFixtures |
| 21 | 21 | end |
| 22 | 22 | |
| 23 | 23 | def self.amloc_hash |
| 24 | - {:name => 'Average Method LOC', :compound => "false", :scope => 'CLASS', :origin => 'Analizo', :language => ['JAVA']} | |
| 24 | + {:name => 'Average Method LOC', :compound => "false", :scope => 'CLASS', :language => ['JAVA']} | |
| 25 | 25 | end |
| 26 | 26 | |
| 27 | 27 | end | ... | ... |
plugins/mezuro/test/functional/myprofile/mezuro_plugin_metric_configuration_controller_test.rb
| ... | ... | @@ -4,6 +4,7 @@ require "#{RAILS_ROOT}/plugins/mezuro/test/fixtures/base_tool_fixtures" |
| 4 | 4 | require "#{RAILS_ROOT}/plugins/mezuro/test/fixtures/metric_fixtures" |
| 5 | 5 | require "#{RAILS_ROOT}/plugins/mezuro/test/fixtures/metric_configuration_fixtures" |
| 6 | 6 | require "#{RAILS_ROOT}/plugins/mezuro/test/fixtures/configuration_fixtures" |
| 7 | +require "#{RAILS_ROOT}/plugins/mezuro/test/fixtures/reading_group_fixtures" | |
| 7 | 8 | |
| 8 | 9 | class MezuroPluginMetricConfigurationControllerTest < ActionController::TestCase |
| 9 | 10 | |
| ... | ... | @@ -26,8 +27,11 @@ class MezuroPluginMetricConfigurationControllerTest < ActionController::TestCase |
| 26 | 27 | @base_tool = BaseToolFixtures.base_tool |
| 27 | 28 | @base_tool_hash = BaseToolFixtures.base_tool_hash |
| 28 | 29 | |
| 29 | -=begin | |
| 30 | 30 | @metric = MetricFixtures.amloc |
| 31 | + | |
| 32 | + @reading_group = ReadingGroupFixtures.reading_group | |
| 33 | + | |
| 34 | +=begin | |
| 31 | 35 | @metric_configuration = MetricConfigurationFixtures.amloc_metric_configuration |
| 32 | 36 | @metric_configuration_hash = MetricConfigurationFixtures.amloc_metric_configuration_hash |
| 33 | 37 | @compound_metric_configuration = MetricConfigurationFixtures.sc_metric_configuration |
| ... | ... | @@ -49,16 +53,25 @@ class MezuroPluginMetricConfigurationControllerTest < ActionController::TestCase |
| 49 | 53 | assert_response 200 |
| 50 | 54 | end |
| 51 | 55 | |
| 52 | -=begin | |
| 53 | - should 'test new metric configuration' do | |
| 54 | - Kalibro::BaseTool.expects(:request).with("BaseTool", :get_base_tool, {:base_tool_name => @base_tool.name}).returns({:base_tool => @base_tool_hash}) | |
| 55 | - get :new_metric_configuration, :profile => @profile.identifier, :id => @content.id, :base_tool => @base_tool.name, :metric_name => @metric.name | |
| 56 | + should 'test new native metric configuration' do | |
| 57 | + Kalibro::BaseTool.expects(:find_by_name).with(@base_tool.name).returns(@base_tool) | |
| 58 | + Kalibro::ReadingGroup.expects(:all).returns([@reading_group]) | |
| 59 | + get :new_native, :profile => @profile.identifier, :id => @content.id, :base_tool_name => @base_tool.name, :metric_name => @metric.name | |
| 56 | 60 | assert_equal @content, assigns(:configuration_content) |
| 57 | 61 | assert_equal @metric.name, assigns(:metric).name |
| 62 | + assert_equal @base_tool.name, assigns(:base_tool_name) | |
| 63 | + assert_equal [[@reading_group.name,@reading_group.id]], assigns(:reading_group_names_and_ids) | |
| 58 | 64 | assert_response 200 |
| 59 | 65 | end |
| 60 | 66 | |
| 67 | + should 'test create native metric configuration' do | |
| 68 | + Kalibro::MetricConfiguration.expects(:new).with(@metric_configuration_hash).returns(@created_metric_configuration) | |
| 69 | + @created_metric_configuration.expects(:save).returns(true) | |
| 70 | + get :create_native, :profile => @profile.identifier, :id => @content.id, :metric_configuration => @native_hash | |
| 71 | + assert_response 200 | |
| 72 | + end | |
| 61 | 73 | |
| 74 | +=begin | |
| 62 | 75 | should 'test new compound metric configuration' do |
| 63 | 76 | Kalibro::Configuration.expects(:request).with("Configuration", :get_configuration, { |
| 64 | 77 | :configuration_name => @content.name}).returns({:configuration => @configuration_hash}) |
| ... | ... | @@ -95,16 +108,6 @@ class MezuroPluginMetricConfigurationControllerTest < ActionController::TestCase |
| 95 | 108 | assert_response 200 |
| 96 | 109 | end |
| 97 | 110 | |
| 98 | - should 'test create native metric configuration' do | |
| 99 | - Kalibro::MetricConfiguration.expects(:request).with("MetricConfiguration", :save_metric_configuration, { | |
| 100 | - :metric_configuration => @metric_configuration.to_hash, | |
| 101 | - :configuration_name => @metric_configuration.configuration_name}) | |
| 102 | - get :create_metric_configuration, | |
| 103 | - :profile => @profile.identifier, | |
| 104 | - :id => @content.id, | |
| 105 | - :metric_configuration => @native_hash | |
| 106 | - assert_response 302 | |
| 107 | - end | |
| 108 | 111 | |
| 109 | 112 | should 'test compound metric creation' do |
| 110 | 113 | Kalibro::MetricConfiguration.expects(:request).with("MetricConfiguration", :save_metric_configuration, { | ... | ... |
plugins/mezuro/test/unit/mezuro_plugin/helpers/content_viewer_helper_test.rb
| ... | ... | @@ -55,4 +55,9 @@ class ContentViewerHelperTest < ActiveSupport::TestCase |
| 55 | 55 | assert_equal 'AverageMethodLOC', @helper.format_name(metric_configuration_snapshot) |
| 56 | 56 | end |
| 57 | 57 | |
| 58 | + should 'create aggregation options array' do | |
| 59 | + assert_equal [["Average","AVERAGE"], ["Median", "MEDIAN"], ["Maximum", "MAXIMUM"], ["Minimum", "MINIMUM"], | |
| 60 | + ["Count", "COUNT"], ["Standard Deviation", "STANDARD_DEVIATION"]], @helper.aggregation_options | |
| 61 | + end | |
| 62 | + | |
| 58 | 63 | end | ... | ... |
plugins/mezuro/views/content_viewer/show_configuration.rhtml
plugins/mezuro/views/mezuro_plugin_metric_configuration/choose_metric.html.erb
| ... | ... | @@ -10,8 +10,8 @@ |
| 10 | 10 | |
| 11 | 11 | <div id="<%=base_tool.name%>" style="display:none"> |
| 12 | 12 | <% base_tool.supported_metrics.each do |metric| %> |
| 13 | -       <%= link_to metric.name, :controller => "mezuro_plugin_metric_configuration", :action => "new_metric_configuration", | |
| 14 | - :metric_name => metric.name, :base_tool => base_tool.name, :id => @configuration_content.id %> | |
| 13 | +       <%= link_to metric.name, :controller => "mezuro_plugin_metric_configuration", :action => "new_native", | |
| 14 | + :metric_name => metric.name, :base_tool_name => base_tool.name, :id => @configuration_content.id %> | |
| 15 | 15 | <hr size="1" width="93%"/> |
| 16 | 16 | <% end %> |
| 17 | 17 | </div> | ... | ... |
plugins/mezuro/views/mezuro_plugin_metric_configuration/edit.html.erb
0 → 100644
| ... | ... | @@ -0,0 +1,50 @@ |
| 1 | +<script src="/plugins/mezuro/javascripts/validations.js" type="text/javascript"></script> | |
| 2 | +<script src="/javascripts/colorpicker.js" type="text/javascript"></script> | |
| 3 | +<script src="/javascripts/colorpicker-noosfero.js" type="text/javascript"></script> | |
| 4 | + | |
| 5 | +<h2><%= @configuration_content.name %> Configuration</h2> | |
| 6 | + | |
| 7 | +<% owner = (not user.nil?) && user.id == @profile.id %> | |
| 8 | + | |
| 9 | +<% if owner %> | |
| 10 | + <%= render :partial => "content_viewer/metric_configuration_form" %> | |
| 11 | +<% else %> | |
| 12 | + <%= render :partial => "content_viewer/metric_configuration_view" %> | |
| 13 | +<% end %> | |
| 14 | + | |
| 15 | +<h5> Ranges </h5><br/> | |
| 16 | + | |
| 17 | +<table id="ranges"> | |
| 18 | + <tr> | |
| 19 | + <td> | |
| 20 | + Label | |
| 21 | + </td> | |
| 22 | + <td> | |
| 23 | + Beginning | |
| 24 | + </td> | |
| 25 | + <td> | |
| 26 | + End | |
| 27 | + </td> | |
| 28 | + <td> | |
| 29 | + Grade | |
| 30 | + </td> | |
| 31 | + <td> | |
| 32 | + Color | |
| 33 | + </td> | |
| 34 | + <td></td> | |
| 35 | + <td></td> | |
| 36 | + </tr> | |
| 37 | + <% if (@metric_configuration.ranges!=nil) | |
| 38 | + @metric_configuration.ranges.each do |range| %> | |
| 39 | + <%= render :partial => "mezuro_plugin_range/range", :locals => {:range => range, :id => @configuration_content.id, | |
| 40 | + :metric_name => @metric.name} %> | |
| 41 | + <% end | |
| 42 | + end %> | |
| 43 | +</table> | |
| 44 | + | |
| 45 | +<br/> | |
| 46 | +<% if owner %> | |
| 47 | + <%= link_to_remote "New Range", :url => {:action =>"new_range", :controller => "mezuro_plugin_range", :id => @configuration_content.id, :metric_name => @metric.name} %> | |
| 48 | +<% end %> | |
| 49 | +<div id="range_form" style="display:none"></div> | |
| 50 | + | ... | ... |
plugins/mezuro/views/mezuro_plugin_metric_configuration/edit_metric_configuration.html.erb
| ... | ... | @@ -1,50 +0,0 @@ |
| 1 | -<script src="/plugins/mezuro/javascripts/validations.js" type="text/javascript"></script> | |
| 2 | -<script src="/javascripts/colorpicker.js" type="text/javascript"></script> | |
| 3 | -<script src="/javascripts/colorpicker-noosfero.js" type="text/javascript"></script> | |
| 4 | - | |
| 5 | -<h2><%= @configuration_content.name %> Configuration</h2> | |
| 6 | - | |
| 7 | -<% owner = (not user.nil?) && user.id == @profile.id %> | |
| 8 | - | |
| 9 | -<% if owner %> | |
| 10 | - <%= render :partial => "content_viewer/metric_configuration_form" %> | |
| 11 | -<% else %> | |
| 12 | - <%= render :partial => "content_viewer/metric_configuration_view" %> | |
| 13 | -<% end %> | |
| 14 | - | |
| 15 | -<h5> Ranges </h5><br/> | |
| 16 | - | |
| 17 | -<table id="ranges"> | |
| 18 | - <tr> | |
| 19 | - <td> | |
| 20 | - Label | |
| 21 | - </td> | |
| 22 | - <td> | |
| 23 | - Beginning | |
| 24 | - </td> | |
| 25 | - <td> | |
| 26 | - End | |
| 27 | - </td> | |
| 28 | - <td> | |
| 29 | - Grade | |
| 30 | - </td> | |
| 31 | - <td> | |
| 32 | - Color | |
| 33 | - </td> | |
| 34 | - <td></td> | |
| 35 | - <td></td> | |
| 36 | - </tr> | |
| 37 | - <% if (@metric_configuration.ranges!=nil) | |
| 38 | - @metric_configuration.ranges.each do |range| %> | |
| 39 | - <%= render :partial => "mezuro_plugin_range/range", :locals => {:range => range, :id => @configuration_content.id, | |
| 40 | - :metric_name => @metric.name} %> | |
| 41 | - <% end | |
| 42 | - end %> | |
| 43 | -</table> | |
| 44 | - | |
| 45 | -<br/> | |
| 46 | -<% if owner %> | |
| 47 | - <%= link_to_remote "New Range", :url => {:action =>"new_range", :controller => "mezuro_plugin_range", :id => @configuration_content.id, :metric_name => @metric.name} %> | |
| 48 | -<% end %> | |
| 49 | -<div id="range_form" style="display:none"></div> | |
| 50 | - |
plugins/mezuro/views/mezuro_plugin_metric_configuration/new_metric_configuration.html.erb
| ... | ... | @@ -1,51 +0,0 @@ |
| 1 | -<script src="/plugins/mezuro/javascripts/validations.js" type="text/javascript"></script> | |
| 2 | - | |
| 3 | -<h2><%= @configuration_content.name %> Configuration</h2> | |
| 4 | - | |
| 5 | -<% form_for :metric_configuration, :url => {:action =>"create_metric_configuration", :controller => "mezuro_plugin_metric_configuration"}, :method => :get do |f| %> | |
| 6 | - <%= hidden_field_tag :id, @configuration_content.id %> | |
| 7 | - <%= f.hidden_field :configuration_name, :value => @configuration_content.name %> | |
| 8 | - | |
| 9 | - <% f.fields_for :metric do |m| %> | |
| 10 | - | |
| 11 | - <% @metric.language.each do |language| %> | |
| 12 | - <%= m.hidden_field :language, :multiple => true, :value => language %> | |
| 13 | - <% end %> | |
| 14 | - | |
| 15 | - <%= m.hidden_field "scope", :value => @metric.scope %> | |
| 16 | - <p> | |
| 17 | - <%= m.label :origin, "Collector Name:" %> | |
| 18 | - <%= @metric.origin %> | |
| 19 | - <%= m.hidden_field "origin", :value => @metric.origin %> | |
| 20 | - </p> | |
| 21 | - <p> | |
| 22 | - <%= m.label :name, "Metric Name:" %> | |
| 23 | - <%= @metric.name %> | |
| 24 | - <%= m.hidden_field "name", :value => @metric.name %> | |
| 25 | - </p> | |
| 26 | - <!--<p>--> | |
| 27 | - <% m.label :description, "Description:" %> | |
| 28 | - <% @metric.description %> | |
| 29 | - <% m.hidden_field "description", :value => @metric.description %> | |
| 30 | - <!--</p>--> | |
| 31 | - <% end %> | |
| 32 | - <p> | |
| 33 | - <%= f.label :code, "Code:" %> | |
| 34 | - <%= f.text_field :code %> | |
| 35 | - </p> | |
| 36 | - <p> | |
| 37 | - <%= f.label :aggregation_form, "Aggregation Form:" %> | |
| 38 | - <%= f.select :aggregation_form, [["Average","AVERAGE"], ["Median", "MEDIAN"], ["Maximum", "MAXIMUM"], ["Minimum", "MINIMUM"], | |
| 39 | - ["Count", "COUNT"], ["Standard Deviation", "STANDARD_DEVIATION"]] %> | |
| 40 | - </p> | |
| 41 | - <p> | |
| 42 | - <%= f.label :weight, "Weight:" %> | |
| 43 | - <%= f.text_field :weight %> | |
| 44 | - </p> | |
| 45 | - | |
| 46 | - <p> | |
| 47 | - <%= f.submit "Add" %> | |
| 48 | - </p> | |
| 49 | - | |
| 50 | -<% end %> | |
| 51 | - |
plugins/mezuro/views/mezuro_plugin_metric_configuration/new_native.html.erb
0 → 100644
| ... | ... | @@ -0,0 +1,44 @@ |
| 1 | +<script src="/plugins/mezuro/javascripts/validations.js" type="text/javascript"></script> | |
| 2 | + | |
| 3 | +<h2><%= @configuration_content.name %> Configuration</h2> | |
| 4 | + | |
| 5 | +<%= hidden_field_tag :id, @configuration_content.id %> | |
| 6 | +<% form_for :metric_configuration, :url => {:action =>"create_native", :controller => "mezuro_plugin_metric_configuration"}, :method => :get do |f| %> | |
| 7 | + <%= f.hidden_field :configuration_id, :value => @configuration_content.configuration_id %> | |
| 8 | + | |
| 9 | + <%= labelled_form_field _('Collector Name:'), f.text_field(:base_tool_name, :value => @base_tool_name, :readonly => true) %> | |
| 10 | + | |
| 11 | + <% f.fields_for :metric do |m| %> | |
| 12 | + | |
| 13 | + <% @metric.language.each do |language| %> | |
| 14 | + <%= m.hidden_field :language, :multiple => true, :value => language %> | |
| 15 | + <% end %> | |
| 16 | + | |
| 17 | + <%= m.hidden_field "scope", :value => @metric.scope %> | |
| 18 | + | |
| 19 | + <p> | |
| 20 | + <%= m.label :name, "Metric Name:" %> | |
| 21 | + <%= @metric.name %> | |
| 22 | + <%= m.hidden_field "name", :value => @metric.name %> | |
| 23 | + </p> | |
| 24 | + <% m.label :description, "Description:" %> | |
| 25 | + <% @metric.description %> | |
| 26 | + <% m.hidden_field "description", :value => @metric.description %> | |
| 27 | + <% end %> | |
| 28 | + | |
| 29 | + <%= required labelled_form_field _('Code:'), f.text_field(:code) %> | |
| 30 | + | |
| 31 | + <%= required labelled_form_field _('Aggregation Form:'), | |
| 32 | + f.select(:aggregation_form, MezuroPlugin::Helpers::ContentViewerHelper.aggregation_options) %> | |
| 33 | + | |
| 34 | + <%= required labelled_form_field _('Weight:'), f.text_field(:weight) %> | |
| 35 | + | |
| 36 | + <%= required labelled_form_field _('Reading Group:'), | |
| 37 | + f.select(:reading_group_id, @reading_group_names_and_ids) %> | |
| 38 | + | |
| 39 | + <p> | |
| 40 | + <%= f.submit "Add" %> | |
| 41 | + </p> | |
| 42 | + | |
| 43 | +<% end %> | |
| 44 | + | ... | ... |