forked from rubocop/rubocop-rails
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfreeze_time.rb
79 lines (66 loc) · 2.55 KB
/
freeze_time.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# frozen_string_literal: true
module RuboCop
module Cop
module Rails
# Identifies usages of `travel_to` with an argument of the current time and
# change them to use `freeze_time` instead.
#
# @safety
# This cop’s autocorrection is unsafe because `freeze_time` just delegates to
# `travel_to` with a default `Time.now`, it is not strictly equivalent to `Time.now`
# if the argument of `travel_to` is the current time considering time zone.
#
# @example
# # bad
# travel_to(Time.now)
# travel_to(Time.new)
# travel_to(DateTime.now)
# travel_to(Time.current)
# travel_to(Time.zone.now)
# travel_to(Time.now.in_time_zone)
# travel_to(Time.current.to_time)
#
# # good
# freeze_time
#
class FreezeTime < Base
extend AutoCorrector
extend TargetRailsVersion
minimum_target_rails_version 5.2
MSG = 'Use `freeze_time` instead of `travel_to`.'
NOW_METHODS = %i[now new current].freeze
CONVERT_METHODS = %i[to_time in_time_zone].freeze
RESTRICT_ON_SEND = %i[travel_to].freeze
# @!method time_now?(node)
def_node_matcher :time_now?, <<~PATTERN
(const {nil? cbase} {:Time :DateTime})
PATTERN
# @!method zoned_time_now?(node)
def_node_matcher :zoned_time_now?, <<~PATTERN
(send (const {nil? cbase} :Time) :zone)
PATTERN
def on_send(node)
child_node, method_name, time_argument = *node.first_argument&.children
return if time_argument || !child_node
return unless current_time?(child_node, method_name) || current_time_with_convert?(child_node, method_name)
add_offense(node) do |corrector|
last_argument = node.last_argument
freeze_time_method = last_argument.block_pass_type? ? "freeze_time(#{last_argument.source})" : 'freeze_time'
corrector.replace(node, freeze_time_method)
end
end
private
def current_time?(node, method_name)
return false unless NOW_METHODS.include?(method_name)
node.send_type? ? zoned_time_now?(node) : time_now?(node)
end
def current_time_with_convert?(node, method_name)
return false unless CONVERT_METHODS.include?(method_name)
child_node, child_method_name, time_argument = *node.children
return false if time_argument
current_time?(child_node, child_method_name)
end
end
end
end
end