devise 3.3.0 → 3.5.10

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of devise might be problematic. Click here for more details.

Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +29 -20
  3. data/CHANGELOG.md +219 -102
  4. data/CODE_OF_CONDUCT.md +22 -0
  5. data/CONTRIBUTING.md +2 -0
  6. data/Gemfile +3 -2
  7. data/Gemfile.lock +101 -80
  8. data/MIT-LICENSE +1 -1
  9. data/README.md +87 -43
  10. data/Rakefile +2 -1
  11. data/app/controllers/devise/confirmations_controller.rb +5 -1
  12. data/app/controllers/devise/omniauth_callbacks_controller.rb +4 -0
  13. data/app/controllers/devise/passwords_controller.rb +14 -4
  14. data/app/controllers/devise/registrations_controller.rb +10 -11
  15. data/app/controllers/devise/sessions_controller.rb +7 -2
  16. data/app/controllers/devise/unlocks_controller.rb +3 -0
  17. data/app/controllers/devise_controller.rb +44 -21
  18. data/app/mailers/devise/mailer.rb +4 -0
  19. data/app/views/devise/confirmations/new.html.erb +7 -3
  20. data/app/views/devise/mailer/password_change.html.erb +3 -0
  21. data/app/views/devise/passwords/edit.html.erb +14 -5
  22. data/app/views/devise/passwords/new.html.erb +7 -3
  23. data/app/views/devise/registrations/edit.html.erb +19 -9
  24. data/app/views/devise/registrations/new.html.erb +18 -7
  25. data/app/views/devise/sessions/new.html.erb +15 -6
  26. data/app/views/devise/shared/{_links.erb → _links.html.erb} +1 -1
  27. data/app/views/devise/unlocks/new.html.erb +7 -3
  28. data/config/locales/en.yml +4 -2
  29. data/devise.gemspec +2 -2
  30. data/gemfiles/Gemfile.rails-3.2-stable.lock +54 -48
  31. data/gemfiles/Gemfile.rails-4.0-stable +1 -0
  32. data/gemfiles/Gemfile.rails-4.0-stable.lock +63 -59
  33. data/gemfiles/{Gemfile.rails-head → Gemfile.rails-4.1-stable} +3 -5
  34. data/gemfiles/Gemfile.rails-4.1-stable.lock +171 -0
  35. data/gemfiles/Gemfile.rails-4.2-stable +30 -0
  36. data/gemfiles/Gemfile.rails-4.2-stable.lock +193 -0
  37. data/lib/devise/controllers/helpers.rb +12 -6
  38. data/lib/devise/controllers/rememberable.rb +9 -2
  39. data/lib/devise/controllers/sign_in_out.rb +2 -8
  40. data/lib/devise/controllers/store_location.rb +3 -1
  41. data/lib/devise/controllers/url_helpers.rb +7 -9
  42. data/lib/devise/encryptor.rb +22 -0
  43. data/lib/devise/failure_app.rb +56 -14
  44. data/lib/devise/hooks/timeoutable.rb +5 -7
  45. data/lib/devise/mapping.rb +2 -1
  46. data/lib/devise/models/authenticatable.rb +28 -28
  47. data/lib/devise/models/confirmable.rb +51 -17
  48. data/lib/devise/models/database_authenticatable.rb +17 -11
  49. data/lib/devise/models/lockable.rb +7 -3
  50. data/lib/devise/models/recoverable.rb +23 -15
  51. data/lib/devise/models/rememberable.rb +56 -22
  52. data/lib/devise/models/timeoutable.rb +0 -6
  53. data/lib/devise/models/trackable.rb +1 -2
  54. data/lib/devise/models/validatable.rb +3 -3
  55. data/lib/devise/models.rb +1 -1
  56. data/lib/devise/rails/routes.rb +33 -27
  57. data/lib/devise/rails.rb +1 -1
  58. data/lib/devise/strategies/authenticatable.rb +8 -6
  59. data/lib/devise/strategies/database_authenticatable.rb +2 -1
  60. data/lib/devise/strategies/rememberable.rb +13 -3
  61. data/lib/devise/test_helpers.rb +2 -2
  62. data/lib/devise/version.rb +1 -1
  63. data/lib/devise.rb +39 -37
  64. data/lib/generators/active_record/devise_generator.rb +2 -1
  65. data/lib/generators/active_record/templates/migration.rb +1 -1
  66. data/lib/generators/active_record/templates/migration_existing.rb +1 -1
  67. data/lib/generators/devise/controllers_generator.rb +44 -0
  68. data/lib/generators/devise/views_generator.rb +14 -3
  69. data/lib/generators/templates/controllers/README +14 -0
  70. data/lib/generators/templates/controllers/confirmations_controller.rb +28 -0
  71. data/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +28 -0
  72. data/lib/generators/templates/controllers/passwords_controller.rb +32 -0
  73. data/lib/generators/templates/controllers/registrations_controller.rb +60 -0
  74. data/lib/generators/templates/controllers/sessions_controller.rb +25 -0
  75. data/lib/generators/templates/controllers/unlocks_controller.rb +28 -0
  76. data/lib/generators/templates/devise.rb +19 -13
  77. data/lib/generators/templates/markerb/confirmation_instructions.markerb +1 -1
  78. data/lib/generators/templates/markerb/password_change.markerb +3 -0
  79. data/lib/generators/templates/markerb/reset_password_instructions.markerb +1 -1
  80. data/lib/generators/templates/markerb/unlock_instructions.markerb +1 -1
  81. data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +1 -1
  82. data/lib/generators/templates/simple_form_for/registrations/new.html.erb +1 -1
  83. data/lib/generators/templates/simple_form_for/sessions/new.html.erb +2 -2
  84. data/test/controllers/custom_registrations_controller_test.rb +6 -1
  85. data/test/controllers/helper_methods_test.rb +21 -0
  86. data/test/controllers/helpers_test.rb +5 -0
  87. data/test/controllers/inherited_controller_i18n_messages_test.rb +51 -0
  88. data/test/controllers/internal_helpers_test.rb +10 -4
  89. data/test/controllers/load_hooks_controller_test.rb +19 -0
  90. data/test/controllers/passwords_controller_test.rb +1 -1
  91. data/test/controllers/sessions_controller_test.rb +3 -3
  92. data/test/controllers/url_helpers_test.rb +6 -0
  93. data/test/devise_test.rb +3 -3
  94. data/test/failure_app_test.rb +47 -0
  95. data/test/generators/controllers_generator_test.rb +48 -0
  96. data/test/generators/views_generator_test.rb +8 -1
  97. data/test/helpers/devise_helper_test.rb +9 -12
  98. data/test/integration/authenticatable_test.rb +1 -1
  99. data/test/integration/database_authenticatable_test.rb +11 -0
  100. data/test/integration/http_authenticatable_test.rb +1 -1
  101. data/test/integration/omniauthable_test.rb +12 -10
  102. data/test/integration/recoverable_test.rb +13 -0
  103. data/test/integration/rememberable_test.rb +50 -3
  104. data/test/integration/timeoutable_test.rb +13 -18
  105. data/test/mailers/confirmation_instructions_test.rb +1 -1
  106. data/test/mapping_test.rb +7 -0
  107. data/test/models/authenticatable_test.rb +10 -0
  108. data/test/models/confirmable_test.rb +99 -42
  109. data/test/models/database_authenticatable_test.rb +20 -0
  110. data/test/models/lockable_test.rb +45 -17
  111. data/test/models/recoverable_test.rb +62 -7
  112. data/test/models/rememberable_test.rb +68 -97
  113. data/test/models/validatable_test.rb +5 -5
  114. data/test/models_test.rb +15 -6
  115. data/test/rails_app/app/active_record/user_without_email.rb +8 -0
  116. data/test/rails_app/app/controllers/admins_controller.rb +0 -5
  117. data/test/rails_app/app/controllers/custom/registrations_controller.rb +10 -0
  118. data/test/rails_app/app/mailers/users/from_proc_mailer.rb +3 -0
  119. data/test/rails_app/app/mailers/users/mailer.rb +0 -9
  120. data/test/rails_app/app/mailers/users/reply_to_mailer.rb +4 -0
  121. data/test/rails_app/app/mongoid/user_without_email.rb +33 -0
  122. data/test/rails_app/config/application.rb +1 -1
  123. data/test/rails_app/config/environments/production.rb +6 -2
  124. data/test/rails_app/config/environments/test.rb +7 -2
  125. data/test/rails_app/config/initializers/devise.rb +12 -15
  126. data/test/rails_app/config/routes.rb +6 -3
  127. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +2 -2
  128. data/test/rails_app/lib/shared_user.rb +1 -1
  129. data/test/rails_app/lib/shared_user_without_email.rb +26 -0
  130. data/test/rails_test.rb +9 -0
  131. data/test/support/helpers.rb +13 -6
  132. data/test/support/integration.rb +2 -2
  133. data/test/test_helper.rb +5 -0
  134. data/test/test_helpers_test.rb +22 -7
  135. data/test/test_models.rb +2 -2
  136. data/test/time_helpers.rb +137 -0
  137. metadata +58 -8
  138. data/gemfiles/Gemfile.rails-head.lock +0 -190
@@ -31,7 +31,7 @@ Devise.setup do |config|
31
31
  # session. If you need permissions, you should implement that in a before filter.
32
32
  # You can also supply hash where the value is a boolean expliciting if authentication
33
33
  # should be aborted or not if the value is not present. By default is empty.
34
- # config.authentication_keys = [ :email ]
34
+ # config.authentication_keys = [:email]
35
35
 
36
36
  # Configure parameters from the request object used for authentication. Each entry
37
37
  # given should be a request method and it will automatically be passed to
@@ -43,12 +43,12 @@ Devise.setup do |config|
43
43
  # Configure which authentication keys should be case-insensitive.
44
44
  # These keys will be downcased upon creating or modifying a user and when used
45
45
  # to authenticate or find a user. Default is :email.
46
- config.case_insensitive_keys = [ :email ]
46
+ config.case_insensitive_keys = [:email]
47
47
 
48
48
  # Configure which authentication keys should have whitespace stripped.
49
49
  # These keys will have whitespace before and after removed upon creating or
50
50
  # modifying a user and when used to authenticate or find a user. Default is :email.
51
- config.strip_whitespace_keys = [ :email ]
51
+ config.strip_whitespace_keys = [:email]
52
52
 
53
53
  # Tell if authentication through request.params is enabled. True by default.
54
54
  # config.params_authenticatable = true
@@ -77,21 +77,18 @@ Devise.setup do |config|
77
77
  # config.allow_unconfirmed_access_for = 2.days
78
78
 
79
79
  # Defines which key will be used when confirming an account
80
- # config.confirmation_keys = [ :email ]
80
+ # config.confirmation_keys = [:email]
81
81
 
82
82
  # ==> Configuration for :rememberable
83
83
  # The time the user will be remembered without asking for credentials again.
84
84
  # config.remember_for = 2.weeks
85
85
 
86
- # If true, a valid remember token can be re-used between multiple browsers.
87
- # config.remember_across_browsers = true
88
-
89
86
  # If true, extends the user's remember period when remembered via cookie.
90
87
  # config.extend_remember_period = false
91
88
 
92
89
  # ==> Configuration for :validatable
93
- # Range for password length. Default is 8..128.
94
- # config.password_length = 8..128
90
+ # Range for password length. Default is 8..72.
91
+ # config.password_length = 8..72
95
92
 
96
93
  # Regex to use to validate the email address
97
94
  # config.email_regexp = /^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i
@@ -108,7 +105,7 @@ Devise.setup do |config|
108
105
  # config.lock_strategy = :failed_attempts
109
106
 
110
107
  # Defines which key will be used when locking and unlocking an account
111
- # config.unlock_keys = [ :email ]
108
+ # config.unlock_keys = [:email]
112
109
 
113
110
  # Defines which strategy will be used to unlock an account.
114
111
  # :email = Sends an unlock link to the user email
@@ -127,20 +124,20 @@ Devise.setup do |config|
127
124
  # ==> Configuration for :recoverable
128
125
  #
129
126
  # Defines which key will be used when recovering the password for an account
130
- # config.reset_password_keys = [ :email ]
127
+ # config.reset_password_keys = [:email]
131
128
 
132
129
  # Time interval you can reset your password with a reset password key.
133
130
  # Don't put a too small interval or your users won't have the time to
134
131
  # change their passwords.
135
132
  config.reset_password_within = 2.hours
136
133
 
134
+ # When set to false, does not sign a user in automatically after their password is
135
+ # reset. Defaults to true, so a user is signed in automatically after a reset.
136
+ # config.sign_in_after_reset_password = true
137
+
137
138
  # Setup a pepper to generate the encrypted password.
138
139
  config.pepper = "d142367154e5beacca404b1a6a4f8bc52c6fdcfa3ccc3cf8eb49f3458a688ee6ac3b9fae488432a3bfca863b8a90008368a9f3a3dfbe5a962e64b6ab8f3a3a1a"
139
140
 
140
- # ==> Configuration for :token_authenticatable
141
- # Defines name of the authentication token params key
142
- # config.token_authentication_key = :auth_token
143
-
144
141
  # ==> Scopes configuration
145
142
  # Turn scoped views on. Before rendering "sessions/new", it will first check for
146
143
  # "users/sessions/new". It's turned off by default because it's slower if you
@@ -13,9 +13,7 @@ Rails.application.routes.draw do
13
13
  end
14
14
  end
15
15
 
16
- resources :admins, only: [:index] do
17
- get :expire, on: :member
18
- end
16
+ resources :admins, only: [:index]
19
17
 
20
18
  # Users scope
21
19
  devise_for :users, controllers: { omniauth_callbacks: "users/omniauth_callbacks" }
@@ -30,6 +28,11 @@ Rails.application.routes.draw do
30
28
  router_name: :fake_engine,
31
29
  module: :devise
32
30
 
31
+ devise_for :user_without_email,
32
+ class_name: 'UserWithoutEmail',
33
+ router_name: :main_app,
34
+ module: :devise
35
+
33
36
  as :user do
34
37
  get "/as/sign_in", to: "devise/sessions#new"
35
38
  end
@@ -33,7 +33,7 @@ class CreateTables < ActiveRecord::Migration
33
33
  t.string :unlock_token # Only if unlock strategy is :email or :both
34
34
  t.datetime :locked_at
35
35
 
36
- t.timestamps
36
+ t.timestamps null: false
37
37
  end
38
38
 
39
39
  create_table :admins do |t|
@@ -60,7 +60,7 @@ class CreateTables < ActiveRecord::Migration
60
60
  ## Attribute for testing route blocks
61
61
  t.boolean :active, default: false
62
62
 
63
- t.timestamps
63
+ t.timestamps null: false
64
64
  end
65
65
  end
66
66
 
@@ -4,7 +4,7 @@ module SharedUser
4
4
  included do
5
5
  devise :database_authenticatable, :confirmable, :lockable, :recoverable,
6
6
  :registerable, :rememberable, :timeoutable,
7
- :trackable, :validatable, :omniauthable, password_length: 7..128
7
+ :trackable, :validatable, :omniauthable, password_length: 7..72
8
8
 
9
9
  attr_accessor :other_key
10
10
 
@@ -0,0 +1,26 @@
1
+ module SharedUserWithoutEmail
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ # NOTE: This is missing :validatable and :confirmable, as they both require
6
+ # an email field at the moment. It is also missing :omniauthable because that
7
+ # adds unnecessary complexity to the setup
8
+ devise :database_authenticatable, :lockable, :recoverable,
9
+ :registerable, :rememberable, :timeoutable,
10
+ :trackable
11
+ end
12
+
13
+ # This test stub is a bit rubbish because it's tied very closely to the
14
+ # implementation where we care about this one case. However, completely
15
+ # removing the email field breaks "recoverable" tests completely, so we are
16
+ # just taking the approach here that "email" is something that is a not an
17
+ # ActiveRecord field.
18
+ def email_changed?
19
+ raise NoMethodError
20
+ end
21
+
22
+ def respond_to?(method_name, include_all=false)
23
+ return false if method_name.to_sym == :email_changed?
24
+ super(method_name, include_all)
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ require 'test_helper'
2
+
3
+ class RailsTest < ActiveSupport::TestCase
4
+ test 'correct initializer position' do
5
+ initializer = Devise::Engine.initializers.detect { |i| i.name == 'devise.omniauth' }
6
+ assert_equal :load_config_initializers, initializer.after
7
+ assert_equal :build_middleware_stack, initializer.before
8
+ end
9
+ end
@@ -8,12 +8,15 @@ class ActiveSupport::TestCase
8
8
  end
9
9
 
10
10
  def store_translations(locale, translations, &block)
11
- begin
12
- I18n.backend.store_translations(locale, translations)
13
- yield
14
- ensure
15
- I18n.reload!
16
- end
11
+ # Calling 'available_locales' before storing the translations to ensure
12
+ # that the I18n backend will be initialized before we store our custom
13
+ # translations, so they will always override the translations for the
14
+ # YML file.
15
+ I18n.available_locales
16
+ I18n.backend.store_translations(locale, translations)
17
+ yield
18
+ ensure
19
+ I18n.reload!
17
20
  end
18
21
 
19
22
  def generate_unique_email
@@ -43,6 +46,10 @@ class ActiveSupport::TestCase
43
46
  Admin.create!(valid_attributes)
44
47
  end
45
48
 
49
+ def create_user_without_email(attributes={})
50
+ UserWithoutEmail.create!(valid_attributes(attributes))
51
+ end
52
+
46
53
  # Execute the block setting the given values and restoring old values after
47
54
  # the block is executed.
48
55
  def swap(object, new_values)
@@ -15,7 +15,7 @@ class ActionDispatch::IntegrationTest
15
15
  created_at: Time.now.utc
16
16
  )
17
17
  user.update_attribute(:confirmation_sent_at, options[:confirmation_sent_at]) if options[:confirmation_sent_at]
18
- user.confirm! unless options[:confirm] == false
18
+ user.confirm unless options[:confirm] == false
19
19
  user.lock_access! if options[:locked] == true
20
20
  user
21
21
  end
@@ -28,7 +28,7 @@ class ActionDispatch::IntegrationTest
28
28
  password: '123456', password_confirmation: '123456',
29
29
  active: options[:active]
30
30
  )
31
- admin.confirm! unless options[:confirm] == false
31
+ admin.confirm unless options[:confirm] == false
32
32
  admin
33
33
  end
34
34
  end
data/test/test_helper.rb CHANGED
@@ -17,6 +17,10 @@ Webrat.configure do |config|
17
17
  config.open_error_files = false
18
18
  end
19
19
 
20
+ if ActiveSupport.respond_to?(:test_order)
21
+ ActiveSupport.test_order = :random
22
+ end
23
+
20
24
  OmniAuth.config.logger = Logger.new('/dev/null')
21
25
 
22
26
  # Add support to load paths so we can overwrite broken webrat setup
@@ -27,3 +31,4 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
27
31
  require "rails/generators/test_case"
28
32
  require "generators/devise/install_generator"
29
33
  require "generators/devise/views_generator"
34
+ require "generators/devise/controllers_generator"
@@ -34,7 +34,7 @@ class TestHelpersTest < ActionController::TestCase
34
34
 
35
35
  test "does not redirect with valid user" do
36
36
  user = create_user
37
- user.confirm!
37
+ user.confirm
38
38
 
39
39
  sign_in user
40
40
  get :index
@@ -46,7 +46,7 @@ class TestHelpersTest < ActionController::TestCase
46
46
  assert_response :redirect
47
47
 
48
48
  user = create_user
49
- user.confirm!
49
+ user.confirm
50
50
 
51
51
  sign_in user
52
52
  get :index
@@ -55,7 +55,7 @@ class TestHelpersTest < ActionController::TestCase
55
55
 
56
56
  test "redirects if valid user signed out" do
57
57
  user = create_user
58
- user.confirm!
58
+ user.confirm
59
59
 
60
60
  sign_in user
61
61
  get :index
@@ -105,7 +105,7 @@ class TestHelpersTest < ActionController::TestCase
105
105
  end
106
106
 
107
107
  user = create_user
108
- user.confirm!
108
+ user.confirm
109
109
  sign_in user
110
110
  ensure
111
111
  Warden::Manager._after_set_user.pop
@@ -118,7 +118,7 @@ class TestHelpersTest < ActionController::TestCase
118
118
  flunk "callback was called while it should not"
119
119
  end
120
120
  user = create_user
121
- user.confirm!
121
+ user.confirm
122
122
 
123
123
  sign_in user
124
124
  sign_out user
@@ -146,7 +146,7 @@ class TestHelpersTest < ActionController::TestCase
146
146
 
147
147
  test "allows to sign in with different users" do
148
148
  first_user = create_user
149
- first_user.confirm!
149
+ first_user.confirm
150
150
 
151
151
  sign_in first_user
152
152
  get :index
@@ -154,10 +154,25 @@ class TestHelpersTest < ActionController::TestCase
154
154
  sign_out first_user
155
155
 
156
156
  second_user = create_user
157
- second_user.confirm!
157
+ second_user.confirm
158
158
 
159
159
  sign_in second_user
160
160
  get :index
161
161
  assert_match /User ##{second_user.id}/, @response.body
162
162
  end
163
+
164
+ test "creates a new warden proxy if the request object has changed" do
165
+ old_warden_proxy = warden
166
+ @request = ActionController::TestRequest.new
167
+ new_warden_proxy = warden
168
+
169
+ assert_not_equal old_warden_proxy, new_warden_proxy
170
+ end
171
+
172
+ test "doesn't create a new warden proxy if the request object hasn't changed" do
173
+ old_warden_proxy = warden
174
+ new_warden_proxy = warden
175
+
176
+ assert_equal old_warden_proxy, new_warden_proxy
177
+ end
163
178
  end
data/test/test_models.rb CHANGED
@@ -20,8 +20,8 @@ class UserWithCustomEncryption < User
20
20
  end
21
21
 
22
22
  class UserWithVirtualAttributes < User
23
- devise case_insensitive_keys: [ :email, :email_confirmation ]
24
- validates :email, presence: true, confirmation: {on: :create}
23
+ devise case_insensitive_keys: [:email, :email_confirmation]
24
+ validates :email, presence: true, confirmation: { on: :create }
25
25
  end
26
26
 
27
27
  class Several < Admin
@@ -0,0 +1,137 @@
1
+ # A copy of Rails time helpers. With this file we can support the `travel_to`
2
+ # helper for Rails versions prior 4.1.
3
+ # File origin: https://github.com/rails/rails/blob/52ce6ece8c8f74064bb64e0a0b1ddd83092718e1/activesupport/lib/active_support/testing/time_helpers.rb
4
+ module ActiveSupport
5
+ module Testing
6
+ class SimpleStubs # :nodoc:
7
+ Stub = Struct.new(:object, :method_name, :original_method)
8
+
9
+ def initialize
10
+ @stubs = {}
11
+ end
12
+
13
+ def stub_object(object, method_name, return_value)
14
+ key = [object.object_id, method_name]
15
+
16
+ if stub = @stubs[key]
17
+ unstub_object(stub)
18
+ end
19
+
20
+ new_name = "__simple_stub__#{method_name}"
21
+
22
+ @stubs[key] = Stub.new(object, method_name, new_name)
23
+
24
+ object.singleton_class.send :alias_method, new_name, method_name
25
+ object.define_singleton_method(method_name) { return_value }
26
+ end
27
+
28
+ def unstub_all!
29
+ @stubs.each_value do |stub|
30
+ unstub_object(stub)
31
+ end
32
+ @stubs = {}
33
+ end
34
+
35
+ private
36
+
37
+ def unstub_object(stub)
38
+ singleton_class = stub.object.singleton_class
39
+ singleton_class.send :undef_method, stub.method_name
40
+ singleton_class.send :alias_method, stub.method_name, stub.original_method
41
+ singleton_class.send :undef_method, stub.original_method
42
+ end
43
+ end
44
+
45
+ # Contains helpers that help you test passage of time.
46
+ module TimeHelpers
47
+ # Changes current time to the time in the future or in the past by a given time difference by
48
+ # stubbing +Time.now+, +Date.today+, and +DateTime.now+.
49
+ #
50
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
51
+ # travel 1.day
52
+ # Time.current # => Sun, 10 Nov 2013 15:34:49 EST -05:00
53
+ # Date.current # => Sun, 10 Nov 2013
54
+ # DateTime.current # => Sun, 10 Nov 2013 15:34:49 -0500
55
+ #
56
+ # This method also accepts a block, which will return the current time back to its original
57
+ # state at the end of the block:
58
+ #
59
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
60
+ # travel 1.day do
61
+ # User.create.created_at # => Sun, 10 Nov 2013 15:34:49 EST -05:00
62
+ # end
63
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
64
+ def travel(duration, &block)
65
+ travel_to Time.now + duration, &block
66
+ end
67
+
68
+ # Changes current time to the given time by stubbing +Time.now+,
69
+ # +Date.today+, and +DateTime.now+ to return the time or date passed into this method.
70
+ #
71
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
72
+ # travel_to Time.new(2004, 11, 24, 01, 04, 44)
73
+ # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
74
+ # Date.current # => Wed, 24 Nov 2004
75
+ # DateTime.current # => Wed, 24 Nov 2004 01:04:44 -0500
76
+ #
77
+ # Dates are taken as their timestamp at the beginning of the day in the
78
+ # application time zone. <tt>Time.current</tt> returns said timestamp,
79
+ # and <tt>Time.now</tt> its equivalent in the system time zone. Similarly,
80
+ # <tt>Date.current</tt> returns a date equal to the argument, and
81
+ # <tt>Date.today</tt> the date according to <tt>Time.now</tt>, which may
82
+ # be different. (Note that you rarely want to deal with <tt>Time.now</tt>,
83
+ # or <tt>Date.today</tt>, in order to honor the application time zone
84
+ # please always use <tt>Time.current</tt> and <tt>Date.current</tt>.)
85
+ #
86
+ # Note that the usec for the time passed will be set to 0 to prevent rounding
87
+ # errors with external services, like MySQL (which will round instead of floor,
88
+ # leading to off-by-one-second errors).
89
+ #
90
+ # This method also accepts a block, which will return the current time back to its original
91
+ # state at the end of the block:
92
+ #
93
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
94
+ # travel_to Time.new(2004, 11, 24, 01, 04, 44) do
95
+ # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
96
+ # end
97
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
98
+ def travel_to(date_or_time)
99
+ if date_or_time.is_a?(Date) && !date_or_time.is_a?(DateTime)
100
+ now = date_or_time.midnight.to_time
101
+ else
102
+ now = date_or_time.to_time.change(usec: 0)
103
+ end
104
+
105
+ simple_stubs.stub_object(Time, :now, now)
106
+ simple_stubs.stub_object(Date, :today, now.to_date)
107
+ simple_stubs.stub_object(DateTime, :now, now.to_datetime)
108
+
109
+ if block_given?
110
+ begin
111
+ yield
112
+ ensure
113
+ travel_back
114
+ end
115
+ end
116
+ end
117
+
118
+ # Returns the current time back to its original state, by removing the stubs added by
119
+ # `travel` and `travel_to`.
120
+ #
121
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
122
+ # travel_to Time.new(2004, 11, 24, 01, 04, 44)
123
+ # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
124
+ # travel_back
125
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
126
+ def travel_back
127
+ simple_stubs.unstub_all!
128
+ end
129
+
130
+ private
131
+
132
+ def simple_stubs
133
+ @simple_stubs ||= SimpleStubs.new
134
+ end
135
+ end
136
+ end
137
+ end