katapult 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 83b5fc8065a8a49c75139d70ffd14b8bd9f46153
4
- data.tar.gz: da1546065c4be23023e74749f5ab80ce217f4a58
3
+ metadata.gz: a3cdc0f0b264fd596277340707b91532b302b8d2
4
+ data.tar.gz: 92f80d989c103b65434d22d9d33c0eb273f2dd40
5
5
  SHA512:
6
- metadata.gz: ce71cf5e9ddd843be50e677070b5591a3f6dc48e2bff36eebc7f86651cbc2a8610f950476c9c47a407431550611202cfe65fd2a20318870f57e6745db9c6ed6a
7
- data.tar.gz: 2285ddc6da1550f9cbf75165b49d2e6f65ba1894b97286d9b1e1b0f2efa61456e0534c37890b9758f5a37cebb40c02d2968933a8e58e26ade886fd9a0f1b9ed5
6
+ metadata.gz: 708499a49443d9384eb273369a0e81bf2f6578e0520a9bc460abeefdc2c8c97ff5d310407e391ac80fc8517791c0c12f89652d60a3ff42b7d0228b9466130871
7
+ data.tar.gz: d122fd0af16935458dd2b06867fa86b5d545a74b8a2fb2f746c49cd61cada320b333426282a627cb5f41340b14dade12795724496a80c2404af42dfa76d0bd0f
data/.gitignore CHANGED
@@ -16,3 +16,4 @@ test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
18
  .pt_project_id
19
+ .idea
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.1.5
1
+ 2.3.0
data/README.md CHANGED
@@ -3,13 +3,17 @@
3
3
  <img src="katapult.png" width="200px" align="right" />
4
4
 
5
5
 
6
- `Katapult` is a kickstart generator for Rails applications. It creates a basic
7
- application and generates ([makandra-flavored](https://leanpub.com/growing-rails))
8
- code from an application model, significantly speeding up the initial phase of a
9
- Rails project.
6
+ `Katapult` is a kickstart generator for Rails applications. It creates new Rails
7
+ applications with [lots of pre-configuration](https://github.com/makandra/katapult/blob/master/lib/generators/katapult/basics/basics_generator.rb)
8
+ and offers ([makandra-flavored](https://leanpub.com/growing-rails)) code
9
+ generation from an application model.
10
+ These two features significally speed up the initial phase of a Rails project by
11
+ doing in minutes what took you weeks. After modeling your application, which
12
+ typically takes about an hour, you can instantly start implementing the meat of
13
+ your application.
10
14
 
11
- `Katapult` will always support current versions of Ruby and Rails, currently
12
- Rails 4.2 and Ruby 2.1.
15
+ `Katapult` will only support current versions of Ruby and Rails, currently
16
+ Rails 4.2 and Ruby 2.3.
13
17
 
14
18
 
15
19
  ## Installation
@@ -18,15 +22,18 @@ Install the `katapult` gem with
18
22
 
19
23
  gem install katapult
20
24
 
25
+ If you intend to extend an existing application, add it to the development group
26
+ in your Gemfile.
27
+
21
28
 
22
29
  ## Usage
23
30
 
24
31
  `Katapult` does two separate things:
25
32
 
26
- 1. Create a new Rails application, set up with many standard gems, snippets,
27
- useful configuration, databases etc.
28
- 2. Generate code from an application model, i.e. create files for models, views,
29
- controllers, routes, stylesheets
33
+ 1. It creates a new Rails application, set up with many standard gems, snippets,
34
+ useful configuration, databases, testing libraries etc. See the [BasicsGenerator](https://github.com/makandra/katapult/blob/master/lib/generators/katapult/basics/basics_generator.rb) for details.
35
+ 2. It generates code from an application model, i.e. creates files for models,
36
+ views, controllers, routes, stylesheets; see the
30
37
 
31
38
  You may use both or only one of them.
32
39
 
@@ -37,28 +44,27 @@ Run the following command:
37
44
 
38
45
  katapult new $APPLICATION_NAME
39
46
 
40
- In detail, this will:
47
+ This will:
41
48
 
42
- - create a new Rails application (without turbolinks)
43
- - install common Gems, some of them commented out
44
- - add a .gitignore and a .ruby-version file
45
- - set up a `database.yml` file (for MySQL)
49
+ - create a new Rails application
50
+ - install common Gems
51
+ - set up a `database.yml` file (for PostgreSQL)
46
52
  - create basic styles
47
- - install some handy initializers
48
53
  - install RSpec and Cucumber to the application
49
- - create `lib/katapult/application_model.rb` (needed in Step 2)
54
+ - install Capistrano
55
+ - create `lib/katapult/application_model.rb` (needed for step 2)
50
56
 
51
- See `lib/generators/katapult/basics/basics_generator.rb`.
57
+ See the [BasicsGenerator](https://github.com/makandra/katapult/blob/master/lib/generators/katapult/basics/basics_generator.rb)
58
+ for details: Its methods are executed one-by-one and their names are a
59
+ description of what it does.
52
60
 
53
- ### Using Katapult in existing Rails applications
61
+ ### Alternative: Using Katapult in existing Rails applications
54
62
  `katapult` expects a clean application (that it would usually generate itself).
55
63
  If you have an existing Rails application, you *may* use `katapult`, but be
56
64
  warned: it is not designed to respect existing files, although it will usually
57
- ask before overwriting files.
65
+ ask before overwriting anything.
58
66
 
59
- To add `katapult` to an existing Rails application, add
60
- `gem 'katapult', group: :development` to the Gemfile. Then run any of the
61
- following generators:
67
+ After adding it to the Gemfile (see above), run any of the following generators:
62
68
 
63
69
  rails generate katapult:basics # Prepare the app with useful defaults
64
70
  rails generate katapult:install # Create application model file
@@ -72,16 +78,20 @@ following generators:
72
78
  > install it with `rails generate katapult:install`. See above.
73
79
 
74
80
  After installation, you will find a file `lib/katapult/application_model.rb`
75
- where you will define the properties of your application.
81
+ where you will define the properties of your application. You're free to create
82
+ more than one application model, however, you'll need to specify their location
83
+ when running the transform.
76
84
 
77
- Inside this file, use `katapult`'s simple DSL (domain specific language) to
78
- express yourself. When you are done developing the application model, transform
85
+ Inside the application model, use `katapult`'s simple DSL (domain specific
86
+ language) to express yourself. When you are done developing the model, transform
79
87
  it into code with:
80
88
 
81
- katapult fire
89
+ katapult fire [path/to/application_model]
82
90
 
83
91
  See an overview of the DSL below. The respective sections hold examples of what
84
- options are available to each element.
92
+ options are available to each element. For details, dive into
93
+ `lib/generators/katapult` where all generators are stored. The method names
94
+ inside a generator tell what it does.
85
95
 
86
96
  ### Generic DSL syntax example
87
97
  The DSL consists of _elements_, e.g. `Model` or `WUI` (Web User Interface). Each
@@ -108,6 +118,10 @@ Defined on Model. Takes a name and options:
108
118
 
109
119
  # Inferred type :email (when attr name matches /email/)
110
120
  model.attr :email
121
+
122
+ # Inferred type :password. Password fields are rendered as password_field in
123
+ # forms, but never rendered in show views.
124
+ model.attr :password
111
125
 
112
126
  # Specify assignable values. Available options: allow_blank, default
113
127
  model.attr :age, type: :integer, assignable_values: 18..99, allow_blank: true
@@ -121,6 +135,10 @@ Defined on Model. Takes a name and options:
121
135
  # Boolean fields are modeled as flags. Default required!
122
136
  model.attr :locked, type: :flag, default: false
123
137
 
138
+ # JSON fields are supported
139
+ model.attr :prefer, type: :json # PostgreSQL "jsonb"
140
+ model.attr :avoid, type: :plain_json # PostgreSQL "json"
141
+
124
142
 
125
143
  ### WUI (Web User Interface)
126
144
  Takes a name, options and a block:
@@ -160,6 +178,16 @@ WUIs.
160
178
  navigation :main
161
179
 
162
180
 
181
+ ### Authenticate
182
+ Takes the name of the user model (currently only `User` (case-insensitive) is
183
+ supported) and an email address. Generates authentication with [Clearance](https://github.com/thoughtbot/clearance).
184
+
185
+ authenticate 'User', system_email: 'system@example.com'
186
+
187
+ The email address will be the sender for Clearance mails like password reset
188
+ requests.
189
+
190
+
163
191
  ## Development
164
192
 
165
193
  ### Basic information
@@ -171,14 +199,12 @@ It caches a pristine Rails application inside its `tmp/` directory to
171
199
  speed up test runs. Keep this in mind, as it may lead to caching issues when
172
200
  switching Ruby versions or installing a new version of the Rails gem.
173
201
 
174
- Since `katapult` has full-stack integration tests, it requires a MySQL account.
175
- Create a dedicated account on your local MySQL server by running this command in
176
- a MySQL console (as-is):
177
-
178
- GRANT ALL ON *.* TO 'katapult'@'localhost' IDENTIFIED BY 'secret';
202
+ Since `katapult` has full-stack integration tests, it requires a PostgreSQL
203
+ account. Create a dedicated account on your local PostgreSQL server:
179
204
 
180
- The user `katapult` is hereby granted any action (SELECT, UPDATE, etc. except
181
- for granting privileges) on any database and table (`*.*`).
205
+ $> sudo -iu postgres
206
+ postgres $> psql
207
+ postgres=# CREATE ROLE katapult WITH createdb LOGIN;
182
208
 
183
209
  ### Continuing development
184
210
  When you continue development on `katapult`, remove its `tmp/` directory first.
data/bin/katapult CHANGED
@@ -4,18 +4,8 @@
4
4
  # that the user else had to perform manually.
5
5
  # See bottom for USAGE.
6
6
 
7
- require_relative '../lib/katapult/util'
8
- require 'bundler'
9
-
10
- def pink_puts(*args)
11
- message = "\n> #{ args.join ' ' }"
12
- Kernel.puts "\e[35m#{ message }\e[0m" # pink
13
- end
14
-
15
- # With clean Bundler env
16
- def run(command)
17
- Bundler.with_clean_env { system command }
18
- end
7
+ require_relative '../lib/katapult/binary_util'
8
+ util = Katapult::BinaryUtil
19
9
 
20
10
  case (transform_command = ARGV.shift)
21
11
  when 'new'
@@ -24,35 +14,35 @@ when 'new'
24
14
  basics_command = 'bundle exec rails generate katapult:basics'
25
15
 
26
16
  if interactive
27
- pink_puts 'Please enter your database user: '
17
+ util.puts 'Please enter your database user: '
28
18
  basics_command << ' --db-user ' << gets.chomp
29
19
 
30
- pink_puts 'Please enter your database password: '
20
+ util.puts 'Please enter your database password: '
31
21
  basics_command << ' --db-password ' << gets.chomp
32
22
  end
33
23
 
34
- pink_puts 'Creating new Rails application ...'
35
- run "rails new #{ app_name } --skip-test-unit --skip-bundle --database mysql"
24
+ util.puts 'Creating new Rails application ...'
25
+ util.create_rails_app app_name
36
26
 
37
27
  Dir.chdir app_name
38
28
 
39
- pink_puts 'Initializing git repository ...'
40
- run 'git init'
41
- Katapult::Util.git_commit "rails new #{ app_name }"
29
+ util.puts 'Initializing git repository ...'
30
+ util.run 'git init --quiet'
31
+ util.git_commit "rails new #{ app_name }", '--quiet'
42
32
 
43
- pink_puts 'Installing katapult ...'
33
+ util.puts 'Installing katapult ...'
44
34
  File.open('Gemfile', 'a') do |file|
45
35
  file.puts "gem 'katapult'#{ ENV['KATAPULT_GEMFILE_OPTIONS'] }, group: :development"
46
36
  end
47
- run 'bundle install'
48
- run 'bundle exec rails generate katapult:install'
49
- Katapult::Util.git_commit 'rails generate katapult:install'
37
+ util.run 'bundle install --quiet'
38
+ util.run 'bundle exec rails generate katapult:install'
39
+ util.git_commit 'rails generate katapult:install', '--quiet'
50
40
 
51
- pink_puts 'Generating katapult basics ...'
52
- run basics_command
53
- Katapult::Util.git_commit 'rails generate katapult:basics'
41
+ util.puts 'Generating katapult basics ...'
42
+ util.run basics_command
43
+ util.git_commit 'rails generate katapult:basics', '--quiet'
54
44
 
55
- pink_puts <<-INSTRUCTIONS
45
+ util.puts <<-INSTRUCTIONS
56
46
  Application initialization done.
57
47
 
58
48
  Next step: Model your application in lib/katapult/application_model.rb and
@@ -60,14 +50,14 @@ transform it into code by running `katapult fire`.
60
50
  INSTRUCTIONS
61
51
 
62
52
  when 'fire'
63
- app_model_path = 'lib/katapult/application_model.rb'
53
+ app_model_path = ARGV.shift || 'lib/katapult/application_model.rb'
64
54
  transform_command = 'bin/rails generate katapult:transform ' + app_model_path
65
55
 
66
- pink_puts 'Loading katapult ...'
67
- run transform_command
68
- Katapult::Util.git_commit transform_command
56
+ util.puts 'Loading katapult ...'
57
+ util.run transform_command
58
+ util.git_commit transform_command
69
59
 
70
- pink_puts <<-INSTRUCTIONS
60
+ util.puts <<-INSTRUCTIONS
71
61
  Model transformation done.
72
62
 
73
63
  Now boot up your development server (e.g. with `rails server`) and try your
@@ -76,7 +66,7 @@ kickstarted application in the browser!
76
66
 
77
67
  else
78
68
  puts <<-USAGE
79
- Usage: katapult [new APP_NAME | fire]
80
- Hint: Suppress database credentials prompt with `--non-interactive`
69
+ Usage: katapult [new APP_NAME | fire [path/to/model] ]
70
+ Suppress database credentials prompt with `--non-interactive`
81
71
  USAGE
82
72
  end
@@ -0,0 +1,344 @@
1
+ Feature: Add authentication to an application
2
+
3
+ Background:
4
+ Given a pristine Rails application
5
+ And I install katapult
6
+ And I generate katapult basics
7
+
8
+
9
+ Scenario: Authenticate with the User model
10
+ When I overwrite "lib/katapult/application_model.rb" with:
11
+ """
12
+ model 'user'
13
+ wui('user') { |w| w.crud }
14
+ authenticate 'user', system_email: 'system@example.com'
15
+ """
16
+ And I successfully transform the application model
17
+ Then the file "Gemfile" should contain "gem 'clearance'"
18
+ And the file "app/controllers/application_controller.rb" should contain:
19
+ """
20
+ include Clearance::Controller
21
+
22
+ before_action :require_login
23
+ """
24
+ And the file "app/controllers/passwords_controller.rb" should contain exactly:
25
+ """
26
+ class PasswordsController < Clearance::PasswordsController
27
+
28
+ def update
29
+ @user = find_user_for_update
30
+
31
+ if @user.update_password password_reset_params
32
+ sign_in @user
33
+ flash[:notice] = 'Password successfully changed' # <<- added
34
+ redirect_to url_after_update
35
+ else
36
+ flash_failure_after_update
37
+ render template: 'passwords/edit'
38
+ end
39
+ end
40
+
41
+ end
42
+ """
43
+ And the file "app/controllers/users_controller.rb" should match /permit.*email.*password/
44
+ And the file "app/views/users/_form.html.haml" should contain:
45
+ """
46
+ %dt
47
+ = form.label :email
48
+ %dd
49
+ = form.email_field :email
50
+ """
51
+ And the file "app/views/users/_form.html.haml" should contain:
52
+ """
53
+ %dt
54
+ = form.label :password
55
+ %dd
56
+ = form.password_field :password
57
+ """
58
+ And the file "app/views/clearance_mailer/change_password.html.haml" should contain exactly:
59
+ """
60
+ %p
61
+ To reset your password, please follow this link:
62
+
63
+ %p
64
+ = link_to 'Change password',
65
+ edit_user_password_url(@user, token: @user.confirmation_token.html_safe)
66
+
67
+ """
68
+ And the file "app/views/clearance_mailer/change_password.text.erb" should contain exactly:
69
+ """
70
+ To reset your password, please follow this link:
71
+
72
+ <%= edit_user_password_url(@user, token: @user.confirmation_token.html_safe) %>
73
+ """
74
+ And the file "app/views/passwords/create.html.haml" should contain exactly:
75
+ """
76
+ %h1
77
+ Password Reset Instructions Sent
78
+
79
+ %p
80
+ We've sent you an email with instructions on how to reset your password.
81
+ """
82
+ And the file "app/views/passwords/edit.html.haml" should contain exactly:
83
+ """
84
+ %h1
85
+ Reset Password
86
+
87
+ %p
88
+ Choose your new password:
89
+
90
+ = form_for :password_reset,
91
+ url: user_password_path(@user, token: @user.confirmation_token),
92
+ html: { method: :put } do |form|
93
+
94
+ .form-group
95
+ = form.label :password
96
+ = form.password_field :password, class: 'form-control',
97
+ placeholder: 'New Password'
98
+
99
+ = form.submit 'Update Password', class: 'btn btn-primary'
100
+ """
101
+ And the file "app/views/passwords/new.html.haml" should contain exactly:
102
+ """
103
+ %h1
104
+ Password Reset
105
+
106
+ %p
107
+ Please enter your email address. We will send you instructions on how
108
+ to reset your password.
109
+
110
+ = form_for :password, url: passwords_path do |form|
111
+ .form-group
112
+ = form.label :email
113
+ = form.email_field :email, class: 'form-control',
114
+ placeholder: 'Email Address'
115
+
116
+ = form.submit 'Request Instructions', class: 'btn btn-primary'
117
+ """
118
+ And the file "app/views/sessions/new.html.haml" should contain exactly:
119
+ """
120
+ %h1
121
+ Please sign in
122
+
123
+ = form_for :session, url: session_path do |form|
124
+ .form-group
125
+ = form.label :email
126
+ = form.email_field :email, class: 'form-control',
127
+ placeholder: 'Email Address', required: true, autofocus: true
128
+
129
+ .form-group
130
+ = form.label :password
131
+ = form.password_field :password, class: 'form-control', required: true,
132
+ placeholder: 'Password'
133
+
134
+ %p
135
+ = form.submit 'Sign in', class: 'btn btn-primary'
136
+
137
+ %p
138
+ = link_to 'Forgot password', new_password_path, class: 'text-muted'
139
+ """
140
+ And the file "config/environments/test.rb" should contain:
141
+ """
142
+ # Enable quick-signin in tests: `visit homepage(as: User.last!)`
143
+ config.middleware.use Clearance::BackDoor
144
+
145
+ """
146
+ And the file "config/initializers/clearance.rb" should contain exactly:
147
+ """
148
+ Clearance.configure do |config|
149
+ config.allow_sign_up = false
150
+ # config.cookie_domain = '.example.com'
151
+ # config.cookie_expiration = lambda { |cookies| 1.year.from_now.utc }
152
+ # config.cookie_name = 'remember_token'
153
+ # config.cookie_path = '/'
154
+ config.routes = false
155
+ # config.httponly = true
156
+ config.mailer_sender = 'system@example.com'
157
+ # config.password_strategy = Clearance::PasswordStrategies::BCrypt
158
+ # config.redirect_url = '/'
159
+ # config.secure_cookie = true
160
+ # config.sign_in_guards = []
161
+ # config.user_model = User
162
+ end
163
+ """
164
+ And a file named "config/locales/clearance.en.yml" should exist
165
+ And the file "config/routes.rb" should contain:
166
+ """
167
+ resources :users do
168
+ resource :password, controller: 'passwords',
169
+ only: %i[edit update]
170
+ end
171
+
172
+ # Clearance
173
+ get '/login', to: 'clearance/sessions#new', as: 'sign_in'
174
+ resource :session, controller: 'clearance/sessions', only: [:create]
175
+ resources :passwords, controller: 'passwords', only: [:create, :new]
176
+ delete '/logout', to: 'clearance/sessions#destroy', as: 'sign_out'
177
+ """
178
+ And there should be a migration with:
179
+ """
180
+ class AddClearanceToUsers < ActiveRecord::Migration
181
+ def self.up
182
+ change_table :users do |t|
183
+ t.string :encrypted_password, limit: 128
184
+ t.string :confirmation_token, limit: 128
185
+ t.string :remember_token, limit: 128
186
+ end
187
+
188
+ add_index :users, :email
189
+ add_index :users, :remember_token
190
+
191
+ users = select_all("SELECT id FROM users WHERE remember_token IS NULL")
192
+
193
+ users.each do |user|
194
+ update <<-SQL
195
+ UPDATE users
196
+ SET remember_token = '#{Clearance::Token.new}'
197
+ WHERE id = '#{user['id']}'
198
+ SQL
199
+ end
200
+ end
201
+
202
+ def self.down
203
+ change_table :users do |t|
204
+ t.remove :encrypted_password, :confirmation_token, :remember_token
205
+ end
206
+ end
207
+ end
208
+ """
209
+ And the file "features/authentication.feature" should contain exactly:
210
+ """
211
+ Feature: Everything about user authentication
212
+
213
+ Scenario: Login is required to visit the homepage
214
+ When I go to the homepage
215
+ Then I should see "Please sign in to continue" within the flash
216
+ And I should be on the sign-in form
217
+
218
+
219
+ Scenario: Login
220
+ Given there is a user with the email "henry@example.com" and the password "password"
221
+
222
+ When I go to the homepage
223
+ Then I should be on the sign-in form
224
+ And I should see "Please sign in"
225
+
226
+ # Wrong email
227
+ When I fill in "Email" with "nonsense"
228
+ And I fill in "Password" with "password"
229
+ And I press "Sign in"
230
+ Then I should see "Bad email or password" within the flash
231
+ And I should see "Please sign in"
232
+
233
+ # Wrong password
234
+ When I fill in "Email" with "admin@example.com"
235
+ And I fill in "Password" with "wrong"
236
+ And I press "Sign in"
237
+ Then I should see "Bad email or password" within the flash
238
+ And I should see "Please sign in"
239
+
240
+ # Correct credentials
241
+ When I fill in "Email" with "henry@example.com"
242
+ And I fill in "Password" with "password"
243
+ And I press "Sign in"
244
+ Then I should be on the homepage
245
+
246
+
247
+ Scenario: Logout
248
+ Given there is a user
249
+ And I am signed in as the user above
250
+
251
+ When I follow "Sign out"
252
+ Then I should be on the sign-in form
253
+
254
+ # Logged out
255
+ When I go to the homepage
256
+ Then I should be on the sign-in form
257
+
258
+
259
+ Scenario: Reset password as a signed-in user
260
+ Given there is a user with the email "henry@example.com"
261
+ And I sign in as the user above
262
+
263
+ When I go to the homepage
264
+ And I follow "henry@example.com" within the current user
265
+ Then I should be on the form for the user above
266
+
267
+ When I fill in "Password" with "new-password"
268
+ And I press "Save"
269
+ Then I should be on the page for the user above
270
+
271
+ When I follow "Sign out"
272
+ And I fill in "Email" with "henry@example.com"
273
+ And I fill in "Password" with "new-password"
274
+ And I press "Sign in"
275
+ Then I should be on the homepage
276
+
277
+
278
+ Scenario: Reset password as a signed-out user
279
+ Given there is a user with the email "henry@example.com"
280
+
281
+ When I go to the sign-in form
282
+ And I follow "Forgot password"
283
+ Then I should be on the reset password page
284
+ And I should see "Password Reset"
285
+
286
+ When I fill in "Email" with "henry@example.com"
287
+ And I press "Request Instructions"
288
+ Then an email should have been sent with:
289
+ \"\"\"
290
+ From: system@example.com
291
+ To: henry@example.com
292
+ Subject: Change your password
293
+
294
+ To reset your password, please follow this link:
295
+ \"\"\"
296
+
297
+ When I follow the first link in the email
298
+ Then I should be on the new password page for the user above
299
+ And I should see "Reset Password"
300
+
301
+ When I fill in "Choose password" with "new-password"
302
+ And I press "Update Password"
303
+ Then I should see "Password successfully changed" within the flash
304
+ And I should be on the homepage
305
+ """
306
+ And the file "features/users.feature" should contain:
307
+ """
308
+
309
+
310
+ Background:
311
+ Given there is a user
312
+ And I sign in as the user above
313
+
314
+ """
315
+ And the file "features/step_definitions/authentication_steps.rb" should contain exactly:
316
+ """
317
+ When /^I (?:am signed|sign) in as the user above$/ do
318
+ user = User.last!
319
+ visit root_path(as: user) # Using Clearance::BackDoor
320
+ end
321
+ """
322
+ And the file "features/support/paths.rb" should contain:
323
+ """
324
+
325
+ # Authentication
326
+ when 'the sign-in form'
327
+ sign_in_path
328
+ when 'the reset password page'
329
+ new_password_path
330
+ when 'the new password page for the user above'
331
+ edit_user_password_path(User.last!)
332
+
333
+ """
334
+ And the file "spec/factories/factories.rb" should contain:
335
+ """
336
+ factory :user do
337
+ sequence(:email) { |i| "user-#{ i }@example.com" }
338
+ password 'password'
339
+ end
340
+ """
341
+
342
+ When I run cucumber
343
+ Then the features should pass
344
+