Class: Magick::Adapters::ActiveRecord

Inherits:
Base
  • Object
show all
Defined in:
lib/magick/adapters/active_record.rb

Instance Method Summary collapse

Constructor Details

#initialize(model_class: nil) ⇒ ActiveRecord

Returns a new instance of ActiveRecord.



6
7
8
9
10
11
12
13
14
# File 'lib/magick/adapters/active_record.rb', line 6

def initialize(model_class: nil)
  @model_class = model_class || default_model_class
  # Verify table exists - raise clear error if it doesn't
  unless @model_class.table_exists?
    raise AdapterError, "Table 'magick_features' does not exist. Please run: rails generate magick:active_record && rails db:migrate"
  end
rescue StandardError => e
  raise AdapterError, "Failed to initialize ActiveRecord adapter: #{e.message}"
end

Instance Method Details

#all_featuresObject



79
80
81
82
83
# File 'lib/magick/adapters/active_record.rb', line 79

def all_features
  @model_class.pluck(:feature_name).uniq
rescue StandardError => e
  raise AdapterError, "Failed to get all features from ActiveRecord: #{e.message}"
end

#delete(feature_name) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/magick/adapters/active_record.rb', line 55

def delete(feature_name)
  feature_name_str = feature_name.to_s
  retries = 5
  begin
    @model_class.where(feature_name: feature_name_str).destroy_all
  rescue ::ActiveRecord::StatementInvalid, ::ActiveRecord::ConnectionTimeoutError => e
    # SQLite busy/locked errors - retry with exponential backoff
    if (e.message.include?('database is locked') || e.message.include?('busy') || e.message.include?('timeout')) && retries > 0
      retries -= 1
      sleep(0.01 * (6 - retries)) # Exponential backoff: 0.01, 0.02, 0.03, 0.04, 0.05
      retry
    end
    raise AdapterError, "Failed to delete from ActiveRecord: #{e.message}"
  rescue StandardError => e
    raise AdapterError, "Failed to delete from ActiveRecord: #{e.message}"
  end
end

#exists?(feature_name) ⇒ Boolean

Returns:

  • (Boolean)


73
74
75
76
77
# File 'lib/magick/adapters/active_record.rb', line 73

def exists?(feature_name)
  @model_class.exists?(feature_name: feature_name.to_s)
rescue StandardError => e
  raise AdapterError, "Failed to check existence in ActiveRecord: #{e.message}"
end

#get(feature_name, key) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/magick/adapters/active_record.rb', line 16

def get(feature_name, key)
  feature_name_str = feature_name.to_s
  record = @model_class.find_by(feature_name: feature_name_str)
  return nil unless record

  # Handle both Hash (from serialize) and Hash/JSON (from attribute :json)
  data = record.data || {}
  value = data.is_a?(Hash) ? data[key.to_s] : nil
  deserialize_value(value)
rescue StandardError => e
  raise AdapterError, "Failed to get from ActiveRecord: #{e.message}"
end

#set(feature_name, key, value) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/magick/adapters/active_record.rb', line 29

def set(feature_name, key, value)
  feature_name_str = feature_name.to_s
  retries = 5
  begin
    record = @model_class.find_or_initialize_by(feature_name: feature_name_str)
    # Ensure data is a Hash (works for both serialize and attribute :json)
    data = record.data || {}
    data = {} unless data.is_a?(Hash)
    data[key.to_s] = serialize_value(value)
    record.data = data
    # Use Time.now if Time.current is not available (for non-Rails environments)
    record.updated_at = defined?(Time.current) ? Time.current : Time.now
    record.save!
  rescue ::ActiveRecord::StatementInvalid, ::ActiveRecord::ConnectionTimeoutError => e
    # SQLite busy/locked errors - retry with exponential backoff
    if (e.message.include?('database is locked') || e.message.include?('busy') || e.message.include?('timeout')) && retries > 0
      retries -= 1
      sleep(0.01 * (6 - retries)) # Exponential backoff: 0.01, 0.02, 0.03, 0.04, 0.05
      retry
    end
    raise AdapterError, "Failed to set in ActiveRecord: #{e.message}"
  rescue StandardError => e
    raise AdapterError, "Failed to set in ActiveRecord: #{e.message}"
  end
end