Repository: pokonski/public_activity
Branch: main
Commit: d8200889330c
Files: 72
Total size: 99.7 KB
Directory structure:
gitextract_a19chaa2/
├── .editorconfig
├── .github/
│ └── workflows/
│ └── main.yml
├── .gitignore
├── .travis.yml
├── Appraisals
├── CHANGELOG.md
├── Gemfile
├── MIT-LICENSE
├── README.md
├── Rakefile
├── gemfiles/
│ ├── .bundle/
│ │ └── config
│ ├── rails_6.1.gemfile
│ ├── rails_7.0.gemfile
│ └── rails_7.1.gemfile
├── lib/
│ ├── generators/
│ │ ├── public_activity/
│ │ │ ├── migration/
│ │ │ │ ├── migration_generator.rb
│ │ │ │ └── templates/
│ │ │ │ └── migration.rb
│ │ │ └── migration_upgrade/
│ │ │ ├── migration_upgrade_generator.rb
│ │ │ └── templates/
│ │ │ └── upgrade.rb
│ │ └── public_activity.rb
│ ├── public_activity/
│ │ ├── actions/
│ │ │ ├── creation.rb
│ │ │ ├── destruction.rb
│ │ │ └── update.rb
│ │ ├── activity.rb
│ │ ├── common.rb
│ │ ├── config.rb
│ │ ├── models/
│ │ │ ├── activist.rb
│ │ │ ├── activity.rb
│ │ │ ├── adapter.rb
│ │ │ └── trackable.rb
│ │ ├── orm/
│ │ │ ├── active_record/
│ │ │ │ ├── activist.rb
│ │ │ │ ├── activity.rb
│ │ │ │ ├── adapter.rb
│ │ │ │ └── trackable.rb
│ │ │ ├── active_record.rb
│ │ │ ├── mongo_mapper/
│ │ │ │ ├── activist.rb
│ │ │ │ ├── activity.rb
│ │ │ │ ├── adapter.rb
│ │ │ │ └── trackable.rb
│ │ │ ├── mongo_mapper.rb
│ │ │ ├── mongoid/
│ │ │ │ ├── activist.rb
│ │ │ │ ├── activity.rb
│ │ │ │ ├── adapter.rb
│ │ │ │ └── trackable.rb
│ │ │ └── mongoid.rb
│ │ ├── renderable.rb
│ │ ├── roles/
│ │ │ ├── deactivatable.rb
│ │ │ └── tracked.rb
│ │ ├── testing.rb
│ │ ├── utility/
│ │ │ ├── store_controller.rb
│ │ │ └── view_helpers.rb
│ │ └── version.rb
│ └── public_activity.rb
├── public_activity.gemspec
├── public_activity.sublime-project
└── test/
├── migrations/
│ ├── 002_create_articles.rb
│ ├── 003_create_users.rb
│ └── 004_add_nonstandard_to_activities.rb
├── mongo_mapper.yml
├── mongoid.yml
├── test_activist.rb
├── test_activity.rb
├── test_common.rb
├── test_controller_integration.rb
├── test_generators.rb
├── test_helper.rb
├── test_testing.rb
├── test_tracking.rb
├── test_view_helpers.rb
└── views/
├── custom/
│ ├── _layout.erb
│ └── _test.erb
├── layouts/
│ └── _activity.erb
└── public_activity/
└── _test.erb
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.{json,yml}]
indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false
================================================
FILE: .github/workflows/main.yml
================================================
name: CI
on:
pull_request:
paths-ignore:
- 'README.md'
push:
paths-ignore:
- 'README.md'
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
ruby_version: ['3.0', '3.1', '3.2', '3.3']
rails_version: ['6.1', '7.0', '7.1']
exclude:
- ruby_version: 3.1
rails_version: 6.1
- ruby_version: 3.2
rails_version: 6.1
- ruby_version: 3.3
rails_version: 6.1
steps:
- uses: actions/checkout@v4
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby_version }}
- name: Build and run test
run: |
bundle
bundle exec appraisal rails_${{ matrix.rails_version }} bundle
bundle exec appraisal rails_${{ matrix.rails_version }} rake
================================================
FILE: .gitignore
================================================
/doc/
/.yardoc/
*.gem
/coverage/
/*.sublime-workspace
/tmp
/Gemfile.lock
/gemfiles/*.lock
================================================
FILE: .travis.yml
================================================
language: ruby
rvm:
- 2.2.6
- 2.3.3
matrix:
include:
- rvm: 2.2.6
gemfile: gemfiles/Gemfile.rails-4.0
env: PA_ORM=active_record
- rvm: 2.3.3
gemfile: gemfiles/Gemfile.rails-4.0
env: PA_ORM=active_record
- rvm: 2.3.3
gemfile: gemfiles/Gemfile.rails-5.0
env: PA_ORM=active_record
- rvm: 2.5.1
gemfile: gemfiles/Gemfile.rails-5.2
env: PA_ORM=active_record
- rvm: 2.2.6
env: PA_ORM=mongoid
env:
- PA_ORM=active_record
- PA_ORM=mongo_mapper
services:
- mongodb
email:
recipients:
- piotrek@okonski.org
on_success: change
on_failure: always
================================================
FILE: Appraisals
================================================
# frozen_string_literal: true
if RUBY_VERSION.to_f < 3.1
appraise 'rails_6.1' do
gem 'rails', '~> 6.1.0'
gem 'openssl'
end
end
appraise 'rails_7.0' do
gem 'rails', '~> 7.0.1'
end
appraise 'rails_7.1' do
gem 'rails', '~> 7.1.0'
end
================================================
FILE: CHANGELOG.md
================================================
# Changelog
## 3.0.2
- **Fixed** Refactor prepare_parameters method to handle nil parameters (s. #387, thanks [Himalaya Pal](https://github.com/palhimalaya))
- **Fixed** CI failure due to lax sqlite3 version constraint (s. #389, thanks [Junichi Sato](https://github.com/sato11))
## 3.0.1
- **Fixed** Rails 6.1/7.0 regression in serialization of `nil`/`NULL` values
- **Fixed** Docs for `ORM::ActiveRecord::Activist`
## 3.0.0
- **Added** Rails 7.1 support (s. #384, thanks [max.jos](https://github.com/yhru))
- **Removed** Ruby <= 2.7 support
- **Removed** Rails <= 6.0 support
## 2.0.2
- **Fixed** Rescue from `ActiveRecord::ConnectionNotEstablished` (s. #372, thanks [Gabe Blair](https://github.com/gblair) & [Vitalie Lazu](https://github.com/vitaliel))
## 2.0.1
- **Fixed** Fix regression in generated migration (s. #368, thanks [Colin Bonner](https://github.com/cfbonner))
## 2.0.0
- **Fixed** Deprecation warnings in Ruby 2.7/3.0 due to double splat operator
- **Fixed** Ruby warnings due to unused variables `e` in exception handling
- **Added** Ruby 3.1 support
- **Added** Rails 7.0 support
- **Removed** Ruby <= 2.4 support
- **Removed** Rails <= 4.2 support
## 1.6.4
- **Fixed** exception when not using MySQL or Postgres (see #335, thanks to [Roland Netzsch](https://github.com/stuxcrystal))
- **Added** `create_activity!` method which raises exception on failures, much like `save!` in ActiveRecord (see #334, thanks to [Jonathan](https://github.com/jtwhittington))
- **Added** support for ActiveRecord 6 by whitelisting it (see #332, thanks to [Emre Demir](https://github.com/demir))
- **Added** frozen_string_literal pragma to Ruby files for better performance (see #329, thanks to [Krzysztof Rybka](https://github.com/krzysiek1507))
## 1.6.3
- **Fixed** a bug which resulted in crashes when PostgreSQL connection failed (see #328, thanks to [Ken Greeff](https://github.com/kengreeff))
- **Fixed** a bug which resulted in crashes when MySQL connection failed (see #327, thanks to [Miquel Sabaté Solà](https://github.com/mssola))
## 1.6.2
- **Fixed** a bug which resulted in crashes when database didn't exist (see #323, thanks to [Anmol Chopra](https://github.com/chopraanmol1))
## 1.6.1
- **Fixed** a bug with requiring not existent file in generated migrations
## 1.6.0
* **Add support for Rails 5.2**
* Make config settings thread safe (fixes #284)
* Fix Rspec tests failing in Rails 5 due to ViewHelpers (see #271, thanks to [Janusz](https://github.com/januszm))
* 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))
## 1.5.0
* Refactor PublicActivity::StoreController to rely on Thread.current instead. (see #252, thanks to [Ryan McGeary](https://github.com/rmm5t))
* Remove support for Ruby 1.9.3 and Ruby 2.0.0 (thanks to [Ryan McGeary](https://github.com/rmm5t))
## 1.4.2
* Fix bug with migrations not having an extension in ActiveRecord >= 4.0.0
## 1.4.1
* Fixed issue with Rails 4 when using ProtectedAttributes gem (see #128)
* General code clean-ups.
## 1.4.0
* Added support for MongoMapper ORM (thanks to [Julio Olivera](https://github.com/julioolvr)) [PR](https://github.com/pokonski/public_activity/pull/101)
* Added support for stable **Rails 4.0** while keeping compatibility with Rails 3.X
* `render_activity` can now render collections of activities instead of just a single one. Also aliased as `render_activities`
* Fix issue in rendering multiple activities when options were incomplete for every subsequent activity after the first one
* `render_activity` now accetps `:locals` option. Works the same way as `:locals` for Rails `render` method.
## 1.1.0
* Fixed an issue when AR was loading despite choosing Mongoid in multi-ORM Rails applications (thanks to [Robert Ulejczyk](https://github.com/robuye))
## 1.0.3
* Fixed a bug which modified globals (thanks to [Weera Wu](https://github.com/wulab))
## 1.0.2
* Fixed undefined constant PublicActivity::Activity for Activist associations (thanks to [Стас Сушков](https://github.com/stas))
## 1.0.1
* #create_activity now correctly returns activity object.
* Fixed :owner not being set correctly when passed to #create_activity (thanks to [Drew Miller](https://github.com/mewdriller))
## 1.0 (released 10/02/2013)
* **Now supports Mongoid 3 and Active Record.**
* Added indexes for polymorphic column pairs to speed up queries in ActiveRecord
* `#create_activity` now returns the newly created Activity object
* Support for custom Activity attributes. Now if you need a custom relation for Activities you can
create a migration which adds the desired column, whitelist the attribute, and then you can simply pass the value to #create_activity
* `#tracked` can now accept a single Symbol for its `:only` and `:except` options.
* It is now possible to include `PublicActivity::Common` in your models if you just want to use `#create_activity` method
and skip the default CRUD tracking.
* `#render_activity` now accepts Symbols or Strings for :layout parameter.
### Example
```ruby
# All look for app/views/layouts/_activity.erb
render_activity @activity, :layout => "activity"
render_activity @activity, :layout => "layouts/activity"
render_activity @activity, :layout => :activity
```
## 0.5.4
* Fixed support for namespaced classes when transforming into view path.
For example `MyNamespace::CamelCase` now correctly transforms to key: `my_namespace_camel_case.create`
================================================
FILE: Gemfile
================================================
# frozen_string_literal: true
source "https://rubygems.org"
gemspec
================================================
FILE: MIT-LICENSE
================================================
Copyright (c) 2011-2013 Piotrek Okoński
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# PublicActivity [](https://codeclimate.com/github/chaps-io/public_activity) [](http://badge.fury.io/rb/public_activity)
`public_activity` provides easy activity tracking for your **ActiveRecord**, **Mongoid 3** and **MongoMapper** models
in Rails 6.1+. Simply put: it records what has been changed or created and gives you the ability to present those
recorded activities to users - similarly to how GitHub does it.
## Table of contents
- [Ruby/Rails version support](#ruby-rails-version-support)
- [Table of contents](#table-of-contents)
- [Example](#example)
- [Online demo](#online-demo)
- [Screencast](#screencast)
- [Setup](#setup)
- [Gem installation](#gem-installation)
- [Database setup](#database-setup)
- [Model configuration](#model-configuration)
- [Custom activities](#custom-activities)
- [Displaying activities](#displaying-activities)
- [Layouts](#layouts)
- [Locals](#locals)
- [Activity views](#activity-views)
- [I18n](#I18n)
- [Testing](#testing)
- [Documentation](#documentation)
- [Common examples](#common-examples)
- [Help](#help)
- [License](#license)
## Ruby/Rails version support
Version `~> 3.0`` supports Ruby 3.0+ and Rails 6.1+. For older Ruby versions
(≤2.7) and Rails 5.0+ you can use version `~> 2.0` until you can upgrade to
newer versions of Ruby + Rails.
Issues related to those unsupported versions of Ruby/Rails will be closed
without resolution and PRs will not be accepted.
## Example
Here is a simple example showing what this gem is about:

### Demo app
The source code of the demo is hosted here: https://github.com/pokonski/activity_blog
## Screencast
Ryan Bates made a [great screencast](http://railscasts.com/episodes/406-public-activity) describing how to integrate Public Activity in your Rails Application.
## Setup
### Gem installation
You can install `public_activity` as you would any other gem:
gem install public_activity
or in your Gemfile:
```ruby
gem 'public_activity'
```
### Database setup
By default _public_activity_ uses Active Record. If you want to use Mongoid or MongoMapper as your backend, create
an initializer file in your Rails application with the corresponding code inside:
For _Mongoid:_
```ruby
# config/initializers/public_activity.rb
PublicActivity::Config.set do
orm :mongoid
end
```
For _MongoMapper:_
```ruby
# config/initializers/public_activity.rb
PublicActivity::Config.set do
orm :mongo_mapper
end
```
**(ActiveRecord only)** Create migration for activities and migrate the database (in your Rails project):
rails g public_activity:migration
rake db:migrate
### Model configuration
Include `PublicActivity::Model` and add `tracked` to the model you want to keep track of:
For _ActiveRecord:_
```ruby
class Article < ActiveRecord::Base
include PublicActivity::Model
tracked
end
```
For _Mongoid:_
```ruby
class Article
include Mongoid::Document
include PublicActivity::Model
tracked
end
```
For _MongoMapper:_
```ruby
class Article
include MongoMapper::Document
include PublicActivity::Model
tracked
end
```
And now, by default create/update/destroy activities are recorded in activities table.
This is all you need to start recording activities for basic CRUD actions.
_Optional_: If you don't need `#tracked` but still want the comfort of `#create_activity`,
you can include only the lightweight `Common` module instead of `Model`.
#### Custom activities
You can trigger custom activities by setting all your required parameters and triggering `create_activity`
on the tracked model, like this:
```ruby
@article.create_activity key: 'article.commented_on', owner: current_user
```
See this entry http://rubydoc.info/gems/public_activity/PublicActivity/Common:create_activity for more details.
### Displaying activities
To display them you simply query the `PublicActivity::Activity` model:
```ruby
# notifications_controller.rb
def index
@activities = PublicActivity::Activity.all
end
```
And in your views:
```erb
<%= render_activities(@activities) %>
```
*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.
*Note*: `render_activities` is an alias for `render_activity` and does the same.
#### Layouts
You can also pass options to both `activity#render` and `#render_activity` methods, which are passed deeper
to the internally used `render_partial` method.
A useful example would be to render activities wrapped in layout, which shares common elements of an activity,
like a timestamp, owner's avatar etc:
```erb
<%= render_activities(@activities, layout: :activity) %>
```
The activity will be wrapped with the `app/views/layouts/_activity.erb` layout, in the above example.
**Important**: please note that layouts for activities are also partials. Hence the `_` prefix.
#### Locals
Sometimes, it's desirable to pass additional local variables to partials. It can be done this way:
```erb
<%= render_activity(@activity, locals: {friends: current_user.friends}) %>
```
*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.
#### Activity views
`public_activity` looks for views in `app/views/public_activity`.
For 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)`.
*Hint*: the `"activity."` prefix in `:key` is completely optional and kept for backwards compatibility, you can skip it in new projects.
If 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).
#### I18n
Translations 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`.
Translations should be put in your locale `.yml` files. To render pure strings from I18n Example structure:
```yaml
activity:
article:
create: 'Article has been created'
update: 'Someone has edited the article'
destroy: 'Some user removed an article!'
```
This structure is valid for activities with keys `"activity.article.create"` or `"article.create"`. As mentioned before, `"activity."` part of the key is optional.
## Testing
For RSpec you can first disable `public_activity` and add the `test_helper` in `rails_helper.rb` with:
```ruby
#rails_helper.rb
require 'public_activity/testing'
PublicActivity.enabled = false
```
In your specs you can then blockwise decide whether to turn `public_activity` on
or off.
```ruby
# file_spec.rb
PublicActivity.with_tracking do
# your test code goes here
end
PublicActivity.without_tracking do
# your test code goes here
end
```
## Documentation
For more documentation go [here](http://rubydoc.info/gems/public_activity/index)
## Common examples
* [[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)
* [[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)
* [[How to] Create custom activities](https://github.com/pokonski/public_activity/wiki/%5BHow-to%5D-Create-custom-activities)
* [[How to] Use custom fields on Activity](https://github.com/pokonski/public_activity/wiki/%5BHow-to%5D-Use-custom-fields-on-Activity)
## Help
If you need help with using public_activity please visit our discussion group and ask a question there:
https://groups.google.com/forum/?fromgroups#!forum/public-activity
Please do not ask general questions in the Github Issues.
## License
Copyright (c) 2011-2013 Piotrek Okoński, released under the MIT license
================================================
FILE: Rakefile
================================================
# frozen_string_literal: true
require 'bundler/gem_tasks'
require 'rake'
require 'yard'
require 'yard/rake/yardoc_task'
require 'rake/testtask'
task default: :test
desc 'Generate documentation for the public_activity plugin.'
YARD::Rake::YardocTask.new do |doc|
doc.files = ['lib/**/*.rb']
end
Rake::TestTask.new do |t|
t.libs << 'test'
t.test_files = FileList['test/test*.rb']
end
================================================
FILE: gemfiles/.bundle/config
================================================
---
BUNDLE_RETRY: "1"
================================================
FILE: gemfiles/rails_6.1.gemfile
================================================
# This file was generated by Appraisal
source "https://rubygems.org"
gem "rails", "~> 6.1.0"
gem "openssl"
gemspec path: "../"
================================================
FILE: gemfiles/rails_7.0.gemfile
================================================
# This file was generated by Appraisal
source "https://rubygems.org"
gem "rails", "~> 7.0.1"
gemspec path: "../"
================================================
FILE: gemfiles/rails_7.1.gemfile
================================================
# This file was generated by Appraisal
source "https://rubygems.org"
gem "rails", "~> 7.1.0"
gemspec path: "../"
================================================
FILE: lib/generators/public_activity/migration/migration_generator.rb
================================================
# frozen_string_literal: true
require 'generators/public_activity'
require 'rails/generators/active_record'
module PublicActivity
module Generators
# Migration generator that creates migration file from template
class MigrationGenerator < ActiveRecord::Generators::Base
extend Base
argument :name, :type => :string, :default => 'create_activities'
# Create migration in project's folder
def generate_files
migration_template 'migration.rb', "db/migrate/#{name}.rb"
end
end
end
end
================================================
FILE: lib/generators/public_activity/migration/templates/migration.rb
================================================
# frozen_string_literal: true
# Migration responsible for creating a table with activities
class CreateActivities < ActiveRecord::Migration[6.1]
def self.up
create_table :activities do |t|
t.belongs_to :trackable, polymorphic: true
t.belongs_to :owner, polymorphic: true
t.string :key
t.text :parameters
t.belongs_to :recipient, polymorphic: true
t.timestamps
end
end
# Drop table
def self.down
drop_table :activities
end
end
================================================
FILE: lib/generators/public_activity/migration_upgrade/migration_upgrade_generator.rb
================================================
# frozen_string_literal: true
require 'generators/public_activity'
require 'rails/generators/active_record'
module PublicActivity
module Generators
# Migration generator that creates migration file from template
class MigrationUpgradeGenerator < ActiveRecord::Generators::Base
extend Base
argument :name, :type => :string, :default => 'upgrade_activities'
# Create migration in project's folder
def generate_files
migration_template 'upgrade.rb', "db/migrate/#{name}.rb"
end
end
end
end
================================================
FILE: lib/generators/public_activity/migration_upgrade/templates/upgrade.rb
================================================
# frozen_string_literal: true
# Migration responsible for creating a table with activities
class UpgradeActivities < ActiveRecord::Migration[5.0]
# Create table
def self.change
change_table :activities do |t|
t.belongs_to :recipient, :polymorphic => true
end
end
end
================================================
FILE: lib/generators/public_activity.rb
================================================
# frozen_string_literal: true
require 'rails/generators/named_base'
module PublicActivity
# A generator module with Activity table schema.
module Generators
# A base module
module Base
# Get path for migration template
def source_root
@_public_activity_source_root ||= File.expand_path(File.join('../public_activity', generator_name, 'templates'), __FILE__)
end
end
end
end
================================================
FILE: lib/public_activity/actions/creation.rb
================================================
# frozen_string_literal: true
module PublicActivity
# Handles creation of Activities upon destruction and update of tracked model.
module Creation
extend ActiveSupport::Concern
included do
after_create :activity_on_create
end
private
# Creates activity upon creation of the tracked model
def activity_on_create
create_activity(:create)
end
end
end
================================================
FILE: lib/public_activity/actions/destruction.rb
================================================
# frozen_string_literal: true
module PublicActivity
# Handles creation of Activities upon destruction of tracked model.
module Destruction
extend ActiveSupport::Concern
included do
before_destroy :activity_on_destroy
end
private
# Records an activity upon destruction of the tracked model
def activity_on_destroy
create_activity(:destroy)
end
end
end
================================================
FILE: lib/public_activity/actions/update.rb
================================================
# frozen_string_literal: true
module PublicActivity
# Handles creation of Activities upon destruction and update of tracked model.
module Update
extend ActiveSupport::Concern
included do
after_update :activity_on_update
end
private
# Creates activity upon modification of the tracked model
def activity_on_update
# Either use #changed? method for Rails < 5.1 or #saved_changes? for recent versions
create_activity(:update) if respond_to?(:saved_changes?) ? saved_changes? : changed?
end
end
end
================================================
FILE: lib/public_activity/activity.rb
================================================
# frozen_string_literal: true
module PublicActivity
# Main model, stores all information about what happened,
# who caused it, when and anything else.
class Activity < inherit_orm("Activity")
end
end
================================================
FILE: lib/public_activity/common.rb
================================================
# frozen_string_literal: true
module PublicActivity
# Happens when creating custom activities without either action or a key.
class NoKeyProvided < Exception; end
# Used to smartly transform value from metadata to data.
# Accepts Symbols, which it will send against context.
# Accepts Procs, which it will execute with controller and context.
# @since 0.4.0
def self.resolve_value(context, thing)
case thing
when Symbol
context.__send__(thing)
when Proc
thing.call(PublicActivity.get_controller, context)
else
thing
end
end
# Common methods shared across the gem.
module Common
extend ActiveSupport::Concern
included do
include Trackable
class_attribute :activity_owner_global, :activity_recipient_global,
:activity_params_global, :activity_hooks, :activity_custom_fields_global
set_public_activity_class_defaults
end
# @!group Global options
# @!attribute activity_owner_global
# Global version of activity owner
# @see #activity_owner
# @return [Model]
# @!attribute activity_recipient_global
# Global version of activity recipient
# @see #activity_recipient
# @return [Model]
# @!attribute activity_params_global
# Global version of activity parameters
# @see #activity_params
# @return [Hash<Symbol, Object>]
# @!attribute activity_hooks
# @return [Hash<Symbol, Proc>]
# Hooks/functions that will be used to decide *if* the activity should get
# created.
#
# The supported keys are:
# * :create
# * :update
# * :destroy
# @!endgroup
# @!group Instance options
# Set or get parameters that will be passed to {Activity} when saving
#
# == Usage:
#
# @article.activity_params = {:article_title => @article.title}
# @article.save
#
# This way you can pass strings that should remain constant, even when model attributes
# change after creating this {Activity}.
# @return [Hash<Symbol, Object>]
attr_accessor :activity_params
@activity_params = {}
# Set or get owner object responsible for the {Activity}.
#
# == Usage:
#
# # where current_user is an object of logged in user
# @article.activity_owner = current_user
# # OR: take @article.author association
# @article.activity_owner = :author
# # OR: provide a Proc with custom code
# @article.activity_owner = proc {|controller, model| model.author }
# @article.save
# @article.activities.last.owner #=> Returns owner object
# @return [Model] Polymorphic model
# @see #activity_owner_global
attr_accessor :activity_owner
@activity_owner = nil
# Set or get recipient for activity.
#
# Association is polymorphic, thus allowing assignment of
# all types of models. This can be used for example in the case of sending
# private notifications for only a single user.
# @return (see #activity_owner)
attr_accessor :activity_recipient
@activity_recipient = nil
# Set or get custom i18n key passed to {Activity}, later used in {Renderable#text}
#
# == Usage:
#
# @article = Article.new
# @article.activity_key = "my.custom.article.key"
# @article.save
# @article.activities.last.key #=> "my.custom.article.key"
#
# @return [String]
attr_accessor :activity_key
@activity_key = nil
# Set or get custom fields for later processing
#
# @return [Hash]
attr_accessor :activity_custom_fields
@activity_custom_fields = {}
# @!visibility private
@@activity_hooks = {}
# @!endgroup
# Provides some global methods for every model class.
class_methods do
#
# @since 1.0.0
# @api private
def set_public_activity_class_defaults
self.activity_owner_global = nil
self.activity_recipient_global = nil
self.activity_params_global = {}
self.activity_hooks = {}
self.activity_custom_fields_global = {}
end
# Extracts a hook from the _:on_ option provided in
# {Tracked::ClassMethods#tracked}. Returns nil when no hook exists for
# given action
# {Common#get_hook}
#
# @see Tracked#get_hook
# @param key [String, Symbol] action to retrieve a hook for
# @return [Proc, nil] callable hook or nil
# @since 0.4.0
# @api private
def get_hook(key)
key = key.to_sym
if activity_hooks.key?(key) && activity_hooks[key].is_a?(Proc)
activity_hooks[key]
end
end
end
#
# Returns true if PublicActivity is enabled
# globally and for this class.
# @return [Boolean]
# @api private
# @since 0.5.0
def public_activity_enabled?
PublicActivity.enabled?
end
#
# Shortcut for {ClassMethods#get_hook}
# @param (see ClassMethods#get_hook)
# @return (see ClassMethods#get_hook)
# @since (see ClassMethods#get_hook)
# @api (see ClassMethods#get_hook)
def get_hook(key)
self.class.get_hook(key)
end
# Calls hook safely.
# If a hook for given action exists, calls it with model (self) and
# controller (if available, see {StoreController})
# @param key (see #get_hook)
# @return [Boolean] if hook exists, it's decision, if there's no hook, true
# @since 0.4.0
# @api private
def call_hook_safe(key)
hook = get_hook(key)
if hook
# provides hook with model and controller
hook.call(self, PublicActivity.get_controller)
else
true
end
end
# Directly creates activity record in the database, based on supplied options.
#
# It's meant for creating custom activities while *preserving* *all*
# *configuration* defined before. If you fire up the simplest of options:
#
# current_user.create_activity(:avatar_changed)
#
# It will still gather data from any procs or symbols you passed as params
# to {Tracked::ClassMethods#tracked}. It will ask the hooks you defined
# whether to really save this activity.
#
# But you can also overwrite instance and global settings with your options:
#
# @article.activity :owner => proc {|controller| controller.current_user }
# @article.create_activity(:commented_on, :owner => @user)
#
# And it's smart! It won't execute your proc, since you've chosen to
# overwrite instance parameter _:owner_ with @user.
#
# [:key]
# The key will be generated from either:
# * the first parameter you pass that is not a hash (*action*)
# * the _:action_ option in the options hash (*action*)
# * the _:key_ option in the options hash (it has to be a full key,
# including model name)
# When you pass an *action* (first two options above), they will be
# added to parameterized model name:
#
# Given Article model and instance: @article,
#
# @article.create_activity :commented_on
# @article.activities.last.key # => "article.commented_on"
#
# For other parameters, see {Tracked#activity}, and "Instance options"
# accessors at {Tracked}, information on hooks is available at
# {Tracked::ClassMethods#tracked}.
# @see #prepare_settings
# @return [Model, nil] If created successfully, new activity
# @since 0.4.0
# @api public
# @overload create_activity(action, options = {})
# @param [Symbol,String] action Name of the action
# @param [Hash] options Options with quality higher than instance options
# set in {Tracked#activity}
# @option options [Activist] :owner Owner
# @option options [Activist] :recipient Recipient
# @option options [Hash] :params Parameters, see
# {PublicActivity.resolve_value}
# @overload create_activity(options = {})
# @param [Hash] options Options with quality higher than instance options
# set in {Tracked#activity}
# @option options [Symbol,String] :action Name of the action
# @option options [String] :key Full key
# @option options [Activist] :owner Owner
# @option options [Activist] :recipient Recipient
# @option options [Hash] :params Parameters, see
# {PublicActivity.resolve_value}
def create_activity(*args)
return unless public_activity_enabled?
options = prepare_settings(*args)
if call_hook_safe(options[:key].split('.').last)
reset_activity_instance_options
return PublicActivity::Adapter.create_activity(self, options)
end
nil
end
# Directly saves activity to database. Works the same as create_activity
# but throws validation error for each supported ORM.
#
# @see #create_activity
def create_activity!(*args)
return unless public_activity_enabled?
options = prepare_settings(*args)
if call_hook_safe(options[:key].split('.').last)
reset_activity_instance_options
PublicActivity::Adapter.create_activity!(self, options)
end
end
# Prepares settings used during creation of Activity record.
# params passed directly to tracked model have priority over
# settings specified in tracked() method
#
# @see #create_activity
# @return [Hash] Settings with preserved options that were passed
# @api private
# @overload prepare_settings(action, options = {})
# @see #create_activity
# @overload prepare_settings(options = {})
# @see #create_activity
def prepare_settings(*args)
raw_options = args.extract_options!
action = [args.first, raw_options.delete(:action)].compact.first
key = prepare_key(action, raw_options)
raise NoKeyProvided, "No key provided for #{self.class.name}" unless key
prepare_custom_fields(raw_options.except(:params)).merge(
{
key: key,
owner: prepare_relation(:owner, raw_options),
recipient: prepare_relation(:recipient, raw_options),
parameters: prepare_parameters(raw_options),
}
)
end
# Prepares and resolves custom fields
# users can pass to `tracked` method
# @private
def prepare_custom_fields(options)
customs = self.class.activity_custom_fields_global.clone
customs.merge!(activity_custom_fields) if activity_custom_fields
customs.merge!(options)
customs.each do |k, v|
customs[k] = PublicActivity.resolve_value(self, v)
end
end
# Prepares i18n parameters that will
# be serialized into the Activity#parameters column
# @private
def prepare_parameters(options)
params = {}
params.merge!(self.class.activity_params_global)
params.merge!(activity_params) if activity_params
params.merge!([options.delete(:parameters), options.delete(:params), {}].compact.first)
params.each { |k, v| params[k] = PublicActivity.resolve_value(self, v) }
end
# Prepares relation to be saved
# to Activity. Can be :recipient or :owner
# @private
def prepare_relation(name, options)
PublicActivity.resolve_value(self,
(options.key?(name) ? options[name] : (
self.send("activity_#{name}") || self.class.send("activity_#{name}_global")
)
)
)
end
# Helper method to serialize class name into relevant key
# @return [String] the resulted key
# @param [Symbol] or [String] the name of the operation to be done on class
# @param [Hash] options to be used on key generation, defaults to {}
def prepare_key(action, options = {})
(
options[:key] ||
activity_key ||
((self.class.name.underscore.gsub('/', '_') + "." + action.to_s) if action)
).try(:to_s)
end
# Resets all instance options on the object
# triggered by a successful #create_activity, should not be
# called from any other place, or from application code.
# @private
def reset_activity_instance_options
@activity_params = {}
@activity_key = nil
@activity_owner = nil
@activity_recipient = nil
@activity_custom_fields = {}
end
end
end
================================================
FILE: lib/public_activity/config.rb
================================================
# frozen_string_literal: true
require 'singleton'
module PublicActivity
# Class used to initialize configuration object.
class Config
include ::Singleton
# Evaluates given block to provide DSL configuration.
# @example Initializer for Rails
# PublicActivity::Config.set do
# orm :mongo_mapper
# enabled false
# table_name "activities"
# end
def self.set(&block)
b = Block.new
b.instance_eval(&block)
instance
orm(b.orm) unless b.orm.nil?
enabled(b.enabled) unless b.enabled.nil?
table_name(b.table_name) unless b.table_name.nil?
end
# alias for {#orm}
# @see #orm
def self.orm=(orm = nil)
orm(orm)
end
# alias for {#enabled}
# @see #enabled
def self.enabled=(value = nil)
enabled(value)
end
# instance version of {Config#orm}
# @see Config#orm
def orm(orm = nil)
self.class.orm(orm)
end
# instance version of {Config#table_name}
# @see Config#orm
def table_name(name = nil)
self.class.table_name(name)
end
# instance version of {Config#enabled}
# @see Config#orm
def enabled(value = nil)
self.class.enabled(value)
end
# Set the ORM for use by PublicActivity.
def self.orm(orm = nil)
if orm.nil?
Thread.current[:public_activity_orm] || :active_record
else
Thread.current[:public_activity_orm] = orm.to_sym
end
end
def self.table_name(name = nil)
if name.nil?
Thread.current[:public_activity_table_name] || "activities"
else
Thread.current[:public_activity_table_name] = name
end
end
def self.enabled(value = nil)
if value.nil?
val = Thread.current[:public_activity_enabled]
val.nil? ? true : val
else
Thread.current[:public_activity_enabled] = value
end
end
# Provides simple DSL for the config block.
class Block
# @see Config#orm
def orm(orm = nil)
@orm = (orm ? orm.to_sym : false) || @orm
end
# Decides whether to enable PublicActivity.
# @param en [Boolean] Enabled?
def enabled(value = nil)
@enabled = (value.nil? ? @enabled : value)
end
# Sets the table_name for the model
def table_name(name = nil)
@table_name = (name.nil? ? @table_name : name)
end
end
end
end
================================================
FILE: lib/public_activity/models/activist.rb
================================================
# frozen_string_literal: true
module PublicActivity
# Provides helper methods for selecting activities from a user.
module Activist
# Delegates to configured ORM.
def self.included(base)
base.extend PublicActivity.inherit_orm('Activist')
end
end
end
================================================
FILE: lib/public_activity/models/activity.rb
================================================
# frozen_string_literal: true
module PublicActivity
class Activity < inherit_orm
end
end
================================================
FILE: lib/public_activity/models/adapter.rb
================================================
# frozen_string_literal: true
module PublicActivity
# Loads database-specific routines for use by PublicActivity.
class Adapter < inherit_orm('Adapter')
end
end
================================================
FILE: lib/public_activity/models/trackable.rb
================================================
# frozen_string_literal: true
module PublicActivity
# Provides association for activities bound to this object by *trackable*.
module Trackable
# Delegates to ORM.
def self.included(base)
base.extend PublicActivity.inherit_orm('Trackable')
end
end
end
================================================
FILE: lib/public_activity/orm/active_record/activist.rb
================================================
# frozen_string_literal: true
module PublicActivity
module ORM
module ActiveRecord
# Module extending classes that serve as owners
module Activist
# Adds ActiveRecord associations to model to simplify fetching
# so you can list activities performed by the owner.
# It is completely optional. Any model can be an owner to an activity
# even without being an explicit activist.
#
# == Usage:
# In model:
#
# class User < ActiveRecord::Base
# include PublicActivity::Model
# activist
# end
#
# In controller:
# User.first.activities_as_owner
# User.first.activities_as_recipient
#
def activist
has_many :activities_as_owner,
class_name: '::PublicActivity::Activity',
as: :owner
has_many :activities_as_recipient,
class_name: '::PublicActivity::Activity',
as: :recipient
end
end
end
end
end
================================================
FILE: lib/public_activity/orm/active_record/activity.rb
================================================
# frozen_string_literal: true
module PublicActivity
unless defined? ::PG::ConnectionBad
module ::PG
class ConnectionBad < Exception; end
end
end
unless defined? Mysql2::Error::ConnectionError
module Mysql2
module Error
class ConnectionError < Exception; end
end
end
end
module ORM
module ActiveRecord
# The ActiveRecord model containing
# details about recorded activity.
class Activity < ::ActiveRecord::Base
include Renderable
self.table_name = PublicActivity.config.table_name
self.abstract_class = true
# Define polymorphic association to the parent
belongs_to :trackable, polymorphic: true
with_options(optional: true) do
# Define ownership to a resource responsible for this activity
belongs_to :owner, polymorphic: true
# Define ownership to a resource targeted by this activity
belongs_to :recipient, polymorphic: true
end
# Serialize parameters Hash
begin
if table_exists?
unless %i[json jsonb hstore].include?(columns_hash['parameters'].type)
if ::ActiveRecord.version.release < Gem::Version.new('7.1')
serialize :parameters, Hash
else
serialize :parameters, coder: YAML, type: Hash
end
end
else
warn("[WARN] table #{name} doesn't exist. Skipping PublicActivity::Activity#parameters's serialization")
end
rescue ::ActiveRecord::NoDatabaseError
warn("[WARN] database doesn't exist. Skipping PublicActivity::Activity#parameters's serialization")
rescue ::ActiveRecord::ConnectionNotEstablished, ::PG::ConnectionBad, Mysql2::Error::ConnectionError
warn("[WARN] couldn't connect to database. Skipping PublicActivity::Activity#parameters's serialization")
end
end
end
end
end
================================================
FILE: lib/public_activity/orm/active_record/adapter.rb
================================================
# frozen_string_literal: true
module PublicActivity
module ORM
# Support for ActiveRecord for PublicActivity. Used by default and supported
# officialy.
module ActiveRecord
# Provides ActiveRecord specific, database-related routines for use by
# PublicActivity.
class Adapter
# Creates the activity on `trackable` with `options`
def self.create_activity(trackable, options)
trackable.activities.create options
end
# Creates activity on `trackable` with `options`; throws error on validation failure
def self.create_activity!(trackable, options)
trackable.activities.create! options
end
end
end
end
end
================================================
FILE: lib/public_activity/orm/active_record/trackable.rb
================================================
# frozen_string_literal: true
module PublicActivity
module ORM
module ActiveRecord
# Implements {PublicActivity::Trackable} for ActiveRecord
# @see PublicActivity::Trackable
module Trackable
# Creates an association for activities where self is the *trackable*
# object.
def self.extended(base)
base.has_many :activities,
class_name: '::PublicActivity::Activity',
as: :trackable
end
end
end
end
end
================================================
FILE: lib/public_activity/orm/active_record.rb
================================================
# frozen_string_literal: true
require 'active_record'
require_relative 'active_record/activity'
require_relative 'active_record/adapter'
require_relative 'active_record/activist'
require_relative 'active_record/trackable'
================================================
FILE: lib/public_activity/orm/mongo_mapper/activist.rb
================================================
# frozen_string_literal: true
module PublicActivity
module ORM
module MongoMapper
# Module extending classes that serve as owners
module Activist
# Adds MongoMapper associations to model to simplify fetching
# so you can list activities performed by the owner.
# It is completely optional. Any model can be an owner to an activity
# even without being an explicit activist.
#
# == Usage:
# In model:
#
# class User
# include MongoMapper::Document
# include PublicActivity::Model
# activist
# end
#
# In controller:
# User.first.activities
#
def activist
many :activities_as_owner,
:class_name => "::PublicActivity::Activity",
:as => :owner
many :activities_as_recipient,
:class_name => "::PublicActivity::Activity",
:as => :recipient
end
end
end
end
end
================================================
FILE: lib/public_activity/orm/mongo_mapper/activity.rb
================================================
# frozen_string_literal: true
require 'mongo_mapper'
require 'active_support/core_ext'
module PublicActivity
module ORM
module MongoMapper
# The MongoMapper document containing
# details about recorded activity.
class Activity
include ::MongoMapper::Document
include Renderable
class SymbolHash < Hash
def self.from_mongo(value)
value.symbolize_keys unless value.nil?
end
end
# Define polymorphic association to the parent
belongs_to :trackable, polymorphic: true
# Define ownership to a resource responsible for this activity
belongs_to :owner, polymorphic: true
# Define ownership to a resource targeted by this activity
belongs_to :recipient, polymorphic: true
key :key, String
key :parameters, SymbolHash
timestamps!
end
end
end
end
================================================
FILE: lib/public_activity/orm/mongo_mapper/adapter.rb
================================================
# frozen_string_literal: true
module PublicActivity
module ORM
module MongoMapper
class Adapter
# Creates the activity on `trackable` with `options`
def self.create_activity(trackable, options)
trackable.activities.create options
end
# Creates activity on `trackable` with `options`; throws error on validation failure
def self.create_activity!(trackable, options)
trackable.activities.create! options
end
end
end
end
end
================================================
FILE: lib/public_activity/orm/mongo_mapper/trackable.rb
================================================
# frozen_string_literal: true
module PublicActivity
module ORM
module MongoMapper
module Trackable
def self.extended(base)
base.many :activities, :class_name => "::PublicActivity::Activity", order: :created_at.asc, :as => :trackable
end
end
end
end
end
================================================
FILE: lib/public_activity/orm/mongo_mapper.rb
================================================
# frozen_string_literal: true
require_relative "mongo_mapper/activity.rb"
require_relative "mongo_mapper/adapter.rb"
require_relative "mongo_mapper/activist.rb"
require_relative "mongo_mapper/trackable.rb"
================================================
FILE: lib/public_activity/orm/mongoid/activist.rb
================================================
# frozen_string_literal: true
module PublicActivity
module ORM
module Mongoid
# Module extending classes that serve as owners
module Activist
# Adds ActiveRecord associations to model to simplify fetching
# so you can list activities performed by the owner.
# It is completely optional. Any model can be an owner to an activity
# even without being an explicit activist.
#
# == Usage:
# In model:
#
# class User < ActiveRecord::Base
# include PublicActivity::Model
# activist
# end
#
# In controller:
# User.first.activities
#
def activist
has_many :activities_as_owner,
:class_name => "::PublicActivity::Activity",
:inverse_of => :owner
has_many :activities_as_recipient,
:class_name => "::PublicActivity::Activity",
:inverse_of => :recipient
end
end
end
end
end
================================================
FILE: lib/public_activity/orm/mongoid/activity.rb
================================================
# frozen_string_literal: true
require 'mongoid'
module PublicActivity
module ORM
module Mongoid
# The ActiveRecord model containing
# details about recorded activity.
class Activity
include ::Mongoid::Document
include ::Mongoid::Timestamps
include ::Mongoid::Attributes::Dynamic if ::Mongoid::VERSION.split('.')[0].to_i >= 4
include Renderable
if ::Mongoid::VERSION.split('.')[0].to_i >= 7
opts = { polymorphic: true, optional: false }
else
opts = { polymorphic: true }
end
# Define polymorphic association to the parent
belongs_to :trackable, opts
# Define ownership to a resource responsible for this activity
belongs_to :owner, opts
# Define ownership to a resource targeted by this activity
belongs_to :recipient, opts
field :key, type: String
field :parameters, type: Hash
end
end
end
end
================================================
FILE: lib/public_activity/orm/mongoid/adapter.rb
================================================
# frozen_string_literal: true
module PublicActivity
module ORM
module Mongoid
class Adapter
# Creates the activity on `trackable` with `options`
def self.create_activity(trackable, options)
trackable.activities.create options
end
# Creates activity on `trackable` with `options`; throws error on validation failure
def self.create_activity!(trackable, options)
trackable.activities.create! options
end
end
end
end
end
================================================
FILE: lib/public_activity/orm/mongoid/trackable.rb
================================================
# frozen_string_literal: true
module PublicActivity
module ORM
module Mongoid
module Trackable
def self.extended(base)
base.has_many :activities, :class_name => "::PublicActivity::Activity", :as => :trackable
end
end
end
end
end
================================================
FILE: lib/public_activity/orm/mongoid.rb
================================================
# frozen_string_literal: true
require_relative "mongoid/activity.rb"
require_relative "mongoid/adapter.rb"
require_relative "mongoid/activist.rb"
require_relative "mongoid/trackable.rb"
================================================
FILE: lib/public_activity/renderable.rb
================================================
# frozen_string_literal: true
module PublicActivity
# Provides logic for rendering activities. Handles both i18n strings
# support and smart partials rendering (different templates per activity key).
module Renderable
# Virtual attribute returning text description of the activity
# using the activity's key to translate using i18n.
def text(params = {})
# TODO: some helper for key transformation for two supported formats
k = key.split('.')
k.unshift('activity') if k.first != 'activity'
k = k.join('.')
translate_params = parameters.merge(params) || {}
I18n.t(k, **translate_params)
end
# Renders activity from views.
#
# @param [ActionView::Base] context
# @return [nil] nil
#
# Renders activity to the given ActionView context with included
# AV::Helpers::RenderingHelper (most commonly just ActionView::Base)
#
# The *preferred* *way* of rendering activities is
# to provide a template specifying how the rendering should be happening.
# However, one may choose using _I18n_ based approach when developing
# an application that supports plenty of languages.
#
# If partial view exists that matches the *key* attribute
# renders that partial with local variables set to contain both
# Activity and activity_parameters (hash with indifferent access)
#
# Otherwise, it outputs the I18n translation to the context
# @example Render a list of all activities from a view (erb)
# <ul>
# <% for activity in PublicActivity::Activity.all %>
# <li><%= render_activity(activity) %></li>
# <% end %>
# </ul>
#
# = Layouts
# You can supply a layout that will be used for activity partials
# with :layout param.
# Keep in mind that layouts for partials are also partials.
# @example Supply a layout
# # in views:
# # All examples look for a layout in app/views/layouts/_activity.erb
# render_activity @activity, :layout => "activity"
# render_activity @activity, :layout => "layouts/activity"
# render_activity @activity, :layout => :activity
#
# # app/views/layouts/_activity.erb
# <p><%= a.created_at %></p>
# <%= yield %>
#
# == Custom Layout Location
# You can customize the layout directory by supplying :layout_root
# or by using an absolute path.
#
# @example Declare custom layout location
#
# # Both examples look for a layout in "app/views/custom/_layout.erb"
#
# render_activity @activity, :layout_root => "custom"
# render_activity @activity, :layout => "/custom/layout"
#
# = Creating a template
# To use templates for formatting how the activity should render,
# create a template based on activity key, for example:
#
# Given a key _activity.article.create_, create directory tree
# _app/views/public_activity/article/_ and create the _create_ partial there
#
# Note that if a key consists of more than three parts splitted by commas, your
# directory structure will have to be deeper, for example:
# activity.article.comments.destroy => app/views/public_activity/articles/comments/_destroy.html.erb
#
# == Custom Directory
# You can override the default `public_directory` template root with the :root parameter
#
# @example Custom template root
# # look for templates inside of /app/views/custom instead of /app/views/public_directory
# render_activity @activity, :root => "custom"
#
# == Variables in templates
# From within a template there are two variables at your disposal:
# * activity (aliased as *a* for a shortcut)
# * params (aliased as *p*) [converted into a HashWithIndifferentAccess]
#
# @example Template for key: _activity.article.create_ (erb)
# <p>
# Article <strong><%= p[:name] %></strong>
# was written by <em><%= p["author"] %></em>
# <%= distance_of_time_in_words_to_now(a.created_at) %>
# </p>
def render(context, params = {})
partial_root = params.delete(:root) || 'public_activity'
partial_path = nil
layout_root = params.delete(:layout_root) || 'layouts'
if params.has_key? :display
if params[:display].to_sym == :"i18n"
text = self.text(params)
return context.render :text => text, :plain => text
else
partial_path = File.join(partial_root, params[:display].to_s)
end
end
context.render(
params.merge({
:partial => prepare_partial(partial_root, partial_path),
:layout => prepare_layout(layout_root, params.delete(:layout)),
:locals => prepare_locals(params)
})
)
end
def prepare_partial(root, path)
path || self.template_path(self.key, root)
end
def prepare_locals(params)
locals = params.delete(:locals) || Hash.new
controller = PublicActivity.get_controller
prepared_params = prepare_parameters(params)
locals.merge(
{
:a => self,
:activity => self,
:controller => controller,
:current_user => controller.respond_to?(:current_user) ? controller.current_user : nil,
:p => prepared_params,
:params => prepared_params
}
)
end
def prepare_layout(root, layout)
if layout
path = layout.to_s
unless path.start_with?(root) || path.start_with?("/")
return File.join(root, path)
end
end
layout
end
def prepare_parameters(params)
if self.parameters
@prepared_params ||= self.parameters.with_indifferent_access.merge(params)
else
@prepared_params ||= params
end
end
protected
# Builds the path to template based on activity key
def template_path(key, partial_root)
path = key.split(".")
path.delete_at(0) if path[0] == "activity"
path.unshift partial_root
path.join("/")
end
end
end
================================================
FILE: lib/public_activity/roles/deactivatable.rb
================================================
# frozen_string_literal: true
module PublicActivity
# Enables per-class disabling of PublicActivity functionality.
module Deactivatable
extend ActiveSupport::Concern
included do
class_attribute :public_activity_enabled_for_model
set_public_activity_class_defaults
end
# Returns true if PublicActivity is enabled
# globally and for this class.
# @return [Boolean]
# @api private
# @since 0.5.0
# overrides the method from Common
def public_activity_enabled?
PublicActivity.enabled? && self.class.public_activity_enabled_for_model
end
# Provides global methods to disable or enable PublicActivity on a per-class
# basis.
module ClassMethods
# Switches public_activity off for this class
def public_activity_off
self.public_activity_enabled_for_model = false
end
# Switches public_activity on for this class
def public_activity_on
self.public_activity_enabled_for_model = true
end
# @since 1.0.0
# @api private
def set_public_activity_class_defaults
super
self.public_activity_enabled_for_model = true
end
end
end
end
================================================
FILE: lib/public_activity/roles/tracked.rb
================================================
# frozen_string_literal: true
module PublicActivity
# Main module extending classes we want to keep track of.
module Tracked
extend ActiveSupport::Concern
# A shortcut method for setting custom key, owner and parameters of {Activity}
# in one line. Accepts a hash with 3 keys:
# :key, :owner, :params. You can specify all of them or just the ones you want to overwrite.
#
# == Options
#
# [:key]
# See {Common#activity_key}
# [:owner]
# See {Common#activity_owner}
# [:params]
# See {Common#activity_params}
# [:recipient]
# Set the recipient for this activity. Useful for private notifications, which should only be visible to a certain user. See {Common#activity_recipient}.
# @example
#
# @article = Article.new
# @article.title = "New article"
# @article.activity :key => "my.custom.article.key", :owner => @article.author, :params => {:title => @article.title}
# @article.save
# @article.activities.last.key #=> "my.custom.article.key"
# @article.activities.last.parameters #=> {:title => "New article"}
#
# @param options [Hash] instance options to set on the tracked model
# @return [nil]
def activity(options = {})
rest = options.clone
self.activity_key = rest.delete(:key) if rest[:key]
self.activity_owner = rest.delete(:owner) if rest[:owner]
self.activity_params = rest.delete(:params) if rest[:params]
self.activity_recipient = rest.delete(:recipient) if rest[:recipient]
self.activity_custom_fields = rest if rest.count > 0
nil
end
# Module with basic +tracked+ method that enables tracking models.
class_methods do
# Adds required callbacks for creating and updating
# tracked models and adds +activities+ relation for listing
# associated activities.
#
# == Parameters:
# [:owner]
# Specify the owner of the {Activity} (person responsible for the action).
# It can be a Proc, Symbol or an ActiveRecord object:
# == Examples:
#
# tracked :owner => :author
# tracked :owner => proc {|o| o.author}
#
# Keep in mind that owner relation is polymorphic, so you can't just
# provide id number of the owner object.
# [:recipient]
# Specify the recipient of the {Activity}
# It can be a Proc, Symbol, or an ActiveRecord object
# == Examples:
#
# tracked :recipient => :author
# tracked :recipient => proc {|o| o.author}
#
# Keep in mind that recipient relation is polymorphic, so you can't just
# provide id number of the owner object.
# [:params]
# Accepts a Hash with custom parameters you want to pass to i18n.translate
# method. It is later used in {Renderable#text} method.
# == Example:
# class Article < ActiveRecord::Base
# include PublicActivity::Model
# tracked :params => {
# :title => :title,
# :author_name => "Michael",
# :category_name => proc {|controller, model_instance| model_instance.category.name},
# :summary => proc {|controller, model_instance| truncate(model.text, :length => 30)}
# }
# end
#
# Values in the :params hash can either be an *exact* *value*, a *Proc/Lambda* executed before saving the activity or a *Symbol*
# which is a an attribute or a method name executed on the tracked model's instance.
#
# Everything specified here has a lower priority than parameters
# specified directly in {#activity} method.
# So treat it as a place where you provide 'default' values or where you
# specify what data should be gathered for every activity.
# For more dynamic settings refer to {Activity} model documentation.
# [:skip_defaults]
# Disables recording of activities on create/update/destroy leaving that to programmer's choice. Check {PublicActivity::Common#create_activity}
# for a guide on how to manually record activities.
# [:only]
# Accepts a symbol or an array of symbols, of which any combination of the three is accepted:
# * _:create_
# * _:update_
# * _:destroy_
# Selecting one or more of these will make PublicActivity create activities
# automatically for the tracked model on selected actions.
#
# Resulting activities will have have keys assigned to, respectively:
# * _article.create_
# * _article.update_
# * _article.destroy_
# Since only three options are valid,
# see _:except_ option for a shorter version
# [:except]
# Accepts a symbol or an array of symbols with values like in _:only_, above.
# Values provided will be subtracted from all default actions:
# (create, update, destroy).
#
# So, passing _create_ would track and automatically create
# activities on _update_ and _destroy_ actions,
# but not on the _create_ action.
# [:on]
# Accepts a Hash with key being the *action* on which to execute *value* (proc)
# Currently supported only for CRUD actions which are enabled in _:only_
# or _:except_ options on this method.
#
# Key-value pairs in this option define callbacks that can decide
# whether to create an activity or not. Procs have two attributes for
# use: _model_ and _controller_. If the proc returns true, the activity
# will be created, if not, then activity will not be saved.
#
# == Example:
# # app/models/article.rb
# tracked :on => {:update => proc {|model, controller| model.published? }}
#
# In the example above, given a model Article with boolean column _published_.
# The activities with key _article.update_ will only be created
# if the published status is set to true on that article.
# @param opts [Hash] options
# @return [nil] options
def tracked(opts = {})
options = opts.clone
include_default_actions(options)
assign_globals options
assign_hooks options
assign_custom_fields options
nil
end
def include_default_actions(options)
defaults = {
create: Creation,
destroy: Destruction,
update: Update
}
return if options[:skip_defaults] == true
modules = if options[:except]
defaults.except(*options[:except])
elsif options[:only]
defaults.slice(*options[:only])
else
defaults
end
modules.each_value { |mod| include mod }
end
def available_options
%i[skip_defaults only except on owner recipient params].freeze
end
def assign_globals(options)
%i[owner recipient params].each do |key|
next unless options[key]
send("activity_#{key}_global=".to_sym, options.delete(key))
end
end
def assign_hooks(options)
return unless options[:on].is_a?(Hash)
self.activity_hooks = options[:on].select { |_, v| v.is_a? Proc }.symbolize_keys
end
def assign_custom_fields(options)
options.except(*available_options).each do |k, v|
activity_custom_fields_global[k] = v
end
end
end
end
end
================================================
FILE: lib/public_activity/testing.rb
================================================
# frozen_string_literal: true
# This file provides functionality for testing your code with public_activity
# activated or deactivated.
# This file should only be required in test/spec code!
#
# To enable PublicActivity testing capabilities do:
# require 'public_activity/testing'
module PublicActivity
# Execute the code block with PublicActiviy active
#
# Example usage:
# PublicActivity.with_tracking do
# # your test code here
# end
def self.with_tracking
current = PublicActivity.config.enabled
PublicActivity.config.enabled(true)
yield
ensure
PublicActivity.config.enabled(current)
end
# Execute the code block with PublicActiviy deactive
#
# Example usage:
# PublicActivity.without_tracking do
# # your test code here
# end
def self.without_tracking
current = PublicActivity.enabled?
PublicActivity.config.enabled(false)
yield
ensure
PublicActivity.config.enabled(current)
end
end
================================================
FILE: lib/public_activity/utility/store_controller.rb
================================================
# frozen_string_literal: true
module PublicActivity
class << self
# Setter for remembering controller instance
def set_controller(controller)
Thread.current[:public_activity_controller] = controller
end
# Getter for accessing the controller instance
def get_controller
Thread.current[:public_activity_controller]
end
end
# Module included in controllers to allow p_a access to controller instance
module StoreController
extend ActiveSupport::Concern
included do
around_action :store_controller_for_public_activity if respond_to?(:around_action)
around_filter :store_controller_for_public_activity unless respond_to?(:around_action)
end
def store_controller_for_public_activity
PublicActivity.set_controller(self)
yield
ensure
PublicActivity.set_controller(nil)
end
end
end
================================================
FILE: lib/public_activity/utility/view_helpers.rb
================================================
# frozen_string_literal: true
# Provides a shortcut from views to the rendering method.
module PublicActivity
# Module extending ActionView::Base and adding `render_activity` helper.
module ViewHelpers
# View helper for rendering an activity, calls {PublicActivity::Activity#render} internally.
def render_activity activities, options = {}
if activities.is_a? PublicActivity::Activity
activities.render self, options
elsif activities.respond_to?(:map)
# depend on ORMs to fetch as needed
# maybe we can support Postgres streaming with this?
activities.map {|activity| activity.render self, options.dup }.join.html_safe
end
end
alias_method :render_activities, :render_activity
# Helper for setting content_for in activity partial, needed to
# flush remains in between partial renders.
def single_content_for(name, content = nil, &block)
@view_flow.set(name, ActiveSupport::SafeBuffer.new)
content_for(name, content, &block)
end
end
end
ActiveSupport.on_load(:action_view) do
include PublicActivity::ViewHelpers
end
================================================
FILE: lib/public_activity/version.rb
================================================
# frozen_string_literal: true
module PublicActivity
VERSION = '3.0.2'
end
================================================
FILE: lib/public_activity.rb
================================================
# frozen_string_literal: true
require 'active_support'
require 'logger'
require 'action_view'
# +public_activity+ keeps track of changes made to models
# and allows you to display them to the users.
#
# Check {PublicActivity::Tracked::ClassMethods#tracked} for more details about customizing and specifying
# ownership to users.
module PublicActivity
extend ActiveSupport::Concern
extend ActiveSupport::Autoload
autoload :Activity, 'public_activity/models/activity'
autoload :Activist, 'public_activity/models/activist'
autoload :Adapter, 'public_activity/models/adapter'
autoload :Trackable, 'public_activity/models/trackable'
autoload :Common
autoload :Config
autoload :Creation, 'public_activity/actions/creation.rb'
autoload :Deactivatable,'public_activity/roles/deactivatable.rb'
autoload :Destruction, 'public_activity/actions/destruction.rb'
autoload :Renderable
autoload :Tracked, 'public_activity/roles/tracked.rb'
autoload :Update, 'public_activity/actions/update.rb'
autoload :VERSION
# Switches PublicActivity on or off.
# @param value [Boolean]
# @since 0.5.0
def self.enabled=(value)
config.enabled(value)
end
# Returns `true` if PublicActivity is on, `false` otherwise.
# Enabled by default.
# @return [Boolean]
# @since 0.5.0
def self.enabled?
config.enabled
end
# Returns PublicActivity's configuration object.
# @since 0.5.0
def self.config
@@config ||= PublicActivity::Config.instance
end
# Method used to choose which ORM to load
# when PublicActivity::Activity class is being autoloaded
def self.inherit_orm(model = 'Activity')
orm = PublicActivity.config.orm
require "public_activity/orm/#{orm}"
"PublicActivity::ORM::#{orm.to_s.classify}::#{model}".constantize
end
# Module to be included in ActiveRecord models. Adds required functionality.
module Model
extend ActiveSupport::Concern
included do
include Common
include Deactivatable
include Tracked
include Activist # optional associations by recipient|owner
end
end
end
require 'public_activity/utility/store_controller'
require 'public_activity/utility/view_helpers'
================================================
FILE: public_activity.gemspec
================================================
# frozen_string_literal: true
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
require 'public_activity/version'
Gem::Specification.new do |s|
s.name = 'public_activity'
s.version = PublicActivity::VERSION
s.platform = Gem::Platform::RUBY
s.authors = ['Juri Hahn', 'Piotrek Okoński', 'Kuba Okoński']
s.email = 'juri.hahn+public-activity@gmail.com'
s.homepage = 'https://github.com/public-activity/public_activity'
s.summary = 'Easy activity tracking for ActiveRecord models'
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.'
s.license = 'MIT'
s.metadata = {
"bug_tracker_uri" => "https://github.com/public-activity/public_activity/issues",
'changelog_uri' => 'https://github.com/public-activity/public_activity/blob/main/CHANGELOG.md',
"documentation_uri" => "https://rubydoc.info/gems/public_activity",
"homepage_uri" => s.homepage,
"source_code_uri" => "https://github.com/public-activity/public_activity",
"rubygems_mfa_required" => "true",
}
s.files = `git ls-files lib`.split("\n") + ['Gemfile', 'Rakefile', 'README.md', 'MIT-LICENSE']
s.test_files = `git ls-files test`.split("\n")
s.require_paths = ['lib']
s.required_ruby_version = '>= 3.0.0'
s.post_install_message = File.read('UPGRADING') if File.exist?('UPGRADING')
s.add_dependency 'actionpack', '>= 6.1'
s.add_dependency 'i18n', '>= 0.5.0'
s.add_dependency 'railties', '>= 6.1'
ENV['PA_ORM'] ||= 'active_record'
case ENV['PA_ORM']
when 'active_record'
s.add_dependency 'activerecord', '>= 6.1'
when 'mongoid'
s.add_dependency 'mongoid', '>= 4.0'
when 'mongo_mapper'
s.add_dependency 'bson_ext'
s.add_dependency 'mongo', '<= 1.9.2'
s.add_dependency 'mongo_mapper', '>= 0.12.0'
end
s.add_development_dependency 'appraisal'
s.add_development_dependency 'minitest'
s.add_development_dependency 'mocha'
s.add_development_dependency 'pry'
s.add_development_dependency 'redcarpet'
s.add_development_dependency 'simplecov'
s.add_development_dependency 'sqlite3', '~> 1.4'
s.add_development_dependency 'test-unit'
s.add_development_dependency 'yard'
s.add_development_dependency 'rake'
end
================================================
FILE: public_activity.sublime-project
================================================
{
"folders":
[
{
"path": "./",
"folder_exclude_patterns": ["coverage", ".yardoc", "doc"]
}
],
"settings":
{
"tab_size": 2,
"translate_tabs_to_spaces": true,
"trim_trailing_white_space_on_save": true
},
"build_systems":
[
{
"name": "PublicActivity Test Suite",
"linux": {
"working_dir": "$project_dir",
"cmd": ["bundle", "exec", "rake", "test"]
},
"selector": "source.ruby"
}
]
}
================================================
FILE: test/migrations/002_create_articles.rb
================================================
# frozen_string_literal: true
class CreateArticles < ActiveRecord::Migration[6.1]
def self.up
create_table :articles do |t|
t.string :name
t.boolean :published
t.belongs_to :user
t.timestamps
end
end
end
================================================
FILE: test/migrations/003_create_users.rb
================================================
# frozen_string_literal: true
class CreateUsers < ActiveRecord::Migration[6.1]
def self.up
create_table :users do |t|
t.string :name
t.timestamps
end
end
end
================================================
FILE: test/migrations/004_add_nonstandard_to_activities.rb
================================================
# frozen_string_literal: true
class AddNonstandardToActivities < ActiveRecord::Migration[6.1]
def change
change_table :activities do |t|
t.string :nonstandard
end
end
end
================================================
FILE: test/mongo_mapper.yml
================================================
test:
host: 127.0.0.1
port: 27017
database: public_activity_test
================================================
FILE: test/mongoid.yml
================================================
test:
clients:
default:
hosts:
- 127.0.0.1:27017
database: public_activity_test
================================================
FILE: test/test_activist.rb
================================================
# frozen_string_literal: true
require 'test_helper'
describe PublicActivity::Activist do
it 'adds owner association' do
klass = article
assert_respond_to klass, :activist
klass.activist
assert_respond_to klass.new, :activities
case ENV['PA_ORM']
when 'active_record'
assert_equal klass.reflect_on_association(:activities_as_owner).options[:as], :owner
when 'mongoid'
assert_equal klass.reflect_on_association(:activities_as_owner).options[:inverse_of], :owner
when 'mongo_mapper'
assert_equal klass.associations[:activities_as_owner].options[:as], :owner
end
if ENV['PA_ORM'] == 'mongo_mapper'
assert_equal klass.associations[:activities_as_owner].options[:class_name], '::PublicActivity::Activity'
else
assert_equal klass.reflect_on_association(:activities_as_owner).options[:class_name], '::PublicActivity::Activity'
end
end
it 'returns activities from association' do
case PublicActivity::Config.orm
when :active_record
class ActivistUser < ActiveRecord::Base
include PublicActivity::Model
self.table_name = 'users'
activist
end
when :mongoid
class ActivistUser
include Mongoid::Document
include PublicActivity::Model
activist
field :name, type: String
end
when :mongo_mapper
class ActivistUser
include MongoMapper::Document
include PublicActivity::Model
activist
key :name, String
end
end
owner = ActivistUser.create(name: 'Peter Pan')
a = article(owner: owner).new
a.save
assert_equal owner.activities_as_owner.length, 1
end
end
================================================
FILE: test/test_activity.rb
================================================
# frozen_string_literal: true
require 'test_helper'
describe 'PublicActivity::Activity Rendering' do
describe '#text' do
subject { PublicActivity::Activity.new(key: 'activity.test', parameters: { one: 1 }) }
specify '#text uses translations' do
subject.save
I18n.config.backend.store_translations(:en, activity: { test: '%{one} %{two}' })
assert_equal subject.text(two: 2), '1 2'
assert_equal subject.parameters, one: 1
end
end
describe '#render' do
subject do
PublicActivity::Activity.new(key: 'activity.test', parameters: { one: 1 }).tap(&:save)
end
let(:template_output) { "<strong>1, 2</strong>\n<em>activity.test, #{subject.id}</em>\n" }
before { @controller.class.prepend_view_path File.expand_path('views', __dir__) }
it 'uses view partials when available' do
PublicActivity.set_controller(Struct.new(:current_user).new('fake'))
subject.render(self, two: 2)
assert_equal rendered, "#{template_output}fake\n"
end
it 'uses requested partial'
it 'uses view partials without controller' do
PublicActivity.set_controller(nil)
subject.render(self, two: 2)
assert_equal rendered, "#{template_output}\n"
end
it 'provides local variables' do
PublicActivity.set_controller(nil)
subject.render(self, locals: { two: 2 })
assert_equal rendered.chomp, '2'
end
it 'uses translations only when requested' do
I18n.config.backend.store_translations(:en, activity: { test: '%{one} %{two}' })
subject.render(self, two: 2, display: :i18n)
assert_equal rendered, '1 2'
end
it 'pass all params to view context' do
view_context = mock('ViewContext')
PublicActivity.set_controller(nil)
view_context.expects(:render).with { |params| params[:formats] == ['json'] }
subject.render(view_context, formats: ['json'])
end
it 'uses specified layout' do
PublicActivity.set_controller(nil)
subject.render(self, layout: 'activity')
assert_includes rendered, 'Here be the layouts'
subject.render(self, layout: 'layouts/activity')
assert_includes rendered, 'Here be the layouts'
subject.render(self, layout: :activity)
assert_includes rendered, 'Here be the layouts'
end
it 'accepts a custom layout root' do
subject.render(self, layout: :layout, layout_root: 'custom')
assert_includes rendered, 'Here be the custom layouts'
end
it 'accepts an absolute layout path' do
subject.render(self, layout: '/custom/layout')
assert_includes rendered, 'Here be the custom layouts'
end
it 'accepts a template root' do
subject.render(self, root: 'custom')
assert_includes rendered, 'Custom Template Root'
end
end
end
================================================
FILE: test/test_common.rb
================================================
# frozen_string_literal: true
require 'test_helper'
describe PublicActivity::Common do
before do
@owner = User.create(name: 'Peter Pan')
@recipient = User.create(name: 'Bruce Wayne')
@options = {
params: {
author_name: 'Peter',
summary: 'Default summary goes here...'
},
owner: @owner,
recipient: @recipient
}
end
subject { article(@options).new }
it 'prioritizes parameters passed to #create_activity' do
subject.save
assert_equal subject.create_activity(:test, params: { author_name: 'Pan' }).parameters[:author_name], 'Pan'
assert_equal subject.create_activity(:test, parameters: { author_name: 'Pan' }).parameters[:author_name], 'Pan'
assert_nil subject.create_activity(:test, params: { author_name: nil }).parameters[:author_name]
assert_nil subject.create_activity(:test, parameters: { author_name: nil }).parameters[:author_name]
end
it 'prioritizes owner passed to #create_activity' do
subject.save
assert_equal subject.create_activity(:test, owner: @recipient).owner, @recipient
assert_nil subject.create_activity(:test, owner: nil).owner
end
it 'prioritizes recipient passed to #create_activity' do
subject.save
assert_equal subject.create_activity(:test, recipient: @owner).recipient, @owner
assert_nil subject.create_activity(:test, recipient: nil).recipient
end
it 'uses global fields' do
subject.save
activity = subject.activities.last
assert_equal activity.parameters, @options[:params]
assert_equal activity.owner, @owner
end
it 'allows custom fields' do
subject.save
subject.create_activity :with_custom_fields, nonstandard: 'Custom allowed'
assert_equal subject.activities.last.nonstandard, 'Custom allowed'
end
it '#create_activity returns a new activity object' do
subject.save
assert subject.create_activity('some.key')
end
it '#create_activity! returns a new activity object' do
subject.save
activity = subject.create_activity!('some.key')
assert activity.persisted?
assert_equal 'article.some.key', activity.key
end
it 'update action should not create activity on save unless model changed' do
subject.save
before_count = subject.activities.count
subject.save
subject.save
after_count = subject.activities.count
assert_equal before_count, after_count
end
it 'allows passing owner through #create_activity' do
article = article().new
article.save
activity = article.create_activity('some.key', owner: @owner)
assert_equal activity.owner, @owner
end
it 'allows resolving custom fields' do
subject.name = 'Resolving is great'
subject.published = true
subject.save
subject.create_activity :with_custom_fields, nonstandard: :name
assert_equal subject.activities.last.nonstandard, 'Resolving is great'
subject.create_activity :with_custom_fields_2, nonstandard: proc { |_, model| model.published.to_s }
assert_equal subject.activities.last.nonstandard, 'true'
end
it 'inherits instance parameters' do
subject.activity params: { author_name: 'Michael' }
subject.save
activity = subject.activities.last
assert_equal activity.parameters[:author_name], 'Michael'
end
it 'accepts instance recipient' do
subject.activity recipient: @recipient
subject.save
assert_equal subject.activities.last.recipient, @recipient
end
it 'accepts instance owner' do
subject.activity owner: @owner
subject.save
assert_equal subject.activities.last.owner, @owner
end
it 'accepts owner as a symbol' do
klass = article(owner: :user)
@article = klass.new(user: @owner)
@article.save
activity = @article.activities.last
assert_equal activity.owner, @owner
end
it 'reports PublicActivity::Activity as the base class' do
if ENV['PA_ORM'] == 'active_record' # Only relevant for ActiveRecord
subject.save
assert_equal subject.activities.last.class.base_class, PublicActivity::Activity
end
end
describe '#prepare_key' do
describe 'for class#activity_key method' do
before do
@article = article(owner: :user).new(user: @owner)
end
it 'assigns key to value of activity_key if set' do
def @article.activity_key; 'my_custom_key' end
assert_equal @article.prepare_key(:create, {}), 'my_custom_key'
end
it 'assigns key based on class name as fallback' do
def @article.activity_key; nil end
assert_equal @article.prepare_key(:create), 'article.create'
end
it 'assigns key value from options hash' do
assert_equal @article.prepare_key(:create, key: :my_custom_key), 'my_custom_key'
end
end
describe 'for camel cased classes' do
before do
class CamelCase < article(owner: :user)
def self.name; 'CamelCase' end
end
@camel_case = CamelCase.new
end
it 'assigns generates key from class name' do
assert_equal @camel_case.prepare_key(:create, {}), 'camel_case.create'
end
end
describe 'for namespaced classes' do
before do
module ::MyNamespace;
class CamelCase < article(owner: :user)
def self.name; 'MyNamespace::CamelCase' end
end
end
@namespaced_camel_case = MyNamespace::CamelCase.new
end
it 'assigns key value from options hash' do
assert_equal @namespaced_camel_case.prepare_key(:create, {}), 'my_namespace_camel_case.create'
end
end
end
# no key implicated or given
specify do
assert_raises(PublicActivity::NoKeyProvided) { subject.prepare_settings }
end
describe 'resolving values' do
it 'allows procs with models and controllers' do
context = mock('context')
context.expects(:accessor).times(2).returns(5)
controller = mock('controller')
controller.expects(:current_user).returns(:cu)
PublicActivity.set_controller(controller)
p = proc { |c, m|
assert_equal :cu, c.current_user
assert_equal 5, m.accessor
}
PublicActivity.resolve_value(context, p)
PublicActivity.resolve_value(context, :accessor)
end
end
def teardown
PublicActivity.set_controller(nil)
end
end
================================================
FILE: test/test_controller_integration.rb
================================================
# frozen_string_literal: true
require 'test_helper'
class StoringController < ActionView::TestCase::TestController
include PublicActivity::StoreController
include ActionController::Testing
end
describe PublicActivity::StoreController do
it 'stores controller' do
controller = StoringController.new
PublicActivity.set_controller(controller)
assert_same controller, Thread.current[:public_activity_controller]
assert_same controller, PublicActivity.get_controller
end
it 'stores controller with a filter in controller' do
controller = StoringController.new
callbacks = controller._process_action_callbacks.select { |c| c.kind == :around }.map(&:filter)
assert_includes(callbacks, :store_controller_for_public_activity)
public_activity_controller =
controller.instance_eval do
store_controller_for_public_activity do
PublicActivity.get_controller
end
end
assert_equal controller, public_activity_controller
end
it 'stores controller in a threadsafe way' do
PublicActivity.set_controller(1)
assert_equal PublicActivity.get_controller, 1
Thread.new do
PublicActivity.set_controller(2)
assert_equal 2, PublicActivity.get_controller
PublicActivity.set_controller(nil)
end
assert_equal PublicActivity.get_controller, 1
PublicActivity.set_controller(nil)
end
end
================================================
FILE: test/test_generators.rb
================================================
# frozen_string_literal: true
if ENV['PA_ORM'] == 'active_record'
require 'test_helper'
require 'rails/generators/test_case'
require 'generators/public_activity/migration/migration_generator'
require 'generators/public_activity/migration_upgrade/migration_upgrade_generator'
class TestMigrationGenerator < Rails::Generators::TestCase
tests PublicActivity::Generators::MigrationGenerator
destination File.expand_path('../tmp', File.dirname(__FILE__))
setup :prepare_destination
def test_generating_activity_model
run_generator
assert_migration 'db/migrate/create_activities.rb'
end
end
class TestMigrationUpgradeGenerator < Rails::Generators::TestCase
tests PublicActivity::Generators::MigrationUpgradeGenerator
destination File.expand_path('../tmp', File.dirname(__FILE__))
setup :prepare_destination
def test_generating_activity_model
run_generator
assert_migration 'db/migrate/upgrade_activities.rb'
end
end
end
================================================
FILE: test/test_helper.rb
================================================
# frozen_string_literal: true
require 'rubygems'
require 'bundler'
require 'logger'
Bundler.setup(:default, :test)
if ENV['COV']
require 'simplecov'
SimpleCov.start do
add_filter '/test/'
end
end
$:.unshift File.expand_path('../lib', __dir__)
require 'active_support/testing/setup_and_teardown'
require 'public_activity'
require 'public_activity/testing'
require 'pry'
require 'minitest/autorun'
require 'mocha/minitest'
PublicActivity::Config.orm = (ENV['PA_ORM'] || :active_record)
case PublicActivity::Config.orm
when :active_record
require 'active_record'
require 'active_record/connection_adapters/sqlite3_adapter'
require 'stringio' # silence the output
$stdout = StringIO.new # from migrator
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
migrations_path = File.expand_path('migrations', __dir__)
active_record_version = ActiveRecord.version.release
if active_record_version >= Gem::Version.new('7.2.0')
migration_context = ActiveRecord::MigrationContext.new(migrations_path)
migrations = migration_context.migrations # => Array<ActiveRecord::MigrationProxy>
connection_pool = ActiveRecord::Tasks::DatabaseTasks.migration_connection_pool
schema_migration = ActiveRecord::SchemaMigration.new(connection_pool)
internal_metadata = ActiveRecord::InternalMetadata.new(connection_pool)
ActiveRecord::Migrator.new(
:up,
migrations,
schema_migration,
internal_metadata
).migrate
else
ActiveRecord::MigrationContext.new(migrations_path, ActiveRecord::SchemaMigration).migrate
end
$stdout = STDOUT
def article(options = {})
Class.new(ActiveRecord::Base) do
self.table_name = 'articles'
include PublicActivity::Model
tracked options
belongs_to :user
def self.name
'Article'
end
end
end
class User < ActiveRecord::Base; end
when :mongoid
require 'mongoid'
Mongoid.load!(File.expand_path('test/mongoid.yml'), :test)
class User
include Mongoid::Document
include Mongoid::Timestamps
has_many :articles
field :name, type: String
end
class Article
include Mongoid::Document
include Mongoid::Timestamps
include PublicActivity::Model
if ::Mongoid::VERSION.split('.')[0].to_i >= 7
belongs_to :user, optional: true
else
belongs_to :user
end
field :name, type: String
field :published, type: Boolean
end
def article(options = {})
Article.class_eval do
set_public_activity_class_defaults
tracked options
end
Article
end
when :mongo_mapper
require 'mongo_mapper'
config = YAML.safe_load(File.read('test/mongo_mapper.yml'), aliases: true)
MongoMapper.setup(config, :test)
class User
include MongoMapper::Document
has_many :articles
key :name, String
timestamps!
end
class Article
include MongoMapper::Document
include PublicActivity::Model
belongs_to :user
key :name, String
key :published, Boolean
end
def article(options = {})
Article.class_eval do
set_public_activity_class_defaults
tracked options
end
Article
end
end
class ViewSpec < Minitest::Spec
prepend ActiveSupport::Testing::SetupAndTeardown
include ActionView::TestCase::Behavior
end
Minitest::Spec.register_spec_type(/Rendering$/, ViewSpec)
================================================
FILE: test/test_testing.rb
================================================
# frozen_string_literal: true
require 'test_helper'
describe PublicActivity do
describe 'self.with_tracking' do
after do
PublicActivity.enabled = true
end
it 'enables tracking inside the block' do
PublicActivity.enabled = false
PublicActivity.with_tracking do
assert PublicActivity.enabled?
end
end
it 'restores previous `enabled` state' do
PublicActivity.enabled = false
PublicActivity.with_tracking do
# something
end
assert_equal PublicActivity.enabled?, false
end
end
describe 'self.without_tracking' do
it 'disables tracking inside the block' do
PublicActivity.enabled = true
PublicActivity.without_tracking do
assert_equal PublicActivity.enabled?, false
end
end
end
end
================================================
FILE: test/test_tracking.rb
================================================
# frozen_string_literal: true
require 'test_helper'
describe PublicActivity::Tracked do
describe 'defining instance options' do
subject { article.new }
let :options do
{
key: 'key',
params: { a: 1 },
owner: User.create,
recipient: User.create
}
end
before(:each) { subject.activity(options) }
let(:activity) { subject.save; subject.activities.last }
specify { assert_same subject.activity_key, options[:key] }
specify { assert_equal activity.key, options[:key] }
specify { assert_same subject.activity_owner, options[:owner] }
specify { assert_equal activity.owner, options[:owner] }
specify { assert_same subject.activity_params, options[:params] }
specify { assert_equal activity.parameters, options[:params] }
specify { assert_same subject.activity_recipient, options[:recipient] }
specify { assert_equal activity.recipient, options[:recipient] }
end
it 'can be tracked and be an activist at the same time' do
case PublicActivity.config.orm
when :mongoid
class ActivistAndTrackedArticle
include Mongoid::Document
include Mongoid::Timestamps
include PublicActivity::Model
if ::Mongoid::VERSION.split('.')[0].to_i >= 7
belongs_to :user, optional: true
else
belongs_to :user
end
field :name, type: String
field :published, type: Boolean
tracked
activist
end
when :mongo_mapper
class ActivistAndTrackedArticle
include MongoMapper::Document
include PublicActivity::Model
belongs_to :user
key :name, String
key :published, Boolean
tracked
activist
timestamps!
end
when :active_record
class ActivistAndTrackedArticle < ActiveRecord::Base
self.table_name = 'articles'
include PublicActivity::Model
tracked
activist
belongs_to :user
end
end
art = ActivistAndTrackedArticle.new
art.save
assert_equal art.activities.last.trackable_id, art.id
assert_nil art.activities.last.owner_id
end
describe 'custom fields' do
describe 'global' do
it 'should resolve symbols' do
a = article(nonstandard: :name).new(name: 'Symbol resolved')
a.save
assert_equal a.activities.last.nonstandard, 'Symbol resolved'
end
it 'should resolve procs' do
a = article(nonstandard: proc { |_, model| model.name }).new(name: 'Proc resolved')
a.save
assert_equal a.activities.last.nonstandard, 'Proc resolved'
end
end
describe 'instance' do
it 'should resolve symbols' do
a = article.new(name: 'Symbol resolved')
a.activity nonstandard: :name
a.save
assert_equal a.activities.last.nonstandard, 'Symbol resolved'
end
it 'should resolve procs' do
a = article.new(name: 'Proc resolved')
a.activity nonstandard: proc { |_, model| model.name }
a.save
assert_equal a.activities.last.nonstandard, 'Proc resolved'
end
end
end
it 'should reset instance options on successful create_activity' do
a = article.new
a.activity key: 'test', params: { test: 1 }
a.save
assert_equal a.activities.count, 1
assert_raises(PublicActivity::NoKeyProvided) { a.create_activity }
assert_empty a.activity_params
a.activity key: 'asd'
a.create_activity
assert_raises(PublicActivity::NoKeyProvided) { a.create_activity }
end
it 'should not accept global key option' do
# this example tests the lack of presence of sth that should not be here
a = article(key: 'asd').new
a.save
assert_raises(PublicActivity::NoKeyProvided) { a.create_activity }
assert_equal a.activities.count, 1
end
it 'should not change global custom fields' do
a = article(nonstandard: 'global').new
a.activity nonstandard: 'instance'
a.save
assert_equal a.class.activity_custom_fields_global, nonstandard: 'global'
end
describe 'disabling functionality' do
it 'allows for global disable' do
PublicActivity.enabled = false
activity_count_before = PublicActivity::Activity.count
@article = article.new
@article.save
assert_equal PublicActivity::Activity.count, activity_count_before
PublicActivity.enabled = true
end
it 'allows for class-wide disable' do
activity_count_before = PublicActivity::Activity.count
klass = article
klass.public_activity_off
@article = klass.new
@article.save
assert_equal PublicActivity::Activity.count, activity_count_before
klass.public_activity_on
@article.name = 'Changed Article'
@article.save
assert(PublicActivity::Activity.count > activity_count_before)
end
end
describe '#tracked' do
subject { article(options) }
let(:options) { {} }
it 'allows skipping the tracking on CRUD actions' do
art =
case PublicActivity.config.orm
when :mongoid
Class.new do
include Mongoid::Document
include Mongoid::Timestamps
include PublicActivity::Model
belongs_to :user
field :name, type: String
field :published, type: Boolean
tracked skip_defaults: true
end
when :mongo_mapper
Class.new do
include MongoMapper::Document
include PublicActivity::Model
belongs_to :user
key :name, String
key :published, Boolean
tracked skip_defaults: true
timestamps!
end
when :active_record
article(skip_defaults: true)
end
assert_includes art, PublicActivity::Common
refute_includes art, PublicActivity::Creation
refute_includes art, PublicActivity::Update
refute_includes art, PublicActivity::Destruction
end
describe 'default options' do
subject { article }
specify { assert_includes subject, PublicActivity::Creation }
specify { assert_includes subject, PublicActivity::Destruction }
specify { assert_includes subject, PublicActivity::Update }
specify do
callbacks = subject._create_callbacks.select do |c|
c.kind.eql?(:after) && c.filter == :activity_on_create
end
refute_empty callbacks
end
specify do
callbacks = subject._update_callbacks.select do |c|
c.kind.eql?(:after) && c.filter == :activity_on_update
end
refute_empty callbacks
end
specify do
callbacks = subject._destroy_callbacks.select do |c|
c.kind.eql?(:before) && c.filter == :activity_on_destroy
end
refute_empty callbacks
end
end
it 'accepts :except option' do
art =
case PublicActivity.config.orm
when :mongoid
Class.new do
include Mongoid::Document
include Mongoid::Timestamps
include PublicActivity::Model
belongs_to :user
field :name, type: String
field :published, type: Boolean
tracked except: [:create]
end
when :mongo_mapper
Class.new do
include MongoMapper::Document
include PublicActivity::Model
belongs_to :user
key :name, String
key :published, Boolean
tracked except: [:create]
timestamps!
end
when :active_record
article(except: [:create])
end
refute_includes art, PublicActivity::Creation
assert_includes art, PublicActivity::Update
assert_includes art, PublicActivity::Destruction
end
it 'accepts :only option' do
art =
case PublicActivity.config.orm
when :mongoid
Class.new do
include Mongoid::Document
include Mongoid::Timestamps
include PublicActivity::Model
belongs_to :user
field :name, type: String
field :published, type: Boolean
tracked only: %i[create update]
end
when :mongo_mapper
Class.new do
include MongoMapper::Document
include PublicActivity::Model
belongs_to :user
key :name, String
key :published, Boolean
tracked only: %i[create update]
end
when :active_record
article(only: %I[create update])
end
assert_includes art, PublicActivity::Creation
refute_includes art, PublicActivity::Destruction
assert_includes art, PublicActivity::Update
end
it 'accepts :owner option' do
owner = mock('owner')
subject.tracked(owner: owner)
assert_equal subject.activity_owner_global, owner
end
it 'accepts :params option' do
params = { a: 1 }
subject.tracked(params: params)
assert_equal subject.activity_params_global, params
end
it 'accepts :on option' do
on = { a: -> {}, b: proc {} }
subject.tracked(on: on)
assert_equal subject.activity_hooks, on
end
it 'accepts :on option with string keys' do
on = { 'a' => -> {} }
subject.tracked(on: on)
assert_equal subject.activity_hooks, on.symbolize_keys
end
it 'accepts :on values that are procs' do
on = { unpassable: 1, proper: -> {}, proper_proc: proc {} }
subject.tracked(on: on)
assert_includes subject.activity_hooks, :proper
assert_includes subject.activity_hooks, :proper_proc
refute_includes subject.activity_hooks, :unpassable
end
describe 'global options' do
subject { article(recipient: :test, owner: :test2, params: { a: 'b' }) }
specify { assert_equal subject.activity_recipient_global, :test }
specify { assert_equal subject.activity_owner_global, :test2 }
specify { assert_equal subject.activity_params_global, a: 'b' }
end
end
describe 'activity hooks' do
subject do
s = article
s.activity_hooks = { test: hook }
s
end
let(:hook) { -> {} }
it 'retrieves hooks' do
assert_same hook, subject.get_hook(:test)
end
it 'retrieves hooks by string keys' do
assert_same hook, subject.get_hook('test')
end
it 'returns nil when no matching hook is present' do
assert_same nil, subject.get_hook(:nonexistent)
end
it 'allows hooks to decide if activity should be created' do
subject.tracked
@article = subject.new(name: 'Some Name')
PublicActivity.set_controller(mock('controller'))
pf = proc { |model, controller|
assert_same controller, PublicActivity.get_controller
assert_equal model.name, 'Some Name'
false
}
pt = proc { |model, controller|
assert_same controller, PublicActivity.get_controller
assert_equal model.name, 'Other Name'
true # this will save the activity with *.update key
}
@article.class.activity_hooks = { create: pf, update: pt, destroy: pt }
assert_empty @article.activities.to_a
@article.save # create
@article.name = 'Other Name'
@article.save # update
@article.destroy # destroy
assert_equal @article.activities.count, 2
assert_equal @article.activities.first.key, 'article.update'
end
end
def teardown
PublicActivity.set_controller(nil)
end
end
================================================
FILE: test/test_view_helpers.rb
================================================
# frozen_string_literal: true
require 'test_helper'
describe 'ViewHelpers Rendering' do
include PublicActivity::ViewHelpers
# is this a proper test?
it 'provides render_activity helper' do
activity = mock('activity')
activity.stubs(:is_a?).with(PublicActivity::Activity).returns(true)
activity.expects(:render).with(self, {})
render_activity(activity)
end
it 'handles multiple activities' do
activity = mock('activity')
activity.expects(:render).with(self, {})
render_activities([activity])
end
it 'flushes content_for between partials renderes' do
@view_flow = mock('view_flow')
@view_flow.expects(:set).twice.with('name', ActiveSupport::SafeBuffer.new)
single_content_for('name', 'content')
assert_equal @name, 'name'
assert_equal @content, 'content'
single_content_for('name', 'content2')
assert_equal @name, 'name'
assert_equal @content, 'content2'
end
def content_for(name, content)
@name = name
@content = content
end
end
================================================
FILE: test/views/custom/_layout.erb
================================================
<h2>Here be the custom layouts</h2><%= yield %>
================================================
FILE: test/views/custom/_test.erb
================================================
Custom Template Root <%= activity.id %>
================================================
FILE: test/views/layouts/_activity.erb
================================================
<h2>Here be the layouts</h2><%= yield %>
================================================
FILE: test/views/public_activity/_test.erb
================================================
<% if defined?(two) == 'local-variable' %>
<% # output local_variable if defined %>
<%= two %>
<% else %>
<strong><%= p[:one] %>, <%= params['two'] %></strong>
<em><%= a.key %>, <%= activity.id %></em>
<%= current_user %>
<% end %>
gitextract_a19chaa2/
├── .editorconfig
├── .github/
│ └── workflows/
│ └── main.yml
├── .gitignore
├── .travis.yml
├── Appraisals
├── CHANGELOG.md
├── Gemfile
├── MIT-LICENSE
├── README.md
├── Rakefile
├── gemfiles/
│ ├── .bundle/
│ │ └── config
│ ├── rails_6.1.gemfile
│ ├── rails_7.0.gemfile
│ └── rails_7.1.gemfile
├── lib/
│ ├── generators/
│ │ ├── public_activity/
│ │ │ ├── migration/
│ │ │ │ ├── migration_generator.rb
│ │ │ │ └── templates/
│ │ │ │ └── migration.rb
│ │ │ └── migration_upgrade/
│ │ │ ├── migration_upgrade_generator.rb
│ │ │ └── templates/
│ │ │ └── upgrade.rb
│ │ └── public_activity.rb
│ ├── public_activity/
│ │ ├── actions/
│ │ │ ├── creation.rb
│ │ │ ├── destruction.rb
│ │ │ └── update.rb
│ │ ├── activity.rb
│ │ ├── common.rb
│ │ ├── config.rb
│ │ ├── models/
│ │ │ ├── activist.rb
│ │ │ ├── activity.rb
│ │ │ ├── adapter.rb
│ │ │ └── trackable.rb
│ │ ├── orm/
│ │ │ ├── active_record/
│ │ │ │ ├── activist.rb
│ │ │ │ ├── activity.rb
│ │ │ │ ├── adapter.rb
│ │ │ │ └── trackable.rb
│ │ │ ├── active_record.rb
│ │ │ ├── mongo_mapper/
│ │ │ │ ├── activist.rb
│ │ │ │ ├── activity.rb
│ │ │ │ ├── adapter.rb
│ │ │ │ └── trackable.rb
│ │ │ ├── mongo_mapper.rb
│ │ │ ├── mongoid/
│ │ │ │ ├── activist.rb
│ │ │ │ ├── activity.rb
│ │ │ │ ├── adapter.rb
│ │ │ │ └── trackable.rb
│ │ │ └── mongoid.rb
│ │ ├── renderable.rb
│ │ ├── roles/
│ │ │ ├── deactivatable.rb
│ │ │ └── tracked.rb
│ │ ├── testing.rb
│ │ ├── utility/
│ │ │ ├── store_controller.rb
│ │ │ └── view_helpers.rb
│ │ └── version.rb
│ └── public_activity.rb
├── public_activity.gemspec
├── public_activity.sublime-project
└── test/
├── migrations/
│ ├── 002_create_articles.rb
│ ├── 003_create_users.rb
│ └── 004_add_nonstandard_to_activities.rb
├── mongo_mapper.yml
├── mongoid.yml
├── test_activist.rb
├── test_activity.rb
├── test_common.rb
├── test_controller_integration.rb
├── test_generators.rb
├── test_helper.rb
├── test_testing.rb
├── test_tracking.rb
├── test_view_helpers.rb
└── views/
├── custom/
│ ├── _layout.erb
│ └── _test.erb
├── layouts/
│ └── _activity.erb
└── public_activity/
└── _test.erb
SYMBOL INDEX (217 symbols across 45 files)
FILE: lib/generators/public_activity.rb
type PublicActivity (line 5) | module PublicActivity
type Generators (line 7) | module Generators
type Base (line 9) | module Base
function source_root (line 11) | def source_root
FILE: lib/generators/public_activity/migration/migration_generator.rb
type PublicActivity (line 6) | module PublicActivity
type Generators (line 7) | module Generators
class MigrationGenerator (line 9) | class MigrationGenerator < ActiveRecord::Generators::Base
method generate_files (line 14) | def generate_files
FILE: lib/generators/public_activity/migration/templates/migration.rb
class CreateActivities (line 4) | class CreateActivities < ActiveRecord::Migration[6.1]
method up (line 5) | def self.up
method down (line 18) | def self.down
FILE: lib/generators/public_activity/migration_upgrade/migration_upgrade_generator.rb
type PublicActivity (line 6) | module PublicActivity
type Generators (line 7) | module Generators
class MigrationUpgradeGenerator (line 9) | class MigrationUpgradeGenerator < ActiveRecord::Generators::Base
method generate_files (line 14) | def generate_files
FILE: lib/generators/public_activity/migration_upgrade/templates/upgrade.rb
class UpgradeActivities (line 4) | class UpgradeActivities < ActiveRecord::Migration[5.0]
method change (line 6) | def self.change
FILE: lib/public_activity.rb
type PublicActivity (line 11) | module PublicActivity
function enabled= (line 32) | def self.enabled=(value)
function enabled? (line 40) | def self.enabled?
function config (line 46) | def self.config
function inherit_orm (line 52) | def self.inherit_orm(model = 'Activity')
type Model (line 59) | module Model
FILE: lib/public_activity/actions/creation.rb
type PublicActivity (line 3) | module PublicActivity
type Creation (line 5) | module Creation
function activity_on_create (line 15) | def activity_on_create
FILE: lib/public_activity/actions/destruction.rb
type PublicActivity (line 3) | module PublicActivity
type Destruction (line 5) | module Destruction
function activity_on_destroy (line 15) | def activity_on_destroy
FILE: lib/public_activity/actions/update.rb
type PublicActivity (line 3) | module PublicActivity
type Update (line 5) | module Update
function activity_on_update (line 15) | def activity_on_update
FILE: lib/public_activity/activity.rb
type PublicActivity (line 3) | module PublicActivity
class Activity (line 6) | class Activity < inherit_orm("Activity")
FILE: lib/public_activity/common.rb
type PublicActivity (line 3) | module PublicActivity
class NoKeyProvided (line 5) | class NoKeyProvided < Exception; end
function resolve_value (line 11) | def self.resolve_value(context, thing)
type Common (line 23) | module Common
function set_public_activity_class_defaults (line 130) | def set_public_activity_class_defaults
function get_hook (line 148) | def get_hook(key)
function public_activity_enabled? (line 161) | def public_activity_enabled?
function get_hook (line 170) | def get_hook(key)
function call_hook_safe (line 181) | def call_hook_safe(key)
function create_activity (line 248) | def create_activity(*args)
function create_activity! (line 265) | def create_activity!(*args)
function prepare_settings (line 287) | def prepare_settings(*args)
function prepare_custom_fields (line 307) | def prepare_custom_fields(options)
function prepare_parameters (line 319) | def prepare_parameters(options)
function prepare_relation (line 330) | def prepare_relation(name, options)
function prepare_key (line 343) | def prepare_key(action, options = {})
function reset_activity_instance_options (line 355) | def reset_activity_instance_options
FILE: lib/public_activity/config.rb
type PublicActivity (line 5) | module PublicActivity
class Config (line 7) | class Config
method set (line 17) | def self.set(&block)
method orm= (line 28) | def self.orm=(orm = nil)
method enabled= (line 34) | def self.enabled=(value = nil)
method orm (line 40) | def orm(orm = nil)
method table_name (line 46) | def table_name(name = nil)
method enabled (line 52) | def enabled(value = nil)
method orm (line 57) | def self.orm(orm = nil)
method table_name (line 65) | def self.table_name(name = nil)
method enabled (line 73) | def self.enabled(value = nil)
class Block (line 83) | class Block
method orm (line 85) | def orm(orm = nil)
method enabled (line 91) | def enabled(value = nil)
method table_name (line 96) | def table_name(name = nil)
FILE: lib/public_activity/models/activist.rb
type PublicActivity (line 3) | module PublicActivity
type Activist (line 5) | module Activist
function included (line 7) | def self.included(base)
FILE: lib/public_activity/models/activity.rb
type PublicActivity (line 3) | module PublicActivity
class Activity (line 4) | class Activity < inherit_orm
FILE: lib/public_activity/models/adapter.rb
type PublicActivity (line 3) | module PublicActivity
class Adapter (line 5) | class Adapter < inherit_orm('Adapter')
FILE: lib/public_activity/models/trackable.rb
type PublicActivity (line 3) | module PublicActivity
type Trackable (line 5) | module Trackable
function included (line 7) | def self.included(base)
FILE: lib/public_activity/orm/active_record/activist.rb
type PublicActivity (line 3) | module PublicActivity
type ORM (line 4) | module ORM
type ActiveRecord (line 5) | module ActiveRecord
type Activist (line 7) | module Activist
function activist (line 25) | def activist
FILE: lib/public_activity/orm/active_record/activity.rb
type PublicActivity (line 3) | module PublicActivity
type ::PG (line 5) | module ::PG
class ConnectionBad (line 6) | class ConnectionBad < Exception; end
type Mysql2 (line 10) | module Mysql2
type Error (line 11) | module Error
class ConnectionError (line 12) | class ConnectionError < Exception; end
type ORM (line 17) | module ORM
type ActiveRecord (line 18) | module ActiveRecord
class Activity (line 21) | class Activity < ::ActiveRecord::Base
FILE: lib/public_activity/orm/active_record/adapter.rb
type PublicActivity (line 3) | module PublicActivity
type ORM (line 4) | module ORM
type ActiveRecord (line 7) | module ActiveRecord
class Adapter (line 10) | class Adapter
method create_activity (line 12) | def self.create_activity(trackable, options)
method create_activity! (line 17) | def self.create_activity!(trackable, options)
FILE: lib/public_activity/orm/active_record/trackable.rb
type PublicActivity (line 3) | module PublicActivity
type ORM (line 4) | module ORM
type ActiveRecord (line 5) | module ActiveRecord
type Trackable (line 8) | module Trackable
function extended (line 11) | def self.extended(base)
FILE: lib/public_activity/orm/mongo_mapper/activist.rb
type PublicActivity (line 3) | module PublicActivity
type ORM (line 4) | module ORM
type MongoMapper (line 5) | module MongoMapper
type Activist (line 7) | module Activist
function activist (line 25) | def activist
FILE: lib/public_activity/orm/mongo_mapper/activity.rb
type PublicActivity (line 6) | module PublicActivity
type ORM (line 7) | module ORM
type MongoMapper (line 8) | module MongoMapper
class Activity (line 11) | class Activity
class SymbolHash (line 15) | class SymbolHash < Hash
method from_mongo (line 16) | def self.from_mongo(value)
FILE: lib/public_activity/orm/mongo_mapper/adapter.rb
type PublicActivity (line 3) | module PublicActivity
type ORM (line 4) | module ORM
type MongoMapper (line 5) | module MongoMapper
class Adapter (line 6) | class Adapter
method create_activity (line 8) | def self.create_activity(trackable, options)
method create_activity! (line 13) | def self.create_activity!(trackable, options)
FILE: lib/public_activity/orm/mongo_mapper/trackable.rb
type PublicActivity (line 3) | module PublicActivity
type ORM (line 4) | module ORM
type MongoMapper (line 5) | module MongoMapper
type Trackable (line 6) | module Trackable
function extended (line 7) | def self.extended(base)
FILE: lib/public_activity/orm/mongoid/activist.rb
type PublicActivity (line 3) | module PublicActivity
type ORM (line 4) | module ORM
type Mongoid (line 5) | module Mongoid
type Activist (line 7) | module Activist
function activist (line 24) | def activist
FILE: lib/public_activity/orm/mongoid/activity.rb
type PublicActivity (line 5) | module PublicActivity
type ORM (line 6) | module ORM
type Mongoid (line 7) | module Mongoid
class Activity (line 10) | class Activity
FILE: lib/public_activity/orm/mongoid/adapter.rb
type PublicActivity (line 3) | module PublicActivity
type ORM (line 4) | module ORM
type Mongoid (line 5) | module Mongoid
class Adapter (line 6) | class Adapter
method create_activity (line 8) | def self.create_activity(trackable, options)
method create_activity! (line 13) | def self.create_activity!(trackable, options)
FILE: lib/public_activity/orm/mongoid/trackable.rb
type PublicActivity (line 3) | module PublicActivity
type ORM (line 4) | module ORM
type Mongoid (line 5) | module Mongoid
type Trackable (line 6) | module Trackable
function extended (line 7) | def self.extended(base)
FILE: lib/public_activity/renderable.rb
type PublicActivity (line 3) | module PublicActivity
type Renderable (line 6) | module Renderable
function text (line 9) | def text(params = {})
function render (line 99) | def render(context, params = {})
function prepare_partial (line 122) | def prepare_partial(root, path)
function prepare_locals (line 126) | def prepare_locals(params)
function prepare_layout (line 143) | def prepare_layout(root, layout)
function prepare_parameters (line 153) | def prepare_parameters(params)
function template_path (line 164) | def template_path(key, partial_root)
FILE: lib/public_activity/roles/deactivatable.rb
type PublicActivity (line 3) | module PublicActivity
type Deactivatable (line 5) | module Deactivatable
function public_activity_enabled? (line 19) | def public_activity_enabled?
type ClassMethods (line 25) | module ClassMethods
function public_activity_off (line 27) | def public_activity_off
function public_activity_on (line 32) | def public_activity_on
function set_public_activity_class_defaults (line 38) | def set_public_activity_class_defaults
FILE: lib/public_activity/roles/tracked.rb
type PublicActivity (line 3) | module PublicActivity
type Tracked (line 5) | module Tracked
function activity (line 32) | def activity(options = {})
function tracked (line 135) | def tracked(opts = {})
function include_default_actions (line 147) | def include_default_actions(options)
function available_options (line 167) | def available_options
function assign_globals (line 171) | def assign_globals(options)
function assign_hooks (line 179) | def assign_hooks(options)
function assign_custom_fields (line 185) | def assign_custom_fields(options)
FILE: lib/public_activity/testing.rb
type PublicActivity (line 9) | module PublicActivity
function with_tracking (line 16) | def self.with_tracking
function without_tracking (line 30) | def self.without_tracking
FILE: lib/public_activity/utility/store_controller.rb
type PublicActivity (line 3) | module PublicActivity
function set_controller (line 6) | def set_controller(controller)
function get_controller (line 11) | def get_controller
type StoreController (line 17) | module StoreController
function store_controller_for_public_activity (line 25) | def store_controller_for_public_activity
FILE: lib/public_activity/utility/view_helpers.rb
type PublicActivity (line 4) | module PublicActivity
type ViewHelpers (line 6) | module ViewHelpers
function render_activity (line 8) | def render_activity activities, options = {}
function single_content_for (line 21) | def single_content_for(name, content = nil, &block)
FILE: lib/public_activity/version.rb
type PublicActivity (line 3) | module PublicActivity
FILE: test/migrations/002_create_articles.rb
class CreateArticles (line 3) | class CreateArticles < ActiveRecord::Migration[6.1]
method up (line 4) | def self.up
FILE: test/migrations/003_create_users.rb
class CreateUsers (line 3) | class CreateUsers < ActiveRecord::Migration[6.1]
method up (line 4) | def self.up
FILE: test/migrations/004_add_nonstandard_to_activities.rb
class AddNonstandardToActivities (line 3) | class AddNonstandardToActivities < ActiveRecord::Migration[6.1]
method change (line 4) | def change
FILE: test/test_activist.rb
class ActivistUser (line 31) | class ActivistUser < ActiveRecord::Base
class ActivistUser (line 37) | class ActivistUser
class ActivistUser (line 45) | class ActivistUser
FILE: test/test_common.rb
function activity_key (line 134) | def @article.activity_key; 'my_custom_key' end
function activity_key (line 140) | def @article.activity_key; nil end
class CamelCase (line 152) | class CamelCase < article(owner: :user)
method name (line 153) | def self.name; 'CamelCase' end
type ::MyNamespace (line 165) | module ::MyNamespace;
class CamelCase (line 166) | class CamelCase < article(owner: :user)
method name (line 167) | def self.name; 'MyNamespace::CamelCase' end
function teardown (line 200) | def teardown
FILE: test/test_controller_integration.rb
class StoringController (line 5) | class StoringController < ActionView::TestCase::TestController
FILE: test/test_generators.rb
class TestMigrationGenerator (line 10) | class TestMigrationGenerator < Rails::Generators::TestCase
method test_generating_activity_model (line 15) | def test_generating_activity_model
class TestMigrationUpgradeGenerator (line 21) | class TestMigrationUpgradeGenerator < Rails::Generators::TestCase
method test_generating_activity_model (line 26) | def test_generating_activity_model
FILE: test/test_helper.rb
function article (line 54) | def article(options = {})
class User (line 67) | class User < ActiveRecord::Base; end
class User (line 73) | class User
class Article (line 82) | class Article
function article (line 97) | def article(options = {})
class User (line 110) | class User
class Article (line 119) | class Article
function article (line 129) | def article(options = {})
class ViewSpec (line 138) | class ViewSpec < Minitest::Spec
FILE: test/test_tracking.rb
class ActivistAndTrackedArticle (line 35) | class ActivistAndTrackedArticle
class ActivistAndTrackedArticle (line 52) | class ActivistAndTrackedArticle
class ActivistAndTrackedArticle (line 65) | class ActivistAndTrackedArticle < ActiveRecord::Base
function teardown (line 404) | def teardown
FILE: test/test_view_helpers.rb
function content_for (line 34) | def content_for(name, content)
Condensed preview — 72 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (110K chars).
[
{
"path": ".editorconfig",
"chars": 227,
"preview": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ni"
},
{
"path": ".github/workflows/main.yml",
"chars": 899,
"preview": "name: CI\non:\n pull_request:\n paths-ignore:\n - 'README.md'\n push:\n paths-ignore:\n - 'README.md'\n workf"
},
{
"path": ".gitignore",
"chars": 89,
"preview": "/doc/\n/.yardoc/\n*.gem\n/coverage/\n/*.sublime-workspace\n/tmp\n/Gemfile.lock\n/gemfiles/*.lock"
},
{
"path": ".travis.yml",
"chars": 631,
"preview": "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 "
},
{
"path": "Appraisals",
"chars": 250,
"preview": "# 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 "
},
{
"path": "CHANGELOG.md",
"chars": 5538,
"preview": "# Changelog\n\n## 3.0.2\n\n- **Fixed** Refactor prepare_parameters method to handle nil parameters (s. #387, thanks [Himalay"
},
{
"path": "Gemfile",
"chars": 69,
"preview": "# frozen_string_literal: true\n\nsource \"https://rubygems.org\"\ngemspec\n"
},
{
"path": "MIT-LICENSE",
"chars": 1064,
"preview": "Copyright (c) 2011-2013 Piotrek Okoński\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of"
},
{
"path": "README.md",
"chars": 8447,
"preview": "# PublicActivity [](https://codeclimate.com/"
},
{
"path": "Rakefile",
"chars": 392,
"preview": "# frozen_string_literal: true\n\nrequire 'bundler/gem_tasks'\nrequire 'rake'\nrequire 'yard'\nrequire 'yard/rake/yardoc_task'"
},
{
"path": "gemfiles/.bundle/config",
"chars": 22,
"preview": "---\nBUNDLE_RETRY: \"1\"\n"
},
{
"path": "gemfiles/rails_6.1.gemfile",
"chars": 130,
"preview": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"rails\", \"~> 6.1.0\"\ngem \"openssl\"\n\ngemspec pa"
},
{
"path": "gemfiles/rails_7.0.gemfile",
"chars": 116,
"preview": "# 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",
"chars": 116,
"preview": "# 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",
"chars": 538,
"preview": "# frozen_string_literal: true\n\nrequire 'generators/public_activity'\nrequire 'rails/generators/active_record'\n\nmodule Pub"
},
{
"path": "lib/generators/public_activity/migration/templates/migration.rb",
"chars": 487,
"preview": "# frozen_string_literal: true\n\n# Migration responsible for creating a table with activities\nclass CreateActivities < Act"
},
{
"path": "lib/generators/public_activity/migration_upgrade/migration_upgrade_generator.rb",
"chars": 544,
"preview": "# frozen_string_literal: true\n\nrequire 'generators/public_activity'\nrequire 'rails/generators/active_record'\n\nmodule Pub"
},
{
"path": "lib/generators/public_activity/migration_upgrade/templates/upgrade.rb",
"chars": 288,
"preview": "# frozen_string_literal: true\n\n# Migration responsible for creating a table with activities\nclass UpgradeActivities < Ac"
},
{
"path": "lib/generators/public_activity.rb",
"chars": 420,
"preview": "# frozen_string_literal: true\n\nrequire 'rails/generators/named_base'\n\nmodule PublicActivity\n # A generator module with "
},
{
"path": "lib/public_activity/actions/creation.rb",
"chars": 398,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n # Handles creation of Activities upon destruction and update of t"
},
{
"path": "lib/public_activity/actions/destruction.rb",
"chars": 401,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n # Handles creation of Activities upon destruction of tracked mode"
},
{
"path": "lib/public_activity/actions/update.rb",
"chars": 551,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n # Handles creation of Activities upon destruction and update of t"
},
{
"path": "lib/public_activity/activity.rb",
"chars": 209,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n # Main model, stores all information about what happened,\n # who"
},
{
"path": "lib/public_activity/common.rb",
"chars": 12360,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n # Happens when creating custom activities without either action o"
},
{
"path": "lib/public_activity/config.rb",
"chars": 2425,
"preview": "# frozen_string_literal: true\n\nrequire 'singleton'\n\nmodule PublicActivity\n # Class used to initialize configuration obj"
},
{
"path": "lib/public_activity/models/activist.rb",
"chars": 275,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n # Provides helper methods for selecting activities from a user.\n "
},
{
"path": "lib/public_activity/models/activity.rb",
"chars": 94,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n class Activity < inherit_orm\n end\nend\n"
},
{
"path": "lib/public_activity/models/adapter.rb",
"chars": 168,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n # Loads database-specific routines for use by PublicActivity.\n c"
},
{
"path": "lib/public_activity/models/trackable.rb",
"chars": 277,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n # Provides association for activities bound to this object by *tr"
},
{
"path": "lib/public_activity/orm/active_record/activist.rb",
"chars": 1082,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n module ORM\n module ActiveRecord\n # Module extending class"
},
{
"path": "lib/public_activity/orm/active_record/activity.rb",
"chars": 1968,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n unless defined? ::PG::ConnectionBad\n module ::PG\n class C"
},
{
"path": "lib/public_activity/orm/active_record/adapter.rb",
"chars": 716,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n module ORM\n # Support for ActiveRecord for PublicActivity. Use"
},
{
"path": "lib/public_activity/orm/active_record/trackable.rb",
"chars": 526,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n module ORM\n module ActiveRecord\n # Implements {PublicActi"
},
{
"path": "lib/public_activity/orm/active_record.rb",
"chars": 223,
"preview": "# frozen_string_literal: true\n\nrequire 'active_record'\nrequire_relative 'active_record/activity'\nrequire_relative 'activ"
},
{
"path": "lib/public_activity/orm/mongo_mapper/activist.rb",
"chars": 1023,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n module ORM\n module MongoMapper\n # Module extending classe"
},
{
"path": "lib/public_activity/orm/mongo_mapper/activity.rb",
"chars": 931,
"preview": "# frozen_string_literal: true\n\nrequire 'mongo_mapper'\nrequire 'active_support/core_ext'\n\nmodule PublicActivity\n module "
},
{
"path": "lib/public_activity/orm/mongo_mapper/adapter.rb",
"chars": 516,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n module ORM\n module MongoMapper\n class Adapter\n # C"
},
{
"path": "lib/public_activity/orm/mongo_mapper/trackable.rb",
"chars": 304,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n module ORM\n module MongoMapper\n module Trackable\n "
},
{
"path": "lib/public_activity/orm/mongo_mapper.rb",
"chars": 207,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"mongo_mapper/activity.rb\"\nrequire_relative \"mongo_mapper/adapter.rb\"\nre"
},
{
"path": "lib/public_activity/orm/mongoid/activist.rb",
"chars": 1022,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n module ORM\n module Mongoid\n # Module extending classes th"
},
{
"path": "lib/public_activity/orm/mongoid/activity.rb",
"chars": 991,
"preview": "# frozen_string_literal: true\n\nrequire 'mongoid'\n\nmodule PublicActivity\n module ORM\n module Mongoid\n # The Acti"
},
{
"path": "lib/public_activity/orm/mongoid/adapter.rb",
"chars": 512,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n module ORM\n module Mongoid\n class Adapter\n # Creat"
},
{
"path": "lib/public_activity/orm/mongoid/trackable.rb",
"chars": 280,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n module ORM\n module Mongoid\n module Trackable\n def "
},
{
"path": "lib/public_activity/orm/mongoid.rb",
"chars": 187,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"mongoid/activity.rb\"\nrequire_relative \"mongoid/adapter.rb\"\nrequire_rela"
},
{
"path": "lib/public_activity/renderable.rb",
"chars": 6159,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n # Provides logic for rendering activities. Handles both i18n stri"
},
{
"path": "lib/public_activity/roles/deactivatable.rb",
"chars": 1197,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n # Enables per-class disabling of PublicActivity functionality.\n "
},
{
"path": "lib/public_activity/roles/tracked.rb",
"chars": 7573,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n # Main module extending classes we want to keep track of.\n modul"
},
{
"path": "lib/public_activity/testing.rb",
"chars": 976,
"preview": "# frozen_string_literal: true\n\n# This file provides functionality for testing your code with public_activity\n# activated"
},
{
"path": "lib/public_activity/utility/store_controller.rb",
"chars": 882,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n class << self\n # Setter for remembering controller instance\n "
},
{
"path": "lib/public_activity/utility/view_helpers.rb",
"chars": 1120,
"preview": "# frozen_string_literal: true\n\n# Provides a shortcut from views to the rendering method.\nmodule PublicActivity\n # Modul"
},
{
"path": "lib/public_activity/version.rb",
"chars": 77,
"preview": "# frozen_string_literal: true\n\nmodule PublicActivity\n VERSION = '3.0.2'\nend\n"
},
{
"path": "lib/public_activity.rb",
"chars": 2218,
"preview": "# frozen_string_literal: true\n\nrequire 'active_support'\nrequire 'logger'\nrequire 'action_view'\n# +public_activity+ keeps"
},
{
"path": "public_activity.gemspec",
"chars": 2323,
"preview": "# frozen_string_literal: true\n\n$LOAD_PATH.push File.expand_path('../lib', __FILE__)\nrequire 'public_activity/version'\n\nG"
},
{
"path": "public_activity.sublime-project",
"chars": 477,
"preview": "{\n \"folders\":\n [\n {\n \"path\": \"./\",\n \"folder_exclude_patterns\": [\"coverage\", \".yardoc\", \"doc\"]\n }\n ],\n"
},
{
"path": "test/migrations/002_create_articles.rb",
"chars": 241,
"preview": "# frozen_string_literal: true\n\nclass CreateArticles < ActiveRecord::Migration[6.1]\n def self.up\n create_table :artic"
},
{
"path": "test/migrations/003_create_users.rb",
"chars": 183,
"preview": "# frozen_string_literal: true\n\nclass CreateUsers < ActiveRecord::Migration[6.1]\n def self.up\n create_table :users do"
},
{
"path": "test/migrations/004_add_nonstandard_to_activities.rb",
"chars": 190,
"preview": "# frozen_string_literal: true\n\nclass AddNonstandardToActivities < ActiveRecord::Migration[6.1]\n def change\n change_t"
},
{
"path": "test/mongo_mapper.yml",
"chars": 70,
"preview": "test:\n host: 127.0.0.1\n port: 27017\n database: public_activity_test"
},
{
"path": "test/mongoid.yml",
"chars": 106,
"preview": "test:\n clients:\n default:\n hosts:\n - 127.0.0.1:27017\n database: public_activity_test\n"
},
{
"path": "test/test_activist.rb",
"chars": 1688,
"preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\ndescribe PublicActivity::Activist do\n it 'adds owner association'"
},
{
"path": "test/test_activity.rb",
"chars": 2806,
"preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\ndescribe 'PublicActivity::Activity Rendering' do\n describe '#text"
},
{
"path": "test/test_common.rb",
"chars": 6327,
"preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\ndescribe PublicActivity::Common do\n before do\n @owner = Us"
},
{
"path": "test/test_controller_integration.rb",
"chars": 1397,
"preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass StoringController < ActionView::TestCase::TestController\n i"
},
{
"path": "test/test_generators.rb",
"chars": 1000,
"preview": "# frozen_string_literal: true\n\nif ENV['PA_ORM'] == 'active_record'\n\n require 'test_helper'\n require 'rails/generators/"
},
{
"path": "test/test_helper.rb",
"chars": 3389,
"preview": "# frozen_string_literal: true\n\nrequire 'rubygems'\nrequire 'bundler'\nrequire 'logger'\nBundler.setup(:default, :test)\n\nif "
},
{
"path": "test/test_testing.rb",
"chars": 814,
"preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\ndescribe PublicActivity do\n describe 'self.with_tracking' do\n "
},
{
"path": "test/test_tracking.rb",
"chars": 11600,
"preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\ndescribe PublicActivity::Tracked do\n describe 'defining instance "
},
{
"path": "test/test_view_helpers.rb",
"chars": 1023,
"preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\ndescribe 'ViewHelpers Rendering' do\n include PublicActivity::View"
},
{
"path": "test/views/custom/_layout.erb",
"chars": 48,
"preview": "<h2>Here be the custom layouts</h2><%= yield %>\n"
},
{
"path": "test/views/custom/_test.erb",
"chars": 40,
"preview": "Custom Template Root <%= activity.id %>\n"
},
{
"path": "test/views/layouts/_activity.erb",
"chars": 40,
"preview": "<h2>Here be the layouts</h2><%= yield %>"
},
{
"path": "test/views/public_activity/_test.erb",
"chars": 232,
"preview": "<% if defined?(two) == 'local-variable' %>\n<% # output local_variable if defined %>\n<%= two %>\n<% else %>\n<strong><%= p["
}
]
About this extraction
This page contains the full source code of the pokonski/public_activity GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 72 files (99.7 KB), approximately 26.7k tokens, and a symbol index with 217 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.