diff --git a/.rubocop.yml b/.rubocop.yml index c3af215..2194f7e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,10 +1,551 @@ -# Omakase Ruby styling for Rails -inherit_gem: { rubocop-rails-omakase: rubocop.yml } - -# Overwrite or add rules to create your own house style -# -# # Use `[a, [b, c]]` not `[ a, [ b, c ] ]` -# Layout/SpaceInsideArrayLiteralBrackets: -# Enabled: false +# This is rubocop configuration file for Sun-Asterisk's coding style. Enabling and disabling is configured +# in separate files. This file adds all other parameters apart from Enabled. + +# Add this to load Rails extension for rubocop +require: rubocop-rails + +# Put the following files into the same directory as this file. +inherit_from: + - .rubocop_enabled.yml + - .rubocop_disabled.yml + +# Common configuration. +AllCops: + # Include gemspec and Rakefile + SuggestExtensions: false + Include: + - 'app/**/*' + - 'lib/**/*' + - '**/*.gemspec' + - 'Gemfile' + Exclude: + - 'vendor/**/*' + - 'config/unicorn.rb' + - 'bin/*' + - 'db/**/*' + - 'spec/**/*' + - 'script/rails' + - 'config/**/*' + - 'app/views/**/*' + - 'app/assets/**/*' + - 'app/javascript/**/*' + - '.idea/**/*' + - '**/.DS_Store' + - '**/.keep' + +# Indent private/protected/public as deep as method definitions +Layout/AccessModifierIndentation: + EnforcedStyle: indent + SupportedStyles: + - outdent + - indent + +# Align the elements of a hash literal if they span more than one line. +Layout/HashAlignment: + # Alignment of entries using hash rocket as separator. Valid values are: + # + # key - left alignment of keys + # 'a' => 2 + # 'bb' => 3 + # separator - alignment of hash rockets, keys are right aligned + # 'a' => 2 + # 'bb' => 3 + # table - left alignment of keys, hash rockets, and values + # 'a' => 2 + # 'bb' => 3 + EnforcedHashRocketStyle: key + # Alignment of entries using colon as separator. Valid values are: + # + # key - left alignment of keys + # a: 0 + # bb: 1 + # separator - alignment of colons, keys are right aligned + # a: 0 + # bb: 1 + # table - left alignment of keys and values + # a: 0 + # bb: 1 + EnforcedColonStyle: key + # Select whether hashes that are the last argument in a method call should be + # inspected? Valid values are: + # + # always_inspect - Inspect both implicit and explicit hashes. + # Registers an offense for: + # function(a: 1, + # b: 2) + # Registers an offense for: + # function({a: 1, + # b: 2}) + # always_ignore - Ignore both implicit and explicit hashes. + # Accepts: + # function(a: 1, + # b: 2) + # Accepts: + # function({a: 1, + # b: 2}) + # ignore_implicit - Ignore only implicit hashes. + # Accepts: + # function(a: 1, + # b: 2) + # Registers an offense for: + # function({a: 1, + # b: 2}) + # ignore_explicit - Ignore only explicit hashes. + # Accepts: + # function({a: 1, + # b: 2}) + # Registers an offense for: + # function(a: 1, + # b: 2) + EnforcedLastArgumentHashStyle: ignore_implicit + SupportedLastArgumentHashStyles: + - always_inspect + - always_ignore + - ignore_implicit + - ignore_explicit + +Layout/ParameterAlignment: + # Alignment of parameters in multi-line method calls. + # + # The `with_first_parameter` style aligns the following lines along the same column + # as the first parameter. + # + # method_call(a, + # b) + # + # The `with_fixed_indentation` style aligns the following lines with one + # level of indentation relative to the start of the line with the method call. + # + # method_call(a, + # b) + EnforcedStyle: with_fixed_indentation + SupportedStyles: + - with_first_parameter + - with_fixed_indentation + +Metrics/BlockNesting: + Max: 3 + +Metrics/AbcSize: + # The ABC size is a calculated magnitude, so this number can be a Fixnum or + # a Float. + Max: 20 + +# Indentation of `when`. +Layout/CaseIndentation: + EnforcedStyle: case + SupportedStyles: + - case + - end + IndentOneStep: false + +Style/ClassAndModuleChildren: + # Checks the style of children definitions at classes and modules. + # + # Basically there are two different styles: + # + # `nested` - have each child on a separate line + # class Foo + # class Bar + # end + # end + # + # `compact` - combine definitions as much as possible + # class Foo::Bar + # end + # + # The compact style is only forced, for classes / modules with one child. + EnforcedStyle: nested + SupportedStyles: + - nested + - compact + +Style/ClassCheck: + EnforcedStyle: is_a? + SupportedStyles: + - is_a? + - kind_of? + +Metrics/ClassLength: + CountComments: false # count full line comments? + Max: 100 + +# Align with the style guide. +Style/CollectionMethods: + # Mapping from undesired method to desired_method + # e.g. to use `detect` over `find`: + # + # CollectionMethods: + # PreferredMethods: + # find: detect + PreferredMethods: + collect: 'map' + collect!: 'map!' + inject: 'reduce' + detect: 'find' + find_all: 'select' + +# Checks formatting of special comments +Style/CommentAnnotation: + Keywords: + - TODO + - FIXME + - OPTIMIZE + - HACK + - REVIEW + +# Avoid complex methods. +Metrics/CyclomaticComplexity: + Max: 6 + +# Multi-line method chaining should be done with leading dots. +Layout/DotPosition: + EnforcedStyle: leading + SupportedStyles: + - leading + - trailing + +# Use empty lines between defs. +Layout/EmptyLineBetweenDefs: + # If true, this parameter means that single line method definitions don't + # need an empty line between them. + AllowAdjacentOneLineDefs: false + +Layout/EmptyLinesAroundBlockBody: + EnforcedStyle: no_empty_lines + SupportedStyles: + - no_empty_lines + +Layout/EmptyLinesAroundClassBody: + EnforcedStyle: no_empty_lines + SupportedStyles: + - no_empty_lines + +Layout/EmptyLinesAroundModuleBody: + EnforcedStyle: no_empty_lines + SupportedStyles: + - no_empty_lines + +Naming/FileName: + Exclude: + - '**/Rakefile' + - '**/Gemfile' + - '**/Capfile' + +# Checks use of for or each in multiline loops. +Style/For: + EnforcedStyle: each + SupportedStyles: + - for + - each + +# Enforce the method used for string formatting. +Style/FormatString: + EnforcedStyle: format + SupportedStyles: + - format + - sprintf + - percent + +# Built-in global variables are allowed by default. +Style/GlobalVars: + AllowedVariables: [] + +# `MinBodyLength` defines the number of lines of the a body of an if / unless +# needs to have to trigger this cop +Style/GuardClause: + MinBodyLength: 1 + +Style/HashSyntax: + EnforcedStyle: ruby19 + SupportedStyles: + - ruby19 + - hash_rockets + +# Checks the indentation of the first key in a hash literal. +Layout/FirstHashElementIndentation: + # The value `special_inside_parentheses` means that hash literals with braces + # that have their opening brace on the same line as a surrounding opening + # round parenthesis, shall have their first key indented relative to the + # first position inside the parenthesis. + # The value `consistent` means that the indentation of the first key shall + # always be relative to the first position of the line where the opening + # brace is. + EnforcedStyle: special_inside_parentheses + SupportedStyles: + - special_inside_parentheses + - consistent + +Style/LambdaCall: + EnforcedStyle: call + SupportedStyles: + - call + - braces + +Metrics/LineLength: + Max: 80 + AllowURI: true + Exclude: + - config/routes.rb + + +Style/Next: + # With `always` all conditions at the end of an iteration needs to be + # replace by next - with `skip_modifier_ifs` the modifier if like this one + # are ignored: [1, 2].each { |a| return 'yes' if a == 1 } + EnforcedStyle: skip_modifier_ifs + SupportedStyles: + - skip_modifier_ifs + - always + +Style/NonNilCheck: + # With `IncludeSemanticChanges` set to `true`, this cop reports offenses for + # `!x.nil?` and autocorrects that and `x != nil` to solely `x`, which is + # **usually** OK, but might change behavior. + # + # With `IncludeSemanticChanges` set to `false`, this cop does not report + # offenses for `!x.nil?` and does no changes that might change behavior. + IncludeSemanticChanges: false + Style/MethodDefParentheses: - Enabled: false + EnforcedStyle: require_no_parentheses + SupportedStyles: + - require_parentheses + - require_no_parentheses + +Metrics/MethodLength: + CountComments: false # count full line comments? + Max: 10 + +Naming/MethodName: + EnforcedStyle: snake_case + SupportedStyles: + - snake_case + - camelCase + +Style/NumericLiterals: + MinDigits: 5 + +Metrics/ParameterLists: + Max: 5 + CountKeywordArgs: true + +# Allow safe assignment in conditions. +Style/ParenthesesAroundCondition: + AllowSafeAssignment: true + +Style/PercentLiteralDelimiters: + PreferredDelimiters: + '%': () + '%i': () + '%q': () + '%Q': () + '%r': '{}' + '%s': () + '%w': () + '%W': () + '%x': () + +Style/RaiseArgs: + EnforcedStyle: exploded + SupportedStyles: + - compact # raise Exception.new(msg) + - exploded # raise Exception, msg + + +Style/RedundantReturn: + # When true allows code like `return x, y`. + AllowMultipleReturnValues: false + +#Style/RegexpLiteral: +# # The maximum number of (escaped) slashes that a slash-delimited regexp is +# # allowed to have. If there are more slashes, a %r regexp shall be used. +# MaxSlashes: 0 + +Style/Semicolon: + # Allow ; to separate several expressions on the same line. + AllowAsExpressionSeparator: false + +Style/SignalException: + EnforcedStyle: only_raise + SupportedStyles: + - only_raise + - only_fail + - semantic + + +Style/SingleLineBlockParams: + Methods: + - reduce: + - a + - e + - inject: + - a + - e + +Style/SingleLineMethods: + AllowIfMethodIsEmpty: true + +Style/StringLiterals: + EnforcedStyle: double_quotes + SupportedStyles: + - single_quotes + - double_quotes + +Layout/SpaceAroundEqualsInParameterDefault: + EnforcedStyle: space + SupportedStyles: + - space + - no_space + +Layout/SpaceBeforeBlockBraces: + EnforcedStyle: no_space + SupportedStyles: + - space + - no_space + +Layout/SpaceInsideBlockBraces: + EnforcedStyle: no_space + SupportedStyles: + - space + - no_space + # Valid values are: space, no_space + EnforcedStyleForEmptyBraces: no_space + # Space between { and |. Overrides EnforcedStyle if there is a conflict. + SpaceBeforeBlockParameters: false + +Layout/SpaceInsideHashLiteralBraces: + EnforcedStyle: no_space + EnforcedStyleForEmptyBraces: no_space + SupportedStyles: + - space + - no_space + +Layout/TrailingEmptyLines: + EnforcedStyle: final_newline + SupportedStyles: + - final_newline + - final_blank_line + +Style/TrailingCommaInArguments: + # If `comma`, the cop requires a comma after the last argument, but only for + # parenthesized method calls where each argument is on its own line. + # If `consistent_comma`, the cop requires a comma after the last argument, + # for all parenthesized method calls with arguments. + EnforcedStyleForMultiline: no_comma + SupportedStylesForMultiline: + - comma + - consistent_comma + - no_comma + +Style/TrailingCommaInArrayLiteral: + # If `comma`, the cop requires a comma after the last item in an array, + # but only when each item is on its own line. + # If `consistent_comma`, the cop requires a comma after the last item of all + # non-empty array literals. + EnforcedStyleForMultiline: no_comma + SupportedStylesForMultiline: + - comma + - consistent_comma + - no_comma + +Style/TrailingCommaInHashLiteral: + # If `comma`, the cop requires a comma after the last item in a hash, + # but only when each item is on its own line. + # If `consistent_comma`, the cop requires a comma after the last item of all + # non-empty hash literals. + EnforcedStyleForMultiline: no_comma + SupportedStylesForMultiline: + - comma + - consistent_comma + - no_comma + +# TrivialAccessors doesn't require exact name matches and doesn't allow +# predicated methods by default. +Style/TrivialAccessors: + ExactNameMatch: false + AllowPredicates: false + # Allows trivial writers that don't end in an equal sign. e.g. + # + # def on_exception(action) + # @on_exception=action + # end + # on_exception :restart + # + # Commonly used in DSLs + AllowDSLWriters: false + AllowedMethods: + - to_ary + - to_a + - to_c + - to_enum + - to_h + - to_hash + - to_i + - to_int + - to_io + - to_open + - to_path + - to_proc + - to_r + - to_regexp + - to_str + - to_s + - to_sym + +Naming/VariableName: + EnforcedStyle: snake_case + SupportedStyles: + - snake_case + - camelCase + +Style/WordArray: + MinSize: 0 + +##################### Lint ################################## + +# Allow safe assignment in conditions. +Lint/AssignmentInCondition: + AllowSafeAssignment: true + +# Align ends correctly. +Layout/EndAlignment: + # The value `keyword` means that `end` should be aligned with the matching + # keyword (if, while, etc.). + # The value `variable` means that in assignments, `end` should be aligned + # with the start of the variable on the left hand side of `=`. In all other + # situations, `end` should still be aligned with the keyword. + EnforcedStyleAlignWith: keyword + +Layout/DefEndAlignment: + # The value `def` means that `end` should be aligned with the def keyword. + # The value `start_of_line` means that `end` should be aligned with method + # calls like `private`, `public`, etc, if present in front of the `def` + # keyword on the same line. + EnforcedStyleAlignWith: start_of_line + +##################### Rails ################################## + +Rails/ActionFilter: + EnforcedStyle: action + SupportedStyles: + - action + - filter + Include: + - app/controllers/**/*.rb + +Rails/HasAndBelongsToMany: + Include: + - app/models/**/*.rb + +Rails/ReadWriteAttribute: + Include: + - app/models/**/*.rb + +Rails/ScopeArgs: + Include: + - app/models/**/*.rb + +Rails/Validation: + Include: + - app/models/**/*.rb diff --git a/.rubocop_disabled.yml b/.rubocop_disabled.yml new file mode 100644 index 0000000..52cf287 --- /dev/null +++ b/.rubocop_disabled.yml @@ -0,0 +1,86 @@ +# These are all the cops that are disabled in the default configuration. + +Style/FrozenStringLiteralComment: + Description: >- + Add the frozen_string_literal comment to the top of files + to help transition from Ruby 2.3.0 to Ruby 3.0. + Enabled: false + +Style/InlineComment: + Description: "Avoid inline comments." + Enabled: false + +Style/MethodCalledOnDoEndBlock: + Description: "Avoid chaining a method call on a do...end block." + Enabled: false + +Style/SymbolArray: + Description: "Use %i or %I for arrays of symbols." + Enabled: false + +Style/Documentation: + Description: "Document classes and non-namespace modules." + Enabled: false + +Layout/EmptyLinesAroundAccessModifier: + Description: "Keep blank lines around access modifiers." + Enabled: false + +Style/EmptyLiteral: + Description: "Prefer literals to Array.new/Hash.new/String.new." + Enabled: false + +Style/ClassAndModuleChildren: + Description: "Checks style of children classes and modules." + Enabled: false + +Metrics/ClassLength: + Description: "Avoid classes longer than 100 lines of code." + Enabled: false + +Metrics/MethodLength: + Description: "Avoid methods longer than 10 lines of code." + Enabled: false + +Metrics/ParameterLists: + Description: "Avoid parameter lists longer than three or four parameters." + Enabled: false + +Metrics/CyclomaticComplexity: + Description: "Avoid complex methods." + Enabled: false + +Layout/ArrayAlignment: + Description: >- + Align the elements of an array literal if they span more than + one line. + Enabled: false + +Style/RedundantPercentQ: + Description: "Checks for %q/%Q when single quotes or double quotes would do." + Enabled: false + +Naming/AccessorMethodName: + Description: Check the naming of accessor methods for get_/set_. + Enabled: false + +#################### Lint ################################ +### Warnings +Lint/AssignmentInCondition: + Description: "Don't use assignment in conditions." + Enabled: false + +Rails/HelperInstanceVariable: + Description: "Do not use instance variables in helpers" + Enabled: false + +Rails/InverseOf: + Description: "Prevent fetching loaded data again through a relationship." + Enabled: false + +Rails/ReflectionClassName: + Description: "Use a string for `class_name` option value in the definition of a reflection." + Enabled: false + +Rails/SkipsModelValidations: + Enabled: false diff --git a/.rubocop_enabled.yml b/.rubocop_enabled.yml new file mode 100644 index 0000000..5da7068 --- /dev/null +++ b/.rubocop_enabled.yml @@ -0,0 +1,745 @@ +# These are all the cops that are enabled in the default configuration. + +Layout/AccessModifierIndentation: + Description: Check indentation of private/protected visibility modifiers. + Enabled: true + +Style/Alias: + Description: "Use alias_method instead of alias." + Enabled: true + +Layout/HashAlignment: + Description: >- + Align the elements of a hash literal if they span more than + one line. + Enabled: true + +Layout/Layout/ParameterAlignment: + Description: >- + Align the parameters of a method call if they span more + than one line. + Enabled: true + +Style/AndOr: + Description: "Use &&/|| instead of and/or." + Enabled: true + +Style/ArrayJoin: + Description: "Use Array#join instead of Array#*." + Enabled: true + +Style/AsciiComments: + Description: "Use only ascii symbols in comments." + Enabled: true + +Naming/AsciiIdentifiers: + Description: "Use only ascii symbols in identifiers." + Enabled: true + +Style/Attr: + Description: "Checks for uses of Module#attr." + Enabled: true + +Style/BeginBlock: + Description: "Avoid the use of BEGIN blocks." + Enabled: true + +Style/BlockComments: + Description: "Do not use block comments." + Enabled: true + +Metrics/BlockNesting: + Description: "Avoid excessive block nesting" + Enabled: true + +Style/BlockDelimiters: + Description: >- + Avoid using {...} for multi-line blocks (multiline chaining is + always ugly). + Prefer {...} over do...end for single-line blocks. + Enabled: true + +Style/CaseEquality: + Description: "Avoid explicit use of the case equality operator(===)." + Enabled: true + +Layout/CaseIndentation: + Description: "Indentation of when in a case/when/[else/]end." + Enabled: true + +Style/CharacterLiteral: + Description: "Checks for uses of character literals." + Enabled: true + +Naming/ClassAndModuleCamelCase: + Description: "Use CamelCase for classes and modules." + Enabled: true + +Style/ClassAndModuleChildren: + Description: "Checks style of children classes and modules." + Enabled: true + +Style/ClassCheck: + Description: "Enforces consistent use of `Object#is_a?` or `Object#kind_of?`." + Enabled: true + +Style/ClassMethods: + Description: "Use self when defining module/class methods." + Enabled: true + +Style/ClassVars: + Description: "Avoid the use of class variables." + Enabled: true + +Style/CollectionMethods: + Description: "Preferred collection methods." + Enabled: true + +Style/ColonMethodCall: + Description: "Do not use :: for method call." + Enabled: true + +Style/CommentAnnotation: + Description: >- + Checks formatting of special comments + (TODO, FIXME, OPTIMIZE, HACK, REVIEW). + Enabled: true + +Layout/CommentIndentation: + Description: "Indentation of comments." + Enabled: true + +Naming/ConstantName: + Description: "Constants should use SCREAMING_SNAKE_CASE." + Enabled: true + +Style/DefWithParentheses: + Description: "Use def with parentheses when there are arguments." + Enabled: true + +Style/PreferredHashMethods: + Description: "Checks for use of deprecated Hash methods." + Enabled: true + +Style/Documentation: + Description: "Document classes and non-namespace modules." + Enabled: true + +Layout/DotPosition: + Description: "Checks the position of the dot in multi-line method calls." + Enabled: true + +Style/DoubleNegation: + Description: "Checks for uses of double negation (!!)." + Enabled: true + +Style/EachWithObject: + Description: "Prefer `each_with_object` over `inject` or `reduce`." + Enabled: true + +Layout/EmptyLineBetweenDefs: + Description: "Use empty lines between defs." + Enabled: true + +Layout/EmptyLines: + Description: "Don't use several empty lines in a row." + Enabled: true + +Layout/EmptyLinesAroundAccessModifier: + Description: "Keep blank lines around access modifiers." + Enabled: true + +Layout/EmptyLinesAroundBlockBody: + Description: "Keeps track of empty lines around expression bodies." + Enabled: true + +Layout/EmptyLinesAroundClassBody: + Description: "Keeps track of empty lines around expression classes." + Enabled: true + +Layout/EmptyLinesAroundModuleBody: + Description: "Keeps track of empty lines around expression modules." + Enabled: true + +Style/EmptyLiteral: + Description: "Prefer literals to Array.new/Hash.new/String.new." + Enabled: true + +Style/Encoding: + Description: "Use UTF-8 as the source file encoding." + Enabled: true + +Style/EndBlock: + Description: "Avoid the use of END blocks." + Enabled: true + +Layout/EndOfLine: + Description: "Use Unix-style line endings." + Enabled: true + +Style/EvenOdd: + Description: "Favor the use of Fixnum#even? && Fixnum#odd?" + Enabled: true + +Naming/FileName: + Description: "Use snake_case for source file names." + Enabled: true + +Lint/FlipFlop: + Description: "Checks for flip flops" + Enabled: true + +Style/For: + Description: "Checks use of for or each in multiline loops." + Enabled: true + +Style/FormatString: + Description: "Enforce the use of Kernel#sprintf, Kernel#format or String#%." + Enabled: true + +Style/GlobalVars: + Description: "Do not introduce global variables." + Enabled: true + +Style/GuardClause: + Description: "Check for conditionals that can be replaced with guard clauses" + Enabled: true + +Style/HashSyntax: + Description: >- + Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax + { :a => 1, :b => 2 }. + Enabled: true + +Style/IfUnlessModifier: + Description: >- + Favor modifier if/unless usage when you have a + single-line body. + Enabled: true + +Style/IfWithSemicolon: + Description: "Never use if x; .... Use the ternary operator instead." + Enabled: true + +Layout/IndentationConsistency: + Description: "Keep indentation straight." + Enabled: true + +Layout/IndentationWidth: + Description: "Use 2 spaces for indentation." + Enabled: true + +Layout/FirstArrayElementIndentation: + Description: >- + Checks the indentation of the first element in an array + literal. + Enabled: true + +Layout/FirstHashElementIndentation: + Description: "Checks the indentation of the first key in a hash literal." + Enabled: true + +Style/Lambda: + Description: "Use the new lambda literal syntax for single-line blocks." + Enabled: true + +Style/LambdaCall: + Description: "Use lambda.call(...) instead of lambda.(...)." + Enabled: true + +Layout/LeadingCommentSpace: + Description: "Comments should start with a space." + Enabled: true + +Style/LineEndConcatenation: + Description: >- + Use \ instead of + or << to concatenate two string literals at + line end. + Enabled: true + +Metrics/LineLength: + Description: "Limit lines to 80 characters." + Enabled: true + +Style/MethodCallWithoutArgsParentheses: + Description: "Do not use parentheses for method calls with no arguments." + Enabled: true + +Style/MethodDefParentheses: + Description: >- + Checks if the method definitions have or don"t have + parentheses. + Enabled: true + +Naming/MethodName: + Description: "Use the configured style when naming methods." + Enabled: true + +Style/ModuleFunction: + Description: "Checks for usage of `extend self` in modules." + Enabled: true + +Style/MultilineBlockChain: + Description: "Avoid multi-line chains of blocks." + Enabled: true + +Style/MultilineIfThen: + Description: "Never use then for multi-line if/unless." + Enabled: true + +Style/MultilineTernaryOperator: + Description: >- + Avoid multi-line ?: (the ternary operator); + use if/unless instead. + Enabled: true + +Style/NegatedIf: + Description: >- + Favor unless over if for negative conditions + (or control flow or). + Enabled: true + +Style/NegatedWhile: + Description: "Favor until over while for negative conditions." + Enabled: true + +Style/NestedTernaryOperator: + Description: "Use one expression per branch in a ternary operator." + Enabled: true + +Style/Next: + Description: "Use `next` to skip iteration instead of a condition at the end." + Enabled: true + +Style/NilComparison: + Description: "Prefer x.nil? to x == nil." + Enabled: true + +Style/NonNilCheck: + Description: "Checks for redundant nil checks." + Enabled: true + +Style/Not: + Description: "Use ! instead of not." + Enabled: true + +Style/NumericLiterals: + Description: >- + Add underscores to large numeric literals to improve their + readability. + Enabled: true + +Style/OneLineConditional: + Description: >- + Favor the ternary operator(?:) over + if/then/else/end constructs. + Enabled: true + +Naming/BinaryOperatorParameterName: + Description: "When defining binary operators, name the argument other." + Enabled: true + +Style/ParenthesesAroundCondition: + Description: >- + Don't use parentheses around the condition of an + if/unless/while. + Enabled: true + +Style/PercentLiteralDelimiters: + Description: "Use `%`-literal delimiters consistently" + Enabled: true + +Style/PerlBackrefs: + Description: "Avoid Perl-style regex back references." + Enabled: true + +Naming/PredicateName: + Description: "Check the names of predicate methods." + Enabled: true + +Style/Proc: + Description: "Use proc instead of Proc.new." + Enabled: true + +Style/RaiseArgs: + Description: "Checks the arguments passed to raise/fail." + Enabled: true + +Style/RedundantBegin: + Description: "Don't use begin blocks when they are not needed." + Enabled: true + +Style/RedundantException: + Description: "Checks for an obsolete RuntimeException argument in raise/fail." + Enabled: true + +Style/RedundantReturn: + Description: "Don't use return where it's not required." + Enabled: true + +Style/RedundantSelf: + Description: "Don't use self where it's not needed." + Enabled: true + +Style/RegexpLiteral: + Description: >- + Use %r for regular expressions matching more than + `MaxSlashes` '/' characters. + Use %r only for regular expressions matching more than + `MaxSlashes` '/' character. + Enabled: true + +Style/RescueModifier: + Description: "Avoid using rescue in its modifier form." + Enabled: true + +Style/SelfAssignment: + Description: "Checks for places where self-assignment shorthand should have been used." + Enabled: true + +Style/Semicolon: + Description: "Don't use semicolons to terminate expressions." + Enabled: true + +Style/SignalException: + Description: "Checks for proper usage of fail and raise." + Enabled: true + +Style/SingleLineBlockParams: + Description: "Enforces the names of some block params." + Enabled: true + +Style/SingleLineMethods: + Description: "Avoid single-line methods." + Enabled: true + +Layout/SpaceBeforeFirstArg: + Description: >- + Checks that exactly one space is used between a method name + and the first argument for method calls without parentheses. + Enabled: true + +Layout/SpaceAfterColon: + Description: "Use spaces after colons." + Enabled: true + +Layout/SpaceAfterComma: + Description: "Use spaces after commas." + Enabled: true + +Layout/SpaceAroundKeyword: + Description: "Use spaces after if/elsif/unless/while/until/case/when." + Enabled: true + +Layout/SpaceAfterMethodName: + Description: >- + Never put a space between a method name and the opening + parenthesis in a method definition. + Enabled: true + +Layout/SpaceAfterNot: + Description: Tracks redundant space after the ! operator. + Enabled: true + +Layout/SpaceAfterSemicolon: + Description: "Use spaces after semicolons." + Enabled: true + +Layout/SpaceBeforeBlockBraces: + Description: >- + Checks that the left block brace has or doesn't have space + before it. + Enabled: true + +Layout/SpaceBeforeComma: + Description: "No spaces before commas." + Enabled: true + +Layout/SpaceBeforeComment: + Description: >- + Checks for missing space between code and a comment on the + same line. + Enabled: true + +Layout/SpaceBeforeSemicolon: + Description: "No spaces before semicolons." + Enabled: true + +Layout/SpaceInsideBlockBraces: + Description: >- + Checks that block braces have or don't have surrounding space. + For blocks taking parameters, checks that the left brace has + or doesn"t have trailing space. + Enabled: true + +Layout/SpaceAroundEqualsInParameterDefault: + Description: >- + Checks that the equals signs in parameter default assignments + have or don't have surrounding space depending on + configuration. + Enabled: true + +Layout/SpaceAroundOperators: + Description: "Use spaces around operators." + Enabled: true + +Layout/SpaceInsideHashLiteralBraces: + Description: "Use spaces inside hash literal braces - or don't." + Enabled: true + +Layout/SpaceInsideParens: + Description: "No spaces after ( or before )." + Enabled: true + +Style/SpecialGlobalVars: + Description: "Avoid Perl-style global variables." + Enabled: true + +Style/StringLiterals: + Description: "Checks if uses of quotes match the configured preference." + Enabled: true + +Layout/IndentationStyle: + Description: "No hard tabs." + Enabled: true + +Layout/TrailingEmptyLines: + Description: "Checks trailing blank lines and final newline." + Enabled: true + +Style/TrailingCommaInArguments: + Description: 'Checks for trailing comma in argument lists.' + StyleGuide: '#no-trailing-params-comma' + Enabled: true + +Style/TrailingCommaInArrayLiteral: + Description: 'Checks for trailing comma in array literals.' + StyleGuide: '#no-trailing-array-commas' + Enabled: true + +Style/TrailingCommaInHashLiteral: + Description: 'Checks for trailing comma in hash literals.' + Enabled: true + +Layout/TrailingWhitespace: + Description: "Avoid trailing whitespace." + Enabled: true + +Style/TrivialAccessors: + Description: "Prefer attr_* methods to trivial readers/writers." + Enabled: true + +Style/UnlessElse: + Description: >- + Never use unless with else. Rewrite these with the positive + case first. + Enabled: true + +Style/RedundantCapitalW: + Description: "Checks for %W when interpolation is not needed." + Enabled: true + +Style/CommandLiteral: + Description: "Checks for %x when `` would do." + Enabled: true + +Style/VariableInterpolation: + Description: >- + Don't interpolate global, instance and class variables + directly in strings. + Enabled: true + +Naming/VariableName: + Description: "Use the configured style when naming variables." + Enabled: true + +Style/WhenThen: + Description: "Use when x then ... for one-line cases." + Enabled: true + +Style/WhileUntilDo: + Description: "Checks for redundant do after while or until." + Enabled: true + +Style/WhileUntilModifier: + Description: >- + Favor modifier while/until usage when you have a + single-line body. + Enabled: true + +Style/WordArray: + Description: "Use %w or %W for arrays of words." + Enabled: true + +#################### Lint ################################ +### Warnings + +Lint/AmbiguousOperator: + Description: >- + Checks for ambiguous operators in the first argument of a + method invocation without parentheses. + Enabled: true + +Lint/AmbiguousRegexpLiteral: + Description: >- + Checks for ambiguous regexp literals in the first argument of + a method invocation without parenthesis. + Enabled: true + +Layout/BlockAlignment: + Description: "Align block ends correctly." + Enabled: true + +Layout/ConditionPosition: + Description: "Checks for condition placed in a confusing position relative to the keyword." + Enabled: true + +Lint/Debugger: + Description: "Check for debugger calls." + Enabled: true + +Layout/DefEndAlignment: + Description: "Align ends corresponding to defs correctly." + Enabled: true + +Lint/DeprecatedClassMethods: + Description: "Check for deprecated class method calls." + Enabled: true + +Lint/ElseLayout: + Description: "Check for odd code arrangement in an else block." + Enabled: true + +Lint/EmptyEnsure: + Description: "Checks for empty ensure block." + Enabled: true + +Lint/EmptyInterpolation: + Description: "Checks for empty string interpolation." + Enabled: true + +Layout/EndAlignment: + Description: "Align ends correctly." + Enabled: true + +Style/EndBlock: + Description: "END blocks should not be placed inside method definitions." + Enabled: true + +Lint/EnsureReturn: + Description: "Never use return in an ensure block." + Enabled: true + +Security/Eval: + Description: "The use of eval represents a serious security risk." + Enabled: true + +Lint/SuppressedException: + Description: "Don't suppress exception." + Enabled: true + +Lint/LiteralInInterpolation: + Description: "Checks for literals used in interpolation." + Enabled: true + +Lint/Loop: + Description: >- + Use Kernel#loop with break rather than begin/end/until or + begin/end/while for post-loop tests. + Enabled: true + +Lint/ParenthesesAsGroupedExpression: + Description: >- + Checks for method calls with a space before the opening + parenthesis. + Enabled: true + +Lint/RequireParentheses: + Description: >- + Use parentheses in the method call to avoid confusion + about precedence. + Enabled: true + +Lint/RescueException: + Description: "Avoid rescuing the Exception class." + Enabled: true + +Lint/ShadowingOuterLocalVariable: + Description: >- + Do not use the same name as outer local variable + for block arguments or block local variables. + Enabled: true + +Lint/RedundantStringCoercion: + Description: "Checks for Object#to_s usage in string interpolation." + Enabled: true + +Lint/UnderscorePrefixedVariableName: + Description: "Do not use prefix `_` for a variable that is used." + Enabled: true + +Lint/UnusedBlockArgument: + Description: "Checks for unused block arguments." + Enabled: true + +Lint/UnusedMethodArgument: + Description: "Checks for unused method arguments." + Enabled: true + +Lint/UnreachableCode: + Description: "Unreachable code." + Enabled: true + +Lint/UselessAccessModifier: + Description: "Checks for useless access modifiers." + Enabled: true + +Lint/UselessAssignment: + Description: "Checks for useless assignment to a local variable." + Enabled: true + +Lint/BinaryOperatorWithIdenticalOperands: + Description: "Checks for comparison of something with itself." + Enabled: true + +Lint/UselessElseWithoutRescue: + Description: "Checks for useless `else` in `begin..end` without `rescue`." + Enabled: true + +Lint/UselessSetterCall: + Description: "Checks for useless setter call to a local variable." + Enabled: true + +Lint/Void: + Description: "Possible use of operator/literal/variable in void context." + Enabled: true + +##################### Rails ################################## + +Rails/ActionFilter: + Description: "Enforces consistent use of action filter methods." + Enabled: true + +Rails/Delegate: + Description: "Prefer delegate method for delegations." + Enabled: true + +Rails/HasAndBelongsToMany: + Description: "Prefer has_many :through to has_and_belongs_to_many." + Enabled: true + +Rails/Output: + Description: "Checks for calls to puts, print, etc." + Enabled: true + +Rails/ReadWriteAttribute: + Description: "Checks for read_attribute(:attr) and write_attribute(:attr, val)." + Enabled: true + +Rails/ScopeArgs: + Description: "Checks the arguments of ActiveRecord scopes." + Enabled: true + +Rails/Validation: + Description: "Use sexy validations." + Enabled: true diff --git a/.sun-ci.yml b/.sun-ci.yml index bfe5f3d..40abbc8 100644 --- a/.sun-ci.yml +++ b/.sun-ci.yml @@ -8,16 +8,16 @@ jobs: image: sunci/ruby:3.2.2 script: - cp database-ci.yml config/database.yml - - bundle _2.5.23_ install --path vendor/bundle + - bundle _2.1.4_ install --path vendor/bundle # _2.1.4_ if ruby 2.7.1 or 3.0.2 cache: - key: vendor_$CI_COMMIT_REF_NAME - paths: - - vendor/bundle + - key: vendor_$CI_BRANCH + paths: + - vendor/bundle - name: test:rspec stage: test image: sunci/ruby:3.2.2 services: - - image: mysql:5.7.20 + - image: mysql:5.7.22 name: mysql-test environment: MYSQL_DATABASE: db-test @@ -25,15 +25,14 @@ jobs: MYSQL_PASSWORD: password-test MYSQL_ROOT_PASSWORD: password-test before_script: - - bundle _2.5.23_ install --path vendor/bundle + - bundle _2.1.4_ install --path vendor/bundle # _2.1.4_ if ruby 2.7.1 or 3.0.2 script: - RAILS_ENV=test bundle exec rails db:drop db:create db:migrate - - bundle _2.5.23_ exec rspec - + - bundle exec rspec # _2.1.4_ if ruby 2.7.1 or 3.0.2 - name: test:rubocop stage: test image: sunci/ruby:3.2.2 before_script: - - bundle _2.5.23_ install --path vendor/bundle + - bundle _2.1.4_ install --path vendor/bundle # _2.1.4_ if ruby 2.7.1 or 3.0.2 script: - bundle exec rubocop --require rubocop/formatter/checkstyle_formatter --format RuboCop::Formatter::CheckstyleFormatter --no-color app/ lib/ diff --git a/Gemfile b/Gemfile index f6a53ea..e75bbf4 100644 --- a/Gemfile +++ b/Gemfile @@ -1,31 +1,30 @@ source "https://rubygems.org" gem "bcrypt", "~> 3.1.7" +gem "bootsnap", require: false gem "bootstrap-sass", "3.4.1" gem "config" gem "faker" -gem "rails", "~> 7.2.2" -gem "sprockets-rails" +gem "i18n" +gem "importmap-rails" +gem "jbuilder" gem "mysql2", "~> 0.5" -gem "puma", ">= 5.0" gem "pagy" -gem "importmap-rails" -gem "i18n" -gem "turbo-rails" -gem "stimulus-rails" +gem "puma", ">= 5.0" +gem "rails", "~> 7.2.2" gem "sassc-rails", "2.1.2" -gem "jbuilder" -gem "tzinfo-data", platforms: %i[ mswin mswin64 mingw x64_mingw jruby ] - -gem "bootsnap", require: false - +gem "sprockets-rails" +gem "stimulus-rails" +gem "turbo-rails" +gem "tzinfo-data", platforms: %i(mswin mswin64 mingw x64_mingw jruby) group :development, :test do - gem "debug", platforms: %i[ mri mswin mswin64 mingw x64_mingw ], require: "debug/prelude" - gem "brakeman", require: false - - gem "rubocop-rails-omakase", require: false + gem "debug", platforms: %i(mri mingw x64_mingw) + gem "rspec-rails" + gem "rubocop", "~> 1.26", require: false + gem "rubocop-checkstyle_formatter", require: false + gem "rubocop-rails", "~> 2.14.0", require: false end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index f4f4643..9d4daeb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -110,18 +110,13 @@ GEM irb (~> 1.10) reline (>= 0.3.8) deep_merge (1.2.2) + diff-lcs (1.5.1) drb (2.2.1) erubi (1.13.0) execjs (2.10.0) faker (3.5.1) i18n (>= 1.8.11, < 2) - ffi (1.17.0) - ffi (1.17.0-aarch64-linux-gnu) - ffi (1.17.0-arm-linux-gnu) - ffi (1.17.0-arm64-darwin) - ffi (1.17.0-x86-linux-gnu) - ffi (1.17.0-x86_64-darwin) - ffi (1.17.0-x86_64-linux-gnu) + ffi (1.17.1) globalid (1.2.1) activesupport (>= 6.1) i18n (1.14.6) @@ -237,6 +232,23 @@ GEM reline (0.5.12) io-console (~> 0.5) rexml (3.3.9) + rspec-core (3.13.2) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-rails (7.1.0) + actionpack (>= 7.0) + activesupport (>= 7.0) + railties (>= 7.0) + rspec-core (~> 3.13) + rspec-expectations (~> 3.13) + rspec-mocks (~> 3.13) + rspec-support (~> 3.13) + rspec-support (3.13.2) rubocop (1.69.1) json (~> 2.3) language_server-protocol (>= 3.17.0) @@ -249,22 +261,12 @@ GEM unicode-display_width (>= 2.4.0, < 4.0) rubocop-ast (1.36.2) parser (>= 3.3.1.0) - rubocop-minitest (0.36.0) - rubocop (>= 1.61, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) - rubocop-performance (1.23.0) - rubocop (>= 1.48.1, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rails (2.27.0) + rubocop-checkstyle_formatter (0.6.0) + rubocop (>= 1.14.0) + rubocop-rails (2.14.2) activesupport (>= 4.2.0) rack (>= 1.1) - rubocop (>= 1.52.0, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rails-omakase (1.0.0) - rubocop - rubocop-minitest - rubocop-performance - rubocop-rails + rubocop (>= 1.7.0, < 2.0) ruby-progressbar (1.13.0) rubyzip (2.3.2) sassc (2.4.0) @@ -342,7 +344,10 @@ DEPENDENCIES pagy puma (>= 5.0) rails (~> 7.2.2) - rubocop-rails-omakase + rspec-rails + rubocop (~> 1.26) + rubocop-checkstyle_formatter + rubocop-rails (~> 2.14.0) sassc-rails (= 2.1.2) selenium-webdriver sprockets-rails diff --git a/app/assets/stylesheets/_variables.scss b/app/assets/stylesheets/_variables.scss index 77f8721..88689e3 100644 --- a/app/assets/stylesheets/_variables.scss +++ b/app/assets/stylesheets/_variables.scss @@ -1,9 +1,10 @@ $gray-light: #777; $gray-medium-light: #eaeaea; -$gray-medium-light: #eaeaea; $border-color: #bbb; $state-danger-text: red; $white: #ffffff; +$background-comment: #f1f1f1; +$time-step: #999; @mixin box_sizing { -moz-box-sizing: border-box; diff --git a/app/assets/stylesheets/custom.scss b/app/assets/stylesheets/custom.scss index 6af02a2..14c0f88 100644 --- a/app/assets/stylesheets/custom.scss +++ b/app/assets/stylesheets/custom.scss @@ -130,3 +130,72 @@ input { background: rgba(0, 0, 0, 0.5); z-index: 1000; } + +/* Comments */ +.chat-container { + display: flex; + flex-direction: column; + gap: 10px; +} + +.chat-comments { + max-height: 300px; + overflow-y: auto; +} + +.chat-comment { + display: flex; + margin-bottom: 15px; +} + +.chat-comment.sent { + justify-content: flex-end; +} + +.chat-comment.received { + justify-content: flex-start; +} + +.comment-content { + max-width: 70%; + background-color: $background-comment; + padding: 10px; + border-radius: 5px; + position: relative; +} + +.chat-comment .avatar { + margin-right: 10px; +} + +.chat-comment .avatar-img { + width: 35px; + height: 35px; + border-radius: 50%; +} + +.username { + font-weight: bold; +} + +.text { + margin-top: 5px; +} + +.timestamp { + font-size: 0.8em; + color: $time-step; + position: absolute; + bottom: 5px; + right: 10px; +} + +.chat-input { + margin-top: 20px; +} + +.comments--box { + border-radius: 10px; + border: solid 1px; + padding: 5px; +} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2f0027e..4d55bfd 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,8 +1,18 @@ class ApplicationController < ActionController::Base include SessionsHelper include Pagy::Backend + rescue_from Exception, with: :render_not_found + + private + def render_not_found _exception + render plain: "404 Not Found", status: :not_found + end def logged_in_user - logged_in? + return if logged_in? + + store_location + flash[:danger] = t "user.please_login" + redirect_to login_url, status: :see_other end end diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb new file mode 100644 index 0000000..8d9b3e1 --- /dev/null +++ b/app/controllers/comments_controller.rb @@ -0,0 +1,41 @@ +class CommentsController < ApplicationController + before_action :set_task + before_action :set_comment, only: %i(destroy) + + def create + @comment = @task.comments.build comment_params + @comment.sender = current_user + + if @comment.save + redirect_to @task + else + flash[:alert] = t "tasks.comment.failed_to_add_comment" + end + end + + def destroy + if @comment.destroy + flash[:success] = t "tasks.comment.comment_deleted_successfully" + else + flash[:alert] = t "tasks.comment.failed_to_delete_comment" + end + redirect_to @task + end + + private + + def comment_params + params.require(:comment).permit Comment::COMMENT_PERMITTED_ATTRIBUTES + end + + def set_task + @task = Task.find params[:task_id] + rescue ActiveRecord::RecordNotFound + flash[:alert] = t "tasks.not_found" + redirect_to tasks_path + end + + def set_comment + @comment = @task.comments.find params[:id] + end +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 3076d52..3f44b58 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -3,23 +3,28 @@ def new; end def create user = User.find_by email: params[:session][:email].downcase - if user&.authenticate params[:session][:password] - forwarding_url = session[:forwarding_url] - reset_session + return unless user&.authenticate params[:session][:password] - if params[:session][:remember_me] == "1" - remember(user) - else - forget(user) - end - - log_in user - redirect_to forwarding_url || root_url - end + handle_remember_me(user) + log_in user + forwarding_url = session[:forwarding_url] + redirect_to forwarding_url || root_url end def destroy log_out if logged_in? + flash[:success] = t "user.logout_successfully" redirect_to login_path, status: :see_other end + + private + + def handle_remember_me user + reset_session + if params[:session][:remember_me] == "1" + remember(user) + else + forget(user) + end + end end diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb index 0711f99..b41f560 100644 --- a/app/controllers/tasks_controller.rb +++ b/app/controllers/tasks_controller.rb @@ -1,16 +1,23 @@ class TasksController < ApplicationController - before_action :set_task, only: %i[edit update destroy] - before_action :logged_in_user, only: %i[create edit destroy] - before_action :set_categories, :set_user, :available_users, only: %i[new index create edit] + before_action :set_task, only: %i(edit update destroy) + before_action :logged_in_user, only: %i(create edit destroy) + before_action :set_categories, :set_user, :available_users, + only: %i(new index create edit) + before_action :set_comment, only: %i(edit) def new @task = Task.new end def index - @tasks = current_user.mentor_role? ? Task.by_mentor_and_mentees(current_user.id) : current_user.tasks + @tasks = if current_user.mentor_role? + Task.by_mentor_and_mentees current_user.id + else + Task.by_naitei current_user.id + end - @pagy, @tasks = pagy @tasks, limit: Settings.default.max_tasks_per_page_5 + @pagy, @tasks = + pagy @tasks, limit: Settings.default.max_tasks_per_page_5 end def create @@ -25,7 +32,8 @@ def create def edit @subtasks = @task.subtasks - @pagy, @subtasks = pagy @subtasks, limit: Settings.default.max_tasks_per_page_5 + @pagy, @subtasks = + pagy @subtasks, limit: Settings.default.max_tasks_per_page_5 end def update @@ -65,10 +73,14 @@ def set_task end def available_users - @users = current_user.mentor_role? ? current_user.mentees : [ current_user ] + @users = current_user.mentor_role? ? current_user.mentees : [current_user] end def set_categories @categories = current_user.categories end + + def set_comment + @comments = @task.comments + end end diff --git a/app/helpers/comments_helper.rb b/app/helpers/comments_helper.rb new file mode 100644 index 0000000..6a1588e --- /dev/null +++ b/app/helpers/comments_helper.rb @@ -0,0 +1,15 @@ +module CommentsHelper + def comment_user comment, current_user + comment.sender == current_user ? :sent : :received + end + + def comment_delete_button comment, task + return unless comment.sender == current_user + + button_to t("tasks.comment.delete"), + task_comment_path(task, comment), + method: :delete, + data: {confirm: t("tasks.comment.confirm_delete")}, + class: "btn btn-danger btn-sm" + end +end diff --git a/app/helpers/tasks_helper.rb b/app/helpers/tasks_helper.rb index b815fe5..505a7ed 100644 --- a/app/helpers/tasks_helper.rb +++ b/app/helpers/tasks_helper.rb @@ -12,20 +12,10 @@ def task_table_headers end def priority_options - Task.priorities.keys.map { |p| [ p.humanize, p ] } + Task.priorities.keys.map{|p| [p.humanize, p]} end def status_options - Task.statuses.keys.map { |s| [ s.humanize, s ] } - end - - def assignee_select(f, task, users, current_user) - if current_user.mentor_role? - f.select :assignee_id, options_from_collection_for_select(users, :id, :name, task.assignee_id), { include_blank: t("tasks.select_assignee") }, class: "form-control" - elsif current_user.naitei_role? - f.select :assignee_id, options_from_collection_for_select([ current_user ], :id, :name, task.assignee_id), { include_blank: t("tasks.select_assignee") }, class: "form-control" - else - f.select :assignee_id, [], { include_blank: t("tasks.select_assignee") }, class: "form-control" - end + Task.statuses.keys.map{|s| [s.humanize, s]} end end diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb index d394c3d..a009ace 100644 --- a/app/jobs/application_job.rb +++ b/app/jobs/application_job.rb @@ -1,7 +1,2 @@ class ApplicationJob < ActiveJob::Base - # Automatically retry jobs that encountered a deadlock - # retry_on ActiveRecord::Deadlocked - - # Most jobs are safe to ignore if the underlying records are no longer available - # discard_on ActiveJob::DeserializationError end diff --git a/app/models/activity.rb b/app/models/activity.rb index 210ddf0..bc6cab4 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -3,5 +3,7 @@ class Activity < ApplicationRecord belongs_to :task, optional: true validates :action, presence: true - validates :description, length: { maximum: Settings.default.comment_max_length_10_000 }, allow_blank: true + validates :description, + length: {maximum: Settings.default.comment_max_length_10_000}, + allow_blank: true end diff --git a/app/models/category.rb b/app/models/category.rb index d685e01..5c7f6a7 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -2,5 +2,6 @@ class Category < ApplicationRecord belongs_to :user has_many :tasks, dependent: :destroy - validates :name, presence: true, length: { maximum: Settings.default.category_max_length_255 } + validates :name, presence: true, + length: {maximum: Settings.default.category_max_length_255} end diff --git a/app/models/comment.rb b/app/models/comment.rb index a0721cf..b469be9 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,6 +1,8 @@ class Comment < ApplicationRecord belongs_to :task - belongs_to :user + belongs_to :sender, class_name: "User", foreign_key: "user_id" + COMMENT_PERMITTED_ATTRIBUTES = %i(content).freeze - validates :content, presence: true, length: { maximum: Settings.default.comment_max_length_10_000 } + validates :content, presence: true, + length: {maximum: Settings.default.comment_max_length_10_000} end diff --git a/app/models/task.rb b/app/models/task.rb index 5554d42..d0d8394 100644 --- a/app/models/task.rb +++ b/app/models/task.rb @@ -2,28 +2,62 @@ class Task < ApplicationRecord belongs_to :user belongs_to :category, optional: true belongs_to :parent_task, class_name: "Task", optional: true - has_many :subtasks, class_name: "Task", foreign_key: :parent_task_id, dependent: :destroy + has_many :subtasks, class_name: "Task", + foreign_key: :parent_task_id, dependent: :destroy has_many :comments, dependent: :destroy has_many :activities, dependent: :destroy enum priority: Settings.default.priorities.to_h.transform_keys(&:to_sym) enum status: Settings.default.status.to_h.transform_keys(&:to_sym) - validates :title, presence: true, length: { maximum: Settings.default.task_title_max_length } + validates :title, presence: true, + length: {maximum: Settings.default.task_title_max_length} validates :priority, presence: true validates :status, presence: true - validates :deadline, comparison: { greater_than: :start_date, allow_blank: true }, if: :start_date? - TASK_PERMITTED_ATTRIBUTES = %i[title description priority status start_date deadline category_id assignee_id].freeze - SUBTASK_PERMITTED_ATTRIBUTES = %i[title description priority status start_date deadline category_id assignee_id parent_task_id].freeze + validates :deadline, + comparison: {greater_than: :start_date, allow_blank: true}, + if: :start_date? - scope :by_naitei, ->(user_id) { where("user_id = :user_id OR assignee_id = :user_id", user_id: user_id) } - scope :by_mentor_and_mentees, ->(mentor_id) { + TASK_PERMITTED_ATTRIBUTES = %i( + title description priority status start_date + deadline category_id assignee_id + ).freeze + + SUBTASK_PERMITTED_ATTRIBUTES = %i( + title description priority status start_date + deadline category_id assignee_id parent_task_id + ).freeze + + scope :by_naitei, lambda {|user_id| + where( + "parent_task_id IS NULL + AND (user_id = :user_id OR assignee_id = :user_id)", + user_id: user_id + ) + } + scope :by_mentor_and_mentees, lambda {|mentor_id| joins("LEFT JOIN users AS mentees ON mentees.id = tasks.assignee_id") - .where("tasks.user_id = :mentor_id OR mentees.mentor_id = :mentor_id", mentor_id: mentor_id) + .where( + "parent_task_id IS NULL + AND (tasks.user_id = :mentor_id OR mentees.mentor_id = :mentor_id)", + mentor_id: mentor_id + ) + } + scope :by_priority, lambda {|priority| + where("parent_task_id IS NULL AND priority = ?", priorities[priority]) + } + scope :by_status, lambda {|status| + where("parent_task_id IS NULL AND status = ?", statuses[status]) + } + scope :filter_by_category, lambda {|category| + if category.present? + where("parent_task_id IS NULL AND category_id = ?", category) + end + } + scope :filter_by_status, lambda {|status| + where("parent_task_id IS NULL AND status = ?", status) if status.present? + } + scope :filter_by_deadline, lambda {|deadline| + where("deadline <= ?", deadline) if deadline.present? } - scope :by_priority, ->(priority) { where(priority: priorities[priority]) } - scope :by_status, ->(status) { where(status: statuses[status]) } - scope :filter_by_category, ->(category) { where(category_id: category) if category.present? } - scope :filter_by_status, ->(status) { where(status: status) if status.present? } - scope :filter_by_deadline, ->(deadline) { where("deadline <= ?", deadline) if deadline.present? } end diff --git a/app/models/user.rb b/app/models/user.rb index f715322..6eb7f89 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -5,23 +5,29 @@ class User < ApplicationRecord attr_accessor :remember_token, :activation_token, :reset_token - USER_PERMITTED_ATTRIBUTES = %i[name email password].freeze + USER_PERMITTED_ATTRIBUTES = %i(name email password).freeze has_many :tasks, dependent: :destroy - has_many :mentees, class_name: "User", foreign_key: :mentor_id, dependent: :nullify + has_many :mentees, class_name: "User", + foreign_key: :mentor_id, dependent: :nullify belongs_to :mentor, class_name: "User", optional: true - has_many :created_tasks, class_name: "Task", foreign_key: :creator_id, dependent: :destroy - has_many :assigned_tasks, class_name: "Task", foreign_key: :assignee_id, dependent: :nullify + has_many :created_tasks, class_name: "Task", + foreign_key: :creator_id, dependent: :destroy + has_many :assigned_tasks, class_name: "Task", + foreign_key: :assignee_id, dependent: :nullify has_many :categories, dependent: :destroy has_many :activities, dependent: :destroy has_many :comments, dependent: :destroy has_many :task_participants, dependent: :destroy - has_many :participated_tasks, through: :task_participants, source: :task - enum role: Settings.default.roles.to_h.transform_keys(&:to_sym), _suffix: true - - validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP } - validates :name, presence: true, length: { maximum: Settings.default.username_max_length_100 } + enum role: Settings.default.roles.to_h.transform_keys(&:to_sym), + _suffix: true + validates :email, + presence: true, + uniqueness: true, + format: {with: URI::MailTo::EMAIL_REGEXP} + validates :name, presence: true, + length: {maximum: Settings.default.username_max_length_100} validates :role, presence: true before_save :downcase_email @@ -34,9 +40,9 @@ def new_token def digest string cost = if ActiveModel::SecurePassword.min_cost BCrypt::Engine::MIN_COST - else - BCrypt::Engine.cost - end + else + BCrypt::Engine.cost + end BCrypt::Password.create(string, cost:) end end @@ -47,7 +53,7 @@ def remember remember_digest end - def authenticated?(attribute, token) + def authenticated? attribute, token digest = send "#{attribute}_digest" return false if digest.nil? diff --git a/app/views/comments/_chat.html.erb b/app/views/comments/_chat.html.erb new file mode 100644 index 0000000..307d65b --- /dev/null +++ b/app/views/comments/_chat.html.erb @@ -0,0 +1,18 @@ +
<%= t("tasks.index.messages.no_subtasks") %>
- <% end %> + <% if @subtasks.present? && @subtasks.any? %> + <%= render "task_table", tasks: @subtasks, pagy: @pagy %> + <% else %> +<%= t("tasks.index.messages.no_subtasks") %>
+ <% end %> - +
<%= t "tasks.comment.no_comments_yet" %>
+ <% end %> +