Class: Settings::StaticStore

Inherits:
ValueStore show all
Defined in:
lib/iron/settings/static_store.rb

Overview

Provides in-memory static (aka file and code-based) settings, suitable for class-level settings for gems, frameworks, command-line tools, etc.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from ValueStore

#get_value, #has_value?, #reload_if_needed, #save, #set_value

Constructor Details

#initialize(root, options = {}) ⇒ StaticStore

Returns a new instance of StaticStore.



9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/iron/settings/static_store.rb', line 9

def initialize(root, options = {})
  file = options.delete(:file)
  files = options.delete(:files)
  @secure = !(options.delete(:secure) === false)
  @ignore_missing = !(options.delete(:ignore_missing) === false)
  super(root, options)
  
  @paths = []
  @paths << file if file
  @paths += files || []
  
  @modified_time = @paths.convert_to_hash(nil)
end

Instance Attribute Details

#pathsObject

Returns the value of attribute paths.



7
8
9
# File 'lib/iron/settings/static_store.rb', line 7

def paths
  @paths
end

Instance Method Details

#loadObject

Load our values from the file(s) specified during creation, in order, respecting the :secure option to only load safe settings files if so specified.



40
41
42
43
# File 'lib/iron/settings/static_store.rb', line 40

def load
  super
  @paths.each {|p| load_file(p) }
end

#load_file(path) ⇒ Object

Loads a single settings file, verifying its existence, ownership/security, etc.

Raises:

  • (RuntimeError)


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/iron/settings/static_store.rb', line 46

def load_file(path)
  # Ensure we have the file, if so required
  raise RuntimeError.new("Missing settings file #{path} - this file is required") unless @ignore_missing || File.exists?(path)

  # Read in the settings file
  verify_file_security(path)
  text = File.read(path) rescue ''
  return if text.blank?
  
  # Create a cursor, and eval the file's contents in the cursor's context
  cursor = Settings::Cursor.new(@root, self)
  cursor.eval_in_context(text)

  # Remember our modified time for future checking
  @modified_time[path] = File.mtime(path)
end

#need_reload?Boolean

True on our reload settings matching (see ValueStore#need_reload?), or if any of our settings files have been modified since our last load.

Returns:

  • (Boolean)


25
26
27
28
29
30
# File 'lib/iron/settings/static_store.rb', line 25

def need_reload?
  return true if super
  @modified_time.any? do |path, time| 
    File.exist?(path) && File.mtime(path) != time 
  end
end

#read_only?Boolean

We don’t support saving our current state, hence “static” :-)

Returns:

  • (Boolean)


33
34
35
# File 'lib/iron/settings/static_store.rb', line 33

def read_only?
  true
end

#save_file(path) ⇒ Object



63
64
65
66
# File 'lib/iron/settings/static_store.rb', line 63

def save_file(path)
  @modified_time[path] = File.mtime(path)
  raise 'Unimplemented!'
end

#verify_file_security(path) ⇒ Object

Implements :secure test for settings files. Verifies that the specified file is:

  • Owned by the same user ID that is running the current process

  • Not world-writable

Raises:

  • (RuntimeError)


73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/iron/settings/static_store.rb', line 73

def verify_file_security(path)
  # Not requiring security?  File doesn't exist?  Then everything is fine...
  return unless (File.exists?(path) && @secure)
  
  # Root can read all files, useful for backups and of course no security lost
  return if Process.uid == 0
  
  # Check file ownership
  stat = File::Stat.new(path)
  raise RuntimeError.new("Cannot load settings file #{path} - file must be owned by the user this program is running as (UID #{Process.uid})") unless stat.owned?
  raise RuntimeError.new("Cannot load settings file #{path} - file cannot be world-writable") if stat.world_writable?
end