[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.{json,yml}]\nindent_size = 2\n\n[*.{diff,md}]\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": ".github/workflows/main.yml",
    "content": "name: CI\non:\n  pull_request:\n    paths-ignore:\n      - 'README.md'\n  push:\n    paths-ignore:\n      - 'README.md'\n  workflow_dispatch:\njobs:\n  test:\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        ruby_version: ['3.0', '3.1', '3.2', '3.3']\n        rails_version: ['6.1', '7.0', '7.1']\n        exclude:\n          - ruby_version: 3.1\n            rails_version: 6.1\n          - ruby_version: 3.2\n            rails_version: 6.1\n          - ruby_version: 3.3\n            rails_version: 6.1\n    steps:\n      - uses: actions/checkout@v4\n      - name: Setup Ruby\n        uses: ruby/setup-ruby@v1\n        with:\n          ruby-version: ${{ matrix.ruby_version }}\n      - name: Build and run test\n        run: |\n          bundle\n          bundle exec appraisal rails_${{ matrix.rails_version }} bundle\n          bundle exec appraisal rails_${{ matrix.rails_version }} rake\n"
  },
  {
    "path": ".gitignore",
    "content": "/doc/\n/.yardoc/\n*.gem\n/coverage/\n/*.sublime-workspace\n/tmp\n/Gemfile.lock\n/gemfiles/*.lock"
  },
  {
    "path": ".travis.yml",
    "content": "language: ruby\nrvm:\n  - 2.2.6\n  - 2.3.3\nmatrix:\n  include:\n    - rvm: 2.2.6\n      gemfile: gemfiles/Gemfile.rails-4.0\n      env: PA_ORM=active_record\n    - rvm: 2.3.3\n      gemfile: gemfiles/Gemfile.rails-4.0\n      env: PA_ORM=active_record\n    - rvm: 2.3.3\n      gemfile: gemfiles/Gemfile.rails-5.0\n      env: PA_ORM=active_record\n    - rvm: 2.5.1\n      gemfile: gemfiles/Gemfile.rails-5.2\n      env: PA_ORM=active_record\n    - rvm: 2.2.6\n      env: PA_ORM=mongoid\nenv:\n  - PA_ORM=active_record\n  - PA_ORM=mongo_mapper\nservices:\n  - mongodb\nemail:\n  recipients:\n    - piotrek@okonski.org\n  on_success: change\n  on_failure: always\n"
  },
  {
    "path": "Appraisals",
    "content": "# frozen_string_literal: true\n\nif RUBY_VERSION.to_f < 3.1\n  appraise 'rails_6.1' do\n    gem 'rails', '~> 6.1.0'\n    gem 'openssl'\n  end\nend\n\nappraise 'rails_7.0' do\n  gem 'rails', '~> 7.0.1'\nend\n\nappraise 'rails_7.1' do\n  gem 'rails', '~> 7.1.0'\nend\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n## 3.0.2\n\n- **Fixed** Refactor prepare_parameters method to handle nil parameters (s. #387, thanks [Himalaya Pal](https://github.com/palhimalaya))\n- **Fixed** CI failure due to lax sqlite3 version constraint (s. #389, thanks [Junichi Sato](https://github.com/sato11))\n\n## 3.0.1\n\n- **Fixed** Rails 6.1/7.0 regression in serialization of `nil`/`NULL` values\n- **Fixed** Docs for `ORM::ActiveRecord::Activist`\n\n## 3.0.0\n\n- **Added** Rails 7.1 support (s. #384, thanks [max.jos](https://github.com/yhru))\n- **Removed** Ruby <= 2.7 support\n- **Removed** Rails <= 6.0 support\n\n## 2.0.2\n\n- **Fixed** Rescue from `ActiveRecord::ConnectionNotEstablished` (s. #372, thanks [Gabe Blair](https://github.com/gblair) & [Vitalie Lazu](https://github.com/vitaliel))\n\n## 2.0.1\n\n- **Fixed** Fix regression in generated migration (s. #368, thanks [Colin Bonner](https://github.com/cfbonner))\n\n## 2.0.0\n\n- **Fixed** Deprecation warnings in Ruby 2.7/3.0 due to double splat operator\n- **Fixed** Ruby warnings due to unused variables `e` in exception handling\n- **Added** Ruby 3.1 support\n- **Added** Rails 7.0 support\n- **Removed** Ruby <= 2.4 support\n- **Removed** Rails <= 4.2 support\n\n## 1.6.4\n\n- **Fixed** exception when not using MySQL or Postgres (see #335, thanks to [Roland Netzsch](https://github.com/stuxcrystal))\n- **Added** `create_activity!` method which raises exception on failures, much like `save!` in ActiveRecord (see #334, thanks to [Jonathan](https://github.com/jtwhittington))\n- **Added** support for ActiveRecord 6 by whitelisting it (see #332, thanks to [Emre Demir](https://github.com/demir))\n- **Added** frozen_string_literal pragma to Ruby files for better performance (see #329, thanks to [Krzysztof Rybka](https://github.com/krzysiek1507))\n\n## 1.6.3\n\n- **Fixed** a bug which resulted in crashes when PostgreSQL connection failed (see #328, thanks to [Ken Greeff](https://github.com/kengreeff))\n- **Fixed** a bug which resulted in crashes when MySQL connection failed (see #327, thanks to [Miquel Sabaté Solà](https://github.com/mssola))\n\n## 1.6.2\n\n- **Fixed** a bug which resulted in crashes when database didn't exist (see #323, thanks to [Anmol Chopra](https://github.com/chopraanmol1))\n\n## 1.6.1\n\n- **Fixed** a bug with requiring not existent file in generated migrations\n\n## 1.6.0\n\n* **Add support for Rails 5.2**\n* Make config settings thread safe (fixes #284)\n* Fix Rspec tests failing in Rails 5 due to ViewHelpers (see #271, thanks to [Janusz](https://github.com/januszm))\n* Support JSON, JSONB and HSTORE types for `parameters` column (see #285, thanks to [djvs](https://github.com/djvs) and #315 (thanks to [mateusg](https://github.com/mateusg))\n\n## 1.5.0\n\n* Refactor PublicActivity::StoreController to rely on Thread.current instead. (see #252, thanks to [Ryan McGeary](https://github.com/rmm5t))\n* Remove support for Ruby 1.9.3 and Ruby 2.0.0 (thanks to [Ryan McGeary](https://github.com/rmm5t))\n\n## 1.4.2\n\n* Fix bug with migrations not having an extension in ActiveRecord >= 4.0.0\n\n## 1.4.1\n\n* Fixed issue with Rails 4 when using ProtectedAttributes gem (see #128)\n* General code clean-ups.\n\n## 1.4.0\n\n* Added support for MongoMapper ORM (thanks to [Julio Olivera](https://github.com/julioolvr)) [PR](https://github.com/pokonski/public_activity/pull/101)\n* Added support for stable **Rails 4.0** while keeping compatibility with Rails 3.X\n* `render_activity` can now render collections of activities instead of just a single one. Also aliased as `render_activities`\n* Fix issue in rendering multiple activities when options were incomplete for every subsequent activity after the first one\n* `render_activity` now accetps `:locals` option. Works the same way as `:locals` for Rails `render` method.\n\n## 1.1.0\n\n* Fixed an issue when AR was loading despite choosing Mongoid in multi-ORM Rails applications (thanks to [Robert Ulejczyk](https://github.com/robuye))\n\n## 1.0.3\n\n* Fixed a bug which modified globals (thanks to [Weera Wu](https://github.com/wulab))\n\n## 1.0.2\n\n* Fixed undefined constant PublicActivity::Activity for Activist associations (thanks to [Стас Сушков](https://github.com/stas))\n\n## 1.0.1\n\n* #create_activity now correctly returns activity object.\n* Fixed :owner not being set correctly when passed to #create_activity (thanks to [Drew Miller](https://github.com/mewdriller))\n\n## 1.0 (released 10/02/2013)\n\n* **Now supports Mongoid 3 and Active Record.**\n* Added indexes for polymorphic column pairs to speed up queries in ActiveRecord\n* `#create_activity` now returns the newly created Activity object\n* Support for custom Activity attributes. Now if you need a custom relation for Activities you can\n  create a migration which adds the desired column, whitelist the attribute, and then you can simply pass the value to #create_activity\n* `#tracked` can now accept a single Symbol for its `:only` and `:except` options.\n* It is now possible to include `PublicActivity::Common` in your models if you just want to use `#create_activity` method\n  and skip the default CRUD tracking.\n* `#render_activity` now accepts Symbols or Strings for :layout parameter.\n  ### Example\n\n  ```ruby\n  # All look for app/views/layouts/_activity.erb\n  render_activity @activity, :layout => \"activity\"\n  render_activity @activity, :layout => \"layouts/activity\"\n  render_activity @activity, :layout => :activity\n  ```\n## 0.5.4\n\n* Fixed support for namespaced classes when transforming into view path.\n\n  For example `MyNamespace::CamelCase` now correctly transforms to key: `my_namespace_camel_case.create`\n"
  },
  {
    "path": "Gemfile",
    "content": "# frozen_string_literal: true\n\nsource \"https://rubygems.org\"\ngemspec\n"
  },
  {
    "path": "MIT-LICENSE",
    "content": "Copyright (c) 2011-2013 Piotrek Okoński\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# PublicActivity [![Code Climate](https://codeclimate.com/github/chaps-io/public_activity.svg)](https://codeclimate.com/github/chaps-io/public_activity) [![Gem Version](https://badge.fury.io/rb/public_activity.svg)](http://badge.fury.io/rb/public_activity)\n\n`public_activity` provides easy activity tracking for your **ActiveRecord**, **Mongoid 3** and **MongoMapper** models\nin Rails 6.1+. Simply put: it records what has been changed or created and gives you the ability to present those\nrecorded activities to users - similarly to how GitHub does it.\n\n## Table of contents\n\n- [Ruby/Rails version support](#ruby-rails-version-support)\n- [Table of contents](#table-of-contents)\n- [Example](#example)\n  - [Online demo](#online-demo)\n- [Screencast](#screencast)\n- [Setup](#setup)\n  - [Gem installation](#gem-installation)\n  - [Database setup](#database-setup)\n  - [Model configuration](#model-configuration)\n    - [Custom activities](#custom-activities)\n  - [Displaying activities](#displaying-activities)\n    - [Layouts](#layouts)\n    - [Locals](#locals)\n    - [Activity views](#activity-views)\n    - [I18n](#I18n)\n- [Testing](#testing)\n- [Documentation](#documentation)\n- [Common examples](#common-examples)\n- [Help](#help)\n- [License](#license)\n\n## Ruby/Rails version support\n\nVersion `~> 3.0`` supports Ruby 3.0+ and Rails 6.1+. For older Ruby versions\n(≤2.7) and Rails 5.0+ you can use version `~> 2.0` until you can upgrade to\nnewer versions of Ruby + Rails.\n\nIssues related to those unsupported versions of Ruby/Rails will be closed\nwithout resolution and PRs will not be accepted.\n\n## Example\n\nHere is a simple example showing what this gem is about:\n\n![Example usage](http://i.imgur.com/q0TVx.png)\n\n### Demo app\n\nThe source code of the demo is hosted here: https://github.com/pokonski/activity_blog\n\n## Screencast\n\nRyan Bates made a [great screencast](http://railscasts.com/episodes/406-public-activity) describing how to integrate Public Activity in your Rails Application.\n\n## Setup\n\n### Gem installation\n\nYou can install `public_activity` as you would any other gem:\n\n    gem install public_activity\n\nor in your Gemfile:\n\n```ruby\ngem 'public_activity'\n```\n\n### Database setup\n\nBy default _public_activity_ uses Active Record. If you want to use Mongoid or MongoMapper as your backend, create\nan initializer file in your Rails application with the corresponding code inside:\n\nFor _Mongoid:_\n\n```ruby\n# config/initializers/public_activity.rb\nPublicActivity::Config.set do\n  orm :mongoid\nend\n```\n\nFor _MongoMapper:_\n\n```ruby\n# config/initializers/public_activity.rb\nPublicActivity::Config.set do\n  orm :mongo_mapper\nend\n```\n\n**(ActiveRecord only)** Create migration for activities and migrate the database (in your Rails project):\n\n    rails g public_activity:migration\n    rake db:migrate\n\n### Model configuration\n\nInclude `PublicActivity::Model` and add `tracked` to the model you want to keep track of:\n\nFor _ActiveRecord:_\n\n```ruby\nclass Article < ActiveRecord::Base\n  include PublicActivity::Model\n  tracked\nend\n```\n\nFor _Mongoid:_\n\n```ruby\nclass Article\n  include Mongoid::Document\n  include PublicActivity::Model\n  tracked\nend\n```\n\nFor _MongoMapper:_\n\n```ruby\nclass Article\n  include MongoMapper::Document\n  include PublicActivity::Model\n  tracked\nend\n```\n\nAnd now, by default create/update/destroy activities are recorded in activities table.\nThis is all you need to start recording activities for basic CRUD actions.\n\n_Optional_: If you don't need `#tracked` but still want the comfort of `#create_activity`,\nyou can include only the lightweight `Common` module instead of `Model`.\n\n#### Custom activities\n\nYou can trigger custom activities by setting all your required parameters and triggering `create_activity`\non the tracked model, like this:\n\n```ruby\n@article.create_activity key: 'article.commented_on', owner: current_user\n```\n\nSee this entry http://rubydoc.info/gems/public_activity/PublicActivity/Common:create_activity for more details.\n\n### Displaying activities\n\nTo display them you simply query the `PublicActivity::Activity` model:\n\n```ruby\n# notifications_controller.rb\ndef index\n  @activities = PublicActivity::Activity.all\nend\n```\n\nAnd in your views:\n\n```erb\n<%= render_activities(@activities) %>\n```\n\n*Note*: `render_activity` is a helper for use in view templates. `render_activity(activity)` can be written as `activity.render(self)` and it will have the same meaning.\n\n*Note*: `render_activities` is an alias for `render_activity` and does the same.\n\n#### Layouts\n\nYou can also pass options to both `activity#render` and `#render_activity` methods, which are passed deeper\nto the internally used `render_partial` method.\nA useful example would be to render activities wrapped in layout, which shares common elements of an activity,\nlike a timestamp, owner's avatar etc:\n\n```erb\n<%= render_activities(@activities, layout: :activity) %>\n```\n\nThe activity will be wrapped with the `app/views/layouts/_activity.erb` layout, in the above example.\n\n**Important**: please note that layouts for activities are also partials. Hence the `_` prefix.\n\n#### Locals\n\nSometimes, it's desirable to pass additional local variables to partials. It can be done this way:\n\n```erb\n<%= render_activity(@activity, locals: {friends: current_user.friends}) %>\n```\n\n*Note*: Before 1.4.0, one could pass variables directly to the options hash for `#render_activity` and access it from activity parameters. This functionality is retained in 1.4.0 and later, but the `:locals` method is **preferred**, since it prevents bugs from shadowing variables from activity parameters in the database.\n\n#### Activity views\n\n`public_activity` looks for views in `app/views/public_activity`.\n\nFor example, if you have an activity with `:key` set to `\"activity.user.changed_avatar\"`, the gem will look for a partial in `app/views/public_activity/user/_changed_avatar.(erb|haml|slim|something_else)`.\n\n*Hint*: the `\"activity.\"` prefix in `:key` is completely optional and kept for backwards compatibility, you can skip it in new projects.\n\nIf a view file does not exist, then p_a falls back to the old behaviour and tries to translate the activity `:key` using `I18n#translate` method (see the section below).\n\n#### I18n\n\nTranslations are used by the `#text` method, to which you can pass additional options in form of a hash. `#render` method uses translations when view templates have not been provided. You can render pure i18n strings by passing `{display: :i18n}` to `#render_activity` or `#render`.\n\nTranslations should be put in your locale `.yml` files. To render pure strings from I18n Example structure:\n\n```yaml\nactivity:\n  article:\n    create: 'Article has been created'\n    update: 'Someone has edited the article'\n    destroy: 'Some user removed an article!'\n```\n\nThis structure is valid for activities with keys `\"activity.article.create\"` or `\"article.create\"`. As mentioned before, `\"activity.\"` part of the key is optional.\n\n## Testing\n\nFor RSpec you can first disable `public_activity` and add the `test_helper` in `rails_helper.rb` with:\n\n```ruby\n#rails_helper.rb\nrequire 'public_activity/testing'\n\nPublicActivity.enabled = false\n```\n\nIn your specs you can then blockwise decide whether to turn `public_activity` on\nor off.\n\n```ruby\n# file_spec.rb\nPublicActivity.with_tracking do\n  # your test code goes here\nend\n\nPublicActivity.without_tracking do\n  # your test code goes here\nend\n```\n\n## Documentation\n\nFor more documentation go [here](http://rubydoc.info/gems/public_activity/index)\n\n## Common examples\n\n* [[How to] Set the Activity's owner to current_user by default](https://github.com/pokonski/public_activity/wiki/%5BHow-to%5D-Set-the-Activity's-owner-to-current_user-by-default)\n* [[How to] Disable tracking for a class or globally](https://github.com/pokonski/public_activity/wiki/%5BHow-to%5D-Disable-tracking-for-a-class-or-globally)\n* [[How to] Create custom activities](https://github.com/pokonski/public_activity/wiki/%5BHow-to%5D-Create-custom-activities)\n* [[How to] Use custom fields on Activity](https://github.com/pokonski/public_activity/wiki/%5BHow-to%5D-Use-custom-fields-on-Activity)\n\n## Help\n\nIf you need help with using public_activity please visit our discussion group and ask a question there:\n\nhttps://groups.google.com/forum/?fromgroups#!forum/public-activity\n\nPlease do not ask general questions in the Github Issues.\n\n## License\nCopyright (c) 2011-2013 Piotrek Okoński, released under the MIT license\n"
  },
  {
    "path": "Rakefile",
    "content": "# frozen_string_literal: true\n\nrequire 'bundler/gem_tasks'\nrequire 'rake'\nrequire 'yard'\nrequire 'yard/rake/yardoc_task'\nrequire 'rake/testtask'\n\ntask default: :test\n\ndesc 'Generate documentation for the public_activity plugin.'\nYARD::Rake::YardocTask.new do |doc|\n  doc.files = ['lib/**/*.rb']\nend\n\nRake::TestTask.new do |t|\n  t.libs << 'test'\n  t.test_files = FileList['test/test*.rb']\nend\n"
  },
  {
    "path": "gemfiles/.bundle/config",
    "content": "---\nBUNDLE_RETRY: \"1\"\n"
  },
  {
    "path": "gemfiles/rails_6.1.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"rails\", \"~> 6.1.0\"\ngem \"openssl\"\n\ngemspec path: \"../\"\n"
  },
  {
    "path": "gemfiles/rails_7.0.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"rails\", \"~> 7.0.1\"\n\ngemspec path: \"../\"\n"
  },
  {
    "path": "gemfiles/rails_7.1.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"rails\", \"~> 7.1.0\"\n\ngemspec path: \"../\"\n"
  },
  {
    "path": "lib/generators/public_activity/migration/migration_generator.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'generators/public_activity'\nrequire 'rails/generators/active_record'\n\nmodule PublicActivity\n  module Generators\n    # Migration generator that creates migration file from template\n    class MigrationGenerator < ActiveRecord::Generators::Base\n      extend Base\n\n      argument :name, :type => :string, :default => 'create_activities'\n      # Create migration in project's folder\n      def generate_files\n        migration_template 'migration.rb', \"db/migrate/#{name}.rb\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/generators/public_activity/migration/templates/migration.rb",
    "content": "# frozen_string_literal: true\n\n# Migration responsible for creating a table with activities\nclass CreateActivities < ActiveRecord::Migration[6.1]\n  def self.up\n    create_table :activities do |t|\n      t.belongs_to :trackable, polymorphic: true\n      t.belongs_to :owner, polymorphic: true\n      t.string :key\n      t.text :parameters\n      t.belongs_to :recipient, polymorphic: true\n\n      t.timestamps\n    end\n  end\n\n  # Drop table\n  def self.down\n    drop_table :activities\n  end\nend\n"
  },
  {
    "path": "lib/generators/public_activity/migration_upgrade/migration_upgrade_generator.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'generators/public_activity'\nrequire 'rails/generators/active_record'\n\nmodule PublicActivity\n  module Generators\n    # Migration generator that creates migration file from template\n    class MigrationUpgradeGenerator < ActiveRecord::Generators::Base\n      extend Base\n\n      argument :name, :type => :string, :default => 'upgrade_activities'\n      # Create migration in project's folder\n      def generate_files\n        migration_template 'upgrade.rb', \"db/migrate/#{name}.rb\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/generators/public_activity/migration_upgrade/templates/upgrade.rb",
    "content": "# frozen_string_literal: true\n\n# Migration responsible for creating a table with activities\nclass UpgradeActivities < ActiveRecord::Migration[5.0]\n  # Create table\n  def self.change\n    change_table :activities do |t|\n      t.belongs_to :recipient, :polymorphic => true\n    end\n  end\nend\n"
  },
  {
    "path": "lib/generators/public_activity.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'rails/generators/named_base'\n\nmodule PublicActivity\n  # A generator module with Activity table schema.\n  module Generators\n    # A base module\n    module Base\n      # Get path for migration template\n      def source_root\n        @_public_activity_source_root ||= File.expand_path(File.join('../public_activity', generator_name, 'templates'), __FILE__)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/actions/creation.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  # Handles creation of Activities upon destruction and update of tracked model.\n  module Creation\n    extend ActiveSupport::Concern\n\n    included do\n      after_create :activity_on_create\n    end\n\n    private\n\n    # Creates activity upon creation of the tracked model\n    def activity_on_create\n      create_activity(:create)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/actions/destruction.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  # Handles creation of Activities upon destruction of tracked model.\n  module Destruction\n    extend ActiveSupport::Concern\n\n    included do\n      before_destroy :activity_on_destroy\n    end\n\n    private\n\n    # Records an activity upon destruction of the tracked model\n    def activity_on_destroy\n      create_activity(:destroy)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/actions/update.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  # Handles creation of Activities upon destruction and update of tracked model.\n  module Update\n    extend ActiveSupport::Concern\n\n    included do\n      after_update :activity_on_update\n    end\n\n    private\n\n    # Creates activity upon modification of the tracked model\n    def activity_on_update\n      # Either use #changed? method for Rails < 5.1 or #saved_changes? for recent versions\n      create_activity(:update) if respond_to?(:saved_changes?) ? saved_changes? : changed?\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/activity.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  # Main model, stores all information about what happened,\n  # who caused it, when and anything else.\n  class Activity < inherit_orm(\"Activity\")\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/common.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  # Happens when creating custom activities without either action or a key.\n  class NoKeyProvided < Exception; end\n\n  # Used to smartly transform value from metadata to data.\n  # Accepts Symbols, which it will send against context.\n  # Accepts Procs, which it will execute with controller and context.\n  # @since 0.4.0\n  def self.resolve_value(context, thing)\n    case thing\n    when Symbol\n      context.__send__(thing)\n    when Proc\n      thing.call(PublicActivity.get_controller, context)\n    else\n      thing\n    end\n  end\n\n  # Common methods shared across the gem.\n  module Common\n    extend ActiveSupport::Concern\n\n    included do\n      include Trackable\n      class_attribute :activity_owner_global, :activity_recipient_global,\n                      :activity_params_global, :activity_hooks, :activity_custom_fields_global\n      set_public_activity_class_defaults\n    end\n\n    # @!group Global options\n\n    # @!attribute activity_owner_global\n    #   Global version of activity owner\n    #   @see #activity_owner\n    #   @return [Model]\n\n    # @!attribute activity_recipient_global\n    #   Global version of activity recipient\n    #   @see #activity_recipient\n    #   @return [Model]\n\n    # @!attribute activity_params_global\n    #   Global version of activity parameters\n    #   @see #activity_params\n    #   @return [Hash<Symbol, Object>]\n\n    # @!attribute activity_hooks\n    #   @return [Hash<Symbol, Proc>]\n    #   Hooks/functions that will be used to decide *if* the activity should get\n    #   created.\n    #\n    #   The supported keys are:\n    #   * :create\n    #   * :update\n    #   * :destroy\n\n    # @!endgroup\n\n    # @!group Instance options\n\n    # Set or get parameters that will be passed to {Activity} when saving\n    #\n    # == Usage:\n    #\n    #   @article.activity_params = {:article_title => @article.title}\n    #   @article.save\n    #\n    # This way you can pass strings that should remain constant, even when model attributes\n    # change after creating this {Activity}.\n    # @return [Hash<Symbol, Object>]\n    attr_accessor :activity_params\n    @activity_params = {}\n    # Set or get owner object responsible for the {Activity}.\n    #\n    # == Usage:\n    #\n    #   # where current_user is an object of logged in user\n    #   @article.activity_owner = current_user\n    #   # OR: take @article.author association\n    #   @article.activity_owner = :author\n    #   # OR: provide a Proc with custom code\n    #   @article.activity_owner = proc {|controller, model| model.author }\n    #   @article.save\n    #   @article.activities.last.owner #=> Returns owner object\n    # @return [Model] Polymorphic model\n    # @see #activity_owner_global\n    attr_accessor :activity_owner\n    @activity_owner = nil\n\n    # Set or get recipient for activity.\n    #\n    # Association is polymorphic, thus allowing assignment of\n    # all types of models. This can be used for example in the case of sending\n    # private notifications for only a single user.\n    # @return (see #activity_owner)\n    attr_accessor :activity_recipient\n    @activity_recipient = nil\n    # Set or get custom i18n key passed to {Activity}, later used in {Renderable#text}\n    #\n    # == Usage:\n    #\n    #   @article = Article.new\n    #   @article.activity_key = \"my.custom.article.key\"\n    #   @article.save\n    #   @article.activities.last.key #=> \"my.custom.article.key\"\n    #\n    # @return [String]\n    attr_accessor :activity_key\n    @activity_key = nil\n\n    # Set or get custom fields for later processing\n    #\n    # @return [Hash]\n    attr_accessor :activity_custom_fields\n    @activity_custom_fields = {}\n\n    # @!visibility private\n    @@activity_hooks = {}\n\n    # @!endgroup\n\n    # Provides some global methods for every model class.\n    class_methods do\n      #\n      # @since 1.0.0\n      # @api private\n      def set_public_activity_class_defaults\n        self.activity_owner_global             = nil\n        self.activity_recipient_global         = nil\n        self.activity_params_global            = {}\n        self.activity_hooks                    = {}\n        self.activity_custom_fields_global     = {}\n      end\n\n      # Extracts a hook from the _:on_ option provided in\n      # {Tracked::ClassMethods#tracked}. Returns nil when no hook exists for\n      # given action\n      # {Common#get_hook}\n      #\n      # @see Tracked#get_hook\n      # @param key [String, Symbol] action to retrieve a hook for\n      # @return [Proc, nil] callable hook or nil\n      # @since 0.4.0\n      # @api private\n      def get_hook(key)\n        key = key.to_sym\n        if activity_hooks.key?(key) && activity_hooks[key].is_a?(Proc)\n          activity_hooks[key]\n        end\n      end\n    end\n    #\n    # Returns true if PublicActivity is enabled\n    # globally and for this class.\n    # @return [Boolean]\n    # @api private\n    # @since 0.5.0\n    def public_activity_enabled?\n      PublicActivity.enabled?\n    end\n    #\n    # Shortcut for {ClassMethods#get_hook}\n    # @param (see ClassMethods#get_hook)\n    # @return (see ClassMethods#get_hook)\n    # @since (see ClassMethods#get_hook)\n    # @api (see ClassMethods#get_hook)\n    def get_hook(key)\n      self.class.get_hook(key)\n    end\n\n    # Calls hook safely.\n    # If a hook for given action exists, calls it with model (self) and\n    # controller (if available, see {StoreController})\n    # @param key (see #get_hook)\n    # @return [Boolean] if hook exists, it's decision, if there's no hook, true\n    # @since 0.4.0\n    # @api private\n    def call_hook_safe(key)\n      hook = get_hook(key)\n      if hook\n        # provides hook with model and controller\n        hook.call(self, PublicActivity.get_controller)\n      else\n        true\n      end\n    end\n\n    # Directly creates activity record in the database, based on supplied options.\n    #\n    # It's meant for creating custom activities while *preserving* *all*\n    # *configuration* defined before. If you fire up the simplest of options:\n    #\n    #   current_user.create_activity(:avatar_changed)\n    #\n    # It will still gather data from any procs or symbols you passed as params\n    # to {Tracked::ClassMethods#tracked}. It will ask the hooks you defined\n    # whether to really save this activity.\n    #\n    # But you can also overwrite instance and global settings with your options:\n    #\n    #   @article.activity :owner => proc {|controller| controller.current_user }\n    #   @article.create_activity(:commented_on, :owner => @user)\n    #\n    # And it's smart! It won't execute your proc, since you've chosen to\n    # overwrite instance parameter _:owner_ with @user.\n    #\n    # [:key]\n    #   The key will be generated from either:\n    #   * the first parameter you pass that is not a hash (*action*)\n    #   * the _:action_ option in the options hash (*action*)\n    #   * the _:key_ option in the options hash (it has to be a full key,\n    #     including model name)\n    #   When you pass an *action* (first two options above), they will be\n    #   added to parameterized model name:\n    #\n    #   Given Article model and instance: @article,\n    #\n    #     @article.create_activity :commented_on\n    #     @article.activities.last.key # => \"article.commented_on\"\n    #\n    # For other parameters, see {Tracked#activity}, and \"Instance options\"\n    # accessors at {Tracked}, information on hooks is available at\n    # {Tracked::ClassMethods#tracked}.\n    # @see #prepare_settings\n    # @return [Model, nil] If created successfully, new activity\n    # @since 0.4.0\n    # @api public\n    # @overload create_activity(action, options = {})\n    #   @param [Symbol,String] action Name of the action\n    #   @param [Hash] options Options with quality higher than instance options\n    #     set in {Tracked#activity}\n    #   @option options [Activist] :owner Owner\n    #   @option options [Activist] :recipient Recipient\n    #   @option options [Hash] :params Parameters, see\n    #     {PublicActivity.resolve_value}\n    # @overload create_activity(options = {})\n    #   @param [Hash] options Options with quality higher than instance options\n    #     set in {Tracked#activity}\n    #   @option options [Symbol,String] :action Name of the action\n    #   @option options [String] :key Full key\n    #   @option options [Activist] :owner Owner\n    #   @option options [Activist] :recipient Recipient\n    #   @option options [Hash] :params Parameters, see\n    #     {PublicActivity.resolve_value}\n    def create_activity(*args)\n      return unless public_activity_enabled?\n\n      options = prepare_settings(*args)\n\n      if call_hook_safe(options[:key].split('.').last)\n        reset_activity_instance_options\n        return PublicActivity::Adapter.create_activity(self, options)\n      end\n\n      nil\n    end\n\n    # Directly saves activity to database. Works the same as create_activity\n    # but throws validation error for each supported ORM.\n    #\n    # @see #create_activity\n    def create_activity!(*args)\n      return unless public_activity_enabled?\n\n      options = prepare_settings(*args)\n\n      if call_hook_safe(options[:key].split('.').last)\n        reset_activity_instance_options\n        PublicActivity::Adapter.create_activity!(self, options)\n      end\n    end\n\n    # Prepares settings used during creation of Activity record.\n    # params passed directly to tracked model have priority over\n    # settings specified in tracked() method\n    #\n    # @see #create_activity\n    # @return [Hash] Settings with preserved options that were passed\n    # @api private\n    # @overload prepare_settings(action, options = {})\n    #   @see #create_activity\n    # @overload prepare_settings(options = {})\n    #   @see #create_activity\n    def prepare_settings(*args)\n      raw_options = args.extract_options!\n      action      = [args.first, raw_options.delete(:action)].compact.first\n      key         = prepare_key(action, raw_options)\n\n      raise NoKeyProvided, \"No key provided for #{self.class.name}\" unless key\n\n      prepare_custom_fields(raw_options.except(:params)).merge(\n        {\n          key:        key,\n          owner:      prepare_relation(:owner,     raw_options),\n          recipient:  prepare_relation(:recipient, raw_options),\n          parameters: prepare_parameters(raw_options),\n        }\n      )\n    end\n\n    # Prepares and resolves custom fields\n    # users can pass to `tracked` method\n    # @private\n    def prepare_custom_fields(options)\n      customs = self.class.activity_custom_fields_global.clone\n      customs.merge!(activity_custom_fields) if activity_custom_fields\n      customs.merge!(options)\n      customs.each do  |k, v|\n        customs[k] = PublicActivity.resolve_value(self, v)\n      end\n    end\n\n    # Prepares i18n parameters that will\n    # be serialized into the Activity#parameters column\n    # @private\n    def prepare_parameters(options)\n      params = {}\n      params.merge!(self.class.activity_params_global)\n      params.merge!(activity_params) if activity_params\n      params.merge!([options.delete(:parameters), options.delete(:params), {}].compact.first)\n      params.each { |k, v| params[k] = PublicActivity.resolve_value(self, v) }\n    end\n\n    # Prepares relation to be saved\n    # to Activity. Can be :recipient or :owner\n    # @private\n    def prepare_relation(name, options)\n      PublicActivity.resolve_value(self,\n        (options.key?(name) ? options[name] : (\n          self.send(\"activity_#{name}\") || self.class.send(\"activity_#{name}_global\")\n          )\n        )\n      )\n    end\n\n    # Helper method to serialize class name into relevant key\n    # @return [String] the resulted key\n    # @param [Symbol] or [String] the name of the operation to be done on class\n    # @param [Hash] options to be used on key generation, defaults to {}\n    def prepare_key(action, options = {})\n      (\n        options[:key] ||\n        activity_key ||\n        ((self.class.name.underscore.gsub('/', '_') + \".\" + action.to_s) if action)\n      ).try(:to_s)\n    end\n\n    # Resets all instance options on the object\n    # triggered by a successful #create_activity, should not be\n    # called from any other place, or from application code.\n    # @private\n    def reset_activity_instance_options\n      @activity_params = {}\n      @activity_key = nil\n      @activity_owner = nil\n      @activity_recipient = nil\n      @activity_custom_fields = {}\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/config.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'singleton'\n\nmodule PublicActivity\n  # Class used to initialize configuration object.\n  class Config\n    include ::Singleton\n\n    # Evaluates given block to provide DSL configuration.\n    # @example Initializer for Rails\n    #   PublicActivity::Config.set do\n    #     orm :mongo_mapper\n    #     enabled false\n    #     table_name \"activities\"\n    #   end\n    def self.set(&block)\n      b = Block.new\n      b.instance_eval(&block)\n      instance\n      orm(b.orm) unless b.orm.nil?\n      enabled(b.enabled) unless b.enabled.nil?\n      table_name(b.table_name) unless b.table_name.nil?\n    end\n\n    # alias for {#orm}\n    # @see #orm\n    def self.orm=(orm = nil)\n      orm(orm)\n    end\n\n    # alias for {#enabled}\n    # @see #enabled\n    def self.enabled=(value = nil)\n      enabled(value)\n    end\n\n    # instance version of {Config#orm}\n    # @see Config#orm\n    def orm(orm = nil)\n      self.class.orm(orm)\n    end\n\n    # instance version of {Config#table_name}\n    # @see Config#orm\n    def table_name(name = nil)\n      self.class.table_name(name)\n    end\n\n    # instance version of {Config#enabled}\n    # @see Config#orm\n    def enabled(value = nil)\n      self.class.enabled(value)\n    end\n\n    # Set the ORM for use by PublicActivity.\n    def self.orm(orm = nil)\n      if orm.nil?\n        Thread.current[:public_activity_orm] || :active_record\n      else\n        Thread.current[:public_activity_orm] = orm.to_sym\n      end\n    end\n\n    def self.table_name(name = nil)\n      if name.nil?\n        Thread.current[:public_activity_table_name] || \"activities\"\n      else\n        Thread.current[:public_activity_table_name] = name\n      end\n    end\n\n    def self.enabled(value = nil)\n      if value.nil?\n        val = Thread.current[:public_activity_enabled]\n        val.nil? ? true : val\n      else\n        Thread.current[:public_activity_enabled] = value\n      end\n    end\n\n    # Provides simple DSL for the config block.\n    class Block\n      # @see Config#orm\n      def orm(orm = nil)\n        @orm = (orm ? orm.to_sym : false) || @orm\n      end\n\n      # Decides whether to enable PublicActivity.\n      # @param en [Boolean] Enabled?\n      def enabled(value = nil)\n        @enabled = (value.nil? ? @enabled : value)\n      end\n\n      # Sets the table_name for the model\n      def table_name(name = nil)\n        @table_name = (name.nil? ? @table_name : name)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/models/activist.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  # Provides helper methods for selecting activities from a user.\n  module Activist\n    # Delegates to configured ORM.\n    def self.included(base)\n      base.extend PublicActivity.inherit_orm('Activist')\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/models/activity.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  class Activity < inherit_orm\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/models/adapter.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  # Loads database-specific routines for use by PublicActivity.\n  class Adapter < inherit_orm('Adapter')\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/models/trackable.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  # Provides association for activities bound to this object by *trackable*.\n  module Trackable\n    # Delegates to ORM.\n    def self.included(base)\n      base.extend PublicActivity.inherit_orm('Trackable')\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/orm/active_record/activist.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  module ORM\n    module ActiveRecord\n      # Module extending classes that serve as owners\n      module Activist\n        # Adds ActiveRecord associations to model to simplify fetching\n        # so you can list activities performed by the owner.\n        # It is completely optional. Any model can be an owner to an activity\n        # even without being an explicit activist.\n        #\n        # == Usage:\n        # In model:\n        #\n        #   class User < ActiveRecord::Base\n        #     include PublicActivity::Model\n        #     activist\n        #   end\n        #\n        # In controller:\n        #   User.first.activities_as_owner\n        #   User.first.activities_as_recipient\n        #\n        def activist\n          has_many :activities_as_owner,\n                   class_name: '::PublicActivity::Activity',\n                   as: :owner\n          has_many :activities_as_recipient,\n                   class_name: '::PublicActivity::Activity',\n                   as: :recipient\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/orm/active_record/activity.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  unless defined? ::PG::ConnectionBad\n    module ::PG\n      class ConnectionBad < Exception; end\n    end\n  end\n  unless defined? Mysql2::Error::ConnectionError\n    module Mysql2\n      module Error\n        class ConnectionError < Exception; end\n      end\n    end\n  end\n\n  module ORM\n    module ActiveRecord\n      # The ActiveRecord model containing\n      # details about recorded activity.\n      class Activity < ::ActiveRecord::Base\n        include Renderable\n        self.table_name = PublicActivity.config.table_name\n        self.abstract_class = true\n\n        # Define polymorphic association to the parent\n        belongs_to :trackable, polymorphic: true\n\n        with_options(optional: true) do\n          # Define ownership to a resource responsible for this activity\n          belongs_to :owner, polymorphic: true\n          # Define ownership to a resource targeted by this activity\n          belongs_to :recipient, polymorphic: true\n        end\n\n        # Serialize parameters Hash\n        begin\n          if table_exists?\n            unless %i[json jsonb hstore].include?(columns_hash['parameters'].type)\n              if ::ActiveRecord.version.release < Gem::Version.new('7.1')\n                serialize :parameters, Hash\n              else\n                serialize :parameters, coder: YAML, type: Hash\n              end\n            end\n          else\n            warn(\"[WARN] table #{name} doesn't exist. Skipping PublicActivity::Activity#parameters's serialization\")\n          end\n        rescue ::ActiveRecord::NoDatabaseError\n          warn(\"[WARN] database doesn't exist. Skipping PublicActivity::Activity#parameters's serialization\")\n        rescue ::ActiveRecord::ConnectionNotEstablished, ::PG::ConnectionBad, Mysql2::Error::ConnectionError\n          warn(\"[WARN] couldn't connect to database. Skipping PublicActivity::Activity#parameters's serialization\")\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/orm/active_record/adapter.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  module ORM\n    # Support for ActiveRecord for PublicActivity. Used by default and supported\n    # officialy.\n    module ActiveRecord\n      # Provides ActiveRecord specific, database-related routines for use by\n      # PublicActivity.\n      class Adapter\n        # Creates the activity on `trackable` with `options`\n        def self.create_activity(trackable, options)\n          trackable.activities.create options\n        end\n\n        # Creates activity on `trackable` with `options`; throws error on validation failure\n        def self.create_activity!(trackable, options)\n          trackable.activities.create! options\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/orm/active_record/trackable.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  module ORM\n    module ActiveRecord\n      # Implements {PublicActivity::Trackable} for ActiveRecord\n      # @see PublicActivity::Trackable\n      module Trackable\n        # Creates an association for activities where self is the *trackable*\n        # object.\n        def self.extended(base)\n          base.has_many :activities,\n                        class_name: '::PublicActivity::Activity',\n                        as: :trackable\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/orm/active_record.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'active_record'\nrequire_relative 'active_record/activity'\nrequire_relative 'active_record/adapter'\nrequire_relative 'active_record/activist'\nrequire_relative 'active_record/trackable'\n"
  },
  {
    "path": "lib/public_activity/orm/mongo_mapper/activist.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  module ORM\n    module MongoMapper\n      # Module extending classes that serve as owners\n      module Activist\n        # Adds MongoMapper associations to model to simplify fetching\n        # so you can list activities performed by the owner.\n        # It is completely optional. Any model can be an owner to an activity\n        # even without being an explicit activist.\n        #\n        # == Usage:\n        # In model:\n        #\n        #   class User\n        #     include MongoMapper::Document\n        #     include PublicActivity::Model\n        #     activist\n        #   end\n        #\n        # In controller:\n        #   User.first.activities\n        #\n        def activist\n          many :activities_as_owner,\n            :class_name => \"::PublicActivity::Activity\",\n            :as => :owner\n          many :activities_as_recipient,\n            :class_name => \"::PublicActivity::Activity\",\n            :as => :recipient\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/orm/mongo_mapper/activity.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'mongo_mapper'\nrequire 'active_support/core_ext'\n\nmodule PublicActivity\n  module ORM\n    module MongoMapper\n      # The MongoMapper document containing\n      # details about recorded activity.\n      class Activity\n        include ::MongoMapper::Document\n        include Renderable\n\n        class SymbolHash < Hash\n          def self.from_mongo(value)\n            value.symbolize_keys unless value.nil?\n          end\n        end\n\n        # Define polymorphic association to the parent\n        belongs_to :trackable,  polymorphic: true\n        # Define ownership to a resource responsible for this activity\n        belongs_to :owner,      polymorphic: true\n        # Define ownership to a resource targeted by this activity\n        belongs_to :recipient,  polymorphic: true\n\n        key :key,         String\n        key :parameters,  SymbolHash\n\n        timestamps!\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/orm/mongo_mapper/adapter.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  module ORM\n    module MongoMapper\n      class Adapter\n        # Creates the activity on `trackable` with `options`\n        def self.create_activity(trackable, options)\n          trackable.activities.create options\n        end\n\n        # Creates activity on `trackable` with `options`; throws error on validation failure\n        def self.create_activity!(trackable, options)\n          trackable.activities.create! options\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/orm/mongo_mapper/trackable.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  module ORM\n    module MongoMapper\n      module Trackable\n        def self.extended(base)\n          base.many :activities, :class_name => \"::PublicActivity::Activity\", order: :created_at.asc, :as => :trackable\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/orm/mongo_mapper.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"mongo_mapper/activity.rb\"\nrequire_relative \"mongo_mapper/adapter.rb\"\nrequire_relative \"mongo_mapper/activist.rb\"\nrequire_relative \"mongo_mapper/trackable.rb\"\n"
  },
  {
    "path": "lib/public_activity/orm/mongoid/activist.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  module ORM\n    module Mongoid\n      # Module extending classes that serve as owners\n      module Activist\n        # Adds ActiveRecord associations to model to simplify fetching\n        # so you can list activities performed by the owner.\n        # It is completely optional. Any model can be an owner to an activity\n        # even without being an explicit activist.\n        #\n        # == Usage:\n        # In model:\n        #\n        #   class User < ActiveRecord::Base\n        #     include PublicActivity::Model\n        #     activist\n        #   end\n        #\n        # In controller:\n        #   User.first.activities\n        #\n        def activist\n          has_many :activities_as_owner,\n            :class_name => \"::PublicActivity::Activity\",\n            :inverse_of => :owner\n\n          has_many :activities_as_recipient,\n            :class_name => \"::PublicActivity::Activity\",\n            :inverse_of => :recipient\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/orm/mongoid/activity.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'mongoid'\n\nmodule PublicActivity\n  module ORM\n    module Mongoid\n      # The ActiveRecord model containing\n      # details about recorded activity.\n      class Activity\n        include ::Mongoid::Document\n        include ::Mongoid::Timestamps\n        include ::Mongoid::Attributes::Dynamic if ::Mongoid::VERSION.split('.')[0].to_i >= 4\n        include Renderable\n\n        if ::Mongoid::VERSION.split('.')[0].to_i >= 7\n          opts = { polymorphic: true, optional: false }\n        else\n          opts = { polymorphic: true }\n        end\n\n        # Define polymorphic association to the parent\n        belongs_to :trackable,  opts\n        # Define ownership to a resource responsible for this activity\n        belongs_to :owner,      opts\n        # Define ownership to a resource targeted by this activity\n        belongs_to :recipient,  opts\n\n        field :key,         type: String\n        field :parameters,  type: Hash\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/orm/mongoid/adapter.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  module ORM\n    module Mongoid\n      class Adapter\n        # Creates the activity on `trackable` with `options`\n        def self.create_activity(trackable, options)\n          trackable.activities.create options\n        end\n\n        # Creates activity on `trackable` with `options`; throws error on validation failure\n        def self.create_activity!(trackable, options)\n          trackable.activities.create! options\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/orm/mongoid/trackable.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  module ORM\n    module Mongoid\n      module Trackable\n        def self.extended(base)\n          base.has_many :activities, :class_name => \"::PublicActivity::Activity\", :as => :trackable\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/orm/mongoid.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"mongoid/activity.rb\"\nrequire_relative \"mongoid/adapter.rb\"\nrequire_relative \"mongoid/activist.rb\"\nrequire_relative \"mongoid/trackable.rb\"\n"
  },
  {
    "path": "lib/public_activity/renderable.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  # Provides logic for rendering activities. Handles both i18n strings\n  # support and smart partials rendering (different templates per activity key).\n  module Renderable\n    # Virtual attribute returning text description of the activity\n    # using the activity's key to translate using i18n.\n    def text(params = {})\n      # TODO: some helper for key transformation for two supported formats\n      k = key.split('.')\n      k.unshift('activity') if k.first != 'activity'\n      k = k.join('.')\n\n      translate_params = parameters.merge(params) || {}\n      I18n.t(k, **translate_params)\n    end\n\n    # Renders activity from views.\n    #\n    # @param [ActionView::Base] context\n    # @return [nil] nil\n    #\n    # Renders activity to the given ActionView context with included\n    # AV::Helpers::RenderingHelper (most commonly just ActionView::Base)\n    #\n    # The *preferred* *way* of rendering activities is\n    # to provide a template specifying how the rendering should be happening.\n    # However, one may choose using _I18n_ based approach when developing\n    # an application that supports plenty of languages.\n    #\n    # If partial view exists that matches the *key* attribute\n    # renders that partial with local variables set to contain both\n    # Activity and activity_parameters (hash with indifferent access)\n    #\n    # Otherwise, it outputs the I18n translation to the context\n    # @example Render a list of all activities from a view (erb)\n    #   <ul>\n    #     <% for activity in PublicActivity::Activity.all %>\n    #      <li><%= render_activity(activity) %></li>\n    #     <% end %>\n    #   </ul>\n    #\n    # = Layouts\n    # You can supply a layout that will be used for activity partials\n    # with :layout param.\n    # Keep in mind that layouts for partials are also partials.\n    # @example Supply a layout\n    #   # in views:\n    #   #   All examples look for a layout in app/views/layouts/_activity.erb\n    #    render_activity @activity, :layout => \"activity\"\n    #    render_activity @activity, :layout => \"layouts/activity\"\n    #    render_activity @activity, :layout => :activity\n    #\n    #   # app/views/layouts/_activity.erb\n    #   <p><%= a.created_at %></p>\n    #   <%= yield %>\n    #\n    # == Custom Layout Location\n    # You can customize the layout directory by supplying :layout_root\n    # or by using an absolute path.\n    #\n    # @example Declare custom layout location\n    #\n    #   # Both examples look for a layout in \"app/views/custom/_layout.erb\"\n    #\n    #    render_activity @activity, :layout_root => \"custom\"\n    #    render_activity @activity, :layout      => \"/custom/layout\"\n    #\n    # = Creating a template\n    # To use templates for formatting how the activity should render,\n    # create a template based on activity key, for example:\n    #\n    # Given a key _activity.article.create_, create directory tree\n    # _app/views/public_activity/article/_ and create the _create_ partial there\n    #\n    # Note that if a key consists of more than three parts splitted by commas, your\n    # directory structure will have to be deeper, for example:\n    #   activity.article.comments.destroy => app/views/public_activity/articles/comments/_destroy.html.erb\n    #\n    # == Custom Directory\n    # You can override the default `public_directory` template root with the :root parameter\n    #\n    # @example Custom template root\n    #    # look for templates inside of /app/views/custom instead of /app/views/public_directory\n    #    render_activity @activity, :root => \"custom\"\n    #\n    # == Variables in templates\n    # From within a template there are two variables at your disposal:\n    # * activity (aliased as *a* for a shortcut)\n    # * params   (aliased as *p*) [converted into a HashWithIndifferentAccess]\n    #\n    # @example Template for key: _activity.article.create_ (erb)\n    #   <p>\n    #     Article <strong><%= p[:name] %></strong>\n    #     was written by <em><%= p[\"author\"] %></em>\n    #     <%= distance_of_time_in_words_to_now(a.created_at) %>\n    #   </p>\n    def render(context, params = {})\n      partial_root  = params.delete(:root)         || 'public_activity'\n      partial_path  = nil\n      layout_root   = params.delete(:layout_root)  || 'layouts'\n\n      if params.has_key? :display\n        if params[:display].to_sym == :\"i18n\"\n          text = self.text(params)\n          return context.render :text => text, :plain => text\n        else\n          partial_path = File.join(partial_root, params[:display].to_s)\n        end\n      end\n\n      context.render(\n        params.merge({\n          :partial => prepare_partial(partial_root, partial_path),\n          :layout  => prepare_layout(layout_root, params.delete(:layout)),\n          :locals  => prepare_locals(params)\n        })\n      )\n    end\n\n    def prepare_partial(root, path)\n      path || self.template_path(self.key, root)\n    end\n\n    def prepare_locals(params)\n      locals = params.delete(:locals) || Hash.new\n\n      controller  = PublicActivity.get_controller\n      prepared_params = prepare_parameters(params)\n      locals.merge(\n        {\n          :a              => self,\n          :activity       => self,\n          :controller     => controller,\n          :current_user   => controller.respond_to?(:current_user) ? controller.current_user : nil,\n          :p              => prepared_params,\n          :params         => prepared_params\n        }\n      )\n    end\n\n    def prepare_layout(root, layout)\n      if layout\n        path = layout.to_s\n        unless path.start_with?(root) || path.start_with?(\"/\")\n          return File.join(root, path)\n        end\n      end\n      layout\n    end\n\n    def prepare_parameters(params)\n      if self.parameters\n        @prepared_params ||= self.parameters.with_indifferent_access.merge(params)\n      else\n        @prepared_params ||= params\n      end\n    end\n\n    protected\n\n    # Builds the path to template based on activity key\n    def template_path(key, partial_root)\n      path = key.split(\".\")\n      path.delete_at(0) if path[0] == \"activity\"\n      path.unshift partial_root\n      path.join(\"/\")\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/roles/deactivatable.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  # Enables per-class disabling of PublicActivity functionality.\n  module Deactivatable\n    extend ActiveSupport::Concern\n\n    included do\n      class_attribute :public_activity_enabled_for_model\n      set_public_activity_class_defaults\n    end\n\n    # Returns true if PublicActivity is enabled\n    # globally and for this class.\n    # @return [Boolean]\n    # @api private\n    # @since 0.5.0\n    # overrides the method from Common\n    def public_activity_enabled?\n      PublicActivity.enabled? && self.class.public_activity_enabled_for_model\n    end\n\n    # Provides global methods to disable or enable PublicActivity on a per-class\n    # basis.\n    module ClassMethods\n      # Switches public_activity off for this class\n      def public_activity_off\n        self.public_activity_enabled_for_model = false\n      end\n\n      # Switches public_activity on for this class\n      def public_activity_on\n        self.public_activity_enabled_for_model = true\n      end\n\n      # @since 1.0.0\n      # @api private\n      def set_public_activity_class_defaults\n        super\n        self.public_activity_enabled_for_model = true\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/roles/tracked.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  # Main module extending classes we want to keep track of.\n  module Tracked\n    extend ActiveSupport::Concern\n    # A shortcut method for setting custom key, owner and parameters of {Activity}\n    # in one line. Accepts a hash with 3 keys:\n    # :key, :owner, :params. You can specify all of them or just the ones you want to overwrite.\n    #\n    # == Options\n    #\n    # [:key]\n    #   See {Common#activity_key}\n    # [:owner]\n    #   See {Common#activity_owner}\n    # [:params]\n    #   See {Common#activity_params}\n    # [:recipient]\n    #   Set the recipient for this activity. Useful for private notifications, which should only be visible to a certain user. See {Common#activity_recipient}.\n    # @example\n    #\n    #   @article = Article.new\n    #   @article.title = \"New article\"\n    #   @article.activity :key => \"my.custom.article.key\", :owner => @article.author, :params => {:title => @article.title}\n    #   @article.save\n    #   @article.activities.last.key #=> \"my.custom.article.key\"\n    #   @article.activities.last.parameters #=> {:title => \"New article\"}\n    #\n    # @param options [Hash] instance options to set on the tracked model\n    # @return [nil]\n    def activity(options = {})\n      rest = options.clone\n      self.activity_key           = rest.delete(:key) if rest[:key]\n      self.activity_owner         = rest.delete(:owner) if rest[:owner]\n      self.activity_params        = rest.delete(:params) if rest[:params]\n      self.activity_recipient     = rest.delete(:recipient) if rest[:recipient]\n      self.activity_custom_fields = rest if rest.count > 0\n      nil\n    end\n\n    # Module with basic +tracked+ method that enables tracking models.\n    class_methods do\n      # Adds required callbacks for creating and updating\n      # tracked models and adds +activities+ relation for listing\n      # associated activities.\n      #\n      # == Parameters:\n      # [:owner]\n      #   Specify the owner of the {Activity} (person responsible for the action).\n      #   It can be a Proc, Symbol or an ActiveRecord object:\n      #   == Examples:\n      #\n      #    tracked :owner => :author\n      #    tracked :owner => proc {|o| o.author}\n      #\n      #   Keep in mind that owner relation is polymorphic, so you can't just\n      #   provide id number of the owner object.\n      # [:recipient]\n      #   Specify the recipient of the {Activity}\n      #   It can be a Proc, Symbol, or an ActiveRecord object\n      #   == Examples:\n      #\n      #    tracked :recipient => :author\n      #    tracked :recipient => proc {|o| o.author}\n      #\n      #   Keep in mind that recipient relation is polymorphic, so you can't just\n      #   provide id number of the owner object.\n      # [:params]\n      #   Accepts a Hash with custom parameters you want to pass to i18n.translate\n      #   method. It is later used in {Renderable#text} method.\n      #   == Example:\n      #    class Article < ActiveRecord::Base\n      #      include PublicActivity::Model\n      #      tracked :params => {\n      #          :title => :title,\n      #          :author_name => \"Michael\",\n      #          :category_name => proc {|controller, model_instance| model_instance.category.name},\n      #          :summary => proc {|controller, model_instance| truncate(model.text, :length => 30)}\n      #      }\n      #    end\n      #\n      #   Values in the :params hash can either be an *exact* *value*, a *Proc/Lambda* executed before saving the activity or a *Symbol*\n      #   which is a an attribute or a method name executed on the tracked model's instance.\n      #\n      #   Everything specified here has a lower priority than parameters\n      #   specified directly in {#activity} method.\n      #   So treat it as a place where you provide 'default' values or where you\n      #   specify what data should be gathered for every activity.\n      #   For more dynamic settings refer to {Activity} model documentation.\n      # [:skip_defaults]\n      #   Disables recording of activities on create/update/destroy leaving that to programmer's choice. Check {PublicActivity::Common#create_activity}\n      #   for a guide on how to manually record activities.\n      # [:only]\n      #   Accepts a symbol or an array of symbols, of which any combination of the three is accepted:\n      #   * _:create_\n      #   * _:update_\n      #   * _:destroy_\n      #   Selecting one or more of these will make PublicActivity create activities\n      #   automatically for the tracked model on selected actions.\n      #\n      #   Resulting activities will have have keys assigned to, respectively:\n      #   * _article.create_\n      #   * _article.update_\n      #   * _article.destroy_\n      #   Since only three options are valid,\n      #   see _:except_ option for a shorter version\n      # [:except]\n      #   Accepts a symbol or an array of symbols with values like in _:only_, above.\n      #   Values provided will be subtracted from all default actions:\n      #   (create, update, destroy).\n      #\n      #   So, passing _create_ would track and automatically create\n      #   activities on _update_ and _destroy_ actions,\n      #   but not on the _create_ action.\n      # [:on]\n      #   Accepts a Hash with key being the *action* on which to execute *value* (proc)\n      #   Currently supported only for CRUD actions which are enabled in _:only_\n      #   or _:except_ options on this method.\n      #\n      #   Key-value pairs in this option define callbacks that can decide\n      #   whether to create an activity or not. Procs have two attributes for\n      #   use: _model_ and _controller_. If the proc returns true, the activity\n      #   will be created, if not, then activity will not be saved.\n      #\n      #   == Example:\n      #     # app/models/article.rb\n      #     tracked :on => {:update => proc {|model, controller| model.published? }}\n      #\n      #   In the example above, given a model Article with boolean column _published_.\n      #   The activities with key _article.update_ will only be created\n      #   if the published status is set to true on that article.\n      # @param opts [Hash] options\n      # @return [nil] options\n      def tracked(opts = {})\n        options = opts.clone\n\n        include_default_actions(options)\n\n        assign_globals       options\n        assign_hooks         options\n        assign_custom_fields options\n\n        nil\n      end\n\n      def include_default_actions(options)\n        defaults = {\n          create:  Creation,\n          destroy: Destruction,\n          update:  Update\n        }\n\n        return if options[:skip_defaults] == true\n\n        modules = if options[:except]\n          defaults.except(*options[:except])\n        elsif options[:only]\n          defaults.slice(*options[:only])\n        else\n          defaults\n        end\n\n        modules.each_value { |mod| include mod }\n      end\n\n      def available_options\n        %i[skip_defaults only except on owner recipient params].freeze\n      end\n\n      def assign_globals(options)\n        %i[owner recipient params].each do |key|\n          next unless options[key]\n\n          send(\"activity_#{key}_global=\".to_sym, options.delete(key))\n        end\n      end\n\n      def assign_hooks(options)\n        return unless options[:on].is_a?(Hash)\n\n        self.activity_hooks = options[:on].select { |_, v| v.is_a? Proc }.symbolize_keys\n      end\n\n      def assign_custom_fields(options)\n        options.except(*available_options).each do |k, v|\n          activity_custom_fields_global[k] = v\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/testing.rb",
    "content": "# frozen_string_literal: true\n\n# This file provides functionality for testing your code with public_activity\n# activated or deactivated.\n# This file should only be required in test/spec code!\n#\n# To enable PublicActivity testing capabilities do:\n#   require 'public_activity/testing'\nmodule PublicActivity\n  # Execute the code block with PublicActiviy active\n  #\n  # Example usage:\n  #   PublicActivity.with_tracking do\n  #     # your test code here\n  #   end\n  def self.with_tracking\n    current = PublicActivity.config.enabled\n    PublicActivity.config.enabled(true)\n    yield\n  ensure\n    PublicActivity.config.enabled(current)\n  end\n\n  # Execute the code block with PublicActiviy deactive\n  #\n  # Example usage:\n  #   PublicActivity.without_tracking do\n  #     # your test code here\n  #   end\n  def self.without_tracking\n    current = PublicActivity.enabled?\n    PublicActivity.config.enabled(false)\n    yield\n  ensure\n    PublicActivity.config.enabled(current)\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/utility/store_controller.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  class << self\n    # Setter for remembering controller instance\n    def set_controller(controller)\n      Thread.current[:public_activity_controller] = controller\n    end\n\n    # Getter for accessing the controller instance\n    def get_controller\n      Thread.current[:public_activity_controller]\n    end\n  end\n\n  # Module included in controllers to allow p_a access to controller instance\n  module StoreController\n    extend ActiveSupport::Concern\n\n    included do\n      around_action :store_controller_for_public_activity if     respond_to?(:around_action)\n      around_filter :store_controller_for_public_activity unless respond_to?(:around_action)\n    end\n\n    def store_controller_for_public_activity\n      PublicActivity.set_controller(self)\n      yield\n    ensure\n      PublicActivity.set_controller(nil)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/public_activity/utility/view_helpers.rb",
    "content": "# frozen_string_literal: true\n\n# Provides a shortcut from views to the rendering method.\nmodule PublicActivity\n  # Module extending ActionView::Base and adding `render_activity` helper.\n  module ViewHelpers\n    # View helper for rendering an activity, calls {PublicActivity::Activity#render} internally.\n    def render_activity activities, options = {}\n      if activities.is_a? PublicActivity::Activity\n        activities.render self, options\n      elsif activities.respond_to?(:map)\n        # depend on ORMs to fetch as needed\n        # maybe we can support Postgres streaming with this?\n        activities.map {|activity| activity.render self, options.dup }.join.html_safe\n      end\n    end\n    alias_method :render_activities, :render_activity\n\n    # Helper for setting content_for in activity partial, needed to\n    # flush remains in between partial renders.\n    def single_content_for(name, content = nil, &block)\n      @view_flow.set(name, ActiveSupport::SafeBuffer.new)\n      content_for(name, content, &block)\n    end\n  end\nend\n\nActiveSupport.on_load(:action_view) do\n  include PublicActivity::ViewHelpers\nend\n"
  },
  {
    "path": "lib/public_activity/version.rb",
    "content": "# frozen_string_literal: true\n\nmodule PublicActivity\n  VERSION = '3.0.2'\nend\n"
  },
  {
    "path": "lib/public_activity.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'active_support'\nrequire 'logger'\nrequire 'action_view'\n# +public_activity+ keeps track of changes made to models\n# and allows you to display them to the users.\n#\n# Check {PublicActivity::Tracked::ClassMethods#tracked} for more details about customizing and specifying\n# ownership to users.\nmodule PublicActivity\n  extend ActiveSupport::Concern\n  extend ActiveSupport::Autoload\n\n  autoload :Activity,     'public_activity/models/activity'\n  autoload :Activist,     'public_activity/models/activist'\n  autoload :Adapter,      'public_activity/models/adapter'\n  autoload :Trackable,    'public_activity/models/trackable'\n  autoload :Common\n  autoload :Config\n  autoload :Creation,     'public_activity/actions/creation.rb'\n  autoload :Deactivatable,'public_activity/roles/deactivatable.rb'\n  autoload :Destruction,  'public_activity/actions/destruction.rb'\n  autoload :Renderable\n  autoload :Tracked,      'public_activity/roles/tracked.rb'\n  autoload :Update,       'public_activity/actions/update.rb'\n  autoload :VERSION\n\n  # Switches PublicActivity on or off.\n  # @param value [Boolean]\n  # @since 0.5.0\n  def self.enabled=(value)\n    config.enabled(value)\n  end\n\n  # Returns `true` if PublicActivity is on, `false` otherwise.\n  # Enabled by default.\n  # @return [Boolean]\n  # @since 0.5.0\n  def self.enabled?\n    config.enabled\n  end\n\n  # Returns PublicActivity's configuration object.\n  # @since 0.5.0\n  def self.config\n    @@config ||= PublicActivity::Config.instance\n  end\n\n  # Method used to choose which ORM to load\n  # when PublicActivity::Activity class is being autoloaded\n  def self.inherit_orm(model = 'Activity')\n    orm = PublicActivity.config.orm\n    require \"public_activity/orm/#{orm}\"\n    \"PublicActivity::ORM::#{orm.to_s.classify}::#{model}\".constantize\n  end\n\n  # Module to be included in ActiveRecord models. Adds required functionality.\n  module Model\n    extend ActiveSupport::Concern\n\n    included do\n      include Common\n      include Deactivatable\n      include Tracked\n      include Activist # optional associations by recipient|owner\n    end\n  end\nend\n\nrequire 'public_activity/utility/store_controller'\nrequire 'public_activity/utility/view_helpers'\n"
  },
  {
    "path": "public_activity.gemspec",
    "content": "# frozen_string_literal: true\n\n$LOAD_PATH.push File.expand_path('../lib', __FILE__)\nrequire 'public_activity/version'\n\nGem::Specification.new do |s|\n  s.name = 'public_activity'\n  s.version = PublicActivity::VERSION\n  s.platform = Gem::Platform::RUBY\n  s.authors = ['Juri Hahn', 'Piotrek Okoński', 'Kuba Okoński']\n  s.email = 'juri.hahn+public-activity@gmail.com'\n  s.homepage = 'https://github.com/public-activity/public_activity'\n  s.summary = 'Easy activity tracking for ActiveRecord models'\n  s.description = 'Easy activity tracking for your ActiveRecord models. Provides Activity model with details about actions performed by your users, like adding comments, responding etc.'\n  s.license = 'MIT'\n  s.metadata = {\n    \"bug_tracker_uri\"   => \"https://github.com/public-activity/public_activity/issues\",\n    'changelog_uri'     => 'https://github.com/public-activity/public_activity/blob/main/CHANGELOG.md',\n    \"documentation_uri\" => \"https://rubydoc.info/gems/public_activity\",\n    \"homepage_uri\"      => s.homepage,\n    \"source_code_uri\"   => \"https://github.com/public-activity/public_activity\",\n    \"rubygems_mfa_required\" => \"true\",\n  }\n\n  s.files = `git ls-files lib`.split(\"\\n\") + ['Gemfile', 'Rakefile', 'README.md', 'MIT-LICENSE']\n  s.test_files = `git ls-files test`.split(\"\\n\")\n  s.require_paths = ['lib']\n\n  s.required_ruby_version = '>= 3.0.0'\n\n  s.post_install_message = File.read('UPGRADING') if File.exist?('UPGRADING')\n\n  s.add_dependency 'actionpack', '>= 6.1'\n  s.add_dependency 'i18n', '>= 0.5.0'\n  s.add_dependency 'railties', '>= 6.1'\n\n  ENV['PA_ORM'] ||= 'active_record'\n  case ENV['PA_ORM']\n  when 'active_record'\n    s.add_dependency 'activerecord', '>= 6.1'\n  when 'mongoid'\n    s.add_dependency 'mongoid',      '>= 4.0'\n  when 'mongo_mapper'\n    s.add_dependency 'bson_ext'\n    s.add_dependency 'mongo', '<= 1.9.2'\n    s.add_dependency 'mongo_mapper', '>= 0.12.0'\n  end\n\n  s.add_development_dependency 'appraisal'\n  s.add_development_dependency 'minitest'\n  s.add_development_dependency 'mocha'\n  s.add_development_dependency 'pry'\n  s.add_development_dependency 'redcarpet'\n  s.add_development_dependency 'simplecov'\n  s.add_development_dependency 'sqlite3', '~> 1.4'\n  s.add_development_dependency 'test-unit'\n  s.add_development_dependency 'yard'\n  s.add_development_dependency 'rake'\nend\n"
  },
  {
    "path": "public_activity.sublime-project",
    "content": "{\n  \"folders\":\n  [\n    {\n      \"path\": \"./\",\n      \"folder_exclude_patterns\": [\"coverage\", \".yardoc\", \"doc\"]\n    }\n  ],\n  \"settings\":\n  {\n    \"tab_size\": 2,\n    \"translate_tabs_to_spaces\": true,\n    \"trim_trailing_white_space_on_save\": true\n  },\n  \"build_systems\":\n  [\n    {\n      \"name\": \"PublicActivity Test Suite\",\n      \"linux\": {\n        \"working_dir\": \"$project_dir\",\n        \"cmd\": [\"bundle\", \"exec\", \"rake\", \"test\"]\n      },\n      \"selector\": \"source.ruby\"\n    }\n  ]\n}\n"
  },
  {
    "path": "test/migrations/002_create_articles.rb",
    "content": "# frozen_string_literal: true\n\nclass CreateArticles < ActiveRecord::Migration[6.1]\n  def self.up\n    create_table :articles do |t|\n      t.string :name\n      t.boolean :published\n      t.belongs_to :user\n      t.timestamps\n    end\n  end\nend\n"
  },
  {
    "path": "test/migrations/003_create_users.rb",
    "content": "# frozen_string_literal: true\n\nclass CreateUsers < ActiveRecord::Migration[6.1]\n  def self.up\n    create_table :users do |t|\n      t.string :name\n      t.timestamps\n    end\n  end\nend\n"
  },
  {
    "path": "test/migrations/004_add_nonstandard_to_activities.rb",
    "content": "# frozen_string_literal: true\n\nclass AddNonstandardToActivities < ActiveRecord::Migration[6.1]\n  def change\n    change_table :activities do |t|\n      t.string :nonstandard\n    end\n  end\nend\n"
  },
  {
    "path": "test/mongo_mapper.yml",
    "content": "test:\n  host: 127.0.0.1\n  port: 27017\n  database: public_activity_test"
  },
  {
    "path": "test/mongoid.yml",
    "content": "test:\n  clients:\n    default:\n      hosts:\n        - 127.0.0.1:27017\n      database: public_activity_test\n"
  },
  {
    "path": "test/test_activist.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\ndescribe PublicActivity::Activist do\n  it 'adds owner association' do\n    klass = article\n    assert_respond_to klass, :activist\n    klass.activist\n    assert_respond_to klass.new, :activities\n\n    case ENV['PA_ORM']\n    when 'active_record'\n      assert_equal klass.reflect_on_association(:activities_as_owner).options[:as], :owner\n    when 'mongoid'\n      assert_equal klass.reflect_on_association(:activities_as_owner).options[:inverse_of], :owner\n    when 'mongo_mapper'\n      assert_equal klass.associations[:activities_as_owner].options[:as], :owner\n    end\n\n    if ENV['PA_ORM'] == 'mongo_mapper'\n      assert_equal klass.associations[:activities_as_owner].options[:class_name], '::PublicActivity::Activity'\n    else\n      assert_equal klass.reflect_on_association(:activities_as_owner).options[:class_name], '::PublicActivity::Activity'\n    end\n  end\n\n  it 'returns activities from association' do\n    case PublicActivity::Config.orm\n    when :active_record\n      class ActivistUser < ActiveRecord::Base\n        include PublicActivity::Model\n        self.table_name = 'users'\n        activist\n      end\n    when :mongoid\n      class ActivistUser\n        include Mongoid::Document\n        include PublicActivity::Model\n        activist\n\n        field :name, type: String\n      end\n    when :mongo_mapper\n      class ActivistUser\n        include MongoMapper::Document\n        include PublicActivity::Model\n        activist\n\n        key :name, String\n      end\n    end\n    owner = ActivistUser.create(name: 'Peter Pan')\n    a = article(owner: owner).new\n    a.save\n\n    assert_equal owner.activities_as_owner.length, 1\n  end\nend\n"
  },
  {
    "path": "test/test_activity.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\ndescribe 'PublicActivity::Activity Rendering' do\n  describe '#text' do\n    subject { PublicActivity::Activity.new(key: 'activity.test', parameters: { one: 1 }) }\n\n    specify '#text uses translations' do\n      subject.save\n      I18n.config.backend.store_translations(:en, activity: { test: '%{one} %{two}' })\n      assert_equal subject.text(two: 2), '1 2'\n      assert_equal subject.parameters, one: 1\n    end\n  end\n\n  describe '#render' do\n    subject do\n      PublicActivity::Activity.new(key: 'activity.test', parameters: { one: 1 }).tap(&:save)\n    end\n\n    let(:template_output) { \"<strong>1, 2</strong>\\n<em>activity.test, #{subject.id}</em>\\n\" }\n    before { @controller.class.prepend_view_path File.expand_path('views', __dir__) }\n\n    it 'uses view partials when available' do\n      PublicActivity.set_controller(Struct.new(:current_user).new('fake'))\n      subject.render(self, two: 2)\n      assert_equal rendered, \"#{template_output}fake\\n\"\n    end\n\n    it 'uses requested partial'\n\n    it 'uses view partials without controller' do\n      PublicActivity.set_controller(nil)\n      subject.render(self, two: 2)\n      assert_equal rendered, \"#{template_output}\\n\"\n    end\n\n    it 'provides local variables' do\n      PublicActivity.set_controller(nil)\n      subject.render(self, locals: { two: 2 })\n      assert_equal rendered.chomp, '2'\n    end\n\n    it 'uses translations only when requested' do\n      I18n.config.backend.store_translations(:en, activity: { test: '%{one} %{two}' })\n      subject.render(self, two: 2, display: :i18n)\n      assert_equal rendered, '1 2'\n    end\n\n    it 'pass all params to view context' do\n      view_context = mock('ViewContext')\n      PublicActivity.set_controller(nil)\n      view_context.expects(:render).with { |params| params[:formats] == ['json'] }\n      subject.render(view_context, formats: ['json'])\n    end\n\n    it 'uses specified layout' do\n      PublicActivity.set_controller(nil)\n      subject.render(self, layout: 'activity')\n      assert_includes rendered, 'Here be the layouts'\n\n      subject.render(self, layout: 'layouts/activity')\n      assert_includes rendered, 'Here be the layouts'\n\n      subject.render(self, layout: :activity)\n      assert_includes rendered, 'Here be the layouts'\n    end\n\n    it 'accepts a custom layout root' do\n      subject.render(self, layout: :layout, layout_root: 'custom')\n      assert_includes rendered, 'Here be the custom layouts'\n    end\n\n    it 'accepts an absolute layout path' do\n      subject.render(self, layout: '/custom/layout')\n      assert_includes rendered, 'Here be the custom layouts'\n    end\n\n    it 'accepts a template root' do\n      subject.render(self, root: 'custom')\n      assert_includes rendered, 'Custom Template Root'\n    end\n  end\nend\n"
  },
  {
    "path": "test/test_common.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\ndescribe PublicActivity::Common do\n  before do\n    @owner     = User.create(name: 'Peter Pan')\n    @recipient = User.create(name: 'Bruce Wayne')\n    @options   = {\n      params: {\n        author_name: 'Peter',\n        summary: 'Default summary goes here...'\n      },\n      owner: @owner,\n      recipient: @recipient\n    }\n  end\n  subject { article(@options).new }\n\n  it 'prioritizes parameters passed to #create_activity' do\n    subject.save\n    assert_equal subject.create_activity(:test, params: { author_name: 'Pan' }).parameters[:author_name], 'Pan'\n    assert_equal subject.create_activity(:test, parameters: { author_name: 'Pan' }).parameters[:author_name], 'Pan'\n    assert_nil subject.create_activity(:test, params: { author_name: nil }).parameters[:author_name]\n    assert_nil subject.create_activity(:test, parameters: { author_name: nil }).parameters[:author_name]\n  end\n\n  it 'prioritizes owner passed to #create_activity' do\n    subject.save\n    assert_equal subject.create_activity(:test, owner: @recipient).owner, @recipient\n    assert_nil subject.create_activity(:test, owner: nil).owner\n  end\n\n  it 'prioritizes recipient passed to #create_activity' do\n    subject.save\n    assert_equal subject.create_activity(:test, recipient: @owner).recipient, @owner\n    assert_nil subject.create_activity(:test, recipient: nil).recipient\n  end\n\n  it 'uses global fields' do\n    subject.save\n    activity = subject.activities.last\n    assert_equal activity.parameters, @options[:params]\n    assert_equal activity.owner, @owner\n  end\n\n  it 'allows custom fields' do\n    subject.save\n    subject.create_activity :with_custom_fields, nonstandard: 'Custom allowed'\n    assert_equal subject.activities.last.nonstandard, 'Custom allowed'\n  end\n\n  it '#create_activity returns a new activity object' do\n    subject.save\n    assert subject.create_activity('some.key')\n  end\n\n  it '#create_activity! returns a new activity object' do\n    subject.save\n    activity = subject.create_activity!('some.key')\n    assert activity.persisted?\n    assert_equal 'article.some.key', activity.key\n  end\n\n  it 'update action should not create activity on save unless model changed' do\n    subject.save\n    before_count = subject.activities.count\n    subject.save\n    subject.save\n    after_count = subject.activities.count\n    assert_equal before_count, after_count\n  end\n\n  it 'allows passing owner through #create_activity' do\n    article = article().new\n    article.save\n    activity = article.create_activity('some.key', owner: @owner)\n    assert_equal activity.owner, @owner\n  end\n\n  it 'allows resolving custom fields' do\n    subject.name      = 'Resolving is great'\n    subject.published = true\n    subject.save\n    subject.create_activity :with_custom_fields, nonstandard: :name\n    assert_equal subject.activities.last.nonstandard, 'Resolving is great'\n    subject.create_activity :with_custom_fields_2, nonstandard: proc { |_, model| model.published.to_s }\n    assert_equal subject.activities.last.nonstandard, 'true'\n  end\n\n  it 'inherits instance parameters' do\n    subject.activity params: { author_name: 'Michael' }\n    subject.save\n    activity = subject.activities.last\n\n    assert_equal activity.parameters[:author_name], 'Michael'\n  end\n\n  it 'accepts instance recipient' do\n    subject.activity recipient: @recipient\n    subject.save\n    assert_equal subject.activities.last.recipient, @recipient\n  end\n\n  it 'accepts instance owner' do\n    subject.activity owner: @owner\n    subject.save\n    assert_equal subject.activities.last.owner, @owner\n  end\n\n  it 'accepts owner as a symbol' do\n    klass = article(owner: :user)\n    @article = klass.new(user: @owner)\n    @article.save\n    activity = @article.activities.last\n\n    assert_equal activity.owner, @owner\n  end\n\n  it 'reports PublicActivity::Activity as the base class' do\n    if ENV['PA_ORM'] == 'active_record' # Only relevant for ActiveRecord\n      subject.save\n      assert_equal subject.activities.last.class.base_class, PublicActivity::Activity\n    end\n  end\n\n  describe '#prepare_key' do\n    describe 'for class#activity_key method' do\n      before do\n        @article = article(owner: :user).new(user: @owner)\n      end\n\n      it 'assigns key to value of activity_key if set' do\n        def @article.activity_key; 'my_custom_key' end\n\n        assert_equal @article.prepare_key(:create, {}), 'my_custom_key'\n      end\n\n      it 'assigns key based on class name as fallback' do\n        def @article.activity_key; nil end\n\n        assert_equal @article.prepare_key(:create), 'article.create'\n      end\n\n      it 'assigns key value from options hash' do\n        assert_equal @article.prepare_key(:create, key: :my_custom_key), 'my_custom_key'\n      end\n    end\n\n    describe 'for camel cased classes' do\n      before do\n        class CamelCase < article(owner: :user)\n          def self.name; 'CamelCase' end\n        end\n        @camel_case = CamelCase.new\n      end\n\n      it 'assigns generates key from class name' do\n        assert_equal @camel_case.prepare_key(:create, {}), 'camel_case.create'\n      end\n    end\n\n    describe 'for namespaced classes' do\n      before do\n        module ::MyNamespace;\n          class CamelCase < article(owner: :user)\n            def self.name; 'MyNamespace::CamelCase' end\n          end\n        end\n        @namespaced_camel_case = MyNamespace::CamelCase.new\n      end\n\n      it 'assigns key value from options hash' do\n        assert_equal @namespaced_camel_case.prepare_key(:create, {}), 'my_namespace_camel_case.create'\n      end\n    end\n  end\n\n  # no key implicated or given\n  specify do\n    assert_raises(PublicActivity::NoKeyProvided) { subject.prepare_settings }\n  end\n\n  describe 'resolving values' do\n    it 'allows procs with models and controllers' do\n      context = mock('context')\n      context.expects(:accessor).times(2).returns(5)\n      controller = mock('controller')\n      controller.expects(:current_user).returns(:cu)\n      PublicActivity.set_controller(controller)\n      p = proc { |c, m|\n        assert_equal :cu, c.current_user\n        assert_equal 5, m.accessor\n      }\n      PublicActivity.resolve_value(context, p)\n      PublicActivity.resolve_value(context, :accessor)\n    end\n  end\n\n  def teardown\n    PublicActivity.set_controller(nil)\n  end\nend\n"
  },
  {
    "path": "test/test_controller_integration.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass StoringController < ActionView::TestCase::TestController\n  include PublicActivity::StoreController\n  include ActionController::Testing\nend\n\ndescribe PublicActivity::StoreController do\n  it 'stores controller' do\n    controller = StoringController.new\n    PublicActivity.set_controller(controller)\n    assert_same controller, Thread.current[:public_activity_controller]\n    assert_same controller, PublicActivity.get_controller\n  end\n\n  it 'stores controller with a filter in controller' do\n    controller = StoringController.new\n\n    callbacks = controller._process_action_callbacks.select { |c| c.kind == :around }.map(&:filter)\n    assert_includes(callbacks, :store_controller_for_public_activity)\n\n    public_activity_controller =\n      controller.instance_eval do\n        store_controller_for_public_activity do\n          PublicActivity.get_controller\n        end\n      end\n\n    assert_equal controller, public_activity_controller\n  end\n\n  it 'stores controller in a threadsafe way' do\n    PublicActivity.set_controller(1)\n    assert_equal PublicActivity.get_controller, 1\n\n    Thread.new do\n      PublicActivity.set_controller(2)\n      assert_equal 2, PublicActivity.get_controller\n      PublicActivity.set_controller(nil)\n    end\n\n    assert_equal PublicActivity.get_controller, 1\n\n    PublicActivity.set_controller(nil)\n  end\nend\n"
  },
  {
    "path": "test/test_generators.rb",
    "content": "# frozen_string_literal: true\n\nif ENV['PA_ORM'] == 'active_record'\n\n  require 'test_helper'\n  require 'rails/generators/test_case'\n  require 'generators/public_activity/migration/migration_generator'\n  require 'generators/public_activity/migration_upgrade/migration_upgrade_generator'\n\n  class TestMigrationGenerator < Rails::Generators::TestCase\n    tests PublicActivity::Generators::MigrationGenerator\n    destination File.expand_path('../tmp', File.dirname(__FILE__))\n    setup :prepare_destination\n\n    def test_generating_activity_model\n      run_generator\n      assert_migration 'db/migrate/create_activities.rb'\n    end\n  end\n\n  class TestMigrationUpgradeGenerator < Rails::Generators::TestCase\n    tests PublicActivity::Generators::MigrationUpgradeGenerator\n    destination File.expand_path('../tmp', File.dirname(__FILE__))\n    setup :prepare_destination\n\n    def test_generating_activity_model\n      run_generator\n      assert_migration 'db/migrate/upgrade_activities.rb'\n    end\n  end\nend\n"
  },
  {
    "path": "test/test_helper.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'rubygems'\nrequire 'bundler'\nrequire 'logger'\nBundler.setup(:default, :test)\n\nif ENV['COV']\n  require 'simplecov'\n  SimpleCov.start do\n    add_filter '/test/'\n  end\nend\n$:.unshift File.expand_path('../lib', __dir__)\nrequire 'active_support/testing/setup_and_teardown'\nrequire 'public_activity'\nrequire 'public_activity/testing'\nrequire 'pry'\nrequire 'minitest/autorun'\nrequire 'mocha/minitest'\n\nPublicActivity::Config.orm = (ENV['PA_ORM'] || :active_record)\n\ncase PublicActivity::Config.orm\nwhen :active_record\n  require 'active_record'\n  require 'active_record/connection_adapters/sqlite3_adapter'\n  require 'stringio'        # silence the output\n  $stdout = StringIO.new    # from migrator\n  ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')\n\n  migrations_path = File.expand_path('migrations', __dir__)\n  active_record_version = ActiveRecord.version.release\n\n  if active_record_version >= Gem::Version.new('7.2.0')\n    migration_context = ActiveRecord::MigrationContext.new(migrations_path)\n    migrations = migration_context.migrations   # => Array<ActiveRecord::MigrationProxy>\n    connection_pool = ActiveRecord::Tasks::DatabaseTasks.migration_connection_pool\n    schema_migration = ActiveRecord::SchemaMigration.new(connection_pool)\n    internal_metadata = ActiveRecord::InternalMetadata.new(connection_pool)\n\n    ActiveRecord::Migrator.new(\n      :up,\n      migrations,\n      schema_migration,\n      internal_metadata\n    ).migrate\n  else\n    ActiveRecord::MigrationContext.new(migrations_path, ActiveRecord::SchemaMigration).migrate\n  end\n\n  $stdout = STDOUT\n\n  def article(options = {})\n    Class.new(ActiveRecord::Base) do\n      self.table_name = 'articles'\n      include PublicActivity::Model\n      tracked options\n      belongs_to :user\n\n      def self.name\n        'Article'\n      end\n    end\n  end\n\n  class User < ActiveRecord::Base; end\nwhen :mongoid\n  require 'mongoid'\n\n  Mongoid.load!(File.expand_path('test/mongoid.yml'), :test)\n\n  class User\n    include Mongoid::Document\n    include Mongoid::Timestamps\n\n    has_many :articles\n\n    field :name, type: String\n  end\n\n  class Article\n    include Mongoid::Document\n    include Mongoid::Timestamps\n    include PublicActivity::Model\n\n    if ::Mongoid::VERSION.split('.')[0].to_i >= 7\n      belongs_to :user, optional: true\n    else\n      belongs_to :user\n    end\n\n    field :name, type: String\n    field :published, type: Boolean\n  end\n\n  def article(options = {})\n    Article.class_eval do\n      set_public_activity_class_defaults\n      tracked options\n    end\n    Article\n  end\nwhen :mongo_mapper\n  require 'mongo_mapper'\n\n  config = YAML.safe_load(File.read('test/mongo_mapper.yml'), aliases: true)\n  MongoMapper.setup(config, :test)\n\n  class User\n    include MongoMapper::Document\n\n    has_many :articles\n\n    key :name, String\n    timestamps!\n  end\n\n  class Article\n    include MongoMapper::Document\n    include PublicActivity::Model\n\n    belongs_to :user\n\n    key :name, String\n    key :published, Boolean\n  end\n\n  def article(options = {})\n    Article.class_eval do\n      set_public_activity_class_defaults\n      tracked options\n    end\n    Article\n  end\nend\n\nclass ViewSpec < Minitest::Spec\n  prepend ActiveSupport::Testing::SetupAndTeardown\n  include ActionView::TestCase::Behavior\nend\nMinitest::Spec.register_spec_type(/Rendering$/, ViewSpec)\n"
  },
  {
    "path": "test/test_testing.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\ndescribe PublicActivity do\n  describe 'self.with_tracking' do\n    after do\n      PublicActivity.enabled = true\n    end\n\n    it 'enables tracking inside the block' do\n      PublicActivity.enabled = false\n\n      PublicActivity.with_tracking do\n        assert PublicActivity.enabled?\n      end\n    end\n\n    it 'restores previous `enabled` state' do\n      PublicActivity.enabled = false\n      PublicActivity.with_tracking do\n        # something\n      end\n\n      assert_equal PublicActivity.enabled?, false\n    end\n  end\n\n  describe 'self.without_tracking' do\n    it 'disables tracking inside the block' do\n      PublicActivity.enabled = true\n\n      PublicActivity.without_tracking do\n        assert_equal PublicActivity.enabled?, false\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "test/test_tracking.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\ndescribe PublicActivity::Tracked do\n  describe 'defining instance options' do\n    subject { article.new }\n    let :options do\n      {\n        key: 'key',\n        params: { a: 1 },\n        owner: User.create,\n        recipient: User.create\n      }\n    end\n    before(:each) { subject.activity(options) }\n    let(:activity) { subject.save; subject.activities.last }\n\n    specify { assert_same subject.activity_key, options[:key] }\n    specify { assert_equal activity.key, options[:key] }\n\n    specify { assert_same subject.activity_owner, options[:owner] }\n    specify { assert_equal activity.owner, options[:owner] }\n\n    specify { assert_same subject.activity_params, options[:params] }\n    specify { assert_equal activity.parameters, options[:params] }\n\n    specify { assert_same subject.activity_recipient, options[:recipient] }\n    specify { assert_equal activity.recipient, options[:recipient] }\n  end\n\n  it 'can be tracked and be an activist at the same time' do\n    case PublicActivity.config.orm\n    when :mongoid\n      class ActivistAndTrackedArticle\n        include Mongoid::Document\n        include Mongoid::Timestamps\n        include PublicActivity::Model\n\n        if ::Mongoid::VERSION.split('.')[0].to_i >= 7\n          belongs_to :user, optional: true\n        else\n          belongs_to :user\n        end\n\n        field :name, type: String\n        field :published, type: Boolean\n        tracked\n        activist\n      end\n    when :mongo_mapper\n      class ActivistAndTrackedArticle\n        include MongoMapper::Document\n        include PublicActivity::Model\n\n        belongs_to :user\n\n        key :name, String\n        key :published, Boolean\n        tracked\n        activist\n        timestamps!\n      end\n    when :active_record\n      class ActivistAndTrackedArticle < ActiveRecord::Base\n        self.table_name = 'articles'\n        include PublicActivity::Model\n        tracked\n        activist\n\n        belongs_to :user\n      end\n    end\n\n    art = ActivistAndTrackedArticle.new\n    art.save\n    assert_equal art.activities.last.trackable_id, art.id\n    assert_nil art.activities.last.owner_id\n  end\n\n  describe 'custom fields' do\n    describe 'global' do\n      it 'should resolve symbols' do\n        a = article(nonstandard: :name).new(name: 'Symbol resolved')\n        a.save\n        assert_equal a.activities.last.nonstandard, 'Symbol resolved'\n      end\n\n      it 'should resolve procs' do\n        a = article(nonstandard: proc { |_, model| model.name }).new(name: 'Proc resolved')\n        a.save\n        assert_equal a.activities.last.nonstandard, 'Proc resolved'\n      end\n    end\n\n    describe 'instance' do\n      it 'should resolve symbols' do\n        a = article.new(name: 'Symbol resolved')\n        a.activity nonstandard: :name\n        a.save\n        assert_equal a.activities.last.nonstandard, 'Symbol resolved'\n      end\n\n      it 'should resolve procs' do\n        a = article.new(name: 'Proc resolved')\n        a.activity nonstandard: proc { |_, model| model.name }\n        a.save\n        assert_equal a.activities.last.nonstandard, 'Proc resolved'\n      end\n    end\n  end\n\n  it 'should reset instance options on successful create_activity' do\n    a = article.new\n    a.activity key: 'test', params: { test: 1 }\n    a.save\n    assert_equal a.activities.count, 1\n    assert_raises(PublicActivity::NoKeyProvided) { a.create_activity }\n    assert_empty a.activity_params\n    a.activity key: 'asd'\n    a.create_activity\n    assert_raises(PublicActivity::NoKeyProvided) { a.create_activity }\n  end\n\n  it 'should not accept global key option' do\n    # this example tests the lack of presence of sth that should not be here\n    a = article(key: 'asd').new\n    a.save\n    assert_raises(PublicActivity::NoKeyProvided) { a.create_activity }\n    assert_equal a.activities.count, 1\n  end\n\n  it 'should not change global custom fields' do\n    a = article(nonstandard: 'global').new\n    a.activity nonstandard: 'instance'\n    a.save\n    assert_equal a.class.activity_custom_fields_global, nonstandard: 'global'\n  end\n\n  describe 'disabling functionality' do\n    it 'allows for global disable' do\n      PublicActivity.enabled = false\n      activity_count_before = PublicActivity::Activity.count\n\n      @article = article.new\n      @article.save\n      assert_equal PublicActivity::Activity.count, activity_count_before\n\n      PublicActivity.enabled = true\n    end\n\n    it 'allows for class-wide disable' do\n      activity_count_before = PublicActivity::Activity.count\n\n      klass = article\n      klass.public_activity_off\n      @article = klass.new\n      @article.save\n      assert_equal PublicActivity::Activity.count, activity_count_before\n\n      klass.public_activity_on\n      @article.name = 'Changed Article'\n      @article.save\n      assert(PublicActivity::Activity.count > activity_count_before)\n    end\n  end\n\n  describe '#tracked' do\n    subject { article(options) }\n    let(:options) { {} }\n\n    it 'allows skipping the tracking on CRUD actions' do\n      art =\n        case PublicActivity.config.orm\n        when :mongoid\n          Class.new do\n            include Mongoid::Document\n            include Mongoid::Timestamps\n            include PublicActivity::Model\n\n            belongs_to :user\n\n            field :name, type: String\n            field :published, type: Boolean\n            tracked skip_defaults: true\n          end\n        when :mongo_mapper\n          Class.new do\n            include MongoMapper::Document\n            include PublicActivity::Model\n\n            belongs_to :user\n\n            key :name, String\n            key :published, Boolean\n            tracked skip_defaults: true\n\n            timestamps!\n          end\n        when :active_record\n          article(skip_defaults: true)\n        end\n\n      assert_includes art, PublicActivity::Common\n      refute_includes art, PublicActivity::Creation\n      refute_includes art, PublicActivity::Update\n      refute_includes art, PublicActivity::Destruction\n    end\n\n    describe 'default options' do\n      subject { article }\n\n      specify { assert_includes subject, PublicActivity::Creation }\n      specify { assert_includes subject, PublicActivity::Destruction }\n      specify { assert_includes subject, PublicActivity::Update }\n\n      specify do\n        callbacks = subject._create_callbacks.select do |c|\n          c.kind.eql?(:after) && c.filter == :activity_on_create\n        end\n\n        refute_empty callbacks\n      end\n\n      specify do\n        callbacks = subject._update_callbacks.select do |c|\n          c.kind.eql?(:after) && c.filter == :activity_on_update\n        end\n\n        refute_empty callbacks\n      end\n\n      specify do\n        callbacks = subject._destroy_callbacks.select do |c|\n          c.kind.eql?(:before) && c.filter == :activity_on_destroy\n        end\n\n        refute_empty callbacks\n      end\n    end\n\n    it 'accepts :except option' do\n      art =\n        case PublicActivity.config.orm\n        when :mongoid\n          Class.new do\n            include Mongoid::Document\n            include Mongoid::Timestamps\n            include PublicActivity::Model\n\n            belongs_to :user\n\n            field :name, type: String\n            field :published, type: Boolean\n            tracked except: [:create]\n          end\n        when :mongo_mapper\n          Class.new do\n            include MongoMapper::Document\n            include PublicActivity::Model\n\n            belongs_to :user\n\n            key :name, String\n            key :published, Boolean\n            tracked except: [:create]\n\n            timestamps!\n          end\n        when :active_record\n          article(except: [:create])\n        end\n\n      refute_includes art, PublicActivity::Creation\n      assert_includes art, PublicActivity::Update\n      assert_includes art, PublicActivity::Destruction\n    end\n\n    it 'accepts :only option' do\n      art =\n        case PublicActivity.config.orm\n        when :mongoid\n          Class.new do\n            include Mongoid::Document\n            include Mongoid::Timestamps\n            include PublicActivity::Model\n\n            belongs_to :user\n\n            field :name, type: String\n            field :published, type: Boolean\n\n            tracked only: %i[create update]\n          end\n        when :mongo_mapper\n          Class.new do\n            include MongoMapper::Document\n            include PublicActivity::Model\n\n            belongs_to :user\n\n            key :name, String\n            key :published, Boolean\n\n            tracked only: %i[create update]\n          end\n        when :active_record\n          article(only: %I[create update])\n        end\n\n      assert_includes art, PublicActivity::Creation\n      refute_includes art, PublicActivity::Destruction\n      assert_includes art, PublicActivity::Update\n    end\n\n    it 'accepts :owner option' do\n      owner = mock('owner')\n      subject.tracked(owner: owner)\n      assert_equal subject.activity_owner_global, owner\n    end\n\n    it 'accepts :params option' do\n      params = { a: 1 }\n      subject.tracked(params: params)\n      assert_equal subject.activity_params_global, params\n    end\n\n    it 'accepts :on option' do\n      on = { a: -> {}, b: proc {} }\n      subject.tracked(on: on)\n      assert_equal subject.activity_hooks, on\n    end\n\n    it 'accepts :on option with string keys' do\n      on = { 'a' => -> {} }\n      subject.tracked(on: on)\n      assert_equal subject.activity_hooks, on.symbolize_keys\n    end\n\n    it 'accepts :on values that are procs' do\n      on = { unpassable: 1, proper: -> {}, proper_proc: proc {} }\n      subject.tracked(on: on)\n      assert_includes subject.activity_hooks, :proper\n      assert_includes subject.activity_hooks, :proper_proc\n      refute_includes subject.activity_hooks, :unpassable\n    end\n\n    describe 'global options' do\n      subject { article(recipient: :test, owner: :test2, params: { a: 'b' }) }\n\n      specify { assert_equal subject.activity_recipient_global, :test }\n      specify { assert_equal subject.activity_owner_global, :test2 }\n      specify { assert_equal subject.activity_params_global, a: 'b' }\n    end\n  end\n\n  describe 'activity hooks' do\n    subject do\n      s = article\n      s.activity_hooks = { test: hook }\n      s\n    end\n    let(:hook) { -> {} }\n\n    it 'retrieves hooks' do\n      assert_same hook, subject.get_hook(:test)\n    end\n\n    it 'retrieves hooks by string keys' do\n      assert_same hook, subject.get_hook('test')\n    end\n\n    it 'returns nil when no matching hook is present' do\n      assert_same nil, subject.get_hook(:nonexistent)\n    end\n\n    it 'allows hooks to decide if activity should be created' do\n      subject.tracked\n      @article = subject.new(name: 'Some Name')\n      PublicActivity.set_controller(mock('controller'))\n      pf = proc { |model, controller|\n        assert_same controller, PublicActivity.get_controller\n        assert_equal model.name, 'Some Name'\n        false\n      }\n      pt = proc { |model, controller|\n        assert_same controller, PublicActivity.get_controller\n        assert_equal model.name, 'Other Name'\n        true # this will save the activity with *.update key\n      }\n      @article.class.activity_hooks = { create: pf, update: pt, destroy: pt }\n\n      assert_empty @article.activities.to_a\n      @article.save # create\n      @article.name = 'Other Name'\n      @article.save # update\n      @article.destroy # destroy\n\n      assert_equal @article.activities.count, 2\n      assert_equal @article.activities.first.key, 'article.update'\n    end\n  end\n\n  def teardown\n    PublicActivity.set_controller(nil)\n  end\nend\n"
  },
  {
    "path": "test/test_view_helpers.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\ndescribe 'ViewHelpers Rendering' do\n  include PublicActivity::ViewHelpers\n\n  # is this a proper test?\n  it 'provides render_activity helper' do\n    activity = mock('activity')\n    activity.stubs(:is_a?).with(PublicActivity::Activity).returns(true)\n    activity.expects(:render).with(self, {})\n    render_activity(activity)\n  end\n\n  it 'handles multiple activities' do\n    activity = mock('activity')\n    activity.expects(:render).with(self, {})\n    render_activities([activity])\n  end\n\n  it 'flushes content_for between partials renderes' do\n    @view_flow = mock('view_flow')\n    @view_flow.expects(:set).twice.with('name', ActiveSupport::SafeBuffer.new)\n\n    single_content_for('name', 'content')\n    assert_equal @name, 'name'\n    assert_equal @content, 'content'\n    single_content_for('name', 'content2')\n    assert_equal @name, 'name'\n    assert_equal @content, 'content2'\n  end\n\n  def content_for(name, content)\n    @name = name\n    @content = content\n  end\nend\n"
  },
  {
    "path": "test/views/custom/_layout.erb",
    "content": "<h2>Here be the custom layouts</h2><%= yield %>\n"
  },
  {
    "path": "test/views/custom/_test.erb",
    "content": "Custom Template Root <%= activity.id %>\n"
  },
  {
    "path": "test/views/layouts/_activity.erb",
    "content": "<h2>Here be the layouts</h2><%= yield %>"
  },
  {
    "path": "test/views/public_activity/_test.erb",
    "content": "<% if defined?(two) == 'local-variable' %>\n<% # output local_variable if defined %>\n<%= two %>\n<% else %>\n<strong><%= p[:one] %>, <%= params['two'] %></strong>\n<em><%= a.key %>, <%= activity.id %></em>\n<%= current_user %>\n<% end %>\n"
  }
]