Commit a865a6e9cc94b606dc75951c8cfd96656ba0a3a6

Authored by João M. M. da Silva + Diego Araújo
Committed by Paulo Meireles
1 parent f388a39c

[Mezuro] Handling exceptions in models, controllers and views

plugins/mezuro/controllers/mezuro_plugin_myprofile_controller.rb
@@ -4,7 +4,7 @@ class MezuroPluginMyprofileController < ProfileController @@ -4,7 +4,7 @@ class MezuroPluginMyprofileController < ProfileController
4 4
5 rescue_from Exception do |exception| 5 rescue_from Exception do |exception|
6 message = URI.escape(CGI.escape(exception.message),'.') 6 message = URI.escape(CGI.escape(exception.message),'.')
7 - redirect_to "/myprofile/#{profile.identifier}/plugin/mezuro/error_page?message=#{message}" 7 + redirect_to_error_page message
8 end 8 end
9 9
10 def error_page 10 def error_page
@@ -31,6 +31,7 @@ class MezuroPluginMyprofileController < ProfileController @@ -31,6 +31,7 @@ class MezuroPluginMyprofileController < ProfileController
31 def new_compound_metric_configuration 31 def new_compound_metric_configuration
32 @configuration_content = profile.articles.find(params[:id]) 32 @configuration_content = profile.articles.find(params[:id])
33 @metric_configurations = @configuration_content.metric_configurations 33 @metric_configurations = @configuration_content.metric_configurations
  34 + look_for_configuration_content_errors
34 end 35 end
35 36
36 def edit_metric_configuration 37 def edit_metric_configuration
@@ -45,7 +46,7 @@ class MezuroPluginMyprofileController < ProfileController @@ -45,7 +46,7 @@ class MezuroPluginMyprofileController < ProfileController
45 @metric_configurations = @configuration_content.metric_configurations 46 @metric_configurations = @configuration_content.metric_configurations
46 @metric = @metric_configuration.metric 47 @metric = @metric_configuration.metric
47 end 48 end
48 - 49 +
49 def create_metric_configuration 50 def create_metric_configuration
50 id = params[:id] 51 id = params[:id]
51 metric_name = params[:metric_configuration][:metric][:name] 52 metric_name = params[:metric_configuration][:metric][:name]
@@ -56,7 +57,9 @@ class MezuroPluginMyprofileController < ProfileController @@ -56,7 +57,9 @@ class MezuroPluginMyprofileController < ProfileController
56 def create_compound_metric_configuration 57 def create_compound_metric_configuration
57 id = params[:id] 58 id = params[:id]
58 metric_name = params[:metric_configuration][:metric][:name] 59 metric_name = params[:metric_configuration][:metric][:name]
59 - Kalibro::MetricConfiguration.new(params[:metric_configuration]).save 60 + metric_configuration = Kalibro::MetricConfiguration.new(params[:metric_configuration])
  61 + metric_configuration.save
  62 + look_for_model_error metric_configuration
60 redirect_to "/myprofile/#{profile.identifier}/plugin/mezuro/edit_compound_metric_configuration?id=#{id}&metric_name=#{metric_name.gsub(/\s/, '+')}" 63 redirect_to "/myprofile/#{profile.identifier}/plugin/mezuro/edit_compound_metric_configuration?id=#{id}&metric_name=#{metric_name.gsub(/\s/, '+')}"
61 end 64 end
62 65
@@ -65,6 +68,7 @@ class MezuroPluginMyprofileController < ProfileController @@ -65,6 +68,7 @@ class MezuroPluginMyprofileController < ProfileController
65 metric_name = params[:metric_configuration][:metric][:name] 68 metric_name = params[:metric_configuration][:metric][:name]
66 metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, metric_name) 69 metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, metric_name)
67 metric_configuration.update_attributes params[:metric_configuration] 70 metric_configuration.update_attributes params[:metric_configuration]
  71 + look_for_model_error metric_configuration
68 redirect_to "/#{profile.identifier}/#{@configuration_content.slug}" 72 redirect_to "/#{profile.identifier}/#{@configuration_content.slug}"
69 end 73 end
70 74
@@ -73,6 +77,7 @@ class MezuroPluginMyprofileController < ProfileController @@ -73,6 +77,7 @@ class MezuroPluginMyprofileController < ProfileController
73 metric_name = params[:metric_configuration][:metric][:name] 77 metric_name = params[:metric_configuration][:metric][:name]
74 metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, metric_name) 78 metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, metric_name)
75 metric_configuration.update_attributes params[:metric_configuration] 79 metric_configuration.update_attributes params[:metric_configuration]
  80 + look_for_model_error metric_configuration
76 redirect_to "/#{profile.identifier}/#{@configuration_content.slug}" 81 redirect_to "/#{profile.identifier}/#{@configuration_content.slug}"
77 end 82 end
78 83
@@ -81,6 +86,7 @@ class MezuroPluginMyprofileController < ProfileController @@ -81,6 +86,7 @@ class MezuroPluginMyprofileController < ProfileController
81 metric_name = params[:metric_name] 86 metric_name = params[:metric_name]
82 metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(configuration_content.name, metric_name) 87 metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(configuration_content.name, metric_name)
83 metric_configuration.destroy 88 metric_configuration.destroy
  89 + look_for_model_error metric_configuration
84 redirect_to "/#{profile.identifier}/#{configuration_content.slug}" 90 redirect_to "/#{profile.identifier}/#{configuration_content.slug}"
85 end 91 end
86 92
@@ -105,6 +111,7 @@ class MezuroPluginMyprofileController < ProfileController @@ -105,6 +111,7 @@ class MezuroPluginMyprofileController < ProfileController
105 metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, metric_name) 111 metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, metric_name)
106 metric_configuration.add_range(@range) 112 metric_configuration.add_range(@range)
107 metric_configuration.save 113 metric_configuration.save
  114 + look_for_model_error metric_configuration
108 end 115 end
109 116
110 def update_range 117 def update_range
@@ -115,6 +122,7 @@ class MezuroPluginMyprofileController < ProfileController @@ -115,6 +122,7 @@ class MezuroPluginMyprofileController < ProfileController
115 index = metric_configuration.ranges.index{ |range| range.beginning == beginning_id.to_f || beginning_id == "-INF" } 122 index = metric_configuration.ranges.index{ |range| range.beginning == beginning_id.to_f || beginning_id == "-INF" }
116 metric_configuration.ranges[index] = Kalibro::Range.new params[:range] 123 metric_configuration.ranges[index] = Kalibro::Range.new params[:range]
117 metric_configuration.save 124 metric_configuration.save
  125 + look_for_model_error metric_configuration
118 end 126 end
119 127
120 def remove_range 128 def remove_range
@@ -124,6 +132,7 @@ class MezuroPluginMyprofileController < ProfileController @@ -124,6 +132,7 @@ class MezuroPluginMyprofileController < ProfileController
124 metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(configuration_content.name, metric_name) 132 metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(configuration_content.name, metric_name)
125 metric_configuration.ranges.delete_if { |range| range.beginning == beginning_id.to_f || beginning_id == "-INF" } 133 metric_configuration.ranges.delete_if { |range| range.beginning == beginning_id.to_f || beginning_id == "-INF" }
126 metric_configuration.save 134 metric_configuration.save
  135 +
127 formatted_metric_name = metric_name.gsub(/\s/, '+') 136 formatted_metric_name = metric_name.gsub(/\s/, '+')
128 if metric_configuration.metric.class == Kalibro::CompoundMetric 137 if metric_configuration.metric.class == Kalibro::CompoundMetric
129 redirect_to "/myprofile/#{profile.identifier}/plugin/mezuro/edit_compound_metric_configuration?id=#{configuration_content.id}&metric_name=#{formatted_metric_name}" 138 redirect_to "/myprofile/#{profile.identifier}/plugin/mezuro/edit_compound_metric_configuration?id=#{configuration_content.id}&metric_name=#{formatted_metric_name}"
@@ -132,4 +141,18 @@ class MezuroPluginMyprofileController < ProfileController @@ -132,4 +141,18 @@ class MezuroPluginMyprofileController < ProfileController
132 end 141 end
133 end 142 end
134 143
  144 + private
  145 +
  146 + def redirect_to_error_page(message)
  147 + redirect_to "/myprofile/#{profile.identifier}/plugin/mezuro/error_page?message=#{message}"
  148 + end
  149 +
  150 + def look_for_configuration_content_errors
  151 + redirect_to_error_page(@configuration_content.errors[:base]) if not @configuration_content.errors.nil?
  152 + end
  153 +
  154 + def look_for_model_error(model)
  155 + redirect_to_error_page(model.errors[0].message) if not model.errors.empty?
  156 + end
  157 +
135 end 158 end
plugins/mezuro/controllers/mezuro_plugin_profile_controller.rb
@@ -2,11 +2,6 @@ class MezuroPluginProfileController < ProfileController @@ -2,11 +2,6 @@ class MezuroPluginProfileController < ProfileController
2 2
3 append_view_path File.join(File.dirname(__FILE__) + '/../views') 3 append_view_path File.join(File.dirname(__FILE__) + '/../views')
4 4
5 - rescue_from Exception do |exception|  
6 - message = URI.escape(CGI.escape(exception.message),'.')  
7 - redirect_to_error_page message  
8 - end  
9 -  
10 def error_page 5 def error_page
11 @message = params[:message] 6 @message = params[:message]
12 end 7 end
@@ -14,52 +9,79 @@ class MezuroPluginProfileController < ProfileController @@ -14,52 +9,79 @@ class MezuroPluginProfileController < ProfileController
14 def project_state 9 def project_state
15 @content = profile.articles.find(params[:id]) 10 @content = profile.articles.find(params[:id])
16 project = @content.project 11 project = @content.project
17 - redirect_to_error_page(project.errors[0].message) if not project.errors.empty?  
18 - state = project.kalibro_error.nil? ? project.state : "ERROR"  
19 - render :text => state 12 + if project_content_errors?
  13 + redirect_to_error_page(@content.errors[:base])
  14 + else
  15 + state = project.kalibro_error.nil? ? project.state : "ERROR"
  16 + render :text => state
  17 + end
20 end 18 end
21 19
22 def project_error 20 def project_error
23 @content = profile.articles.find(params[:id]) 21 @content = profile.articles.find(params[:id])
24 @project = @content.project 22 @project = @content.project
25 - render :partial => 'content_viewer/project_error' 23 + if project_content_errors?
  24 + redirect_to_error_page(@content.errors[:base])
  25 + else
  26 + render :partial => 'content_viewer/project_error'
  27 + end
26 end 28 end
27 29
28 def project_result 30 def project_result
29 @content = profile.articles.find(params[:id]) 31 @content = profile.articles.find(params[:id])
30 date = params[:date] 32 date = params[:date]
31 @project_result = date.nil? ? @content.project_result : @content.project_result_with_date(date) 33 @project_result = date.nil? ? @content.project_result : @content.project_result_with_date(date)
32 - render :partial => 'content_viewer/project_result' 34 + if project_content_errors?
  35 + redirect_to_error_page(@content.errors[:base])
  36 + else
  37 + render :partial => 'content_viewer/project_result'
  38 + end
33 end 39 end
34 40
35 def module_result 41 def module_result
36 @content = profile.articles.find(params[:id]) 42 @content = profile.articles.find(params[:id])
37 @module_result = @content.module_result(params) 43 @module_result = @content.module_result(params)
38 - render :partial => 'content_viewer/module_result' 44 + if project_content_errors?
  45 + redirect_to_error_page(@content.errors[:base])
  46 + else
  47 + render :partial => 'content_viewer/module_result'
  48 + end
39 end 49 end
40 50
41 def project_tree 51 def project_tree
42 @content = profile.articles.find(params[:id]) 52 @content = profile.articles.find(params[:id])
43 date = params[:date] 53 date = params[:date]
44 project_result = date.nil? ? @content.project_result : @content.project_result_with_date(date) 54 project_result = date.nil? ? @content.project_result : @content.project_result_with_date(date)
45 - @project_name = @content.project.name  
46 - @source_tree = project_result.node_of(params[:module_name])  
47 - render :partial =>'content_viewer/source_tree' 55 + @project_name = @content.project.name if not @content.project.nil?
  56 + if project_content_errors?
  57 + redirect_to_error_page(@content.errors[:base])
  58 + else
  59 + @source_tree = project_result.node_of(params[:module_name])
  60 + render :partial =>'content_viewer/source_tree'
  61 + end
48 end 62 end
49 63
50 def module_metrics_history 64 def module_metrics_history
51 metric_name = params[:metric_name] 65 metric_name = params[:metric_name]
52 @content = profile.articles.find(params[:id]) 66 @content = profile.articles.find(params[:id])
53 module_history = @content.result_history(params[:module_name]) 67 module_history = @content.result_history(params[:module_name])
54 - @score_history = filtering_metric_history(metric_name, module_history)  
55 - render :partial => 'content_viewer/score_history' 68 + if project_content_errors?
  69 + redirect_to_error_page(@content.errors[:base])
  70 + else
  71 + @score_history = filtering_metric_history(metric_name, module_history)
  72 + render :partial => 'content_viewer/score_history'
  73 + end
56 end 74 end
57 75
58 def module_grade_history 76 def module_grade_history
59 @content = profile.articles.find(params[:id]) 77 @content = profile.articles.find(params[:id])
60 modules_results = @content.result_history(params[:module_name]) 78 modules_results = @content.result_history(params[:module_name])
61 - @score_history = modules_results.collect { |module_result| module_result.grade }  
62 - render :partial => 'content_viewer/score_history' 79 + if project_content_errors?
  80 + redirect_to_error_page(@content.errors[:base])
  81 + else
  82 + @score_history = modules_results.collect { |module_result| module_result.grade }
  83 + render :partial => 'content_viewer/score_history'
  84 + end
63 end 85 end
64 86
65 private 87 private
@@ -82,5 +104,8 @@ class MezuroPluginProfileController < ProfileController @@ -82,5 +104,8 @@ class MezuroPluginProfileController < ProfileController
82 redirect_to "/profile/#{profile.identifier}/plugin/mezuro/error_page?message=#{message}" 104 redirect_to "/profile/#{profile.identifier}/plugin/mezuro/error_page?message=#{message}"
83 end 105 end
84 106
  107 + def project_content_errors?
  108 + not @content.errors.nil?
  109 + end
85 end 110 end
86 111
plugins/mezuro/lib/mezuro_plugin/configuration_content.rb
@@ -35,7 +35,12 @@ class MezuroPlugin::ConfigurationContent < Article @@ -35,7 +35,12 @@ class MezuroPlugin::ConfigurationContent < Article
35 end 35 end
36 36
37 def kalibro_configuration_names 37 def kalibro_configuration_names
38 - ["None"] + Kalibro::Configuration.all_names.sort 38 + begin
  39 + ["None"] + Kalibro::Configuration.all_names.sort
  40 + rescue Exception => exception
  41 + errors.add_to_base(exception.message)
  42 + ["None"]
  43 + end
39 end 44 end
40 45
41 private 46 private
plugins/mezuro/lib/mezuro_plugin/project_content.rb
@@ -25,8 +25,8 @@ class MezuroPlugin::ProjectContent < Article @@ -25,8 +25,8 @@ class MezuroPlugin::ProjectContent < Article
25 @project ||= Kalibro::Project.find_by_name(name) 25 @project ||= Kalibro::Project.find_by_name(name)
26 rescue Exception => error 26 rescue Exception => error
27 errors.add_to_base(error.message) 27 errors.add_to_base(error.message)
28 - @project  
29 end 28 end
  29 + @project
30 end 30 end
31 31
32 def project_result 32 def project_result
@@ -35,6 +35,7 @@ class MezuroPlugin::ProjectContent < Article @@ -35,6 +35,7 @@ class MezuroPlugin::ProjectContent < Article
35 rescue Exception => error 35 rescue Exception => error
36 errors.add_to_base(error.message) 36 errors.add_to_base(error.message)
37 end 37 end
  38 + @project_result
38 end 39 end
39 40
40 def project_result_with_date(date) 41 def project_result_with_date(date)
@@ -44,6 +45,7 @@ Kalibro::ProjectResult.first_result_after(name, date) @@ -44,6 +45,7 @@ Kalibro::ProjectResult.first_result_after(name, date)
44 rescue Exception => error 45 rescue Exception => error
45 errors.add_to_base(error.message) 46 errors.add_to_base(error.message)
46 end 47 end
  48 + @project_result
47 end 49 end
48 50
49 def module_result(attributes) 51 def module_result(attributes)
@@ -52,8 +54,9 @@ Kalibro::ProjectResult.first_result_after(name, date) @@ -52,8 +54,9 @@ Kalibro::ProjectResult.first_result_after(name, date)
52 begin 54 begin
53 @module_result ||= Kalibro::ModuleResult.find_by_project_name_and_module_name_and_date(name, module_name, date) 55 @module_result ||= Kalibro::ModuleResult.find_by_project_name_and_module_name_and_date(name, module_name, date)
54 rescue Exception => error 56 rescue Exception => error
55 - raise error 57 + errors.add_to_base(error.message)
56 end 58 end
  59 + @module_result
57 end 60 end
58 61
59 def result_history(module_name) 62 def result_history(module_name)
@@ -74,6 +77,7 @@ Kalibro::ProjectResult.first_result_after(name, date) @@ -74,6 +77,7 @@ Kalibro::ProjectResult.first_result_after(name, date)
74 existing = Kalibro::Project.all_names 77 existing = Kalibro::Project.all_names
75 rescue Exception => error 78 rescue Exception => error
76 errors.add_to_base(error.message) 79 errors.add_to_base(error.message)
  80 + existing = []
77 end 81 end
78 82
79 if existing.any?{|existing_name| existing_name.casecmp(name)==0} # existing.include?(name) + case insensitive 83 if existing.any?{|existing_name| existing_name.casecmp(name)==0} # existing.include?(name) + case insensitive
plugins/mezuro/test/functional/mezuro_plugin_myprofile_controller_test.rb
@@ -39,7 +39,7 @@ class MezuroPluginMyprofileControllerTest < ActionController::TestCase @@ -39,7 +39,7 @@ class MezuroPluginMyprofileControllerTest < ActionController::TestCase
39 @range = RangeFixtures.range_excellent 39 @range = RangeFixtures.range_excellent
40 @range_hash = RangeFixtures.range_excellent_hash 40 @range_hash = RangeFixtures.range_excellent_hash
41 end 41 end
42 - 42 +
43 should 'test choose base tool' do 43 should 'test choose base tool' do
44 Kalibro::BaseTool.expects(:request).with("BaseTool", :get_base_tool_names).returns({:base_tool_name => @base_tool.name}) 44 Kalibro::BaseTool.expects(:request).with("BaseTool", :get_base_tool_names).returns({:base_tool_name => @base_tool.name})
45 get :choose_base_tool, :profile => @profile.identifier, :id => @content.id 45 get :choose_base_tool, :profile => @profile.identifier, :id => @content.id
plugins/mezuro/test/functional/mezuro_plugin_profile_controller_test.rb
@@ -25,13 +25,6 @@ class MezuroPluginProfileControllerTest < ActionController::TestCase @@ -25,13 +25,6 @@ class MezuroPluginProfileControllerTest < ActionController::TestCase
25 @content.save 25 @content.save
26 end 26 end
27 27
28 - should 'show an error page if an exception is raised' #do  
29 -# Kalibro::Project.expects(:request).with("Project", :get_project, :project_name => @project.name).raises(Exception, "Error message")  
30 -# get :project_state, :profile => @profile.identifier, :id => @content.id  
31 -# assert_response 302  
32 -# assert_select('h2', 'An error occured: ')  
33 -# end  
34 -  
35 should 'test project state without kalibro_error' do 28 should 'test project state without kalibro_error' do
36 Kalibro::Project.expects(:request).with("Project", :get_project, :project_name => @project.name).returns({:project => @project.to_hash}) 29 Kalibro::Project.expects(:request).with("Project", :get_project, :project_name => @project.name).returns({:project => @project.to_hash})
37 get :project_state, :profile => @profile.identifier, :id => @content.id 30 get :project_state, :profile => @profile.identifier, :id => @content.id
plugins/mezuro/views/cms/mezuro_plugin/_configuration_content.html.erb
1 <h1> <%= _(MezuroPlugin::ConfigurationContent.short_description) %> </h1> 1 <h1> <%= _(MezuroPlugin::ConfigurationContent.short_description) %> </h1>
2 2
3 <% 3 <%
4 - begin  
5 - kalibro_configuration = @article.title.nil? ? nil : @article.kalibro_configuration  
6 - rescue  
7 - kalibro_configuration = nil  
8 - end 4 + kalibro_configuration = @article.title.nil? ? nil : @article.kalibro_configuration
  5 + kalibro_configuration_names = @article.kalibro_configuration_names
9 %> 6 %>
10 7
11 <%= error_messages_for 'kalibro_configuration' %> 8 <%= error_messages_for 'kalibro_configuration' %>
@@ -13,8 +10,7 @@ @@ -13,8 +10,7 @@
13 <%= hidden_field_tag 'kalibro_configuration[profile_id]', profile.id %> 10 <%= hidden_field_tag 'kalibro_configuration[profile_id]', profile.id %>
14 <%= hidden_field_tag 'id', @article.id %> 11 <%= hidden_field_tag 'id', @article.id %>
15 12
16 -<% kalibro_configuration_names = @article.kalibro_configuration_names %>  
17 - 13 +
18 <% selected = (kalibro_configuration.nil? ? "None" : @article.configuration_to_clone_name) %> 14 <% selected = (kalibro_configuration.nil? ? "None" : @article.configuration_to_clone_name) %>
19 15
20 <%= required_fields_message %> 16 <%= required_fields_message %>
plugins/mezuro/views/cms/mezuro_plugin/_project_content.html.erb
1 <h1> <%= _(MezuroPlugin::ProjectContent.short_description) %> </h1> 1 <h1> <%= _(MezuroPlugin::ProjectContent.short_description) %> </h1>
2 2
3 <% 3 <%
  4 + @project = @article.title.nil? ? nil : @article.project
4 begin 5 begin
5 - @project = @article.title.nil? ? nil : Kalibro::Project.find_by_name(@article.title)  
6 - rescue  
7 - @project = nil 6 + @repository_types = Kalibro::Repository.repository_types.sort
  7 + @configuration_names = Kalibro::Configuration.all_names.sort
  8 + rescue Exception => exception
  9 + @article.errors.add_to_base(exception.message)
  10 + @repository_types = []
  11 + @configuration_names = []
8 end 12 end
9 %> 13 %>
10 14
@@ -24,14 +28,12 @@ @@ -24,14 +28,12 @@
24 28
25 <%= f.text_field :description %><br/> 29 <%= f.text_field :description %><br/>
26 30
27 -<% @repository_types = Kalibro::Repository.repository_types.sort %>  
28 <% @selected = (@project.nil? ? @repository_types : @project.repository.type) %> 31 <% @selected = (@project.nil? ? @repository_types : @project.repository.type) %>
29 <%= required labelled_form_field _('Repository type'), 32 <%= required labelled_form_field _('Repository type'),
30 f.select(:repository_type, @repository_types, {:selected => @selected}) %><br/> 33 f.select(:repository_type, @repository_types, {:selected => @selected}) %><br/>
31 34
32 <%= required f.text_field(:repository_url) %><br/> 35 <%= required f.text_field(:repository_url) %><br/>
33 36
34 -<% @configuration_names = Kalibro::Configuration.all_names.sort %>  
35 <% @selected = (@project.nil? ? @configuration_names[0] : @project.configuration_name) %> 37 <% @selected = (@project.nil? ? @configuration_names[0] : @project.configuration_name) %>
36 38
37 <% if !@project.nil? && !@article.id.nil? %> 39 <% if !@project.nil? && !@article.id.nil? %>
plugins/mezuro/views/mezuro_plugin_profile/error_page.html.erb
1 <h2> An error occured: </h2> 1 <h2> An error occured: </h2>
2 <%= @message %> 2 <%= @message %>
3 -<!-- The <%=h is for escaping the URL properly --!>