├── .github ├── screenshot1.png ├── screenshot2.png ├── screenshot3.png ├── screenshot4.png ├── screenshot5.png └── workflows │ ├── lint.yml │ └── test.yml ├── .gitignore ├── .rubocop.yml ├── .ruby-version ├── Gemfile ├── MIT-LICENSE ├── README.md ├── Rakefile ├── active_insights.gemspec ├── app ├── assets │ └── stylesheets │ │ └── active_insights │ │ ├── application.css │ │ ├── calibre-bold.woff2 │ │ ├── calibre-regular-italic.woff2 │ │ ├── calibre-regular.woff2 │ │ ├── calibre-semibold-italic.woff2 │ │ └── calibre-semibold.woff2 ├── controllers │ └── active_insights │ │ ├── application_controller.rb │ │ ├── jobs_controller.rb │ │ ├── jobs_latencies_controller.rb │ │ ├── jobs_p_values_controller.rb │ │ ├── jpm_controller.rb │ │ ├── requests_controller.rb │ │ ├── requests_p_values_controller.rb │ │ └── rpm_controller.rb ├── helpers │ └── active_insights │ │ └── application_helper.rb ├── models │ └── active_insights │ │ ├── job.rb │ │ ├── record.rb │ │ └── request.rb └── views │ ├── active_insights │ ├── jobs │ │ └── index.html.erb │ ├── jobs_latencies │ │ └── index.html.erb │ ├── jobs_p_values │ │ └── index.html.erb │ ├── jpm │ │ └── index.html.erb │ ├── requests │ │ └── index.html.erb │ ├── requests_p_values │ │ └── index.html.erb │ └── rpm │ │ └── index.html.erb │ └── layouts │ └── active_insights │ └── application.html.erb ├── bin ├── importmap ├── publish ├── rails ├── setup └── test ├── config ├── initializers │ └── importmap.rb └── routes.rb ├── db └── migrate │ ├── 20240111225806_create_active_insights_tables.rb │ ├── 20241015142450_add_ip_address_to_requests.rb │ └── 20241016142157_add_user_agent_to_requests.rb ├── lib ├── active_insights.rb ├── active_insights │ ├── engine.rb │ ├── seeders │ │ ├── jobs.rb │ │ └── requests.rb │ └── version.rb ├── activeinsights.rb ├── generators │ └── active_insights │ │ ├── install │ │ └── install_generator.rb │ │ └── update │ │ └── update_generator.rb └── tasks │ └── active_insights_tasks.rake └── test ├── active_insights_test.rb ├── controllers └── active_insights │ ├── jobs_controller_test.rb │ ├── jobs_latencies_controller_test.rb │ ├── jobs_p_values_controller_test.rb │ ├── jpm_controller_test.rb │ ├── requests_controller_test.rb │ ├── requests_p_values_controller_test.rb │ └── rpm_controller_test.rb ├── dummy ├── Rakefile ├── app │ ├── assets │ │ ├── images │ │ │ └── .keep │ │ └── stylesheets │ │ │ └── application.css │ ├── channels │ │ └── application_cable │ │ │ ├── channel.rb │ │ │ └── connection.rb │ ├── controllers │ │ ├── application_controller.rb │ │ ├── concerns │ │ │ └── .keep │ │ └── make_requests_controller.rb │ ├── helpers │ │ └── application_helper.rb │ ├── javascript │ │ └── application.js │ ├── jobs │ │ ├── application_job.rb │ │ └── dummy_job.rb │ ├── mailers │ │ └── application_mailer.rb │ ├── models │ │ ├── application_record.rb │ │ └── concerns │ │ │ └── .keep │ └── views │ │ ├── layouts │ │ └── application.html.erb │ │ └── make_requests │ │ └── index.html.erb ├── bin │ ├── importmap │ ├── rails │ ├── rake │ └── setup ├── config.ru ├── config │ ├── application.rb │ ├── boot.rb │ ├── cable.yml │ ├── database.pg.yml │ ├── database.trilogy.yml │ ├── database.yml │ ├── environment.rb │ ├── environments │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── importmap.rb │ ├── initializers │ │ ├── content_security_policy.rb │ │ ├── filter_parameter_logging.rb │ │ ├── inflections.rb │ │ └── permissions_policy.rb │ ├── locales │ │ └── en.yml │ ├── puma.rb │ ├── routes.rb │ ├── solid_queue.yml │ └── storage.yml ├── db │ └── schema.rb ├── log │ └── .keep └── public │ ├── 404.html │ ├── 422.html │ ├── 500.html │ ├── apple-touch-icon-precomposed.png │ ├── apple-touch-icon.png │ └── favicon.ico ├── fixtures └── active_insights │ ├── jobs.yml │ └── requests.yml └── test_helper.rb /.github/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npezza93/activeinsights/7fea0352e68dc9c9ad2391a0629bb6d282873e01/.github/screenshot1.png -------------------------------------------------------------------------------- /.github/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npezza93/activeinsights/7fea0352e68dc9c9ad2391a0629bb6d282873e01/.github/screenshot2.png -------------------------------------------------------------------------------- /.github/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npezza93/activeinsights/7fea0352e68dc9c9ad2391a0629bb6d282873e01/.github/screenshot3.png -------------------------------------------------------------------------------- /.github/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npezza93/activeinsights/7fea0352e68dc9c9ad2391a0629bb6d282873e01/.github/screenshot4.png -------------------------------------------------------------------------------- /.github/screenshot5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npezza93/activeinsights/7fea0352e68dc9c9ad2391a0629bb6d282873e01/.github/screenshot5.png -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: lint 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | name: Linting 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Set up Ruby 16 | uses: ruby/setup-ruby@v1 17 | - name: Install dependencies 18 | run: | 19 | sudo apt-get install libsqlite3-dev 20 | bundle install 21 | - name: Run tests 22 | run: bundle exec rubocop --config=./.rubocop.yml --parallel 23 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | build-sqlite: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Set up Ruby 15 | uses: ruby/setup-ruby@v1 16 | with: 17 | bundler-cache: true 18 | - name: Install dependencies 19 | run: | 20 | sudo apt-get install libsqlite3-dev -y 21 | bundle install 22 | - name: Run tests 23 | run: | 24 | bin/rails db:create db:test:prepare 25 | bin/test 26 | 27 | build-pg: 28 | runs-on: ubuntu-latest 29 | env: 30 | POSTGRES_USER: postgres 31 | POSTGRES_PASSWORD: postgres 32 | services: 33 | postgres: 34 | image: postgres:latest 35 | ports: 36 | - 5432:5432 37 | env: 38 | POSTGRES_USER: ${{ env.POSTGRES_USER }} 39 | POSTGRES_PASSWORD: ${{ env.POSTGRES_PASSWORD }} 40 | steps: 41 | - uses: actions/checkout@v4 42 | - name: Set up Ruby 43 | uses: ruby/setup-ruby@v1 44 | with: 45 | bundler-cache: true 46 | - name: Install dependencies 47 | run: | 48 | sudo apt-get -yqq install libpq-dev 49 | bundle install 50 | mv test/dummy/config/database.pg.yml test/dummy/config/database.yml 51 | bin/rails db:create db:test:prepare 52 | - name: Run tests 53 | run: bin/test 54 | 55 | build-mysql: 56 | runs-on: ubuntu-latest 57 | services: 58 | mysql: 59 | image: mysql:8.0 60 | env: 61 | MYSQL_ALLOW_EMPTY_PASSWORD: yes 62 | ports: 63 | - 3306 64 | steps: 65 | - uses: actions/checkout@v4 66 | - name: Set up Ruby 67 | uses: ruby/setup-ruby@v1 68 | with: 69 | bundler-cache: true 70 | - name: Install dependencies 71 | run: | 72 | sudo service mysql start 73 | sudo mysqladmin -proot password '' 74 | mv test/dummy/config/database.trilogy.yml test/dummy/config/database.yml 75 | bin/rails db:drop db:create db:migrate db:test:prepare 76 | mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql 77 | - name: Run tests 78 | run: bin/test 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | log/*.log 3 | pkg/ 4 | test/dummy/db/*.sqlite3 5 | test/dummy/db/*.sqlite3-journal 6 | test/dummy/db/*.sqlite3-* 7 | test/dummy/log/*.log 8 | test/dummy/storage/ 9 | test/dummy/tmp/ 10 | coverage/ 11 | Gemfile.lock 12 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | require: 2 | - rubocop-performance 3 | - rubocop-rails 4 | AllCops: 5 | NewCops: enable 6 | Exclude: 7 | - vendor/**/* 8 | - Gemfile.lock 9 | - db/schema.rb 10 | - node_modules/**/* 11 | - tmp/**/* 12 | - test/dummy/db/schema.rb 13 | - test/dummy/db/migrate/**/* 14 | - test/dummy/config/**/* 15 | - test/dummy/bin/**/* 16 | - gemfiles/**/* 17 | - bin/**/* 18 | TargetRubyVersion: 3.2 19 | 20 | # Department Bundler 21 | Bundler/DuplicatedGem: 22 | Description: Checks for duplicate gem entries in Gemfile. 23 | Enabled: true 24 | 25 | Bundler/InsecureProtocolSource: 26 | Description: The source `:gemcutter`, `:rubygems` and `:rubyforge` are deprecated because HTTP requests are insecure. Please change your source to 'https://rubygems.org' if possible, or 'http://rubygems.org' if not. 27 | Enabled: true 28 | 29 | Bundler/OrderedGems: 30 | Description: Gems within groups in the Gemfile should be alphabetically sorted. 31 | Enabled: true 32 | 33 | # Department Gemspec 34 | Gemspec/OrderedDependencies: 35 | Description: Dependencies in the gemspec should be alphabetically sorted. 36 | Enabled: true 37 | 38 | # Department Layout 39 | Layout/AccessModifierIndentation: 40 | Description: Check indentation of private/protected visibility modifiers. 41 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#indent-public-private-protected 42 | Enabled: false 43 | 44 | Layout/ArrayAlignment: 45 | Description: Align the elements of an array literal if they span more than one line. 46 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#align-multiline-arrays 47 | Enabled: true 48 | 49 | Layout/HashAlignment: 50 | Description: Align the elements of a hash literal if they span more than one line. 51 | Enabled: true 52 | 53 | Layout/ParameterAlignment: 54 | Description: Align the parameters of a method call if they span more than one line. 55 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-double-indent 56 | Enabled: true 57 | 58 | Layout/BlockEndNewline: 59 | Description: Put end statement of multiline block on its own line. 60 | Enabled: true 61 | 62 | Layout/CaseIndentation: 63 | Description: Indentation of when in a case/when/[else/]end. 64 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#indent-when-to-case 65 | Enabled: true 66 | 67 | Layout/ClosingParenthesisIndentation: 68 | Description: Checks the indentation of hanging closing parentheses. 69 | Enabled: false 70 | 71 | Layout/CommentIndentation: 72 | Description: Indentation of comments. 73 | Enabled: false 74 | 75 | Layout/DotPosition: 76 | Description: Checks the position of the dot in multi-line method calls. 77 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains 78 | Enabled: true 79 | EnforcedStyle: trailing 80 | 81 | Layout/ElseAlignment: 82 | Description: Align elses and elsifs correctly. 83 | Enabled: true 84 | 85 | Layout/EmptyLineBetweenDefs: 86 | Description: Use empty lines between defs. 87 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#empty-lines-between-methods 88 | Enabled: true 89 | 90 | Layout/EmptyLines: 91 | Description: Don't use several empty lines in a row. 92 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#two-or-more-empty-lines 93 | Enabled: true 94 | 95 | Layout/EmptyLinesAroundAccessModifier: 96 | Description: Keep blank lines around access modifiers. 97 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#empty-lines-around-access-modifier 98 | Enabled: false 99 | 100 | Layout/EmptyLinesAroundBeginBody: 101 | Description: Keeps track of empty lines around begin-end bodies. 102 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#empty-lines-around-bodies 103 | Enabled: false 104 | 105 | Layout/EmptyLinesAroundBlockBody: 106 | Description: Keeps track of empty lines around block bodies. 107 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#empty-lines-around-bodies 108 | Enabled: false 109 | 110 | Layout/EmptyLinesAroundClassBody: 111 | Description: Keeps track of empty lines around class bodies. 112 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#empty-lines-around-bodies 113 | Enabled: false 114 | 115 | Layout/EmptyLinesAroundExceptionHandlingKeywords: 116 | Description: Keeps track of empty lines around exception handling keywords. 117 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#empty-lines-around-bodies 118 | Enabled: false 119 | 120 | Layout/EmptyLinesAroundModuleBody: 121 | Description: Keeps track of empty lines around module bodies. 122 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#empty-lines-around-bodies 123 | Enabled: false 124 | 125 | Layout/EmptyLinesAroundMethodBody: 126 | Description: Keeps track of empty lines around method bodies. 127 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#empty-lines-around-bodies 128 | Enabled: false 129 | 130 | Layout/EndOfLine: 131 | Description: Use Unix-style line endings. 132 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#crlf 133 | Enabled: false 134 | 135 | Layout/ExtraSpacing: 136 | Description: Do not use unnecessary spacing. 137 | Enabled: true 138 | 139 | Layout/FirstArrayElementLineBreak: 140 | Description: Checks for a line break before the first element in a multi-line array. 141 | Enabled: false 142 | 143 | Layout/FirstHashElementLineBreak: 144 | Description: Checks for a line break before the first element in a multi-line hash. 145 | Enabled: false 146 | 147 | Layout/FirstMethodArgumentLineBreak: 148 | Description: Checks for a line break before the first argument in a multi-line method call. 149 | Enabled: false 150 | 151 | Layout/FirstMethodParameterLineBreak: 152 | Description: Checks for a line break before the first parameter in a multi-line method parameter definition. 153 | Enabled: false 154 | 155 | Layout/InitialIndentation: 156 | Description: Checks the indentation of the first non-blank non-comment line in a file. 157 | Enabled: false 158 | 159 | Layout/FirstArgumentIndentation: 160 | Description: Checks the indentation of the first parameter in a method call. 161 | Enabled: false 162 | 163 | Layout/IndentationConsistency: 164 | Description: Keep indentation straight. 165 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-indentation 166 | Enabled: false 167 | 168 | Layout/IndentationWidth: 169 | Description: Use 2 spaces for indentation. 170 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-indentation 171 | Enabled: true 172 | 173 | Layout/FirstArrayElementIndentation: 174 | Description: Checks the indentation of the first element in an array literal. 175 | Enabled: false 176 | 177 | Layout/AssignmentIndentation: 178 | Description: Checks the indentation of the first line of the right-hand-side of a multi-line assignment. 179 | Enabled: true 180 | 181 | Layout/FirstHashElementIndentation: 182 | Description: Checks the indentation of the first key in a hash literal. 183 | Enabled: false 184 | 185 | Layout/HeredocIndentation: 186 | Description: This cops checks the indentation of the here document bodies. 187 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#squiggly-heredocs 188 | Enabled: true 189 | 190 | Layout/SpaceInLambdaLiteral: 191 | Description: Checks for spaces in lambda literals. 192 | Enabled: true 193 | 194 | Layout/LeadingCommentSpace: 195 | Description: Comments should start with a space. 196 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#hash-space 197 | Enabled: true 198 | 199 | Layout/MultilineArrayBraceLayout: 200 | Description: Checks that the closing brace in an array literal is either on the same line as the last array element, or a new line. 201 | Enabled: true 202 | 203 | Layout/MultilineAssignmentLayout: 204 | Description: Check for a newline after the assignment operator in multi-line assignments. 205 | StyleGuide: https://github.com/bbatsov/ruby-style-guid#indent-conditional-assignment 206 | Enabled: false 207 | 208 | Layout/MultilineBlockLayout: 209 | Description: Ensures newlines after multiline block do statements. 210 | Enabled: true 211 | 212 | Layout/MultilineHashBraceLayout: 213 | Description: Checks that the closing brace in a hash literal is either on the same line as the last hash element, or a new line. 214 | Enabled: true 215 | 216 | Layout/MultilineMethodCallBraceLayout: 217 | Description: Checks that the closing brace in a method call is either on the same line as the last method argument, or a new line. 218 | Enabled: true 219 | 220 | Layout/MultilineMethodCallIndentation: 221 | Description: Checks indentation of method calls with the dot operator that span more than one line. 222 | Enabled: true 223 | 224 | Layout/MultilineMethodDefinitionBraceLayout: 225 | Description: Checks that the closing brace in a method definition is either on the same line as the last method parameter, or a new line. 226 | Enabled: true 227 | 228 | Layout/MultilineOperationIndentation: 229 | Description: Checks indentation of binary operations that span more than one line. 230 | Enabled: false 231 | 232 | Layout/EmptyLineAfterMagicComment: 233 | Description: Add an empty line after magic comments to separate them from code. 234 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#separate-magic-comments-from-code 235 | Enabled: true 236 | 237 | Layout/RescueEnsureAlignment: 238 | Description: Align rescues and ensures correctly. 239 | Enabled: false 240 | 241 | Layout/SpaceBeforeFirstArg: 242 | Description: Checks that exactly one space is used between a method name and the first argument for method calls without parentheses. 243 | Enabled: true 244 | 245 | Layout/SpaceAfterColon: 246 | Description: Use spaces after colons. 247 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators 248 | Enabled: true 249 | 250 | Layout/SpaceAfterComma: 251 | Description: Use spaces after commas. 252 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators 253 | Enabled: true 254 | 255 | Layout/SpaceAfterMethodName: 256 | Description: Do not put a space between a method name and the opening parenthesis in a method definition. 257 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#parens-no-spaces 258 | Enabled: true 259 | 260 | Layout/SpaceAfterNot: 261 | Description: Tracks redundant space after the ! operator. 262 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-space-bang 263 | Enabled: false 264 | 265 | Layout/SpaceAfterSemicolon: 266 | Description: Use spaces after semicolons. 267 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators 268 | Enabled: true 269 | 270 | Layout/SpaceBeforeBlockBraces: 271 | Description: Checks that the left block brace has or doesn't have space before it. 272 | Enabled: false 273 | 274 | Layout/SpaceBeforeComma: 275 | Description: No spaces before commas. 276 | Enabled: false 277 | 278 | Layout/SpaceBeforeComment: 279 | Description: Checks for missing space between code and a comment on the same line. 280 | Enabled: false 281 | 282 | Layout/SpaceBeforeSemicolon: 283 | Description: No spaces before semicolons. 284 | Enabled: false 285 | 286 | Layout/SpaceInsideBlockBraces: 287 | 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. 288 | Enabled: false 289 | 290 | Layout/SpaceAroundBlockParameters: 291 | Description: Checks the spacing inside and after block parameters pipes. 292 | Enabled: true 293 | 294 | Layout/SpaceAroundEqualsInParameterDefault: 295 | Description: Checks that the equals signs in parameter default assignments have or don't have surrounding space depending on configuration. 296 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-around-equals 297 | Enabled: true 298 | 299 | Layout/SpaceAroundKeyword: 300 | Description: Use a space around keywords if appropriate. 301 | Enabled: true 302 | 303 | Layout/SpaceAroundOperators: 304 | Description: Use a single space around operators. 305 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators 306 | Enabled: true 307 | 308 | Layout/SpaceInsideArrayPercentLiteral: 309 | Description: No unnecessary additional spaces between elements in %i/%w literals. 310 | Enabled: true 311 | 312 | Layout/SpaceInsidePercentLiteralDelimiters: 313 | Description: No unnecessary spaces inside delimiters of %i/%w/%x literals. 314 | Enabled: true 315 | 316 | Layout/SpaceInsideHashLiteralBraces: 317 | Description: Use spaces inside hash literal braces - or don't. 318 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators 319 | Enabled: true 320 | 321 | Layout/SpaceInsideParens: 322 | Description: No spaces after ( or before ). 323 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-spaces-braces 324 | Enabled: true 325 | 326 | Layout/SpaceInsideRangeLiteral: 327 | Description: No spaces inside range literals. 328 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-space-inside-range-literals 329 | Enabled: true 330 | 331 | Layout/SpaceInsideStringInterpolation: 332 | Description: Checks for padding/surrounding spaces inside string interpolation. 333 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#string-interpolation 334 | Enabled: false 335 | 336 | Layout/IndentationStyle: 337 | Description: No hard tabs. 338 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-indentation 339 | Enabled: false 340 | 341 | Layout/SpaceAroundMethodCallOperator: 342 | Enabled: true 343 | 344 | Layout/TrailingEmptyLines: 345 | Description: Checks trailing blank lines and final newline. 346 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#newline-eof 347 | Enabled: false 348 | 349 | Layout/TrailingWhitespace: 350 | Description: Avoid trailing whitespace. 351 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace 352 | Enabled: false 353 | 354 | # Department Lint 355 | Lint/AmbiguousBlockAssociation: 356 | Description: Checks for ambiguous block association with method when param passed without parentheses. 357 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#syntax 358 | Enabled: false 359 | 360 | Lint/AmbiguousOperator: 361 | Description: Checks for ambiguous operators in the first argument of a method invocation without parentheses. 362 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#method-invocation-parens 363 | Enabled: true 364 | 365 | Lint/AmbiguousRegexpLiteral: 366 | Description: Checks for ambiguous regexp literals in the first argument of a method invocation without parentheses. 367 | Enabled: true 368 | 369 | Lint/AssignmentInCondition: 370 | Description: Don't use assignment in conditions. 371 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition 372 | Enabled: true 373 | 374 | Layout/BlockAlignment: 375 | Description: Align block ends correctly. 376 | Enabled: true 377 | 378 | Lint/BooleanSymbol: 379 | Description: Check for `:true` and `:false` symbols. 380 | Enabled: true 381 | 382 | Lint/CircularArgumentReference: 383 | Description: Default values in optional keyword arguments and optional ordinal arguments should not refer back to the name of the argument. 384 | Enabled: true 385 | 386 | Layout/ConditionPosition: 387 | Description: Checks for condition placed in a confusing position relative to the keyword. 388 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#same-line-condition 389 | Enabled: true 390 | 391 | Lint/Debugger: 392 | Description: Check for debugger calls. 393 | Enabled: true 394 | 395 | Layout/DefEndAlignment: 396 | Description: Align ends corresponding to defs correctly. 397 | Enabled: true 398 | 399 | Lint/DeprecatedClassMethods: 400 | Description: Check for deprecated class method calls. 401 | Enabled: true 402 | 403 | Lint/DuplicateCaseCondition: 404 | Description: Do not repeat values in case conditionals. 405 | Enabled: true 406 | 407 | Lint/DuplicateMethods: 408 | Description: Check for duplicate method definitions. 409 | Enabled: true 410 | 411 | Lint/DuplicateHashKey: 412 | Description: Check for duplicate keys in hash literals. 413 | Enabled: true 414 | 415 | Lint/EachWithObjectArgument: 416 | Description: Check for immutable argument given to each_with_object. 417 | Enabled: true 418 | 419 | Lint/ElseLayout: 420 | Description: Check for odd code arrangement in an else block. 421 | Enabled: true 422 | 423 | Lint/EmptyEnsure: 424 | Description: Checks for empty ensure block. 425 | Enabled: true 426 | AutoCorrect: false 427 | 428 | Lint/EmptyExpression: 429 | Description: Checks for empty expressions. 430 | Enabled: true 431 | 432 | Lint/EmptyInterpolation: 433 | Description: Checks for empty string interpolation. 434 | Enabled: true 435 | 436 | Lint/EmptyWhen: 437 | Description: Checks for `when` branches with empty bodies. 438 | Enabled: true 439 | 440 | Layout/EndAlignment: 441 | Description: Align ends correctly. 442 | Enabled: true 443 | 444 | Lint/EnsureReturn: 445 | Description: Do not use return in an ensure block. 446 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-return-ensure 447 | Enabled: true 448 | 449 | Lint/FloatOutOfRange: 450 | Description: Catches floating-point literals too large or small for Ruby to represent. 451 | Enabled: true 452 | 453 | Lint/FormatParameterMismatch: 454 | Description: The number of parameters to format/sprint must match the fields. 455 | Enabled: true 456 | 457 | Lint/SuppressedException: 458 | Description: Don't suppress exception. 459 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions 460 | Enabled: true 461 | 462 | Lint/ImplicitStringConcatenation: 463 | Description: Checks for adjacent string literals on the same line, which could better be represented as a single string literal. 464 | Enabled: true 465 | 466 | Lint/IneffectiveAccessModifier: 467 | Description: Checks for attempts to use `private` or `protected` to set the visibility of a class method, which does not work. 468 | Enabled: true 469 | 470 | Lint/InheritException: 471 | Description: Avoid inheriting from the `Exception` class. 472 | Enabled: true 473 | 474 | Lint/InterpolationCheck: 475 | Description: Raise warning for interpolation in single q strs 476 | Enabled: true 477 | 478 | Lint/LiteralAsCondition: 479 | Description: Checks of literals used in conditions. 480 | Enabled: true 481 | 482 | Lint/LiteralInInterpolation: 483 | Description: Checks for literals used in interpolation. 484 | Enabled: true 485 | 486 | Lint/Loop: 487 | Description: Use Kernel#loop with break rather than begin/end/until or begin/end/while for post-loop tests. 488 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#loop-with-break 489 | Enabled: true 490 | 491 | Lint/MultipleComparison: 492 | Description: Use `&&` operator to compare multiple value. 493 | Enabled: true 494 | 495 | Lint/NestedMethodDefinition: 496 | Description: Do not use nested method definitions. 497 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-methods 498 | Enabled: true 499 | 500 | Lint/NextWithoutAccumulator: 501 | Description: Do not omit the accumulator when calling `next` in a `reduce`/`inject` block. 502 | Enabled: true 503 | 504 | Lint/NonLocalExitFromIterator: 505 | Description: Do not use return in iterator to cause non-local exit. 506 | Enabled: true 507 | 508 | Lint/ParenthesesAsGroupedExpression: 509 | Description: Checks for method calls with a space before the opening parenthesis. 510 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#parens-no-spaces 511 | Enabled: true 512 | 513 | Lint/PercentStringArray: 514 | Description: Checks for unwanted commas and quotes in %w/%W literals. 515 | Enabled: true 516 | 517 | Lint/PercentSymbolArray: 518 | Description: Checks for unwanted commas and colons in %i/%I literals. 519 | Enabled: true 520 | 521 | Lint/RandOne: 522 | Description: Checks for `rand(1)` calls. Such calls always return `0` and most likely a mistake. 523 | Enabled: true 524 | 525 | Lint/RedundantWithIndex: 526 | Description: Checks for redundant `with_index`. 527 | Enabled: true 528 | 529 | Lint/RedundantWithObject: 530 | Description: Checks for redundant `with_object`. 531 | Enabled: true 532 | 533 | Lint/RegexpAsCondition: 534 | Description: Do not use regexp literal as a condition. The regexp literal matches `$_` implicitly. 535 | Enabled: true 536 | 537 | Lint/RequireParentheses: 538 | Description: Use parentheses in the method call to avoid confusion about precedence. 539 | Enabled: true 540 | 541 | Lint/RescueException: 542 | Description: Avoid rescuing the Exception class. 543 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-blind-rescues 544 | Enabled: true 545 | 546 | Lint/RescueType: 547 | Description: Avoid rescuing from non constants that could result in a `TypeError`. 548 | Enabled: true 549 | 550 | Lint/SafeNavigationChain: 551 | Description: Do not chain ordinary method call after safe navigation operator. 552 | Enabled: true 553 | 554 | Lint/ScriptPermission: 555 | Description: Grant script file execute permission. 556 | Enabled: true 557 | 558 | Lint/ShadowedException: 559 | Description: Avoid rescuing a higher level exception before a lower level exception. 560 | Enabled: true 561 | 562 | Lint/ShadowingOuterLocalVariable: 563 | Description: Do not use the same name as outer local variable for block arguments or block local variables. 564 | Enabled: true 565 | 566 | Lint/RedundantStringCoercion: 567 | Description: Checks for Object#to_s usage in string interpolation. 568 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-to-s 569 | Enabled: true 570 | 571 | Lint/UnderscorePrefixedVariableName: 572 | Description: Do not use prefix `_` for a variable that is used. 573 | Enabled: true 574 | 575 | Lint/UnifiedInteger: 576 | Description: Use Integer instead of Fixnum or Bignum 577 | Enabled: true 578 | 579 | Lint/RedundantRequireStatement: 580 | Description: Checks for unnecessary `require` statement. 581 | Enabled: true 582 | 583 | Lint/RedundantSplatExpansion: 584 | Description: Checks for splat unnecessarily being called on literals 585 | Enabled: true 586 | 587 | Lint/UnusedBlockArgument: 588 | Description: Checks for unused block arguments. 589 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars 590 | Enabled: true 591 | 592 | Lint/UnusedMethodArgument: 593 | Description: Checks for unused method arguments. 594 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars 595 | Enabled: true 596 | 597 | Lint/UnreachableCode: 598 | Description: Unreachable code. 599 | Enabled: true 600 | 601 | Lint/UriEscapeUnescape: 602 | Description: '`URI.escape` method is obsolete and should not be used. Instead, use `CGI.escape`, `URI.encode_www_form` or `URI.encode_www_form_component` depending on your specific use case. Also `URI.unescape` method is obsolete and should not be used. Instead, use `CGI.unescape`, `URI.decode_www_form` or `URI.decode_www_form_component` depending on your specific use case.' 603 | Enabled: true 604 | 605 | Lint/UriRegexp: 606 | Description: Use `URI::DEFAULT_PARSER.make_regexp` instead of `URI.regexp`. 607 | Enabled: true 608 | 609 | Lint/UselessAccessModifier: 610 | Description: Checks for useless access modifiers. 611 | Enabled: true 612 | ContextCreatingMethods: [] 613 | MethodCreatingMethods: [] 614 | 615 | Lint/UselessAssignment: 616 | Description: Checks for useless assignment to a local variable. 617 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars 618 | Enabled: true 619 | 620 | Lint/BinaryOperatorWithIdenticalOperands: 621 | Description: Checks for comparison of something with itself. 622 | Enabled: true 623 | 624 | Lint/ReturnInVoidContext: 625 | Description: Checks for return in void context. 626 | Enabled: true 627 | 628 | Lint/UselessSetterCall: 629 | Description: Checks for useless setter call to a local variable. 630 | Enabled: true 631 | 632 | Lint/Void: 633 | Description: Possible use of operator/literal/variable in void context. 634 | Enabled: true 635 | 636 | # Department Metrics 637 | Metrics/AbcSize: 638 | Description: A calculated magnitude based on number of assignments, branches, and conditions. 639 | Reference: http://c2.com/cgi/wiki?AbcMetric 640 | Enabled: true 641 | Max: 15 642 | Exclude: 643 | - db/migrate/*.rb 644 | 645 | Metrics/BlockLength: 646 | Description: Avoid long blocks with many lines. 647 | Enabled: true 648 | Exclude: 649 | - Rakefile 650 | - "**/*.rake" 651 | - spec/**/*.rb 652 | - 'config/environments/*.rb' 653 | 654 | Metrics/BlockNesting: 655 | Description: Avoid excessive block nesting 656 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count 657 | Enabled: true 658 | Max: 4 659 | 660 | Metrics/ClassLength: 661 | Description: Avoid classes longer than 100 lines of code. 662 | Enabled: true 663 | Max: 100 664 | 665 | Metrics/CyclomaticComplexity: 666 | Description: A complexity metric that is strongly correlated to the number of test cases needed to validate a method. 667 | Enabled: true 668 | 669 | Layout/LineLength: 670 | Description: Limit lines to 80 characters. 671 | StyleGuide: '#80-character-limits' 672 | Enabled: true 673 | Max: 80 674 | Exclude: 675 | - 'db/**/*' 676 | - 'config/**/*' 677 | 678 | Metrics/MethodLength: 679 | Description: Avoid methods longer than 10 lines of code. 680 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#short-methods 681 | Enabled: true 682 | Max: 7 683 | Exclude: 684 | - 'db/**/*' 685 | 686 | Metrics/ModuleLength: 687 | Description: Avoid modules longer than 100 lines of code. 688 | Enabled: true 689 | Max: 100 690 | 691 | Metrics/ParameterLists: 692 | Description: Avoid parameter lists longer than three or four parameters. 693 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#too-many-params 694 | Enabled: false 695 | 696 | Metrics/PerceivedComplexity: 697 | Description: A complexity metric geared towards measuring complexity for a human reader. 698 | Enabled: true 699 | 700 | # Department Naming 701 | Naming/AccessorMethodName: 702 | Description: Check the naming of accessor methods for get_/set_. 703 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#accessor_mutator_method_names 704 | Enabled: false 705 | 706 | Naming/AsciiIdentifiers: 707 | Description: Use only ascii symbols in identifiers. 708 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#english-identifiers 709 | Enabled: false 710 | 711 | Naming/ClassAndModuleCamelCase: 712 | Description: Use CamelCase for classes and modules. 713 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#camelcase-classes 714 | Enabled: true 715 | 716 | Naming/ConstantName: 717 | Description: Constants should use SCREAMING_SNAKE_CASE. 718 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#screaming-snake-case 719 | Enabled: true 720 | 721 | Naming/FileName: 722 | Description: Use snake_case for source file names. 723 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-files 724 | Enabled: true 725 | 726 | Naming/HeredocDelimiterCase: 727 | Description: Use configured case for heredoc delimiters. 728 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#heredoc-delimiters 729 | Enabled: true 730 | 731 | Naming/HeredocDelimiterNaming: 732 | Description: Use descriptive heredoc delimiters. 733 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#heredoc-delimiters 734 | Enabled: true 735 | 736 | Naming/MethodName: 737 | Description: Use the configured style when naming methods. 738 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars 739 | Enabled: false 740 | 741 | Naming/PredicateName: 742 | Description: Check the names of predicate methods. 743 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark 744 | Enabled: true 745 | 746 | Naming/BinaryOperatorParameterName: 747 | Description: When defining binary operators, name the argument other. 748 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#other-arg 749 | Enabled: true 750 | 751 | Naming/VariableName: 752 | Description: Use the configured style when naming variables. 753 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars 754 | Enabled: true 755 | 756 | Naming/VariableNumber: 757 | Description: Use the configured style when numbering variables. 758 | Enabled: false 759 | 760 | Naming/MemoizedInstanceVariableName: 761 | Enabled: true 762 | Exclude: 763 | - app/controllers/*.rb 764 | # Department Performance 765 | Performance/Caller: 766 | Description: Use `caller(n..n)` instead of `caller`. 767 | Enabled: true 768 | 769 | Performance/Casecmp: 770 | Description: Use `casecmp` rather than `downcase ==`, `upcase ==`, `== downcase`, or `== upcase`.. 771 | Reference: https://github.com/JuanitoFatas/fast-ruby#stringcasecmp-vs-stringdowncase---code 772 | Enabled: true 773 | 774 | Performance/CaseWhenSplat: 775 | Description: Place `when` conditions that use splat at the end of the list of `when` branches. 776 | Enabled: true 777 | 778 | Performance/Count: 779 | Description: Use `count` instead of `select...size`, `reject...size`, `select...count`, `reject...count`, `select...length`, and `reject...length`. 780 | SafeAutoCorrect: true 781 | Enabled: true 782 | 783 | Performance/Detect: 784 | Description: Use `detect` instead of `select.first`, `find_all.first`, `select.last`, and `find_all.last`. 785 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code' 786 | SafeAutoCorrect: true 787 | Enabled: true 788 | 789 | Performance/DoubleStartEndWith: 790 | Description: Use `str.{start,end}_with?(x, ..., y, ...)` instead of `str.{start,end}_with?(x, ...) || str.{start,end}_with?(y, ...)`. 791 | Enabled: true 792 | 793 | Performance/EndWith: 794 | Description: Use `end_with?` instead of a regex match anchored to the end of a string. 795 | Reference: https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end 796 | AutoCorrect: false 797 | Enabled: true 798 | 799 | Performance/FixedSize: 800 | Description: Do not compute the size of statically sized objects except in constants 801 | Enabled: true 802 | 803 | Performance/FlatMap: 804 | Description: Use `Enumerable#flat_map` instead of `Enumerable#map...Array#flatten(1)` or `Enumberable#collect..Array#flatten(1)` 805 | Reference: https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code 806 | Enabled: true 807 | EnabledForFlattenWithoutParams: false 808 | 809 | Performance/RangeInclude: 810 | Description: Use `Range#cover?` instead of `Range#include?`. 811 | Reference: https://github.com/JuanitoFatas/fast-ruby#cover-vs-include-code 812 | Enabled: true 813 | 814 | Performance/RedundantBlockCall: 815 | Description: Use `yield` instead of `block.call`. 816 | Reference: https://github.com/JuanitoFatas/fast-ruby#proccall-vs-yield-code 817 | Enabled: true 818 | 819 | Performance/RedundantMatch: 820 | Description: Use `=~` instead of `String#match` or `Regexp#match` in a context where the returned `MatchData` is not needed. 821 | Enabled: true 822 | 823 | Performance/RedundantMerge: 824 | Description: Use Hash#[]=, rather than Hash#merge! with a single key-value pair. 825 | Reference: https://github.com/JuanitoFatas/fast-ruby#hashmerge-vs-hash-code 826 | Enabled: true 827 | 828 | Style/ExponentialNotation: 829 | Enabled: true 830 | 831 | Style/RedundantSortBy: 832 | Description: Use `sort` instead of `sort_by { |x| x }`. 833 | Enabled: true 834 | 835 | Performance/RegexpMatch: 836 | Description: Use `match?` instead of `Regexp#match`, `String#match`, `Symbol#match`, `Regexp#===`, or `=~` when `MatchData` is not used. 837 | Enabled: true 838 | 839 | Performance/ReverseEach: 840 | Description: Use `reverse_each` instead of `reverse.each`. 841 | Reference: https://github.com/JuanitoFatas/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code 842 | Enabled: true 843 | 844 | Style/Sample: 845 | Description: Use `sample` instead of `shuffle.first`, `shuffle.last`, and `shuffle[Integer]`. 846 | Reference: https://github.com/JuanitoFatas/fast-ruby#arrayshufflefirst-vs-arraysample-code 847 | Enabled: true 848 | 849 | Performance/Size: 850 | Description: Use `size` instead of `count` for counting the number of elements in `Array` and `Hash`. 851 | Reference: https://github.com/JuanitoFatas/fast-ruby#arraycount-vs-arraysize-code 852 | Enabled: true 853 | 854 | Performance/CompareWithBlock: 855 | Description: Use `sort_by(&:foo)` instead of `sort { |a, b| a.foo <=> b.foo }`. 856 | Enabled: true 857 | 858 | Performance/StartWith: 859 | Description: Use `start_with?` instead of a regex match anchored to the beginning of a string. 860 | Reference: https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end 861 | AutoCorrect: false 862 | Enabled: true 863 | 864 | Performance/StringReplacement: 865 | Description: Use `tr` instead of `gsub` when you are replacing the same number of characters. Use `delete` instead of `gsub` when you are deleting characters. 866 | Reference: https://github.com/JuanitoFatas/fast-ruby#stringgsub-vs-stringtr-code 867 | Enabled: true 868 | 869 | Performance/TimesMap: 870 | Description: Checks for .times.map calls. 871 | AutoCorrect: false 872 | Enabled: true 873 | 874 | Performance/UnfreezeString: 875 | Description: Use unary plus to get an unfrozen string literal. 876 | Enabled: true 877 | 878 | Performance/UriDefaultParser: 879 | Description: Use `URI::DEFAULT_PARSER` instead of `URI::Parser.new`. 880 | Enabled: true 881 | 882 | # Department Security 883 | Security/Eval: 884 | Description: The use of eval represents a serious security risk. 885 | Enabled: true 886 | 887 | Security/JSONLoad: 888 | Description: Prefer usage of `JSON.parse` over `JSON.load` due to potential security issues. See reference for more information. 889 | Reference: http://ruby-doc.org/stdlib-2.3.0/libdoc/json/rdoc/JSON.html#method-i-load 890 | Enabled: true 891 | AutoCorrect: false 892 | 893 | Security/MarshalLoad: 894 | Description: Avoid using of `Marshal.load` or `Marshal.restore` due to potential security issues. See reference for more information. 895 | Reference: http://ruby-doc.org/core-2.3.3/Marshal.html#module-Marshal-label-Security+considerations 896 | Enabled: true 897 | 898 | Security/YAMLLoad: 899 | Description: Prefer usage of `YAML.safe_load` over `YAML.load` due to potential security issues. See reference for more information. 900 | Reference: https://ruby-doc.org/stdlib-2.3.3/libdoc/yaml/rdoc/YAML.html#module-YAML-label-Security 901 | Enabled: true 902 | 903 | Security/Open: 904 | Exclude: 905 | - db/migrate/*.rb 906 | # Department Style 907 | Style/Alias: 908 | Description: Use alias instead of alias_method. 909 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#alias-method 910 | Enabled: false 911 | 912 | Style/AndOr: 913 | Description: Use &&/|| instead of and/or. 914 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-and-or-or 915 | Enabled: true 916 | 917 | Style/ArrayJoin: 918 | Description: Use Array#join instead of Array#*. 919 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#array-join' 920 | Enabled: false 921 | 922 | Style/AsciiComments: 923 | Description: Use only ascii symbols in comments. 924 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#english-comments 925 | Enabled: false 926 | 927 | Style/Attr: 928 | Description: Checks for uses of Module#attr. 929 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#attr 930 | Enabled: true 931 | 932 | Style/AutoResourceCleanup: 933 | Description: Suggests the usage of an auto resource cleanup version of a method (if available). 934 | Enabled: false 935 | 936 | Style/BeginBlock: 937 | Description: Avoid the use of BEGIN blocks. 938 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-BEGIN-blocks 939 | Enabled: true 940 | 941 | Style/BarePercentLiterals: 942 | Description: Checks if usage of %() or %Q() matches configuration. 943 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-q-shorthand 944 | Enabled: true 945 | 946 | Style/BlockComments: 947 | Description: Do not use block comments. 948 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-block-comments 949 | Enabled: false 950 | 951 | Style/BlockDelimiters: 952 | Description: Avoid using {...} for multi-line blocks (multiline chaining is always ugly). Prefer {...} over do...end for single-line blocks. 953 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#single-line-blocks 954 | Enabled: true 955 | 956 | Style/CaseEquality: 957 | Description: Avoid explicit use of the case equality operator(===). 958 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-case-equality 959 | Enabled: false 960 | 961 | Style/CharacterLiteral: 962 | Description: Checks for uses of character literals. 963 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-character-literals 964 | Enabled: false 965 | 966 | Style/ClassAndModuleChildren: 967 | Description: Checks style of children classes and modules. 968 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#namespace-definition 969 | Enabled: false 970 | 971 | Style/ClassCheck: 972 | Description: Enforces consistent use of `Object#is_a?` or `Object#kind_of?`. 973 | Enabled: true 974 | 975 | Style/ClassMethods: 976 | Description: Use self when defining module/class methods. 977 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#def-self-class-methods 978 | Enabled: false 979 | 980 | Style/ClassVars: 981 | Description: Avoid the use of class variables. 982 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-class-vars 983 | Enabled: false 984 | 985 | Style/CollectionMethods: 986 | Description: Preferred collection methods. 987 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#map-find-select-reduce-size 988 | Enabled: false 989 | 990 | Style/ColonMethodCall: 991 | Description: 'Do not use :: for method call.' 992 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#double-colons 993 | Enabled: true 994 | 995 | Style/CommandLiteral: 996 | Description: Use `` or %x around command literals. 997 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-x 998 | Enabled: false 999 | 1000 | Style/CommentAnnotation: 1001 | Description: Checks formatting of special comments (TODO, FIXME, OPTIMIZE, HACK, REVIEW). 1002 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#annotate-keywords 1003 | Enabled: false 1004 | 1005 | Style/CommentedKeyword: 1006 | Description: Do not place comments on the same line as certain keywords. 1007 | Enabled: false 1008 | 1009 | Style/ConditionalAssignment: 1010 | Description: Use the return value of `if` and `case` statements for assignment to a variable and variable comparison instead of assigning that variable inside of each branch. 1011 | Enabled: true 1012 | 1013 | Style/Copyright: 1014 | Description: Include a copyright notice in each file before any code. 1015 | Enabled: false 1016 | 1017 | Style/DateTime: 1018 | Description: Use Date or Time over DateTime. 1019 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#date--time 1020 | Enabled: false 1021 | 1022 | Style/DefWithParentheses: 1023 | Description: Use def with parentheses when there are arguments. 1024 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#method-parens 1025 | Enabled: true 1026 | 1027 | Style/Dir: 1028 | Description: Use the `__dir__` method to retrieve the canonicalized absolute path to the current file. 1029 | Enabled: true 1030 | 1031 | Style/Documentation: 1032 | Description: Document classes and non-namespace modules. 1033 | Enabled: false 1034 | Exclude: 1035 | - 'spec/**/*' 1036 | - 'test/**/*' 1037 | 1038 | Style/DocumentationMethod: 1039 | Description: Public methods. 1040 | Enabled: false 1041 | Exclude: 1042 | - 'spec/**/*' 1043 | - 'test/**/*' 1044 | 1045 | Style/DoubleNegation: 1046 | Description: Checks for uses of double negation (!!). 1047 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-bang-bang 1048 | Enabled: true 1049 | 1050 | Style/EachForSimpleLoop: 1051 | Description: Use `Integer#times` for a simple loop which iterates a fixed number of times. 1052 | Enabled: true 1053 | 1054 | Style/EachWithObject: 1055 | Description: Prefer `each_with_object` over `inject` or `reduce`. 1056 | Enabled: false 1057 | 1058 | Style/EmptyElse: 1059 | Description: Avoid empty else-clauses. 1060 | Enabled: true 1061 | 1062 | Style/EmptyCaseCondition: 1063 | Description: Avoid empty condition in case statements. 1064 | Enabled: false 1065 | 1066 | Style/EmptyLiteral: 1067 | Description: Prefer literals to Array.new/Hash.new/String.new. 1068 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#literal-array-hash 1069 | Enabled: false 1070 | 1071 | Style/EmptyMethod: 1072 | Description: Checks the formatting of empty method definitions. 1073 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-single-line-methods 1074 | Enabled: false 1075 | 1076 | Style/EndBlock: 1077 | Description: Avoid the use of END blocks. 1078 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-END-blocks 1079 | Enabled: true 1080 | 1081 | Style/Encoding: 1082 | Description: Use UTF-8 as the source file encoding. 1083 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#utf-8 1084 | Enabled: true 1085 | 1086 | Style/EvenOdd: 1087 | Description: Favor the use of Integer#even? && Integer#odd? 1088 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#predicate-methods 1089 | Enabled: true 1090 | 1091 | Style/FrozenStringLiteralComment: 1092 | Description: Add the frozen_string_literal comment to the top of files to help transition from Ruby 2.3.0 to Ruby 3.0. 1093 | Enabled: true 1094 | 1095 | Lint/FlipFlop: 1096 | Description: Checks for flip flops 1097 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-flip-flops 1098 | Enabled: true 1099 | 1100 | Style/For: 1101 | Description: Checks use of for or each in multiline loops. 1102 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-for-loops 1103 | Enabled: true 1104 | 1105 | Style/FormatString: 1106 | Description: Enforce the use of Kernel#sprintf, Kernel#format or String#%. 1107 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#sprintf 1108 | Enabled: false 1109 | 1110 | Style/FormatStringToken: 1111 | Description: Use a consistent style for format string tokens. 1112 | Enabled: true 1113 | 1114 | Style/GlobalVars: 1115 | Description: Do not introduce global variables. 1116 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#instance-vars 1117 | Reference: http://www.zenspider.com/Languages/Ruby/QuickRef.html 1118 | Enabled: true 1119 | 1120 | Style/GuardClause: 1121 | Description: Check for conditionals that can be replaced with guard clauses 1122 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals 1123 | Enabled: true 1124 | 1125 | Style/HashSyntax: 1126 | Description: 'Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax { :a => 1, :b => 2 }.' 1127 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#hash-literals 1128 | Enabled: true 1129 | 1130 | Style/IfInsideElse: 1131 | Description: Finds if nodes inside else, which can be converted to elsif. 1132 | Enabled: true 1133 | 1134 | Style/IfUnlessModifier: 1135 | Description: Favor modifier if/unless usage when you have a single-line body. 1136 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier 1137 | Enabled: true 1138 | 1139 | Style/IfUnlessModifierOfIfUnless: 1140 | Description: Avoid modifier if/unless usage on conditionals. 1141 | Enabled: true 1142 | 1143 | Style/IfWithSemicolon: 1144 | Description: Do not use if x; .... Use the ternary operator instead. 1145 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-semicolon-ifs 1146 | Enabled: true 1147 | 1148 | Style/IdenticalConditionalBranches: 1149 | Description: Checks that conditional statements do not have an identical line at the end of each branch, which can validly be moved out of the conditional. 1150 | Enabled: true 1151 | 1152 | Style/InfiniteLoop: 1153 | Description: Use Kernel#loop for infinite loops. 1154 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#infinite-loop 1155 | Enabled: true 1156 | 1157 | Style/InlineComment: 1158 | Description: Avoid trailing inline comments. 1159 | Enabled: false 1160 | 1161 | Style/InverseMethods: 1162 | Description: Use the inverse method instead of `!.method` if an inverse method is defined. 1163 | Enabled: true 1164 | 1165 | Style/ImplicitRuntimeError: 1166 | Description: Use `raise` or `fail` with an explicit exception class and message, rather than just a message. 1167 | Enabled: false 1168 | 1169 | Style/Lambda: 1170 | Description: Use the new lambda literal syntax for single-line blocks. 1171 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#lambda-multi-line 1172 | Enabled: true 1173 | 1174 | Style/LambdaCall: 1175 | Description: Use lambda.call(...) instead of lambda.(...). 1176 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#proc-call 1177 | Enabled: false 1178 | 1179 | Style/LineEndConcatenation: 1180 | Description: Use \ instead of + or << to concatenate two string literals at line end. 1181 | Enabled: true 1182 | 1183 | Style/MethodCallWithoutArgsParentheses: 1184 | Description: Do not use parentheses for method calls with no arguments. 1185 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#method-invocation-parens 1186 | Enabled: true 1187 | 1188 | Style/MethodCallWithArgsParentheses: 1189 | Description: Use parentheses for method calls with arguments. 1190 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#method-invocation-parens 1191 | Enabled: false 1192 | 1193 | Style/MethodCalledOnDoEndBlock: 1194 | Description: Avoid chaining a method call on a do...end block. 1195 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#single-line-blocks 1196 | Enabled: false 1197 | 1198 | Style/MethodDefParentheses: 1199 | Description: Checks if the method definitions have or don't have parentheses. 1200 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#method-parens 1201 | Enabled: true 1202 | 1203 | Style/MissingRespondToMissing: 1204 | Enabled: true 1205 | 1206 | Style/MinMax: 1207 | Description: Use `Enumerable#minmax` instead of `Enumerable#min` and `Enumerable#max` in conjunction.' 1208 | Enabled: true 1209 | 1210 | Style/MissingElse: 1211 | Description: Require if/case expressions to have an else branches. If enabled, it is recommended that Style/UnlessElse and Style/EmptyElse be enabled. This will conflict with Style/EmptyElse if Style/EmptyElse is configured to style "both" 1212 | Enabled: false 1213 | EnforcedStyle: both 1214 | SupportedStyles: 1215 | - if 1216 | - case 1217 | - both 1218 | 1219 | Style/MixinGrouping: 1220 | Description: Checks for grouping of mixins in `class` and `module` bodies. 1221 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#mixin-grouping 1222 | Enabled: true 1223 | 1224 | Style/ModuleFunction: 1225 | Description: Checks for usage of `extend self` in modules. 1226 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#module-function 1227 | Enabled: true 1228 | 1229 | Style/MultilineBlockChain: 1230 | Description: Avoid multi-line chains of blocks. 1231 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#single-line-blocks 1232 | Enabled: false 1233 | 1234 | Style/MultilineIfThen: 1235 | Description: Do not use then for multi-line if/unless. 1236 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-then 1237 | Enabled: true 1238 | 1239 | Style/MultilineIfModifier: 1240 | Description: Only use if/unless modifiers on single line statements. 1241 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-multiline-if-modifiers 1242 | Enabled: true 1243 | 1244 | Style/MultilineMemoization: 1245 | Description: Wrap multiline memoizations in a `begin` and `end` block. 1246 | Enabled: true 1247 | 1248 | Style/MultilineTernaryOperator: 1249 | Description: 'Avoid multi-line ?: (the ternary operator); use if/unless instead.' 1250 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-multiline-ternary 1251 | Enabled: true 1252 | 1253 | Style/MultipleComparison: 1254 | Description: Avoid comparing a variable with multiple items in a conditional, use Array#include? instead. 1255 | Enabled: true 1256 | 1257 | Style/MutableConstant: 1258 | Description: Do not assign mutable objects to constants. 1259 | Enabled: false 1260 | 1261 | Style/NegatedIf: 1262 | Description: Favor unless over if for negative conditions (or control flow or). 1263 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#unless-for-negatives 1264 | Enabled: true 1265 | 1266 | Style/NegatedWhile: 1267 | Description: Favor until over while for negative conditions. 1268 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#until-for-negatives 1269 | Enabled: true 1270 | 1271 | Style/NestedModifier: 1272 | Description: Avoid using nested modifiers. 1273 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-modifiers 1274 | Enabled: true 1275 | 1276 | Style/NestedParenthesizedCalls: 1277 | Description: Parenthesize method calls which are nested inside the argument list of another parenthesized method call. 1278 | Enabled: true 1279 | 1280 | Style/NestedTernaryOperator: 1281 | Description: Use one expression per branch in a ternary operator. 1282 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-ternary 1283 | Enabled: true 1284 | 1285 | Style/Next: 1286 | Description: Use `next` to skip iteration instead of a condition at the end. 1287 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals 1288 | Enabled: true 1289 | 1290 | Style/NilComparison: 1291 | Description: 'Prefer x.nil? to x == nil.' 1292 | StyleGuide: '#predicate-methods' 1293 | Enabled: true 1294 | 1295 | Style/NonNilCheck: 1296 | Description: Checks for redundant nil checks. 1297 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-non-nil-checks 1298 | Enabled: true 1299 | 1300 | Style/Not: 1301 | Description: Use ! instead of not. 1302 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#bang-not-not 1303 | Enabled: true 1304 | 1305 | Style/NumericLiterals: 1306 | Description: Add underscores to large numeric literals to improve their readability. 1307 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#underscores-in-numerics 1308 | Enabled: true 1309 | 1310 | Style/NumericLiteralPrefix: 1311 | Description: Use smallcase prefixes for numeric literals. 1312 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#numeric-literal-prefixes 1313 | Enabled: true 1314 | 1315 | Style/NumericPredicate: 1316 | Description: Checks for the use of predicate- or comparison methods for numeric comparisons. 1317 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#predicate-methods 1318 | AutoCorrect: false 1319 | Enabled: true 1320 | 1321 | Style/OneLineConditional: 1322 | Description: Favor the ternary operator(?:) over if/then/else/end constructs. 1323 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#ternary-operator 1324 | Enabled: false 1325 | 1326 | Style/OptionalArguments: 1327 | Description: Checks for optional arguments that do not appear at the end of the argument list 1328 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#optional-arguments 1329 | Enabled: false 1330 | 1331 | Style/OptionHash: 1332 | Description: Don't use option hashes when you can use keyword arguments. 1333 | Enabled: false 1334 | 1335 | Style/OrAssignment: 1336 | Description: Recommend usage of double pipe equals (||=) where applicable. 1337 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#double-pipe-for-uninit 1338 | Enabled: true 1339 | 1340 | Style/ParallelAssignment: 1341 | Description: Check for simple usages of parallel assignment. It will only warn when the number of variables matches on both sides of the assignment. 1342 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#parallel-assignment 1343 | Enabled: false 1344 | 1345 | Style/ParenthesesAroundCondition: 1346 | Description: Don't use parentheses around the condition of an if/unless/while. 1347 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-parens-around-condition 1348 | Enabled: true 1349 | 1350 | Style/PercentLiteralDelimiters: 1351 | Description: Use `%`-literal delimiters consistently 1352 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-literal-braces 1353 | Enabled: true 1354 | PreferredDelimiters: 1355 | default: () 1356 | '%i': '()' 1357 | '%I': '()' 1358 | '%r': '{}' 1359 | '%w': '()' 1360 | '%W': '()' 1361 | 1362 | Style/PercentQLiterals: 1363 | Description: Checks if uses of %Q/%q match the configured preference. 1364 | Enabled: true 1365 | 1366 | Style/PerlBackrefs: 1367 | Description: Avoid Perl-style regex back references. 1368 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers 1369 | Enabled: false 1370 | 1371 | Style/PreferredHashMethods: 1372 | Description: Checks use of `has_key?` and `has_value?` Hash methods. 1373 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#hash-key 1374 | Enabled: true 1375 | 1376 | Style/Proc: 1377 | Description: Use proc instead of Proc.new. 1378 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#proc 1379 | Enabled: true 1380 | 1381 | Style/RaiseArgs: 1382 | Description: Checks the arguments passed to raise/fail. 1383 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#exception-class-messages 1384 | Enabled: false 1385 | 1386 | Style/RedundantBegin: 1387 | Description: Don't use begin blocks when they are not needed. 1388 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#begin-implicit 1389 | Enabled: true 1390 | 1391 | Style/RedundantConditional: 1392 | Description: Don't return true/false from a conditional. 1393 | Enabled: true 1394 | 1395 | Style/RedundantException: 1396 | Description: Checks for an obsolete RuntimeException argument in raise/fail. 1397 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-explicit-runtimeerror 1398 | Enabled: true 1399 | 1400 | Style/RedundantFreeze: 1401 | Description: Checks usages of Object#freeze on immutable objects. 1402 | Enabled: true 1403 | 1404 | Style/RedundantParentheses: 1405 | Description: Checks for parentheses that seem not to serve any purpose. 1406 | Enabled: true 1407 | 1408 | Style/RedundantReturn: 1409 | Description: Don't use return where it's not required. 1410 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-explicit-return 1411 | Enabled: true 1412 | 1413 | Style/RedundantSelf: 1414 | Description: Don't use self where it's not needed. 1415 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-self-unless-required 1416 | Enabled: true 1417 | 1418 | Style/RegexpLiteral: 1419 | Description: Use / or %r around regular expressions. 1420 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-r 1421 | Enabled: false 1422 | 1423 | Style/RescueModifier: 1424 | Description: Avoid using rescue in its modifier form. 1425 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-rescue-modifiers 1426 | Enabled: false 1427 | 1428 | Style/RescueStandardError: 1429 | Description: Avoid rescuing without specifying an error class. 1430 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-blind-rescues' 1431 | Enabled: true 1432 | 1433 | Style/ReturnNil: 1434 | Description: Use return instead of return nil. 1435 | Enabled: false 1436 | 1437 | Style/SafeNavigation: 1438 | Description: This cop transforms usages of a method call safeguarded by a check for the existance of the object to safe navigation (`&.`). 1439 | Enabled: true 1440 | 1441 | Style/SelfAssignment: 1442 | Description: Checks for places where self-assignment shorthand should have been used. 1443 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#self-assignment 1444 | Enabled: true 1445 | 1446 | Style/Semicolon: 1447 | Description: Don't use semicolons to terminate expressions. 1448 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-semicolon 1449 | Enabled: true 1450 | 1451 | Style/Send: 1452 | Description: Prefer `Object#__send__` or `Object#public_send` to `send`, as `send` may overlap with existing methods. 1453 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#prefer-public-send 1454 | Enabled: false 1455 | 1456 | Style/SignalException: 1457 | Description: Checks for proper usage of fail and raise. 1458 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#prefer-raise-over-fail 1459 | Enabled: true 1460 | 1461 | Style/SingleLineBlockParams: 1462 | Description: Enforces the names of some block params. 1463 | Enabled: false 1464 | 1465 | Style/SingleLineMethods: 1466 | Description: Avoid single-line methods. 1467 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-single-line-methods 1468 | Enabled: false 1469 | 1470 | Style/SpecialGlobalVars: 1471 | Description: Avoid Perl-style global variables. 1472 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms 1473 | Enabled: false 1474 | 1475 | Style/StabbyLambdaParentheses: 1476 | Description: Check for the usage of parentheses around stabby lambda arguments. 1477 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#stabby-lambda-with-args 1478 | Enabled: true 1479 | 1480 | Style/StderrPuts: 1481 | Description: Use `warn` instead of `$stderr.puts`. 1482 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#warn 1483 | Enabled: true 1484 | 1485 | Style/StringLiterals: 1486 | Description: Checks if uses of quotes match the configured preference. 1487 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-string-literals 1488 | EnforcedStyle: double_quotes 1489 | Enabled: true 1490 | 1491 | Style/StringLiteralsInInterpolation: 1492 | Description: Checks if uses of quotes inside expressions in interpolated strings match the configured preference. 1493 | Enabled: true 1494 | 1495 | Style/StringMethods: 1496 | Description: Checks if configured preferred methods are used over non-preferred. 1497 | Enabled: false 1498 | 1499 | Style/StructInheritance: 1500 | Description: Checks for inheritance from Struct.new. 1501 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-extend-struct-new 1502 | Enabled: false 1503 | 1504 | Style/SymbolArray: 1505 | Description: Use %i or %I for arrays of symbols. 1506 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-i 1507 | Enabled: true 1508 | 1509 | Style/SymbolLiteral: 1510 | Description: Use plain symbols instead of string symbols when possible. 1511 | Enabled: false 1512 | 1513 | Style/SymbolProc: 1514 | Description: Use symbols as procs instead of blocks when possible. 1515 | Enabled: false 1516 | 1517 | Style/TernaryParentheses: 1518 | Description: Checks for use of parentheses around ternary conditions. 1519 | Enabled: true 1520 | 1521 | Style/MixinUsage: 1522 | Description: Checks that `include`, `extend` and `prepend` exists at the top level. 1523 | Enabled: true 1524 | 1525 | Style/TrailingCommaInArguments: 1526 | Description: Checks for trailing comma in argument lists. 1527 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-trailing-params-comma' 1528 | Enabled: false 1529 | 1530 | Style/TrailingCommaInArrayLiteral: 1531 | Description: Checks for trailing comma in array and hash literals.' 1532 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas 1533 | Enabled: false 1534 | 1535 | Style/TrailingCommaInHashLiteral: 1536 | Description: Checks for trailing comma in array and hash literals.' 1537 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas 1538 | Enabled: false 1539 | 1540 | Style/TrivialAccessors: 1541 | Description: Prefer attr_* methods to trivial readers/writers. 1542 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#attr_family 1543 | Enabled: false 1544 | 1545 | Style/UnlessElse: 1546 | Description: Do not use unless with else. Rewrite these with the positive case first. 1547 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-else-with-unless 1548 | Enabled: true 1549 | 1550 | Style/RedundantCapitalW: 1551 | Description: Checks for %W when interpolation is not needed. 1552 | Enabled: false 1553 | 1554 | Style/RedundantInterpolation: 1555 | Description: Checks for strings that are just an interpolated expression. 1556 | Enabled: true 1557 | 1558 | Style/RedundantPercentQ: 1559 | Description: Checks for %q/%Q when single quotes or double quotes would do. 1560 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-q 1561 | Enabled: false 1562 | 1563 | Style/TrailingUnderscoreVariable: 1564 | Description: Checks for the usage of unneeded trailing underscores at the end of parallel variable assignment. 1565 | AllowNamedUnderscoreVariables: true 1566 | Enabled: false 1567 | 1568 | Style/VariableInterpolation: 1569 | Description: Don't interpolate global, instance and class variables directly in strings. 1570 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#curlies-interpolate 1571 | Enabled: true 1572 | 1573 | Style/WhenThen: 1574 | Description: Use when x then ... for one-line cases. 1575 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#one-line-cases 1576 | Enabled: true 1577 | 1578 | Style/WhileUntilDo: 1579 | Description: Checks for redundant do after while or until. 1580 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-multiline-while-do 1581 | Enabled: true 1582 | 1583 | Style/WhileUntilModifier: 1584 | Description: Favor modifier while/until usage when you have a single-line body. 1585 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#while-as-a-modifier 1586 | Enabled: true 1587 | 1588 | Style/WordArray: 1589 | Description: Use %w or %W for arrays of words. 1590 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-w 1591 | Enabled: true 1592 | 1593 | Style/YodaCondition: 1594 | Description: Do not use literals as the first operand of a comparison. 1595 | Reference: https://en.wikipedia.org/wiki/Yoda_conditions 1596 | Enabled: true 1597 | 1598 | Style/ZeroLengthPredicate: 1599 | Description: Use #empty? when testing for objects of length 0. 1600 | Enabled: true 1601 | 1602 | Lint/RaiseException: 1603 | Description: Checks for `raise` or `fail` statements which are raising `Exception` class. 1604 | Enabled: true 1605 | Lint/StructNewOverride: 1606 | Description: 'Disallow overriding the `Struct` built-in methods via `Struct.new`.' 1607 | Enabled: true 1608 | Style/HashEachMethods: 1609 | Description: 'Use Hash#each_key and Hash#each_value.' 1610 | Enabled: true 1611 | Safe: false 1612 | Style/HashTransformKeys: 1613 | Description: 'Prefer `transform_keys` over `each_with_object` and `map`.' 1614 | Enabled: true 1615 | Safe: false 1616 | Style/HashTransformValues: 1617 | Description: 'Prefer `transform_values` over `each_with_object` and `map`.' 1618 | Enabled: true 1619 | Safe: false 1620 | 1621 | Layout/EmptyLinesAroundAttributeAccessor: 1622 | Enabled: true 1623 | Lint/DeprecatedOpenSSLConstant: 1624 | Enabled: true 1625 | Style/SlicingWithRange: 1626 | Enabled: true 1627 | Lint/MixedRegexpCaptureTypes: 1628 | Enabled: true 1629 | Style/RedundantRegexpCharacterClass: 1630 | Enabled: true 1631 | Style/RedundantRegexpEscape: 1632 | Enabled: true 1633 | Lint/MissingSuper: 1634 | Enabled: false 1635 | 1636 | 1637 | 1638 | Lint/DuplicateElsifCondition: 1639 | Enabled: true 1640 | Lint/DuplicateRescueException: 1641 | Enabled: true 1642 | Lint/EmptyConditionalBody: 1643 | Enabled: true 1644 | Lint/FloatComparison: 1645 | Enabled: true 1646 | Lint/OutOfRangeRegexpRef: 1647 | Enabled: true 1648 | Lint/SelfAssignment: 1649 | Enabled: true 1650 | Lint/TopLevelReturnWithArgument: 1651 | Enabled: true 1652 | Lint/UnreachableLoop: 1653 | Enabled: true 1654 | Style/AccessorGrouping: 1655 | Enabled: true 1656 | Style/ArrayCoercion: 1657 | Enabled: true 1658 | Style/BisectedAttrAccessor: 1659 | Enabled: true 1660 | Style/CaseLikeIf: 1661 | Enabled: true 1662 | Style/ExplicitBlockArgument: 1663 | Enabled: false 1664 | Style/GlobalStdStream: 1665 | Enabled: true 1666 | Style/HashAsLastArrayItem: 1667 | Enabled: false 1668 | Style/HashLikeCase: 1669 | Enabled: true 1670 | Style/OptionalBooleanParameter: 1671 | Enabled: true 1672 | Style/RedundantAssignment: 1673 | Enabled: true 1674 | Style/RedundantFetchBlock: 1675 | Enabled: true 1676 | Style/RedundantFileExtensionInRequire: 1677 | Enabled: true 1678 | Style/SingleArgumentDig: 1679 | Enabled: true 1680 | Style/StringConcatenation: 1681 | Enabled: true 1682 | Performance/AncestorsInclude: 1683 | Enabled: true 1684 | Performance/BigDecimalWithNumericArgument: 1685 | Enabled: true 1686 | Performance/RedundantSortBlock: 1687 | Enabled: true 1688 | Performance/RedundantStringChars: 1689 | Enabled: true 1690 | Performance/ReverseFirst: 1691 | Enabled: true 1692 | Performance/SortReverse: 1693 | Enabled: true 1694 | Performance/Squeeze: 1695 | Enabled: true 1696 | Performance/StringInclude: 1697 | Enabled: true 1698 | Rails/ActiveRecordCallbacksOrder: 1699 | Enabled: true 1700 | Rails/FindById: 1701 | Enabled: true 1702 | Rails/Inquiry: 1703 | Enabled: false 1704 | Rails/MailerName: 1705 | Enabled: true 1706 | Rails/MatchRoute: 1707 | Enabled: true 1708 | Rails/NegateInclude: 1709 | Enabled: true 1710 | Rails/Pluck: 1711 | Enabled: true 1712 | Rails/PluckInWhere: 1713 | Enabled: true 1714 | Rails/RenderInline: 1715 | Enabled: true 1716 | Rails/RenderPlainText: 1717 | Enabled: true 1718 | Rails/ShortI18n: 1719 | Enabled: true 1720 | Rails/WhereExists: 1721 | Enabled: true 1722 | Rails/SkipsModelValidations: 1723 | Enabled: false 1724 | Rails/UniqueValidationWithoutIndex: 1725 | Enabled: false 1726 | Rails/InverseOf: 1727 | Enabled: false 1728 | Rails/LexicallyScopedActionFilter: 1729 | Enabled: false 1730 | Rails/OutputSafety: 1731 | Enabled: false 1732 | Naming/BlockForwarding: 1733 | Enabled: false 1734 | Rails/ReversibleMigration: 1735 | Enabled: false 1736 | Rails/I18nLocaleTexts: 1737 | Enabled: false 1738 | Rails/MigrationClassName: 1739 | Enabled: false 1740 | Rails/BulkChangeTable: 1741 | Enabled: false 1742 | Rails/DotSeparatedKeys: 1743 | Enabled: false 1744 | Rails/AttributeDefaultBlockValue: 1745 | Enabled: false 1746 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.3.5 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 5 | 6 | gemspec 7 | 8 | group :development, :test do 9 | gem "activerecord-enhancedsqlite3-adapter" 10 | gem "pg" 11 | gem "simplecov" 12 | gem "trilogy" 13 | 14 | gem "rubocop" 15 | gem "rubocop-performance" 16 | gem "rubocop-rails" 17 | 18 | gem "propshaft" 19 | gem "puma" 20 | 21 | gem "importmap-rails" 22 | gem "stimulus-rails" 23 | 24 | gem "rubystats" 25 | 26 | gem "solid_queue" 27 | end 28 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Nick Pezza 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ActiveInsights 2 | 3 | One of the fundamental tools needed when taking your Rails app to production is 4 | a way to track response times. Unfortunately, there aren't many free, easy, 5 | open source ways to track them for small or medium apps. Skylight, Honeybadger, 6 | Sentry, AppSignal, etc. are great, but they are are closed source and 7 | there should be an easy open source alternative where you control the data. 8 | With Rails 8's ethos of No PAAS, there should be a way for new apps to start out 9 | with a basic error reporter and not be forced to pay a third party for one. 10 | 11 | ActiveInsights hooks into the ActiveSupport [instrumention](https://guides.rubyonrails.org/active_support_instrumentation.html#) 12 | baked directly into Rails. ActiveInsights tracks RPM, RPM per controller, and 13 | p50/p95/p99 response times and charts all those by the minute. It also tracks 14 | jobs per minute, their duration, and latency. 15 | 16 | ![screenshot 1](https://github.com/npezza93/activeinsights/blob/main/.github/screenshot1.png) 17 | ![screenshot 2](https://github.com/npezza93/activeinsights/blob/main/.github/screenshot2.png) 18 | ![screenshot 3](https://github.com/npezza93/activeinsights/blob/main/.github/screenshot3.png) 19 | ![screenshot 4](https://github.com/npezza93/activeinsights/blob/main/.github/screenshot4.png) 20 | ![screenshot 5](https://github.com/npezza93/activeinsights/blob/main/.github/screenshot5.png) 21 | 22 | ## Installation 23 | Add this line to your application's Gemfile: 24 | 25 | ```ruby 26 | gem "activeinsights" 27 | ``` 28 | 29 | And then execute: 30 | ```bash 31 | $ bundle 32 | ``` 33 | 34 | Or install it yourself as: 35 | ```bash 36 | $ gem install activeinsights 37 | ``` 38 | 39 | Run the installer and migrate: 40 | ```bash 41 | bin/rails active_insights:install 42 | bin/rails rails db:migrate 43 | ``` 44 | 45 | This will mount a route in your routes file to view the insights at `/insights`. 46 | 47 | 48 | ##### Config 49 | 50 | You can supply a hash of connection options to `connects_to` set the connection 51 | options for the `Request` model. 52 | 53 | ```ruby 54 | config.active_insights.connects_to = { database: { writing: :requests, reading: :requests } } 55 | ``` 56 | 57 | You can supply an array of ignored endpoints 58 | 59 | ```ruby 60 | config.active_insights.ignored_endpoints = ["Rails::HealthController#show"] 61 | ``` 62 | 63 | If you are using Sprockets, add the ActiveInsights css file to manifest.js: 64 | ```javascript 65 | //= link active_insights/application.css 66 | ``` 67 | 68 | ## Development 69 | 70 | After checking out the repo, run `bin/setup` to install dependencies. Then, run 71 | `rails test` to run the unit tests. 72 | 73 | To install this gem onto your local machine, run `bundle exec rake install`. To 74 | release a new version, execute `bin/publish (major|minor|patch)` which will 75 | update the version number in `version.rb`, create a git tag for the version, 76 | push git commits and tags, and push the `.gem` file to GitHub. 77 | 78 | ## Contributing 79 | 80 | Bug reports and pull requests are welcome on 81 | [GitHub](https://github.com/npezza93/activeinsights). This project is intended to 82 | be a safe, welcoming space for collaboration, and contributors are expected to 83 | adhere to the [Contributor Covenant](http://contributor-covenant.org) code of 84 | conduct. 85 | 86 | ## License 87 | 88 | The gem is available as open source under the terms of the 89 | [MIT License](https://opensource.org/licenses/MIT). 90 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | begin 4 | require "bundler/setup" 5 | rescue LoadError 6 | puts "You must `gem install bundler` and `bundle install` to run rake tasks" 7 | end 8 | 9 | APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__) 10 | load "rails/tasks/engine.rake" 11 | 12 | load "rails/tasks/statistics.rake" 13 | 14 | require "bundler/gem_tasks" 15 | 16 | require "rake/testtask" 17 | 18 | Rake::TestTask.new(:test) do |t| 19 | t.libs << "test" 20 | t.pattern = "test/**/*_test.rb" 21 | t.verbose = false 22 | end 23 | 24 | task default: :test 25 | -------------------------------------------------------------------------------- /active_insights.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $:.push File.expand_path("lib", __dir__) 4 | 5 | require "active_insights/version" 6 | 7 | Gem::Specification.new do |spec| 8 | spec.name = "activeinsights" 9 | spec.version = ActiveInsights::VERSION 10 | spec.authors = ["Nick Pezza"] 11 | spec.email = ["pezza@hey.com"] 12 | spec.homepage = "https://github.com/npezza93/activeinsights" 13 | spec.summary = "Rails performance tracking" 14 | spec.license = "MIT" 15 | 16 | spec.metadata["rubygems_mfa_required"] = "true" 17 | spec.required_ruby_version = ">= 3.2.0" 18 | spec.files = Dir["{app,config,db,lib,vendor}/**/*", "LICENSE.md", 19 | "Rakefile", "README.md"] 20 | 21 | spec.add_dependency "chartkick" 22 | spec.add_dependency "importmap-rails" 23 | spec.add_dependency "rails", ">= 7.2" 24 | end 25 | -------------------------------------------------------------------------------- /app/assets/stylesheets/active_insights/application.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Calibre'; 3 | font-style: normal; 4 | font-weight: 400; 5 | src: url('./calibre-regular.woff2') format('woff2'); 6 | font-display: swap; 7 | } 8 | 9 | @font-face { 10 | font-family: 'Calibre'; 11 | font-style: italic; 12 | font-weight: 400; 13 | src: url('./calibre-regular-italic.woff2') format('woff2'); 14 | font-display: swap; 15 | } 16 | 17 | @font-face { 18 | font-family: 'Calibre'; 19 | font-style: normal; 20 | font-weight: 600; 21 | src: url('./calibre-semibold.woff2') format('woff2'); 22 | font-display: swap; 23 | } 24 | 25 | @font-face { 26 | font-family: 'Calibre'; 27 | font-style: italic; 28 | font-weight: 600; 29 | src: url('./calibre-semibold-italic.woff2') format('woff2'); 30 | font-display: swap; 31 | } 32 | 33 | @font-face { 34 | font-family: 'Calibre'; 35 | font-style: normal; 36 | font-weight: 700; 37 | src: url('./calibre-bold.woff2') format('woff2'); 38 | font-display: swap; 39 | } 40 | 41 | body { 42 | background-color: #fff; 43 | color: #333; 44 | color-scheme: light dark; 45 | supported-color-schemes: light dark; 46 | margin: 0px; 47 | } 48 | 49 | body, p, ol, ul, td, input { 50 | font-family: helvetica, verdana, arial, sans-serif; 51 | font-size: 15px; 52 | line-height: 18px; 53 | } 54 | 55 | form { 56 | margin-bottom: 0px; 57 | } 58 | 59 | pre { 60 | font-size: 11px; 61 | white-space: pre-wrap; 62 | } 63 | 64 | pre.box { 65 | border: 1px solid #EEE; 66 | padding: 10px; 67 | margin: 0px; 68 | width: 958px; 69 | } 70 | 71 | header { 72 | color: #D30001; 73 | background: rgb(238, 231, 233); 74 | padding: 0.5em 1.5em; 75 | display: flex; 76 | flex-direction: row; 77 | justify-content: space-between; 78 | align-items: center; 79 | } 80 | 81 | .tooltip { 82 | background: rgb(238, 231, 233); 83 | } 84 | 85 | header h1 { 86 | font-family: 'Calibre'; 87 | } 88 | 89 | h1 { 90 | overflow-wrap: break-word; 91 | margin: 0.2em 0; 92 | line-height: 1.1em; 93 | font-size: 2em; 94 | } 95 | 96 | h2 { 97 | color: #C00; 98 | line-height: 25px; 99 | } 100 | 101 | code.traces { 102 | font-size: 11px; 103 | } 104 | 105 | .response-heading, .request-heading { 106 | margin-top: 30px; 107 | } 108 | 109 | .exception-message { 110 | padding: 8px 0; 111 | } 112 | 113 | .exception-message .message { 114 | margin-bottom: 8px; 115 | line-height: 25px; 116 | font-size: 1.5em; 117 | font-weight: bold; 118 | color: #C00; 119 | } 120 | 121 | .details { 122 | border: 1px solid #D0D0D0; 123 | border-radius: 4px; 124 | margin: 1em 0px; 125 | display: block; 126 | max-width: 978px; 127 | } 128 | 129 | .summary { 130 | padding: 8px 15px; 131 | border-bottom: 1px solid #D0D0D0; 132 | display: block; 133 | } 134 | 135 | a.summary { 136 | color: #F0F0F0; 137 | text-decoration: none; 138 | background: #C52F24; 139 | border-bottom: none; 140 | } 141 | 142 | .details pre { 143 | margin: 5px; 144 | border: none; 145 | } 146 | 147 | #container { 148 | box-sizing: border-box; 149 | width: 100%; 150 | padding: 0 1.5em; 151 | } 152 | 153 | .source * { 154 | margin: 0px; 155 | padding: 0px; 156 | } 157 | 158 | .source { 159 | border: 1px solid #D9D9D9; 160 | background: #ECECEC; 161 | max-width: 978px; 162 | } 163 | 164 | .source pre { 165 | padding: 10px 0px; 166 | border: none; 167 | } 168 | 169 | .source .data { 170 | font-size: 80%; 171 | overflow: auto; 172 | background-color: #FFF; 173 | } 174 | 175 | .info { 176 | padding: 0.5em; 177 | } 178 | 179 | .source .data .line_numbers { 180 | background-color: #ECECEC; 181 | color: #555; 182 | padding: 1em .5em; 183 | border-right: 1px solid #DDD; 184 | text-align: right; 185 | } 186 | 187 | .line { 188 | padding-left: 10px; 189 | white-space: pre; 190 | } 191 | 192 | .line:hover { 193 | background-color: #F6F6F6; 194 | } 195 | 196 | .line.active { 197 | background-color: #FCC; 198 | } 199 | 200 | .error_highlight { 201 | display: inline-block; 202 | background-color: #FF9; 203 | text-decoration: #F00 wavy underline; 204 | } 205 | 206 | .error_highlight_tip { 207 | color: #666; 208 | padding: 2px 2px; 209 | font-size: 10px; 210 | } 211 | 212 | .button_to { 213 | display: inline-block; 214 | margin-top: 0.75em; 215 | margin-bottom: 0.75em; 216 | } 217 | 218 | .hidden { 219 | display: none; 220 | } 221 | 222 | .red { 223 | color: #D30001; 224 | } 225 | 226 | .calibre { 227 | font-family: 'Calibre'; 228 | } 229 | 230 | .link { 231 | color: #261B23; 232 | font-size: 20px; 233 | &:hover { 234 | color: #D30001; 235 | } 236 | } 237 | 238 | .datepicker { 239 | background-color: #D30001; 240 | border-radius: 9999px; 241 | padding-left: 15px; 242 | padding-right: 15px; 243 | padding-top: 5px; 244 | padding-bottom: 5px; 245 | border: none; 246 | cursor: pointer; 247 | } 248 | 249 | .correction { 250 | list-style-type: none; 251 | } 252 | 253 | input[type="submit"] { 254 | color: white; 255 | background-color: #C00; 256 | border: none; 257 | border-radius: 12px; 258 | box-shadow: 0 3px #F99; 259 | font-size: 13px; 260 | font-weight: bold; 261 | margin: 0; 262 | padding: 10px 18px; 263 | cursor: pointer; 264 | -webkit-appearance: none; 265 | } 266 | input[type="submit"]:focus, 267 | input[type="submit"]:hover { 268 | opacity: 0.8; 269 | } 270 | input[type="submit"]:active { 271 | box-shadow: 0 2px #F99; 272 | transform: translateY(1px) 273 | } 274 | 275 | a.trace-frames { 276 | color: #666; 277 | overflow-wrap: break-word; 278 | } 279 | a:hover, a.trace-frames.selected { color: #C00; } 280 | a.summary:hover { color: #FFF; } 281 | 282 | table { 283 | margin: 0; 284 | border-collapse: collapse; 285 | word-wrap:break-word; 286 | table-layout: auto; 287 | width: 100%; 288 | margin-top: 50px; 289 | } 290 | 291 | table thead tr { 292 | border-bottom: 3px solid rgba(38,27,35,0.1); 293 | } 294 | 295 | table th { 296 | padding-left: 30px; 297 | text-align: left; 298 | font-family: 'Calibre'; 299 | font-size: 20px; 300 | } 301 | 302 | table tbody tr { 303 | border-bottom: 3px solid rgba(38,27,35,0.1); 304 | td { 305 | font-family: 'Calibre'; 306 | font-size: 20px; 307 | 308 | a { 309 | color: #261B23; 310 | &:hover { 311 | color: #D30001; 312 | } 313 | } 314 | } 315 | } 316 | 317 | table tbody tr:nth-child(odd) { 318 | background: rgb(238, 231, 233); 319 | } 320 | 321 | table td { 322 | padding: 10px 30px; 323 | } 324 | 325 | .pl-30px { 326 | padding-left: 30px; 327 | } 328 | 329 | .pt-30px { 330 | padding-top: 30px; 331 | } 332 | 333 | .flex { 334 | display: flex; 335 | } 336 | 337 | .flex-col { 338 | flex-direction: column; 339 | } 340 | .flex-row { 341 | flex-direction: row; 342 | } 343 | .justify-around { 344 | justify-content: space-around; 345 | } 346 | 347 | .justify-center { 348 | justify-content: center; 349 | } 350 | .items-center { 351 | align-items: center; 352 | } 353 | .font-size-30 { 354 | font-size: 30px; 355 | line-height: 30px; 356 | } 357 | .no-underline { 358 | text-decoration: none; 359 | } 360 | .p-16px { 361 | padding: 16px; 362 | } 363 | 364 | a.white { 365 | color: #fff; 366 | } 367 | .mr-15px { 368 | margin-right: 15px; 369 | } 370 | .button { 371 | transition: all 0.25s cubic-bezier(0.33, 1, 0.68, 1); 372 | padding: 5px 20px; 373 | border-radius: 6px; 374 | 375 | &:hover { 376 | background: #F0E7E9; 377 | } 378 | } 379 | .relative { 380 | position: relative; 381 | } 382 | .absolute { 383 | position: absolute; 384 | } 385 | .h-400px { 386 | height: 400px; 387 | } 388 | .h-100 { 389 | height: 100%; 390 | } 391 | .text-center { 392 | text-align: center; 393 | } 394 | .w-175px { 395 | width: 175px; 396 | } 397 | -------------------------------------------------------------------------------- /app/assets/stylesheets/active_insights/calibre-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npezza93/activeinsights/7fea0352e68dc9c9ad2391a0629bb6d282873e01/app/assets/stylesheets/active_insights/calibre-bold.woff2 -------------------------------------------------------------------------------- /app/assets/stylesheets/active_insights/calibre-regular-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npezza93/activeinsights/7fea0352e68dc9c9ad2391a0629bb6d282873e01/app/assets/stylesheets/active_insights/calibre-regular-italic.woff2 -------------------------------------------------------------------------------- /app/assets/stylesheets/active_insights/calibre-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npezza93/activeinsights/7fea0352e68dc9c9ad2391a0629bb6d282873e01/app/assets/stylesheets/active_insights/calibre-regular.woff2 -------------------------------------------------------------------------------- /app/assets/stylesheets/active_insights/calibre-semibold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npezza93/activeinsights/7fea0352e68dc9c9ad2391a0629bb6d282873e01/app/assets/stylesheets/active_insights/calibre-semibold-italic.woff2 -------------------------------------------------------------------------------- /app/assets/stylesheets/active_insights/calibre-semibold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npezza93/activeinsights/7fea0352e68dc9c9ad2391a0629bb6d282873e01/app/assets/stylesheets/active_insights/calibre-semibold.woff2 -------------------------------------------------------------------------------- /app/controllers/active_insights/application_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveInsights 4 | class ApplicationController < ActionController::Base 5 | protect_from_forgery with: :exception 6 | 7 | around_action :setup_time_zone 8 | before_action :set_date 9 | 10 | private 11 | 12 | def set_date 13 | @date = requested_date..([requested_date.end_of_day, Time.current].min) 14 | end 15 | 16 | def setup_time_zone(&block) # rubocop:disable Style/ArgumentsForwarding 17 | Time.use_zone("Eastern Time (US & Canada)", &block) # rubocop:disable Style/ArgumentsForwarding 18 | end 19 | 20 | def requested_date 21 | if params[:date].present? 22 | Date.parse(params[:date]) 23 | else 24 | Date.current 25 | end.beginning_of_day 26 | end 27 | 28 | def base_request_scope 29 | scope = ActiveInsights::Request.where(started_at: @date) 30 | if ActiveInsights.ignored_endpoints.present? 31 | scope = scope.where. 32 | not(formatted_controller: ActiveInsights.ignored_endpoints) 33 | end 34 | scope 35 | end 36 | 37 | def base_jobs_scope 38 | ActiveInsights::Job.where(started_at: @date) 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /app/controllers/active_insights/jobs_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveInsights 4 | class JobsController < ApplicationController 5 | def index 6 | @jobs = base_jobs_scope.with_durations.select(:job, :queue). 7 | group(:job, :queue).sort_by(&:agony).reverse 8 | 9 | @latency = 10 | base_jobs_scope.select("AVG(queue_time) as latency").to_a.first.latency 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/controllers/active_insights/jobs_latencies_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveInsights 4 | class JobsLatenciesController < ApplicationController 5 | def index 6 | @latencies = minutes.map do |minute| 7 | [minute.pretty_started_at, minute.latency.round(1)] 8 | end 9 | end 10 | 11 | def redirection 12 | redirect_to jobs_latency_path(params[:date]) 13 | end 14 | 15 | private 16 | 17 | def minutes 18 | @minutes ||= base_jobs_scope.minute_by_minute.select_started_at. 19 | select("AVG(queue_time) as latency") 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/controllers/active_insights/jobs_p_values_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveInsights 4 | class JobsPValuesController < ApplicationController 5 | def index 6 | @p50 = minutes.map{ |minute| [minute.pretty_started_at, minute.p50] } 7 | @p95 = minutes.map{ |minute| [minute.pretty_started_at, minute.p95] } 8 | @p99 = minutes.map{ |minute| [minute.pretty_started_at, minute.p99] } 9 | 10 | fetch_jpm 11 | end 12 | 13 | def redirection 14 | if job.present? 15 | redirect_to job_p_values_path(params[:date], job) 16 | else 17 | redirect_to jobs_p_values_path(params[:date]) 18 | end 19 | end 20 | 21 | private 22 | 23 | def minutes 24 | @minutes ||= begin 25 | scope = base_jobs_scope.minute_by_minute.with_durations 26 | scope = scope.where(job:) if job.present? 27 | scope.select_started_at 28 | end 29 | end 30 | 31 | def job 32 | params[:job].presence 33 | end 34 | 35 | def fetch_jpm 36 | return if job.blank? 37 | 38 | @jpm = 39 | minutes.select("COUNT(id) AS jpm").map do |minute| 40 | [minute.started_at.strftime("%-l:%M%P"), minute.jpm] 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /app/controllers/active_insights/jpm_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveInsights 4 | class JpmController < ApplicationController 5 | def index 6 | @minutes = 7 | base_jobs_scope.minute_by_minute.select("COUNT(id) AS jpm"). 8 | select_started_at.map do |minute| 9 | [minute.started_at.strftime("%-l:%M%P"), minute.jpm] 10 | end 11 | end 12 | 13 | def redirection 14 | redirect_to jpm_path(params[:date]) 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/active_insights/requests_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveInsights 4 | class RequestsController < ApplicationController 5 | def index 6 | @requests = 7 | base_request_scope.with_durations.select(:formatted_controller). 8 | group(:formatted_controller).sort_by(&:agony).reverse 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/active_insights/requests_p_values_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveInsights 4 | class RequestsPValuesController < ApplicationController 5 | def index 6 | @p50 = minutes.map{ |minute| [minute.pretty_started_at, minute.p50] } 7 | @p95 = minutes.map{ |minute| [minute.pretty_started_at, minute.p95] } 8 | @p99 = minutes.map{ |minute| [minute.pretty_started_at, minute.p99] } 9 | 10 | fetch_rpm 11 | end 12 | 13 | def redirection 14 | if formatted_controller.present? 15 | redirect_to controller_p_values_path(params[:date], 16 | formatted_controller) 17 | else 18 | redirect_to requests_p_values_path(params[:date]) 19 | end 20 | end 21 | 22 | private 23 | 24 | def minutes 25 | @minutes ||= begin 26 | scope = base_request_scope.minute_by_minute.with_durations 27 | scope = scope.where(formatted_controller:) if 28 | formatted_controller.present? 29 | scope.select_started_at 30 | end 31 | end 32 | 33 | def formatted_controller 34 | params[:formatted_controller].presence 35 | end 36 | 37 | def fetch_rpm 38 | return if formatted_controller.blank? 39 | 40 | @rpm = 41 | minutes.select("COUNT(id) AS rpm").map do |minute| 42 | [minute.started_at.strftime("%-l:%M%P"), minute.rpm] 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /app/controllers/active_insights/rpm_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveInsights 4 | class RpmController < ApplicationController 5 | def index 6 | @minutes = 7 | base_request_scope.minute_by_minute.select("COUNT(id) AS rpm"). 8 | select_started_at.map do |minute| 9 | [minute.started_at.strftime("%-l:%M%P"), minute.rpm] 10 | end 11 | end 12 | 13 | def redirection 14 | redirect_to rpm_path(params[:date]) 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/helpers/active_insights/application_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveInsights 4 | module ApplicationHelper 5 | def active_insights_importmap_tags 6 | importmap = ActiveInsights::Engine.importmap 7 | 8 | safe_join [ 9 | javascript_inline_importmap_tag(importmap.to_json(resolver: self)), 10 | javascript_importmap_module_preload_tags(importmap), 11 | ], "\n" 12 | end 13 | 14 | def display_date(date) 15 | if Date.current.year == date.year 16 | date.strftime("%B %-d") 17 | else 18 | date.strftime("%B %-d, %Y") 19 | end 20 | end 21 | 22 | def p50(data) 23 | percentile_value(data, 0.5) 24 | end 25 | 26 | def p95(data) 27 | percentile_value(data, 0.95) 28 | end 29 | 30 | def p99(data) 31 | percentile_value(data, 0.99) 32 | end 33 | 34 | def per_minute(amount, duration) 35 | (amount / duration.in_minutes).round(1) 36 | end 37 | 38 | private 39 | 40 | def percentile_value(data, percentile) 41 | value = data.sort[(percentile * data.size).ceil - 1] 42 | 43 | value&.round(1) || "N/A" 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /app/models/active_insights/job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveInsights 4 | class Job < ::ActiveInsights::Record 5 | def self.setup(started, finished, unique_id, payload) 6 | create!(started_at: started, finished_at: finished, uuid: unique_id, 7 | db_runtime: payload[:db_runtime], job: payload[:job].class.to_s, 8 | queue: payload[:job].queue_name, 9 | scheduled_at: payload[:job].scheduled_at) 10 | end 11 | 12 | before_validation do 13 | self.queue_time ||= 14 | if scheduled_at.blank? 15 | 0.0 16 | else 17 | (started_at - scheduled_at) * 1000.0 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/models/active_insights/record.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveInsights 4 | class Record < ApplicationRecord 5 | self.abstract_class = true 6 | 7 | connects_to(**ActiveInsights.connects_to) if ActiveInsights.connects_to 8 | 9 | scope :with_durations, lambda { 10 | case connection.adapter_name 11 | when "SQLite" 12 | select("JSON_GROUP_ARRAY(duration) AS durations") 13 | when "Mysql2", "Mysql2Spatial", "Mysql2Rgeo", "Trilogy" 14 | select("JSON_ARRAYAGG(duration) AS durations") 15 | when "PostgreSQL" 16 | select("JSON_AGG(duration) AS durations") 17 | end 18 | } 19 | scope :minute_by_minute, lambda { 20 | case connection.adapter_name 21 | when "SQLite" 22 | group("strftime('%Y-%m-%d %H:%M:00 UTC', " \ 23 | "'#{table_name}'.'started_at')") 24 | when "Mysql2", "Mysql2Spatial", "Mysql2Rgeo", "Trilogy" 25 | group("CONVERT_TZ(DATE_FORMAT(#{table_name.split('.').map do |arg| 26 | "`#{arg}`" 27 | end.join('.')}.`started_at`" \ 28 | ", '%Y-%m-%d %H:%i:00'), 'Etc/UTC', '+00:00')") 29 | when "PostgreSQL" 30 | group("DATE_TRUNC('minute', \"#{table_name}\"." \ 31 | "\"started_at\"::timestamptz AT TIME ZONE 'Etc/UTC') " \ 32 | "AT TIME ZONE 'Etc/UTC'") 33 | end 34 | } 35 | scope :select_started_at, lambda { 36 | case connection.adapter_name 37 | when "SQLite", "Mysql2", "Mysql2Spatial", "Mysql2Rgeo", "Trilogy" 38 | select(:started_at) 39 | when "PostgreSQL" 40 | select("DATE_TRUNC('minute', \"#{table_name}\"." \ 41 | "\"started_at\"::timestamptz AT TIME ZONE 'Etc/UTC') " \ 42 | "AT TIME ZONE 'Etc/UTC' as started_at") 43 | end 44 | } 45 | 46 | before_validation do 47 | self.duration ||= (finished_at - started_at) * 1000.0 48 | end 49 | 50 | def agony 51 | parsed_durations.sum 52 | end 53 | 54 | def parsed_durations 55 | return unless respond_to?(:durations) 56 | 57 | @parsed_durations ||= 58 | if durations.is_a?(Array) then durations 59 | else 60 | JSON.parse(durations) 61 | end.sort 62 | end 63 | 64 | def pretty_started_at 65 | started_at.strftime("%-l:%M%P") 66 | end 67 | 68 | def p50 69 | percentile_value(0.5) 70 | end 71 | 72 | def p95 73 | percentile_value(0.95) 74 | end 75 | 76 | def p99 77 | percentile_value(0.99) 78 | end 79 | 80 | private 81 | 82 | def percentile_value(percentile) 83 | parsed_durations[(percentile * parsed_durations.size).ceil - 1].round(1) 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /app/models/active_insights/request.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveInsights 4 | class Request < ::ActiveInsights::Record 5 | def self.setup(started, finished, unique_id, payload) 6 | req = payload[:request] 7 | 8 | create!(started_at: started, ip_address: req.remote_ip, 9 | finished_at: finished, uuid: unique_id, 10 | http_method: payload[:method], 11 | user_agent: req.user_agent.to_s.first(255), 12 | **payload.slice(:controller, :action, :format, :status, 13 | :view_runtime, :db_runtime, :path)) 14 | end 15 | 16 | def percentage(others) 17 | (agony / others.sum(&:agony)) * 100.0 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /app/views/active_insights/jobs/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

Metrics for <%= display_date(@date.first) %>

3 |
4 | <%= link_to "View request metrics", active_insights.requests_path, class: "link calibre mr-15px" %> 5 | 6 | <%= form_with url: active_insights.jobs_path, method: :get do |f| %> 7 | <%= f.date_field :date, max: Date.current, onchange: "this.form.submit()", class: "datepicker", value: @date.first.to_date %> 8 | <% f.submit "submit", class: "hidden" %> 9 | <% end %> 10 |
11 |
12 | 13 |
14 | <% @jobs.flat_map(&:parsed_durations).tap do |durations| %> 15 | <%= link_to jpm_path(@date.first.to_date), class: "button calibre red flex flex-col justify-center items-center no-underline" do %> 16 |
<%= per_minute(durations.size, (@date.last - @date.first).seconds) %>
17 | JPM 18 | <% end %> 19 | 20 | <%= link_to jobs_p_values_path(@date.first.to_date), class: "button calibre red flex flex-col justify-center items-center no-underline" do %> 21 |
<%= p50(durations) %> ms
22 | p50 23 | <% end %> 24 | 25 | <%= link_to jobs_p_values_path(@date.first.to_date), class: "button calibre red flex flex-col justify-center items-center no-underline" do %> 26 |
<%= p95(durations) %> ms
27 | p95 28 | <% end %> 29 | 30 | <%= link_to jobs_p_values_path(@date.first.to_date), class: "button calibre red flex flex-col justify-center items-center no-underline" do %> 31 |
<%= p99(durations) %> ms
32 | p99 33 | <% end %> 34 | 35 | <%= link_to jobs_latency_path(@date.first.to_date), class: "button calibre red flex flex-col justify-center items-center no-underline" do %> 36 |
<%= @latency.to_f.round(1) %> ms
37 | Latency 38 | <% end %> 39 | <% end %> 40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | <% @jobs.each do |model| %> 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | <% end %> 64 | 65 |
NameQueueJPMp50p95p99
<%= link_to model.job, job_p_values_path(@date.first.to_date, model.job) %><%= model.queue %><%= per_minute(model.parsed_durations.size, (@date.last - @date.first).seconds) %><%= model.p50 %> ms<%= model.p95 %> ms<%= model.p99 %> ms
66 | -------------------------------------------------------------------------------- /app/views/active_insights/jobs_latencies/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

Job Latency for <%= display_date(@date.first) %> (in ms)

3 | 4 | <%= form_with url: active_insights.jobs_latency_redirection_path, method: :get do |f| %> 5 | <%= f.date_field :date, max: Date.current, onchange: "this.form.submit()", class: "datepicker", value: @date.first.to_date %> 6 | <% f.submit "submit", class: "hidden" %> 7 | <% end %> 8 |
9 | 10 |
11 | <%= column_chart @latencies, height: "400px", colors: ["#C00"], library: { 12 | borderSkipped: true, barPercentage: 1, categoryPercentage: 1, 13 | plugins: { tooltip: { backgroundColor: 'rgb(238, 231, 233)', cornerRadius: 5, bodyColor: "#000", bodyAlign: 'center', bodyFont: { size: 20, family: "Calibre" }, titleColor: "#000", titleFont: { size: 20, family: "Calibre" } }, zoom: { zoom: { wheel: { enabled: false }, drag: { enabled: true, backgroundColor: 'rgb(238, 231, 233)' }, mode: 'x' } } }, 14 | scales: { 15 | x: { border: { color: "rgb(238, 231, 233)" }, barPercentage: 1.0, grid: { color: "rgb(238, 231, 233)" }, ticks: { color: "#000", font: { size: 16, family: "Calibre" } }, autoSkip: false, display: true }, 16 | y: { border: { color: "rgb(238, 231, 233)" }, grid: { display: false }, ticks: { font: { size: 16, family: "Calibre" }, color: "#000" }, min: (@latencies.map(&:second).min.to_f * 0.98).ceil, max: (@latencies.map(&:second).max) } 17 | } } %> 18 |
19 | -------------------------------------------------------------------------------- /app/views/active_insights/jobs_p_values/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <% if params[:job].present? %> 3 |

<%= params[:job] %> metrics for <%= display_date(@date.first) %>

4 | <% else %> 5 |

Job Metrics for <%= display_date(@date.first) %> (in ms)

6 | <% end %> 7 | 8 | <%= form_with url: active_insights.jobs_p_values_redirection_path, method: :get do |f| %> 9 | <%= f.hidden_field :job, value: params[:job] %> 10 | <%= f.date_field :date, max: Date.current, onchange: "this.form.submit()", class: "datepicker", value: @date.first.to_date %> 11 | <% f.submit "submit", class: "hidden" %> 12 | <% end %> 13 |
14 | 15 |
16 |

Response Times
(in ms)

17 | <%= line_chart [{ name: "p50", data: @p50 }, { name: "p95", data: @p95 }, { name: "p99", data: @p99 }], height: "500px", colors: ["#C00", "rgb(223, 180, 115)", "#000"], library: { 18 | plugins: { legend: { position: "bottom" }, tooltip: { backgroundColor: 'rgb(238, 231, 233)', cornerRadius: 5, bodyColor: "#000", bodyAlign: 'center', bodyFont: { size: 20, family: "Calibre" }, titleColor: "#000", titleFont: { size: 20, family: "Calibre" } }, zoom: { zoom: { wheel: { enabled: false }, drag: { enabled: true, backgroundColor: 'rgb(238, 231, 233)' }, mode: 'x' } } }, 19 | elements: { point: { radius: 0 } }, 20 | scales: { 21 | x: { border: { color: "rgb(238, 231, 233)" }, grid: { color: "rgb(238, 231, 233)" }, ticks: { color: "#000", font: { size: 16, family: "Calibre" } }, autoSkip: false, display: true }, 22 | y: { border: { color: "rgb(238, 231, 233)" }, grid: { display: false }, ticks: { font: { size: 16, family: "Calibre" }, color: "#000" }, min: (@p50.map(&:second).min.to_f * 0.98).ceil, max: (@p99.map(&:second).max) } 23 | } } %> 24 |
25 | 26 | <% if @jpm %> 27 |
28 |

JPM

29 | <%= column_chart @jpm, height: "400px", colors: ["#C00"], library: { 30 | borderSkipped: true, barPercentage: 1, categoryPercentage: 1, 31 | plugins: { tooltip: { backgroundColor: 'rgb(238, 231, 233)', cornerRadius: 5, bodyColor: "#000", bodyAlign: 'center', bodyFont: { size: 20, family: "Calibre" }, titleColor: "#000", titleFont: { size: 20, family: "Calibre" } }, zoom: { zoom: { wheel: { enabled: false }, drag: { enabled: true, backgroundColor: 'rgb(238, 231, 233)' }, mode: 'x' } } }, 32 | scales: { 33 | x: { border: { color: "rgb(238, 231, 233)" }, barPercentage: 1.0, grid: { color: "rgb(238, 231, 233)" }, ticks: { color: "#000", font: { size: 16, family: "Calibre" } }, autoSkip: false, display: true }, 34 | y: { border: { color: "rgb(238, 231, 233)" }, grid: { display: false }, ticks: { font: { size: 16, family: "Calibre" }, color: "#000" }, min: (@jpm.map(&:second).min.to_f * 0.98).ceil, max: (@jpm.map(&:second).max) } 35 | } } %> 36 |
37 | <% end %> 38 | -------------------------------------------------------------------------------- /app/views/active_insights/jpm/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

JPM Metrics for <%= display_date(@date.first) %>

3 | <%= form_with url: active_insights.jpm_redirection_path, method: :get do |f| %> 4 | <%= f.date_field :date, max: Date.current, onchange: "this.form.submit()", class: "datepicker", value: @date.first.to_date %> 5 | <% f.submit "submit", class: "hidden" %> 6 | <% end %> 7 |
8 | 9 |
10 | <%= column_chart @minutes, height: "400px", colors: ["#C00"], library: { 11 | borderSkipped: true, barPercentage: 1, categoryPercentage: 1, 12 | plugins: { tooltip: { backgroundColor: 'rgb(238, 231, 233)', cornerRadius: 5, bodyColor: "#000", bodyAlign: 'center', bodyFont: { size: 20, family: "Calibre" }, titleColor: "#000", titleFont: { size: 20, family: "Calibre" } }, zoom: { zoom: { wheel: { enabled: false }, drag: { enabled: true, backgroundColor: 'rgb(238, 231, 233)' }, mode: 'x' } } }, 13 | scales: { 14 | x: { border: { color: "rgb(238, 231, 233)" }, barPercentage: 1.0, grid: { color: "rgb(238, 231, 233)" }, ticks: { color: "#000", font: { size: 16, family: "Calibre" } }, autoSkip: false, display: true }, 15 | y: { border: { color: "rgb(238, 231, 233)" }, grid: { display: false }, ticks: { font: { size: 16, family: "Calibre" }, color: "#000" }, min: (@minutes.map(&:second).min.to_f * 0.98).ceil, max: (@minutes.map(&:second).max) } 16 | } } %> 17 |
18 | -------------------------------------------------------------------------------- /app/views/active_insights/requests/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

Metrics for <%= display_date(@date.first) %>

3 |
4 | <%= link_to "View job metrics", active_insights.jobs_path, class: "link calibre mr-15px" %> 5 | 6 | <%= form_with url: active_insights.requests_path, method: :get do |f| %> 7 | <%= f.date_field :date, max: Date.current, onchange: "this.form.submit()", class: "datepicker", value: @date.first.to_date %> 8 | <% f.submit "submit", class: "hidden" %> 9 | <% end %> 10 |
11 |
12 | 13 |
14 | <% @requests.flat_map(&:parsed_durations).tap do |durations| %> 15 | <%= link_to rpm_path(@date.first.to_date), class: "button calibre red flex flex-col justify-center items-center no-underline" do %> 16 |
<%= per_minute(durations.size, (@date.last - @date.first).seconds) %>
17 | RPM 18 | <% end %> 19 | 20 | <%= link_to requests_p_values_path(@date.first.to_date), class: "button calibre red flex flex-col justify-center items-center no-underline" do %> 21 |
<%= p50(durations) %> ms
22 | p50 23 | <% end %> 24 | 25 | <%= link_to requests_p_values_path(@date.first.to_date), class: "button calibre red flex flex-col justify-center items-center no-underline" do %> 26 |
<%= p95(durations) %> ms
27 | p95 28 | <% end %> 29 | 30 | <%= link_to requests_p_values_path(@date.first.to_date), class: "button calibre red flex flex-col justify-center items-center no-underline" do %> 31 |
<%= p99(durations) %> ms
32 | p99 33 | <% end %> 34 | <% end %> 35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | <% @requests.each do |model| %> 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | <% end %> 59 | 60 |
ControllerRPM%p50p95p99
<%= link_to model.formatted_controller, controller_p_values_path(@date.first.to_date, model.formatted_controller) %><%= per_minute(model.parsed_durations.size, (@date.last - @date.first).seconds) %><%= number_to_percentage model.percentage(@requests), precision: 1 %><%= model.p50 %> ms<%= model.p95 %> ms<%= model.p99 %> ms
61 | -------------------------------------------------------------------------------- /app/views/active_insights/requests_p_values/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <% if params[:formatted_controller].present? %> 3 |

<%= params[:formatted_controller] %> metrics for <%= display_date(@date.first) %>

4 | <% else %> 5 |

Response Metrics for <%= display_date(@date.first) %>

6 | <% end %> 7 | 8 | <%= form_with url: active_insights.requests_p_values_redirection_path, method: :get do |f| %> 9 | <%= f.hidden_field :formatted_controller, value: params[:formatted_controller] %> 10 | <%= f.date_field :date, max: Date.current, onchange: "this.form.submit()", class: "datepicker", value: @date.first.to_date %> 11 | <% f.submit "submit", class: "hidden" %> 12 | <% end %> 13 |
14 | 15 |
16 |

Response Times
(in ms)

17 | <%= line_chart [{ name: "p50", data: @p50 }, { name: "p95", data: @p95 }, { name: "p99", data: @p99 }], height: "500px", colors: ["#C00", "rgb(223, 180, 115)", "#000"], library: { 18 | plugins: { legend: { position: "bottom" }, tooltip: { backgroundColor: 'rgb(238, 231, 233)', cornerRadius: 5, bodyColor: "#000", bodyAlign: 'center', bodyFont: { size: 20, family: "Calibre" }, titleColor: "#000", titleFont: { size: 20, family: "Calibre" } }, zoom: { zoom: { wheel: { enabled: false }, drag: { enabled: true, backgroundColor: 'rgb(238, 231, 233)' }, mode: 'x' } } }, 19 | elements: { point: { radius: 0 } }, 20 | scales: { 21 | x: { border: { color: "rgb(238, 231, 233)" }, grid: { color: "rgb(238, 231, 233)" }, ticks: { color: "#000", font: { size: 16, family: "Calibre" } }, autoSkip: false, display: true }, 22 | y: { border: { color: "rgb(238, 231, 233)" }, grid: { display: false }, ticks: { font: { size: 16, family: "Calibre" }, color: "#000" }, min: (@p50.map(&:second).min.to_f * 0.98).ceil, max: (@p99.map(&:second).max) } 23 | } } %> 24 |
25 | 26 | <% if @rpm %> 27 |
28 |

RPM

29 | <%= column_chart @rpm, height: "400px", colors: ["#C00"], library: { 30 | borderSkipped: true, barPercentage: 1, categoryPercentage: 1, 31 | plugins: { tooltip: { backgroundColor: 'rgb(238, 231, 233)', cornerRadius: 5, bodyColor: "#000", bodyAlign: 'center', bodyFont: { size: 20, family: "Calibre" }, titleColor: "#000", titleFont: { size: 20, family: "Calibre" } }, zoom: { zoom: { wheel: { enabled: false }, drag: { enabled: true, backgroundColor: 'rgb(238, 231, 233)' }, mode: 'x' } } }, 32 | scales: { 33 | x: { border: { color: "rgb(238, 231, 233)" }, barPercentage: 1.0, grid: { color: "rgb(238, 231, 233)" }, ticks: { color: "#000", font: { size: 16, family: "Calibre" } }, autoSkip: false, display: true }, 34 | y: { border: { color: "rgb(238, 231, 233)" }, grid: { display: false }, ticks: { font: { size: 16, family: "Calibre" }, color: "#000" }, min: (@rpm.map(&:second).min.to_f * 0.98).ceil, max: (@rpm.map(&:second).max) } 35 | } } %> 36 |
37 | <% end %> 38 | -------------------------------------------------------------------------------- /app/views/active_insights/rpm/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

RPM Metrics for <%= display_date(@date.first) %>

3 | <%= form_with url: active_insights.rpm_redirection_path, method: :get do |f| %> 4 | <%= f.date_field :date, max: Date.current, onchange: "this.form.submit()", class: "datepicker", value: @date.first.to_date %> 5 | <% f.submit "submit", class: "hidden" %> 6 | <% end %> 7 |
8 | 9 |
10 | <%= column_chart @minutes, height: "400px", colors: ["#C00"], library: { 11 | borderSkipped: true, barPercentage: 1, categoryPercentage: 1, 12 | plugins: { tooltip: { backgroundColor: 'rgb(238, 231, 233)', cornerRadius: 5, bodyColor: "#000", bodyAlign: 'center', bodyFont: { size: 20, family: "Calibre" }, titleColor: "#000", titleFont: { size: 20, family: "Calibre" } }, zoom: { zoom: { wheel: { enabled: false }, drag: { enabled: true, backgroundColor: 'rgb(238, 231, 233)' }, mode: 'x' } } }, 13 | scales: { 14 | x: { border: { color: "rgb(238, 231, 233)" }, barPercentage: 1.0, grid: { color: "rgb(238, 231, 233)" }, ticks: { color: "#000", font: { size: 16, family: "Calibre" } }, autoSkip: false, display: true }, 15 | y: { border: { color: "rgb(238, 231, 233)" }, grid: { display: false }, ticks: { font: { size: 16, family: "Calibre" }, color: "#000" }, min: (@minutes.map(&:second).min.to_f * 0.98).ceil, max: (@minutes.map(&:second).max) } 16 | } } %> 17 |
18 | -------------------------------------------------------------------------------- /app/views/layouts/active_insights/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= csrf_meta_tags %> 7 | <%= csp_meta_tag %> 8 | <%= active_insights_importmap_tags %> 9 | 33 | 34 | Active Insights 35 | <%= stylesheet_link_tag "active_insights/application" %> 36 | 37 | 38 | <%= yield %> 39 | 40 | -------------------------------------------------------------------------------- /bin/importmap: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative "../test/dummy/config/application" 4 | require "importmap/commands" 5 | -------------------------------------------------------------------------------- /bin/publish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require "pathname" 5 | require "fileutils" 6 | require_relative "../lib/active_insights/version" 7 | 8 | # path to your application root. 9 | APP_ROOT = Pathname.new File.expand_path("..", __dir__) 10 | MAIN_CHECK = <<~MAIN_CHECK 11 | if [ $(git symbolic-ref --short -q HEAD) != 'main' ]; 12 | then exit 1; 13 | fi 14 | MAIN_CHECK 15 | VERSION_TYPES = %w(major minor patch).freeze 16 | 17 | def system!(*args) 18 | system(*args) || abort("\n== Command #{args} failed ==") 19 | end 20 | 21 | abort("\n== Version Type incorrect ==") unless VERSION_TYPES.include?(ARGV[0]) 22 | 23 | abort("\n== Not on main") unless system(MAIN_CHECK) 24 | 25 | current_version = ActiveInsights::VERSION.split(".").map(&:to_i) 26 | 27 | case ARGV[0] 28 | when "major" 29 | current_version[0] += 1 30 | current_version[1] = 0 31 | current_version[2] = 0 32 | when "minor" 33 | current_version[1] += 1 34 | current_version[2] = 0 35 | when "patch" 36 | current_version[2] += 1 37 | end 38 | 39 | joined_version = current_version.join(".") 40 | 41 | FileUtils.chdir APP_ROOT do 42 | contents = <<~FILE 43 | # frozen_string_literal: true 44 | 45 | module ActiveInsights 46 | VERSION = "#{joined_version}" 47 | end 48 | FILE 49 | 50 | puts "== Updating version to #{joined_version} ==" 51 | File.write("lib/active_insights/version.rb", contents) 52 | 53 | system! "git add lib/active_insights/version.rb" 54 | 55 | puts "== Committing updated files ==" 56 | system! "git commit -m 'Version bump to #{joined_version}'" 57 | system! "git push" 58 | 59 | puts "== Publishing gem ==" 60 | system! "bundle exec rake release" 61 | end 62 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails gems 3 | # installed from the root of your application. 4 | 5 | ENGINE_ROOT = File.expand_path('..', __dir__) 6 | ENGINE_PATH = File.expand_path('../lib/active_insights/engine', __dir__) 7 | APP_PATH = File.expand_path('../test/dummy/config/application', __dir__) 8 | 9 | # Set up gems listed in the Gemfile. 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 11 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 12 | 13 | require 'rails/all' 14 | require 'rails/engine/commands' 15 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /bin/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $: << File.expand_path("../test", __dir__) 3 | 4 | require "bundler/setup" 5 | require "rails/plugin/test" 6 | -------------------------------------------------------------------------------- /config/initializers/importmap.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "active_insights" 4 | 5 | ActiveInsights::Engine.importmap.draw do 6 | pin "chartkick", to: "https://ga.jspm.io/npm:chartkick@5.0.1/dist/chartkick.esm.js" 7 | pin "chart.js", to: "https://ga.jspm.io/npm:chart.js@4.4.4/dist/chart.js" 8 | 9 | pin "chartjs-adapter-date-fns", to: "https://ga.jspm.io/npm:chartjs-adapter-date-fns@3.0.0/dist/chartjs-adapter-date-fns.esm.js" 10 | pin "date-fns", to: "https://ga.jspm.io/npm:date-fns@3.2.0/index.mjs" 11 | 12 | pin "chartjs-plugin-zoom", to: "https://ga.jspm.io/npm:chartjs-plugin-zoom@2.0.1/dist/chartjs-plugin-zoom.esm.js" 13 | pin "hammerjs", to: "https://ga.jspm.io/npm:hammerjs@2.0.8/hammer.js" 14 | pin "@kurkle/color", to: "https://ga.jspm.io/npm:@kurkle/color@0.3.2/dist/color.esm.js" 15 | pin "chart.js/helpers", to: "https://ga.jspm.io/npm:chart.js@4.4.4/helpers/helpers.js" 16 | end 17 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ActiveInsights::Engine.routes.draw do # rubocop:disable Metrics/BlockLength 4 | resources :requests, only: %i(index) 5 | resources :jobs, only: %i(index) 6 | get "/jobs/:date", to: "jobs#index" 7 | get "/requests/:date", to: "requests#index" 8 | 9 | get "/requests/rpm/redirection", to: "rpm#redirection", as: :rpm_redirection 10 | get "/requests/:date/rpm", to: "rpm#index", as: :rpm 11 | 12 | get "/requests/p_values/redirection", to: "requests_p_values#redirection", 13 | as: :requests_p_values_redirection 14 | get "/requests/:date/p_values", to: "requests_p_values#index", as: :requests_p_values 15 | get "/requests/:date/:formatted_controller/p_values", 16 | to: "requests_p_values#index", as: :controller_p_values 17 | get "/requests/:formatted_controller/p_values/redirection", 18 | to: "requests_p_values#redirection", 19 | as: :controller_p_values_redirection 20 | 21 | get "/jobs/jpm/redirection", to: "jpm#redirection", as: :jpm_redirection 22 | get "/jobs/:date/jpm", to: "jpm#index", as: :jpm 23 | 24 | get "/jobs/p_values/redirection", to: "jobs_p_values#redirection", 25 | as: :jobs_p_values_redirection 26 | get "/jobs/:date/p_values", to: "jobs_p_values#index", as: :jobs_p_values 27 | 28 | get "/jobs/:date/:job/p_values", to: "jobs_p_values#index", as: :job_p_values 29 | get "/jobs/:job/p_values/redirection", 30 | to: "jobs_p_values#redirection", 31 | as: :job_p_values_redirection 32 | 33 | get "/jobs/:date/latencies", to: "jobs_latencies#index", as: :jobs_latency 34 | get "/jobs/latencies/redirection", to: "jobs_latencies#redirection", 35 | as: :jobs_latency_redirection 36 | 37 | root "requests#index" 38 | end 39 | -------------------------------------------------------------------------------- /db/migrate/20240111225806_create_active_insights_tables.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateActiveInsightsTables < ActiveRecord::Migration[7.1] 4 | def change 5 | create_table :active_insights_requests, if_not_exists: true do |t| 6 | t.string :controller 7 | t.string :action 8 | t.string :format 9 | t.string :http_method 10 | t.text :path 11 | t.integer :status 12 | t.float :view_runtime 13 | t.float :db_runtime 14 | t.datetime :started_at 15 | t.datetime :finished_at 16 | t.string :uuid 17 | t.float :duration 18 | case connection.adapter_name 19 | when "Mysql2", "Mysql2Spatial", "Mysql2Rgeo", "Trilogy" 20 | t.virtual :formatted_controller, type: :string, as: "CONCAT(controller, '#', action)", stored: true 21 | else 22 | t.virtual :formatted_controller, type: :string, as: "controller || '#'|| action", stored: true 23 | end 24 | 25 | t.index :started_at 26 | t.index %i(started_at duration) 27 | t.index %i(started_at formatted_controller) 28 | 29 | t.timestamps 30 | end 31 | 32 | create_table :active_insights_jobs, if_not_exists: true do |t| 33 | t.string :job 34 | t.string :queue 35 | t.float :db_runtime 36 | t.datetime :scheduled_at 37 | t.datetime :started_at 38 | t.datetime :finished_at 39 | t.string :uuid 40 | t.float :duration 41 | t.float :queue_time 42 | 43 | t.index :started_at 44 | t.index %i(started_at duration) 45 | t.index %i(started_at duration queue_time) 46 | 47 | t.timestamps 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /db/migrate/20241015142450_add_ip_address_to_requests.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddIpAddressToRequests < ActiveRecord::Migration[7.1] 4 | def change 5 | add_column :active_insights_requests, :ip_address, :string 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20241016142157_add_user_agent_to_requests.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddUserAgentToRequests < ActiveRecord::Migration[7.2] 4 | def change 5 | add_column :active_insights_requests, :user_agent, :string 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/active_insights.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "importmap-rails" 4 | require "chartkick" 5 | 6 | require "active_insights/version" 7 | require "active_insights/engine" 8 | 9 | module ActiveInsights 10 | mattr_accessor :connects_to, :ignored_endpoints, :enabled 11 | 12 | class << self 13 | def ignored_endpoint?(payload) 14 | ignored_endpoints.to_a.include?( 15 | "#{payload[:controller]}##{payload[:action]}" 16 | ) 17 | end 18 | 19 | def enabled? 20 | if enabled.nil? 21 | !Rails.env.development? 22 | else 23 | enabled.present? 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/active_insights/engine.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveInsights 4 | class Engine < ::Rails::Engine 5 | isolate_namespace ActiveInsights 6 | 7 | def self.importmap 8 | @importmap ||= 9 | Importmap::Map.new.tap do |mapping| 10 | mapping.draw(Engine.root.join("config/importmap.rb")) 11 | end 12 | end 13 | 14 | config.active_insights = ActiveSupport::OrderedOptions.new 15 | 16 | initializer "active_insights.config" do 17 | config.active_insights.each do |name, value| 18 | ActiveInsights.public_send(:"#{name}=", value) 19 | end 20 | end 21 | 22 | initializer "active_insights.importmap", before: "importmap" do |app| 23 | app.config.importmap.paths << 24 | Engine.root.join("config/initializers/importmap.rb") 25 | end 26 | 27 | initializer "active_insights.job_subscriber" do 28 | ActiveSupport::Notifications. 29 | subscribe("perform.active_job") do |_name, 30 | started, finished, unique_id, payload| 31 | next unless ActiveInsights.enabled? 32 | 33 | ActiveInsights::Job.setup(started, finished, unique_id, payload) 34 | end 35 | end 36 | 37 | initializer "active_insights.request_subscriber" do 38 | ActiveSupport::Notifications. 39 | subscribe("process_action.action_controller") do |_name, 40 | started, finished, unique_id, payload| 41 | next if ActiveInsights.ignored_endpoint?(payload) || 42 | !ActiveInsights.enabled? 43 | 44 | Thread.new do 45 | Rails.application.executor.wrap do 46 | ActiveInsights::Request. 47 | setup(started, finished, unique_id, payload) 48 | end 49 | end 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/active_insights/seeders/jobs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # :nocov: 4 | require "rubystats" 5 | 6 | module ActiveInsights 7 | class JobSeeder 8 | def initialize(date, rpm, p50, p95, p99) 9 | @date = date 10 | @rpm = rpm 11 | @p50 = p50 12 | @p95 = p95 13 | @p99 = p99 14 | end 15 | 16 | def seed 17 | ActiveInsights::Job.insert_all(seed_attributes) 18 | end 19 | 20 | def find_percentile(sorted_data, percentile) 21 | sorted_data[(percentile * sorted_data.length).ceil - 1] 22 | end 23 | 24 | private 25 | 26 | attr_reader :date, :rpm, :p50, :p95, :p99 27 | 28 | def seed_attributes 29 | 24.times.flat_map do |hour| 30 | 60.times.flat_map do |min| 31 | processing_times.map { |duration| attributes(hour, min, duration) } 32 | end 33 | end 34 | end 35 | 36 | def processing_times 37 | Array.new(calculate_rpm) do 38 | p50 + (beta_distribution.rng * (p95 - p50)) 39 | end.select { |time| time <= p99 } 40 | end 41 | 42 | def calculate_rpm 43 | (rpm * 0.9) + (rpm * 0.2 * rand) 44 | end 45 | 46 | def beta_distribution 47 | @beta_distribution ||= Rubystats::BetaDistribution.new(2, 5) 48 | end 49 | 50 | def sample_job 51 | %w(UserNotificationSenderJob DailyReportGeneratorJob DataCleanupWorkerJob 52 | InvoiceProcessingJob EmailDigestSenderJob DatabaseBackupJob 53 | OrderFulfillmentJob ProductCatalogUpdateJob MonthlyBillingJob 54 | SubscriptionRenewalCheckerJob SocialMediaPostSchedulerJob 55 | CustomerDataImporterJob SearchIndexRebuilderJob EventReminderSenderJob 56 | FileExportJob).sample 57 | end 58 | 59 | def attributes(hour, min, duration) 60 | started_at = date.dup.to_time.change(hour:, min:) 61 | job = sample_job 62 | queue = sample_queue(job) 63 | 64 | default_attributes.merge(duration:, started_at:, job:, queue:, 65 | scheduled_at: started_at - sample_latency, 66 | finished_at: started_at + (duration / 1000.0)) 67 | end 68 | 69 | def default_attributes 70 | { created_at: Time.current, updated_at: Time.current, 71 | uuid: SecureRandom.uuid } 72 | end 73 | 74 | def sample_latency 75 | (1..500).to_a.sample.seconds 76 | end 77 | 78 | def sample_queue(job) 79 | @sample_queues ||= {} 80 | @sample_queues[job] ||= 81 | %w(default mailers high_priority low_priority).sample 82 | end 83 | end 84 | end 85 | # :nocov: 86 | -------------------------------------------------------------------------------- /lib/active_insights/seeders/requests.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # :nocov: 4 | require "rubystats" 5 | 6 | module ActiveInsights 7 | class RequestSeeder 8 | def initialize(date, rpm, p50, p95, p99) 9 | @date = date 10 | @rpm = rpm 11 | @p50 = p50 12 | @p95 = p95 13 | @p99 = p99 14 | end 15 | 16 | def seed 17 | ActiveInsights::Request.insert_all(seed_attributes) 18 | end 19 | 20 | def find_percentile(sorted_data, percentile) 21 | sorted_data[(percentile * sorted_data.length).ceil - 1] 22 | end 23 | 24 | private 25 | 26 | attr_reader :date, :rpm, :p50, :p95, :p99 27 | 28 | def seed_attributes 29 | 24.times.flat_map do |hour| 30 | 60.times.flat_map do |min| 31 | response_times.map { |duration| attributes(hour, min, duration) } 32 | end 33 | end 34 | end 35 | 36 | def response_times 37 | Array.new(calculate_rpm) do 38 | p50 + (beta_distribution.rng * (p95 - p50)) 39 | end.select { |time| time <= p99 } 40 | end 41 | 42 | def calculate_rpm 43 | (rpm * 0.9) + (rpm * 0.2 * rand) 44 | end 45 | 46 | def beta_distribution 47 | @beta_distribution ||= Rubystats::BetaDistribution.new(2, 5) 48 | end 49 | 50 | def sample_controller 51 | ["AppsController#show", "OrganizationsController#show", 52 | "AgentController#create", "AppComponentsController#index", 53 | "BadgesController#show", "ContactController#create", 54 | "AppsController#update"].sample.split("#") 55 | end 56 | 57 | def attributes(hour, min, duration) 58 | sample_controller.then do |controller, action| 59 | started_at = date.dup.to_time.change(hour:, min:) 60 | 61 | default_attributes.merge(controller:, action:, duration:, started_at:, 62 | finished_at: started_at + (duration / 1000.0)) 63 | end 64 | end 65 | 66 | def default_attributes 67 | { created_at: Time.current, updated_at: Time.current, format: :html, 68 | http_method: :get, uuid: SecureRandom.uuid } 69 | end 70 | end 71 | end 72 | # :nocov: 73 | -------------------------------------------------------------------------------- /lib/active_insights/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveInsights 4 | VERSION = "1.3.3" 5 | end 6 | -------------------------------------------------------------------------------- /lib/activeinsights.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "active_insights" 4 | -------------------------------------------------------------------------------- /lib/generators/active_insights/install/install_generator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ActiveInsights::InstallGenerator < Rails::Generators::Base 4 | def add_route 5 | route "mount ActiveInsights::Engine => \"/insights\"" 6 | end 7 | 8 | def create_migrations 9 | rails_command "active_insights:install:migrations", inline: true 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/generators/active_insights/update/update_generator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ActiveInsights::UpdateGenerator < Rails::Generators::Base 4 | def create_migrations 5 | rails_command "active_insights:install:migrations", inline: true 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/tasks/active_insights_tasks.rake: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | desc "Copy over the migrations and mount route for ActiveInsights" 4 | namespace :active_insights do 5 | task install: :environment do 6 | Rails::Command.invoke :generate, ["active_insights:install"] 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/active_insights_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class ActiveInsightsTest < ActiveSupport::TestCase 6 | test "it has a version number" do 7 | assert ActiveInsights::VERSION 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/controllers/active_insights/jobs_controller_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class ActiveInsights::JobsControllerTest < ActionDispatch::IntegrationTest 6 | test "#index" do 7 | get active_insights.jobs_path 8 | 9 | assert_response :success 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/controllers/active_insights/jobs_latencies_controller_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class ActiveInsights::JobsLatenciesControllerTest < 6 | ActionDispatch::IntegrationTest 7 | 8 | test "#index" do 9 | Time.use_zone("Eastern Time (US & Canada)") do 10 | get active_insights.jobs_latency_path(Date.current) 11 | 12 | assert_response :success 13 | end 14 | end 15 | 16 | test "#redirection" do 17 | get active_insights.jobs_latency_redirection_path, 18 | params: { date: Date.new(2025, 1, 4) } 19 | 20 | assert_redirected_to active_insights.jobs_latency_path(Date.new(2025, 1, 4)) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/controllers/active_insights/jobs_p_values_controller_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class ActiveInsights::JobsPValuesControllerTest < 6 | ActionDispatch::IntegrationTest 7 | test "#index" do 8 | Time.use_zone("Eastern Time (US & Canada)") do 9 | get active_insights.jobs_p_values_path(Date.current) 10 | 11 | assert_response :success 12 | end 13 | end 14 | 15 | test "#redirection" do 16 | get active_insights.jobs_p_values_redirection_path, 17 | params: { date: Date.new(2025, 1, 4) } 18 | 19 | assert_redirected_to active_insights.jobs_p_values_path(Date.new(2025, 20 | 1, 4)) 21 | end 22 | 23 | test "#index with job" do 24 | Time.use_zone("Eastern Time (US & Canada)") do 25 | get active_insights.job_p_values_path(Date.current, "DummyJob") 26 | 27 | assert_response :success 28 | end 29 | end 30 | 31 | test "#redirection with controller" do 32 | get active_insights.job_p_values_redirection_path("DummyJob"), params: { 33 | date: Date.new(2025, 1, 4) 34 | } 35 | 36 | assert_redirected_to active_insights.job_p_values_path( 37 | Date.new(2025, 1, 4), "DummyJob" 38 | ) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /test/controllers/active_insights/jpm_controller_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class ActiveInsights::JpmControllerTest < ActionDispatch::IntegrationTest 6 | test "#index" do 7 | Time.use_zone("Eastern Time (US & Canada)") do 8 | get active_insights.jpm_path(Date.current) 9 | 10 | assert_response :success 11 | end 12 | end 13 | 14 | test "#redirection" do 15 | get active_insights.jpm_redirection_path, 16 | params: { date: Date.new(2025, 1, 4) } 17 | 18 | assert_redirected_to active_insights.jpm_path(Date.new(2025, 1, 4)) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/controllers/active_insights/requests_controller_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class ActiveInsights::RequestsControllerTest < ActionDispatch::IntegrationTest 6 | test "#index" do 7 | get active_insights.requests_path 8 | 9 | assert_response :success 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/controllers/active_insights/requests_p_values_controller_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class ActiveInsights::RequestsPValuesControllerTest < 6 | ActionDispatch::IntegrationTest 7 | test "#index" do 8 | Time.use_zone("Eastern Time (US & Canada)") do 9 | get active_insights.requests_p_values_path(Date.current) 10 | 11 | assert_response :success 12 | end 13 | end 14 | 15 | test "#redirection" do 16 | get active_insights.requests_p_values_redirection_path, 17 | params: { date: Date.new(2025, 1, 4) } 18 | 19 | assert_redirected_to active_insights.requests_p_values_path(Date.new(2025, 20 | 1, 4)) 21 | end 22 | 23 | test "#index with controller" do 24 | Time.use_zone("Eastern Time (US & Canada)") do 25 | get active_insights.controller_p_values_path( 26 | Date.current, "ActiveInsights::ControllerPValuesController#index" 27 | ) 28 | 29 | assert_response :success 30 | end 31 | end 32 | 33 | test "#redirection with controller" do 34 | get active_insights.controller_p_values_redirection_path( 35 | "ActiveInsights::ControllerPValuesController#index" 36 | ), params: { date: Date.new(2025, 1, 4) } 37 | 38 | assert_redirected_to active_insights.controller_p_values_path( 39 | Date.new(2025, 1, 4), "ActiveInsights::ControllerPValuesController#index" 40 | ) 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /test/controllers/active_insights/rpm_controller_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class ActiveInsights::RpmControllerTest < ActionDispatch::IntegrationTest 6 | test "#index" do 7 | Time.use_zone("Eastern Time (US & Canada)") do 8 | get active_insights.rpm_path(Date.current) 9 | 10 | assert_response :success 11 | end 12 | end 13 | 14 | test "#redirection" do 15 | get active_insights.rpm_redirection_path, 16 | params: { date: Date.new(2025, 1, 4) } 17 | 18 | assert_redirected_to active_insights.rpm_path(Date.new(2025, 1, 4)) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/dummy/Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Add your own tasks in files placed in lib/tasks ending in .rake, 4 | # for example lib/tasks/capistrano.rake, and they will automatically be 5 | # available to Rake. 6 | 7 | require_relative "config/application" 8 | 9 | Rails.application.load_tasks 10 | -------------------------------------------------------------------------------- /test/dummy/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npezza93/activeinsights/7fea0352e68dc9c9ad2391a0629bb6d282873e01/test/dummy/app/assets/images/.keep -------------------------------------------------------------------------------- /test/dummy/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* Application styles */ 2 | -------------------------------------------------------------------------------- /test/dummy/app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ApplicationCable 4 | class Channel < ActionCable::Channel::Base 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/dummy/app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ApplicationCable 4 | class Connection < ActionCable::Connection::Base 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationController < ActionController::Base 4 | end 5 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npezza93/activeinsights/7fea0352e68dc9c9ad2391a0629bb6d282873e01/test/dummy/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /test/dummy/app/controllers/make_requests_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class MakeRequestsController < ApplicationController 4 | def index 5 | sleep (0..120).to_a.sample / 1000.0 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ApplicationHelper 4 | end 5 | -------------------------------------------------------------------------------- /test/dummy/app/javascript/application.js: -------------------------------------------------------------------------------- 1 | // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails 2 | -------------------------------------------------------------------------------- /test/dummy/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationJob < ActiveJob::Base 4 | # Automatically retry jobs that encountered a deadlock 5 | # retry_on ActiveRecord::Deadlocked 6 | 7 | # Most jobs are safe to ignore if the underlying records are no longer 8 | # available discard_on ActiveJob::DeserializationError 9 | end 10 | -------------------------------------------------------------------------------- /test/dummy/app/jobs/dummy_job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class DummyJob < ApplicationJob 4 | def perform 5 | sleep 1 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationMailer < ActionMailer::Base 4 | default from: "from@example.com" 5 | layout "mailer" 6 | end 7 | -------------------------------------------------------------------------------- /test/dummy/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationRecord < ActiveRecord::Base 4 | primary_abstract_class 5 | end 6 | -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npezza93/activeinsights/7fea0352e68dc9c9ad2391a0629bb6d282873e01/test/dummy/app/models/concerns/.keep -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dummy 5 | 6 | <%= csrf_meta_tags %> 7 | <%= csp_meta_tag %> 8 | 9 | <%= stylesheet_link_tag "application" %> 10 | <%= javascript_importmap_tags %> 11 | 12 | 13 | 14 | <%= yield %> 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/dummy/app/views/make_requests/index.html.erb: -------------------------------------------------------------------------------- 1 | <%= link_to "insights", active_insights.requests_path %> 2 | -------------------------------------------------------------------------------- /test/dummy/bin/importmap: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative "../config/application" 4 | require "importmap/commands" 5 | -------------------------------------------------------------------------------- /test/dummy/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path("../config/application", __dir__) 3 | require_relative "../config/boot" 4 | require "rails/commands" 5 | -------------------------------------------------------------------------------- /test/dummy/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative "../config/boot" 3 | require "rake" 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /test/dummy/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "fileutils" 3 | 4 | # path to your application root. 5 | APP_ROOT = File.expand_path("..", __dir__) 6 | 7 | def system!(*args) 8 | system(*args, exception: true) 9 | end 10 | 11 | FileUtils.chdir APP_ROOT do 12 | # This script is a way to set up or update your development environment automatically. 13 | # This script is idempotent, so that you can run it at any time and get an expectable outcome. 14 | # Add necessary setup steps to this file. 15 | 16 | puts "== Installing dependencies ==" 17 | system! "gem install bundler --conservative" 18 | system("bundle check") || system!("bundle install") 19 | 20 | # puts "\n== Copying sample files ==" 21 | # unless File.exist?("config/database.yml") 22 | # FileUtils.cp "config/database.yml.sample", "config/database.yml" 23 | # end 24 | 25 | puts "\n== Preparing database ==" 26 | system! "bin/rails db:prepare" 27 | 28 | puts "\n== Removing old logs and tempfiles ==" 29 | system! "bin/rails log:clear tmp:clear" 30 | 31 | puts "\n== Restarting application server ==" 32 | system! "bin/rails restart" 33 | end 34 | -------------------------------------------------------------------------------- /test/dummy/config.ru: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # This file is used by Rack-based servers to start the application. 4 | 5 | require_relative "config/environment" 6 | 7 | run Rails.application 8 | Rails.application.load_server 9 | -------------------------------------------------------------------------------- /test/dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative "boot" 2 | 3 | require "rails/all" 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module Dummy 10 | class Application < Rails::Application 11 | config.load_defaults Rails::VERSION::STRING.to_f 12 | 13 | # Please, add to the `ignore` list any other `lib` subdirectories that do 14 | # not contain `.rb` files, or that should not be reloaded or eager loaded. 15 | # Common ones are `templates`, `generators`, or `middleware`, for example. 16 | config.autoload_lib(ignore: %w(assets tasks)) 17 | 18 | # Configuration for the application, engines, and railties goes here. 19 | # 20 | # These settings can be overridden in specific environments using the files 21 | # in config/environments, which are processed later. 22 | # 23 | # config.time_zone = "Central Time (US & Canada)" 24 | # config.eager_load_paths << Rails.root.join("extras") 25 | config.active_job.queue_adapter = :solid_queue 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | # Set up gems listed in the Gemfile. 2 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile", __dir__) 3 | 4 | require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"]) 5 | $LOAD_PATH.unshift File.expand_path("../../../lib", __dir__) 6 | -------------------------------------------------------------------------------- /test/dummy/config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: test 6 | 7 | production: 8 | adapter: redis 9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 10 | channel_prefix: dummy_production 11 | -------------------------------------------------------------------------------- /test/dummy/config/database.pg.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | adapter: postgresql 3 | encoding: unicode 4 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 5 | timeout: 5000 6 | password: <%= ENV.fetch("POSTGRES_PASSWORD") {} %> 7 | username: <%= ENV.fetch("POSTGRES_USER") {} %> 8 | port: <%= ENV.fetch("POSTGRES_PORT") { 5432 } %> 9 | host: <%= ENV.fetch("POSTGRES_HOST") { "localhost" } %> 10 | 11 | development: 12 | <<: *default 13 | database: dev 14 | 15 | test: 16 | <<: *default 17 | database: test 18 | 19 | production: 20 | <<: *default 21 | database: production 22 | -------------------------------------------------------------------------------- /test/dummy/config/database.trilogy.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | adapter: trilogy 3 | encoding: utf8mb4 4 | host: '127.0.0.1' 5 | username: root 6 | password: 7 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 8 | reconnect: true 9 | variables: 10 | sql_mode: TRADITIONAL 11 | 12 | development: 13 | <<: *default 14 | database: development 15 | 16 | test: 17 | <<: *default 18 | database: test 19 | 20 | production: 21 | <<: *default 22 | database: production 23 | -------------------------------------------------------------------------------- /test/dummy/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite. Versions 3.8.0 and up are supported. 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem "sqlite3" 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: storage/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: storage/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: storage/production.sqlite3 26 | -------------------------------------------------------------------------------- /test/dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative "application" 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /test/dummy/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # In the development environment your application's code is reloaded any time 7 | # it changes. This slows down response time but is perfect for development 8 | # since you don't have to restart the web server when you make code changes. 9 | config.enable_reloading = true 10 | 11 | # Do not eager load code on boot. 12 | config.eager_load = false 13 | 14 | # Show full error reports. 15 | config.consider_all_requests_local = true 16 | 17 | # Enable server timing 18 | config.server_timing = true 19 | 20 | # Enable/disable caching. By default caching is disabled. 21 | # Run rails dev:cache to toggle caching. 22 | if Rails.root.join("tmp/caching-dev.txt").exist? 23 | config.action_controller.perform_caching = true 24 | config.action_controller.enable_fragment_cache_logging = true 25 | 26 | config.cache_store = :memory_store 27 | config.public_file_server.headers = { 28 | "Cache-Control" => "public, max-age=#{2.days.to_i}" 29 | } 30 | else 31 | config.action_controller.perform_caching = false 32 | 33 | config.cache_store = :null_store 34 | end 35 | 36 | # Store uploaded files on the local file system (see config/storage.yml for options). 37 | config.active_storage.service = :local 38 | 39 | # Don't care if the mailer can't send. 40 | config.action_mailer.raise_delivery_errors = false 41 | 42 | config.action_mailer.perform_caching = false 43 | 44 | # Print deprecation notices to the Rails logger. 45 | config.active_support.deprecation = :log 46 | 47 | # Raise exceptions for disallowed deprecations. 48 | config.active_support.disallowed_deprecation = :raise 49 | 50 | # Tell Active Support which deprecation messages to disallow. 51 | config.active_support.disallowed_deprecation_warnings = [] 52 | 53 | # Raise an error on page load if there are pending migrations. 54 | config.active_record.migration_error = :page_load 55 | 56 | # Highlight code that triggered database queries in logs. 57 | config.active_record.verbose_query_logs = true 58 | 59 | # Highlight code that enqueued background job in logs. 60 | config.active_job.verbose_enqueue_logs = true 61 | 62 | 63 | # Raises error for missing translations. 64 | # config.i18n.raise_on_missing_translations = true 65 | 66 | # Annotate rendered view with file names. 67 | # config.action_view.annotate_rendered_view_with_filenames = true 68 | 69 | # Uncomment if you wish to allow Action Cable access from any origin. 70 | # config.action_cable.disable_request_forgery_protection = true 71 | 72 | # Raise error when a before_action's only/except options reference missing actions 73 | config.action_controller.raise_on_missing_callback_actions = true 74 | end 75 | -------------------------------------------------------------------------------- /test/dummy/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # Code is not reloaded between requests. 7 | config.enable_reloading = false 8 | 9 | # Eager load code on boot. This eager loads most of Rails and 10 | # your application in memory, allowing both threaded web servers 11 | # and those relying on copy on write to perform better. 12 | # Rake tasks automatically ignore this option for performance. 13 | config.eager_load = true 14 | 15 | # Full error reports are disabled and caching is turned on. 16 | config.consider_all_requests_local = false 17 | config.action_controller.perform_caching = true 18 | 19 | # Ensures that a master key has been made available in ENV["RAILS_MASTER_KEY"], config/master.key, or an environment 20 | # key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files). 21 | # config.require_master_key = true 22 | 23 | # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead. 24 | # config.public_file_server.enabled = false 25 | 26 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 27 | # config.asset_host = "http://assets.example.com" 28 | 29 | # Specifies the header that your server uses for sending files. 30 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache 31 | # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX 32 | 33 | # Store uploaded files on the local file system (see config/storage.yml for options). 34 | config.active_storage.service = :local 35 | 36 | # Mount Action Cable outside main process or domain. 37 | # config.action_cable.mount_path = nil 38 | # config.action_cable.url = "wss://example.com/cable" 39 | # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ] 40 | 41 | # Assume all access to the app is happening through a SSL-terminating reverse proxy. 42 | # Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies. 43 | # config.assume_ssl = true 44 | 45 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 46 | config.force_ssl = true 47 | 48 | # Log to STDOUT by default 49 | config.logger = ActiveSupport::Logger.new(STDOUT) 50 | .tap { |logger| logger.formatter = ::Logger::Formatter.new } 51 | .then { |logger| ActiveSupport::TaggedLogging.new(logger) } 52 | 53 | # Prepend all log lines with the following tags. 54 | config.log_tags = [ :request_id ] 55 | 56 | # Info include generic and useful information about system operation, but avoids logging too much 57 | # information to avoid inadvertent exposure of personally identifiable information (PII). If you 58 | # want to log everything, set the level to "debug". 59 | config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") 60 | 61 | # Use a different cache store in production. 62 | # config.cache_store = :mem_cache_store 63 | 64 | # Use a real queuing backend for Active Job (and separate queues per environment). 65 | config.active_job.queue_adapter = :solid_queue 66 | # config.active_job.queue_name_prefix = "dummy_production" 67 | 68 | config.action_mailer.perform_caching = false 69 | 70 | # Ignore bad email addresses and do not raise email delivery errors. 71 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 72 | # config.action_mailer.raise_delivery_errors = false 73 | 74 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 75 | # the I18n.default_locale when a translation cannot be found). 76 | config.i18n.fallbacks = true 77 | 78 | # Don't log any deprecations. 79 | config.active_support.report_deprecations = false 80 | 81 | # Do not dump schema after migrations. 82 | config.active_record.dump_schema_after_migration = false 83 | 84 | # Enable DNS rebinding protection and other `Host` header attacks. 85 | # config.hosts = [ 86 | # "example.com", # Allow requests from example.com 87 | # /.*\.example\.com/ # Allow requests from subdomains like `www.example.com` 88 | # ] 89 | # Skip DNS rebinding protection for the default health check endpoint. 90 | # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } 91 | end 92 | -------------------------------------------------------------------------------- /test/dummy/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | # The test environment is used exclusively to run your application's 4 | # test suite. You never need to work with it otherwise. Remember that 5 | # your test database is "scratch space" for the test suite and is wiped 6 | # and recreated between test runs. Don't rely on the data there! 7 | 8 | Rails.application.configure do 9 | # Settings specified here will take precedence over those in config/application.rb. 10 | 11 | # While tests run files are not watched, reloading is not necessary. 12 | config.enable_reloading = false 13 | 14 | # Eager loading loads your entire application. When running a single test locally, 15 | # this is usually not necessary, and can slow down your test suite. However, it's 16 | # recommended that you enable it in continuous integration systems to ensure eager 17 | # loading is working properly before deploying your code. 18 | config.eager_load = ENV["CI"].present? 19 | 20 | # Configure public file server for tests with Cache-Control for performance. 21 | config.public_file_server.enabled = true 22 | config.public_file_server.headers = { 23 | "Cache-Control" => "public, max-age=#{1.hour.to_i}" 24 | } 25 | 26 | # Show full error reports and disable caching. 27 | config.consider_all_requests_local = true 28 | config.action_controller.perform_caching = false 29 | config.cache_store = :null_store 30 | 31 | # Render exception templates for rescuable exceptions and raise for other exceptions. 32 | config.action_dispatch.show_exceptions = :rescuable 33 | 34 | # Disable request forgery protection in test environment. 35 | config.action_controller.allow_forgery_protection = false 36 | 37 | # Store uploaded files on the local file system in a temporary directory. 38 | config.active_storage.service = :test 39 | 40 | config.action_mailer.perform_caching = false 41 | 42 | # Tell Action Mailer not to deliver emails to the real world. 43 | # The :test delivery method accumulates sent emails in the 44 | # ActionMailer::Base.deliveries array. 45 | config.action_mailer.delivery_method = :test 46 | 47 | # Print deprecation notices to the stderr. 48 | config.active_support.deprecation = :stderr 49 | 50 | # Raise exceptions for disallowed deprecations. 51 | config.active_support.disallowed_deprecation = :raise 52 | 53 | # Tell Active Support which deprecation messages to disallow. 54 | config.active_support.disallowed_deprecation_warnings = [] 55 | 56 | # Raises error for missing translations. 57 | # config.i18n.raise_on_missing_translations = true 58 | 59 | # Annotate rendered view with file names. 60 | # config.action_view.annotate_rendered_view_with_filenames = true 61 | 62 | # Raise error when a before_action's only/except options reference missing actions 63 | config.action_controller.raise_on_missing_callback_actions = true 64 | end 65 | -------------------------------------------------------------------------------- /test/dummy/config/importmap.rb: -------------------------------------------------------------------------------- 1 | # Pin npm packages by running ./bin/importmap 2 | 3 | pin "application" 4 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide content security policy. 4 | # See the Securing Rails Applications Guide for more information: 5 | # https://guides.rubyonrails.org/security.html#content-security-policy-header 6 | 7 | # Rails.application.configure do 8 | # config.content_security_policy do |policy| 9 | # policy.default_src :self, :https 10 | # policy.font_src :self, :https, :data 11 | # policy.img_src :self, :https, :data 12 | # policy.object_src :none 13 | # policy.script_src :self, :https 14 | # policy.style_src :self, :https 15 | # # Specify URI for violation reports 16 | # # policy.report_uri "/csp-violation-report-endpoint" 17 | # end 18 | # 19 | # # Generate session nonces for permitted importmap, inline scripts, and inline styles. 20 | # config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } 21 | # config.content_security_policy_nonce_directives = %w(script-src style-src) 22 | # 23 | # # Report violations without enforcing the policy. 24 | # # config.content_security_policy_report_only = true 25 | # end 26 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. 4 | # Use this to limit dissemination of sensitive information. 5 | # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. 6 | Rails.application.config.filter_parameters += [ 7 | :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn 8 | ] 9 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, "\\1en" 8 | # inflect.singular /^(ox)en/i, "\\1" 9 | # inflect.irregular "person", "people" 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym "RESTful" 16 | # end 17 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/permissions_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide HTTP permissions policy. For further 4 | # information see: https://developers.google.com/web/updates/2018/06/feature-policy 5 | 6 | # Rails.application.config.permissions_policy do |policy| 7 | # policy.camera :none 8 | # policy.gyroscope :none 9 | # policy.microphone :none 10 | # policy.usb :none 11 | # policy.fullscreen :self 12 | # policy.payment :self, "https://secure.example.com" 13 | # end 14 | -------------------------------------------------------------------------------- /test/dummy/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization and 2 | # are automatically loaded by Rails. If you want to use locales other than 3 | # English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t "hello" 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t("hello") %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more about the API, please read the Rails Internationalization guide 20 | # at https://guides.rubyonrails.org/i18n.html. 21 | # 22 | # Be aware that YAML interprets the following case-insensitive strings as 23 | # booleans: `true`, `false`, `on`, `off`, `yes`, `no`. Therefore, these strings 24 | # must be quoted to be interpreted as strings. For example: 25 | # 26 | # en: 27 | # "yes": yup 28 | # enabled: "ON" 29 | 30 | en: 31 | hello: "Hello world" 32 | -------------------------------------------------------------------------------- /test/dummy/config/puma.rb: -------------------------------------------------------------------------------- 1 | # This configuration file will be evaluated by Puma. The top-level methods that 2 | # are invoked here are part of Puma's configuration DSL. For more information 3 | # about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. 4 | 5 | # Puma can serve each request in a thread from an internal thread pool. 6 | # The `threads` method setting takes two numbers: a minimum and maximum. 7 | # Any libraries that use thread pools should be configured to match 8 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 9 | # and maximum; this matches the default thread size of Active Record. 10 | max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 11 | min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } 12 | threads min_threads_count, max_threads_count 13 | 14 | # Specifies that the worker count should equal the number of processors in production. 15 | if ENV["RAILS_ENV"] == "production" 16 | require "concurrent-ruby" 17 | worker_count = Integer(ENV.fetch("WEB_CONCURRENCY") { Concurrent.physical_processor_count }) 18 | workers worker_count if worker_count > 1 19 | end 20 | 21 | # Specifies the `worker_timeout` threshold that Puma will use to wait before 22 | # terminating a worker in development environments. 23 | worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development" 24 | 25 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 26 | port ENV.fetch("PORT") { 3000 } 27 | 28 | # Specifies the `environment` that Puma will run in. 29 | environment ENV.fetch("RAILS_ENV") { "development" } 30 | 31 | # Specifies the `pidfile` that Puma will use. 32 | pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } 33 | 34 | # Allow puma to be restarted by `bin/rails restart` command. 35 | plugin :tmp_restart 36 | -------------------------------------------------------------------------------- /test/dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | mount ActiveInsights::Engine => "/insights" 3 | resources :make_requests, only: :index 4 | end 5 | -------------------------------------------------------------------------------- /test/dummy/config/solid_queue.yml: -------------------------------------------------------------------------------- 1 | #default: &default 2 | # dispatchers: 3 | # - polling_interval: 1 4 | # batch_size: 500 5 | # workers: 6 | # - queues: "*" 7 | # threads: 5 8 | # processes: 1 9 | # polling_interval: 0.1 10 | # 11 | # development: 12 | # <<: *default 13 | # 14 | # test: 15 | # <<: *default 16 | # 17 | # production: 18 | # <<: *default 19 | -------------------------------------------------------------------------------- /test/dummy/config/storage.yml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | # Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) 10 | # amazon: 11 | # service: S3 12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> 13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> 14 | # region: us-east-1 15 | # bucket: your_own_bucket-<%= Rails.env %> 16 | 17 | # Remember not to checkin your GCS keyfile to a repository 18 | # google: 19 | # service: GCS 20 | # project: your_project 21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> 22 | # bucket: your_own_bucket-<%= Rails.env %> 23 | 24 | # Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) 25 | # microsoft: 26 | # service: AzureStorage 27 | # storage_account_name: your_account_name 28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> 29 | # container: your_container_name-<%= Rails.env %> 30 | 31 | # mirror: 32 | # service: Mirror 33 | # primary: local 34 | # mirrors: [ amazon, google, microsoft ] 35 | -------------------------------------------------------------------------------- /test/dummy/db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # This file is the source Rails uses to define your schema when running `bin/rails 6 | # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to 7 | # be faster and is potentially less error prone than running all of your 8 | # migrations from scratch. Old migrations may fail to apply correctly if those 9 | # migrations use external dependencies or application code. 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema[7.2].define(version: 2024_10_16_142157) do 14 | create_table "active_insights_jobs", force: :cascade do |t| 15 | t.string "job" 16 | t.string "queue" 17 | t.float "db_runtime" 18 | t.datetime "scheduled_at" 19 | t.datetime "started_at" 20 | t.datetime "finished_at" 21 | t.string "uuid" 22 | t.float "duration" 23 | t.float "queue_time" 24 | t.datetime "created_at", null: false 25 | t.datetime "updated_at", null: false 26 | t.index ["started_at", "duration", "queue_time"], name: "idx_on_started_at_duration_queue_time_010695b74f" 27 | t.index ["started_at", "duration"], name: "index_active_insights_jobs_on_started_at_and_duration" 28 | t.index ["started_at"], name: "index_active_insights_jobs_on_started_at" 29 | end 30 | 31 | create_table "active_insights_requests", force: :cascade do |t| 32 | t.string "controller" 33 | t.string "action" 34 | t.string "format" 35 | t.string "http_method" 36 | t.text "path" 37 | t.integer "status" 38 | t.float "view_runtime" 39 | t.float "db_runtime" 40 | t.datetime "started_at" 41 | t.datetime "finished_at" 42 | t.string "uuid" 43 | t.float "duration" 44 | t.virtual "formatted_controller", type: :string, as: "controller || '#'|| action", stored: true 45 | t.datetime "created_at", null: false 46 | t.datetime "updated_at", null: false 47 | t.string "ip_address" 48 | t.string "user_agent" 49 | t.index ["started_at", "duration"], name: "index_active_insights_requests_on_started_at_and_duration" 50 | t.index ["started_at", "formatted_controller"], name: "idx_on_started_at_formatted_controller_5d659a01d9" 51 | t.index ["started_at"], name: "index_active_insights_requests_on_started_at" 52 | end 53 | 54 | create_table "solid_queue_blocked_executions", force: :cascade do |t| 55 | t.integer "job_id", null: false 56 | t.string "queue_name", null: false 57 | t.integer "priority", default: 0, null: false 58 | t.string "concurrency_key", null: false 59 | t.datetime "expires_at", null: false 60 | t.datetime "created_at", null: false 61 | t.index ["expires_at", "concurrency_key"], name: "index_solid_queue_blocked_executions_for_maintenance" 62 | t.index ["job_id"], name: "index_solid_queue_blocked_executions_on_job_id", unique: true 63 | end 64 | 65 | create_table "solid_queue_claimed_executions", force: :cascade do |t| 66 | t.integer "job_id", null: false 67 | t.bigint "process_id" 68 | t.datetime "created_at", null: false 69 | t.index ["job_id"], name: "index_solid_queue_claimed_executions_on_job_id", unique: true 70 | t.index ["process_id", "job_id"], name: "index_solid_queue_claimed_executions_on_process_id_and_job_id" 71 | end 72 | 73 | create_table "solid_queue_failed_executions", force: :cascade do |t| 74 | t.integer "job_id", null: false 75 | t.text "error" 76 | t.datetime "created_at", null: false 77 | t.index ["job_id"], name: "index_solid_queue_failed_executions_on_job_id", unique: true 78 | end 79 | 80 | create_table "solid_queue_jobs", force: :cascade do |t| 81 | t.string "queue_name", null: false 82 | t.string "class_name", null: false 83 | t.text "arguments" 84 | t.integer "priority", default: 0, null: false 85 | t.string "active_job_id" 86 | t.datetime "scheduled_at" 87 | t.datetime "finished_at" 88 | t.string "concurrency_key" 89 | t.datetime "created_at", null: false 90 | t.datetime "updated_at", null: false 91 | t.index ["active_job_id"], name: "index_solid_queue_jobs_on_active_job_id" 92 | t.index ["class_name"], name: "index_solid_queue_jobs_on_class_name" 93 | t.index ["finished_at"], name: "index_solid_queue_jobs_on_finished_at" 94 | t.index ["queue_name", "finished_at"], name: "index_solid_queue_jobs_for_filtering" 95 | t.index ["scheduled_at", "finished_at"], name: "index_solid_queue_jobs_for_alerting" 96 | end 97 | 98 | create_table "solid_queue_pauses", force: :cascade do |t| 99 | t.string "queue_name", null: false 100 | t.datetime "created_at", null: false 101 | t.index ["queue_name"], name: "index_solid_queue_pauses_on_queue_name", unique: true 102 | end 103 | 104 | create_table "solid_queue_processes", force: :cascade do |t| 105 | t.string "kind", null: false 106 | t.datetime "last_heartbeat_at", null: false 107 | t.bigint "supervisor_id" 108 | t.integer "pid", null: false 109 | t.string "hostname" 110 | t.text "metadata" 111 | t.datetime "created_at", null: false 112 | t.index ["last_heartbeat_at"], name: "index_solid_queue_processes_on_last_heartbeat_at" 113 | t.index ["supervisor_id"], name: "index_solid_queue_processes_on_supervisor_id" 114 | end 115 | 116 | create_table "solid_queue_ready_executions", force: :cascade do |t| 117 | t.integer "job_id", null: false 118 | t.string "queue_name", null: false 119 | t.integer "priority", default: 0, null: false 120 | t.datetime "created_at", null: false 121 | t.index ["job_id"], name: "index_solid_queue_ready_executions_on_job_id", unique: true 122 | t.index ["priority", "job_id"], name: "index_solid_queue_poll_all" 123 | t.index ["queue_name", "priority", "job_id"], name: "index_solid_queue_poll_by_queue" 124 | end 125 | 126 | create_table "solid_queue_scheduled_executions", force: :cascade do |t| 127 | t.integer "job_id", null: false 128 | t.string "queue_name", null: false 129 | t.integer "priority", default: 0, null: false 130 | t.datetime "scheduled_at", null: false 131 | t.datetime "created_at", null: false 132 | t.index ["job_id"], name: "index_solid_queue_scheduled_executions_on_job_id", unique: true 133 | t.index ["scheduled_at", "priority", "job_id"], name: "index_solid_queue_dispatch_all" 134 | end 135 | 136 | create_table "solid_queue_semaphores", force: :cascade do |t| 137 | t.string "key", null: false 138 | t.integer "value", default: 1, null: false 139 | t.datetime "expires_at", null: false 140 | t.datetime "created_at", null: false 141 | t.datetime "updated_at", null: false 142 | t.index ["expires_at"], name: "index_solid_queue_semaphores_on_expires_at" 143 | t.index ["key", "value"], name: "index_solid_queue_semaphores_on_key_and_value" 144 | t.index ["key"], name: "index_solid_queue_semaphores_on_key", unique: true 145 | end 146 | end 147 | -------------------------------------------------------------------------------- /test/dummy/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npezza93/activeinsights/7fea0352e68dc9c9ad2391a0629bb6d282873e01/test/dummy/log/.keep -------------------------------------------------------------------------------- /test/dummy/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /test/dummy/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /test/dummy/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /test/dummy/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npezza93/activeinsights/7fea0352e68dc9c9ad2391a0629bb6d282873e01/test/dummy/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /test/dummy/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npezza93/activeinsights/7fea0352e68dc9c9ad2391a0629bb6d282873e01/test/dummy/public/apple-touch-icon.png -------------------------------------------------------------------------------- /test/dummy/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npezza93/activeinsights/7fea0352e68dc9c9ad2391a0629bb6d282873e01/test/dummy/public/favicon.ico -------------------------------------------------------------------------------- /test/fixtures/active_insights/jobs.yml: -------------------------------------------------------------------------------- 1 | one: 2 | job: DummyJob 3 | db_runtime: 75.225 4 | scheduled_at: <%= Date.current.to_time.change(hour: 9) %> 5 | started_at: <%= Date.current.to_time.change(hour: 10) %> 6 | finished_at: <%= Date.current.to_time.change(hour: 10, min: 10) %> 7 | uuid: 66218413e477a26ff1d8 8 | duration: 153.681 9 | queue: default 10 | queue_time: 100.011 11 | -------------------------------------------------------------------------------- /test/fixtures/active_insights/requests.yml: -------------------------------------------------------------------------------- 1 | one: 2 | controller: ActiveInsights::ControllerPValuesController 3 | action: index 4 | format: html 5 | http_method: GET 6 | path: 7 | status: 200 8 | view_runtime: 6.097999983467162 9 | db_runtime: 75.22500003688037 10 | started_at: <%= Date.current.to_time.change(hour: 10) %> 11 | finished_at: <%= Date.current.to_time.change(hour: 10, min: 10) %> 12 | uuid: 66218413e477a26ff1d8 13 | duration: 153.681 14 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Configure Rails Environment 4 | ENV["RAILS_ENV"] = "test" 5 | 6 | if ENV["COV"] 7 | require "simplecov" 8 | SimpleCov.start do 9 | enable_coverage :branch 10 | track_files "{app,lib}/**/*.rb" 11 | add_filter "/test/" 12 | add_group "Controllers", "app/controllers" 13 | add_group "Models", "app/models" 14 | add_group "Lib", "lib" 15 | end 16 | end 17 | 18 | require_relative "../test/dummy/config/environment" 19 | ActiveRecord::Migrator.migrations_paths = 20 | [File.expand_path("../test/dummy/db/migrate", __dir__)] 21 | ActiveRecord::Migrator.migrations_paths << 22 | File.expand_path("../db/migrate", __dir__) 23 | require "rails/test_help" 24 | Rails.application.eager_load! 25 | 26 | Minitest.backtrace_filter = Minitest::BacktraceFilter.new 27 | 28 | # Load fixtures from the engine 29 | if ActiveSupport::TestCase.respond_to?(:fixture_path=) 30 | ActiveSupport::TestCase.fixture_paths = 31 | [File.expand_path("fixtures", __dir__)] 32 | ActionDispatch::IntegrationTest.fixture_paths = 33 | ActiveSupport::TestCase.fixture_paths 34 | ActiveSupport::TestCase.file_fixture_path = 35 | "#{ActiveSupport::TestCase.fixture_paths.first}/files" 36 | ActiveSupport::TestCase.fixtures :all 37 | end 38 | --------------------------------------------------------------------------------