sanger/sequencescape

View on GitHub
app/models/study.rb

Summary

Maintainability
C
1 day
Test Coverage
A
95%

File study.rb has 396 lines of code (exceeds 250 allowed). Consider refactoring.
Open

require 'aasm'

# A Study is a collection of various {Sample samples} and the work done on them.
# They are perhaps slightly overloaded, and provide:
# - A means of grouping together samples for administrative purposes
Severity: Minor
Found in app/models/study.rb - About 5 hrs to fix

    Class Study has 33 methods (exceeds 20 allowed). Consider refactoring.
    Open

    class Study < ApplicationRecord # rubocop:todo Metrics/ClassLength
      # It has to be here, as there are has_many through: :roles associations in modules
      # Includes / Extendes
      has_many :roles
    
    
    Severity: Minor
    Found in app/models/study.rb - About 4 hrs to fix

      Study has 21 constants
      Open

      class Study < ApplicationRecord # rubocop:todo Metrics/ClassLength
      Severity: Minor
      Found in app/models/study.rb by reek

      Too Many Constants is a special case of LargeClass.

      Example

      Given this configuration

      TooManyConstants:
        max_constants: 3

      and this code:

      class TooManyConstants
        CONST_1 = :dummy
        CONST_2 = :dummy
        CONST_3 = :dummy
        CONST_4 = :dummy
      end

      Reek would emit the following warning:

      test.rb -- 1 warnings:
        [1]:TooManyConstants has 4 constants (TooManyConstants)

      Study#unprocessed_submissions? refers to 'o' more than self (maybe move it to another class?)
      Open

          study.orders.any? { |o| o.submission.nil? || o.submission.unprocessed? }
      Severity: Minor
      Found in app/models/study.rb by reek

      Feature Envy occurs when a code fragment references another object more often than it references itself, or when several clients do the same series of manipulations on a particular type of object.

      Feature Envy reduces the code's ability to communicate intent: code that "belongs" on one class but which is located in another can be hard to find, and may upset the "System of Names" in the host class.

      Feature Envy also affects the design's flexibility: A code fragment that is in the wrong class creates couplings that may not be natural within the application's domain, and creates a loss of cohesion in the unwilling host class.

      Feature Envy often arises because it must manipulate other objects (usually its arguments) to get them into a useful form, and one force preventing them (the arguments) doing this themselves is that the common knowledge lives outside the arguments, or the arguments are of too basic a type to justify extending that type. Therefore there must be something which 'knows' about the contents or purposes of the arguments. That thing would have to be more than just a basic type, because the basic types are either containers which don't know about their contents, or they are single objects which can't capture their relationship with their fellows of the same type. So, this thing with the extra knowledge should be reified into a class, and the utility method will most likely belong there.

      Example

      Running Reek on:

      class Warehouse
        def sale_price(item)
          (item.price - item.rebate) * @vat
        end
      end

      would report:

      Warehouse#total_price refers to item more than self (FeatureEnvy)

      since this:

      (item.price - item.rebate)

      belongs to the Item class, not the Warehouse.

      Study#completed refers to 'counts' more than self (maybe move it to another class?)
      Open

          total = counts.values.sum
          failed = counts['failed'] || 0
          cancelled = counts['cancelled'] || 0
          (total - failed - cancelled) > 0 ? (counts.fetch('passed', 0) * 100) / (total - failed - cancelled) : 0
      Severity: Minor
      Found in app/models/study.rb by reek

      Feature Envy occurs when a code fragment references another object more often than it references itself, or when several clients do the same series of manipulations on a particular type of object.

      Feature Envy reduces the code's ability to communicate intent: code that "belongs" on one class but which is located in another can be hard to find, and may upset the "System of Names" in the host class.

      Feature Envy also affects the design's flexibility: A code fragment that is in the wrong class creates couplings that may not be natural within the application's domain, and creates a loss of cohesion in the unwilling host class.

      Feature Envy often arises because it must manipulate other objects (usually its arguments) to get them into a useful form, and one force preventing them (the arguments) doing this themselves is that the common knowledge lives outside the arguments, or the arguments are of too basic a type to justify extending that type. Therefore there must be something which 'knows' about the contents or purposes of the arguments. That thing would have to be more than just a basic type, because the basic types are either containers which don't know about their contents, or they are single objects which can't capture their relationship with their fellows of the same type. So, this thing with the extra knowledge should be reified into a class, and the utility method will most likely belong there.

      Example

      Running Reek on:

      class Warehouse
        def sale_price(item)
          (item.price - item.rebate) * @vat
        end
      end

      would report:

      Warehouse#total_price refers to item more than self (FeatureEnvy)

      since this:

      (item.price - item.rebate)

      belongs to the Item class, not the Warehouse.

      Study has at least 33 methods
      Open

      class Study < ApplicationRecord # rubocop:todo Metrics/ClassLength
      Severity: Minor
      Found in app/models/study.rb by reek

      Too Many Methods is a special case of LargeClass.

      Example

      Given this configuration

      TooManyMethods:
        max_methods: 3

      and this code:

      class TooManyMethods
        def one; end
        def two; end
        def three; end
        def four; end
      end

      Reek would emit the following warning:

      test.rb -- 1 warning:
        [1]:TooManyMethods has at least 4 methods (TooManyMethods)

      Study#each_well_for_qc_report_in_batches is controlled by argument 'exclude_existing'
      Open

          scope = exclude_existing ? base_scope.without_report(product_criteria) : base_scope
      Severity: Minor
      Found in app/models/study.rb by reek

      Control Parameter is a special case of Control Couple

      Example

      A simple example would be the "quoted" parameter in the following method:

      def write(quoted)
        if quoted
          write_quoted @value
        else
          write_unquoted @value
        end
      end

      Fixing those problems is out of the scope of this document but an easy solution could be to remove the "write" method alltogether and to move the calls to "writequoted" / "writeunquoted" in the initial caller of "write".

      Study#completed has approx 6 statements
      Open

        def completed
      Severity: Minor
      Found in app/models/study.rb by reek

      A method with Too Many Statements is any method that has a large number of lines.

      Too Many Statements warns about any method that has more than 5 statements. Reek's smell detector for Too Many Statements counts +1 for every simple statement in a method and +1 for every statement within a control structure (if, else, case, when, for, while, until, begin, rescue) but it doesn't count the control structure itself.

      So the following method would score +6 in Reek's statement-counting algorithm:

      def parse(arg, argv, &error)
        if !(val = arg) and (argv.empty? or /\A-/ =~ (val = argv[0]))
          return nil, block, nil                                         # +1
        end
        opt = (val = parse_arg(val, &error))[1]                          # +2
        val = conv_arg(*val)                                             # +3
        if opt and !arg
          argv.shift                                                     # +4
        else
          val[0] = nil                                                   # +5
        end
        val                                                              # +6
      end

      (You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)

      Study#each_well_for_qc_report_in_batches is controlled by argument 'plate_purposes'
      Open

              .on_plate_purpose_included(PlatePurpose.where(name: plate_purposes || STOCK_PLATE_PURPOSES))
      Severity: Minor
      Found in app/models/study.rb by reek

      Control Parameter is a special case of Control Couple

      Example

      A simple example would be the "quoted" parameter in the following method:

      def write(quoted)
        if quoted
          write_quoted @value
        else
          write_unquoted @value
        end
      end

      Fixing those problems is out of the scope of this document but an easy solution could be to remove the "write" method alltogether and to move the calls to "writequoted" / "writeunquoted" in the initial caller of "write".

      Study#asset_progress refers to 'assets' more than self (maybe move it to another class?)
      Open

          wheres = { asset_id: assets.map(&:id) } if assets.present?
      Severity: Minor
      Found in app/models/study.rb by reek

      Feature Envy occurs when a code fragment references another object more often than it references itself, or when several clients do the same series of manipulations on a particular type of object.

      Feature Envy reduces the code's ability to communicate intent: code that "belongs" on one class but which is located in another can be hard to find, and may upset the "System of Names" in the host class.

      Feature Envy also affects the design's flexibility: A code fragment that is in the wrong class creates couplings that may not be natural within the application's domain, and creates a loss of cohesion in the unwilling host class.

      Feature Envy often arises because it must manipulate other objects (usually its arguments) to get them into a useful form, and one force preventing them (the arguments) doing this themselves is that the common knowledge lives outside the arguments, or the arguments are of too basic a type to justify extending that type. Therefore there must be something which 'knows' about the contents or purposes of the arguments. That thing would have to be more than just a basic type, because the basic types are either containers which don't know about their contents, or they are single objects which can't capture their relationship with their fellows of the same type. So, this thing with the extra knowledge should be reified into a class, and the utility method will most likely belong there.

      Example

      Running Reek on:

      class Warehouse
        def sale_price(item)
          (item.price - item.rebate) * @vat
        end
      end

      would report:

      Warehouse#total_price refers to item more than self (FeatureEnvy)

      since this:

      (item.price - item.rebate)

      belongs to the Item class, not the Warehouse.

      Study#text_comments refers to 'c' more than self (maybe move it to another class?)
      Open

          comments.each_with_object([]) { |c, array| array << c.description if c.description.present? }.join(', ')
      Severity: Minor
      Found in app/models/study.rb by reek

      Feature Envy occurs when a code fragment references another object more often than it references itself, or when several clients do the same series of manipulations on a particular type of object.

      Feature Envy reduces the code's ability to communicate intent: code that "belongs" on one class but which is located in another can be hard to find, and may upset the "System of Names" in the host class.

      Feature Envy also affects the design's flexibility: A code fragment that is in the wrong class creates couplings that may not be natural within the application's domain, and creates a loss of cohesion in the unwilling host class.

      Feature Envy often arises because it must manipulate other objects (usually its arguments) to get them into a useful form, and one force preventing them (the arguments) doing this themselves is that the common knowledge lives outside the arguments, or the arguments are of too basic a type to justify extending that type. Therefore there must be something which 'knows' about the contents or purposes of the arguments. That thing would have to be more than just a basic type, because the basic types are either containers which don't know about their contents, or they are single objects which can't capture their relationship with their fellows of the same type. So, this thing with the extra knowledge should be reified into a class, and the utility method will most likely belong there.

      Example

      Running Reek on:

      class Warehouse
        def sale_price(item)
          (item.price - item.rebate) * @vat
        end
      end

      would report:

      Warehouse#total_price refers to item more than self (FeatureEnvy)

      since this:

      (item.price - item.rebate)

      belongs to the Item class, not the Warehouse.

      Study tests 'ethical_approval_required?' at least 3 times
      Open

            if ethical_approval_required?
              'should be either true or false for this study.'
            else
              'should be not applicable (null) not false.'
            end
      Severity: Minor
      Found in app/models/study.rb by reek

      Repeated Conditional is a special case of Simulated Polymorphism. Basically it means you are checking the same value throughout a single class and take decisions based on this.

      Example

      Given

      class RepeatedConditionals
        attr_accessor :switch
      
        def repeat_1
          puts "Repeat 1!" if switch
        end
      
        def repeat_2
          puts "Repeat 2!" if switch
        end
      
        def repeat_3
          puts "Repeat 3!" if switch
        end
      end

      Reek would emit the following warning:

      test.rb -- 4 warnings:
        [5, 9, 13]:RepeatedConditionals tests switch at least 3 times (RepeatedConditional)

      If you get this warning then you are probably not using the right abstraction or even more probable, missing an additional abstraction.

      Study#unprocessed_submissions? calls 'o.submission' 2 times
      Open

          study.orders.any? { |o| o.submission.nil? || o.submission.unprocessed? }
      Severity: Minor
      Found in app/models/study.rb by reek

      Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.

      Reek implements a check for Duplicate Method Call.

      Example

      Here's a very much simplified and contrived example. The following method will report a warning:

      def double_thing()
        @other.thing + @other.thing
      end

      One quick approach to silence Reek would be to refactor the code thus:

      def double_thing()
        thing = @other.thing
        thing + thing
      end

      A slightly different approach would be to replace all calls of double_thing by calls to @other.double_thing:

      class Other
        def double_thing()
          thing + thing
        end
      end

      The approach you take will depend on balancing other factors in your code.

      Study#completed calls 'total - failed - cancelled' 2 times
      Open

          (total - failed - cancelled) > 0 ? (counts.fetch('passed', 0) * 100) / (total - failed - cancelled) : 0
      Severity: Minor
      Found in app/models/study.rb by reek

      Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.

      Reek implements a check for Duplicate Method Call.

      Example

      Here's a very much simplified and contrived example. The following method will report a warning:

      def double_thing()
        @other.thing + @other.thing
      end

      One quick approach to silence Reek would be to refactor the code thus:

      def double_thing()
        thing = @other.thing
        thing + thing
      end

      A slightly different approach would be to replace all calls of double_thing by calls to @other.double_thing:

      class Other
        def double_thing()
          thing + thing
        end
      end

      The approach you take will depend on balancing other factors in your code.

      Study#completed calls 'total - failed' 2 times
      Open

          (total - failed - cancelled) > 0 ? (counts.fetch('passed', 0) * 100) / (total - failed - cancelled) : 0
      Severity: Minor
      Found in app/models/study.rb by reek

      Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.

      Reek implements a check for Duplicate Method Call.

      Example

      Here's a very much simplified and contrived example. The following method will report a warning:

      def double_thing()
        @other.thing + @other.thing
      end

      One quick approach to silence Reek would be to refactor the code thus:

      def double_thing()
        thing = @other.thing
        thing + thing
      end

      A slightly different approach would be to replace all calls of double_thing by calls to @other.double_thing:

      class Other
        def double_thing()
          thing + thing
        end
      end

      The approach you take will depend on balancing other factors in your code.

      Study#text_comments calls 'c.description' 2 times
      Open

          comments.each_with_object([]) { |c, array| array << c.description if c.description.present? }.join(', ')
      Severity: Minor
      Found in app/models/study.rb by reek

      Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.

      Reek implements a check for Duplicate Method Call.

      Example

      Here's a very much simplified and contrived example. The following method will report a warning:

      def double_thing()
        @other.thing + @other.thing
      end

      One quick approach to silence Reek would be to refactor the code thus:

      def double_thing()
        thing = @other.thing
        thing + thing
      end

      A slightly different approach would be to replace all calls of double_thing by calls to @other.double_thing:

      class Other
        def double_thing()
          thing + thing
        end
      end

      The approach you take will depend on balancing other factors in your code.

      Complex method Study#completed (22.6)
      Open

        def completed
          counts = requests.standard.group('state').count
          total = counts.values.sum
          failed = counts['failed'] || 0
          cancelled = counts['cancelled'] || 0
      Severity: Minor
      Found in app/models/study.rb by flog

      Flog calculates the ABC score for methods. The ABC score is based on assignments, branches (method calls), and conditions.

      You can read more about ABC metrics or the flog tool

      Study#approval is a writable attribute
      Open

        attr_accessor :approval, :run_count, :total_price
      Severity: Minor
      Found in app/models/study.rb by reek

      A class that publishes a setter for an instance variable invites client classes to become too intimate with its inner workings, and in particular with its representation of state.

      The same holds to a lesser extent for getters, but Reek doesn't flag those.

      Example

      Given:

      class Klass
        attr_accessor :dummy
      end

      Reek would emit the following warning:

      reek test.rb
      
      test.rb -- 1 warning:
        [2]:Klass declares the writable attribute dummy (Attribute)

      Study has missing safe method 'validate_ena_required_fields!'
      Open

        def validate_ena_required_fields!
      Severity: Minor
      Found in app/models/study.rb by reek

      A candidate method for the Missing Safe Method smell are methods whose names end with an exclamation mark.

      An exclamation mark in method names means (the explanation below is taken from here ):

      The ! in method names that end with ! means, “This method is dangerous”—or, more precisely, this method is the “dangerous” version of an otherwise equivalent method, with the same name minus the !. “Danger” is relative; the ! doesn’t mean anything at all unless the method name it’s in corresponds to a similar but bang-less method name. So, for example, gsub! is the dangerous version of gsub. exit! is the dangerous version of exit. flatten! is the dangerous version of flatten. And so forth.

      Such a method is called Missing Safe Method if and only if her non-bang version does not exist and this method is reported as a smell.

      Example

      Given

      class C
        def foo; end
        def foo!; end
        def bar!; end
      end

      Reek would report bar! as Missing Safe Method smell but not foo!.

      Reek reports this smell only in a class context, not in a module context in order to allow perfectly legit code like this:

      class Parent
        def foo; end
      end
      
      module Dangerous
        def foo!; end
      end
      
      class Son < Parent
        include Dangerous
      end
      
      class Daughter < Parent
      end

      In this example, Reek would not report the Missing Safe Method smell for the method foo of the Dangerous module.

      Study#total_price is a writable attribute
      Open

        attr_accessor :approval, :run_count, :total_price
      Severity: Minor
      Found in app/models/study.rb by reek

      A class that publishes a setter for an instance variable invites client classes to become too intimate with its inner workings, and in particular with its representation of state.

      The same holds to a lesser extent for getters, but Reek doesn't flag those.

      Example

      Given:

      class Klass
        attr_accessor :dummy
      end

      Reek would emit the following warning:

      reek test.rb
      
      test.rb -- 1 warning:
        [2]:Klass declares the writable attribute dummy (Attribute)

      Study#run_count is a writable attribute
      Open

        attr_accessor :approval, :run_count, :total_price
      Severity: Minor
      Found in app/models/study.rb by reek

      A class that publishes a setter for an instance variable invites client classes to become too intimate with its inner workings, and in particular with its representation of state.

      The same holds to a lesser extent for getters, but Reek doesn't flag those.

      Example

      Given:

      class Klass
        attr_accessor :dummy
      end

      Reek would emit the following warning:

      reek test.rb
      
      test.rb -- 1 warning:
        [2]:Klass declares the writable attribute dummy (Attribute)

      Study#unprocessed_submissions? performs a nil-check
      Open

          study.orders.any? { |o| o.submission.nil? || o.submission.unprocessed? }
      Severity: Minor
      Found in app/models/study.rb by reek

      A NilCheck is a type check. Failures of NilCheck violate the "tell, don't ask" principle.

      Additionally, type checks often mask bigger problems in your source code like not using OOP and / or polymorphism when you should.

      Example

      Given

      class Klass
        def nil_checker(argument)
          if argument.nil?
            puts "argument isn't nil!"
          end
        end
      end

      Reek would emit the following warning:

      test.rb -- 1 warning:
        [3]:Klass#nil_checker performs a nil-check. (NilCheck)

      Study#valid_ethically_approved? performs a nil-check
      Open

          ethical_approval_required? ? !ethically_approved.nil? : ethically_approved != false
      Severity: Minor
      Found in app/models/study.rb by reek

      A NilCheck is a type check. Failures of NilCheck violate the "tell, don't ask" principle.

      Additionally, type checks often mask bigger problems in your source code like not using OOP and / or polymorphism when you should.

      Example

      Given

      class Klass
        def nil_checker(argument)
          if argument.nil?
            puts "argument isn't nil!"
          end
        end
      end

      Reek would emit the following warning:

      test.rb -- 1 warning:
        [3]:Klass#nil_checker performs a nil-check. (NilCheck)

      Study#unprocessed_submissions? has the variable name 'o'
      Open

          study.orders.any? { |o| o.submission.nil? || o.submission.unprocessed? }
      Severity: Minor
      Found in app/models/study.rb by reek

      An Uncommunicative Variable Name is a variable name that doesn't communicate its intent well enough.

      Poor names make it hard for the reader to build a mental picture of what's going on in the code. They can also be mis-interpreted; and they hurt the flow of reading, because the reader must slow down to interpret the names.

      Study#text_comments has the variable name 'c'
      Open

          comments.each_with_object([]) { |c, array| array << c.description if c.description.present? }.join(', ')
      Severity: Minor
      Found in app/models/study.rb by reek

      An Uncommunicative Variable Name is a variable name that doesn't communicate its intent well enough.

      Poor names make it hard for the reader to build a mental picture of what's going on in the code. They can also be mis-interpreted; and they hurt the flow of reading, because the reader must slow down to interpret the names.

      Study has the variable name 'v'
      Open

            }.transform_values { |v| v.index_by { |b| b.downcase } }
      Severity: Minor
      Found in app/models/study.rb by reek

      An Uncommunicative Variable Name is a variable name that doesn't communicate its intent well enough.

      Poor names make it hard for the reader to build a mental picture of what's going on in the code. They can also be mis-interpreted; and they hurt the flow of reading, because the reader must slow down to interpret the names.

      Study has the variable name 'b'
      Open

            }.transform_values { |v| v.index_by { |b| b.downcase } }
      Severity: Minor
      Found in app/models/study.rb by reek

      An Uncommunicative Variable Name is a variable name that doesn't communicate its intent well enough.

      Poor names make it hard for the reader to build a mental picture of what's going on in the code. They can also be mis-interpreted; and they hurt the flow of reading, because the reader must slow down to interpret the names.

      TODO found
      Open

          # TODO[mb14] optimize if needed
      Severity: Minor
      Found in app/models/study.rb by fixme

      TODO found
      Open

        # TODO - Look into this is the person that created it really the owner?
      Severity: Minor
      Found in app/models/study.rb by fixme

      TODO found
      Open

          # TODO: remove
      Severity: Minor
      Found in app/models/study.rb by fixme

      TODO found
      Open

        # TODO - Should be "owners" and return all owners or empty array - done
      Severity: Minor
      Found in app/models/study.rb by fixme

      Identical blocks of code found in 2 locations. Consider refactoring.
      Open

          before_validation do |record|
            record.reference_genome_id = 1 if record.reference_genome_id.blank?
      
            # Unfortunately it appears that some of the functionality of this implementation relies on non-capitalisation!
            # So we remap the lowercased versions to their proper values here
      Severity: Minor
      Found in app/models/study.rb and 1 other location - About 40 mins to fix
      app/models/sample.rb on lines 201..208

      Duplicated Code

      Duplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:

      Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

      When you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).

      Tuning

      This issue has a mass of 38.

      We set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.

      The threshold configuration represents the minimum mass a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.

      If the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.

      See codeclimate-duplication's documentation for more information about tuning the mass threshold in your .codeclimate.yml.

      Refactorings

      Further Reading

      There are no issues that match your filters.

      Category
      Status