Class: Setting

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/setting.rb

Defined Under Namespace

Classes: AlreadyLoaded, FileError, NotFound

Constant Summary collapse

NUM_KLASS =
if RUBY_VERSION.split(/\./)[0].to_i == 2 && RUBY_VERSION.split(/\./)[1].to_i >= 4
  Integer
else
  Fixnum
end

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeSetting

Instance Methods



89
90
91
# File 'lib/setting.rb', line 89

def initialize
  @available_settings ||= Hashie::Mash.new
end

Instance Attribute Details

#available_settingsObject (readonly)

Returns the value of attribute available_settings.



18
19
20
# File 'lib/setting.rb', line 18

def available_settings
  @available_settings
end

Class Method Details

.[](value) ⇒ Object

In [] invocation syntax, we return settings value ‘as is’ without Hash conversions.

For example, if the YML data is: tax:

default: 0.0
california: 7.5

Then calling Setting returns

{ 'default' => "0.0", 'california' => "7.5"}


76
77
78
# File 'lib/setting.rb', line 76

def self.[](value)
  self.instance.value_for(value)
end

.available_settingsObject

DEPRECATED: Please use method accessors instead.



81
82
83
# File 'lib/setting.rb', line 81

def self.available_settings
  self.instance.available_settings
end

.load(args = {}) ⇒ Object

This method can be called only once.

Parameter hash looks like this:

{  :files => [ "file1.yml", "file2.yml", ...],
   :path  => "/var/www/apps/my-app/current/config/settings",
   :local => true }

If :local => true is set, we will load all *.yml files under :path/local directory after all files in :files have been loaded. “Local” settings thus take precedence by design. See README for more details.

Raises:



32
33
34
35
# File 'lib/setting.rb', line 32

def self.load(args = {})
  raise AlreadyLoaded.new('Settings already loaded') if self.instance.loaded?
  self.instance.load(args)
end

.method_missing(method, *args, &block) ⇒ Object

In Method invocation syntax we collapse Hash values and return a single value if ‘default’ is found among keys or Hash has only one key/value pair.

For example, if the YML data is: tax:

default: 0.0
california: 7.5

Then calling Setting.tax returns “0.0”“

This is the preferred method of using settings class.



54
55
56
57
58
# File 'lib/setting.rb', line 54

def self.method_missing(method, *args, &block)
  self.instance.value_for(method, args) do |v, args|
    self.instance.collapse_hashes(v, args)
  end
end

.reload(args = {}) ⇒ Object



37
38
39
# File 'lib/setting.rb', line 37

def self.reload(args = {})
  self.instance.load(args)
end

.respond_to?(method_name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


60
61
62
63
# File 'lib/setting.rb', line 60

def self.respond_to?(method_name, include_private = false)
  self.instance.available_settings.has_key?(method_name.to_s.sub(/\?\z/, '')) ||
    super
end

Instance Method Details

#collapse_hashes(v, args) ⇒ Object

This method performs collapsing of the Hash settings values if the Hash contains ‘default’ value, or just 1 element.



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/setting.rb', line 123

def collapse_hashes(v, args)
  out = if v.is_a?(Hash)
    if args.empty?
      if v.has_key?("default")
        v['default'].nil? ? "" : v['default']
      elsif v.keys.size == 1
        v.values.first
      else
        v
      end
    else
      v[args.shift.to_s]
    end
  else
    v
  end
  if out.is_a?(Hash) && !args.empty?
      collapse_hashes(out, args)
  elsif out.is_a?(Hash) && out.has_key?('default')
    out['default']
  else
    out
  end
end

#has_key?(key) ⇒ Boolean

Returns:

  • (Boolean)


93
94
95
96
# File 'lib/setting.rb', line 93

def has_key?(key)
  @available_settings.has_key?(key) ||
    (key[-1,1] == '?' && @available_settings.has_key?(key.chop))
end

#load(params) ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/setting.rb', line 152

def load(params)
  # reset settings hash
  @available_settings = Hashie::Mash.new
  @loaded = false

  files = []
  path  = params[:path] || Dir.pwd
  params[:files].each do |file|
    files << File.join(path, file)
  end

  if params[:local]
    files << Dir.glob(File.join(path, 'local', '*.yml')).sort
  end

  files.flatten.each do |file|
    begin
      # Ruby versions before 3.0.3 include Psych < 3.3.2, which does not include `unsafe_load`. In those versions,
      # `load` is the behavior we want (in later versions, `load` uses `safe_load`, which doesn't support aliases and
      # requires allowlisting classes used in files.
      if Psych::VERSION < '3.3.2'
        @available_settings.deep_merge!(YAML::load(ERB.new(IO.read(file)).result) || {}) if File.exists?(file)
      else
        @available_settings.deep_merge!(YAML::unsafe_load(ERB.new(IO.read(file)).result) || {}) if File.exists?(file)
      end
    rescue Exception => e
      raise FileError.new("Error parsing file #{file}, with: #{e.message}")
    end
  end

  @loaded = true
  @available_settings
end

#loaded?Boolean

Returns:

  • (Boolean)


148
149
150
# File 'lib/setting.rb', line 148

def loaded?
  @loaded
end

#value_for(key, args = []) ⇒ Object

Raises:



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/setting.rb', line 98

def value_for(key, args = [])
  name = key.to_s
  raise NotFound.new("#{name} was not found") unless has_key?(name)
  bool = false
  if name[-1,1] == '?'
    name.chop!
    bool = true
  end

  v = @available_settings[name]
  if block_given?
    v = yield(v, args)
  end


  if v.is_a?(NUM_KLASS) && bool
    v.to_i > 0
  else
    v
  end
end