remarkable_devise 1.0.0.alpha3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. data/README.markdown +70 -7
  2. data/lib/remarkable/devise/matchers/base_matcher.rb +17 -0
  3. data/lib/remarkable/devise/matchers/be_a_confirmable_matcher.rb +7 -3
  4. data/lib/remarkable/devise/matchers/be_a_database_authenticatable_matcher.rb +11 -4
  5. data/lib/remarkable/devise/matchers/be_a_lockable_matcher.rb +34 -0
  6. data/lib/remarkable/devise/matchers/be_a_registerable_matcher.rb +19 -0
  7. data/lib/remarkable/devise/matchers/be_a_rememberable_matcher.rb +7 -3
  8. data/lib/remarkable/devise/matchers/be_a_timeoutable_matcher.rb +23 -0
  9. data/lib/remarkable/devise/matchers/be_a_token_authenticatable_matcher.rb +7 -3
  10. data/lib/remarkable/devise/matchers/be_a_validatable_matcher.rb +7 -3
  11. data/lib/remarkable/devise/matchers/be_an_authenticatable_matcher.rb +19 -0
  12. data/lib/remarkable/devise/version.rb +1 -1
  13. data/locale/en.yml +70 -0
  14. data/remarkable_devise.gemspec +1 -1
  15. data/spec/example_models.rb +10 -2
  16. data/spec/examples/user_spec.rb +24 -0
  17. data/spec/matchers/be_a_confirmable_spec.rb +32 -4
  18. data/spec/matchers/be_a_database_authenticatable_spec.rb +55 -5
  19. data/spec/matchers/be_a_lockable_spec.rb +274 -0
  20. data/spec/matchers/be_a_registerable_spec.rb +27 -0
  21. data/spec/matchers/be_a_rememberable_spec.rb +81 -1
  22. data/spec/matchers/be_a_timeoutable_spec.rb +59 -0
  23. data/spec/matchers/be_a_token_authenticatable_spec.rb +33 -1
  24. data/spec/matchers/be_a_validatable_spec.rb +58 -5
  25. data/spec/matchers_spec.rb +23 -5
  26. data/spec/shared_examples.rb +122 -0
  27. data/spec/spec_helper.rb +13 -4
  28. metadata +23 -12
@@ -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
- should_be_a_database_authenticatable
22
- should_be_a_token_authenticatable
23
- should_be_a_confirmable
24
- should_be_a_recoverable
25
- should_be_a_rememberable
26
- should_be_a_trackable
27
- should_be_a_validatable
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 < Base
5
- assertion :included?, :has_email_column?, :has_encrypted_password_column?, :has_password_salt_column?
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
- assertions :included?, :has_remember_token_column?, :has_remember_created_at_column?
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
- assertions :included?, :has_authentication_token_column?
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
- assertion :included?
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
@@ -1,7 +1,7 @@
1
1
  module Remarkable
2
2
  module Devise
3
3
  module Version
4
- STRING = '1.0.0.alpha3'
4
+ STRING = '1.0.0'
5
5
  end
6
6
  end
7
7
  end
@@ -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"