snappier 0.1.4 → 0.1.5
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 +4 -4
- data/.tool-versions +1 -0
- data/README.md +50 -10
- data/lib/snappier/changes.rb +14 -12
- data/lib/snappier/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e522706b988ca8ec71eb16695db3dd727aa31b3e529d0c23353828106433078f
|
4
|
+
data.tar.gz: 80d6a461f498c1ee78ae550c70707fdecebe9c9eea3dd1fb1b408c7eff0e5a08
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 266a8c2f2d3ee1fc8f1efa945d711ef9b0591fb80d6365b270e18c8c6f940782576cde9fd22da74d8978ef0dce0eb499676b2c2c81d68818d5383347782fa8d8
|
7
|
+
data.tar.gz: 69ddaa90ce8b412ca44b32ffc4c602a1eba8a1862462488262c8a0f4b68fa7ab2e6f95e3080b1e74c0cb8bca3fe5a97d8a090d61861fe8a2f6925f2971d90b47
|
data/.tool-versions
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby 3.4.4
|
data/README.md
CHANGED
@@ -1,28 +1,68 @@
|
|
1
1
|
# Snappier
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/snappier`. To experiment with that code, run `bin/console` for an interactive prompt.
|
3
|
+
Imagine a dream where every moment your objects whisper their truths into the ether—each change a shimmer in time, gently bottled and sealed. Snappier is the archivist of that dream: a watchful spirit that captures the essence of Ruby objects, encasing their state in crystalline snapshots. Through enchanted serializers and portals like S3, their stories are preserved across realms. You shape the ritual—what is seen, how it's told, where it rests—yet Snappier hums quietly, faithfully, beneath it all.
|
6
4
|
|
7
5
|
## Installation
|
8
6
|
|
9
|
-
TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
|
10
|
-
|
11
|
-
Install the gem and add to the application's Gemfile by executing:
|
12
|
-
|
13
7
|
```bash
|
14
|
-
bundle add
|
8
|
+
bundle add snappier
|
15
9
|
```
|
16
10
|
|
17
11
|
If bundler is not being used to manage dependencies, install the gem by executing:
|
18
12
|
|
19
13
|
```bash
|
20
|
-
gem install
|
14
|
+
gem install snappier
|
21
15
|
```
|
22
16
|
|
23
17
|
## Usage
|
24
18
|
|
25
|
-
|
19
|
+
Right, so you’ve decided to track your data like a proper psycho—every twist, every turn, every bloody mutation. You take Snappier, slap it in your Gemfile like it owes you money, and set it up to watch your objects spill their guts. You tell it how to remember—JSON, YAML, dark magic—whatever. Then you point it somewhere to stash the loot: S3, disk, down the pub. When the time comes, you take the snapshot. Click. Another wee lie safely stored. Choose life. Choose history. Choose Snappier.
|
20
|
+
|
21
|
+
Call `Snappier::Take.for(entity)` in your application code and a snapshopt will be persisted via a sidekiq job (this specific dependency on sidekiq for async processing may be abstracted later).
|
22
|
+
|
23
|
+
You may call this in an active record model callback (like `after_save`) or anywhere else in your application code.
|
24
|
+
|
25
|
+
There is no specific dependency on active record but the methods `attributes`, `previously_new_record?` and `destroyed?` are used by default (the latter two deciding if snapshot is related to create/delete otherwise defaulting to update).
|
26
|
+
|
27
|
+
To attribute changes to a specific user, call `Snappier::Who.current = "<current user description>"` and that
|
28
|
+
information will be persisted with any subsequent snapshots. In rails, you may set this in a `before_action`
|
29
|
+
method to capture a description of the current user - this setting exists only in the current thread.
|
30
|
+
|
31
|
+
By default snapshots are persisted to `tmp/snappier`. You can instead persist to S3 using the `snappier-aws_s3` extension gem and the following configuration when your application starts up:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
persistence = Snappier::AwsS3::Persistence.new(
|
35
|
+
region: aws_region,
|
36
|
+
bucket_name: bucket_name,
|
37
|
+
credentials: aws_credentials,
|
38
|
+
)
|
39
|
+
Snappier::Registry.register_persistence(persistence)
|
40
|
+
```
|
41
|
+
|
42
|
+
By default, snapshot state is persisted by calling `record.attributes`, if you want to persist more or less information then you can create a module and register it when your application starts up:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
module OrderSnapshot
|
46
|
+
def self.snap(order)
|
47
|
+
order.attributes.without(:created_at, :updated_at).tap do |attributes|
|
48
|
+
attributes["line_items"] = order.line_items.map { |line_item| line_item.attributes }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
Snappier::Registry.register(
|
54
|
+
"Order" => "OrderSnapshot"
|
55
|
+
)
|
56
|
+
```
|
57
|
+
|
58
|
+
It is possible to replay the snapshots for a specific record which will calculate any change for presentation in a UI:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
Snappier::Replay.for(
|
62
|
+
type: Order,
|
63
|
+
id: "1"
|
64
|
+
) { |change| pp change }
|
65
|
+
```
|
26
66
|
|
27
67
|
## Development
|
28
68
|
|
data/lib/snappier/changes.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "fileutils"
|
4
|
-
|
5
3
|
module Snappier
|
6
4
|
module Changes
|
7
5
|
def self.between(previous_state, current_state)
|
@@ -47,25 +45,29 @@ module Snappier
|
|
47
45
|
end
|
48
46
|
|
49
47
|
def self.append_changes_for_collections(changes, previous_collection, current_collection, path)
|
50
|
-
ids =
|
51
|
-
previous_collection ||= []
|
52
|
-
current_collection ||= []
|
53
|
-
previous_collection.each { |e| ids << e["id"] if e["id"] }
|
54
|
-
current_collection.each { |e| ids << e["id"] if e["id"] }
|
48
|
+
ids = ids_for_collections(previous_collection, current_collection)
|
55
49
|
|
56
50
|
if ids.empty?
|
57
|
-
changes[path] = [previous_collection, current_collection]
|
51
|
+
changes[path] = [previous_collection, current_collection] unless previous_collection == current_collection
|
58
52
|
return
|
59
53
|
end
|
60
54
|
|
61
55
|
ids.each do |id|
|
62
|
-
previous_value = previous_collection.find { |r| r["id"] == id } || {}
|
63
|
-
current_value = current_collection.find { |r| r["id"] == id } || {}
|
64
|
-
previous_value
|
65
|
-
current_value
|
56
|
+
previous_value = (previous_collection || []).find { |r| r["id"] == id } || {}
|
57
|
+
current_value = (current_collection || []).find { |r| r["id"] == id } || {}
|
58
|
+
previous_value&.delete("id")
|
59
|
+
current_value&.delete("id")
|
66
60
|
|
67
61
|
append_changes(changes, previous_value, current_value, path + [id])
|
68
62
|
end
|
69
63
|
end
|
64
|
+
|
65
|
+
def self.ids_for_collections(*collections)
|
66
|
+
Set.new.tap do |ids|
|
67
|
+
collections.each do |collection|
|
68
|
+
(collection || []).each { |e| ids << e["id"] if e["id"] }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
70
72
|
end
|
71
73
|
end
|
data/lib/snappier/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: snappier
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark Ryall
|
@@ -32,6 +32,7 @@ extra_rdoc_files: []
|
|
32
32
|
files:
|
33
33
|
- ".rspec"
|
34
34
|
- ".rubocop.yml"
|
35
|
+
- ".tool-versions"
|
35
36
|
- CODE_OF_CONDUCT.md
|
36
37
|
- LICENSE.txt
|
37
38
|
- README.md
|