Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New optional column: item_subtype #1143

Merged
merged 3 commits into from
Aug 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ Naming/PredicateName:
Naming/UncommunicativeMethodParamName:
Enabled: false

# This cop does not seem to work in rubocop-rspec 1.28.0
RSpec/DescribeClass:
Enabled: false

# Yes, ideally examples would be short. Is it possible to pick a limit and say,
# "no example will ever be longer than this"? Hard to say. Sometimes they're
# quite long.
Expand Down
11 changes: 0 additions & 11 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,6 @@ respectively.

- `paper_trail-association_tracking` is no longer a runtime dependency. If you
use it (`track_associations = true`) you must now add it to your own `Gemfile`.
- [#1108](https://github.com/paper-trail-gem/paper_trail/pull/1108) -
In `versions.item_type`, we now store the subclass name instead of
the base_class.
- You must migrate existing `versions` records if you use
[STI][1]. A migration generator has been provided. Generator `update_sti`
creates a migration that updates existing `version` entries such that
`item_type` then refers to the specific class name instead of base_class.
See [5.c. Generators][2] for instructions.
- [#1130](https://github.com/paper-trail-gem/paper_trail/pull/1130) -
Removed `save_changes`. For those wanting to save space, it's more effective
to drop the `object` column. If you need ultimate control over the
Expand Down Expand Up @@ -1060,6 +1052,3 @@ in the `PaperTrail::Version` class through a `Rails::Engine` when the gem is use
- [#160](https://github.com/paper-trail-gem/paper_trail/pull/160) - Fixed failing tests and resolved out of date dependency issues.
- [#157](https://github.com/paper-trail-gem/paper_trail/pull/157) - Refactored `class_attribute` names on the `ClassMethods` module
for names that are not obviously pertaining to PaperTrail to prevent method name collision.

[1]: https://api.rubyonrails.org/v5.2.0/classes/ActiveRecord/Base.html#class-ActiveRecord::Base-label-Single+table+inheritance
[2]: https://github.com/paper-trail-gem/paper_trail/blob/master/README.md#5c-generators
97 changes: 10 additions & 87 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -934,15 +934,8 @@ class Banana < Fruit
end
```

A change in what `item_type` stores for subclassed models was introduced in
[PR#1108](https://github.com/paper-trail-gem/paper_trail/pull/1108), recording
the subclass name instead of the base class. This simplifies
reifying through associations, and allows for a change in PT-AT that fixes
[issue 594](https://github.com/paper-trail-gem/paper_trail/issues/594).

For those that have existing version data from STI models, `item_type` can be
updated by using a generator, `rails generate paper_trail:update_sti`. More
information is found in section [5.c. Generators](#5c-generators).
However, there is a known issue when reifying [associations](#associations),
see https://github.com/paper-trail-gem/paper_trail/issues/594

### 5.b. Configuring the `versions` Association

Expand All @@ -962,97 +955,27 @@ Overriding associations is not recommended in general.

### 5.c. Generators

#### `paper_trail:install`

Used to set up PT for the first time. Writes, but does not run, a migration
file. Creates an initializer for configuration. The migration adds the
`versions` table.
PaperTrail has one generator, `paper_trail:install`. It writes, but does not
run, a migration file. It also creates a PaperTrail configuration initializer.
The migration adds (at least) the `versions` table. The
most up-to-date documentation for this generator can be found by running `rails
generate paper_trail:install --help`, but a copy is included here for
convenience.

```
Usage:
rails generate paper_trail:install --help
rails generate paper_trail:install [options]

Options:
# Includes the `object_changes` column, for storing changeset (diff) with each version
[--with-changes], [--no-with-changes]
[--with-changes], [--no-with-changes] # Store changeset (diff) with each version

Runtime options:
-f, [--force] # Overwrite files that already exist
-p, [--pretend], [--no-pretend] # Run but do not make any changes
-q, [--quiet], [--no-quiet] # Suppress status output
-s, [--skip], [--no-skip] # Skip files that already exist
```

#### `paper_trail:update_sti`

Updates `versions.item_type` from base class name to subclass name. If you use
STI, and you have records in `versions` created prior to
[#1108](https://github.com/paper-trail-gem/paper_trail/pull/1108), you must run
this migration.

Writes, but does not run, a migration. The migration scans existing data in the
versions table to identify models which use STI. Upon finding each entry which
refers to an object from a subclassed model, `versions.item_type` is updated to
reflect the subclass name, providing full compatibility with how the `versions`
association works after PR#1108. In order to more quickly update a large volume
of version records, updates are batched to affect 100 records at a time.

Hints can be used in rare cases when the STI structure is modified over time
such as when establishing additional intermediary inheritance structure,
pointing to a new base class, abandoning STI entirely, or changing the name of
the inheritance_column. The vast majority of users will probably never need to
supply hints when using this generator.

Let's give an example where the inheritance_column is changed, say originally
there is an Animal class that uses the default `type` column, but after
generating 500 Bird and Cat objects is modified to instead use the `species`
column with a line like this:

```
Animal.inheritance_column = "species"
```

With this change a hint can be used to build the migration in a way that
indicates how to treat the older records. For those first 500 Animal objects,
the `item_type` should be derived from `type`, and for the rest, from `species`.
We would need to research the ID numbers for versions that utilise `type` in the
`object` and `object_changes` data. In this example let's say that those first
500 Animals had version IDs between 249 and 1124. These objects would have been
created having an `item_type` of Animal prior to PR#1108. Of course versions
representing all manner of other changes would also be intermingled along with
creates and updates for Animal objects. Perhaps another STI model exists for Car
that inherits from Vehicle, and 50 Car objects were also built around the same
timeframe as Bird and Cat objects, with various creates and updates for those
being referened in versions with IDs from 382 to 516. For Vehicle let's say that
initially the inheritance_column was set to `kind`, but then it changed to
something else after ID 516 in the versions table. In this situation, to
properly use the generator then these hints can be supplied:

`rails generate update_sti Animal(type):249..1124 Vehicle(kind):382..516`

The resulting migration will include these lines near the top:

```
# Versions of item_type "Animal" with IDs between 249 and 1124 will be updated based on `type`
# Versions of item_type "Vehicle" with IDs between 382 and 516 will be updated based on `kind`
hints = {"Animal"=>{249..1124=>"type"}, "Vehicle"=>{382..516=>"kind"}}
```

It is important to note that the IDs are not those of the Bird, Cat, or Car
objects, but rather the IDs from the create, update, and destroy entries in the
versions table. As well, these hints are only needed in situations where the
inheritance_column has changed at some point in time, or in cases where the
entire STI structure is modified or is abandoned. It ultimately facilitates
better reporting for historic subclassed items, and allows PT-AT to properly
reify these historic objects through associations.

Once you have run the migration, please inform PaperTrail, so that it won't warn
you about this.

```
# config/initializers/paper_trail.rb
::PaperTrail.config.i_have_updated_my_existing_item_types = true
Generates (but does not run) a migration to add a versions table. Also generates an initializer file for configuring PaperTrail
```

### 5.d. Protected Attributes
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
Description:
Generates (but does not run) a migration to add a versions table. See section
5.c. Generators in README.md for more information.
Generates (but does not run) a migration to add a versions table.
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# frozen_string_literal: true

require_relative "../migration_generator"
require "rails/generators"
require "rails/generators/active_record"

module PaperTrail
# Installs PaperTrail in a rails app.
class InstallGenerator < MigrationGenerator
class InstallGenerator < ::Rails::Generators::Base
include ::Rails::Generators::Migration

# Class names of MySQL adapters.
# - `MysqlAdapter` - Used by gems: `mysql`, `activerecord-jdbcmysql-adapter`.
# - `Mysql2Adapter` - Used by `mysql2` gem.
Expand All @@ -22,16 +25,34 @@ class InstallGenerator < MigrationGenerator
)

desc "Generates (but does not run) a migration to add a versions table." \
" Also generates an initializer file for configuring PaperTrail." \
" See section 5.c. Generators in README.md for more information."
" Also generates an initializer file for configuring PaperTrail"

def create_migration_file
add_paper_trail_migration("create_versions",
item_type_options: item_type_options,
versions_table_options: versions_table_options)
add_paper_trail_migration("create_versions")
add_paper_trail_migration("add_object_changes_to_versions") if options.with_changes?
end

def self.next_migration_number(dirname)
::ActiveRecord::Generators::Base.next_migration_number(dirname)
end

protected

def add_paper_trail_migration(template)
migration_dir = File.expand_path("db/migrate")
if self.class.migration_exists?(migration_dir, template)
::Kernel.warn "Migration already exists: #{template}"
else
migration_template(
"#{template}.rb.erb",
"db/migrate/#{template}.rb",
item_type_options: item_type_options,
migration_version: migration_version,
versions_table_options: versions_table_options
)
end
end

private

# MySQL 5.6 utf8mb4 limit is 191 chars for keys used in indexes.
Expand All @@ -42,6 +63,13 @@ def item_type_options
", #{opt}"
end

def migration_version
major = ActiveRecord::VERSION::MAJOR
if major >= 5
"[#{major}.#{ActiveRecord::VERSION::MINOR}]"
end
end

def mysql?
MYSQL_ADAPTERS.include?(ActiveRecord::Base.connection.class.name)
end
Expand Down
37 changes: 0 additions & 37 deletions lib/generators/paper_trail/migration_generator.rb

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class CreateVersions < ActiveRecord::Migration<%= migration_version %>
# the `created_at` column.
# (https://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html)
#
# MySQL users should also upgrade to at least rails 4.2, which is the first
# MySQL users should also upgrade to rails 4.2, which is the first
# version of ActiveRecord with support for fractional seconds in MySQL.
# (https://github.com/rails/rails/pull/14359)
#
Expand Down
4 changes: 0 additions & 4 deletions lib/generators/paper_trail/update_sti/USAGE

This file was deleted.

This file was deleted.

17 changes: 0 additions & 17 deletions lib/generators/paper_trail/update_sti/update_sti_generator.rb

This file was deleted.

2 changes: 0 additions & 2 deletions lib/paper_trail/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ class Config

attr_accessor(
:association_reify_error_behaviour,
:classes_warned_about_sti_item_types,
:i_have_updated_my_existing_item_types,
:object_changes_adapter,
:serializer,
:version_limit
Expand Down
Loading