Class: BetterAuth::Adapters::Memory

Inherits:
Base
  • Object
show all
Includes:
JoinSupport
Defined in:
lib/better_auth/adapters/memory.rb

Constant Summary collapse

LOCK_REGISTRY_GUARD =
Mutex.new
LOCKS_BY_DATABASE =
{}

Constants inherited from Base

Base::TRANSACTION_CONTEXT_KEY

Instance Attribute Summary collapse

Attributes inherited from Base

#options

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#atomic_transactions?

Constructor Details

#initialize(options, db = nil) ⇒ Memory

Returns a new instance of Memory.



25
26
27
28
29
# File 'lib/better_auth/adapters/memory.rb', line 25

def initialize(options, db = nil)
  super(options)
  @db = db || build_db
  @lock = self.class.lock_for(@db)
end

Instance Attribute Details

#dbObject (readonly)

Returns the value of attribute db.



23
24
25
# File 'lib/better_auth/adapters/memory.rb', line 23

def db
  @db
end

Class Method Details

.lock_for(database) ⇒ Object



16
17
18
19
20
# File 'lib/better_auth/adapters/memory.rb', line 16

def lock_for(database)
  LOCK_REGISTRY_GUARD.synchronize do
    LOCKS_BY_DATABASE[database.object_id] ||= Monitor.new
  end
end

Instance Method Details

#consume_one(model:, where:) ⇒ Object



114
115
116
117
118
119
120
# File 'lib/better_auth/adapters/memory.rb', line 114

def consume_one(model:, where:)
  @lock.synchronize do
    table = table_for(model)
    index = table.index { |record| matches_where?(record, where || []) }
    index ? table.delete_at(index) : nil
  end
end

#count(model:, where: nil) ⇒ Object



110
111
112
# File 'lib/better_auth/adapters/memory.rb', line 110

def count(model:, where: nil)
  find_many(model: model, where: where || []).length
end

#create(model:, data:, force_allow_id: false) ⇒ Object



31
32
33
34
35
36
37
# File 'lib/better_auth/adapters/memory.rb', line 31

def create(model:, data:, force_allow_id: false)
  @lock.synchronize do
    model = model.to_s
    table_for(model) << transform_input(model, data, "create", force_allow_id)
    table_for(model).last
  end
end

#create_if_absent(model:, data:, conflict_field: "id", force_allow_id: true) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/better_auth/adapters/memory.rb', line 39

def create_if_absent(model:, data:, conflict_field: "id", force_allow_id: true)
  @lock.synchronize do
    model = model.to_s
    fields = Schema.auth_tables(options).fetch(model).fetch(:fields)
    field = atomic_schema_field(fields, conflict_field)
    input = transform_input(model, data, "create", force_allow_id)
    raise APIError.new("BAD_REQUEST", message: "Missing conflict field #{conflict_field}") unless input.key?(field)
    next false if table_for(model).any? { |record| record[field] == input[field] }

    table_for(model) << input
    true
  end
end

#delete(model:, where:) ⇒ Object



96
97
98
99
# File 'lib/better_auth/adapters/memory.rb', line 96

def delete(model:, where:)
  delete_many(model: model, where: where)
  nil
end

#delete_many(model:, where:) ⇒ Object



101
102
103
104
105
106
107
108
# File 'lib/better_auth/adapters/memory.rb', line 101

def delete_many(model:, where:)
  @lock.synchronize do
    table = table_for(model)
    matches = table.select { |record| matches_where?(record, where || []) }
    @db[model.to_s] = table.reject { |record| matches.include?(record) }
    matches.length
  end
end

#find_many(model:, where: [], sort_by: nil, limit: nil, offset: nil, select: nil, join: nil) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/better_auth/adapters/memory.rb', line 57

def find_many(model:, where: [], sort_by: nil, limit: nil, offset: nil, select: nil, join: nil)
  @lock.synchronize do
    model = model.to_s
    records = table_for(model).select { |record| matches_where?(record, where || []) }.map(&:dup)
    records = records.map { |record| apply_join(model, record, join) } if join
    records = sort_records(model, records, sort_by) if sort_by
    records = records.drop(offset.to_i) if offset
    records = records.first(limit.to_i) if limit
    records = records.map { |record| select_fields(model, record, select, join: join) } if select && !select.empty?
    records
  end
end

#find_one(model:, where: [], select: nil, join: nil) ⇒ Object



53
54
55
# File 'lib/better_auth/adapters/memory.rb', line 53

def find_one(model:, where: [], select: nil, join: nil)
  find_many(model: model, where: where, select: select, join: join, limit: 1).first
end

#increment_one(model:, where:, increment:, set: nil, allow_server_managed: false) ⇒ Object



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/better_auth/adapters/memory.rb', line 122

def increment_one(model:, where:, increment:, set: nil, allow_server_managed: false)
  @lock.synchronize do
    model = model.to_s
    increments = normalized_increments(model, increment, allow_server_managed)
    assignments = normalized_increment_set(model, set)
    raise APIError.new("BAD_REQUEST", message: "increment_one requires a non-empty increment or set") if increments.empty? && assignments.empty?

    target = table_for(model).find { |record| matches_where?(record, where || []) }
    next nil unless target

    increments.each do |field, delta|
      current = target[field]
      target[field] = (current.is_a?(Numeric) ? current : 0) + delta
    end
    target.merge!(assignments)
    target
  end
end

#transactionObject



141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/better_auth/adapters/memory.rb', line 141

def transaction
  return yield active_transaction_adapter if active_transaction_adapter

  @lock.synchronize do
    snapshot = transaction_snapshot(db)
    begin
      with_transaction_context(self) { yield self }
    rescue
      db.replace(snapshot)
      raise
    end
  end
end

#update(model:, where:, update:) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/better_auth/adapters/memory.rb', line 70

def update(model:, where:, update:)
  @lock.synchronize do
    model = model.to_s
    next nil if Array(where).empty?

    ensure_update_input_has_fields!(model, update)
    records = table_for(model).select { |record| matches_where?(record, where) }
    data = transform_input(model, update, "update", true)
    ensure_update_data!(data)
    records.each { |record| record.merge!(data) }
    records.first
  end
end

#update_many(model:, where:, update:) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
# File 'lib/better_auth/adapters/memory.rb', line 84

def update_many(model:, where:, update:)
  @lock.synchronize do
    model = model.to_s
    ensure_update_input_has_fields!(model, update)
    records = table_for(model).select { |record| matches_where?(record, where || []) }
    data = transform_input(model, update, "update", true)
    ensure_update_data!(data)
    records.each { |record| record.merge!(data) }
    records.length
  end
end