{"id":19780523,"url":"https://github.com/musaffa/polymorphic_constraints","last_synced_at":"2025-04-30T21:33:30.374Z","repository":{"id":21508334,"uuid":"24827363","full_name":"musaffa/polymorphic_constraints","owner":"musaffa","description":"Database referential integrity enforcer for Rails polymorphic associations using triggers.","archived":false,"fork":false,"pushed_at":"2016-04-01T18:23:29.000Z","size":94,"stargazers_count":18,"open_issues_count":1,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-04-23T13:18:36.941Z","etag":null,"topics":["polymorphic-relationships","rails"],"latest_commit_sha":null,"homepage":null,"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/musaffa.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"MIT-LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-10-05T22:00:56.000Z","updated_at":"2020-01-07T20:26:55.000Z","dependencies_parsed_at":"2022-08-17T18:50:09.898Z","dependency_job_id":null,"html_url":"https://github.com/musaffa/polymorphic_constraints","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/musaffa%2Fpolymorphic_constraints","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/musaffa%2Fpolymorphic_constraints/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/musaffa%2Fpolymorphic_constraints/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/musaffa%2Fpolymorphic_constraints/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/musaffa","download_url":"https://codeload.github.com/musaffa/polymorphic_constraints/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224225348,"owners_count":17276435,"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":["polymorphic-relationships","rails"],"created_at":"2024-11-12T05:40:13.714Z","updated_at":"2024-11-12T05:40:14.493Z","avatar_url":"https://github.com/musaffa.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Polymorphic Constraints\n\n[![Gem Version](https://badge.fury.io/rb/polymorphic_constraints.svg)](http://badge.fury.io/rb/polymorphic_constraints)\n[![Build Status](https://travis-ci.org/musaffa/polymorphic_constraints.svg)](https://travis-ci.org/musaffa/polymorphic_constraints)\n[![Dependency Status](https://gemnasium.com/musaffa/polymorphic_constraints.svg)](https://gemnasium.com/musaffa/polymorphic_constraints)\n[![Coverage Status](https://coveralls.io/repos/musaffa/polymorphic_constraints/badge.png)](https://coveralls.io/r/musaffa/polymorphic_constraints)\n[![Code Climate](https://codeclimate.com/github/musaffa/polymorphic_constraints/badges/gpa.svg)](https://codeclimate.com/github/musaffa/polymorphic_constraints)\n\nPolymorphic Constraints gem introduces some methods to your migrations to help to maintain the referential integrity for your Rails polymorphic associations.\n\nIt uses triggers to enforce the constraints. It enforces constraints on `insert`, `update` and `delete`. `update` and `delete` constraints works like the `:restrict` option of foreign key. \n\n## Support\n\nIt supports the following adapters:\n\n* sqlite3\n* postgresql\n* mysql2\n\nSupported platforms:\n\n* Rails versions - 3 and 4.\n\n## Installation\n\nAdd the following to your Gemfile:\n\n```ruby\ngem 'polymorphic_constraints'\n```\n\n## API Examples\n\nThis gem adds the following methods to your migrations:\n\n* add_polymorphic_constraints(relation, associated_table, options)\n* update_polymorphic_constraints(relation, associated_table, options)\n* remove_polymorphic_constraints(relation)\n\nFrom [Rails Guide](http://guides.rubyonrails.org/association_basics.html#polymorphic-associations)\ntake these examples:\n\n```ruby\nclass Picture \u003c ActiveRecord::Base\n  belongs_to :imageable, polymorphic: true\nend\n \nclass Employee \u003c ActiveRecord::Base\n  has_many :pictures, as: :imageable, dependent: :destroy\nend\n \nclass Product \u003c ActiveRecord::Base\n  has_many :pictures, as: :imageable\nend\n```\n\nAdd a new migration:\n\n```ruby\nclass AddPolymorphicConstraints \u003c ActiveRecord::Migration\n  def change\n    add_polymorphic_constraints :imageable, :pictures\n  end\nend\n```\nOr you can add it to pictures migration:\n\n```ruby\nclass CreateComments \u003c ActiveRecord::Migration\n  create_table :pictures do |t|\n    t.references :imageable, polymorphic: true\n    t.timestamps\n  end\n\n  add_polymorphic_constraints :imageable, :pictures\nend\n```\nFor the second method to work properly, the polymorphic tables `employees` and `products` have to be in the database first i.e `pictures` migration should come after the migrations of `employees` and `products`.\n\nrun: `rake db:migrate`\n\nThis migration will create the necessary triggers to apply insert, update and delete constraints on `imageable` polymorphic relation.\n\n```ruby\n# insert\n\u003e\u003e picture = Picture.new\n\u003e\u003e picture.imageable_id = 1\n\u003e\u003e picture.imageable_type = 'Product'\n\u003e\u003e picture.save # raises ActiveRecord::RecordNotFound exception. there's no product with id 1\n\n\u003e\u003e product = Product.create\n\n\u003e\u003e picture.imageable_id = product.id\n\u003e\u003e picture.imageable_type = 'World'\n\u003e\u003e picture.save # raises ActiveRecord::RecordNotFound exception. there's no imageable model named 'World'.\n\n\u003e\u003e picture.imageable_type = product.class.to_s # 'Product'\n\u003e\u003e picture.save # saves successfully\n\n# update\n\u003e\u003e picture.imageable_type = 'Hello'\n\u003e\u003e picture.save # raises ActiveRecord::RecordNotFound exception. there's no imageable model named 'Hello'.\n\n\u003e\u003e employee = Employee.create\n\n\u003e\u003e picture.imageable_id = employee.id\n\u003e\u003e picture.imageable_type = employee.class.to_s # 'Employee'\n\u003e\u003e picture.save # update completes successfully\n\n# delete/destroy\n\u003e\u003e employee.delete # raises ActiveRecord::ReferenceViolation exeption. cannot delete because the picture still refers to the employee as the imageable.\n\u003e\u003e employee.destroy # destroys successfully. unlike product, employee implements dependent destroy on imageable. so it destroys the picture first, then it destroys itself.\n\u003e\u003e Employee.count # 0\n\u003e\u003e Picture.count # 0\n\n\u003e\u003e picture = Picture.new\n\u003e\u003e picture.imageable_id = product.id\n\u003e\u003e picture.imageable_type = product.class.to_s # 'Product'\n\u003e\u003e picture.save\n\n\u003e\u003e product.delete # raises ActiveRecord::ReferenceViolation exeption. cannot delete because the picture still refers to the product as the imageable.\n\u003e\u003e product.destroy # raises ActiveRecord::ReferenceViolation exeption. works the same as delete because product model hasn't implemented dependent destroy on imageable.\n\n\u003e\u003e another_product = Product.create\n\u003e\u003e another_product.delete # deletes successfully as no picture refers to this product.\n```\n\n## Exceptions\n\n* `ActiveRecord::RecordNotFound` - It is raised when insert/update is attempted on a table with a record that refers to a non-existent polymorphic record in another table.\n* `ActiveRecord::ReferenceViolation` - It is raised when a delete is attempted from a polymorphic table that is referred to by a record in the associated table.\n\n_naming convention:_ in the above example `Product` and `Employee` are polymorphic tables. `Picture` is an associated table. \n\n## Model Search Strategy:\n\nWhen you add polymorphic constraints like this:\n\n```ruby\nadd_polymorphic_constraints :imageable, :pictures\n```\nthe gem will search for models acting as imageable using `ActiveRecord::Base.descendants`. This will search all the models including your gems, models directory etc.\n\nYou can also explicitly specify the models with which you want to create polymorphic constraints.\n\n```ruby\nadd_polymorphic_constraints :imageable, :pictures, polymorphic_models: [:employee]\n```\nThis will create polymorphic constraints only between `pictures` and `employees`. `:polymorphic_models` will supersede `ActiveRecord::Base.descendants` search_strategy.\n\n**Note:** `:polymorphic_models` option requires an array. The models specified in the array should be in singular form. Make sure the models indeed have the polymorphic relationship (in this example, `:employee` acting as `:imageable` with `:pictures`).\n\n## Update Constraints\n\nThis gem creates triggers using the existing state of the application. If you add any model later or add new polymorphic relationships in the existing model, it wont have any polymorphic constraint applied to it. For example, if you add a member class later in the application life cycle:\n\n```ruby\nclass Member \u003c ActiveRecord::Base\n  has_many :pictures, as: :imageable, dependent: :destroy\nend\n```\nThere will be no polymorphic constraints between `pictures` and `members`. You have to renew the `imageable` constraints by adding another migration:\n\n```ruby\nclass AgainUpdatePolymorphicConstraints \u003c ActiveRecord::Migration\n  def change\n    update_polymorphic_constraints :imageable, :pictures\n  end\nend\n```\nThis will delete all the existing `:imageable` constraints and create new ones. You can also specify `:polymorphic_models` options with `update_polymorphic_constraints` method. See [Model Search Strategy](#model-search-strategy)\n\n**Note:** `update_polymorphic_constraints` is simply an alias to `add_polymorphic_constraints`.\n\n## Schema Dump\n\nThe gem doesn't support `ruby` schema dump yet. You have to dump `sql` instead of schema.rb. To do this, change the application config settings:\n\n```ruby\n# app/config/application.rb\nconfig.active_record.schema_format = :sql\n```\n\n```ruby\nrake db:structure:dump\n```\n\n## Migration Rollback\n\n`add_polymorphic_constraints` and `update_polymorphic_constraints` are both reversible. So you don't need to worry about rollback.\n\n```ruby\nclass AddPolymorphicConstraints \u003c ActiveRecord::Migration\n  def change\n    add_polymorphic_constraints :imageable, :pictures\n  end\nend\n```\n\nYou can also use `up` and `down` like this:\n\n```ruby\nclass AddPolymorphicConstraints \u003c ActiveRecord::Migration\n  def self.up\n    add_polymorphic_constraints :imageable, :pictures\n  end\n\n  def self.down\n    remove_polymorphic_constraints :imageable\n  end\nend\n```\n\nThis `remove_polymorphic_constraints` will delete all the existing `:imageable` constraints during rollback.\n\n**Caution:** After migration, always test if rollback works properly.\n\n## Tests\n\n```ruby\n$ rake\n$ rake test:unit\n$ rake test:integration:all\n$ rake test:integration:sqlite\n$ rake test:integration:postgresql\n$ rake test:integration:mysql\n\n# test different active model versions\n$ appraisal install\n$ appraisal rake\n```\n\n## Problems\n\nPlease use GitHub's [issue tracker](http://github.com/musaffa/polymorphic_constraints/issues).\n\n## TODO\n1. Ruby schema dump\n2. Supporting `on_delete`, `on_update` options with `:nullify`, `:restrict` and `:cascade`.\n\n## Contributing\n\n1. Fork it\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Added some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n\n## License\n\nThis project rocks and uses MIT-LICENSE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmusaffa%2Fpolymorphic_constraints","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmusaffa%2Fpolymorphic_constraints","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmusaffa%2Fpolymorphic_constraints/lists"}