Skip to content

Commit 23a59e3

Browse files
authored
Use native_json as default storage method for new installations (#67)
- Switch default storage method to native SQL JSON columns - No longer recommend to set `ActiveSnapshot.config.storage_method`, its only retained to support legacy installations - Drop support for Rails 6.0
1 parent 1d41e90 commit 23a59e3

File tree

13 files changed

+77
-89
lines changed

13 files changed

+77
-89
lines changed

.github/workflows/test.yml

+5-11
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ jobs:
2525
- ruby: "3.2"
2626
- ruby: "3.3"
2727
### TEST RAILS VERSIONS
28-
- ruby: "2.6"
29-
rails_version: "~> 6.0.0"
3028
- ruby: "2.6"
3129
rails_version: "~> 6.1.0"
3230
- ruby: "3.3"
@@ -38,34 +36,30 @@ jobs:
3836
rails_version: "~> 7.2.0"
3937
- ruby: "3.3"
4038
rails_version: "~> 8.0.0"
39+
### DB TESTING TESTING
40+
- ruby: 3.3
41+
db_gem: "mysql2"
42+
- ruby: 3.3
43+
db_gem: "pg"
4144
### STORAGE METHOD TESTING
4245
- ruby: 3.3
4346
db_gem: "sqlite3"
4447
active_snapshot_storage_method: "serialized_json"
4548
- ruby: 3.3
4649
db_gem: "sqlite3"
4750
active_snapshot_storage_method: "serialized_yaml"
48-
- ruby: 3.3
49-
db_gem: "sqlite3"
50-
active_snapshot_storage_method: "native_json"
5151
- ruby: 3.3
5252
db_gem: "mysql2"
5353
active_snapshot_storage_method: "serialized_json"
5454
- ruby: 3.3
5555
db_gem: "mysql2"
5656
active_snapshot_storage_method: "serialized_yaml"
57-
- ruby: 3.3
58-
db_gem: "mysql2"
59-
active_snapshot_storage_method: "native_json"
6057
- ruby: 3.3
6158
db_gem: "pg"
6259
active_snapshot_storage_method: "serialized_json"
6360
- ruby: 3.3
6461
db_gem: "pg"
6562
active_snapshot_storage_method: "serialized_yaml"
66-
- ruby: 3.3
67-
db_gem: "pg"
68-
active_snapshot_storage_method: "native_json"
6963

7064
services:
7165
mysql:

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ Gemfile.lock
1616
/test/**/tmp/
1717

1818
test/dummy_app/**/*.sqlite*
19+
test/dummy_app/db/schema.rb
1920
test/dummy_app/**/*.log

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ CHANGELOG
33

44
- **Unreleased**
55
* [View Diff](https://github.com/westonganger/active_snapshot/compare/v0.5.1...master)
6-
* Nothing yet
6+
* [#67](https://github.com/westonganger/active_snapshot/pull/67) - Switch default storage method to native SQL JSON columns. No longer recommend to set `ActiveSnapshot.config.storage_method`, its only retained to support legacy installations
7+
* Drop support for Rails 6.0. Rails 6.1 is minimum required version now.
78

89
- **v0.5.1** - Nov 11, 2024
910
* [View Diff](https://github.com/westonganger/active_snapshot/compare/v0.5.0...v0.5.1)

README.md

-19
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,6 @@ It defines an optional extension to your model: `has_snapshot_children`.
5353

5454
It defines one instance method to your model: `create_snapshot!`
5555

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-
7556
# Basic Usage
7657

7758
You now have access to the following methods:

active_snapshot.gemspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
1717
s.files = Dir.glob("{lib/**/*}") + %w{ LICENSE README.md Rakefile CHANGELOG.md }
1818
s.require_path = 'lib'
1919

20-
s.add_runtime_dependency "activerecord", ">= 6.0"
20+
s.add_runtime_dependency "activerecord", ">= 6.1"
2121
s.add_runtime_dependency "railties"
2222

2323
s.add_development_dependency "rake"

lib/active_snapshot/config.rb

+21-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
module ActiveSnapshot
22
class Config
3-
attr_reader :storage_method
4-
53
def initialize
6-
@storage_method = 'serialized_json'
4+
end
5+
6+
def storage_method
7+
if @storage_method.nil?
8+
if ActiveSnapshot::SnapshotItem.table_exists? && ActiveSnapshot::SnapshotItem.type_for_attribute(:object).type == :text
9+
# for legacy active_snapshot configurations only
10+
self.storage_method = 'serialized_json'
11+
else
12+
self.storage_method = 'native_json'
13+
end
14+
end
15+
16+
@storage_method
717
end
818

919
def storage_method=(value)
20+
# for legacy active_snapshot configurations only
21+
1022
value_str = value.to_s
1123

1224
if ['serialized_yaml', 'serialized_json', 'native_json'].include?(value_str)
@@ -17,15 +29,15 @@ def storage_method=(value)
1729
end
1830

1931
def storage_method_yaml?
20-
@storage_method == 'serialized_yaml'
32+
# for legacy active_snapshot configurations only
33+
storage_method == 'serialized_yaml'
2134
end
2235

23-
def storage_method_json?
24-
@storage_method == 'serialized_json'
36+
def storage_method_serialized_json?
37+
# for legacy active_snapshot configurations only
38+
storage_method == 'serialized_json'
2539
end
40+
alias_method :storage_method_json?, :storage_method_serialized_json?
2641

27-
def storage_method_native_json?
28-
@storage_method == 'native_json'
29-
end
3042
end
3143
end

lib/active_snapshot/models/snapshot.rb

+8-4
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,33 @@ class Snapshot < ActiveRecord::Base
1818
def metadata
1919
return @metadata if @metadata
2020

21-
if ActiveSnapshot.config.storage_method_json?
21+
if ActiveSnapshot.config.storage_method_serialized_json?
22+
# for legacy active_snapshot configurations only
2223
@metadata = JSON.parse(self[:metadata])
2324
elsif ActiveSnapshot.config.storage_method_yaml?
25+
# for legacy active_snapshot configurations only
2426
yaml_method = "unsafe_load"
2527

2628
if !YAML.respond_to?("unsafe_load")
2729
yaml_method = "load"
2830
end
2931

3032
@metadata = YAML.send(yaml_method, self[:metadata])
31-
elsif ActiveSnapshot.config.storage_method_native_json?
33+
else
3234
@metadata = self[:metadata]
3335
end
3436
end
3537

3638
def metadata=(h)
3739
@metadata = nil
3840

39-
if ActiveSnapshot.config.storage_method_json?
41+
if ActiveSnapshot.config.storage_method_serialized_json?
42+
# for legacy active_snapshot configurations only
4043
self[:metadata] = h.to_json
4144
elsif ActiveSnapshot.config.storage_method_yaml?
45+
# for legacy active_snapshot configurations only
4246
self[:metadata] = YAML.dump(h)
43-
elsif ActiveSnapshot.config.storage_method_native_json?
47+
else
4448
self[:metadata] = h
4549
end
4650
end

lib/active_snapshot/models/snapshot_item.rb

+8-6
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,29 @@ class SnapshotItem < ActiveRecord::Base
1717
def object
1818
return @object if @object
1919

20-
if ActiveSnapshot.config.storage_method_json?
20+
if ActiveSnapshot.config.storage_method_serialized_json?
21+
# for legacy active_snapshot configurations only
2122
@object = self[:object] ? JSON.parse(self[:object]) : {}
2223
elsif ActiveSnapshot.config.storage_method_yaml?
24+
# for legacy active_snapshot configurations only
2325
yaml_method = YAML.respond_to?(:unsafe_load) ? :unsafe_load : :load
2426

2527
@object = self[:object] ? YAML.public_send(yaml_method, self[:object]) : {}
26-
elsif ActiveSnapshot.config.storage_method_native_json?
27-
@object = self[:object]
2828
else
29-
raise StandardError, "Unsupported storage_method: `#{ActiveSnapshot.config.storage_method}`"
29+
@object = self[:object]
3030
end
3131
end
3232

3333
def object=(h)
3434
@object = nil
3535

36-
if ActiveSnapshot.config.storage_method_json?
36+
if ActiveSnapshot.config.storage_method_serialized_json?
37+
# for legacy active_snapshot configurations only
3738
self[:object] = h.to_json
3839
elsif ActiveSnapshot.config.storage_method_yaml?
40+
# for legacy active_snapshot configurations only
3941
self[:object] = YAML.dump(h)
40-
elsif ActiveSnapshot.config.storage_method_native_json?
42+
else
4143
self[:object] = h
4244
end
4345
end

lib/generators/active_snapshot/install/templates/create_snapshots_tables.rb.erb

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class <%= migration_name %> < ActiveRecord::Migration::Current
88
t.string :identifier, index: true
99
t.index [:identifier, :item_id, :item_type], unique: true
1010

11-
t.<%= ActiveSnapshot.config.storage_method == 'native_json' ? 'json' : 'text' %> :metadata
11+
t.json :metadata
1212

1313
t.datetime :created_at, null: false
1414
end
@@ -18,7 +18,7 @@ class <%= migration_name %> < ActiveRecord::Migration::Current
1818
t.belongs_to :item, polymorphic: true, null: false, index: true
1919
t.index [:snapshot_id, :item_id, :item_type], unique: true
2020

21-
t.<%= ActiveSnapshot.config.storage_method == 'native_json' ? 'json' : 'text' %> :object, null: false
21+
t.json :object, null: false
2222

2323
t.datetime :created_at, null: false
2424
t.string :child_group_name

test/models/snapshot_item_test.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ def test_validations
3737
assert_equal ["can't be blank"], instance.errors[attr] ### presence error
3838
end
3939

40-
shared_post = DATA[:shared_post]
41-
snapshot = shared_post.snapshots.first
40+
post = Post.first!
41+
snapshot = post.snapshots.first
4242

4343
instance = @snapshot_item_klass.new(item: snapshot.item, snapshot: snapshot)
4444

test/models/snapshot_test.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def teardown
1010
end
1111

1212
def test_relationships
13-
shared_post = DATA[:shared_post]
13+
shared_post = Post.first!
1414

1515
instance = @snapshot_klass.new
1616

@@ -38,7 +38,7 @@ def test_relationships
3838
end
3939

4040
def test_validations
41-
shared_post = DATA[:shared_post]
41+
shared_post = Post.first!
4242
snapshot = shared_post.snapshots.first
4343

4444
instance = @snapshot_klass.new

test/test_helper.rb

+5-23
Original file line numberDiff line numberDiff line change
@@ -57,30 +57,12 @@ class ActiveSupport::TestCase
5757
ActiveRecord::MigrationContext.new(File.expand_path("dummy_app/db/migrate/", __dir__)).migrate
5858
end
5959

60-
require 'rspec/mocks'
61-
module MinitestRSpecMocksIntegration
62-
include RSpec::Mocks::ExampleMethods
63-
64-
def before_setup
65-
RSpec::Mocks.setup
66-
super
67-
end
68-
69-
def after_teardown
70-
super
71-
RSpec::Mocks.verify
72-
ensure
73-
RSpec::Mocks.teardown
74-
end
75-
end
76-
Minitest::Test.send(:include, MinitestRSpecMocksIntegration)
77-
78-
DATA = {}.with_indifferent_access
60+
require "rspec/mocks/minitest_integration"
7961

80-
DATA[:shared_post] = Post.create!(a: 1, b: 3)
81-
DATA[:shared_post].create_snapshot!(identifier: 'v1')
82-
DATA[:shared_post].update_columns(a: 2, b: 4)
83-
DATA[:shared_post].create_snapshot!(identifier: 'v2')
62+
post = Post.create!(a: 1, b: 3)
63+
post.create_snapshot!(identifier: 'v1')
64+
post.update_columns(a: 2, b: 4)
65+
post.create_snapshot!(identifier: 'v2')
8466

8567
def assert_time_match(a, b)
8668
format = "%d-%m-%Y %h:%M:%S.%L" ### MUST LIMIT THE MILLISECONDS TO 3 decimal places of accuracy, the rest are dropped

test/unit/config_test.rb

+20-9
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,30 @@ def teardown
1111
ActiveSnapshot.config.storage_method = @orig_storage_method
1212
end
1313

14-
def test_defaults_to_serialized_json
14+
def test_defaults_to_serialized_json_if_text_column_exists
1515
if ENV["ACTIVE_SNAPSHOT_STORAGE_METHOD"].present?
1616
skip
1717
end
1818

19+
ActiveSnapshot.config.instance_variable_set("@storage_method", nil)
20+
21+
allow(ActiveSnapshot::SnapshotItem).to receive(:type_for_attribute).with(:object).and_return(ActiveRecord::Type::Text.new)
22+
1923
assert_equal 'serialized_json', ActiveSnapshot.config.storage_method
2024

2125
assert_equal false, ActiveSnapshot.config.storage_method_yaml?
22-
assert_equal true, ActiveSnapshot.config.storage_method_json?
23-
assert_equal false, ActiveSnapshot.config.storage_method_native_json?
26+
assert_equal true, ActiveSnapshot.config.storage_method_serialized_json?
27+
end
28+
29+
def test_defaults_to_native_json
30+
if ENV["ACTIVE_SNAPSHOT_STORAGE_METHOD"].present?
31+
skip
32+
end
33+
34+
assert_equal 'native_json', ActiveSnapshot.config.storage_method
35+
36+
assert_equal false, ActiveSnapshot.config.storage_method_yaml?
37+
assert_equal false, ActiveSnapshot.config.storage_method_serialized_json?
2438
end
2539

2640
def test_accepts_to_serialized_json
@@ -29,8 +43,7 @@ def test_accepts_to_serialized_json
2943
assert_equal 'serialized_json', ActiveSnapshot.config.storage_method
3044

3145
assert_equal false, ActiveSnapshot.config.storage_method_yaml?
32-
assert_equal true, ActiveSnapshot.config.storage_method_json?
33-
assert_equal false, ActiveSnapshot.config.storage_method_native_json?
46+
assert_equal true, ActiveSnapshot.config.storage_method_serialized_json?
3447
end
3548

3649

@@ -40,8 +53,7 @@ def test_accepts_serialized_yaml
4053
assert_equal 'serialized_yaml', ActiveSnapshot.config.storage_method
4154

4255
assert_equal true, ActiveSnapshot.config.storage_method_yaml?
43-
assert_equal false, ActiveSnapshot.config.storage_method_json?
44-
assert_equal false, ActiveSnapshot.config.storage_method_native_json?
56+
assert_equal false, ActiveSnapshot.config.storage_method_serialized_json?
4557
end
4658

4759
def test_accepts_native_json
@@ -50,8 +62,7 @@ def test_accepts_native_json
5062
assert_equal "native_json", ActiveSnapshot.config.storage_method, "native_json"
5163

5264
assert_equal false, ActiveSnapshot.config.storage_method_yaml?
53-
assert_equal false, ActiveSnapshot.config.storage_method_json?
54-
assert_equal true, ActiveSnapshot.config.storage_method_native_json?
65+
assert_equal false, ActiveSnapshot.config.storage_method_serialized_json?
5566
end
5667

5768
def test_config_doesnt_accept_not_specified_storage_methods

0 commit comments

Comments
 (0)