Skip to content

Instantly share code, notes, and snippets.

@railyboy
Created September 12, 2012 11:20
Show Gist options
  • Save railyboy/3706025 to your computer and use it in GitHub Desktop.
Save railyboy/3706025 to your computer and use it in GitHub Desktop.
Loading .yml configuration data with the code from http://speakmy.name/2011/05/29/simple-configuration-for-ruby-apps
module AppData
#require 'deep_symbolizable'
# again - it's a singleton, thus implemented as a self-extended module
extend self
@_settings = {}
attr_reader :_settings
# This is the main point of entry - we call Settings.load! and provide
# a name of the file to read as it's argument. We can also pass in some
# options, but at the moment it's being used to allow per-environment
# overrides in Rails
def load!(filename, options = {})
newsets = YAML::load_file(filename).deep_symbolize
newsets = newsets[options[:env].to_sym] if \
options[:env] && \
newsets[options[:env].to_sym]
deep_merge!(@_settings, newsets)
end
# Deep merging of hashes
# deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
def deep_merge!(target, data)
merger = proc{|key, v1, v2|
Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
target.merge! data, &merger
end
def method_missing(name, *args, &block)
@_settings[name.to_sym] ||
fail(NoMethodError, "unknown configuration root #{name}", caller)
end
end
# Symbolizes all of hash's keys and subkeys.
# Also allows for custom pre-processing of keys (e.g. downcasing, etc)
# if the block is given:
#
# somehash.deep_symbolize { |key| key.downcase }
#
# Usage: either include it into global Hash class to make it available to
# to all hashes, or extend only your own hash objects with this
# module.
# E.g.:
# 1) class Hash; include DeepSymbolizable; end
# 2) myhash.extend DeepSymbolizable
module DeepSymbolizable
class Hash
include DeepSymbolizable
end
def deep_symbolize(&block)
method = self.class.to_s.downcase.to_sym
syms = DeepSymbolizable::Symbolizers
syms.respond_to?(method) ? syms.send(method, self, &block) : self
end
module Symbolizers
extend self
# the primary method - symbolizes keys of the given hash,
# preprocessing them with a block if one was given, and recursively
# going into all nested enumerables
def hash(hash, &block)
hash.inject({}) do |result, (key, value)|
# Recursively deep-symbolize subhashes
value = _recurse_(value, &block)
# Pre-process the key with a block if it was given
key = yield key if block_given?
# Symbolize the key string if it responds to to_sym
sym_key = key.to_sym rescue key
# write it back into the result and return the updated hash
result[sym_key] = value
result
end
end
# walking over arrays and symbolizing all nested elements
def array(ary, &block)
ary.map { |v| _recurse_(v, &block) }
end
# handling recursion - any Enumerable elements (except String)
# is being extended with the module, and then symbolized
def _recurse_(value, &block)
if value.is_a?(Enumerable) && !value.is_a?(String)
# support for a use case without extended core Hash
value.extend DeepSymbolizable unless value.class.include?(DeepSymbolizable)
value = value.deep_symbolize(&block)
end
value
end
end
end
marks-imac:lib railsdev$ pwd
/Users/railsdev/Development/railsprojects/Learning/Marks-Attempts/Test-Area/bootstrap-dropdown-buttons/lib
marks-imac:lib railsdev$ cd generic_search/
marks-imac:generic_search railsdev$ rails c
Loading development environment (Rails 3.2.5)
1.9.3-p125 :001 > AppData.load!("#{Rails.root}/config/initializers/search_configurations.yml")
NameError: uninitialized constant AppData
from (irb):1
from /Users/railsdev/.rvm/gems/ruby-1.9.3-p125@bootstrap-dropdown-buttons/gems/railties-3.2.5/lib/rails/commands/console.rb:47:in `start'
from /Users/railsdev/.rvm/gems/ruby-1.9.3-p125@bootstrap-dropdown-buttons/gems/railties-3.2.5/lib/rails/commands/console.rb:8:in `start'
from /Users/railsdev/.rvm/gems/ruby-1.9.3-p125@bootstrap-dropdown-buttons/gems/railties-3.2.5/lib/rails/commands.rb:41:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
1.9.3-p125 :002 > GenericSearch::AppData.load!("#{Rails.root}/config/initializers/search_configurations.yml")
LoadError: Expected /Users/railsdev/Development/railsprojects/Learning/Marks-Attempts/Test-Area/bootstrap-dropdown-buttons/lib/generic_search/app_data.rb to define GenericSearch::AppData
from /Users/railsdev/.rvm/gems/ruby-1.9.3-p125@bootstrap-dropdown-buttons/gems/activesupport-3.2.5/lib/active_support/dependencies.rb:503:in `load_missing_constant'
from /Users/railsdev/.rvm/gems/ruby-1.9.3-p125@bootstrap-dropdown-buttons/gems/activesupport-3.2.5/lib/active_support/dependencies.rb:192:in `block in const_missing'
from /Users/railsdev/.rvm/gems/ruby-1.9.3-p125@bootstrap-dropdown-buttons/gems/activesupport-3.2.5/lib/active_support/dependencies.rb:190:in `each'
from /Users/railsdev/.rvm/gems/ruby-1.9.3-p125@bootstrap-dropdown-buttons/gems/activesupport-3.2.5/lib/active_support/dependencies.rb:190:in `const_missing'
from (irb):2
from /Users/railsdev/.rvm/gems/ruby-1.9.3-p125@bootstrap-dropdown-buttons/gems/railties-3.2.5/lib/rails/commands/console.rb:47:in `start'
from /Users/railsdev/.rvm/gems/ruby-1.9.3-p125@bootstrap-dropdown-buttons/gems/railties-3.2.5/lib/rails/commands/console.rb:8:in `start'
from /Users/railsdev/.rvm/gems/ruby-1.9.3-p125@bootstrap-dropdown-buttons/gems/railties-3.2.5/lib/rails/commands.rb:41:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
1.9.3-p125 :003 > GenericSearch
=> GenericSearch
1.9.3-p125 :004 > AppData.load!("#{Rails.root}/config/initializers/search_configurations.yml")
NoMethodError: undefined method `deep_symbolize' for #<Hash:0x007fe146e2abd8>
from /Users/railsdev/Development/railsprojects/Learning/Marks-Attempts/Test-Area/bootstrap-dropdown-buttons/lib/generic_search/app_data.rb:16:in `load!'
from (irb):4
from /Users/railsdev/.rvm/gems/ruby-1.9.3-p125@bootstrap-dropdown-buttons/gems/railties-3.2.5/lib/rails/commands/console.rb:47:in `start'
from /Users/railsdev/.rvm/gems/ruby-1.9.3-p125@bootstrap-dropdown-buttons/gems/railties-3.2.5/lib/rails/commands/console.rb:8:in `start'
from /Users/railsdev/.rvm/gems/ruby-1.9.3-p125@bootstrap-dropdown-buttons/gems/railties-3.2.5/lib/rails/commands.rb:41:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
1.9.3-p125 :005 >
# Search Conditions
# manually configured
#
# EDIT with CARE
#
generic_conditions:
- contains
- equals
- 'starts with'
- 'ends with'
- 'from/to'
- 'matches'
models:
diagnosis:
fields:
- code
- description
conditions:
- contains
- equals
- 'starts with'
- 'ends with'
- 'from/to'
- 'matches'
services:
fields:
code:
- contains
- equals
- 'starts with'
- 'ends with'
- matches
description:
- 'valid from'
emails:
admin: someadmin@somewhere.net
support: support@somewhere.net
urls:
search: http://google.com
blog: http://speakmy.name
@railyboy
Copy link
Author

File locations

lib/generic_search/app_data.rb
config/initializers/deep_symbolizable.rb
config/initializers/search_configurations.yml

@railyboy
Copy link
Author

Was this the correct place to put the class override?

module DeepSymbolizable

class Hash
include DeepSymbolizable
end

def deep_symbolize(&block)
method = self.class.to_s.downcase.to_sym
syms = DeepSymbolizable::Symbolizers
syms.respond_to?(method) ? syms.send(method, self, &block) : self
end

@morhekil
Copy link

you can just do the inclusion at the end of deep_symbolizable.rb, like here: https://gist.github.com/3706140

File locations in this case can be like this:

config/initializers/01_deep_symbolizable.rb
config/initializers/99_app_data.rb
config/data/search_configurations.yml

If will auto-load and execute deep_symbolizable.rb first, then app_data.rb - after that you can load your yml files. As app_data.rb is executed at the end - you can even load your data at the end of that file, as Hash should already be set up by that time

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment