remarkable_devise 1.0.0.alpha3 → 1.0.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.
- data/README.markdown +70 -7
- data/lib/remarkable/devise/matchers/base_matcher.rb +17 -0
- data/lib/remarkable/devise/matchers/be_a_confirmable_matcher.rb +7 -3
- data/lib/remarkable/devise/matchers/be_a_database_authenticatable_matcher.rb +11 -4
- data/lib/remarkable/devise/matchers/be_a_lockable_matcher.rb +34 -0
- data/lib/remarkable/devise/matchers/be_a_registerable_matcher.rb +19 -0
- data/lib/remarkable/devise/matchers/be_a_rememberable_matcher.rb +7 -3
- data/lib/remarkable/devise/matchers/be_a_timeoutable_matcher.rb +23 -0
- data/lib/remarkable/devise/matchers/be_a_token_authenticatable_matcher.rb +7 -3
- data/lib/remarkable/devise/matchers/be_a_validatable_matcher.rb +7 -3
- data/lib/remarkable/devise/matchers/be_an_authenticatable_matcher.rb +19 -0
- data/lib/remarkable/devise/version.rb +1 -1
- data/locale/en.yml +70 -0
- data/remarkable_devise.gemspec +1 -1
- data/spec/example_models.rb +10 -2
- data/spec/examples/user_spec.rb +24 -0
- data/spec/matchers/be_a_confirmable_spec.rb +32 -4
- data/spec/matchers/be_a_database_authenticatable_spec.rb +55 -5
- data/spec/matchers/be_a_lockable_spec.rb +274 -0
- data/spec/matchers/be_a_registerable_spec.rb +27 -0
- data/spec/matchers/be_a_rememberable_spec.rb +81 -1
- data/spec/matchers/be_a_timeoutable_spec.rb +59 -0
- data/spec/matchers/be_a_token_authenticatable_spec.rb +33 -1
- data/spec/matchers/be_a_validatable_spec.rb +58 -5
- data/spec/matchers_spec.rb +23 -5
- data/spec/shared_examples.rb +122 -0
- data/spec/spec_helper.rb +13 -4
- metadata +23 -12
data/README.markdown
CHANGED
@@ -15,18 +15,81 @@ Add the require after the remarkable/active_record line in your spec_heplers.rb:
|
|
15
15
|
require 'remarkable/devise'
|
16
16
|
|
17
17
|
## Usage
|
18
|
+
|
19
|
+
Suppose that we require authentication of users. And we want to use Devise. The problem that we face a number of requirements:
|
20
|
+
|
21
|
+
* The user must be authorized by email or login
|
22
|
+
* Password should contain 8 to 20 characters
|
23
|
+
* After 3 unsuccessful attempts to authenticate, the account should be locked in 5 hours
|
24
|
+
* and much more
|
25
|
+
|
26
|
+
Following the BDD way, we first write a specs:
|
18
27
|
|
19
28
|
# spec/models/user_spec.rb
|
20
29
|
describe User do
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
30
|
+
it { should be_a_confirmable(:confirm_within => 2.days) }
|
31
|
+
it { should be_a_rememberable(:remember_for => 2.weeks) }
|
32
|
+
it { should be_a_validatable(:password_length => 8..20) }
|
33
|
+
it { should be_a_timeoutable(:timeout_in => 15.minutes) }
|
34
|
+
it { should be_a_token_authenticatable(:token_authentication_key => :auth_token) }
|
35
|
+
it { should be_a_trackable }
|
36
|
+
it { should be_a_registerable }
|
37
|
+
it { should be_a_recoverable }
|
38
|
+
|
39
|
+
should_be_a_lockable do |o|
|
40
|
+
o.lock_strategy = :failed_attempts
|
41
|
+
o.maximum_attempts => 3
|
42
|
+
o.unlock_strategy => :time
|
43
|
+
end
|
44
|
+
|
45
|
+
should_be_a_database_authenticatable do |o|
|
46
|
+
o.stretches = 20
|
47
|
+
o.encryptor = :clearance_sha1
|
48
|
+
o.params_authenticatable = false
|
49
|
+
o.authentication_keys = [:email, :login]
|
50
|
+
end
|
28
51
|
end
|
29
52
|
|
53
|
+
After number of steps (red/green/refactor) we will get the following
|
54
|
+
result (assuming that you are familiar with how to use Devise and
|
55
|
+
know what to do):
|
56
|
+
|
57
|
+
# app/models/user.rb
|
58
|
+
class User < ActiveRecord::Base
|
59
|
+
devise :database_authenticatable, :stretches => 20, :encryptor => :clearance_sha1,
|
60
|
+
:authentication_keys => [:email, :login], :params_authenticatable => false
|
61
|
+
|
62
|
+
devise :confirmable, :confirm_within => 2.days
|
63
|
+
devise :recoverable
|
64
|
+
devise :rememberable, :remember_for => 2.weeks, :extend_remember_period => true
|
65
|
+
devise :trackable
|
66
|
+
devise :validatable, :password_length => 8..20
|
67
|
+
devise :token_authenticatable, :token_authentication_key => :auth_token
|
68
|
+
devise :timeoutable, :timeout_in => 15.minutes
|
69
|
+
|
70
|
+
devise :lockable, :maximum_attempts => 3, :lock_strategy => :failed_attempts,
|
71
|
+
:unlock_strategy => :time, :unlock_in => 5.hours
|
72
|
+
|
73
|
+
devise :registerable
|
74
|
+
end
|
75
|
+
|
76
|
+
# rspec spec/models/user_spec.rb --format=documentation
|
77
|
+
User
|
78
|
+
should be a confirmable within 2 days
|
79
|
+
should be a rememberable with 14 days remember period and with extendable remember period
|
80
|
+
should be a validatable with password length 8..20
|
81
|
+
should be a timeoutable within 900 seconds
|
82
|
+
should be a token authenticatable with :auth_token as authentication key
|
83
|
+
should be a trackable
|
84
|
+
should be a registerable
|
85
|
+
should be a recoverable
|
86
|
+
should be a lockable with :failed_attempts lock strategy, with :time unlock strategy, with unlock in 5 hours, and with 3 maxumum attempts
|
87
|
+
should be a database authenticatable with [:email, :login] as authentication keys, without params authenticatable, with password stretches 20, and with :clearance_sha1 password encryptor
|
88
|
+
|
89
|
+
## Documentation
|
90
|
+
|
91
|
+
Coming soon
|
92
|
+
|
30
93
|
## See alse
|
31
94
|
|
32
95
|
* [http://github.com/remarkable/remarkable](http://github.com/remarkable/remarkable)
|
@@ -8,6 +8,23 @@ module Remarkable
|
|
8
8
|
subject_class.column_names.include?(column_name)
|
9
9
|
end
|
10
10
|
|
11
|
+
def options_match?
|
12
|
+
actual = get_devise_model_options(@options.keys)
|
13
|
+
|
14
|
+
return actual == @options, :actual => actual.inspect
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_devise_model_options(keys)
|
18
|
+
keys.inject({}) do |hash, key|
|
19
|
+
hash[key] = subject_class.send(key.to_sym)
|
20
|
+
hash
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def interpolation_options
|
25
|
+
{ :options => @options.inspect }
|
26
|
+
end
|
27
|
+
|
11
28
|
def method_missing(m, *args)
|
12
29
|
if m.to_s.match(/has_(.+)_column?/)
|
13
30
|
return has_column?($1)
|
@@ -2,8 +2,12 @@ module Remarkable
|
|
2
2
|
module Devise
|
3
3
|
module Matchers
|
4
4
|
class BeAConfirmableMatcher < Base
|
5
|
+
arguments
|
6
|
+
|
5
7
|
assertion :included?, :has_confirmation_token_column?, :has_confirmed_at_column?,
|
6
|
-
:has_confirmation_sent_at_column?
|
8
|
+
:has_confirmation_sent_at_column?, :options_match?
|
9
|
+
|
10
|
+
optional :confirm_within
|
7
11
|
|
8
12
|
protected
|
9
13
|
|
@@ -12,8 +16,8 @@ module Remarkable
|
|
12
16
|
end
|
13
17
|
end
|
14
18
|
|
15
|
-
def be_a_confirmable
|
16
|
-
BeAConfirmableMatcher.new
|
19
|
+
def be_a_confirmable(*args, &block)
|
20
|
+
BeAConfirmableMatcher.new(*args, &block).spec(self)
|
17
21
|
end
|
18
22
|
end
|
19
23
|
end
|
@@ -1,8 +1,15 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'be_an_authenticatable_matcher')
|
2
|
+
|
1
3
|
module Remarkable
|
2
4
|
module Devise
|
3
5
|
module Matchers
|
4
|
-
class BeADatabaseAuthenticatableMatcher <
|
5
|
-
|
6
|
+
class BeADatabaseAuthenticatableMatcher < BeAnAuthenticatableMatcher
|
7
|
+
arguments
|
8
|
+
|
9
|
+
assertion :included?, :has_email_column?, :has_encrypted_password_column?, :has_password_salt_column?,
|
10
|
+
:options_match?
|
11
|
+
|
12
|
+
optionals :stretches, :encryptor
|
6
13
|
|
7
14
|
protected
|
8
15
|
|
@@ -11,8 +18,8 @@ module Remarkable
|
|
11
18
|
end
|
12
19
|
end
|
13
20
|
|
14
|
-
def be_a_database_authenticatable
|
15
|
-
BeADatabaseAuthenticatableMatcher.new
|
21
|
+
def be_a_database_authenticatable(*args, &block)
|
22
|
+
BeADatabaseAuthenticatableMatcher.new(*args, &block).spec(self)
|
16
23
|
end
|
17
24
|
end
|
18
25
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Remarkable
|
2
|
+
module Devise
|
3
|
+
module Matchers
|
4
|
+
class BeALockableMatcher < Base
|
5
|
+
arguments
|
6
|
+
|
7
|
+
assertion :included?, :has_failed_attempts_column?, :has_unlock_token_column?, :has_locked_at_column?,
|
8
|
+
:options_match?
|
9
|
+
|
10
|
+
optionals :maximum_attempts, :lock_strategy, :unlock_strategy, :unlock_in
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def included?
|
15
|
+
subject_class.ancestors.include?(::Devise::Models::Lockable)
|
16
|
+
end
|
17
|
+
|
18
|
+
def has_failed_attempts_column?
|
19
|
+
return true if subject_class.lock_strategy == :none
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_unlock_token_column?
|
24
|
+
return true unless [:email, :both].include?(subject_class.unlock_strategy)
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def be_a_lockable(*args, &block)
|
30
|
+
BeALockableMatcher.new(*args, &block).spec(self)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Remarkable
|
2
|
+
module Devise
|
3
|
+
module Matchers
|
4
|
+
class BeARegisterableMatcher < Base
|
5
|
+
assertion :included?
|
6
|
+
|
7
|
+
protected
|
8
|
+
|
9
|
+
def included?
|
10
|
+
subject_class.ancestors.include?(::Devise::Models::Registerable)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def be_a_registerable
|
15
|
+
BeARegisterableMatcher.new.spec(self)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -2,7 +2,11 @@ module Remarkable
|
|
2
2
|
module Devise
|
3
3
|
module Matchers
|
4
4
|
class BeARememberableMatcher < Base
|
5
|
-
|
5
|
+
arguments
|
6
|
+
|
7
|
+
assertions :included?, :has_remember_token_column?, :has_remember_created_at_column?, :options_match?
|
8
|
+
|
9
|
+
optionals :remember_for, :extend_remember_period, :cookie_domain
|
6
10
|
|
7
11
|
protected
|
8
12
|
|
@@ -11,8 +15,8 @@ module Remarkable
|
|
11
15
|
end
|
12
16
|
end
|
13
17
|
|
14
|
-
def be_a_rememberable
|
15
|
-
BeARememberableMatcher.new
|
18
|
+
def be_a_rememberable(*args, &block)
|
19
|
+
BeARememberableMatcher.new(*args, &block).spec(self)
|
16
20
|
end
|
17
21
|
end
|
18
22
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Remarkable
|
2
|
+
module Devise
|
3
|
+
module Matchers
|
4
|
+
class BeATimeoutableMatcher < Base
|
5
|
+
arguments
|
6
|
+
|
7
|
+
assertion :included?, :options_match?
|
8
|
+
|
9
|
+
optional :timeout_in
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def included?
|
14
|
+
subject_class.ancestors.include?(::Devise::Models::Timeoutable)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def be_a_timeoutable(*args, &block)
|
19
|
+
BeATimeoutableMatcher.new(*args, &block).spec(self)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -2,8 +2,12 @@ module Remarkable
|
|
2
2
|
module Devise
|
3
3
|
module Matchers
|
4
4
|
class BeATokenAuthenticatableMatcher < Base
|
5
|
-
|
5
|
+
arguments
|
6
|
+
|
7
|
+
assertions :included?, :has_authentication_token_column?, :options_match?
|
6
8
|
|
9
|
+
optionals :token_authentication_key
|
10
|
+
|
7
11
|
protected
|
8
12
|
|
9
13
|
def included?
|
@@ -11,8 +15,8 @@ module Remarkable
|
|
11
15
|
end
|
12
16
|
end
|
13
17
|
|
14
|
-
def be_a_token_authenticatable
|
15
|
-
BeATokenAuthenticatableMatcher.new
|
18
|
+
def be_a_token_authenticatable(*args, &block)
|
19
|
+
BeATokenAuthenticatableMatcher.new(*args, &block).spec(self)
|
16
20
|
end
|
17
21
|
end
|
18
22
|
end
|
@@ -2,7 +2,11 @@ module Remarkable
|
|
2
2
|
module Devise
|
3
3
|
module Matchers
|
4
4
|
class BeAValidatableMatcher < Base
|
5
|
-
|
5
|
+
arguments
|
6
|
+
|
7
|
+
assertion :included?, :options_match?
|
8
|
+
|
9
|
+
optionals :password_length, :email_regexp
|
6
10
|
|
7
11
|
protected
|
8
12
|
|
@@ -11,8 +15,8 @@ module Remarkable
|
|
11
15
|
end
|
12
16
|
end
|
13
17
|
|
14
|
-
def be_a_validatable
|
15
|
-
BeAValidatableMatcher.new
|
18
|
+
def be_a_validatable(*args, &block)
|
19
|
+
BeAValidatableMatcher.new(*args, &block).spec(self)
|
16
20
|
end
|
17
21
|
end
|
18
22
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Remarkable
|
2
|
+
module Devise
|
3
|
+
module Matchers
|
4
|
+
class BeAnAuthenticatableMatcher < Base
|
5
|
+
arguments
|
6
|
+
|
7
|
+
assertion :has_authenticatable_module_included?, :options_match?
|
8
|
+
|
9
|
+
optionals :authentication_keys, :params_authenticatable
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def has_authenticatable_module_included?
|
14
|
+
subject_class.ancestors.include?(::Devise::Models::Authenticatable)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/locale/en.yml
CHANGED
@@ -4,18 +4,37 @@ en:
|
|
4
4
|
be_a_database_authenticatable:
|
5
5
|
description: "be a database authenticatable"
|
6
6
|
expectations:
|
7
|
+
has_authenticatable_module_included: "%{subject_name} to have Devise :authenticatable model"
|
7
8
|
included: "%{subject_name} to include Devise :database_authenticatable model"
|
9
|
+
options_match: "%{subject_name} to be a database authenticatable with options %{options}, got %{actual}"
|
8
10
|
has_email_column: "%{subject_name} to have email column"
|
9
11
|
has_encrypted_password_column: "%{subject_name} to have encrypted_password column"
|
10
12
|
has_password_salt_column: "%{subject_name} to have password_salt column"
|
13
|
+
stretches_match: "%{subject_name} to have password stretches equal to %{stretches}, got %{actual}"
|
14
|
+
encryptor_match: "%{subject_name} to have %{encryptor} password encryptor, got %{actual}"
|
15
|
+
optionals:
|
16
|
+
stretches:
|
17
|
+
positive: "with password stretches %{inspect}"
|
18
|
+
encryptor:
|
19
|
+
positive: "with %{inspect} password encryptor"
|
20
|
+
authentication_keys:
|
21
|
+
positive: "with %{inspect} as authentication keys"
|
22
|
+
params_authenticatable:
|
23
|
+
positive: "with params authenticatable"
|
24
|
+
negative: "without params authenticatable"
|
11
25
|
|
12
26
|
be_a_confirmable:
|
13
27
|
description: "be a confirmable"
|
14
28
|
expectations:
|
15
29
|
included: "%{subject_name} to include Devise :confirmable model"
|
30
|
+
options_match: "%{subject_name} to be a confirmable with options %{options}, got %{actual}"
|
16
31
|
has_confirmation_token_column: "%{subject_name} to have confirmation_token column"
|
17
32
|
has_confirmed_at_column: "%{subject_name} to have confirmed_at column"
|
18
33
|
has_confirmation_sent_at_column: "%{subject_name} to have confirmation_sent_at column"
|
34
|
+
confirmation_period_matches: "%{subject_name} to have %{confirm_within} of confirmation period, got %{actual}"
|
35
|
+
optionals:
|
36
|
+
confirm_within:
|
37
|
+
positive: "within %{inspect}"
|
19
38
|
|
20
39
|
be_a_recoverable:
|
21
40
|
description: "be a recoverable"
|
@@ -29,6 +48,15 @@ en:
|
|
29
48
|
included: "%{subject_name} to include Devise :rememberable model"
|
30
49
|
has_remember_token_column: "%{subject_name} to have remember_token column"
|
31
50
|
has_remember_created_at_column: "%{subject_name} to have remember_created_at column"
|
51
|
+
options_match: "%{subject_name} to be rememberable with options %{options}, got %{actual}"
|
52
|
+
optionals:
|
53
|
+
remember_for:
|
54
|
+
positive: "with %{inspect} remember period"
|
55
|
+
extend_remember_period:
|
56
|
+
positive: "with extendable remember period"
|
57
|
+
negative: "without extandable remember period"
|
58
|
+
cookie_domain:
|
59
|
+
positive: "with %{inspect} cookie domain"
|
32
60
|
|
33
61
|
be_a_trackable:
|
34
62
|
description: "be a trackable"
|
@@ -44,9 +72,51 @@ en:
|
|
44
72
|
description: "be a validatable"
|
45
73
|
expectations:
|
46
74
|
included: "%{subject_name} to include Devise :validatable model"
|
75
|
+
options_match: "%{subject_name} to be a validatable with options %{options}, got %{actual}"
|
76
|
+
optionals:
|
77
|
+
password_length:
|
78
|
+
positive: "with password length %{inspect}"
|
79
|
+
email_regexp:
|
80
|
+
positive: "with email regexp %{inspect}"
|
47
81
|
|
48
82
|
be_a_token_authenticatable:
|
49
83
|
description: "be a token authenticatable"
|
50
84
|
expectations:
|
51
85
|
included: "%{subject_name} to include Devise :token_authenticatable model"
|
52
86
|
has_authentication_token_column: "%{subject_name} to have authentication_token column"
|
87
|
+
options_match: "%{subject_name} to be a token authenticatable with options %{options}, got %{actual}"
|
88
|
+
optionals:
|
89
|
+
token_authentication_key:
|
90
|
+
positive: "with %{inspect} as token authentication key"
|
91
|
+
|
92
|
+
be_a_timeoutable:
|
93
|
+
description: "be a timeoutable"
|
94
|
+
expectations:
|
95
|
+
included: "%{subject_name} to include Devise :timeoutable model"
|
96
|
+
options_match: "%{subject_name} to be a timeoutable with options %{options}, got %{actual}"
|
97
|
+
optionals:
|
98
|
+
timeout_in:
|
99
|
+
positive: "within %{inspect}"
|
100
|
+
|
101
|
+
be_a_lockable:
|
102
|
+
description: "be a lockable"
|
103
|
+
expectations:
|
104
|
+
included: "%{subject_name} to have Devise :lockable model"
|
105
|
+
has_failed_attempts_column: "%{subject_name} to have failed_attempts column"
|
106
|
+
has_unlock_token_column: "%{subject_name} to have unlock_token column"
|
107
|
+
has_locked_at_column: "%{subject_name} to have locked_at column"
|
108
|
+
options_match: "%{subject_name} to be a lockable with options %{options}, got %{actual}"
|
109
|
+
optionals:
|
110
|
+
maximum_attempts:
|
111
|
+
positive: "with %{inspect} maximum attempts"
|
112
|
+
lock_strategy:
|
113
|
+
positive: "with %{inspect} lock strategy"
|
114
|
+
unlock_strategy:
|
115
|
+
positive: "with %{inspect} unlock strategy"
|
116
|
+
unlock_in:
|
117
|
+
positive: "with unlock in %{inspect}"
|
118
|
+
|
119
|
+
be_a_registerable:
|
120
|
+
description: "be a registerable"
|
121
|
+
expectations:
|
122
|
+
included: "%{subject_name} to have Devise :registerable model"
|