Skip to content

Instantly share code, notes, and snippets.

@morhekil
Forked from railyboy/app_data.rb
Created September 12, 2012 11:54
Show Gist options
  • Save morhekil/3706140 to your computer and use it in GitHub Desktop.
Save morhekil/3706140 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
class Hash; include DeepSymbolizable; 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

Thanks

I can see I need to remove the class Hash I had in DeepSymbolizable (which I had put up near the top).

Another simple question. I thought in rails console (rails c) reload! reloaded the environment? I think part of my problem was that I assumed this would pick up changes I had made to the files (after saving), but when I had made all the changes they still didn't work with a simple reload!

I was getting the error:
1.9.3-p125 :026 > reload!
Reloading...
=> true
1.9.3-p125 :027 > AppData.load!("#{Rails.root}/config/initializers/search_configurations.yml")
NameError: uninitialized constant AppData

Have I misunderstood what reload! in Rails console does?

Also, if I want to store this code except the .yml file in lib what do I need to do? I assume its just setup the autoload which I already had?

Really appreciate your help on this

@morhekil
Copy link
Author

reload! does reload the application code indeed, but it does not always pickup changes made to you basic environment settings (such at the ones you do in config/application.rb or config/environments/*), and also I had it not reloading changes in initializers as well. Restarting the console helps in this cases, and sometimes it affects the server running dev environment, too - again, restarting the server helps.

Regarding placing your code in lib - of course, you can do it. You can put the code for the module itself in, say, lib/deep_symbolizable.rb, make sure it is required or auto-loaded, and include the method into Hash class in an initializer - basically just take the last line from deep_symbolizable.rb above and put it into config/initializers/app_data.rb, followed by loading of yml files.

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