{"id":51612150,"url":"https://github.com/barturba/ruby-duplicates","last_synced_at":"2026-07-12T09:30:30.692Z","repository":{"id":365695969,"uuid":"1240430911","full_name":"barturba/ruby-duplicates","owner":"barturba","description":"Find structurally similar Ruby methods with normalized Ripper fingerprints","archived":false,"fork":false,"pushed_at":"2026-05-16T06:03:26.000Z","size":7,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-18T14:06:50.788Z","etag":null,"topics":["code-metrics","duplicate-code","ripper","ruby","static-analysis"],"latest_commit_sha":null,"homepage":"https://rubygems.org/gems/ruby-duplicates","language":"Ruby","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/barturba.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"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,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-16T05:56:08.000Z","updated_at":"2026-05-16T06:07:08.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/barturba/ruby-duplicates","commit_stats":null,"previous_names":["barturba/ruby-duplicates"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/barturba/ruby-duplicates","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/barturba%2Fruby-duplicates","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/barturba%2Fruby-duplicates/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/barturba%2Fruby-duplicates/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/barturba%2Fruby-duplicates/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/barturba","download_url":"https://codeload.github.com/barturba/ruby-duplicates/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/barturba%2Fruby-duplicates/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35388394,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-12T02:00:06.386Z","response_time":87,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["code-metrics","duplicate-code","ripper","ruby","static-analysis"],"created_at":"2026-07-12T09:30:30.126Z","updated_at":"2026-07-12T09:30:30.676Z","avatar_url":"https://github.com/barturba.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ruby-duplicates\n\nA small duplicate-code metric for Ruby.\n\n`ruby-duplicates` parses Ruby with the standard library `Ripper`, normalizes syntax trees so names and literal values do not dominate the comparison, fingerprints method subtrees, and reports methods with high Jaccard similarity.\n\nIt is inspired by Uncle Bob's [`dry4clj`](https://github.com/unclebob/dry4clj), which applies the same broad idea to Clojure code: compare normalized structure instead of doing plain text clone detection.\n\nThis is a metric tool, not a refactoring engine. It points at suspiciously similar methods so a human or coding agent can decide whether the duplication is accidental, intentional symmetry, or data-shaped boilerplate.\n\n## Install\n\nFrom RubyGems:\n\n```bash\ngem install ruby-duplicates\nruby-duplicates app lib test\n```\n\nFrom this repo:\n\n```bash\nbundle install\nexe/ruby-duplicates app lib test\n```\n\n## Usage\n\n```bash\nruby-duplicates [options] [file-or-directory ...]\n```\n\nExamples:\n\n```bash\nruby-duplicates app lib test\nruby-duplicates --threshold 0.9 --min-lines 5 --min-nodes 30 app\nruby-duplicates --json app/models app/controllers\n```\n\nOptions:\n\n```bash\n--threshold N    Minimum similarity score, default 0.82\n--min-lines N    Minimum method source lines, default 4\n--min-nodes N    Minimum normalized syntax nodes, default 20\n--max-results N  Maximum matches to print, default 50\n--format F       text or json, default text\n--json           Same as --format json\n--ignore-dir N   Directory basename or path to skip; may be repeated\n```\n\nExample output:\n\n```text\nruby_duplicates candidates=3 matches=1 threshold=0.82\n\nDUPLICATE score=1.00 shared=21\n  examples/duplicate_sample.rb:1-4 alpha nodes=64\n  examples/duplicate_sample.rb:7-10 beta nodes=64\n```\n\n## How It Works\n\nFor each Ruby method, the scanner:\n\n1. Parses the file with `Ripper.sexp`.\n2. Extracts `def` and `defs` method nodes.\n3. Normalizes identifiers, constants, instance variables, globals, labels, strings, and numbers into token classes.\n4. Normalizes most non-head symbols so tiny operator/name differences do not hide repeated shape.\n5. Fingerprints every normalized subtree with SHA1.\n6. Compares method fingerprint sets with Jaccard similarity.\n\nThe defaults intentionally favor high-signal matches. Lower `--threshold`, `--min-lines`, or `--min-nodes` when exploring.\n\n## Limits\n\n- It only scans Ruby methods, not arbitrary repeated blocks.\n- It is structural, not semantic.\n- Metaprogrammed code can look sparse because the useful behavior is hidden in data.\n- Rails controllers and tests can produce intentional symmetry. Treat those as review candidates, not automatic refactors.\n\n## Development\n\n```bash\nruby -Ilib test/ruby_duplicates_test.rb\ngem build ruby-duplicates.gemspec\n```\n\n## Inspiration\n\n- Uncle Bob's `dry4clj`: https://github.com/unclebob/dry4clj\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbarturba%2Fruby-duplicates","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbarturba%2Fruby-duplicates","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbarturba%2Fruby-duplicates/lists"}