[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-7899":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":16,"stars30d":16,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":17,"rankGlobal":10,"rankLanguage":10,"license":18,"archived":19,"fork":19,"defaultBranch":20,"hasWiki":21,"hasPages":19,"topics":22,"createdAt":10,"pushedAt":10,"updatedAt":23,"readmeContent":24,"aiSummary":25,"trendingCount":16,"starSnapshotCount":16,"syncStatus":26,"lastSyncTime":27,"discoverSource":28},7899,"paranoia","rubysherpas\u002Fparanoia","rubysherpas","acts_as_paranoid for Rails 5, 6 and 7","",null,"Ruby",2917,528,29,81,0,60.17,"Other",false,"core",true,[],"2026-06-12 04:00:36","[![Gem Version](https:\u002F\u002Fbadge.fury.io\u002Frb\u002Fparanoia.svg)](https:\u002F\u002Fbadge.fury.io\u002Frb\u002Fparanoia)\n[![build](https:\u002F\u002Fgithub.com\u002Frubysherpas\u002Fparanoia\u002Factions\u002Fworkflows\u002Fbuild.yml\u002Fbadge.svg)](https:\u002F\u002Fgithub.com\u002Frubysherpas\u002Fparanoia\u002Factions\u002Fworkflows\u002Fbuild.yml)\n\n**Notice:**\n\n`paranoia` has some surprising behaviour (like overriding ActiveRecord's `delete` and `destroy`) and is not recommended for new projects. See [`discard`'s README](https:\u002F\u002Fgithub.com\u002Fjhawthorn\u002Fdiscard#why-not-paranoia-or-acts_as_paranoid) for more details.\n\nParanoia will continue to accept bug fixes and support new versions of Rails but isn't accepting new features.\n\n# Paranoia\n\nParanoia is a re-implementation of [acts\\_as\\_paranoid](http:\u002F\u002Fgithub.com\u002FActsAsParanoid\u002Facts_as_paranoid) for Rails 3\u002F4\u002F5, using much, much, much less code.\n\nWhen your app is using Paranoia, calling `destroy` on an ActiveRecord object doesn't actually destroy the database record, but just *hides* it. Paranoia does this by setting a `deleted_at` field to the current time when you `destroy` a record, and hides it by scoping all queries on your model to only include records which do not have a `deleted_at` field.\n\nIf you wish to actually destroy an object you may call `really_destroy!`. **WARNING**: This will also *really destroy* all `dependent: :destroy` records, so please aim this method away from face when using.\n\nIf a record has `has_many` associations defined AND those associations have `dependent: :destroy` set on them, then they will also be soft-deleted if `acts_as_paranoid` is set, otherwise the normal destroy will be called. ***See [Destroying through association callbacks](#destroying-through-association-callbacks) for clarifying examples.***\n\n## Getting Started Video\nSetup and basic usage of the paranoia gem\n[GoRails #41](https:\u002F\u002Fgorails.com\u002Fepisodes\u002Fsoft-delete-with-paranoia)\n\n## Installation & Usage\n\nFor Rails 3, please use version 1 of Paranoia:\n\n``` ruby\ngem \"paranoia\", \"~> 1.0\"\n```\n\nFor Rails 4 and 5, please use version 2 of Paranoia (2.2 or greater required for rails 5):\n\n``` ruby\ngem \"paranoia\", \"~> 2.2\"\n```\n\nOf course you can install this from GitHub as well from one of these examples:\n\n``` ruby\ngem \"paranoia\", github: \"rubysherpas\u002Fparanoia\", branch: \"rails3\"\ngem \"paranoia\", github: \"rubysherpas\u002Fparanoia\", branch: \"rails4\"\ngem \"paranoia\", github: \"rubysherpas\u002Fparanoia\", branch: \"rails5\"\n```\n\nThen run:\n\n``` shell\nbundle install\n```\n\nUpdating is as simple as `bundle update paranoia`.\n\n#### Run your migrations for the desired models\n\nRun:\n\n``` shell\nbin\u002Frails generate migration AddDeletedAtToClients deleted_at:datetime:index\n```\n\nand now you have a migration\n\n``` ruby\nclass AddDeletedAtToClients \u003C ActiveRecord::Migration\n  def change\n    add_column :clients, :deleted_at, :datetime\n    add_index :clients, :deleted_at\n  end\nend\n```\n\n### Usage\n\n#### In your model:\n\n``` ruby\nclass Client \u003C ActiveRecord::Base\n  acts_as_paranoid\n\n  # ...\nend\n```\n\nHey presto, it's there! Calling `destroy` will now set the `deleted_at` column:\n\n\n``` ruby\n>> client.deleted_at\n# => nil\n>> client.destroy\n# => client\n>> client.deleted_at\n# => [current timestamp]\n```\n\nIf you really want it gone *gone*, call `really_destroy!`:\n\n``` ruby\n>> client.deleted_at\n# => nil\n>> client.really_destroy!\n# => client\n```\n\nIf you need skip updating timestamps for deleting records, call `really_destroy!(update_destroy_attributes: false)`.\nWhen we call `really_destroy!(update_destroy_attributes: false)` on the parent `client`, then each child `email` will also have `really_destroy!(update_destroy_attributes: false)` called.\n\n``` ruby\n>> client.really_destroy!(update_destroy_attributes: false)\n# => client\n```\n\nIf you want to use a column other than `deleted_at`, you can pass it as an option:\n\n``` ruby\nclass Client \u003C ActiveRecord::Base\n  acts_as_paranoid column: :destroyed_at\n\n  ...\nend\n```\n\n\nIf you want to skip adding the default scope:\n\n``` ruby\nclass Client \u003C ActiveRecord::Base\n  acts_as_paranoid without_default_scope: true\n\n  ...\nend\n```\n\nIf you want to access soft-deleted associations, override the getter method:\n\n``` ruby\ndef product\n  Product.unscoped { super }\nend\n```\n\nIf you want to include associated soft-deleted objects, you can (un)scope the association:\n\n``` ruby\nclass Person \u003C ActiveRecord::Base\n  belongs_to :group, -> { with_deleted }\nend\n\nPerson.includes(:group).all\n```\n\nIf you want to find all records, even those which are deleted:\n\n``` ruby\nClient.with_deleted\n```\n\nIf you want to exclude deleted records, when not able to use the default_scope (e.g. when using without_default_scope):\n\n``` ruby\nClient.without_deleted\n```\n\nIf you want to find only the deleted records:\n\n``` ruby\nClient.only_deleted\n```\n\nIf you want to check if a record is soft-deleted:\n\n``` ruby\nclient.paranoia_destroyed?\n# or\nclient.deleted?\n```\n\nIf you want to restore a record:\n\n``` ruby\nClient.restore(id)\n# or\nclient.restore\n```\n\nIf you want to restore a whole bunch of records:\n\n``` ruby\nClient.restore([id1, id2, ..., idN])\n```\n\nIf you want to restore a record and their dependently destroyed associated records:\n\n``` ruby\nClient.restore(id, :recursive => true)\n# or\nclient.restore(:recursive => true)\n```\n\nIf you want to restore a record and only those dependently destroyed associated records that were deleted within 2 minutes of the object upon which they depend:\n\n``` ruby\nClient.restore(id, :recursive => true, :recovery_window => 2.minutes)\n# or\nclient.restore(:recursive => true, :recovery_window => 2.minutes)\n```\n\nIf you want to trigger an after_commit callback when restoring a record:\n\n``` ruby\nclass Client \u003C ActiveRecord::Base\n  acts_as_paranoid after_restore_commit: true\n\n  after_commit          :commit_called, on: :restore\n  # or\n  after_restore_commit  :commit_called\n  ...\nend\n```\n\nNote that by default paranoia will not prevent that a soft destroyed object can't be associated with another object of a different model.\nA Rails validator is provided should you require this functionality:\n  ``` ruby\nvalidates :some_assocation, association_not_soft_destroyed: true\n```\nThis validator makes sure that `some_assocation` is not soft destroyed. If the object is soft destroyed the main object is rendered invalid and an validation error is added.\n\nFor more information, please look at the tests.\n\n#### About indexes:\n\nBeware that you should adapt all your indexes for them to work as fast as previously.\nFor example,\n\n``` ruby\nadd_index :clients, :group_id\nadd_index :clients, [:group_id, :other_id]\n```\n\nshould be replaced with\n\n``` ruby\nadd_index :clients, :group_id, where: \"deleted_at IS NULL\"\nadd_index :clients, [:group_id, :other_id], where: \"deleted_at IS NULL\"\n```\n\nOf course, this is not necessary for the indexes you always use in association with `with_deleted` or `only_deleted`.\n\n##### Unique Indexes\n\nBecause NULL != NULL in standard SQL, we can not simply create a unique index\non the deleted_at column and expect it to enforce that there only be one record\nwith a certain combination of values.\n\nIf your database supports them, good alternatives include partial indexes\n(above) and indexes on computed columns. E.g.\n\n``` ruby\nadd_index :clients, [:group_id, 'COALESCE(deleted_at, false)'], unique: true\n```\n\nIf not, an alternative is to create a separate column which is maintained\nalongside deleted_at for the sake of enforcing uniqueness. To that end,\nparanoia makes use of two method to make its destroy and restore actions:\nparanoia_restore_attributes and paranoia_destroy_attributes.\n\n``` ruby\nadd_column :clients, :active, :boolean\nadd_index :clients, [:group_id, :active], unique: true\n\nclass Client \u003C ActiveRecord::Base\n  # optionally have paranoia make use of your unique column, so that\n  # your lookups will benefit from the unique index\n  acts_as_paranoid column: :active, sentinel_value: true\n\n  def paranoia_restore_attributes\n    {\n      deleted_at: nil,\n      active: true\n    }\n  end\n\n  def paranoia_destroy_attributes\n    {\n      deleted_at: current_time_from_proper_timezone,\n      active: nil\n    }\n  end\nend\n```\n\n##### Destroying through association callbacks\n\nWhen dealing with `dependent: :destroy` associations and `acts_as_paranoid`, it's important to remember that whatever method is called on the parent model will be called on the child model. For example, given both models of an association have `acts_as_paranoid` defined:\n\n``` ruby\nclass Client \u003C ActiveRecord::Base\n  acts_as_paranoid\n\n  has_many :emails, dependent: :destroy\nend\n\nclass Email \u003C ActiveRecord::Base\n  acts_as_paranoid\n\n  belongs_to :client\nend\n```\n\nWhen we call `destroy` on the parent `client`, it will call `destroy` on all of its associated children `emails`:\n\n``` ruby\n>> client.emails.count\n# => 5\n>> client.destroy\n# => client\n>> client.deleted_at\n# => [current timestamp]\n>> Email.where(client_id: client.id).count\n# => 0\n>> Email.with_deleted.where(client_id: client.id).count\n# => 5\n```\n\nSimilarly, when we call `really_destroy!` on the parent `client`, then each child `email` will also have `really_destroy!` called:\n\n``` ruby\n>> client.emails.count\n# => 5\n>> client.id\n# => 12345\n>> client.really_destroy!\n# => client\n>> Client.find 12345\n# => ActiveRecord::RecordNotFound\n>> Email.with_deleted.where(client_id: client.id).count\n# => 0\n```\n\nHowever, if the child model `Email` does not have `acts_as_paranoid` set, then calling `destroy` on the parent `client` will also call `destroy` on each child `email`, thereby actually destroying them:\n\n``` ruby\nclass Client \u003C ActiveRecord::Base\n  acts_as_paranoid\n\n  has_many :emails, dependent: :destroy\nend\n\nclass Email \u003C ActiveRecord::Base\n  belongs_to :client\nend\n\n>> client.emails.count\n# => 5\n>> client.destroy\n# => client\n>> Email.where(client_id: client.id).count\n# => 0\n>> Email.with_deleted.where(client_id: client.id).count\n# => NoMethodError: undefined method `with_deleted' for #\u003CClass:0x0123456>\n```\n\n#### delete_all:\n\nThe gem supports `delete_all` method, however it is disabled by default, to enable it add this in your `environment` file\n\n``` ruby\nParanoia.delete_all_enabled = true\n```\nalternatively, you can enable\u002Fdisable it for specific models as follow:\n\n``` ruby\nclass User \u003C ActiveRecord::Base\n  acts_as_paranoid(delete_all_enabled: true)\nend\n```\n\n## Acts As Paranoid Migration\n\nYou can replace the older `acts_as_paranoid` methods as follows:\n\n| Old Syntax                 | New Syntax                     |\n|:-------------------------- |:------------------------------ |\n|`find_with_deleted(:all)`   | `Client.with_deleted`          |\n|`find_with_deleted(:first)` | `Client.with_deleted.first`    |\n|`find_with_deleted(id)`     | `Client.with_deleted.find(id)` |\n\n\nThe `recover` method in `acts_as_paranoid` runs `update` callbacks.  Paranoia's\n`restore` method does not do this.\n\n## Callbacks\n\nParanoia provides several callbacks. It triggers `destroy` callback when the record is marked as deleted and `real_destroy` when the record is completely removed from database. It also calls `restore` callback when the record is restored via paranoia\n\nFor example if you want to index your records in some search engine you can go like this:\n\n```ruby\nclass Product \u003C ActiveRecord::Base\n  acts_as_paranoid\n\n  after_destroy      :update_document_in_search_engine\n  after_restore      :update_document_in_search_engine\n  after_real_destroy :remove_document_from_search_engine\nend\n```\n\nYou can use these events just like regular Rails callbacks with before, after and around hooks.\n\n## License\n\nThis gem is released under the MIT license.\n","Paranoia 是一个为 Rails 5、6 和 7 设计的软删除插件，它通过设置 `deleted_at` 字段来隐藏而不是真正删除数据库记录。其核心功能是重写 ActiveRecord 的 `destroy` 方法，使其在执行时仅标记记录为已删除而非物理删除，并且所有查询默认排除已标记为删除的记录；如果需要彻底删除记录，可以调用 `really_destroy!` 方法。尽管项目维护者不再推荐在新项目中使用 Paranoia 并停止接受新特性请求，但仍然会进行必要的 bug 修复和支持新版 Rails。此工具适用于需要实现数据软删除功能的 Rails 应用场景，特别是那些对数据完整性和可恢复性有较高要求的应用。",2,"2026-06-11 03:14:59","top_language"]