Added Mash#load with YAML file support.
This commit is contained in:
parent
bff2a8908c
commit
59069f13ea
|
@ -1,5 +1,6 @@
|
|||
## 3.2.1 (Next)
|
||||
|
||||
* [#183](https://github.com/intridea/hashie/pull/183) Added Mash#load with YAML file support - [@gregory](https://github.com/gregory).
|
||||
* Your contribution here.
|
||||
|
||||
## 3.2.0 (7/10/2014)
|
||||
|
|
43
README.md
43
README.md
|
@ -224,6 +224,49 @@ mash.inspect # => <Hashie::Mash>
|
|||
|
||||
**Note:** The `?` method will return false if a key has been set to false or nil. In order to check if a key has been set at all, use the `mash.key?('some_key')` method instead.
|
||||
|
||||
Mash allows you also to transform any files into a Mash objects.
|
||||
|
||||
### Example:
|
||||
|
||||
```yml
|
||||
#/etc/config/settings/twitter.yml
|
||||
development:
|
||||
api_key: 'api_key'
|
||||
production:
|
||||
api_key: <%= ENV['API_KEY'] %> #let's say that ENV['API_KEY'] is set to 'abcd'
|
||||
```
|
||||
|
||||
```ruby
|
||||
mash = Mash.load('settings/twitter.yml')
|
||||
mash.development.api_key # => 'localhost'
|
||||
mash.development.api_key = "foo" # => <# RuntimeError can't modify frozen ...>
|
||||
mash.development.api_key? # => true
|
||||
```
|
||||
|
||||
You can access a Mash from another class:
|
||||
|
||||
```ruby
|
||||
mash = Mash.load('settings/twitter.yml')[ENV['RACK_ENV']]
|
||||
Twitter.extend mash.to_module # NOTE: if you want another name than settings, call: to_module('my_settings')
|
||||
Twitter.settings.api_key # => 'abcd'
|
||||
```
|
||||
|
||||
You can use another parser (by default: YamlErbParser):
|
||||
|
||||
```
|
||||
#/etc/data/user.csv
|
||||
id | name | lastname
|
||||
---|------------- | -------------
|
||||
1 |John | Doe
|
||||
2 |Laurent | Garnier
|
||||
```
|
||||
|
||||
```ruby
|
||||
mash = Mash.load('data/user.csv', parser: MyCustomCsvParser)
|
||||
# => { 1 => { name: 'John', lastname: 'Doe'}, 2 => { name: 'Laurent', lastname: 'Garnier' } }
|
||||
mash[1] #=> { name: 'John', lastname: 'Doe' }
|
||||
```
|
||||
|
||||
## Dash
|
||||
|
||||
Dash is an extended Hash that has a discrete set of defined properties and only those properties may be set on the hash. Additionally, you can set defaults for each property. You can also flag a property as required. Required properties will raise an exception if unset.
|
||||
|
|
|
@ -24,6 +24,10 @@ module Hashie
|
|||
autoload :PrettyInspect, 'hashie/extensions/pretty_inspect'
|
||||
autoload :KeyConversion, 'hashie/extensions/key_conversion'
|
||||
|
||||
module Parsers
|
||||
autoload :YamlErbParser, 'hashie/extensions/parsers/yaml_erb_parser'
|
||||
end
|
||||
|
||||
module Dash
|
||||
autoload :IndifferentAccess, 'hashie/extensions/dash/indifferent_access'
|
||||
end
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
require 'yaml'
|
||||
require 'erb'
|
||||
module Hashie
|
||||
module Extensions
|
||||
module Parsers
|
||||
class YamlErbParser
|
||||
def initialize(file_path)
|
||||
@content = File.read(file_path)
|
||||
end
|
||||
|
||||
def perform
|
||||
YAML.load ERB.new(@content).result
|
||||
end
|
||||
|
||||
def self.perform(file_path)
|
||||
new(file_path).perform
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -59,6 +59,25 @@ module Hashie
|
|||
|
||||
ALLOWED_SUFFIXES = %w(? ! = _)
|
||||
|
||||
def self.load(path, options = {})
|
||||
@_mashes ||= new do |h, file_path|
|
||||
fail ArgumentError, "The following file doesn't exist: #{file_path}" unless File.file?(file_path)
|
||||
|
||||
parser = options.fetch(:parser) { Hashie::Extensions::Parsers::YamlErbParser }
|
||||
h[file_path] = new(parser.perform(file_path)).freeze
|
||||
end
|
||||
@_mashes[path]
|
||||
end
|
||||
|
||||
def to_module(mash_method_name = :settings)
|
||||
mash = self
|
||||
Module.new do |m|
|
||||
m.send :define_method, mash_method_name.to_sym do
|
||||
mash
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :to_s, :inspect
|
||||
|
||||
# If you pass in an existing hash, it will
|
||||
|
|
|
@ -499,4 +499,96 @@ describe Hashie::Mash do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.load(filename, options = {})' do
|
||||
let(:config) do
|
||||
{
|
||||
'production' => {
|
||||
'foo' => 'production_foo'
|
||||
}
|
||||
}
|
||||
end
|
||||
let(:path) { 'database.yml' }
|
||||
let(:parser) { double(:parser) }
|
||||
|
||||
subject { described_class.load(path, parser: parser) }
|
||||
|
||||
before do |ex|
|
||||
unless ex.metadata == :test_cache
|
||||
described_class.instance_variable_set('@_mashes', nil) # clean the cached mashes
|
||||
end
|
||||
end
|
||||
|
||||
context 'if the file exists' do
|
||||
before do
|
||||
expect(File).to receive(:file?).with(path).and_return(true)
|
||||
expect(parser).to receive(:perform).with(path).and_return(config)
|
||||
end
|
||||
|
||||
it { is_expected.to be_a(Hashie::Mash) }
|
||||
|
||||
it 'return a Mash from a file' do
|
||||
expect(subject.production).not_to be_nil
|
||||
expect(subject.production.keys).to eq config['production'].keys
|
||||
expect(subject.production.foo).to eq config['production']['foo']
|
||||
end
|
||||
|
||||
it 'freeze the attribtues' do
|
||||
expect { subject.production = {} }.to raise_exception(RuntimeError, /can't modify frozen/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'if the fils does not exists' do
|
||||
before do
|
||||
expect(File).to receive(:file?).with(path).and_return(false)
|
||||
end
|
||||
|
||||
it 'raise an ArgumentError' do
|
||||
expect { subject }.to raise_exception(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'results are cached' do
|
||||
let(:parser) { double(:parser) }
|
||||
|
||||
subject { described_class.load(path, parser: parser) }
|
||||
|
||||
before do
|
||||
expect(File).to receive(:file?).with(path).and_return(true)
|
||||
expect(File).to receive(:file?).with("#{path}+1").and_return(true)
|
||||
expect(parser).to receive(:perform).once.with(path).and_return(config)
|
||||
expect(parser).to receive(:perform).once.with("#{path}+1").and_return(config)
|
||||
end
|
||||
|
||||
it 'cache the loaded yml file', :test_cache do
|
||||
2.times do
|
||||
expect(subject).to be_a(described_class)
|
||||
expect(described_class.load("#{path}+1", parser: parser)).to be_a(described_class)
|
||||
end
|
||||
|
||||
expect(subject.object_id).to eq subject.object_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_module(mash_method_name)' do
|
||||
let(:mash) { described_class.new }
|
||||
subject { Class.new.extend mash.to_module }
|
||||
|
||||
it 'defines a settings method on the klass class that extends the module' do
|
||||
expect(subject).to respond_to(:settings)
|
||||
expect(subject.settings).to eq mash
|
||||
end
|
||||
|
||||
context 'when a settings_method_name is set' do
|
||||
let(:mash_method_name) { 'config' }
|
||||
|
||||
subject { Class.new.extend mash.to_module(mash_method_name) }
|
||||
|
||||
it 'defines a settings method on the klass class that extends the module' do
|
||||
expect(subject).to respond_to(mash_method_name.to_sym)
|
||||
expect(subject.send(mash_method_name.to_sym)).to eq mash
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue