Module: RSpec::Armour::Checker

Defined in:
lib/rspec/armour/checker.rb

Constant Summary collapse

RESTRICTED_CLASS_METHODS =
%i[
  find find_by find_by! where all first last count sum average minimum
  maximum pluck pick exists? create create! find_or_create_by
  find_or_create_by! find_or_initialize_by update update! destroy_all
  delete_all delete_by destroy_by first_or_create first_or_create!
  first_or_initialize
].freeze
RESTRICTED_INSTANCE_METHODS =
%i[
  save save! update update! update_attribute update_columns update_column
  destroy destroy! delete delete! touch reload increment! decrement!
  toggle!
].freeze
DISABLED_KEY =
:rspec_armour_disabled
NEGATIVE_EXPECTATION_KEY =
:rspec_armour_negative_expectation

Class Method Summary collapse

Class Method Details

.as_negative_expectation!Object



43
44
45
46
47
48
49
# File 'lib/rspec/armour/checker.rb', line 43

def self.as_negative_expectation!
  previous = Thread.current[NEGATIVE_EXPECTATION_KEY]
  Thread.current[NEGATIVE_EXPECTATION_KEY] = true
  yield
ensure
  Thread.current[NEGATIVE_EXPECTATION_KEY] = previous
end

.association_method_on_class?(klass, method_name) ⇒ Boolean

Returns:

  • (Boolean)


88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/rspec/armour/checker.rb', line 88

def self.association_method_on_class?(klass, method_name)
  return false unless klass.respond_to?(:reflections)

  reflections = klass.reflections
  reflections.any? do |name, _reflection|
    singular_name = name.to_s.singularize
    [
      name.to_sym,
      :"#{name}=",
      :"#{singular_name}_ids",
      :"#{singular_name}_ids=",
    ].include?(method_name)
  end
end

.disable!Object



107
108
109
110
111
112
113
# File 'lib/rspec/armour/checker.rb', line 107

def self.disable!
  previous = Thread.current[DISABLED_KEY]
  Thread.current[DISABLED_KEY] = true
  yield
ensure
  Thread.current[DISABLED_KEY] = previous
end

.disabled?Boolean

Returns:

  • (Boolean)


103
104
105
# File 'lib/rspec/armour/checker.rb', line 103

def self.disabled?
  Thread.current[DISABLED_KEY]
end

.negative_expectation?Boolean

Returns:

  • (Boolean)


39
40
41
# File 'lib/rspec/armour/checker.rb', line 39

def self.negative_expectation?
  Thread.current[NEGATIVE_EXPECTATION_KEY]
end

.resolve_double_info(target) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/rspec/armour/checker.rb', line 64

def self.resolve_double_info(target)
  ref = target.instance_variable_get(:@doubled_module)
  return [false, nil] unless ref.respond_to?(:target)

  underlying_target = ref.target
  case target
  when ::RSpec::Mocks::InstanceVerifyingDouble
    [false, underlying_target]
  when ::RSpec::Mocks::ClassVerifyingDouble
    [true, underlying_target]
  when ::RSpec::Mocks::ObjectVerifyingDouble
    is_class = underlying_target.is_a?(Class)
    [is_class, is_class ? underlying_target : underlying_target.class]
  else
    [false, nil]
  end
end

.resolve_target_info(target) ⇒ Object



51
52
53
54
55
56
57
58
# File 'lib/rspec/armour/checker.rb', line 51

def self.resolve_target_info(target)
  if test_double?(target)
    resolve_double_info(target)
  else
    is_class = target.is_a?(Class)
    [is_class, is_class ? target : target.class]
  end
end

.restricted?(target, method_name) ⇒ Boolean

Returns:

  • (Boolean)


26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/rspec/armour/checker.rb', line 26

def self.restricted?(target, method_name)
  return false if disabled?
  return false if negative_expectation?

  method_sym = method_name.to_sym
  is_class, klass = resolve_target_info(target)

  return false unless klass.is_a?(Class) && klass < ::ActiveRecord::Base

  restricted_by_list?(is_class,
                      method_sym) || association_method_on_class?(klass, method_sym)
end

.restricted_by_list?(is_class, method_sym) ⇒ Boolean

Returns:

  • (Boolean)


82
83
84
85
86
# File 'lib/rspec/armour/checker.rb', line 82

def self.restricted_by_list?(is_class, method_sym)
  return true if RESTRICTED_INSTANCE_METHODS.include?(method_sym)

  is_class && RESTRICTED_CLASS_METHODS.include?(method_sym)
end

.test_double?(target) ⇒ Boolean

Returns:

  • (Boolean)


60
61
62
# File 'lib/rspec/armour/checker.rb', line 60

def self.test_double?(target)
  defined?(::RSpec::Mocks::TestDouble) && target.is_a?(::RSpec::Mocks::TestDouble)
end