{"id":14955409,"url":"https://github.com/maxlap/activerecord_follow_assoc","last_synced_at":"2025-10-01T18:31:23.518Z","repository":{"id":59150360,"uuid":"401116385","full_name":"MaxLap/activerecord_follow_assoc","owner":"MaxLap","description":"Follow associations within your ActiveRecord queries","archived":false,"fork":false,"pushed_at":"2024-04-20T14:05:42.000Z","size":389,"stargazers_count":17,"open_issues_count":1,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-14T11:14:12.432Z","etag":null,"topics":["activerecord","rails","rails5","rails6","ruby","sql"],"latest_commit_sha":null,"homepage":"","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/MaxLap.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","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},"funding":{"github":"MaxLap","ko_fi":"maxlap"}},"created_at":"2021-08-29T18:40:56.000Z","updated_at":"2024-04-20T14:05:46.000Z","dependencies_parsed_at":"2024-01-13T20:39:34.916Z","dependency_job_id":"422ee311-ba5a-4ec4-a133-261ddb653fa4","html_url":"https://github.com/MaxLap/activerecord_follow_assoc","commit_stats":{"total_commits":49,"total_committers":3,"mean_commits":"16.333333333333332","dds":0.04081632653061229,"last_synced_commit":"ebc6a6487cd9f9be82df9abcb694edaa15ee2df0"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaxLap%2Factiverecord_follow_assoc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaxLap%2Factiverecord_follow_assoc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaxLap%2Factiverecord_follow_assoc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaxLap%2Factiverecord_follow_assoc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MaxLap","download_url":"https://codeload.github.com/MaxLap/activerecord_follow_assoc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234719912,"owners_count":18876528,"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":["activerecord","rails","rails5","rails6","ruby","sql"],"created_at":"2024-09-24T13:11:06.908Z","updated_at":"2025-10-01T18:31:18.211Z","avatar_url":"https://github.com/MaxLap.png","language":"Ruby","readme":"# ActiveRecord Follow Assoc\n\n![Test supported versions](https://github.com/MaxLap/activerecord_follow_assoc/workflows/Test%20supported%20versions/badge.svg)\n\nLet's say that, in your Rails app, you want to get all of the comments to the recent posts the\ncurrent user made.\n\nThink of how you would do it.\n\nHere's how this gem allows you to do it:\n\n```ruby\ncurrent_user.posts.recent.follow_assoc(:comments)\n```\n\nThe `follow_assoc` method, added by this gem, allows you to query the specified association\nof the records that the current query would return.\n\nHere is a more complete [introduction to this gem](INTRODUCTION.md).\n\nBenefits of `follow_assoc`:\n* Works the same way for all kinds of association `belongs_to`, `has_many`, `has_one`, `has_and_belongs_to_many`\n* You can use `where`, `order` and other such methods on the result\n* By nesting SQL queries, the only records that need to be loaded are the final ones, so the above example\n  wouldn't have loaded any `Post` from the database. This usually leads to faster code.\n* You avoid many [problems with the alternative options](ALTERNATIVES_PROBLEMS.md).\n\n## Why / when do you need this?\n\nAs applications grow, you can end up with quite complex data model and even more complex business rules. You may end up\nneeding to fetch records that are deep in your associations.\n\nAs a simple example, let's say you have a helper which receives sections of a blog and must return the recent comments\nin those sections.\n```ruby\ndef recent_comments_within(sections)\n  sections.follow_assoc(:posts, :comments).recent\nend\n```\n\nNote that this won't work if `sections` is an `Array`. `follow_assoc` is available in the same places as `where`. See [Usage](#Usage) for details.\n\nDoing this without follow_assoc can be verbose, error-prone and less efficient depending on the approach taken.\n\n## Installation\n\nRails 4.1 to 6.1 are supported with Ruby 2.1 to 3.0. Tested against SQLite3, PostgreSQL and MySQL. The gem\nonly depends on the `activerecord` gem.\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'activerecord_follow_assoc'\n```\n\nAnd then execute:\n\n    $ bundle install\n\nOr install it yourself with:\n\n    $ gem install activerecord_follow_assoc\n\n## Usage\n\nStarting from a query or a model, you call `follow_assoc` with an association's name. It returns another query that:\n\n* searches in the association's model\n* has a `where` to only return the records that are associated with the records that the initial query would have returned.\n\nSo `my_comments.follow_assoc(:posts)` gives you a query on `Post` which only returns the posts that are\nassociated to the records of `my_comments`.\n\n```ruby\n# Getting the spam comments to posts by a specific author\nspam_comments = author.posts.follow_assoc(:comments).spam\n```\n\nAs a shortcut, you can also give multiple association to `follow_assoc`. Doing so is equivalent to consecutive calls to it.\n```ruby\n# Getting the spam comments to posts in some sections\nspam_comments_in_section = my_sections.follow_assoc(:posts, :comments).spam\n# Equivalent to\nspam_comments_in_section = my_sections.follow_assoc(:posts).follow_assoc(:comments).spam\n```\n\nThe `follow_assoc` method is only available on models and queries (also often called relation or scope). You cannot use\nit on an `Array` of record. If you need to use `follow_assoc` in that situation, then you must make a query yourself:\n```ruby\nsections_query = Section.where(id: my_sections)\n# Then you can use `follow_assoc`\nspam_comments_in_section = sections_query.follow_assoc(:posts, :comments).spam\n```\n\nDetailed doc is [here](https://maxlap.dev/activerecord_follow_assoc/ActiveRecordFollowAssoc/QueryMethods.html).\n\n## Known issues\n\n**No support for recursive has_one**\n\nThe SQL to handle recursive has_one while isolating the different layers of conditions is a mess and I worry about\nthe resulting performance. So for now, this will raise an exception. You can use the `ignore_limit: true` option\nto treat the has_one as a has_many.\n\n**MySQL doesn't support sub-limit**\n\nOn MySQL databases, it is not possible to use has_one associations.\n\nI do not know of a way to do a SQL query that can deal with all the specifics of has_one for MySQL. If you have one, then please suggest it in an issue/pull request.\n\nIn order to work around this, you must use the `ignore_limit: true` option, which means that the `has_one` will be treated\nlike a `has_many`.\n\n## Another recommended gem\n\nIf you feel a need for this gem's feature, you may also be interested in another gem I made: [activerecord_where_assoc](https://github.com/MaxLap/activerecord_where_assoc).\n\nIt allows you to make conditions based on your associations (without changing the kind of objects returned). For simple cases, it's possible that both can build the query your need, but each can handle different situations. Here is an example:\n\n```ruby\n# Find every posts that have comments by an admin\nPost.where_assoc_exists([:comments, :author], \u0026:admins)\n```\n\nThis could be done with `follow_assoc`: `User.admins.follow_assoc(:comments, :post)`. But if you wanted conditions on a second association, then `follow_assoc` wouldn't work. On the other hand, if you received a scope on users and wanted their posts, then `follow_assoc` would be a nicer tool for the job. It all depends on the context where you need to do the query and what starting point you have.\n\n## Development\n\nAfter checking out the repo, run `bundle install` to install dependencies. Then, run `rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\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 at https://github.com/MaxLap/activerecord_follow_assoc.\n\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\n## Code of Conduct\n\nEveryone interacting in the ActiveRecordFollowAssoc project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/MaxLap/activerecord_follow_assoc/blob/master/CODE_OF_CONDUCT.md).\n\n\n\n","funding_links":["https://github.com/sponsors/MaxLap","https://ko-fi.com/maxlap"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxlap%2Factiverecord_follow_assoc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaxlap%2Factiverecord_follow_assoc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxlap%2Factiverecord_follow_assoc/lists"}