katapult 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.ruby-version +1 -1
  4. data/README.md +61 -35
  5. data/bin/katapult +24 -34
  6. data/features/authenticate.feature +344 -0
  7. data/features/basics.feature +590 -0
  8. data/features/binary.feature +33 -1
  9. data/features/model.feature +9 -13
  10. data/features/step_definitions/rails_steps.rb +3 -2
  11. data/features/wui.feature +49 -4
  12. data/lib/generators/katapult/basics/basics_generator.rb +63 -17
  13. data/lib/generators/katapult/basics/templates/.gitignore +1 -0
  14. data/lib/generators/katapult/basics/templates/.ruby-version +1 -1
  15. data/lib/generators/katapult/basics/templates/Capfile +25 -0
  16. data/lib/generators/katapult/basics/templates/Gemfile +14 -6
  17. data/lib/generators/katapult/basics/templates/Guardfile +44 -0
  18. data/lib/generators/katapult/basics/templates/config/database.sample.yml +3 -2
  19. data/lib/generators/katapult/basics/templates/config/database.yml +3 -2
  20. data/lib/generators/katapult/basics/templates/config/deploy/production.rb +8 -0
  21. data/lib/generators/katapult/basics/templates/config/deploy/staging.rb +7 -0
  22. data/lib/generators/katapult/basics/templates/config/deploy.rb +37 -0
  23. data/lib/generators/katapult/basics/templates/config/initializers/ext.rb +3 -0
  24. data/lib/generators/katapult/basics/templates/features/support/factory_girl.rb +1 -0
  25. data/lib/generators/katapult/basics/templates/features/support/paths.rb +6 -0
  26. data/lib/generators/katapult/basics/templates/lib/capistrano/tasks/db.rake +28 -0
  27. data/lib/generators/katapult/basics/templates/lib/capistrano/tasks/deploy.rake +15 -0
  28. data/lib/generators/katapult/basics/templates/lib/capistrano/tasks/passenger.rake +8 -0
  29. data/lib/generators/katapult/basics/templates/{config/initializers → lib/ext/action_view}/form_for_with_development_errors.rb +0 -2
  30. data/lib/generators/katapult/basics/templates/lib/ext/action_view/spec_label.rb +46 -0
  31. data/lib/generators/katapult/basics/templates/{config/initializers → lib/ext/active_record}/find_by_anything.rb +0 -0
  32. data/lib/generators/katapult/basics/templates/lib/ext/active_record/these.rb +7 -0
  33. data/lib/generators/katapult/basics/templates/lib/ext/array/xss_aware_join.rb +10 -0
  34. data/lib/generators/katapult/basics/templates/lib/ext/enumerable/natural_sort.rb +15 -0
  35. data/lib/generators/katapult/basics/templates/lib/ext/hash/infinite.rb +7 -0
  36. data/lib/generators/katapult/basics/templates/lib/ext/string/html_entities.rb +11 -0
  37. data/lib/generators/katapult/basics/templates/lib/ext/string/to_sort_atoms.rb +52 -0
  38. data/lib/generators/katapult/basics/templates/lib/tasks/pending_migrations.rake +24 -0
  39. data/lib/generators/katapult/basics/templates/spec/factories/factories.rb +9 -0
  40. data/lib/generators/katapult/basics/templates/spec/support/factory_girl.rb +3 -0
  41. data/lib/generators/katapult/clearance/clearance_generator.rb +125 -0
  42. data/lib/generators/katapult/clearance/templates/app/controllers/passwords_controller.rb +16 -0
  43. data/lib/generators/katapult/clearance/templates/app/views/clearance_mailer/change_password.html.haml +6 -0
  44. data/lib/generators/katapult/clearance/templates/app/views/clearance_mailer/change_password.text.erb +3 -0
  45. data/lib/generators/katapult/clearance/templates/app/views/passwords/create.html.haml +5 -0
  46. data/lib/generators/katapult/clearance/templates/app/views/passwords/edit.html.haml +16 -0
  47. data/lib/generators/katapult/clearance/templates/app/views/passwords/new.html.haml +14 -0
  48. data/lib/generators/katapult/clearance/templates/app/views/sessions/new.html.haml +19 -0
  49. data/lib/generators/katapult/clearance/templates/config/initializers/clearance.rb +15 -0
  50. data/lib/generators/katapult/clearance/templates/config/locales/clearance.en.yml +59 -0
  51. data/lib/generators/katapult/clearance/templates/features/authentication.feature +94 -0
  52. data/lib/generators/katapult/clearance/templates/features/step_definitions/authentication_steps.rb +4 -0
  53. data/lib/generators/katapult/cucumber_features/templates/feature.feature +11 -7
  54. data/lib/generators/katapult/haml/haml_generator.rb +5 -0
  55. data/lib/generators/katapult/haml/templates/_form.html.haml +4 -4
  56. data/lib/generators/katapult/haml/templates/app/views/layouts/_flashes.html.haml +3 -0
  57. data/lib/generators/katapult/haml/templates/app/views/layouts/application.html.haml +9 -3
  58. data/lib/generators/katapult/haml/templates/index.html.haml +1 -1
  59. data/lib/generators/katapult/haml/templates/show.html.haml +2 -4
  60. data/lib/generators/katapult/install/templates/lib/katapult/application_model.rb +9 -7
  61. data/lib/generators/katapult/model/model_generator.rb +1 -1
  62. data/lib/generators/katapult/w_u_i/templates/controller.rb +1 -1
  63. data/lib/generators/katapult/w_u_i/w_u_i_generator.rb +4 -2
  64. data/lib/katapult/application_model.rb +8 -1
  65. data/lib/katapult/attribute.rb +10 -11
  66. data/lib/katapult/authentication.rb +25 -0
  67. data/lib/katapult/binary_util.rb +37 -0
  68. data/lib/katapult/element.rb +1 -1
  69. data/lib/katapult/generator.rb +6 -0
  70. data/lib/katapult/model.rb +13 -1
  71. data/lib/katapult/parser.rb +7 -0
  72. data/lib/katapult/version.rb +1 -1
  73. data/lib/katapult/wui.rb +4 -0
  74. data/lib/katapult.rb +2 -0
  75. data/spec/attribute_spec.rb +13 -0
  76. data/spec/element_spec.rb +5 -0
  77. data/spec/model_spec.rb +5 -4
  78. data/spec/util_spec.rb +8 -8
  79. data/spec/wui_spec.rb +19 -0
  80. metadata +44 -8
  81. data/features/katapult.feature +0 -271
  82. data/lib/katapult/util.rb +0 -16
@@ -0,0 +1,10 @@
1
+ Array.class_eval do
2
+ def xss_aware_join(delimiter = '')
3
+ ''.html_safe.tap do |str|
4
+ each_with_index do |element, i|
5
+ str << delimiter if i > 0
6
+ str << element
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,15 @@
1
+ module Enumerable
2
+
3
+ def natural_sort
4
+ natural_sort_by
5
+ end
6
+
7
+ def natural_sort_by(&stringifier)
8
+ sort_by do |element|
9
+ element = stringifier.call(element) if stringifier
10
+ element = element.to_s unless element.respond_to?(:to_sort_atoms)
11
+ element.to_sort_atoms
12
+ end
13
+ end
14
+
15
+ end
@@ -0,0 +1,7 @@
1
+ class Hash
2
+
3
+ def self.infinite
4
+ new { |h, k| h[k] = new(&h.default_proc) }
5
+ end
6
+
7
+ end
@@ -0,0 +1,11 @@
1
+ class String
2
+
3
+ def self.nbsp
4
+ ' '
5
+ end
6
+
7
+ def self.ndash
8
+ '–'
9
+ end
10
+
11
+ end
@@ -0,0 +1,52 @@
1
+ class SmartSortAtom
2
+
3
+ attr_reader :value
4
+
5
+ def initialize(value)
6
+ @value = value
7
+ end
8
+
9
+ def <=>(other)
10
+ other.is_a?(self.class) or raise "Can only smart compare with other SmartSortAtom"
11
+ left_value = value
12
+ right_value = other.value
13
+ if left_value.class == right_value.class
14
+ left_value <=> right_value
15
+ elsif left_value.is_a?(Float)
16
+ -1
17
+ else
18
+ 1
19
+ end
20
+ end
21
+
22
+ def self.parse(string)
23
+ # Loosely based on http://stackoverflow.com/a/4079031
24
+ string.scan(/[^\d\.]+|[\d\.]+/).collect do |atom|
25
+ if atom.match(/\d+(\.\d+)?/)
26
+ atom = atom.to_f
27
+ else
28
+ atom = normalize_string(atom)
29
+ end
30
+ new(atom)
31
+ end
32
+
33
+ end
34
+
35
+ private
36
+
37
+ def self.normalize_string(string)
38
+ string = ActiveSupport::Inflector.transliterate(string)
39
+ string = string.downcase
40
+ string
41
+ end
42
+
43
+ end
44
+
45
+ String.class_eval do
46
+
47
+ def to_sort_atoms
48
+ SmartSortAtom.parse(self)
49
+ end
50
+
51
+ end
52
+
@@ -0,0 +1,24 @@
1
+ namespace :db do
2
+
3
+ desc 'Warns if there are pending migrations'
4
+ task :warn_if_pending_migrations => :environment do
5
+ if defined? ActiveRecord
6
+ all_migrations = ActiveRecord::Migrator.migrations('db/migrate')
7
+ pending_migrations = ActiveRecord::Migrator.new(:up, all_migrations).pending_migrations
8
+
9
+ if pending_migrations.any?
10
+ puts ''
11
+ puts '======================================================='
12
+ puts "You have #{ pending_migrations.size } pending migrations:"
13
+ pending_migrations.each do |pending_migration|
14
+ puts ' %4d %s' % [pending_migration.version, pending_migration.name]
15
+ end
16
+ puts 'Run cap <stage> deploy:migrations'
17
+ puts '======================================================='
18
+ puts ''
19
+ end
20
+
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,9 @@
1
+ FactoryGirl.define do
2
+
3
+ factory :EXAMPLE do
4
+ status 'pending'
5
+ uuid { SecureRandom.uuid }
6
+ sequence(:title) { |i| "Titel #{ i }"}
7
+ end
8
+
9
+ end
@@ -0,0 +1,3 @@
1
+ RSpec.configure do |config|
2
+ config.include FactoryGirl::Syntax::Methods
3
+ end
@@ -0,0 +1,125 @@
1
+ # Generate authentication with Clearance
2
+
3
+ require 'katapult/generator'
4
+
5
+ module Katapult
6
+ module Generators
7
+ class ClearanceGenerator < Katapult::Generator
8
+
9
+ desc 'Generate authentication with Clearance'
10
+
11
+ check_class_collision
12
+ source_root File.expand_path('../templates', __FILE__)
13
+
14
+
15
+ def migrate
16
+ rake 'db:migrate' # Clearance must see the users table in the db
17
+ end
18
+
19
+ def install_clearance
20
+ insert_into_file 'Gemfile', <<-GEM, before: "gem 'katapult'"
21
+ gem 'clearance', '< 1.14.0' # Has broken InstallGenerator :(
22
+
23
+ GEM
24
+ run 'bundle install --quiet'
25
+ generate 'clearance:install'
26
+ end
27
+
28
+ def require_login
29
+ file = 'app/controllers/application_controller.rb'
30
+ insert_into_file file, <<-CONTENT, after: "Clearance::Controller\n"
31
+
32
+ before_action :require_login
33
+ CONTENT
34
+ end
35
+
36
+ def overwrite_clearance_controllers
37
+ template 'app/controllers/passwords_controller.rb'
38
+ end
39
+
40
+ def create_clearance_views
41
+ directory 'app/views/clearance_mailer'
42
+ directory 'app/views/passwords'
43
+ directory 'app/views/sessions'
44
+ end
45
+
46
+ def install_backdoor
47
+ # This creepy indentation leads to correct formatting in the file
48
+ application <<-CONTENT, env: 'test'
49
+ # Enable quick-signin in tests: `visit homepage(as: User.last!)`
50
+ config.middleware.use Clearance::BackDoor
51
+ CONTENT
52
+ end
53
+
54
+ def create_initializer
55
+ template 'config/initializers/clearance.rb', force: true
56
+ end
57
+
58
+ def create_translations
59
+ template 'config/locales/clearance.en.yml'
60
+ end
61
+
62
+ def create_routes
63
+ route <<-ROUTES
64
+ resources :users do
65
+ resource :password, controller: 'passwords',
66
+ only: %i[edit update]
67
+ end
68
+
69
+ # Clearance
70
+ get '/login', to: 'clearance/sessions#new', as: 'sign_in'
71
+ resource :session, controller: 'clearance/sessions', only: [:create]
72
+ resources :passwords, controller: 'passwords', only: [:create, :new]
73
+ delete '/logout', to: 'clearance/sessions#destroy', as: 'sign_out'
74
+ ROUTES
75
+ end
76
+
77
+ def add_sign_in_background_to_all_features
78
+ Dir['features/*.feature'].each do |file|
79
+ inject_into_file file, <<-CONTENT, after: /^Feature: .*$/
80
+
81
+
82
+ Background:
83
+ Given there is a user
84
+ And I sign in as the user above
85
+ CONTENT
86
+ end
87
+ end
88
+
89
+ def create_authentication_feature
90
+ template 'features/authentication.feature'
91
+ end
92
+
93
+ def create_authentication_steps
94
+ template 'features/step_definitions/authentication_steps.rb'
95
+ end
96
+
97
+ def add_authentication_paths
98
+ inject_into_file 'features/support/paths.rb', <<-CONTENT, after: 'case page_name'
99
+
100
+
101
+ # Authentication
102
+ when 'the sign-in form'
103
+ sign_in_path
104
+ when 'the reset password page'
105
+ new_password_path
106
+ when 'the new password page for the user above'
107
+ edit_user_password_path(User.last!)
108
+
109
+ CONTENT
110
+ end
111
+
112
+ def add_user_factory
113
+ inject_into_file 'spec/factories/factories.rb', <<-'CONTENT', after: 'FactoryGirl.define do'
114
+
115
+ factory :user do
116
+ sequence(:email) { |i| "user-#{ i }@example.com" }
117
+ password 'password'
118
+ end
119
+
120
+ CONTENT
121
+ end
122
+
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,16 @@
1
+ class PasswordsController < Clearance::PasswordsController
2
+
3
+ def update
4
+ @user = find_user_for_update
5
+
6
+ if @user.update_password password_reset_params
7
+ sign_in @user
8
+ flash[:notice] = 'Password successfully changed' # <<- added
9
+ redirect_to url_after_update
10
+ else
11
+ flash_failure_after_update
12
+ render template: 'passwords/edit'
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,6 @@
1
+ %p
2
+ To reset your password, please follow this link:
3
+
4
+ %p
5
+ = link_to 'Change password',
6
+ edit_user_password_url(@user, token: @user.confirmation_token.html_safe)
@@ -0,0 +1,3 @@
1
+ To reset your password, please follow this link:
2
+
3
+ <%= edit_user_password_url(@user, token: @user.confirmation_token.html_safe) %>
@@ -0,0 +1,5 @@
1
+ %h1
2
+ Password Reset Instructions Sent
3
+
4
+ %p
5
+ We've sent you an email with instructions on how to reset your password.
@@ -0,0 +1,16 @@
1
+ %h1
2
+ Reset Password
3
+
4
+ %p
5
+ Choose your new password:
6
+
7
+ = form_for :password_reset,
8
+ url: user_password_path(@user, token: @user.confirmation_token),
9
+ html: { method: :put } do |form|
10
+
11
+ .form-group
12
+ = form.label :password
13
+ = form.password_field :password, class: 'form-control',
14
+ placeholder: 'New Password'
15
+
16
+ = form.submit 'Update Password', class: 'btn btn-primary'
@@ -0,0 +1,14 @@
1
+ %h1
2
+ Password Reset
3
+
4
+ %p
5
+ Please enter your email address. We will send you instructions on how
6
+ to reset your password.
7
+
8
+ = form_for :password, url: passwords_path do |form|
9
+ .form-group
10
+ = form.label :email
11
+ = form.email_field :email, class: 'form-control',
12
+ placeholder: 'Email Address'
13
+
14
+ = form.submit 'Request Instructions', class: 'btn btn-primary'
@@ -0,0 +1,19 @@
1
+ %h1
2
+ Please sign in
3
+
4
+ = form_for :session, url: session_path do |form|
5
+ .form-group
6
+ = form.label :email
7
+ = form.email_field :email, class: 'form-control',
8
+ placeholder: 'Email Address', required: true, autofocus: true
9
+
10
+ .form-group
11
+ = form.label :password
12
+ = form.password_field :password, class: 'form-control', required: true,
13
+ placeholder: 'Password'
14
+
15
+ %p
16
+ = form.submit 'Sign in', class: 'btn btn-primary'
17
+
18
+ %p
19
+ = link_to 'Forgot password', new_password_path, class: 'text-muted'
@@ -0,0 +1,15 @@
1
+ Clearance.configure do |config|
2
+ config.allow_sign_up = false
3
+ # config.cookie_domain = '.example.com'
4
+ # config.cookie_expiration = lambda { |cookies| 1.year.from_now.utc }
5
+ # config.cookie_name = 'remember_token'
6
+ # config.cookie_path = '/'
7
+ config.routes = false
8
+ # config.httponly = true
9
+ config.mailer_sender = 'system@example.com'
10
+ # config.password_strategy = Clearance::PasswordStrategies::BCrypt
11
+ # config.redirect_url = '/'
12
+ # config.secure_cookie = true
13
+ # config.sign_in_guards = []
14
+ # config.user_model = User
15
+ end
@@ -0,0 +1,59 @@
1
+ ---
2
+ en:
3
+ clearance:
4
+ models:
5
+ clearance_mailer:
6
+ change_password: Change your password
7
+ clearance_mailer:
8
+ change_password:
9
+ closing: If you didn't request this, ignore this email. Your password has
10
+ not been changed.
11
+ link_text: Change my password
12
+ opening: "Someone, hopefully you, requested we send you a link to change
13
+ your password:"
14
+ flashes:
15
+ failure_after_create: Bad email or password.
16
+ failure_after_update: Password can't be blank.
17
+ failure_when_forbidden: Please double check the URL or try submitting
18
+ the form again.
19
+ failure_when_not_signed_in: Please sign in to continue.
20
+ helpers:
21
+ label:
22
+ password:
23
+ email: Email address
24
+ password_reset:
25
+ password: Choose password
26
+ submit:
27
+ password:
28
+ submit: Reset password
29
+ password_reset:
30
+ submit: Save this password
31
+ session:
32
+ submit: Sign in
33
+ user:
34
+ create: Sign up
35
+ layouts:
36
+ application:
37
+ sign_in: Sign in
38
+ sign_out: Sign out
39
+ passwords:
40
+ create:
41
+ description: You will receive an email within the next few minutes. It
42
+ contains instructions for changing your password.
43
+ edit:
44
+ description: Your password has been reset. Choose a new password below.
45
+ title: Change your password
46
+ new:
47
+ description: To be emailed a link to reset your password, please enter
48
+ your email address.
49
+ title: Reset your password
50
+ sessions:
51
+ form:
52
+ forgot_password: Forgot password?
53
+ sign_up: Sign up
54
+ new:
55
+ title: Sign in
56
+ users:
57
+ new:
58
+ sign_in: Sign in
59
+ title: Sign up
@@ -0,0 +1,94 @@
1
+ Feature: Everything about user authentication
2
+
3
+ Scenario: Login is required to visit the homepage
4
+ When I go to the homepage
5
+ Then I should see "Please sign in to continue" within the flash
6
+ And I should be on the sign-in form
7
+
8
+
9
+ Scenario: Login
10
+ Given there is a user with the email "henry@example.com" and the password "password"
11
+
12
+ When I go to the homepage
13
+ Then I should be on the sign-in form
14
+ And I should see "Please sign in"
15
+
16
+ # Wrong email
17
+ When I fill in "Email" with "nonsense"
18
+ And I fill in "Password" with "password"
19
+ And I press "Sign in"
20
+ Then I should see "Bad email or password" within the flash
21
+ And I should see "Please sign in"
22
+
23
+ # Wrong password
24
+ When I fill in "Email" with "admin@example.com"
25
+ And I fill in "Password" with "wrong"
26
+ And I press "Sign in"
27
+ Then I should see "Bad email or password" within the flash
28
+ And I should see "Please sign in"
29
+
30
+ # Correct credentials
31
+ When I fill in "Email" with "henry@example.com"
32
+ And I fill in "Password" with "password"
33
+ And I press "Sign in"
34
+ Then I should be on the homepage
35
+
36
+
37
+ Scenario: Logout
38
+ Given there is a user
39
+ And I am signed in as the user above
40
+
41
+ When I follow "Sign out"
42
+ Then I should be on the sign-in form
43
+
44
+ # Logged out
45
+ When I go to the homepage
46
+ Then I should be on the sign-in form
47
+
48
+
49
+ Scenario: Reset password as a signed-in user
50
+ Given there is a user with the email "henry@example.com"
51
+ And I sign in as the user above
52
+
53
+ When I go to the homepage
54
+ And I follow "henry@example.com" within the current user
55
+ Then I should be on the form for the user above
56
+
57
+ When I fill in "Password" with "new-password"
58
+ And I press "Save"
59
+ Then I should be on the page for the user above
60
+
61
+ When I follow "Sign out"
62
+ And I fill in "Email" with "henry@example.com"
63
+ And I fill in "Password" with "new-password"
64
+ And I press "Sign in"
65
+ Then I should be on the homepage
66
+
67
+
68
+ Scenario: Reset password as a signed-out user
69
+ Given there is a user with the email "henry@example.com"
70
+
71
+ When I go to the sign-in form
72
+ And I follow "Forgot password"
73
+ Then I should be on the reset password page
74
+ And I should see "Password Reset"
75
+
76
+ When I fill in "Email" with "henry@example.com"
77
+ And I press "Request Instructions"
78
+ Then an email should have been sent with:
79
+ """
80
+ From: system@example.com
81
+ To: henry@example.com
82
+ Subject: Change your password
83
+
84
+ To reset your password, please follow this link:
85
+ """
86
+
87
+ When I follow the first link in the email
88
+ Then I should be on the new password page for the user above
89
+ And I should see "Reset Password"
90
+
91
+ When I fill in "Choose password" with "new-password"
92
+ And I press "Update Password"
93
+ Then I should see "Password successfully changed" within the flash
94
+ And I should be on the homepage
@@ -0,0 +1,4 @@
1
+ When /^I (?:am signed|sign) in as the user above$/ do
2
+ user = User.last!
3
+ visit root_path(as: user) # Using Clearance::BackDoor
4
+ end
@@ -4,16 +4,18 @@ Feature: <%= model.name(:humans).titleize %>
4
4
  Given I am on the list of <%= model.name(:variables) %>
5
5
 
6
6
  # create
7
- When I follow "Add <%= model.name(:variable) %>"
7
+ When I follow "Add <%= model.name(:human) %>"
8
8
  <% model.attrs.each do |attr| -%>
9
9
  <%- if attr.assignable_values -%>
10
10
  And I select "<%= attr.test_value %>" from "<%= attr.name.humanize %>"
11
11
  <%- else -%>
12
12
  <%- case attr.type -%>
13
- <%- when :string, :email, :url, :integer, :money, :text, :markdown, :datetime -%>
13
+ <%- when :string, :email, :url, :integer, :money, :text, :password -%>
14
14
  And I fill in "<%= attr.name.humanize %>" with "<%= attr.test_value %>"
15
15
  <%- when :flag -%>
16
16
  And I check "<%= attr.name.humanize %>"
17
+ <%- when :datetime -%>
18
+ And I fill in "<%= attr.name.humanize %>" with "<%= attr.test_value.to_date %>"
17
19
  <%- end -%>
18
20
  <%- end -%>
19
21
  <% end -%>
@@ -23,12 +25,12 @@ Feature: <%= model.name(:humans).titleize %>
23
25
  Then I should be on the page for the <%= model.name(:variable) %> above
24
26
  <% model.attrs.each do |attr| -%>
25
27
  <%- case attr.type -%>
26
- <%- when :string, :email, :url, :integer, :money, :text, :markdown -%>
28
+ <%- when :string, :email, :url, :integer, :money, :text -%>
27
29
  And I should see "<%= attr.test_value %>"
28
30
  <%- when :flag -%>
29
31
  And I should see "<%= attr.name.humanize %> Yes"
30
32
  <%- when :datetime -%>
31
- And I should see "<%= I18n.localize(attr.test_value) %>"
33
+ And I should see "<%= I18n.localize(attr.test_value.to_date) %>"
32
34
  <%- end -%>
33
35
  <% end -%>
34
36
 
@@ -40,20 +42,22 @@ Feature: <%= model.name(:humans).titleize %>
40
42
  And "<%= attr.test_value %>" should be selected for "<%= attr.name.humanize %>"
41
43
  <%- else -%>
42
44
  <%- case attr.type -%>
43
- <%- when :string, :email, :url, :integer, :money, :text, :markdown, :datetime -%>
45
+ <%- when :string, :email, :url, :integer, :money, :text -%>
44
46
  And the "<%= attr.name.humanize %>" field should contain "<%= attr.test_value %>"
45
47
  <%- when :flag -%>
46
48
  And the "<%= attr.name.humanize %>" checkbox should be checked
49
+ <%- when :datetime -%>
50
+ And the "<%= attr.name.humanize %>" field should contain "<%= attr.test_value.to_date %>"
47
51
  <%- end -%>
48
52
  <%- end -%>
49
53
  <% end -%>
50
54
 
51
- <% if model.label_attr # do not crash when the model has no label attr -%>
55
+ <% if model.label_attr -%>
52
56
  # destroy
53
57
  When I go to the list of <%= model.name(:variables) %>
54
58
  Then I should see "<%= model.label_attr.test_value %>"
55
59
 
56
- When I follow "Destroy"
60
+ When I follow "Destroy <%= model.label_attr.test_value %>"
57
61
  Then I should be on the list of <%= model.name(:variables) %>
58
62
  But I should not see "<%= model.label_attr.test_value %>"
59
63
  <% end -%>
@@ -14,6 +14,7 @@ module Katapult
14
14
  def install_application_layout
15
15
  remove_file 'app/views/layouts/application.html.erb'
16
16
  template 'app/views/layouts/application.html.haml'
17
+ template 'app/views/layouts/_flashes.html.haml'
17
18
  end
18
19
 
19
20
  def create_views_directory
@@ -63,6 +64,10 @@ module Katapult
63
64
  def navigation
64
65
  wui.application_model.navigation
65
66
  end
67
+
68
+ def authentication
69
+ wui.application_model.authentication
70
+ end
66
71
  end
67
72
 
68
73
  private
@@ -1,7 +1,7 @@
1
1
  = form_for <%= model_name(:ivar) %> do |form|
2
2
 
3
3
  %dl.controls
4
- <% wui.model.attrs.each do |attribute| -%>
4
+ <% wui.model.editable_attrs.each do |attribute| -%>
5
5
  %dt
6
6
  = form.label <%= attribute.name(:symbol) %>
7
7
  %dd
@@ -13,6 +13,8 @@
13
13
  = form.text_field <%= attribute.name(:symbol) %>
14
14
  <%- when :email -%>
15
15
  = form.email_field <%= attribute.name(:symbol) %>
16
+ <%- when :password -%>
17
+ = form.password_field <%= attribute.name(:symbol) %>
16
18
  <%- when :url -%>
17
19
  = form.url_field <%= attribute.name(:symbol) %>
18
20
  <%- when :integer -%>
@@ -22,12 +24,10 @@
22
24
 
23
25
  <%- when :text -%>
24
26
  = form.text_area <%= attribute.name(:symbol) %>, rows: 5
25
- <%- when :markdown -%>
26
- = form.text_area <%= attribute.name(:symbol) %>, rows: 15
27
27
  <%- when :flag -%>
28
28
  = form.check_box <%= attribute.name(:symbol) %>
29
29
  <%- when :datetime -%>
30
- = form.datetime_field <%= attribute.name(:symbol) %>
30
+ = form.date_field <%= attribute.name(:symbol) %>
31
31
  <%- end -%>
32
32
  <%- end -%>
33
33
  <% end -%>
@@ -0,0 +1,3 @@
1
+ - flash.each do |level, message|
2
+ .flash.alert.alert-info
3
+ = message
@@ -15,12 +15,18 @@
15
15
 
16
16
  .layout__head
17
17
  %h2 <%= app_name.titlecase %>
18
- <%- if navigation -%>
18
+ <%- if navigation -%>
19
19
  = render_navigation Navigation.<%= navigation.name(:variable) %>
20
- <%- end -%>
20
+ <%- end -%>
21
+ <%- if authentication -%>
22
+ - if signed_in?
23
+ .current-user
24
+ = link_to current_user.email, edit_user_path(current_user)
25
+ = link_to 'Sign out', sign_out_path, method: :delete
26
+ <%- end -%>
21
27
 
22
28
  .layout__main
23
- =# render 'layouts/flashes'
29
+ = render 'layouts/flashes'
24
30
  = yield
25
31
 
26
32
  .layout__tail