diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-19 15:09:20 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-19 15:09:20 +0300 |
commit | 6559f0ee67c564d62e12936e91cef6abf37ce102 (patch) | |
tree | ac843e2187f4fa22eade9be5ae380a7fc793e31b /rubocop | |
parent | 93e4425400aa60f54f1bbccb26ef6581503952f3 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'rubocop')
-rw-r--r-- | rubocop/cop/migration/add_limit_to_text_columns.rb | 10 | ||||
-rw-r--r-- | rubocop/cop/rspec/factory_bot/inline_association.rb | 109 |
2 files changed, 119 insertions, 0 deletions
diff --git a/rubocop/cop/migration/add_limit_to_text_columns.rb b/rubocop/cop/migration/add_limit_to_text_columns.rb index b578e73f19e..b2e37ad5137 100644 --- a/rubocop/cop/migration/add_limit_to_text_columns.rb +++ b/rubocop/cop/migration/add_limit_to_text_columns.rb @@ -6,6 +6,10 @@ module RuboCop module Cop module Migration # Cop that enforces always adding a limit on text columns + # + # Text columns starting with `encrypted_` are very likely used + # by `attr_encrypted` which controls the text length. Those columns + # should not add a text limit. class AddLimitToTextColumns < RuboCop::Cop::Cop include MigrationHelpers @@ -102,6 +106,8 @@ module RuboCop # Check if there is an `add_text_limit` call for the provided # table and attribute name def text_limit_missing?(node, table_name, attribute_name) + return false if encrypted_attribute_name?(attribute_name) + limit_found = false node.each_descendant(:send) do |send_node| @@ -118,6 +124,10 @@ module RuboCop !limit_found end + + def encrypted_attribute_name?(attribute_name) + attribute_name.to_s.start_with?('encrypted_') + end end end end diff --git a/rubocop/cop/rspec/factory_bot/inline_association.rb b/rubocop/cop/rspec/factory_bot/inline_association.rb new file mode 100644 index 00000000000..1c2b8b55b46 --- /dev/null +++ b/rubocop/cop/rspec/factory_bot/inline_association.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module RSpec + module FactoryBot + # This cop encourages the use of inline associations in FactoryBot. + # The explicit use of `create` and `build` is discouraged. + # + # See https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#inline-definition + # + # @example + # + # Context: + # + # Factory.define do + # factory :project, class: 'Project' + # # EXAMPLE below + # end + # end + # + # # bad + # creator { create(:user) } + # creator { create(:user, :admin) } + # creator { build(:user) } + # creator { FactoryBot.build(:user) } + # creator { ::FactoryBot.build(:user) } + # add_attribute(:creator) { build(:user) } + # + # # good + # creator { association(:user) } + # creator { association(:user, :admin) } + # add_attribute(:creator) { association(:user) } + # + # # Accepted + # after(:build) do |instance| + # instance.creator = create(:user) + # end + # + # initialize_with do + # create(:project) + # end + # + # creator_id { create(:user).id } + # + class InlineAssociation < RuboCop::Cop::Cop + MSG = 'Prefer inline `association` over `%{type}`. ' \ + 'See https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#factories' + + REPLACEMENT = 'association' + + def_node_matcher :create_or_build, <<~PATTERN + ( + send + ${ nil? (const { nil? (cbase) } :FactoryBot) } + ${ :create :build } + (sym _) + ... + ) + PATTERN + + def_node_matcher :association_definition, <<~PATTERN + (block + { + (send nil? $_) + (send nil? :add_attribute (sym $_)) + } + ... + ) + PATTERN + + def_node_matcher :chained_call?, <<~PATTERN + (send _ _) + PATTERN + + SKIP_NAMES = %i[initialize_with].to_set.freeze + + def on_send(node) + _receiver, type = create_or_build(node) + return unless type + return if chained_call?(node.parent) + return unless inside_assocation_definition?(node) + + add_offense(node, message: format(MSG, type: type)) + end + + def autocorrect(node) + lambda do |corrector| + receiver, type = create_or_build(node) + receiver = "#{receiver.source}." if receiver + expression = "#{receiver}#{type}" + replacement = node.source.sub(expression, REPLACEMENT) + corrector.replace(node.source_range, replacement) + end + end + + private + + def inside_assocation_definition?(node) + node.each_ancestor(:block).any? do |parent| + name = association_definition(parent) + name && !SKIP_NAMES.include?(name) + end + end + end + end + end + end +end |