{"id":13415407,"url":"https://github.com/julianrubisch/attractor","last_synced_at":"2025-05-16T05:05:47.564Z","repository":{"id":35094935,"uuid":"203579939","full_name":"julianrubisch/attractor","owner":"julianrubisch","description":"code complexity metrics visualization and exploration tool for ruby and javascript","archived":false,"fork":false,"pushed_at":"2024-01-27T13:08:56.000Z","size":8537,"stargazers_count":383,"open_issues_count":13,"forks_count":8,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-12T12:44:46.919Z","etag":null,"topics":["churn","code-quality","complexity","es6","javascript","ruby"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/julianrubisch.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null},"funding":{"github":["julianrubisch"],"patreon":"user?u=24747270","open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2019-08-21T12:31:07.000Z","updated_at":"2025-04-28T15:10:16.000Z","dependencies_parsed_at":"2024-01-27T14:38:14.846Z","dependency_job_id":null,"html_url":"https://github.com/julianrubisch/attractor","commit_stats":{"total_commits":271,"total_committers":10,"mean_commits":27.1,"dds":"0.12177121771217714","last_synced_commit":"d3ed197bf6c349cef0528aa499edb5da1c04c6ac"},"previous_names":[],"tags_count":36,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julianrubisch%2Fattractor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julianrubisch%2Fattractor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julianrubisch%2Fattractor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julianrubisch%2Fattractor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/julianrubisch","download_url":"https://codeload.github.com/julianrubisch/attractor/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254471061,"owners_count":22076585,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["churn","code-quality","complexity","es6","javascript","ruby"],"created_at":"2024-07-30T21:00:48.310Z","updated_at":"2025-05-16T05:05:42.554Z","avatar_url":"https://github.com/julianrubisch.png","language":"JavaScript","funding_links":["https://github.com/sponsors/julianrubisch","https://patreon.com/user?u=24747270","https://www.patreon.com/user?u=24747270"],"categories":["JavaScript"],"sub_categories":[],"readme":"\u003c!-- MARKDOWN LINKS \u0026 IMAGES --\u003e\n\u003c!-- Shields --\u003e\n[forks-shield]: https://img.shields.io/github/forks/julianrubisch/attractor.svg?style=flat-square\n[forks-url]: https://github.com/julianrubisch/attractor/network/members\n[stars-shield]: https://img.shields.io/github/stars/julianrubisch/attractor.svg?style=flat-square\n[stars-url]: https://github.com/julianrubisch/attractor/stargazers\n[issues-shield]: https://img.shields.io/github/issues/julianrubisch/attractor.svg?style=flat-square\n[issues-url]: https://github.com/julianrubisch/attractor/issues\n[license-shield]: https://img.shields.io/github/license/julianrubisch/attractor.svg?style=flat-square\n[license-url]: https://github.com/julianrubisch/attractor/blob/master/LICENSE\n[twitter-url]: https://twitter.com/AttractorGem\n[twitter-shield]: https://img.shields.io/twitter/follow/AttractorGem?style=social\n[twitter-shield]: https://img.shields.io/twitter/follow/AttractorGem?style=social\n[ruby-tests-action-shield]: https://github.com/julianrubisch/attractor/workflows/Ruby%20Tests/badge.svg\n\u003c!-- Media --\u003e\n[demo-gif]: https://user-images.githubusercontent.com/4352208/67033292-b41e4280-f115-11e9-8c91-81b3bea4451c.gif\n[logo-source]: https://thenounproject.com/term/black-hole/1043893\n\u003c!-- References --\u003e\n[medium-article]: https://medium.com/better-programming/why-i-made-my-own-code-quality-tool-c44b40ceaafd\n[sandi-metz-article]: https://www.sandimetz.com/blog/2017/9/13/breaking-up-the-behemoth\n[michael-feathers-article]: https://www.agileconnection.com/article/getting-empirical-about-refactoring\n[bundler]: https://bundler.io\n[rack-livereload]: https://github.com/johnbintz/rack-livereload\n[guard-livereload]: https://github.com/guard/guard-livereload\n[attractor-action]: https://github.com/julianrubisch/attractor-action\n[attractor-action-marketplace]: https://github.com/marketplace/actions/attractor-action\n[repo]: https://github.com/julianrubisch/attractor\n\n\u003c!-- PROJECT LOGO --\u003e\n\u003cbr /\u003e\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://github.com/julianrubisch/attractor\"\u003e\n    \u003cimg src=\"https://user-images.githubusercontent.com/4352208/65411858-3dc84200-ddee-11e9-99b6-c9cdbeb533c5.png\" alt=\"Logo\" width=\"80\" height=\"80\"\u003e\n  \u003c/a\u003e\n  \u003ch2 align=\"center\"\u003eAttractor\u003c/h2\u003e\n  \u003cp align=\"center\"\u003eA code complexity metrics visualization and exploration tool for Ruby and JavaScript\u003c/p\u003e\n\n---\n\n\u003c!-- PROJECT SHIELDS --\u003e\n![Ruby Tests Action Status][ruby-tests-action-shield]\n[![Forks][forks-shield]][forks-url]\n[![Stargazers][stars-shield]][stars-url]\n[![Issues][issues-shield]][issues-url]\n[![MIT License][license-shield]][license-url]\n[![Twitter follow][twitter-shield]][twitter-url]\n\u003c!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section --\u003e\n[![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors-)\n\u003c!-- ALL-CONTRIBUTORS-BADGE:END --\u003e\n\n  \u003ca href=\"https://www.patreon.com/user?u=24747270\"\u003e\u003cimg src=\"https://c5.patreon.com/external/logo/become_a_patron_button@2x.png\" alt=\"Become a Patron!\" width=\"160\" /\u003e\u003c/a\u003e\n\u003c/div\u003e\n\n\u003c!-- GIF --\u003e\n![attractor_v0 6 1][demo-gif]\n\n**If you are looking for a managed solution to observe your code's quality, check out [UseAttractor](https://useattr.actor)**\n\n## Table of Contents\n\n- [Table of Contents](#table-of-contents)\n- [Introduction](#introduction)\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Live Reloading](#live-reloading)\n- [CI Usage](#ci-usage)\n  - [Github Action](#github-action)\n  - [Gitlab Example](#gitlab-example)\n- [CLI Commands and Options](#cli-commands-and-options)\n- [Development](#development)\n- [Contributing](#contributing)\n- [Logo Attribution](#logo-attribution)\n- [Contributors ✨](#contributors-%e2%9c%a8)\n\n## Introduction\n\nMany authors ([Michael Feathers][michael-feathers-article], [Sandi Metz][sandi-metz-article]) have shown that an evaluation of churn vs complexity of files in software projects provide a valuable metric towards code quality. This is another take on the matter, for ruby code, using the `churn` and `flog` projects.\n\nHere's an [article on medium][medium-article] explaining the approach in greater detail.\n\n## Installation\n\nAttractor's installation is standard for a Ruby gem:\n\n```sh\ngem install attractor\n```\n\nYou'll also want to install some plugins to go along with the main gem:\n\n```sh\ngem install attractor-ruby # https://github.com/julianrubisch/attractor-ruby\ngem install attractor-javascript # https://github.com/julianrubisch/attractor-javascript\n```\n\nYou will most likely want to install Attractor using [Bundler][bundler]:\n\n```ruby\ngem 'attractor'\ngem 'attractor-ruby'\ngem 'attractor-javascript'\n```\n\nAnd then execute:\n\n```sh\nbundle\n```\n\n## Usage\n\nTo create a HTML report in `attractor_output/index.html`:\n\n```sh\nattractor report\n```\n\nIf you'd like to specify a directory, use the file prefix option:\n\n```sh\nattractor report --file_prefix app/models\n```\n\nOr shorter:\n\n```sh\nattractor report -p app/models\n```\n\nCheck JavaScript:\n\n```sh\nattractor report -p app/javascript -t js\n```\n\nWatch for file changes:\n\n```sh\nattractor report -p app/models --watch\n```\n\nServe at `http://localhost:7890`:\n\n```sh\nattractor serve -p app/models\n```\n\nEnable [rack-livereload][rack-livereload]:\n\n```sh\nattractor serve -p app/models --watch\n```\n\n_Make sure you prefix these commands with `bundle exec` if you are using Bundler._\n\n### Live Reloading\n\nIf you have [guard-livereload][guard-livereload] (or a similar service) running on your project, you can leverage the hot reloading functionality by specifying `--watch|-w`. Attractor will then live-reload the browser window when a file watched by `guard-livereload` changes.\n\n## CI Usage\n\nTo use this CLI in a CI environment, use the `--ci` option, which will suppress automatic opening of a browser window.\n\n### Github Action\n\nThere is a dedicated [Github Action][attractor-action] that will compile Attractor's output.\n\nYou can quickly integrate it into your action's workflow by grabbing it on the [Marketplace][attractor-action-marketplace].\n\n### Gitlab Example\n\nThe simplest use case is to store the `attractor_output` directory as an artifact.\n\n```yml\nattractor:\n  stage: your-stage-label\n  image: ruby:latest\n  script:\n    - gem install attractor\n    - attractor report --ci\n  artifacts:\n    when: on_success\n    paths:\n      - attractor_output\n```\n\n## CLI Commands and Options\n\nInitialize the local cache:\n\n```sh\nattractor init\n  --file_prefix|-p app/models\n  --type|-t rb|js\n  --start_ago|-s  (e.g. 5y, 3m, 7w)\n  --minimum_churn|-c (minimum times a file must have changed to be processed)\n  --ignore|-i 'spec/*_spec.rb,db/schema.rb,tmp'\n``` \n\nPrint a simple output to console:\n\n```sh\nattractor calc\n  --file_prefix|-p app/models\n  --type|-t rb|js\n  --watch|-w\n  --start_ago|-s  (e.g. 5y, 3m, 7w)\n  --minimum_churn|-c (minimum times a file must have changed to be processed)\n  --ignore|-i 'spec/*_spec.rb,db/schema.rb,tmp'\n```\n\nGenerate a full report\n\n```sh\nattractor report\n  --file_prefix|-p app/models\n  --type|-t rb|js\n  --watch|-w\n  --no-open-browser|--ci\n  --start_ago|-s  (e.g. 5y, 3m, 7w)\n  --minimum_churn|-c (minimum times a file must have changed to be processed)\n  --ignore|-i 'spec/*_spec.rb,db/schema.rb,tmp'\n```\n\nServe the output on `http://localhost:7890`\n\n```sh\nattractor serve\n  --file_prefix|-p app/models\n  --watch|-w\n  --no-open-browser|--ci\n  --start_ago|-s  (e.g. 5y, 3m, 7w)\n  --minimum_churn|-c (minimum times a file must have changed to be processed)\n  --ignore|-i 'spec/*_spec.rb,db/schema.rb,tmp'\n```\n\nClear the local cache:\n\n```sh\nattractor clean\n``` \n\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo run all tests, run `bin/test`. You can run the specs by themselves with `bundle exec rspec`, and the cucumber features with `bundle exec cucumber`.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on [GitHub][repo].\n\n## Logo Attribution\n\n[Black Hole by Eynav Raphael from the Noun Project][logo-source]\n\n## Contributors ✨\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"http://www.julianrubisch.at\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/4352208?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eJulian Rubisch\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/julianrubisch/attractor/commits?author=julianrubisch\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"https://github.com/julianrubisch/attractor/commits?author=julianrubisch\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/olimart\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/547754?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eOlivier\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#maintenance-olimart\" title=\"Maintenance\"\u003e🚧\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://www.andrewmason.me/\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/18423853?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eAndrew Mason\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/julianrubisch/attractor/commits?author=andrewmcodes\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"https://github.com/julianrubisch/attractor/pulls?q=is%3Apr+reviewed-by%3Aandrewmcodes\" title=\"Reviewed Pull Requests\"\u003e👀\u003c/a\u003e \u003ca href=\"https://github.com/julianrubisch/attractor/commits?author=andrewmcodes\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://www.ombulabs.com\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/17584?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eErnesto Tagwerker\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/julianrubisch/attractor/commits?author=etagwerker\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://experimentslabs.com\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/1732268?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eManuel Tancoigne\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/julianrubisch/attractor/commits?author=mtancoigne\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c!-- markdownlint-restore --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjulianrubisch%2Fattractor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjulianrubisch%2Fattractor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjulianrubisch%2Fattractor/lists"}