{"id":16687966,"url":"https://github.com/danielpclark/polybelongsto","last_synced_at":"2025-06-21T15:05:38.018Z","repository":{"id":26072269,"uuid":"29516045","full_name":"danielpclark/PolyBelongsTo","owner":"danielpclark","description":"Uniform Omni-Relational ActiveRecord Methods","archived":false,"fork":false,"pushed_at":"2018-11-26T00:42:45.000Z","size":197,"stargazers_count":46,"open_issues_count":1,"forks_count":4,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-06-21T15:05:31.293Z","etag":null,"topics":["activerecord","database","integrity","metaprogramming","polymorphic-relations","record-duplication","ruby-gem"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"dmbrownlee/playbook","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danielpclark.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"MIT-LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-01-20T06:48:51.000Z","updated_at":"2022-06-12T02:45:47.000Z","dependencies_parsed_at":"2022-08-06T08:15:45.395Z","dependency_job_id":null,"html_url":"https://github.com/danielpclark/PolyBelongsTo","commit_stats":null,"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/danielpclark/PolyBelongsTo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielpclark%2FPolyBelongsTo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielpclark%2FPolyBelongsTo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielpclark%2FPolyBelongsTo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielpclark%2FPolyBelongsTo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danielpclark","download_url":"https://codeload.github.com/danielpclark/PolyBelongsTo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielpclark%2FPolyBelongsTo/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261143144,"owners_count":23115672,"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","database","integrity","metaprogramming","polymorphic-relations","record-duplication","ruby-gem"],"created_at":"2024-10-12T15:26:11.664Z","updated_at":"2025-06-21T15:05:33.003Z","avatar_url":"https://github.com/danielpclark.png","language":"Ruby","readme":"# PolyBelongsTo\n\n[![Gem Version](https://badge.fury.io/rb/poly_belongs_to.svg)](http://badge.fury.io/rb/poly_belongs_to)\n[![Code Climate](https://codeclimate.com/github/danielpclark/PolyBelongsTo/badges/gpa.svg)](https://codeclimate.com/github/danielpclark/PolyBelongsTo)\n[![Build Status](https://travis-ci.org/danielpclark/PolyBelongsTo.svg)](https://travis-ci.org/danielpclark/PolyBelongsTo)\n[![Test Coverage](https://codeclimate.com/github/danielpclark/PolyBelongsTo/badges/coverage.svg)](https://codeclimate.com/github/danielpclark/PolyBelongsTo)\n[![Inline docs](http://inch-ci.org/github/danielpclark/PolyBelongsTo.svg?branch=master)](http://inch-ci.org/github/danielpclark/PolyBelongsTo)\n[![Featured In Ruby Weekly #233](https://img.shields.io/badge/Featured%20In-Ruby%20Weekly%20%23233-brightgreen.svg)](http://rubyweekly.com/issues/233)\n[![Featured In Green Ruby #118](https://img.shields.io/badge/Featured%20In-Green%20Ruby%20%23118-brightgreen.svg)](http://greenruby.org/grn-118.html)\n[![SayThanks.io](https://img.shields.io/badge/SayThanks.io-%E2%98%BC-1EAEDB.svg)](https://saythanks.io/to/danielpclark)\n\n**Original Purpose:** A standard way to check belongs_to relations on any belongs_to Object and let you check your DB Objects polymorphism in a more across-the-board meta-programatically friendly way.\n\nPolyBelongsTo has grown into a powerful tool for working with all kinds of ActiveRecord relationships and situations.  PBT makes handling things in AR easier to deal with in a more generic way.  There are also some hierarchal tools provided which make coding with AR relationships all the more powerful.  See anything that's missing?  Please open an issue and suggest a feature!\n\n**Database integrity** can be checked with a few methods to find orphaned record relations and incorrectly typed polymorphic relations.\n\n**Deep cloning** of records with associations is an added feature this gem provides.  It requires no configuration to use, you can simply invoke: `pbt_deep_dup_build`.  This makes for much easier record duplication than the [deep_cloneable](https://github.com/moiristo/deep_cloneable) gem.\n\n# Installation\n\nJust include it in your Gemfile and then run bundle:\n```ruby\ngem 'poly_belongs_to', '~\u003e 1.0'\n```\n\n## Recommended Usage\n\n##### On model class\n```ruby\n# Is Polymorphic?\nMyOject.poly?\n# =\u003e true\nUser.poly?\n# =\u003e false\n    \n# Belongs To Relation Table\nMyObject.pbt\n# =\u003e :my_objectable\nUser.pbt\n# =\u003e nil \n\n# Multiple Belongs To Relations\nTire.pbts\n# =\u003e [:user, :car]\n\n# Multiple Has One Relations\nProfile.has_one_of\n# =\u003e [:photo]\n\n# Multiple Has Many Relations\nProfile.has_many_of\n# =\u003e [:phones, :addresses]\n\n# Multiple Has And Belongs To Many Relations\nAssembly.habtm_of\n# =\u003e [:parts]\n\n# Get orphaned objects for records of class type\nMyObject.pbt_orphans\n# =\u003e #\u003cActiveRecord::Relation []\u003e # nil for objects without belongs_to\n\n# Get polymorphic objects for records of invalid class type(s)\nMyObject.pbt_mistyped\n# =\u003e #\u003cActiveRecord::Relation []\u003e \n\n# Get the invalid class types on polymorphic records as a Array of Strings\nMyObject.pbt_mistypes\n# =\u003e [\"Object\", \"Class\", \"MyObjectable\"]\n\n# Get the valid class types on polymorphic records as a Array of Strings\nMyObject.pbt_valid_types\n# =\u003e [\"User\", \"MyObject\"]\n\n# Params name\nMyObject.pbt_params_name\n# =\u003e :my_objectable_attributes\nMyObject.pbt_params_name(false)\n# =\u003e :my_object\nUser.pbt_params_name\n# =\u003e :user\n\n# DB column names\nMyObject.pbt_id_sym             \n# =\u003e :my_objectable_id\nMyObject.pbt_type_sym           \n# =\u003e :my_objectable_type        # nil for non polymorphic Objects\n```\n##### On model instances\n```ruby\n# Belongs To Relations ID\nMyObject.first.pbt_id\n# =\u003e 123\n    \n# Polymorphic Belongs To Relations Type\nMyObject.first.pbt_type\n\"User\"                          # nil for non polymorphic Objects\n\n# Get Parent Object (Works on all belongs_to Objects)\nMyObject.first.pbt_parent\n# =\u003e #\u003cUser id: 123 ... \u003e\n\n# Get Top Hierarchical Parent Object (Works on all belongs_to Objects)\nMyObject.first.pbt_top_parent\n# =\u003e #\u003cUser id: 123 ... \u003e\n\n# Mutliple Parent Objects (List of one item for Polymorphic, full list otherwise.)\nTire.first.pbt_parents\n# =\u003e [#\u003cUser id: 123 ... \u003e, #\u003cCar id: 234 ... \u003e]\n\n# Determine if object is orphaned (parent no longer exists)\nTire.first.orphan?\n# =\u003e false\n```\n\n## Also Available\n```ruby\n# --- Model Instances ---\n# NOTE: touches db if object isn't already instantiated\n\n# Is Polymorphic?\nMyObject.new.poly?\n# =\u003e true\nUser.first.poly?\n# =\u003e false\n\n# Belongs To Relation Table\nMyObject.new.pbt\n# =\u003e :my_objectable\nUser.first.pbt\n# =\u003e nil\n\n# Multiple Belongs To Relations\nTire.first.pbts\n# =\u003e [:user, :car]\n\n# Params name\nMyObject.new.pbt_params_name\n# =\u003e :my_objectable_attributes\nMyObject.new.pbt_params_name(false)\n# =\u003e :my_object\nUser.first.pbt_params_name\n# =\u003e :user\n\n# DB column names\nMyObject.new.pbt_id_sym\n# =\u003e :my_objectable_id\nMyObject.new.pbt_type_sym       # nil for non polymorphic Objects\n# =\u003e :my_objectable_type\n```\n\n## Internal Methods Available\n\n```ruby\n# For cleaning attributes for use with build\nPolyBelongsTo::Pbt::AttrSanitizer[ obj ]\n\n# Returns string of either 'child.build' or 'build_child'\nPolyBelongsTo::Pbt::BuildCmd[ obj, child ]\n\n# Returns has_one and has_many relationships for obj as an Array of symbols\nPolyBelongsTo::Pbt::Reflects[ obj, habtm = false ]\n\n# Returns Class Ojects for each has_one and has_many child associations\nPolyBelongsTo::Pbt::ReflectsAsClasses[ obj, habtm = false ]\n\n# Boolean of whether child object/class is a has(one/many) relationship to obj\nPolyBelongsTo::Pbt::IsReflected[ obj, child ]\n\n# Returns :singular if obj-\u003echild is has_one and :plural if obj-\u003echild is has_many\nPolyBelongsTo::Pbt::SingularOrPlural[ obj, child ]\n\n# Returns true if obj-\u003echild relationship is has_one\nPolyBelongsTo::Pbt::IsSingular[ obj, child ]\n\n# Returns true if obj-\u003echild relationship is has_many\nPolyBelongsTo::Pbt::IsPlural[ obj, child ]\n\n# Returns the symbol for the CollectionProxy the child belongs to in relation to obj\n# NOTE: For has_one the symbol is not a CollectionProxy, but represents the instance\nPolyBelongsTo::Pbt::CollectionProxy[ obj, child ]\n\n# Always returns a collection proxy; fakes a collection proxy for has_one\nPolyBelongsTo::Pbt::AsCollectionProxy[ obj, child ]\n\n# Wrapper for has_one objects to be a collection proxy\nPolyBelongsTo::FakedCollection.new(obj, child)\n\n# Track which DB records have already been processed\nPolyBelongsTo::SingletonSet.new\n```\n\u003e In methods that have more than one type of ownership the order or precedence is\npolymorphic relationships first, primary key next (or first reflection in lookup).\n\n\n## Record Duplication\n\n**This gives you the advantage of duplicating records regardless of polymorphic associations or\notherwise**.  You can duplicate a record, or use a self recursive command **pbt_deep_dup_build**\nto duplicate a record and all of it's has_one/has_many children records at once.  Afterwards\nbe sure to use the save method.\n\n\n#### Known Issues\n - Carrierwave records won't duplicate.  To ensure that other records will still save and\nprevent any rollback issues use .save(validate: false) ... I'm considering possible options\nto remedy this and\nother scenarios.\n\n### How To Use\n\nUse the dup/build methods as follows\n\n```ruby\n# If you were to create a new contact for example\ncontact = User.first.contacts.new\n\n# This is just like contact.profile.build( { ... user's profile attributes ... } )\ncontact.pbt_dup_build( User.last.profile )\n\n# Save it!\ncontact.save\n\n\n# For a fully recursive copy do the same with pbt_deep_dup_build\ncontact.pbt_deep_dup_build( User.last.profile )\n\n# Remeber to save!\ncontact.save\n```\n\n## Contributing\n\nFeel free to fork and make pull requests.  Please bring up an issue before a pull\nrequest if it's a non-fix change.  Please add applicable fixtures and tests for\nany new features/implementations you add.\n\nThank You!\n\n\n# License\n\nThe MIT License (MIT)\n\nCopyright (C) 2015-2017 by Daniel P. Clark\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielpclark%2Fpolybelongsto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanielpclark%2Fpolybelongsto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielpclark%2Fpolybelongsto/lists"}