recorder 1.2.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 11964998eab29e2ade0b5991c2dcd55f62918304afab5eab82acb8f7f4574813
4
- data.tar.gz: 6a54bce4305e0529736140246332879b95bfb3770396ceb7ef6e4d64e383fd0f
3
+ metadata.gz: 2d2e570a4fef05c798efd2a10ded9db849f500e41fab1e13c6f9e485ebfdfdee
4
+ data.tar.gz: 3a3e7f23e99544e05e0e0008de975f9bea9ed2089de89d999ac601a80b3cbe11
5
5
  SHA512:
6
- metadata.gz: be73f56ca6cf864fcce8ff25cb66e2fe0c5b0c5cb59de32eb564c234a6edb5e8bba519d7bdc6255ad4ca559da5eda8456da009a09d376312ee3f23a6b6f84ca5
7
- data.tar.gz: 7df337554a0136656250967b56d5df939f3eaf82c5164702ea4f159be322109a77ad994e0653f03616d22485e03c8fa6c06a2c167c3c8c6d8f8d3e3544dfe076
6
+ metadata.gz: 4202283d1af6b5da82c360a3537623516794855e56a31973ed43ff0f0bceb04cdfa412d180af0dab9dc30b096a5213966663dd0cb19af1130e76d52360c24570
7
+ data.tar.gz: 4b7451c330c0115755813f0736135b5323b5d1f72b0f0583e21e88dcc725f1adf6fd44c5ebc6fd6060046304c75be96e282ab590a01c0b47d65eb6d4edf8ca80
data/CHANGELOG.md ADDED
@@ -0,0 +1,113 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.3.0] - 2026-09-16
11
+
12
+ ### Added
13
+
14
+ - Support for Rails 7.0, 7.1, 7.2, 8.0, and 8.1. Rails 6.1 remains supported.
15
+ - Support for Ruby 3.3 and 3.4. Ruby 3.0 remains the minimum.
16
+ - Declared and tested the supported Ruby versions (#10).
17
+ - The specs and RuboCop now run in GitHub Actions (#9).
18
+ - Releases are published to RubyGems.org from GitHub Actions when a version tag
19
+ is pushed (#22).
20
+ - Gem metadata: `source_code_uri`, `changelog_uri`, `bug_tracker_uri`, and
21
+ `rubygems_mfa_required`.
22
+ - README coverage of the public API: configuration, `Recorder.meta=`,
23
+ `recorder_disabled!`, and the association tracking options (#20).
24
+
25
+ ### Changed
26
+
27
+ - `activerecord` and `activesupport` requirements widen from `~> 6.1` to
28
+ `>= 6.1, < 9`, and the `< 3.4` Ruby ceiling is removed. Rails 6.1 and 7.0
29
+ cannot run on Ruby 3.4; every other combination is exercised in CI.
30
+ - The released gem now ships only `lib/`, the README, the LICENSE, and this
31
+ changelog. Previous releases also carried repository scaffolding — CI
32
+ configuration, `Rakefile`, `bin/`, and dotfiles — that a host app never loads.
33
+ - `recorder_revisions.item_id` and `recorder_revisions.user_id` are now
34
+ `bigint`. Rails has defaulted primary keys to `bigint` since 5.1, so the
35
+ `integer` columns could not hold a key from any table this gem audits once it
36
+ passed 2,147,483,647. No release since Rails 5.0 shipped a migration that could
37
+ run; installs from the 0.1.x line on Rails 4 have the narrow columns and are
38
+ unaffected, because the gem ships no migration that alters an existing table
39
+ (#18).
40
+
41
+ ### Removed
42
+
43
+ - `pg` is no longer a runtime dependency. Revisions still require PostgreSQL
44
+ column types, but the adapter is the host application's to declare, so the
45
+ gem no longer forces `pg` into its bundle.
46
+ - The `--with_partitions` generator option. It dispatched to a template that was
47
+ never added to the gem, so the run wrote the first migration and then aborted,
48
+ leaving a partial install. It never completed once (#18).
49
+ - The `--with_number_column` generator option. The `number` column it added was
50
+ never read by the gem, and the migration it generated could not run on any
51
+ Rails this gem supports, so there is no working installed base. Anyone who has
52
+ the column keeps it — the counter lives in a trigger in their own schema,
53
+ which this does not touch, and the gem never writes the column. Optional
54
+ cleanup, including a working `DROP TRIGGER`, is in #14 — the `down` the gem
55
+ originally shipped is a syntax error and cannot roll it back (#14).
56
+
57
+ ### Fixed
58
+
59
+ - Asynchronous recording no longer enqueues job arguments Sidekiq rejects. The
60
+ `meta` hash supplied through `Recorder.meta=` reached the worker with its
61
+ original keys and values, and Sidekiq raises on arguments that are not JSON
62
+ native from 7.0 onwards. Because the raise happened inside an `after_create`,
63
+ it rolled back the host application's own write. The enqueued payload is now
64
+ normalised to JSON natives in full (#19).
65
+ - `rails generate recorder:install` now produces migrations that run. The
66
+ templates subclassed a bare `ActiveRecord::Migration`, which Rails has
67
+ rejected since 5.0, so `rails db:migrate` raised on the only documented way to
68
+ install the gem. The version is templated from the host app's Rails (#18).
69
+ - `Recorder.version` returns the version instead of raising `TypeError`. It
70
+ scoped into `VERSION::STRING`, but `VERSION` is a `String` (#17).
71
+ - `recorder_disabled!` re-enables recording when the block raises. The re-enable
72
+ was skipped on the exception path, so recording stayed off for the rest of the
73
+ request or job and every later save went silently unaudited (#17).
74
+ - Made the spec suite runnable and trustworthy again (#8).
75
+
76
+ ## [1.2.3] - 2023-04-25
77
+
78
+ ### Fixed
79
+
80
+ - A revision is created when an item's associations change.
81
+ - No revision is created for an `:update` event when nothing changed.
82
+
83
+ ## [1.2.2] - 2022-11-17
84
+
85
+ ### Fixed
86
+
87
+ - Refactoring for full Rails 6 compatibility.
88
+
89
+ ## [1.2.1] - 2022-11-16
90
+
91
+ ### Fixed
92
+
93
+ - Fixed parsing of empty associations.
94
+
95
+ ## [1.2.0] - 2022-11-15
96
+
97
+ ### Added
98
+
99
+ - Rails 6 support.
100
+
101
+ ## [1.1.1] - 2021-09-13
102
+
103
+ Releases at and before 1.1.1 predate this changelog. See the
104
+ [commit history](https://github.com/jetrockets/recorder/commits/master) for
105
+ details.
106
+
107
+ [Unreleased]: https://github.com/jetrockets/recorder/compare/v1.3.0...HEAD
108
+ [1.3.0]: https://github.com/jetrockets/recorder/compare/v1.2.3...v1.3.0
109
+ [1.2.3]: https://github.com/jetrockets/recorder/compare/v1.2.2...v1.2.3
110
+ [1.2.2]: https://github.com/jetrockets/recorder/compare/v1.2.1...v1.2.2
111
+ [1.2.1]: https://github.com/jetrockets/recorder/compare/v1.2.0...v1.2.1
112
+ [1.2.0]: https://github.com/jetrockets/recorder/compare/v1.1.1...v1.2.0
113
+ [1.1.1]: https://github.com/jetrockets/recorder/releases/tag/v1.1.1
data/README.md CHANGED
@@ -1,6 +1,29 @@
1
1
  # Recorder
2
2
 
3
- Recorder tracks changes of your Rails models
3
+ [![CI](https://github.com/jetrockets/recorder/actions/workflows/ci.yml/badge.svg)](https://github.com/jetrockets/recorder/actions/workflows/ci.yml)
4
+ [![Gem Version](https://img.shields.io/gem/v/recorder)](https://rubygems.org/gems/recorder)
5
+
6
+ Recorder tracks changes of your Rails models. Each create, update, and destroy on
7
+ an observed model writes a `recorder_revisions` row holding an attribute snapshot,
8
+ the changes, and — when the controller concern is included — the user and IP
9
+ behind the request.
10
+
11
+ ## Requirements
12
+
13
+ - PostgreSQL — revisions are stored in `jsonb` and `inet` columns
14
+ - Ruby and Rails per the table below
15
+
16
+ | Rails | Supported Ruby |
17
+ |-------|----------------|
18
+ | 6.1 | 3.0 – 3.3 |
19
+ | 7.0 | 3.0 – 3.3 |
20
+ | 7.1 | 3.0 – 3.4 |
21
+ | 7.2 | 3.1 – 3.4 |
22
+ | 8.0 | 3.2 – 3.4 |
23
+ | 8.1 | 3.2 – 3.4 |
24
+
25
+ Rails 6.1 and 7.0 cannot run on Ruby 3.4 — they require `mutex_m`, which left the
26
+ default gems in that release. Every combination in the table is exercised in CI.
4
27
 
5
28
  ## Installation
6
29
 
@@ -14,15 +37,25 @@ And then execute:
14
37
 
15
38
  $ bundle
16
39
 
17
- Or install it yourself as:
40
+ Generate the migration for the `recorder_revisions` table:
41
+
42
+ $ rails generate recorder:install
43
+
44
+ The generator writes the migration but does not run it. Run it yourself:
45
+
46
+ $ rails db:migrate
18
47
 
19
- $ gem install recorder
48
+ The generator accepts one option:
49
+
50
+ - `--with_index_by_user_id` — adds an index on `user_id`.
20
51
 
21
52
  ## Usage
22
53
 
23
- To enable logging on a model you just need to include `Recorder::Observer` into the model and configure logging options for it:
54
+ ### Observing a model
24
55
 
25
- ``` ruby
56
+ Include `Recorder::Observer` into the model and configure logging options for it:
57
+
58
+ ```ruby
26
59
  class Post < ActiveRecord::Base
27
60
  include ::Recorder::Observer
28
61
 
@@ -41,7 +74,28 @@ Recorder supports the following options:
41
74
  * `associations: {hash} (hash)` - allows to set what associations will be logged alongside with the model. For each association you can also set ignore and only options;
42
75
  * `async: bool` - a logging strategy (true - asynchronous, false - synchronous).
43
76
 
44
- There are two strategies for logging: synchronous and asynchronous. When the synchronous strategy is used, a revision record is saved immediately after a model is saved, and the async strategy moves creating of revision records to [Sidekiq](https://github.com/mperham/sidekiq).
77
+ These per-model options do not reach the recorder yet see [Known issues](#known-issues).
78
+ Until they do, every observed model records a full attribute snapshot, filtered
79
+ only by the global `Recorder.config.ignore`.
80
+
81
+ ### Global configuration
82
+
83
+ ```ruby
84
+ Recorder.config do |config|
85
+ config.ignore = %i[created_at updated_at]
86
+ config.async = false
87
+ config.sidekiq_options = {queue: 'recorder', retry: 10, backtrace: true}
88
+ end
89
+ ```
90
+
91
+ `ignore` defaults to `[]`, `async` to `false`, and `sidekiq_options` to the hash
92
+ shown above.
93
+
94
+ There are two strategies for logging: synchronous and asynchronous. When the synchronous strategy is used, a revision record is saved immediately after a model is saved, and the async strategy moves creating of revision records to [Sidekiq](https://github.com/sidekiq/sidekiq). Under the async
95
+ strategy the revision is enqueued to `Recorder::Sidekiq::RevisionsWorker` two
96
+ seconds out; the worker is loaded by the railtie when `Sidekiq` is defined.
97
+
98
+ ### Recording the current user
45
99
 
46
100
  To enable storing of such data as user_id and ip, you need to include `Recorder::Rails::ControllerConcern` to `ApplicationController`. Recorder uses [request_store](https://github.com/steveklabnik/request_store) to safely store these data on a thread level.
47
101
 
@@ -52,11 +106,118 @@ To enable storing of such data as user_id and ip, you need to include `Recorder:
52
106
  end
53
107
  ```
54
108
 
109
+ The concern reads `current_user`; override `recorder_user_id` to name a different
110
+ method. Override `recorder_meta` to store a hash alongside every revision. A
111
+ revision without a user is valid — `user_id` stays `nil`.
112
+
113
+ ### Turning recording off
114
+
115
+ `Recorder::Manager` suspends recording for the current request or thread:
116
+
117
+ ```ruby
118
+ class Importer
119
+ include Recorder::Manager
120
+
121
+ def call
122
+ recorder_disabled! do
123
+ # nothing recorded in here
124
+ end
125
+ end
126
+ end
127
+ ```
128
+
129
+ The block form re-enables recording on the way out, including when the block
130
+ raises. Called without a block, `recorder_disabled!` stays in effect until
131
+ `recorder_enabled!`.
132
+
133
+ ### Reading revisions
134
+
135
+ Observed models get a `revisions` association:
136
+
137
+ ```ruby
138
+ revision = post.revisions.ordered_by_created_at.first
139
+
140
+ revision.event # "update"
141
+ revision.data # {"attributes" => {...}, "changes" => {...}, "associations" => {...}}
142
+ revision.user_id
143
+ revision.action_date
144
+ ```
145
+
146
+ `#item_changeset` wraps `data['changes']` in a `Recorder::Changeset`, which reads
147
+ the values back as the model's own types:
148
+
149
+ ```ruby
150
+ changeset = revision.item_changeset
151
+
152
+ changeset.keys # attributes that changed
153
+ changeset.previous(:title) # value before the change
154
+ changeset.next(:title) # value after it
155
+ changeset.human_attribute_name(:title)
156
+ changeset.previous_version # a copy of the record with the old values
157
+ ```
158
+
159
+ Changed associations are reachable the same way:
160
+
161
+ ```ruby
162
+ revision.changed_associations # ["author"]
163
+ revision.association_changeset('author')
164
+ ```
165
+
166
+ To render a changeset yourself, define `PostChangeset` — the class is looked up
167
+ as `"#{model}Changeset"` — or point at another one with a
168
+ `recorder_changeset_class` class method on the model. Otherwise
169
+ `Recorder::Changeset` is used.
170
+
171
+ ## Known issues
172
+
173
+ The gem is under active maintenance and these defects are known as of 1.2.3:
174
+
175
+ - The per-model options above (`ignore:`, `only:`, `associations:`, `async:`)
176
+ are not applied. `Recorder::Tape` asks the record instance for
177
+ `recorder_options`, but `Recorder::Observer` defines that method on the class,
178
+ so the lookup always falls back to `{}` — every observed model records a full
179
+ attribute snapshot, synchronously. Global configuration is unaffected.
180
+ - `Recorder.enabled=` does not switch recording off. It writes to
181
+ `Recorder.config`, which nothing on the recording path reads — the gates are in
182
+ `Recorder.store`, which `Recorder::Manager` drives.
183
+ - Collection associations are never recorded. `associations:` handles only
184
+ singular associations; a `has_many` reflection is skipped without a warning.
185
+
55
186
  ## Development
56
187
 
57
188
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
58
189
 
59
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
190
+ The specs need a PostgreSQL server. Connection settings are read from the
191
+ environment — `RECORDER_DB_HOST`, `RECORDER_DB_PORT`, `RECORDER_DB_USERNAME`,
192
+ `RECORDER_DB_PASSWORD`, `RECORDER_DB_NAME` — and default to `postgres:postgres`
193
+ on `localhost:5432` against a `recorder_test` database, which must already exist.
194
+
195
+ `rake spec` runs against whichever Rails version the root `Gemfile` resolves to.
196
+ To run against a specific one, use the appraisal gemfiles:
197
+
198
+ ```bash
199
+ bundle exec appraisal install # once, to generate gemfiles/
200
+ bundle exec appraisal rails-7.2 rake spec
201
+ ```
202
+
203
+ CI runs the suite across the whole Ruby × Rails matrix above, plus `bundle exec
204
+ rubocop`. Both must pass before a pull request merges.
205
+
206
+ To install this gem onto your local machine, run `bundle exec rake install`.
207
+
208
+ To release a new version, bump `Recorder::VERSION` and move the `[Unreleased]`
209
+ changelog entries under it in a pull request. Once that merges, tag the merge
210
+ commit and push the tag:
211
+
212
+ ```bash
213
+ git fetch origin
214
+ git tag vX.Y.Z origin/master
215
+ git push origin vX.Y.Z
216
+ ```
217
+
218
+ The Release workflow then runs CI against the tagged commit, checks the tag
219
+ matches `Recorder::VERSION`, publishes the gem to [rubygems.org](https://rubygems.org)
220
+ through trusted publishing, and creates the GitHub release from the changelog.
60
221
 
61
222
  ## Contributing
62
223
 
@@ -64,11 +225,10 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/jetroc
64
225
 
65
226
  ## Credits
66
227
 
67
- ![JetRockets](https://media.jetrockets.pro/jetrockets-white.png)
228
+ ![JetRockets](https://media.jetrockets.com/jetrockets-white.png)
68
229
 
69
- Recorder is maintained by [JetRockets](https://www.jetrockets.pro]).
230
+ Recorder is maintained by [JetRockets](https://www.jetrockets.com).
70
231
 
71
232
  ## License
72
233
 
73
234
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
74
-
@@ -10,20 +10,6 @@ module Recorder
10
10
 
11
11
  source_root File.expand_path('templates', __dir__)
12
12
 
13
- class_option(
14
- :with_partitions,
15
- type: :boolean,
16
- default: false,
17
- desc: 'Create partitions to `recorder_revisions` table'
18
- )
19
-
20
- class_option(
21
- :with_number_column,
22
- type: :boolean,
23
- default: false,
24
- desc: 'Add `number` column to `recorder_revisions` table'
25
- )
26
-
27
13
  class_option(
28
14
  :with_index_by_user_id,
29
15
  type: :boolean,
@@ -35,9 +21,7 @@ module Recorder
35
21
 
36
22
  def create_migration_file
37
23
  add_or_skip_recorder_migration('create_recorder_revisions')
38
- add_or_skip_recorder_migration('add_number_column_to_recorder_revisions') if options.with_number_column?
39
24
  add_or_skip_recorder_migration('add_index_by_user_id_to_recorder_revisions') if options.with_index_by_user_id?
40
- add_or_skip_recorder_migration('add_partitions_to_recorder_revisions') if options.with_partitions?
41
25
  end
42
26
 
43
27
  def self.next_migration_number(dirname)
@@ -47,11 +31,11 @@ module Recorder
47
31
  protected
48
32
 
49
33
  def add_or_skip_recorder_migration(template)
50
- migration_dir = File.expand_path('db/migrate')
34
+ migration_dir = File.expand_path('db/migrate', destination_root)
51
35
  if self.class.migration_exists?(migration_dir, template)
52
36
  ::Kernel.warn "Migration already exists: #{template}"
53
37
  else
54
- migration_template "#{template}.rb", "db/migrate/#{template}.rb"
38
+ migration_template "#{template}.rb.tt", "db/migrate/#{template}.rb"
55
39
  end
56
40
  end
57
41
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # This migration adds number column to the `revisions` table.
4
- class AddIndexByUserIdToRecorderRevisions < ActiveRecord::Migration
3
+ # This migration adds an index by `user_id` to the `recorder_revisions` table.
4
+ class AddIndexByUserIdToRecorderRevisions < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
5
5
  def change
6
6
  add_index :recorder_revisions, :user_id
7
7
  end
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # This migration creates the `recorder_revisions` table.
4
- class CreateRecorderRevisions < ActiveRecord::Migration
4
+ class CreateRecorderRevisions < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
5
5
  def change
6
6
  create_table :recorder_revisions do |t|
7
7
  t.string :item_type, null: false
8
- t.integer :item_id
8
+ t.bigint :item_id
9
9
  t.string :event, null: false
10
10
  t.jsonb :data, null: false
11
11
  t.inet :ip
12
12
  t.date :action_date, null: false
13
- t.integer :user_id
13
+ t.bigint :user_id
14
14
  t.jsonb :meta
15
15
  t.datetime :created_at, null: false
16
16
  end
@@ -4,9 +4,11 @@ module Recorder
4
4
  module Manager
5
5
  def recorder_disabled!
6
6
  Recorder.store.recorder_disabled!
7
+ return unless block_given?
7
8
 
8
- if block_given?
9
+ begin
9
10
  yield
11
+ ensure
10
12
  Recorder.store.recorder_enabled!
11
13
  end
12
14
  end
@@ -76,7 +76,7 @@ module Recorder
76
76
  # If `#recorder_changeset_class` method is not defined, then class name is generated as "#{class}Changeset"
77
77
  # @api private
78
78
  def changeset_class(object)
79
- klass = defined?(Draper) && object.decorated? ? object.source.class : object.class
79
+ klass = (defined?(Draper) && object.decorated?) ? object.source.class : object.class
80
80
  klass = klass.base_class
81
81
 
82
82
  return klass.send(:recorder_changeset_class) if klass.respond_to?(:recorder_changeset_class)
@@ -10,11 +10,13 @@ module Recorder
10
10
  end
11
11
 
12
12
  def data_for(event, options = {})
13
- {
13
+ data = {
14
14
  **attributes_for(event, options),
15
15
  **changes_for(event, options),
16
16
  **associations_for(event, options)
17
17
  }
18
+
19
+ record_changed?(data, event) ? data : {}
18
20
  end
19
21
 
20
22
  def attributes_for(_event, options)
@@ -22,11 +24,7 @@ module Recorder
22
24
  end
23
25
 
24
26
  def changes_for(event, options)
25
- changes =
26
- case event.to_sym
27
- when :update
28
- sanitize_attributes(item.saved_changes, options)
29
- end
27
+ changes = sanitize_attributes(item.saved_changes, options)
30
28
 
31
29
  changes.present? ? {changes: changes} : {}
32
30
  end
@@ -78,6 +76,16 @@ module Recorder
78
76
  end
79
77
  end
80
78
  end
79
+
80
+ def record_changed?(data, event)
81
+ event.to_sym != :update || data[:changes] || associations_changed?(data)
82
+ end
83
+
84
+ def associations_changed?(data)
85
+ return if data[:associations].nil?
86
+
87
+ data[:associations].any? { |name, association| association[:changes] }
88
+ end
81
89
  end
82
90
  end
83
91
  end
@@ -28,13 +28,15 @@ module Recorder
28
28
  options[:async].nil? ? Recorder.config.async : options[:async]
29
29
  end
30
30
 
31
+ # Sidekiq accepts only JSON-native job arguments. `data` is serialised
32
+ # ahead of the round trip so it survives as a string for the worker to
33
+ # parse back; everything else is normalised by the round trip itself.
31
34
  def record_async(params, options)
32
35
  params[:data] = params[:data].to_json
33
- params[:action_date] = params[:action_date].to_s
34
36
 
35
37
  Recorder::Sidekiq::RevisionsWorker.perform_in(
36
38
  options[:delay] || 2.seconds,
37
- params.stringify_keys
39
+ JSON.parse(params.to_json)
38
40
  )
39
41
  end
40
42
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Recorder
4
- VERSION = '1.2.2'
4
+ VERSION = '1.3.0'
5
5
  end
data/lib/recorder.rb CHANGED
@@ -60,7 +60,7 @@ module Recorder
60
60
 
61
61
  # Returns version of Recorder as +String+
62
62
  def version
63
- VERSION::STRING
63
+ VERSION
64
64
  end
65
65
  end
66
66
  end
metadata CHANGED
@@ -1,71 +1,83 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: recorder
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Alexandrov
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-17 00:00:00.000000000 Z
11
+ date: 2026-09-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '6.1'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '9'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - "~>"
27
+ - - ">="
25
28
  - !ruby/object:Gem::Version
26
29
  version: '6.1'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '9'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: activesupport
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
- - - "~>"
37
+ - - ">="
32
38
  - !ruby/object:Gem::Version
33
39
  version: '6.1'
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '9'
34
43
  type: :runtime
35
44
  prerelease: false
36
45
  version_requirements: !ruby/object:Gem::Requirement
37
46
  requirements:
38
- - - "~>"
47
+ - - ">="
39
48
  - !ruby/object:Gem::Version
40
49
  version: '6.1'
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '9'
41
53
  - !ruby/object:Gem::Dependency
42
- name: pg
54
+ name: request_store
43
55
  requirement: !ruby/object:Gem::Requirement
44
56
  requirements:
45
57
  - - ">="
46
58
  - !ruby/object:Gem::Version
47
- version: '0'
59
+ version: '1.0'
48
60
  type: :runtime
49
61
  prerelease: false
50
62
  version_requirements: !ruby/object:Gem::Requirement
51
63
  requirements:
52
64
  - - ">="
53
65
  - !ruby/object:Gem::Version
54
- version: '0'
66
+ version: '1.0'
55
67
  - !ruby/object:Gem::Dependency
56
- name: request_store
68
+ name: appraisal
57
69
  requirement: !ruby/object:Gem::Requirement
58
70
  requirements:
59
- - - ">="
71
+ - - "~>"
60
72
  - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :runtime
73
+ version: '2.5'
74
+ type: :development
63
75
  prerelease: false
64
76
  version_requirements: !ruby/object:Gem::Requirement
65
77
  requirements:
66
- - - ">="
78
+ - - "~>"
67
79
  - !ruby/object:Gem::Version
68
- version: '0'
80
+ version: '2.5'
69
81
  - !ruby/object:Gem::Dependency
70
82
  name: bundler
71
83
  requirement: !ruby/object:Gem::Requirement
@@ -84,16 +96,16 @@ dependencies:
84
96
  name: generator_spec
85
97
  requirement: !ruby/object:Gem::Requirement
86
98
  requirements:
87
- - - ">="
99
+ - - "~>"
88
100
  - !ruby/object:Gem::Version
89
- version: '0'
101
+ version: '0.9'
90
102
  type: :development
91
103
  prerelease: false
92
104
  version_requirements: !ruby/object:Gem::Requirement
93
105
  requirements:
94
- - - ">="
106
+ - - "~>"
95
107
  - !ruby/object:Gem::Version
96
- version: '0'
108
+ version: '0.9'
97
109
  - !ruby/object:Gem::Dependency
98
110
  name: jetrockets-standard
99
111
  requirement: !ruby/object:Gem::Requirement
@@ -109,47 +121,47 @@ dependencies:
109
121
  - !ruby/object:Gem::Version
110
122
  version: '0'
111
123
  - !ruby/object:Gem::Dependency
112
- name: rake
124
+ name: pg
113
125
  requirement: !ruby/object:Gem::Requirement
114
126
  requirements:
115
- - - "~>"
127
+ - - ">="
116
128
  - !ruby/object:Gem::Version
117
- version: '13.0'
129
+ version: '1.0'
118
130
  type: :development
119
131
  prerelease: false
120
132
  version_requirements: !ruby/object:Gem::Requirement
121
133
  requirements:
122
- - - "~>"
134
+ - - ">="
123
135
  - !ruby/object:Gem::Version
124
- version: '13.0'
136
+ version: '1.0'
125
137
  - !ruby/object:Gem::Dependency
126
- name: rspec-rails
138
+ name: rake
127
139
  requirement: !ruby/object:Gem::Requirement
128
140
  requirements:
129
- - - ">="
141
+ - - "~>"
130
142
  - !ruby/object:Gem::Version
131
- version: '0'
143
+ version: '13.0'
132
144
  type: :development
133
145
  prerelease: false
134
146
  version_requirements: !ruby/object:Gem::Requirement
135
147
  requirements:
136
- - - ">="
148
+ - - "~>"
137
149
  - !ruby/object:Gem::Version
138
- version: '0'
150
+ version: '13.0'
139
151
  - !ruby/object:Gem::Dependency
140
- name: rails-dummy
152
+ name: rspec-rails
141
153
  requirement: !ruby/object:Gem::Requirement
142
154
  requirements:
143
155
  - - ">="
144
156
  - !ruby/object:Gem::Version
145
- version: '0'
157
+ version: '5.0'
146
158
  type: :development
147
159
  prerelease: false
148
160
  version_requirements: !ruby/object:Gem::Requirement
149
161
  requirements:
150
162
  - - ">="
151
163
  - !ruby/object:Gem::Version
152
- version: '0'
164
+ version: '5.0'
153
165
  description: Recorder tracks changes of your Rails models
154
166
  email:
155
167
  - igor.alexandrov@jetrockets.com
@@ -157,21 +169,13 @@ executables: []
157
169
  extensions: []
158
170
  extra_rdoc_files: []
159
171
  files:
160
- - ".gitignore"
161
- - ".rspec"
162
- - ".rubocop.yml"
163
- - ".travis.yml"
164
- - Gemfile
172
+ - CHANGELOG.md
165
173
  - LICENSE.txt
166
174
  - README.md
167
- - Rakefile
168
- - bin/console
169
- - bin/setup
170
175
  - lib/generators/recorder/USAGE
171
176
  - lib/generators/recorder/install_generator.rb
172
- - lib/generators/recorder/templates/add_index_by_user_id_to_recorder_revisions.rb
173
- - lib/generators/recorder/templates/add_number_column_to_recorder_revisions.rb
174
- - lib/generators/recorder/templates/create_recorder_revisions.rb
177
+ - lib/generators/recorder/templates/add_index_by_user_id_to_recorder_revisions.rb.tt
178
+ - lib/generators/recorder/templates/create_recorder_revisions.rb.tt
175
179
  - lib/recorder.rb
176
180
  - lib/recorder/changeset.rb
177
181
  - lib/recorder/config.rb
@@ -186,11 +190,14 @@ files:
186
190
  - lib/recorder/tape/data.rb
187
191
  - lib/recorder/tape/record.rb
188
192
  - lib/recorder/version.rb
189
- - recorder.gemspec
190
193
  homepage: https://github.com/jetrockets/recorder
191
194
  licenses:
192
195
  - MIT
193
- metadata: {}
196
+ metadata:
197
+ source_code_uri: https://github.com/jetrockets/recorder
198
+ changelog_uri: https://github.com/jetrockets/recorder/blob/master/CHANGELOG.md
199
+ bug_tracker_uri: https://github.com/jetrockets/recorder/issues
200
+ rubygems_mfa_required: 'true'
194
201
  post_install_message:
195
202
  rdoc_options: []
196
203
  require_paths:
@@ -199,14 +206,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
199
206
  requirements:
200
207
  - - ">="
201
208
  - !ruby/object:Gem::Version
202
- version: '0'
209
+ version: '3.0'
203
210
  required_rubygems_version: !ruby/object:Gem::Requirement
204
211
  requirements:
205
212
  - - ">="
206
213
  - !ruby/object:Gem::Version
207
214
  version: '0'
208
215
  requirements: []
209
- rubygems_version: 3.3.7
216
+ rubygems_version: 3.5.22
210
217
  signing_key:
211
218
  specification_version: 4
212
219
  summary: Rails model auditor
data/.gitignore DELETED
@@ -1,15 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
-
11
- /spec/dummy/config/database.yml
12
-
13
- # Ignore vim swap files
14
- *.swp
15
- *.swo
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --format documentation
2
- --color
data/.rubocop.yml DELETED
@@ -1,27 +0,0 @@
1
- inherit_gem:
2
- jetrockets-standard: config/gems.yml
3
-
4
- AllCops:
5
- Exclude:
6
- - 'bin/*'
7
- - 'tmp/**/*'
8
- - 'docs/**/*'
9
- - 'Gemfile'
10
- - 'vendor/**/*'
11
- - 'gemfiles/**/*'
12
- DisplayCopNames: true
13
- TargetRubyVersion: 2.5
14
-
15
- Style/TrailingCommaInArrayLiteral:
16
- EnforcedStyleForMultiline: no_comma
17
-
18
- Style/TrailingCommaInHashLiteral:
19
- EnforcedStyleForMultiline: no_comma
20
-
21
- Layout/ParameterAlignment:
22
- EnforcedStyle: with_first_parameter
23
-
24
- # See https://github.com/rubocop-hq/rubocop/issues/4222
25
- Lint/AmbiguousBlockAssociation:
26
- Exclude:
27
- - 'spec/**/*'
data/.travis.yml DELETED
@@ -1,4 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.2.4
4
- before_install: gem install bundler -v 1.11.2
data/Gemfile DELETED
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
-
5
- # Specify your gem's dependencies in recorder.gemspec
6
- gemspec
data/Rakefile DELETED
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/gem_tasks'
4
- require 'rspec/core/rake_task'
5
- require 'rails/dummy/tasks'
6
-
7
- RSpec::Core::RakeTask.new(:spec)
8
-
9
- task default: :spec
data/bin/console DELETED
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'bundler/setup'
5
- require 'recorder'
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require "pry"
12
- # Pry.start
13
-
14
- require 'irb'
15
- IRB.start
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # This migration adds number column to the `revisions` table.
4
- class AddNumberColumnToRecorderRevisions < ActiveRecord::Migration
5
- def up
6
- add_column :recorder_revisions, :number, :integer, null: false, default: 0
7
-
8
- execute <<~SQL
9
- CREATE OR REPLACE FUNCTION get_recorder_revisions_number()
10
- RETURNS trigger AS
11
- $BODY$
12
- BEGIN
13
- SELECT COALESCE(MAX(recorder_revisions.number), 0) + 1
14
- INTO NEW.number
15
- FROM
16
- recorder_revisions
17
- WHERE
18
- recorder_revisions.item_type = NEW.item_type
19
- AND recorder_revisions.item_id = NEW.item_id;
20
- #{" "}
21
- RETURN NEW;
22
- END;
23
- $BODY$ LANGUAGE plpgsql;
24
- SQL
25
-
26
- execute <<~SQL
27
- CREATE TRIGGER update_recorder_revisions_number
28
- BEFORE INSERT ON recorder_revisions FOR EACH ROW
29
- EXECUTE PROCEDURE get_recorder_revisions_number();
30
- SQL
31
- end
32
-
33
- def down
34
- execute <<-SQL
35
- DROP TRIGGER update_recorder_revisions_number;
36
- SQL
37
-
38
- execute <<-SQL
39
- DROP FUNCTION IF EXISTS get_recorder_revisions_number;
40
- SQL
41
-
42
- remove_column :recorder_revisions, :number
43
- end
44
- end
data/recorder.gemspec DELETED
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- lib = File.expand_path('lib', __dir__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require 'recorder/version'
6
-
7
- Gem::Specification.new do |spec|
8
- spec.name = 'recorder'
9
- spec.version = Recorder::VERSION
10
- spec.authors = ['Igor Alexandrov']
11
- spec.email = ['igor.alexandrov@jetrockets.com']
12
-
13
- spec.summary = 'Rails model auditor'
14
- spec.description = 'Recorder tracks changes of your Rails models'
15
- spec.homepage = 'https://github.com/jetrockets/recorder'
16
- spec.license = 'MIT'
17
-
18
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
- spec.bindir = 'exe'
20
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
- spec.require_paths = ['lib']
22
-
23
- spec.add_dependency 'activerecord', '~> 6.1'
24
- spec.add_dependency 'activesupport', '~> 6.1'
25
- spec.add_dependency 'pg'
26
- spec.add_dependency 'request_store'
27
-
28
- spec.add_development_dependency 'bundler', '>= 2.0'
29
- # spec.add_development_dependency 'database_cleaner'
30
- spec.add_development_dependency 'generator_spec'
31
- spec.add_development_dependency 'jetrockets-standard'
32
- spec.add_development_dependency 'rake', '~> 13.0'
33
- spec.add_development_dependency 'rspec-rails'
34
- # spec.add_development_dependency 'timecop', '~> 0.8.1'
35
- # spec.add_development_dependency 'yard'
36
- spec.add_development_dependency 'rails-dummy'
37
- end