katapult 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/README.md +61 -35
- data/bin/katapult +24 -34
- data/features/authenticate.feature +344 -0
- data/features/basics.feature +590 -0
- data/features/binary.feature +33 -1
- data/features/model.feature +9 -13
- data/features/step_definitions/rails_steps.rb +3 -2
- data/features/wui.feature +49 -4
- data/lib/generators/katapult/basics/basics_generator.rb +63 -17
- data/lib/generators/katapult/basics/templates/.gitignore +1 -0
- data/lib/generators/katapult/basics/templates/.ruby-version +1 -1
- data/lib/generators/katapult/basics/templates/Capfile +25 -0
- data/lib/generators/katapult/basics/templates/Gemfile +14 -6
- data/lib/generators/katapult/basics/templates/Guardfile +44 -0
- data/lib/generators/katapult/basics/templates/config/database.sample.yml +3 -2
- data/lib/generators/katapult/basics/templates/config/database.yml +3 -2
- data/lib/generators/katapult/basics/templates/config/deploy/production.rb +8 -0
- data/lib/generators/katapult/basics/templates/config/deploy/staging.rb +7 -0
- data/lib/generators/katapult/basics/templates/config/deploy.rb +37 -0
- data/lib/generators/katapult/basics/templates/config/initializers/ext.rb +3 -0
- data/lib/generators/katapult/basics/templates/features/support/factory_girl.rb +1 -0
- data/lib/generators/katapult/basics/templates/features/support/paths.rb +6 -0
- data/lib/generators/katapult/basics/templates/lib/capistrano/tasks/db.rake +28 -0
- data/lib/generators/katapult/basics/templates/lib/capistrano/tasks/deploy.rake +15 -0
- data/lib/generators/katapult/basics/templates/lib/capistrano/tasks/passenger.rake +8 -0
- data/lib/generators/katapult/basics/templates/{config/initializers → lib/ext/action_view}/form_for_with_development_errors.rb +0 -2
- data/lib/generators/katapult/basics/templates/lib/ext/action_view/spec_label.rb +46 -0
- data/lib/generators/katapult/basics/templates/{config/initializers → lib/ext/active_record}/find_by_anything.rb +0 -0
- data/lib/generators/katapult/basics/templates/lib/ext/active_record/these.rb +7 -0
- data/lib/generators/katapult/basics/templates/lib/ext/array/xss_aware_join.rb +10 -0
- data/lib/generators/katapult/basics/templates/lib/ext/enumerable/natural_sort.rb +15 -0
- data/lib/generators/katapult/basics/templates/lib/ext/hash/infinite.rb +7 -0
- data/lib/generators/katapult/basics/templates/lib/ext/string/html_entities.rb +11 -0
- data/lib/generators/katapult/basics/templates/lib/ext/string/to_sort_atoms.rb +52 -0
- data/lib/generators/katapult/basics/templates/lib/tasks/pending_migrations.rake +24 -0
- data/lib/generators/katapult/basics/templates/spec/factories/factories.rb +9 -0
- data/lib/generators/katapult/basics/templates/spec/support/factory_girl.rb +3 -0
- data/lib/generators/katapult/clearance/clearance_generator.rb +125 -0
- data/lib/generators/katapult/clearance/templates/app/controllers/passwords_controller.rb +16 -0
- data/lib/generators/katapult/clearance/templates/app/views/clearance_mailer/change_password.html.haml +6 -0
- data/lib/generators/katapult/clearance/templates/app/views/clearance_mailer/change_password.text.erb +3 -0
- data/lib/generators/katapult/clearance/templates/app/views/passwords/create.html.haml +5 -0
- data/lib/generators/katapult/clearance/templates/app/views/passwords/edit.html.haml +16 -0
- data/lib/generators/katapult/clearance/templates/app/views/passwords/new.html.haml +14 -0
- data/lib/generators/katapult/clearance/templates/app/views/sessions/new.html.haml +19 -0
- data/lib/generators/katapult/clearance/templates/config/initializers/clearance.rb +15 -0
- data/lib/generators/katapult/clearance/templates/config/locales/clearance.en.yml +59 -0
- data/lib/generators/katapult/clearance/templates/features/authentication.feature +94 -0
- data/lib/generators/katapult/clearance/templates/features/step_definitions/authentication_steps.rb +4 -0
- data/lib/generators/katapult/cucumber_features/templates/feature.feature +11 -7
- data/lib/generators/katapult/haml/haml_generator.rb +5 -0
- data/lib/generators/katapult/haml/templates/_form.html.haml +4 -4
- data/lib/generators/katapult/haml/templates/app/views/layouts/_flashes.html.haml +3 -0
- data/lib/generators/katapult/haml/templates/app/views/layouts/application.html.haml +9 -3
- data/lib/generators/katapult/haml/templates/index.html.haml +1 -1
- data/lib/generators/katapult/haml/templates/show.html.haml +2 -4
- data/lib/generators/katapult/install/templates/lib/katapult/application_model.rb +9 -7
- data/lib/generators/katapult/model/model_generator.rb +1 -1
- data/lib/generators/katapult/w_u_i/templates/controller.rb +1 -1
- data/lib/generators/katapult/w_u_i/w_u_i_generator.rb +4 -2
- data/lib/katapult/application_model.rb +8 -1
- data/lib/katapult/attribute.rb +10 -11
- data/lib/katapult/authentication.rb +25 -0
- data/lib/katapult/binary_util.rb +37 -0
- data/lib/katapult/element.rb +1 -1
- data/lib/katapult/generator.rb +6 -0
- data/lib/katapult/model.rb +13 -1
- data/lib/katapult/parser.rb +7 -0
- data/lib/katapult/version.rb +1 -1
- data/lib/katapult/wui.rb +4 -0
- data/lib/katapult.rb +2 -0
- data/spec/attribute_spec.rb +13 -0
- data/spec/element_spec.rb +5 -0
- data/spec/model_spec.rb +5 -4
- data/spec/util_spec.rb +8 -8
- data/spec/wui_spec.rb +19 -0
- metadata +44 -8
- data/features/katapult.feature +0 -271
- data/lib/katapult/util.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3cdc0f0b264fd596277340707b91532b302b8d2
|
4
|
+
data.tar.gz: 92f80d989c103b65434d22d9d33c0eb273f2dd40
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 708499a49443d9384eb273369a0e81bf2f6578e0520a9bc460abeefdc2c8c97ff5d310407e391ac80fc8517791c0c12f89652d60a3ff42b7d0228b9466130871
|
7
|
+
data.tar.gz: d122fd0af16935458dd2b06867fa86b5d545a74b8a2fb2f746c49cd61cada320b333426282a627cb5f41340b14dade12795724496a80c2404af42dfa76d0bd0f
|
data/.gitignore
CHANGED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
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
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
12
|
-
Rails 4.2 and Ruby 2.
|
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.
|
27
|
-
useful configuration, databases etc.
|
28
|
-
2.
|
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
|
-
|
47
|
+
This will:
|
41
48
|
|
42
|
-
- create a new Rails application
|
43
|
-
- install common Gems
|
44
|
-
-
|
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
|
-
-
|
54
|
+
- install Capistrano
|
55
|
+
- create `lib/katapult/application_model.rb` (needed for step 2)
|
50
56
|
|
51
|
-
See
|
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
|
65
|
+
ask before overwriting anything.
|
58
66
|
|
59
|
-
|
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
|
78
|
-
express yourself. When you are done developing the
|
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
|
175
|
-
Create a dedicated account on your local
|
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
|
-
|
181
|
-
|
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/
|
8
|
-
|
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
|
-
|
17
|
+
util.puts 'Please enter your database user: '
|
28
18
|
basics_command << ' --db-user ' << gets.chomp
|
29
19
|
|
30
|
-
|
20
|
+
util.puts 'Please enter your database password: '
|
31
21
|
basics_command << ' --db-password ' << gets.chomp
|
32
22
|
end
|
33
23
|
|
34
|
-
|
35
|
-
|
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
|
-
|
40
|
-
run 'git init'
|
41
|
-
|
29
|
+
util.puts 'Initializing git repository ...'
|
30
|
+
util.run 'git init --quiet'
|
31
|
+
util.git_commit "rails new #{ app_name }", '--quiet'
|
42
32
|
|
43
|
-
|
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
|
-
|
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
|
-
|
52
|
-
run basics_command
|
53
|
-
|
41
|
+
util.puts 'Generating katapult basics ...'
|
42
|
+
util.run basics_command
|
43
|
+
util.git_commit 'rails generate katapult:basics', '--quiet'
|
54
44
|
|
55
|
-
|
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
|
-
|
67
|
-
run transform_command
|
68
|
-
|
56
|
+
util.puts 'Loading katapult ...'
|
57
|
+
util.run transform_command
|
58
|
+
util.git_commit transform_command
|
69
59
|
|
70
|
-
|
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
|
-
|
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
|
+
|