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
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
+