{"id":14955229,"url":"https://github.com/rails/rails-observers","last_synced_at":"2025-04-13T18:30:08.474Z","repository":{"id":5701022,"uuid":"6911642","full_name":"rails/rails-observers","owner":"rails","description":"Rails observer (removed from core in Rails 4.0)","archived":false,"fork":false,"pushed_at":"2023-07-10T13:34:01.000Z","size":124,"stargazers_count":523,"open_issues_count":23,"forks_count":120,"subscribers_count":26,"default_branch":"master","last_synced_at":"2025-04-06T15:05:50.008Z","etag":null,"topics":[],"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/rails.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}},"created_at":"2012-11-28T23:01:24.000Z","updated_at":"2025-02-15T15:29:50.000Z","dependencies_parsed_at":"2024-06-18T12:20:25.845Z","dependency_job_id":"c7302532-1038-41ec-b2c7-17f87285a996","html_url":"https://github.com/rails/rails-observers","commit_stats":{"total_commits":128,"total_committers":29,"mean_commits":4.413793103448276,"dds":0.703125,"last_synced_commit":"389b577322d8b17336730b2dc4e179060a23c8e7"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rails%2Frails-observers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rails%2Frails-observers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rails%2Frails-observers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rails%2Frails-observers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rails","download_url":"https://codeload.github.com/rails/rails-observers/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248760185,"owners_count":21157302,"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":[],"created_at":"2024-09-24T13:10:42.335Z","updated_at":"2025-04-13T18:30:08.433Z","avatar_url":"https://github.com/rails.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"[![Build Status](https://secure.travis-ci.org/rails/rails-observers.png)](https://travis-ci.org/rails/rails-observers)\n# Rails::Observers\n\nRails Observers (removed from core in Rails 4.0)\n\n## Installation\n\nAdd this line to your application's Gemfile:\n```ruby\ngem 'rails-observers'\n```\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install rails-observers\n\n## Usage\n\nThis gem contains two observers:\n\n* Active Record Observer\n* Action Controller Sweeper\n\n### Active Record Observer\n\nObserver classes respond to life cycle callbacks to implement trigger-like\nbehavior outside the original class. This is a great way to reduce the\nclutter that normally comes when the model class is burdened with\nfunctionality that doesn't pertain to the core responsibility of the\nclass. Observers are put in `app/models` (e.g.\n`app/models/comment_observer.rb`). Example:\n\n```ruby\nclass CommentObserver \u003c ActiveRecord::Observer\n  def after_save(comment)\n    Notifications.comment(\"admin@do.com\", \"New comment was posted\", comment).deliver\n  end\nend\n```\n\nThis Observer sends an email when a Comment#save is finished.\n\n```ruby\nclass ContactObserver \u003c ActiveRecord::Observer\n  def after_create(contact)\n    contact.logger.info('New contact added!')\n  end\n\n  def after_destroy(contact)\n    contact.logger.warn(\"Contact with an id of #{contact.id} was destroyed!\")\n  end\nend\n```\n\nThis Observer uses logger to log when specific callbacks are triggered.\n\nThe convention is to name observers after the class they observe. If you\nabsolutely need to override this, or want to use one observer for several\nclasses, use `observe`:\n\n```ruby\nclass NotificationsObserver \u003c ActiveRecord::Observer\n  observe :comment, :like\n\n  def after_create(record)\n    # notifiy users of new comment or like\n  end\n\nend\n```\n\nPlease note that observers are called in the order that they are defined. This means that callbacks in an observer\nwill always be called *after* callbacks defined in the model itself. Likewise, `has_one` and `has_many`\nuse callbacks to enforce `dependent: :destroy`. Therefore, associated records will be destroyed before\nthe observer's `before_destroy` is called.\n\nFor an observer to be active, it must be registered first. This can be done by adding the following line into the `application.rb`:\n\n```\n    config.active_record.observers = :contact_observer\n```\n\nEnable multiple observers using array:\n\n```\n    config.active_record.observers = [:contact_observer, :user_observer]\n```\n\nObservers can also be registered on an environment-specific basis by simply using the corresponding environment's configuration file instead of `application.rb`.\n\n### Action Controller Sweeper\n\nSweepers are the terminators of the caching world and responsible for expiring caches when model objects change.\nThey do this by being half-observers, half-filters and implementing callbacks for both roles. A Sweeper example:\n\n```ruby\nclass ListSweeper \u003c ActionController::Caching::Sweeper\n  observe List, Item\n\n  def after_save(record)\n    list = record.is_a?(List) ? record : record.list\n    expire_page(controller: \"lists\", action: %w( show public feed ), id: list.id)\n    expire_action(controller: \"lists\", action: \"all\")\n    list.shares.each { |share| expire_page(controller: \"lists\", action: \"show\", id: share.url_key) }\n  end\nend\n```\n\nThe sweeper is assigned in the controllers that wish to have its job performed using the `cache_sweeper` class method:\n\n```ruby\nclass ListsController \u003c ApplicationController\n  caches_action :index, :show, :public, :feed\n  cache_sweeper :list_sweeper, only: [ :edit, :destroy, :share ]\nend\n```\n\nIn the example above, four actions are cached and three actions are responsible for expiring those caches.\n\nYou can also name an explicit class in the declaration of a sweeper, which is needed if the sweeper is in a module:\n\n```ruby\nclass ListsController \u003c ApplicationController\n  caches_action :index, :show, :public, :feed\n  cache_sweeper OpenBar::Sweeper, only: [ :edit, :destroy, :share ]\nend\n```\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 new Pull Request\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frails%2Frails-observers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frails%2Frails-observers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frails%2Frails-observers/lists"}