comfy_imprint 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/LICENSE +20 -0
  2. data/README.md +59 -0
  3. data/Rakefile +40 -0
  4. data/app/assets/javascripts/comfy_imprint/application.js +15 -0
  5. data/app/assets/javascripts/comfy_imprint/contacts.js +2 -0
  6. data/app/assets/stylesheets/comfy_imprint/application.css +13 -0
  7. data/app/assets/stylesheets/comfy_imprint/contacts.css +4 -0
  8. data/app/assets/stylesheets/scaffold.css +56 -0
  9. data/app/controllers/comfy_imprint/application_controller.rb +2 -0
  10. data/app/controllers/comfy_imprint/contacts_controller.rb +49 -0
  11. data/app/helpers/comfy_imprint/application_helper.rb +4 -0
  12. data/app/helpers/comfy_imprint/contacts_helper.rb +4 -0
  13. data/app/mailers/comfy_imprint/contact_mailer.rb +11 -0
  14. data/app/models/comfy_imprint/contact.rb +44 -0
  15. data/app/validators/clean_text_validator.rb +7 -0
  16. data/app/validators/company_name_validator.rb +7 -0
  17. data/app/validators/contact_method_validator.rb +7 -0
  18. data/app/validators/email_validator.rb +7 -0
  19. data/app/validators/name_validator.rb +7 -0
  20. data/app/validators/phone_validator.rb +7 -0
  21. data/app/validators/url_validator.rb +7 -0
  22. data/app/views/admin/comfy_imprint/contacts/edit.html.haml +3 -0
  23. data/app/views/admin/comfy_imprint/contacts/index.html.haml +15 -0
  24. data/app/views/admin/comfy_imprint/contacts/new.html.haml +3 -0
  25. data/app/views/admin/comfy_imprint/contacts/show.html.haml +6 -0
  26. data/app/views/comfy_imprint/contact_mailer/contact_email.html.haml +7 -0
  27. data/app/views/comfy_imprint/contact_mailer/contact_email.text.plain.haml +4 -0
  28. data/app/views/comfy_imprint/contacts/_form.html.haml +13 -0
  29. data/app/views/comfy_imprint/contacts/_submission.html.haml +8 -0
  30. data/app/views/comfy_imprint/contacts/new.html.haml +3 -0
  31. data/app/views/comfy_imprint/contacts/show.html.haml +4 -0
  32. data/config/initializers/comfy_imprint.rb +26 -0
  33. data/config/initializers/simple_form.rb +199 -0
  34. data/config/locales/comfy_imprint.en.yml +55 -0
  35. data/config/locales/comfy_imprint.ru.yml +55 -0
  36. data/config/routes.rb +4 -0
  37. data/db/migrate/20130711211512_create_comfy_imprint_contacts.rb +16 -0
  38. data/lib/comfy_imprint.rb +13 -0
  39. data/lib/comfy_imprint/engine.rb +26 -0
  40. data/lib/comfy_imprint/version.rb +3 -0
  41. data/lib/tasks/comfy_imprint_tasks.rake +13 -0
  42. data/test/comfy_imprint_test.rb +7 -0
  43. data/test/controllers/comfy_imprint/contacts_controller_test.rb +51 -0
  44. data/test/dummy/README.rdoc +261 -0
  45. data/test/dummy/Rakefile +7 -0
  46. data/test/dummy/app/assets/javascripts/application.js +15 -0
  47. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  48. data/test/dummy/app/controllers/application_controller.rb +3 -0
  49. data/test/dummy/app/helpers/application_helper.rb +2 -0
  50. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  51. data/test/dummy/config.ru +4 -0
  52. data/test/dummy/config/application.rb +59 -0
  53. data/test/dummy/config/boot.rb +10 -0
  54. data/test/dummy/config/database.yml +25 -0
  55. data/test/dummy/config/environment.rb +5 -0
  56. data/test/dummy/config/environments/development.rb +37 -0
  57. data/test/dummy/config/environments/production.rb +67 -0
  58. data/test/dummy/config/environments/test.rb +37 -0
  59. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  60. data/test/dummy/config/initializers/inflections.rb +15 -0
  61. data/test/dummy/config/initializers/mime_types.rb +5 -0
  62. data/test/dummy/config/initializers/secret_token.rb +7 -0
  63. data/test/dummy/config/initializers/session_store.rb +8 -0
  64. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  65. data/test/dummy/config/locales/en.yml +5 -0
  66. data/test/dummy/config/routes.rb +4 -0
  67. data/test/dummy/db/development.sqlite3 +0 -0
  68. data/test/dummy/db/migrate/20130718151203_create_comfy_imprint_contacts.comfy_imprint.rb +17 -0
  69. data/test/dummy/db/schema.rb +30 -0
  70. data/test/dummy/log/development.log +0 -0
  71. data/test/dummy/public/404.html +26 -0
  72. data/test/dummy/public/422.html +26 -0
  73. data/test/dummy/public/500.html +25 -0
  74. data/test/dummy/public/favicon.ico +0 -0
  75. data/test/dummy/script/rails +6 -0
  76. data/test/dummy/tmp/pids/server.pid +1 -0
  77. data/test/fixtures/comfy_imprint/contacts.yml +11 -0
  78. data/test/helpers/comfy_imprint/contacts_helper_test.rb +6 -0
  79. data/test/integration/navigation_test.rb +10 -0
  80. data/test/mailers/comfy_imprint/contact_mailer_test.rb +9 -0
  81. data/test/models/comfy_imprint/contact_test.rb +9 -0
  82. data/test/test_helper.rb +15 -0
  83. metadata +291 -0
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Nick Gorbikoff
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,59 @@
1
+ #ComfyImprint
2
+ This is what is commonly known as a "Contact Us" page, done as a mountable Rails 3 engine. It's designed to be used in conjunction with Comfortable Mexican Sofa Content Management System ( CMS CMS :-) ) I highly suggest to check out CMS as it runs circles around all other Rails CMS systems out there.
3
+
4
+ ##Demo
5
+ I don't like heroku, so there is no obligatory heroku demo, thou I might put up one later at some other provider. However you can see this gem in action on my personal blog at: http://www.gorbikoff.com/contact-us
6
+
7
+ ##Features / Bugs
8
+
9
+ - Uses your DB as a storage (creates comfy_imprint_contacts table) for all contact attempts on your website. Really convenient if you want to find that one person who emailed you from your website about buying it for $1000000 and you accidentally deleted that email. Also can be used to send notifications to your users. (There is an opt-in / opt-out Boolean field to let users sign up for your SPAM.)
10
+ - Several default fields that should be sufficient in 90% of use cases. Remember this should be an **EASY** contact-us form, not a credit application.
11
+ - Built in validations / error checking for all fields ( phone, email, name, body, url, company name, is_subscribed). Has default error messages.
12
+ - Default Placeholders (May the force be with you!).
13
+ - Easily customizable to work with your CSS framework. Used in production on sites with both Bootstrap and Zurb Foundation.
14
+ - I18n ready. Right now has English & Russian locales. More translations are welcome.
15
+ - Fields are configurable to be displayed / not displayed and required / not required.
16
+ - Uses your Rails apps mail configuration. But you can configure who should receive notifications from contact form.
17
+ - Right now it's not coupled to tightly with CMS so you might be able to use it outside of it as well. At least I don't see any reason why it wouldn't work.
18
+ - Uses HAML & SimpleForm, but it can be easily overridden in your views if you want to.
19
+ - I use it with Rails 3 and CMS 1.8 , however it should be usable with Rails 4 and CMS 1.9 . I will shortly start a new project that should confirm this.
20
+ - Uses new hash syntax, so it's Ruby > 1.9 only.
21
+ - Modify order in which fields are rendered. No need for overriding the view.
22
+ - Uses minimal styling inside views, so entirely possible to style your contact form without touching gem at all with just a few rules in your application.css . If you use simple_form elsewhere it will just use your simple_form styling you already setup in your app.
23
+
24
+ ## Upcoming Features
25
+ #### Pull Requests are welcome!
26
+
27
+ - Tests, Tests, Tests!
28
+ - Admin interface tie-in. Right now you need to use either a GUI tool for your DB or do some kind of export on ComfyImprint::Contact.all from console or rake task if you want to look at your contact attempts outside of your email.
29
+ - Often Contact Us page has address / phone information & google / yahoo / bing map. So adding those would be nice. A proper Imprint / Impressum / Masthead page like in German-speaking countries. Right now this can be done manually by overriding view but in the future I would like to add this functionality so my users can update their own phone numbers and such.
30
+ - Integration with delayed_job for sending emails. Right now I do this manually by overriding controller.
31
+ - Adding some kind of Captcha mechanism.
32
+
33
+ ##Usage
34
+ ```ruby
35
+ # Gemfile
36
+ gem 'comfy_imprint'
37
+ bundle install
38
+
39
+ # Migrations
40
+ comfy_imprint:install:migrations
41
+ rake db:migrate
42
+
43
+ # Routes
44
+ mount ComfyImprint::Engine, at: "/contact-us"
45
+
46
+ # Config
47
+ # copy & edit initializer
48
+ rake comfy_imprint:install:copy_initializer
49
+
50
+ ```
51
+
52
+ ## Name
53
+ Why didn't I name this ComfyContact or something similar? Because dealing with ComfyContact::Contact.contact is confusing and because I already have unrelated resource named Contact. While this gem is properly namespaced, it would be confusing for anyone else not familiar with your app. I'm guessing I'm not the only genius to have unrelated model called Contact in my app.
54
+
55
+ ## Acknowledgement and thank yous
56
+ This gem was made possible due to generosity of folks at http://www.birica.com
57
+
58
+
59
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'ComfyImprint'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ require 'rake/testtask'
31
+
32
+ Rake::TestTask.new(:test) do |t|
33
+ t.libs << 'lib'
34
+ t.libs << 'test'
35
+ t.pattern = 'test/**/*_test.rb'
36
+ t.verbose = false
37
+ end
38
+
39
+
40
+ task :default => :test
@@ -0,0 +1,15 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require_tree .
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,56 @@
1
+ body { background-color: #fff; color: #333; }
2
+
3
+ body, p, ol, ul, td {
4
+ font-family: verdana, arial, helvetica, sans-serif;
5
+ font-size: 13px;
6
+ line-height: 18px;
7
+ }
8
+
9
+ pre {
10
+ background-color: #eee;
11
+ padding: 10px;
12
+ font-size: 11px;
13
+ }
14
+
15
+ a { color: #000; }
16
+ a:visited { color: #666; }
17
+ a:hover { color: #fff; background-color:#000; }
18
+
19
+ div.field, div.actions {
20
+ margin-bottom: 10px;
21
+ }
22
+
23
+ #notice {
24
+ color: green;
25
+ }
26
+
27
+ .field_with_errors {
28
+ padding: 2px;
29
+ background-color: red;
30
+ display: table;
31
+ }
32
+
33
+ #error_explanation {
34
+ width: 450px;
35
+ border: 2px solid red;
36
+ padding: 7px;
37
+ padding-bottom: 0;
38
+ margin-bottom: 20px;
39
+ background-color: #f0f0f0;
40
+ }
41
+
42
+ #error_explanation h2 {
43
+ text-align: left;
44
+ font-weight: bold;
45
+ padding: 5px 5px 5px 15px;
46
+ font-size: 12px;
47
+ margin: -7px;
48
+ margin-bottom: 0px;
49
+ background-color: #c00;
50
+ color: #fff;
51
+ }
52
+
53
+ #error_explanation ul li {
54
+ font-size: 12px;
55
+ list-style: square;
56
+ }
@@ -0,0 +1,2 @@
1
+ class ComfyImprint::ApplicationController < ApplicationController
2
+ end
@@ -0,0 +1,49 @@
1
+ require_dependency "comfy_imprint/application_controller"
2
+
3
+ module ComfyImprint
4
+ class ContactsController < ApplicationController
5
+ before_filter :set_contact, only: [:show, :destroy]
6
+
7
+ def index
8
+ redirect_to root_path
9
+ end
10
+ # GET /contacts/1
11
+ def show
12
+ end
13
+
14
+ # GET /contacts/new
15
+ def new
16
+ @contact = Contact.new
17
+ end
18
+
19
+ # POST /contacts
20
+ def create
21
+ @contact = Contact.new(contact_params)
22
+ if @contact.save
23
+ # Contact Attempt passed validation so it's safe to mail it.
24
+ # In the future I may want to impleme nt some kind of delayed_job or redis hook here
25
+ ContactMailer.contact_email(@contact).deliver
26
+ redirect_to @contact#, notice: 'Contact was successfully created.'
27
+ else
28
+ render action: :new
29
+ end
30
+ end
31
+
32
+ # DELETE /contacts/1
33
+ def destroy
34
+ @contact.destroy
35
+ redirect_to contacts_url#, notice: 'Contact was successfully destroyed.'
36
+ end
37
+
38
+ private
39
+ # Use callbacks to share common setup or constraints between actions.
40
+ def set_contact
41
+ @contact = Contact.find(params[:id])
42
+ end
43
+
44
+ # Only allow a trusted parameter "white list" through.
45
+ def contact_params
46
+ params[:contact]
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,4 @@
1
+ module ComfyImprint
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module ComfyImprint
2
+ module ContactsHelper
3
+ end
4
+ end
@@ -0,0 +1,11 @@
1
+ module ComfyImprint
2
+ class ContactMailer < ActionMailer::Base
3
+ default from: ComfyImprint.configuration.mail_from,
4
+ to: ComfyImprint.configuration.mail_to,
5
+ subject: ComfyImprint.configuration.mail_subject
6
+ def contact_email(contact)
7
+ @contact = contact
8
+ mail
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,44 @@
1
+ module ComfyImprint
2
+ class Contact < ActiveRecord::Base
3
+ attr_accessible :name, :email, :phone, :url, :company, :is_subscribed, :message
4
+
5
+ # You need to explictely set allow_blank: true on these validations, otherwise you'll run into a problem of blank values being invalid or too_short
6
+ # See comfy_imprint/app/validators for custom validators used here
7
+
8
+
9
+ validates :name,
10
+ length: {minimum: 2, maximum: 100},
11
+ presence: {if: -> {ComfyImprint.configuration.attributes_to_require.include?(:name)}},
12
+ allow_blank: {unless: -> {ComfyImprint.configuration.attributes_to_require.include?(:name)}},
13
+ name: true
14
+ validates :email,
15
+ length: {minimum: 5, maximum: 100},
16
+ presence: {if: -> {ComfyImprint.configuration.attributes_to_require.include?(:email)}},
17
+ allow_blank: {unless: -> {ComfyImprint.configuration.attributes_to_require.include?(:email)}},
18
+ email: true
19
+ validates :phone,
20
+ length: {minimum: 10, maximum: 20},
21
+ presence: {if: -> {ComfyImprint.configuration.attributes_to_require.include?(:phone)}},
22
+ allow_blank: {unless: -> {ComfyImprint.configuration.attributes_to_require.include?(:phone)}},
23
+ phone: true
24
+ validates :company,
25
+ length: {minimum: 2, maximum: 200},
26
+ presence: {if: -> {ComfyImprint.configuration.attributes_to_require.include?(:company)}},
27
+ allow_blank: {unless: -> {ComfyImprint.configuration.attributes_to_require.include?(:company)}},
28
+ company_name: true
29
+ validates :url,
30
+ length: {minimum: 5, maximum: 200},
31
+ presence: {if: -> {ComfyImprint.configuration.attributes_to_require.include?(:url)}},
32
+ allow_blank: {unless: -> {ComfyImprint.configuration.attributes_to_require.include?(:url)}},
33
+ url: true
34
+ validates :is_subscribed,
35
+ inclusion: {in: [true, false]},
36
+ presence: {if: -> {ComfyImprint.configuration.attributes_to_require.include?(:is_subscribed)}},
37
+ allow_blank: {unless: -> {ComfyImprint.configuration.attributes_to_require.include?(:is_subscribed)}}
38
+ validates :message,
39
+ length: {minimum: 5, maximum: 1000},
40
+ presence: {if: -> {ComfyImprint.configuration.attributes_to_require.include?(:message)}},
41
+ allow_blank: {unless: -> {ComfyImprint.configuration.attributes_to_require.include?(:message)}},
42
+ clean_text: true
43
+ end
44
+ end
@@ -0,0 +1,7 @@
1
+ class CleanTextValidator < ActiveModel::EachValidator
2
+ def validate_each(record, attribute, value)
3
+ unless value =~ /^[a-zA-Z0-9\\(\)\-.\!\?\'\,\s]*$/i
4
+ record.errors.add attribute, (options[:message] || :invalid_clean_text)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class CompanyNameValidator < ActiveModel::EachValidator
2
+ def validate_each(record, attribute, value)
3
+ unless value =~ /^[a-zA-Z0-9_\,\.\ \-]*$/i
4
+ record.errors.add attribute, (options[:message] || :invalid_company_name)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class ContactMethodValidator < ActiveModel::Validator
2
+ def validate(record)
3
+ unless (!record.addresses.empty? or !record.phones.empty? or !record.emails.empty? or !record.urls.empty?)
4
+ record.errors[:base] << "This contact doesn't have any methods of contacting them: like email or phone"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class EmailValidator < ActiveModel::EachValidator
2
+ def validate_each(record, attribute, value)
3
+ unless value =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
4
+ record.errors.add attribute, (options[:message] || :invalid_email)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class NameValidator < ActiveModel::EachValidator
2
+ def validate_each(record, attribute, value)
3
+ unless value =~ /^[a-zA-Z0-9_\'\ \-]*$/i
4
+ record.errors.add attribute, (options[:message] || :invalid_name)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class PhoneValidator < ActiveModel::EachValidator
2
+ def validate_each(record, attribute, value)
3
+ unless value =~ /^[0-9x\(\)\ \.\-]*$/i
4
+ record.errors.add attribute, (options[:message] || :invalid_phone)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class UrlValidator < ActiveModel::EachValidator
2
+ def validate_each(record, attribute, value)
3
+ unless value =~ /[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$/ix
4
+ record.errors.add attribute, (options[:message] || :invalid_url)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ %h2
2
+ = t('comfy_imprint.contact_us')
3
+ = render 'form'
@@ -0,0 +1,15 @@
1
+ %h2 Listing contacts
2
+ %table
3
+ %thead
4
+ %tr
5
+ %th
6
+ %th
7
+ %th
8
+ %tbody
9
+ - @contacts.each do |contact|
10
+ %tr
11
+ %td= link_to 'Show', contact
12
+ %td= link_to 'Edit', edit_contact_path(contact)
13
+ %td= link_to 'Destroy', contact, method: :delete, data: { confirm: 'Are you sure?' }
14
+ %br/
15
+ = link_to 'New Contact', new_contact_path
@@ -0,0 +1,3 @@
1
+ %h2
2
+ = t('comfy_imprint.contact_us')
3
+ = render 'form'
@@ -0,0 +1,6 @@
1
+ %h2
2
+ = t('comfy_imprint.contact_us')
3
+
4
+ = link_to 'Edit', edit_contact_path(@contact)
5
+ |
6
+ \#{link_to 'Back', contacts_path}