Module: Lich::Common::Vars

Defined in:
documented/common/vars.rb,
documented/vars.rb

Overview

Provides persistent variable storage per game/character combination. Variables are automatically loaded from SQLite database on first access and periodically saved every 5 minutes via background thread.

All keys are normalized to strings for consistent access regardless of whether they’re accessed via bracket notation or method syntax.

Examples:

Basic usage

Vars['my_var'] = 'value'
Vars['my_var']  #=> 'value'
Vars[:my_var]   #=> 'value' (symbols are converted to strings)

Using method syntax

Vars.my_var = 'value'
Vars.my_var  #=> 'value'

Using ||= operator

Vars['config'] ||= { setting: 'default' }

Constant Summary collapse

@@vars =
Hash.new
@@md5 =
nil
@@load_state =
LoadState::UNLOADED
@@load =

Proc that loads variables from database on first access

proc {
  Lich.db_mutex.synchronize {
    if @@load_state == LoadState::UNLOADED
      @@load_state = LoadState::LOADING
      begin
        h = Lich.db.get_first_value(
          'SELECT hash FROM uservars WHERE scope=?;',
          ["#{XMLData.game}:#{XMLData.name}".encode('UTF-8')]
        )
      rescue SQLite3::BusyException
        sleep 0.1
        retry
      end
       if h
        begin
          hash = Marshal.load(h)
          # Normalize all keys to strings during load
          hash.each { |k, v| @@vars[normalize_key(k)] = v }
          @@md5 = Digest::MD5.hexdigest(hash.to_s)
        rescue StandardError => e
          respond "--- Lich: error: #{e}"
          respond e.backtrace[0..2]
        end
      end
      @@load_state = LoadState::LOADED
    end
  }
  nil
}
@@save =

Proc that saves variables to database if modified

proc {
  Lich.db_mutex.synchronize {
    if @@load_state == LoadState::LOADED
      current_md5 = Digest::MD5.hexdigest(@@vars.to_s)
      if current_md5 != @@md5
        @@md5 = current_md5
        blob = SQLite3::Blob.new(Marshal.dump(@@vars))
        begin
          Lich.db.execute(
            'INSERT OR REPLACE INTO uservars(scope,hash) VALUES(?,?);',
            ["#{XMLData.game}:#{XMLData.name}".encode('UTF-8'), blob]
          )
        rescue SQLite3::BusyException
          sleep 0.1
          retry
        end
      end
    end
  }
  nil
}

Class Method Summary collapse

Class Method Details

.[](name) ⇒ Object?

Retrieves a variable value by name

Keys are normalized to strings, so symbols and strings are equivalent.

Examples:

Vars['my_setting']  #=> "some value"
Vars[:my_setting]   #=> "some value" (same result)

Parameters:

  • name (String, Symbol)

    the variable name

Returns:

  • (Object, nil)

    the variable value, or nil if not set



92
93
94
95
# File 'documented/vars.rb', line 92

def Vars.[](name)
  @@load.call unless @@load_state == LoadState::LOADED
  @@vars[normalize_key(name)]
end

.[]=(name, val) ⇒ Object?

Sets a variable value by name

Keys are normalized to strings, so symbols and strings are equivalent.

Examples:

Vars['my_setting'] = 'new value'
Vars[:my_setting] = 'new value'  # equivalent
Vars['my_setting'] = nil  # deletes the variable

Parameters:

  • name (String, Symbol)

    the variable name

  • val (Object, nil)

    the value to set; nil deletes the variable

Returns:

  • (Object, nil)

    the value that was set



103
104
105
106
107
108
109
110
111
# File 'documented/vars.rb', line 103

def Vars.[]=(name, val)
  @@load.call unless @@load_state == LoadState::LOADED
  key = normalize_key(name)
  if val.nil?
    @@vars.delete(key)
  else
    @@vars[key] = val
  end
end

.listHash

Returns a duplicate of all variables as a Hash

Examples:

all_vars = Vars.list
all_vars.keys  #=> ['var1', 'var2', ...]

Returns:

  • (Hash)

    a copy of all stored variables with string keys



117
118
119
120
# File 'documented/vars.rb', line 117

def Vars.list
  @@load.call unless @@load_state == LoadState::LOADED
  @@vars.dup
end

.method_missing(method_name, *args) ⇒ Object?

Handles dynamic method calls for variable access

Supports both getter and setter syntax. All keys are normalized to strings.

  • ‘Vars.my_var` retrieves variable named “my_var”

  • ‘Vars.my_var = value` sets variable named “my_var”

  • Vars` and `Vars = value` also work through this

Examples:

Vars.my_setting = 'value'
Vars.my_setting  #=> 'value'

Parameters:

  • method_name (Symbol)

    the method name being called

  • args (Array)

    arguments passed to the method

Returns:

  • (Object, nil)

    the variable value or result of setter



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'documented/common/vars.rb', line 185

def Vars.method_missing(method_name, *args)
  @@load.call unless @@load_state == LoadState::LOADED

  # Handle []= called through method_missing
  if method_name == :[]= && args.length == 2
    key = normalize_key(args[0])
    if args[1].nil?
      @@vars.delete(key)
    else
      @@vars[key] = args[1]
    end
  # Handle [] called through method_missing
  elsif method_name == :[] && args.length == 1
    @@vars[normalize_key(args[0])]
  # Handle setter methods (e.g., foo=)
  elsif method_name.to_s.end_with?('=')
    key = normalize_key(method_name.to_s.chop)
    if args[0].nil?
      @@vars.delete(key)
    else
      @@vars[key] = args[0]
    end
  # Handle getter methods
  else
    @@vars[normalize_key(method_name.to_s)]
  end
end

.normalize_key(key) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Normalizes a key to a string for consistent storage and retrieval

Parameters:

  • key (String, Symbol, Object)

    the key to normalize

Returns:

  • (String)

    the normalized string key



39
40
41
# File 'documented/common/vars.rb', line 39

def self.normalize_key(key)
  key.to_s
end

.respond_to_missing?(method_name, _include_private = false) ⇒ Boolean

Declares that method_missing can respond to valid Ruby method names

Only returns true for method names that could be valid Ruby identifiers or the bracket operators. This helps catch obvious typos while still allowing dynamic variable access.

Parameters:

  • method_name (Symbol)

    the method name to check

  • _include_private (Boolean) (defaults to: false)

    whether to include private methods

Returns:

  • (Boolean)

    true if the method name is a valid variable name



164
165
166
167
168
169
170
171
172
173
174
# File 'documented/vars.rb', line 164

def Vars.respond_to_missing?(method_name, _include_private = false)
  method_str = method_name.to_s

  # Allow bracket operators
  return true if method_name == :[] || method_name == :[]=

  # Allow valid Ruby method names (with or without trailing =)
  # Valid: starts with letter or underscore, contains letters/digits/underscores
  # and optionally ends with = for setters
  method_str.match?(/\A[a-zA-Z_][a-zA-Z0-9_]*=?\z/)
end

.savenil

Immediately saves all variables to the database

Examples:

Vars['important'] = 'data'
Vars.save  # Force immediate save instead of waiting for auto-save

Returns:

  • (nil)


126
127
128
# File 'documented/vars.rb', line 126

def Vars.save
  @@save.call
end