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.
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"