Class: Magick::Versioning
- Inherits:
-
Object
- Object
- Magick::Versioning
- Defined in:
- lib/magick/versioning.rb
Defined Under Namespace
Classes: Version
Constant Summary collapse
- DEFAULT_MAX_VERSIONS =
50- STORE_PREFIX =
Version history lives under a reserved pseudo-feature namespace so that feature reads (get_all_data) never drag snapshot blobs along, and so deleting a feature does not destroy its ActiveRecord archive row.
'__magick_versions:'- VERSION_KEY_PREFIX =
The hot window keeps one store key per snapshot. Appending writes only its own key, so two containers saving at the same time interleave into one history instead of each rewriting the whole list over the other's entries.
'version_'- VERSION_KEY_PATTERN =
/\A#{VERSION_KEY_PREFIX}(\d+)\z/.freeze
- ARCHIVE_ROW_INFIX =
The archive keeps one store ROW per version, not one key per version inside the feature's single row. An ActiveRecord write is a read-modify-write of the whole row blob under a row lock, so appending version N to a shared row rewrote all N-1 predecessors with it; its own row makes an append cost one snapshot no matter how long the history is.
'#v'- ARCHIVE_DATA_KEY =
'snapshot'- SEQUENCE_ROW_SUFFIX =
The shared counter that hands out version numbers, in a row of its own so that allocating a number never rewrites archived snapshots alongside it.
'#seq'- SEQUENCE_KEY =
'sequence'- LEGACY_WINDOW_KEY =
1.5.0 wrote the whole hot window as a single JSON list under this key. Still read (and migrated forward on the next append) so history written before the upgrade stays visible.
'versions'
Instance Attribute Summary collapse
-
#max_versions ⇒ Object
readonly
Returns the value of attribute max_versions.
Instance Method Summary collapse
-
#get_versions(feature_name, all: false) ⇒ Object
Hot window (last max_versions, memory/Redis) by default; all: true merges the unlimited ActiveRecord archive when one is configured.
-
#initialize(adapter_registry, max_versions: DEFAULT_MAX_VERSIONS) ⇒ Versioning
constructor
A new instance of Versioning.
-
#record_change(feature, action: nil, created_by: nil, snapshot: nil) ⇒ Object
Called by Feature#record_change after every successful mutation.
-
#rollback(feature_name, version) ⇒ Object
Restore the feature to the snapshot stored in the given version, then record the rollback itself as a new version + audit entry (history only ever rolls forward).
-
#save_version(feature_name, version: nil, created_by: nil) ⇒ Object
Explicit manual snapshot.
Constructor Details
#initialize(adapter_registry, max_versions: DEFAULT_MAX_VERSIONS) ⇒ Versioning
Returns a new instance of Versioning.
61 62 63 64 |
# File 'lib/magick/versioning.rb', line 61 def initialize(adapter_registry, max_versions: DEFAULT_MAX_VERSIONS) @adapter_registry = adapter_registry @max_versions = max_versions.to_i.positive? ? max_versions.to_i : DEFAULT_MAX_VERSIONS end |
Instance Attribute Details
#max_versions ⇒ Object (readonly)
Returns the value of attribute max_versions.
66 67 68 |
# File 'lib/magick/versioning.rb', line 66 def max_versions @max_versions end |
Instance Method Details
#get_versions(feature_name, all: false) ⇒ Object
Hot window (last max_versions, memory/Redis) by default; all: true merges the unlimited ActiveRecord archive when one is configured.
84 85 86 87 88 89 90 |
# File 'lib/magick/versioning.rb', line 84 def get_versions(feature_name, all: false) name = feature_name.to_s hot = hot_entries(name) return sorted(hot).last(@max_versions) unless all sorted(hot.merge(archive_entries(name))) end |
#record_change(feature, action: nil, created_by: nil, snapshot: nil) ⇒ Object
Called by Feature#record_change after every successful mutation. snapshot: allows callers (delete) to capture state before the mutation.
77 78 79 80 |
# File 'lib/magick/versioning.rb', line 77 def record_change(feature, action: nil, created_by: nil, snapshot: nil) data = snapshot ? deep_symbolize(JSON.parse(JSON.generate(snapshot))) : snapshot_of(feature) append(feature.name, data, created_by: created_by, action: action) end |
#rollback(feature_name, version) ⇒ Object
Restore the feature to the snapshot stored in the given version, then record the rollback itself as a new version + audit entry (history only ever rolls forward). Restores state wholesale: value (including false/ empty), status, group, the entire targeting hash, and dependencies.
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/magick/versioning.rb', line 96 def rollback(feature_name, version) entry = find_version(feature_name, version) return false unless entry feature = Magick.features[feature_name.to_s] || Magick[feature_name] Magick.suppress_change_recording do feature.restore_snapshot!(entry.feature_data) end actor = Magick.current_actor Magick.audit_log&.log(feature_name, 'rollback', user_id: actor, changes: { rolled_back_to: version }) record_change(feature, action: 'rollback', created_by: actor) if defined?(Magick::Rails::Events) && Magick::Rails::Events.rails8? Magick::Rails::Events.rollback(feature_name, version: version) end true end |
#save_version(feature_name, version: nil, created_by: nil) ⇒ Object
Explicit manual snapshot. Kept as public API from earlier releases; since 1.5.0 every Feature mutation also snapshots automatically.
70 71 72 73 |
# File 'lib/magick/versioning.rb', line 70 def save_version(feature_name, version: nil, created_by: nil) feature = Magick.features[feature_name.to_s] || Magick[feature_name] append(feature.name, snapshot_of(feature), version: version, created_by: created_by, action: 'manual') end |