Class: Mistri::Workspace::ActiveRecord
- Inherits:
-
Object
- Object
- Mistri::Workspace::ActiveRecord
- Defined in:
- lib/mistri/workspace/active_record.rb
Overview
Documents in the host's own database, through a model class the host supplies, optionally scoped (per session, per tenant). Not auto-required; load it with require "mistri/workspace/active_record".
The model needs a non-null primary key plus non-null path, content, and scope columns, with a unique index whose columns are exactly the scope plus path. Atomic edits use that index plus a short row-locking transaction; no lock_version column is required. A migration to copy:
create_table :mistri_documents do |t|
t.string :session_id, null: false
t.string :path, null: false, limit: 512
t.text :content, size: :medium, null: false
t.
end
add_index :mistri_documents, [:session_id, :path], unique: true
Instance Method Summary collapse
- #atomic_writes? ⇒ Boolean
- #compare_and_write(path, content, expected_revision:) ⇒ Object
- #delete(path) ⇒ Object
-
#initialize(model, scope: {}) ⇒ ActiveRecord
constructor
A new instance of ActiveRecord.
- #list(prefix = nil) ⇒ Object
- #read(path) ⇒ Object
- #snapshot(path) ⇒ Object
- #write(path, content) ⇒ Object
Constructor Details
#initialize(model, scope: {}) ⇒ ActiveRecord
Returns a new instance of ActiveRecord.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/mistri/workspace/active_record.rb', line 27 def initialize(model, scope: {}) raise ArgumentError, "scope must be a Hash" unless scope.is_a?(Hash) names = scope.keys.map(&:to_s) if names.uniq.length != names.length raise ArgumentError, "scope keys must name distinct columns" end reserved = names & %w[path content] unless reserved.empty? raise ArgumentError, "scope cannot contain #{reserved.map(&:inspect).join(" or ")}" end @model = model @scope = own_scope(scope) end |
Instance Method Details
#atomic_writes? ⇒ Boolean
55 56 57 58 59 |
# File 'lib/mistri/workspace/active_record.rb', line 55 def atomic_writes? return @atomic_writes if defined?(@atomic_writes) @atomic_writes = atomic_storage? end |
#compare_and_write(path, content, expected_revision:) ⇒ Object
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/mistri/workspace/active_record.rb', line 67 def compare_and_write(path, content, expected_revision:) verify_atomic_context! key = path.to_s return create_if_absent(key, content) if expected_revision.nil? @model.transaction do record = @model.lock.find_by(**@scope, path: key) actual = record && Snapshot.for(record.content.to_s).revision unless actual == expected_revision raise WorkspaceConflictError.new( key, expected_revision:, actual_revision: actual ) end record.content = content.to_s record.save! committed_snapshot(record, key) end end |
#delete(path) ⇒ Object
87 88 89 90 |
# File 'lib/mistri/workspace/active_record.rb', line 87 def delete(path) @model.where(**@scope, path: path.to_s).delete_all nil end |
#list(prefix = nil) ⇒ Object
92 93 94 95 |
# File 'lib/mistri/workspace/active_record.rb', line 92 def list(prefix = nil) paths = @model.where(**@scope).pluck(:path).sort prefix ? paths.select { |p| p.start_with?(prefix.to_s) } : paths end |
#read(path) ⇒ Object
44 45 46 |
# File 'lib/mistri/workspace/active_record.rb', line 44 def read(path) @model.where(**@scope, path: path.to_s).pick(:content) end |
#snapshot(path) ⇒ Object
61 62 63 64 65 |
# File 'lib/mistri/workspace/active_record.rb', line 61 def snapshot(path) verify_atomic_context! content = relation(path).pick(:content) content.nil? ? nil : Snapshot.for(content.to_s) end |
#write(path, content) ⇒ Object
48 49 50 51 52 53 |
# File 'lib/mistri/workspace/active_record.rb', line 48 def write(path, content) record = @model.find_or_initialize_by(**@scope, path: path.to_s) record.content = content.to_s record.save! nil end |