active_snapshot 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 204744938dec9e53545cc73a6497d47b9463b76b0622491d442b5ed4169b7c83
4
- data.tar.gz: dee647e71ff99f91e79764f6094e9253fa74b9641e6f2e611ea10485e3d1d570
3
+ metadata.gz: 89b0b688c46d6f42572e718276d0bb406beedf4aa3fcca78329ebd598d7a53f2
4
+ data.tar.gz: 210d3f36e4a8c12d4e85665e6d0324a0054333771ab0d7cb6b104f28be7b5b46
5
5
  SHA512:
6
- metadata.gz: fe9542a0beecba0f52d3d25f0dfbd1197f31eb4ab9758f7ccfaf0e5455dfd150ef6ee1ba81fc138383ff8004a9e26a037a6ad72c004766bac1f0a3bf68edb4e6
7
- data.tar.gz: eeab5ae90f2dc09f34ece44aa51992df834cf552693849d4595554e08448d4d8fceac954cb5ccbbe47089b1a868eeb089b5391ed3cf8a410aad6d436e6a3ef6a
6
+ metadata.gz: 4ff8613e2c3eaf100e8234b7da297ffc82d47cc387c3b7d22511f947b6e934382261751fb8c8518f5c06e8274341432416b9bcd42e9ab07b513717de9bfa5116
7
+ data.tar.gz: a9d43b41f96ace17d9a9320bbe231dabc326effc049ea1d82f44cc0cae920a61aeecc0d6a43c412e1680740a944bee56a253bdd0b8cf7b2770ea24fa71da0fde
data/CHANGELOG.md CHANGED
@@ -1,9 +1,22 @@
1
1
  CHANGELOG
2
2
  ---------
3
3
 
4
- - **UNRELEASED**
5
- * [View Diff](https://github.com/westonganger/active_snapshot/compare/v0.2.4...master)
6
- * Nothing yet
4
+ - **Unreleased**
5
+ * [View Diff](https://github.com/westonganger/active_snapshot/compare/v0.3.0...master)
6
+ - Nothing yet
7
+
8
+ - **v0.3.0** - November 14, 2022
9
+ * [View Diff](https://github.com/westonganger/active_snapshot/compare/v0.2.4...v0.3.0)
10
+ * [PR #24](https://github.com/westonganger/active_snapshot/pull/24) - Fix arguments for db migration for mysql
11
+ * [PR #29](https://github.com/westonganger/active_snapshot/pull/29) - Deprecate :identifier argument as a positional argument
12
+ * [PR #30](https://github.com/westonganger/active_snapshot/pull/30) - Make snapshot identifier optional
13
+ * [PR #32](https://github.com/westonganger/active_snapshot/pull/26) - Add configuration option `ActiveSnapshot.config.storage_method = 'serialized_json'` with support for `serialized_json`, `serialized_yaml`, `native_json`
14
+ * [PR #32](https://github.com/westonganger/active_snapshot/pull/32) - Change default storage method from `serialized_yaml` to `serialized_json`.
15
+ * [PR #32](https://github.com/westonganger/active_snapshot/pull/32) - `snapshot.metadata` and `snapshot_item.object` no longer return a HashWithIndifferentAccess. Now they simply return a regular Hash.
16
+ * **Upgrade Instructions**
17
+ * Change all instances of `create_snapshot!("my-snapshot-1"` to `create_snapshot!(identifier: "my-snapshot-1"`
18
+ * Create a migration with the following `change_column_null :snapshots, :identifier, true` to remove the null constraint here
19
+ * If you have existing snapshots from a previous version then please set `ActiveSnapshot.config.storage_method = "serialized_yaml"`
7
20
 
8
21
  - **v0.2.4** - Feb 25, 2022
9
22
  * [View Diff](https://github.com/westonganger/active_snapshot/compare/v0.2.3...v0.2.4)
data/README.md CHANGED
@@ -53,6 +53,25 @@ It defines an optional extension to your model: `has_snapshot_children`.
53
53
 
54
54
  It defines one instance method to your model: `create_snapshot!`
55
55
 
56
+ # Using a different storage format
57
+
58
+ By default ActiveSnapshot encodes objects to JSON and stores in the database as plain text. If you prefer to have YAML encoded columns or native JSON DB columns you can configure this as follows:
59
+
60
+ ```ruby
61
+ ActiveSnapshot.config do |config|
62
+ config.storage_method = "serialized_json" # default, for text column
63
+ #config.storage_method = "serialized_yaml" # for text column
64
+ #config.storage_method = "native_json" # for json/jsonb column
65
+ end
66
+ ```
67
+
68
+ If using a native json column, you should configure the `storage_method` before generating the migration. If this step was missed then you would need to create a migration to change the `:object` and `:metadata` columns to json (or jsonb)
69
+
70
+ ```ruby
71
+ change_column :snapshots, :object, :json
72
+ change_column :snapshots, :metadata, :json
73
+ ```
74
+
56
75
  # Basic Usage
57
76
 
58
77
  You now have access to the following methods:
@@ -60,9 +79,9 @@ You now have access to the following methods:
60
79
  ```ruby
61
80
  post = Post.first
62
81
 
63
- # Create snapshot grouped by identifier, only :identifier argument is required, all others are optional
82
+ # Create snapshot, all fields are optional
64
83
  snapshot = post.create_snapshot!(
65
- "snapshot_1", # Required
84
+ identifier: "snapshot_1",
66
85
  user: current_user,
67
86
  metadata: {
68
87
  foo: :bar
@@ -117,15 +136,15 @@ Now when you run `create_snapshot!` the associations will be tracked accordingly
117
136
  You can view all of the reified snapshot items by calling the following method. Its completely up to you on how to use this data.
118
137
 
119
138
  ```ruby
120
- reified_items = snapshot.fetch_reified_items
139
+ reified_parent, reified_children_hash = snapshot.fetch_reified_items
121
140
  ```
122
141
 
123
142
  As a safety these records have the `@readonly = true` attribute set on them. If you want to perform any write actions on the returned instances you will have to set `@readonly = nil`.
124
143
 
125
144
  ```ruby
126
- writable_reified_items = snapshot.fetch_reified_items.transform_values do |array|
127
- array.map{|x| x.instance_variable_set("@readonly", false); x}
128
- end
145
+ reified_parent, reified_children_hash = snapshot.fetch_reified_items
146
+
147
+ reified_parent.instance_variable_set("@readonly", false)
129
148
  ```
130
149
 
131
150
  # Key Models Provided & Additional Customizations
@@ -138,10 +157,10 @@ I strongly encourage you to read the code for this library to understand how it
138
157
  * Defines `snapshots` and `snapshot_items` has_many associations
139
158
  * Defines `create_snapshot!` and `has_snapshot_children` methods
140
159
  - [Snapshot](./lib/active_snapshot/models/snapshot.rb)
141
- * Contains a unique `identifier` column
160
+ * Contains a unique `identifier` column (optional, but available for custom identification purposes)
142
161
  * `has_many :item_snapshots`
143
162
  - [SnapshotItem](./lib/active_snapshot/models/snapshot_item.rb)
144
- * Contains `object` column with yaml encoded model instance `attributes`
163
+ * Contains `object` column which contains an encoded database row
145
164
  * `belongs_to :snapshot`
146
165
 
147
166
 
@@ -0,0 +1,31 @@
1
+ module ActiveSnapshot
2
+ class Config
3
+ attr_reader :storage_method
4
+
5
+ def initialize
6
+ @storage_method = 'serialized_json'
7
+ end
8
+
9
+ def storage_method=(value)
10
+ value_str = value.to_s
11
+
12
+ if ['serialized_yaml', 'serialized_json', 'native_json'].include?(value_str)
13
+ @storage_method = value_str
14
+ else
15
+ raise ArgumentError.new("Invalid storage_method provided")
16
+ end
17
+ end
18
+
19
+ def storage_method_yaml?
20
+ @storage_method == 'serialized_yaml'
21
+ end
22
+
23
+ def storage_method_json?
24
+ @storage_method == 'serialized_json'
25
+ end
26
+
27
+ def storage_method_native_json?
28
+ @storage_method == 'native_json'
29
+ end
30
+ end
31
+ end
@@ -8,7 +8,13 @@ module ActiveSnapshot
8
8
  has_many :snapshot_items, as: :item, class_name: 'ActiveSnapshot::SnapshotItem'
9
9
  end
10
10
 
11
- def create_snapshot!(identifier, user: nil, metadata: nil)
11
+ def create_snapshot!(legacy_identifier=nil, identifier: nil, user: nil, metadata: nil)
12
+ if identifier.nil? && legacy_identifier
13
+ identifier = legacy_identifier
14
+
15
+ ActiveSupport::Deprecation.warn(LEGACY_POSITIONAL_ARGUMENT_WARNING)
16
+ end
17
+
12
18
  snapshot = snapshots.create!({
13
19
  identifier: identifier,
14
20
  user_id: (user.id if user),
@@ -119,5 +125,7 @@ module ActiveSnapshot
119
125
  end
120
126
  end
121
127
 
128
+ LEGACY_POSITIONAL_ARGUMENT_WARNING = "Supplying the snapshots :identifier as a positional argument is now deprecated and will be removed in upcoming versions. Please supply the snapshot identifier using the :identifier keyword argument instead.".freeze
129
+
122
130
  end
123
131
  end
@@ -12,27 +12,42 @@ module ActiveSnapshot
12
12
 
13
13
  validates :item_id, presence: true
14
14
  validates :item_type, presence: true
15
- validates :identifier, presence: true, uniqueness: { scope: [:item_id, :item_type] }
15
+ validates :identifier, uniqueness: { scope: [:item_id, :item_type], allow_nil: true}
16
16
  validates :user_type, presence: true, if: :user_id
17
17
 
18
18
  def metadata
19
- yaml_method = "unsafe_load"
19
+ return @metadata if @metadata
20
20
 
21
- if !YAML.respond_to?("unsafe_load")
22
- yaml_method = "load"
23
- end
21
+ if ActiveSnapshot.config.storage_method_json?
22
+ @metadata = JSON.parse(self[:metadata])
23
+ elsif ActiveSnapshot.config.storage_method_yaml?
24
+ yaml_method = "unsafe_load"
25
+
26
+ if !YAML.respond_to?("unsafe_load")
27
+ yaml_method = "load"
28
+ end
24
29
 
25
- @metadata ||= YAML.send(yaml_method, self[:metadata]).with_indifferent_access
30
+ @metadata = YAML.send(yaml_method, self[:metadata])
31
+ elsif ActiveSnapshot.config.storage_method_native_json?
32
+ @metadata = self[:metadata]
33
+ end
26
34
  end
27
35
 
28
36
  def metadata=(h)
29
37
  @metadata = nil
30
- self[:metadata] = YAML.dump(h)
38
+
39
+ if ActiveSnapshot.config.storage_method_json?
40
+ self[:metadata] = h.to_json
41
+ elsif ActiveSnapshot.config.storage_method_yaml?
42
+ self[:metadata] = YAML.dump(h)
43
+ elsif ActiveSnapshot.config.storage_method_native_json?
44
+ self[:metadata] = h
45
+ end
31
46
  end
32
47
 
33
48
  def build_snapshot_item(instance, child_group_name: nil)
34
49
  self.snapshot_items.new({
35
- object: instance.attributes,
50
+ object: instance.attributes,
36
51
  item_id: instance.id,
37
52
  item_type: instance.class.name,
38
53
  child_group_name: child_group_name,
@@ -86,7 +101,7 @@ module ActiveSnapshot
86
101
 
87
102
  reified_parent = nil
88
103
 
89
- snapshot_items.each do |si|
104
+ snapshot_items.each do |si|
90
105
  reified_item = si.item_type.constantize.new(si.object)
91
106
 
92
107
  reified_item.readonly!
@@ -14,23 +14,38 @@ module ActiveSnapshot
14
14
  validates :item_type, presence: true, uniqueness: { scope: [:snapshot_id, :item_id] }
15
15
 
16
16
  def object
17
- yaml_method = "unsafe_load"
17
+ return @object if @object
18
18
 
19
- if !YAML.respond_to?("unsafe_load")
20
- yaml_method = "load"
21
- end
19
+ if ActiveSnapshot.config.storage_method_json?
20
+ @object = JSON.parse(self[:object])
21
+ elsif ActiveSnapshot.config.storage_method_yaml?
22
+ yaml_method = "unsafe_load"
23
+
24
+ if !YAML.respond_to?("unsafe_load")
25
+ yaml_method = "load"
26
+ end
22
27
 
23
- @metadata ||= YAML.send(yaml_method, self[:object]).with_indifferent_access
28
+ @object = YAML.send(yaml_method, self[:object])
29
+ elsif ActiveSnapshot.config.storage_method_native_json?
30
+ @object = self[:object]
31
+ end
24
32
  end
25
33
 
26
34
  def object=(h)
27
35
  @object = nil
28
- self[:object] = YAML.dump(h)
36
+
37
+ if ActiveSnapshot.config.storage_method_json?
38
+ self[:object] = h.to_json
39
+ elsif ActiveSnapshot.config.storage_method_yaml?
40
+ self[:object] = YAML.dump(h)
41
+ elsif ActiveSnapshot.config.storage_method_native_json?
42
+ self[:object] = h
43
+ end
29
44
  end
30
45
 
31
46
  def restore_item!
32
47
  ### Add any custom logic here
33
-
48
+
34
49
  if !item
35
50
  item_klass = item_type.constantize
36
51
 
@@ -1,3 +1,3 @@
1
1
  module ActiveSnapshot
2
- VERSION = "0.2.4".freeze
2
+ VERSION = "0.3.0".freeze
3
3
  end
@@ -2,6 +2,7 @@ require "active_record"
2
2
  require "activerecord-import"
3
3
 
4
4
  require "active_snapshot/version"
5
+ require "active_snapshot/config"
5
6
 
6
7
  require "active_snapshot/models/snapshot"
7
8
  require "active_snapshot/models/snapshot_item"
@@ -14,4 +15,14 @@ module ActiveSnapshot
14
15
  included do
15
16
  include ActiveSnapshot::SnapshotsConcern
16
17
  end
18
+
19
+ @@config = ActiveSnapshot::Config.new
20
+
21
+ def self.config(&block)
22
+ if block_given?
23
+ block.call(@@config)
24
+ else
25
+ return @@config
26
+ end
27
+ end
17
28
  end
@@ -3,16 +3,16 @@ class <%= migration_name %> < ActiveRecord::Migration::Current
3
3
  def change
4
4
  create_table :snapshots<%= table_options %> do |t|
5
5
  t.belongs_to :item, polymorphic: true, null: false, index: true
6
- t.string :identifier, null: false, unique: [:item_id, :item_type], index: true
6
+ t.string :identifier, unique: [:item_id, :item_type], index: true
7
7
  t.belongs_to :user, polymorphic: true
8
- t.text :metadata
8
+ t.<%= ActiveSnapshot.config.storage_method == 'native_json' ? 'json' : 'text' %> :metadata
9
9
  t.datetime :created_at, null: false
10
10
  end
11
11
 
12
12
  create_table :snapshot_items<%= table_options %> do |t|
13
13
  t.belongs_to :snapshot, null: false, index: true
14
14
  t.belongs_to :item, polymorphic: true, null: false, unique: [:snapshot_id], index: true
15
- t.text :object, null: false
15
+ t.<%= ActiveSnapshot.config.storage_method == 'native_json' ? 'json' : 'text' %> :object, null: false
16
16
  t.datetime :created_at, null: false
17
17
  t.string :child_group_name
18
18
  end
@@ -66,7 +66,7 @@ module ActiveSnapshot
66
66
  #
67
67
  def table_options
68
68
  if mysql?
69
- ', { options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci" }'
69
+ ', options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci"'
70
70
  else
71
71
  ""
72
72
  end
@@ -6,6 +6,10 @@ Bundler.require
6
6
 
7
7
  module Dummy
8
8
  class Application < Rails::Application
9
+ if Rails::VERSION::STRING.to_f >= 5.1
10
+ config.load_defaults Rails::VERSION::STRING.to_f
11
+ end
12
+
9
13
  # Settings in config/environments/* take precedence over those specified here.
10
14
  # Application configuration should go into files in config/initializers
11
15
  # -- all .rb files in that directory are automatically loaded.
@@ -42,12 +46,5 @@ module Dummy
42
46
  config.after_initialize do
43
47
  ActiveRecord::Migration.migrate(Rails.root.join("db/migrate/*").to_s)
44
48
  end
45
-
46
- if ActiveRecord.respond_to?(:gem_version)
47
- gem_version = ActiveRecord.gem_version
48
- if gem_version.to_s.start_with?("5.2.")
49
- config.active_record.sqlite3.represent_boolean_as_integer = true
50
- end
51
- end
52
49
  end
53
50
  end
@@ -3,16 +3,28 @@ class CreateSnapshotsTables < ActiveRecord::Migration::Current
3
3
  def change
4
4
  create_table :snapshots do |t|
5
5
  t.belongs_to :item, polymorphic: true, null: false, index: true
6
- t.string :identifier, null: false, unique: true, index: true
6
+ t.string :identifier, unique: true, index: true
7
7
  t.belongs_to :user, polymorphic: true
8
- t.text :metadata
8
+
9
+ if ActiveSnapshot.config.storage_method_native_json?
10
+ t.json :metadata
11
+ else
12
+ t.text :metadata
13
+ end
14
+
9
15
  t.datetime :created_at, null: false
10
16
  end
11
17
 
12
18
  create_table :snapshot_items do |t|
13
19
  t.belongs_to :snapshot, null: false, index: true
14
20
  t.belongs_to :item, polymorphic: true, null: false, unique: [:snapshot_id], index: true
15
- t.text :object, null: false
21
+
22
+ if ActiveSnapshot.config.storage_method_native_json?
23
+ t.json :object, null: false
24
+ else
25
+ t.text :object, null: false
26
+ end
27
+
16
28
  t.datetime :created_at, null: false
17
29
  t.string :child_group_name
18
30
  end
Binary file