yamload 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cdaf18e91f87fa915da696af9e2d41d171313512
4
- data.tar.gz: 93ed1e30105693344286755d3bd04db2f22d9032
3
+ metadata.gz: 831e871ab4c0d0dbe55c9465bad82ba229730d87
4
+ data.tar.gz: 52dbd15b58e1adc00928261b9490da7761bbc607
5
5
  SHA512:
6
- metadata.gz: 8be6ad15459108a185e1d68c256e8174a7f7f2e92579c057184fcd28d94dbf9cdd4ec04674cb15e8e9fc0051c60d0d748e3ab67eafe52ed0d58ea07a494b4310
7
- data.tar.gz: 2d2ce03a03a7818d5195e5d1efe8eaf0c1c1439ab6bbd41dff97587cc178758a31ee4c1f3b5a125fad41df3706ddbd2453be8c6b5ac84f933d00a2104737d147
6
+ metadata.gz: 36e4c622fabf2326406df940a8caa541268372cbedc3f1b829b3fa3fdbf9b5766d1d8322817cab0fce255c936b3e75df49ed09fd8312d720d1a35229849c47a9
7
+ data.tar.gz: ca1f592c5e75d2f1a34aa12385a9b8b16dd333bbc6d1f99d550ddf9a8569ae2475a3000eac9301ccc1354ad2c10ed6b37492b2031a32dea9c4963fc02e722ac4
data/CHANGELOG.md ADDED
@@ -0,0 +1,48 @@
1
+ ## 0.1.0 (2015-02-17)
2
+
3
+ - use proper semantic versioning (semver.org)
4
+
5
+ Features:
6
+
7
+ - support loading valid yml files which don't define a hash
8
+ - deprecates `Yamload::Loader#loaded_hash` in favour of `Yamload::Loader#content`
9
+
10
+ ## 0.0.6 (2015-02-04)
11
+
12
+ Bugfixes:
13
+
14
+ - check for directory existence
15
+
16
+ ## 0.0.5 (2015-02-04)
17
+
18
+ Bugfixes:
19
+
20
+ - check for file existence
21
+ - check for validity of file contents
22
+
23
+ ## 0.0.4 (2015-02-04)
24
+
25
+ Features:
26
+
27
+ - implement `Yamload::Loader#exist?` to check for file presence
28
+
29
+ ## 0.0.3 (2015-02-04)
30
+
31
+ Features:
32
+
33
+ - use a faster schema validation engine with no dependencies
34
+
35
+ ## 0.0.2 (2015-02-04)
36
+
37
+ Features:
38
+
39
+ - freeze the loaded hash
40
+
41
+ ## 0.0.1 (2015-02-04)
42
+
43
+ Features:
44
+
45
+ - load valid yml files defining a hash
46
+ - conversion of loaded hash to immutable object
47
+ - default values
48
+ - schema validation
data/README.md CHANGED
@@ -44,7 +44,7 @@ Load YAML files from the directory and access keys
44
44
  ```ruby
45
45
  # Load config/test.yml
46
46
  loader = Yamload::Loader.new(:test)
47
- loader.loaded_hash('attribute')
47
+ loader.content('attribute')
48
48
  # => true
49
49
  loader.obj.attribute
50
50
  # => true
@@ -0,0 +1,20 @@
1
+ module Yamload
2
+ module Conversion
3
+ class Array
4
+ def initialize(array)
5
+ fail ArgumentError, "#{array} is not an Array" unless array.is_a?(::Array)
6
+ @array = array
7
+ end
8
+
9
+ def to_immutable
10
+ convert_elements.freeze
11
+ end
12
+
13
+ private
14
+
15
+ def convert_elements
16
+ @array.map { |element| Object.new(element).to_immutable }
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ require 'facets/hash/rekey'
2
+ require 'anima'
3
+
4
+ module Yamload
5
+ module Conversion
6
+ class Hash
7
+ def initialize(hash)
8
+ fail ArgumentError, "#{array} is not a Hash" unless hash.is_a?(::Hash)
9
+ @hash = hash.rekey
10
+ end
11
+
12
+ def to_immutable
13
+ immutable_objects_factory.new(converted_hash)
14
+ end
15
+
16
+ private
17
+
18
+ def immutable_objects_factory
19
+ anima = Anima.new(*@hash.keys)
20
+ Class.new do
21
+ include Adamantium
22
+ include anima
23
+ end
24
+ end
25
+
26
+ def converted_hash
27
+ @hash.map.with_object({}) { |(key, value), hash|
28
+ hash[key] = Object.new(value).to_immutable
29
+ }
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,29 @@
1
+ require 'facets/object/dup'
2
+ require 'ice_nine'
3
+
4
+ module Yamload
5
+ module Conversion
6
+ class Object
7
+ def initialize(object)
8
+ @object = object.clone? ? object.clone : object
9
+ end
10
+
11
+ def to_immutable
12
+ convert
13
+ end
14
+
15
+ private
16
+
17
+ def convert
18
+ case @object
19
+ when ::Array
20
+ Array.new(@object).to_immutable
21
+ when ::Hash
22
+ Hash.new(@object).to_immutable
23
+ else
24
+ IceNine.deep_freeze!(@object)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,8 @@
1
+ module Yamload
2
+ module Conversion
3
+ end
4
+ end
5
+
6
+ require 'yamload/conversion/array'
7
+ require 'yamload/conversion/hash'
8
+ require 'yamload/conversion/object'
@@ -0,0 +1,26 @@
1
+ require 'facets/hash/deep_merge'
2
+
3
+ module Yamload
4
+ module Defaults
5
+ class Hash
6
+ attr_reader :defaults
7
+
8
+ def initialize(defaults = nil)
9
+ self.defaults = defaults
10
+ end
11
+
12
+ def defaults=(defaults)
13
+ unless defaults.is_a?(::Hash) || defaults.nil?
14
+ fail ArgumentError, "#{defaults} is not a hash"
15
+ end
16
+ @defaults = defaults
17
+ end
18
+
19
+ def merge(hash)
20
+ return hash if @defaults.nil?
21
+ fail ArgumentError, "#{hash} is not a hash" unless hash.is_a?(::Hash)
22
+ @defaults.deep_merge(hash)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,6 @@
1
+ module Yamload
2
+ module Defaults
3
+ end
4
+ end
5
+
6
+ require 'yamload/defaults/hash'
@@ -1,76 +1,84 @@
1
1
  require 'yaml'
2
- require 'classy_hash'
3
- require 'facets/hash/deep_merge'
2
+ require 'ice_nine'
3
+ require 'yamload/loading'
4
+ require 'yamload/conversion'
5
+ require 'yamload/defaults'
6
+ require 'yamload/validation'
4
7
 
5
8
  module Yamload
6
9
  class Loader
7
10
  def initialize(file, dir = Yamload.dir)
8
- @file = file
9
- @dir = dir
11
+ @loader = Loading::Yaml.new(file, dir)
10
12
  end
11
13
 
12
14
  def exist?
13
- File.exist?(filepath)
15
+ @loader.exist?
14
16
  end
15
17
 
18
+ # <b>DEPRECATED:</b> Please use <tt>content</tt> instead.
16
19
  def loaded_hash
17
- @loaded_hash ||= IceNine.deep_freeze(defaults.deep_merge(load))
20
+ warn '[DEPRECATION] `loaded_hash` is deprecated. Please use `content` instead.'
21
+ content
22
+ end
23
+
24
+ def content
25
+ @content ||= IceNine.deep_freeze(content_with_defaults)
18
26
  end
19
27
 
20
28
  def obj
21
- @immutable_obj ||= HashToImmutableObject.new(loaded_hash).call
29
+ @immutable_obj ||= Conversion::Object.new(content).to_immutable
22
30
  end
23
31
 
24
32
  def reload
25
- @loaded_hash = @immutable_obj = nil
26
- loaded_hash
33
+ @content = @immutable_obj = nil
34
+ @loader.reload
35
+ content
27
36
  end
28
37
 
29
- attr_writer :schema
38
+ def defaults=(defaults)
39
+ defaults_merger.defaults = defaults
40
+ end
30
41
 
31
- def schema
32
- @schema ||= {}
42
+ def defaults
43
+ defaults_merger.defaults
33
44
  end
34
45
 
35
- attr_writer :defaults
46
+ def schema=(schema)
47
+ validator.schema = schema
48
+ end
36
49
 
37
- def defaults
38
- @defaults ||= {}
50
+ def schema
51
+ validator.schema
39
52
  end
40
53
 
41
54
  def valid?
42
- validate!
43
- true
44
- rescue SchemaError
45
- false
55
+ validation_result.valid?
46
56
  end
47
57
 
48
58
  def validate!
49
- @error = nil
50
- ClassyHash.validate(loaded_hash, schema)
51
- rescue RuntimeError => e
52
- @error = e.message
53
- raise SchemaError, @error
59
+ fail SchemaError, validation_result.error unless validation_result.valid?
54
60
  end
55
61
 
56
62
  def error
57
- return nil if valid?
58
- @error
63
+ validation_result.error
59
64
  end
60
65
 
61
66
  private
62
67
 
63
- def load
64
- fail IOError, "#{@file}.yml could not be found" unless exist?
65
- YAML.load_file(filepath).tap do |hash|
66
- fail IOError, "#{@file}.yml is invalid" unless hash.is_a? Hash
67
- end
68
+ def content_with_defaults
69
+ defaults_merger.merge(@loader.content)
70
+ end
71
+
72
+ def defaults_merger
73
+ @defaults_merger ||= Defaults::Hash.new
74
+ end
75
+
76
+ def validator
77
+ @validator ||= Validation::Hash.new
68
78
  end
69
79
 
70
- def filepath
71
- fail IOError, 'No yml files directory specified' if @dir.nil?
72
- fail IOError, "#{@dir} is not a valid directory" unless File.directory?(@dir)
73
- File.join(@dir, "#{@file}.yml")
80
+ def validation_result
81
+ validator.validate(content)
74
82
  end
75
83
  end
76
84
 
@@ -0,0 +1,41 @@
1
+ require 'facets/kernel'
2
+ require 'ice_nine'
3
+
4
+ module Yamload
5
+ module Loading
6
+ class Yaml
7
+ def initialize(file, dir)
8
+ @file = file
9
+ @dir = dir
10
+ end
11
+
12
+ def exist?
13
+ File.exist?(filepath)
14
+ end
15
+
16
+ def content
17
+ @content ||= IceNine.deep_freeze(load)
18
+ end
19
+
20
+ def reload
21
+ @content = @immutable_obj = nil
22
+ content
23
+ end
24
+
25
+ private
26
+
27
+ def load
28
+ fail IOError, "#{@file}.yml could not be found" unless exist?
29
+ YAML.load_file(filepath).tap do |content|
30
+ fail IOError, "#{@file}.yml is blank" if content.blank?
31
+ end
32
+ end
33
+
34
+ def filepath
35
+ fail IOError, 'No yml files directory specified' if @dir.nil?
36
+ fail IOError, "#{@dir} is not a valid directory" unless File.directory?(@dir)
37
+ File.join(@dir, "#{@file}.yml")
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,6 @@
1
+ module Yamload
2
+ module Loading
3
+ end
4
+ end
5
+
6
+ require 'yamload/loading/yaml'
@@ -0,0 +1,28 @@
1
+ require 'classy_hash'
2
+
3
+ module Yamload
4
+ module Validation
5
+ class Hash
6
+ attr_reader :schema
7
+
8
+ def initialize(schema = nil)
9
+ self.schema = schema
10
+ end
11
+
12
+ def schema=(schema)
13
+ unless schema.is_a?(::Hash) || schema.nil?
14
+ fail ArgumentError, "#{schema} is not a hash"
15
+ end
16
+ @schema = schema
17
+ end
18
+
19
+ def validate(hash)
20
+ fail ArgumentError, "#{hash} is not a hash" unless hash.is_a?(::Hash)
21
+ ClassyHash.validate(hash, @schema) unless @schema.nil?
22
+ Result.new(true)
23
+ rescue RuntimeError => e
24
+ Result.new(false, e.message)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ module Yamload
2
+ module Validation
3
+ class Result
4
+ attr_reader :error
5
+
6
+ def initialize(valid, error = nil)
7
+ @valid = valid
8
+ @error = error
9
+ end
10
+
11
+ def valid?
12
+ @valid
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ module Yamload
2
+ module Validation
3
+ end
4
+ end
5
+
6
+ require 'yamload/validation/hash'
7
+ require 'yamload/validation/result'
@@ -1,3 +1,3 @@
1
1
  module Yamload
2
- VERSION = '0.0.6'
2
+ VERSION = '0.1.0'
3
3
  end
data/lib/yamload.rb CHANGED
@@ -7,4 +7,3 @@ module Yamload
7
7
  end
8
8
 
9
9
  require 'yamload/loader'
10
- require 'yamload/hash_to_immutable_object'
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ require 'yamload/conversion'
4
+
5
+ describe Yamload::Conversion do
6
+ let(:number) { 42 }
7
+ let(:string) { 'a string' }
8
+ let(:array) { [number, string] }
9
+ let(:hash) {
10
+ {
11
+ string: string,
12
+ array: array,
13
+ sub_hash: { something: 'else' }
14
+ }
15
+ }
16
+
17
+ subject!(:immutable_object) { converter.to_immutable }
18
+
19
+ context 'when converting a number' do
20
+ let(:converter) { Yamload::Conversion::Object.new(number) }
21
+ specify { is_expected.to eq number }
22
+ end
23
+
24
+ context 'when converting a string' do
25
+ let(:converter) { Yamload::Conversion::Object.new(string) }
26
+ specify { expect(string).not_to be_frozen }
27
+ specify { is_expected.to be_frozen }
28
+ specify { is_expected.to eq string }
29
+ end
30
+
31
+ context 'when converting an array' do
32
+ let(:converter) { Yamload::Conversion::Object.new(array) }
33
+ specify { expect(array).not_to be_frozen }
34
+ specify { is_expected.to be_frozen }
35
+ specify { is_expected.to be_an Array }
36
+ specify { expect(immutable_object.size).to eq 2 }
37
+ specify { expect(immutable_object[0]).to eq number }
38
+ specify { expect(immutable_object[1]).to be_frozen }
39
+ specify { expect(immutable_object[1]).to eq string }
40
+ end
41
+
42
+ context 'when converting a hash' do
43
+ let(:converter) { Yamload::Conversion::Object.new(hash) }
44
+ specify { expect(hash).not_to be_frozen }
45
+ specify { is_expected.to be_frozen }
46
+ specify { expect(immutable_object.string).to eq string }
47
+ specify { expect(immutable_object.array.size).to eq 2 }
48
+ specify { expect(immutable_object.array[0]).to eq number }
49
+ specify { expect(immutable_object.array[1]).to be_frozen }
50
+ specify { expect(immutable_object.array[1]).to eq string }
51
+ specify { expect(immutable_object.sub_hash).to be_frozen }
52
+ specify { expect(immutable_object.sub_hash.something).to be_frozen }
53
+ specify { expect(immutable_object.sub_hash.something).to eq 'else' }
54
+ end
55
+ end
@@ -0,0 +1,4 @@
1
+ ---
2
+ - first
3
+ - second
4
+ - third
@@ -1 +0,0 @@
1
-
@@ -0,0 +1,7 @@
1
+ ---
2
+ 1
3
+ first
4
+ 2
5
+ second
6
+ 3
7
+ third
@@ -22,4 +22,4 @@ users:
22
22
  country: Specland
23
23
  email: speccy.speccer@spec.com
24
24
  settings:
25
- remote_access: true
25
+ remote_access: true
data/spec/loader_spec.rb CHANGED
@@ -2,201 +2,278 @@ require 'spec_helper'
2
2
 
3
3
  require 'yamload'
4
4
 
5
- describe Yamload do
5
+ describe Yamload::Loader do
6
6
  let(:file) { :test }
7
7
  let(:loader) { Yamload::Loader.new(file) }
8
-
9
- specify { expect(loader).to exist }
8
+ let(:content) { loader.content }
10
9
 
11
10
  context 'if the directory is not specified' do
12
11
  let(:loader) { Yamload::Loader.new(file, nil) }
13
- specify { expect { config }.to raise_error IOError, 'No yml files directory specified' }
12
+ specify { expect { content }.to raise_error IOError, 'No yml files directory specified' }
14
13
  end
15
14
 
16
15
  context 'if the directory is invalid' do
17
16
  let(:current_file_dir) { File.expand_path(File.dirname(__FILE__)) }
18
17
  let(:invalid_dir) { File.join(current_file_dir, 'invalid') }
19
18
  let(:loader) { Yamload::Loader.new(file, invalid_dir) }
20
- specify { expect { config }.to raise_error IOError, "#{invalid_dir} is not a valid directory" }
19
+ specify { expect { content }.to raise_error IOError, "#{invalid_dir} is not a valid directory" }
21
20
  end
22
21
 
23
22
  context 'with a non existing file' do
24
23
  let(:file) { :non_existing }
25
24
  specify { expect(loader).not_to exist }
26
- specify { expect { config }.to raise_error IOError, 'non_existing.yml could not be found' }
25
+ specify { expect { content }.to raise_error IOError, 'non_existing.yml could not be found' }
27
26
  end
28
27
 
29
- let(:config) { loader.loaded_hash }
30
-
31
28
  context 'with an empty file' do
32
29
  let(:file) { :empty }
33
30
  specify { expect(loader).to exist }
34
- specify { expect { config }.to raise_error IOError, 'empty.yml is invalid' }
31
+ specify { expect { content }.to raise_error IOError, 'empty.yml is blank' }
35
32
  end
36
33
 
37
- let(:expected_config) {
38
- {
39
- 'test' => true,
40
- 'users' => [
41
- {
42
- 'first_name' => 'Testy',
43
- 'last_name' => 'Tester',
44
- 'address' => {
45
- 'address_line_1' => '1 Test Avenue',
46
- 'address_line_2' => nil,
47
- 'city' => 'Testville',
48
- 'state' => 'TST',
49
- 'post_code' => 1234,
50
- 'country' => 'Testalia'
51
- },
52
- 'email' => 'testy.tester@test.com'
53
- },
54
- {
55
- 'first_name' => 'Speccy',
56
- 'last_name' => 'Speccer',
57
- 'address' => {
58
- 'address_line_1' => 'Unit 1',
59
- 'address_line_2' => '42 Spec Street',
60
- 'city' => 'Specwood',
61
- 'state' => 'SPC',
62
- 'post_code' => 5678,
63
- 'country' => 'Specland'
64
- },
65
- 'email' => 'speccy.speccer@spec.com'
66
- }
67
- ],
68
- 'settings' => {
69
- 'remote_access' => true
34
+ context 'with a file defining an array' do
35
+ let(:file) { :array }
36
+ let(:expected_content) { %w(first second third) }
37
+ specify { expect(loader).to exist }
38
+ specify { expect { content }.not_to raise_error }
39
+ specify { expect(content).to eq expected_content }
40
+
41
+ context 'when defaults are defined' do
42
+ let(:defaults) { { test: true } }
43
+ before { loader.defaults = defaults }
44
+ specify {
45
+ expect { content }
46
+ .to raise_error ArgumentError, "#{expected_content} is not a hash"
70
47
  }
71
- }
72
- }
73
-
74
- specify { expect(config).to eq expected_config }
75
-
76
- let(:config_obj) { loader.obj }
77
-
78
- specify { expect(config_obj.test).to eq true }
79
- specify { expect(config_obj.users[0].first_name).to eq 'Testy' }
80
- specify { expect(config_obj.users[0].last_name).to eq 'Tester' }
81
- specify { expect(config_obj.users[0].address.address_line_1).to eq '1 Test Avenue' }
82
- specify { expect(config_obj.users[0].address.address_line_2).to eq nil }
83
- specify { expect(config_obj.users[0].address.city).to eq 'Testville' }
84
- specify { expect(config_obj.users[0].address.state).to eq 'TST' }
85
- specify { expect(config_obj.users[0].address.post_code).to eq 1234 }
86
- specify { expect(config_obj.users[0].address.country).to eq 'Testalia' }
87
- specify { expect(config_obj.users[0].email).to eq 'testy.tester@test.com' }
88
- specify { expect(config_obj.users[1].first_name).to eq 'Speccy' }
89
- specify { expect(config_obj.users[1].last_name).to eq 'Speccer' }
90
- specify { expect(config_obj.users[1].address.address_line_1).to eq 'Unit 1' }
91
- specify { expect(config_obj.users[1].address.address_line_2).to eq '42 Spec Street' }
92
- specify { expect(config_obj.users[1].address.city).to eq 'Specwood' }
93
- specify { expect(config_obj.users[1].address.state).to eq 'SPC' }
94
- specify { expect(config_obj.users[1].address.post_code).to eq 5678 }
95
- specify { expect(config_obj.users[1].address.country).to eq 'Specland' }
96
- specify { expect(config_obj.users[1].email).to eq 'speccy.speccer@spec.com' }
97
- specify { expect(config_obj.settings.remote_access).to eq true }
98
-
99
- context 'when trying to modify the loaded hash' do
100
- let(:new_user) { double('new user') }
101
- specify 'the hash should be immutable' do
102
- expect { config['users'] << new_user }
103
- .to raise_error RuntimeError, "can't modify frozen Array"
104
- expect(config['users']).not_to include new_user
105
48
  end
106
- end
107
49
 
108
- context 'when trying to modify the configuration object' do
109
- let(:new_user) { double('new user') }
110
- specify 'the object should be immutable' do
111
- expect { config_obj.users << new_user }
112
- .to raise_error RuntimeError, "can't modify frozen Array"
113
- expect(config_obj.users).not_to include new_user
50
+ context 'when a schema is defined' do
51
+ let(:schema) { { test: true } }
52
+ before { loader.schema = schema }
53
+ specify {
54
+ expect { loader.valid? }
55
+ .to raise_error ArgumentError, "#{expected_content} is not a hash"
56
+ }
114
57
  end
115
58
  end
116
59
 
117
- context 'when no schema is defined' do
118
- specify { expect(loader).to be_valid }
119
- specify { expect(loader.error).to be_nil }
120
- specify { expect { loader.validate! }.not_to raise_error }
60
+ context 'with a file defining a string' do
61
+ let(:file) { :string }
62
+ let(:expected_content) { '1 first 2 second 3 third' }
63
+ specify { expect(loader).to exist }
64
+ specify { expect { content }.not_to raise_error }
65
+ specify { expect(content).to eq expected_content }
66
+
67
+ context 'when defaults are defined' do
68
+ let(:defaults) { { test: true } }
69
+ before { loader.defaults = defaults }
70
+ specify {
71
+ expect { content }
72
+ .to raise_error ArgumentError, "#{expected_content} is not a hash"
73
+ }
74
+ end
75
+
76
+ context 'when a schema is defined' do
77
+ let(:schema) { { test: true } }
78
+ before { loader.schema = schema }
79
+ specify {
80
+ expect { loader.valid? }
81
+ .to raise_error ArgumentError, "#{expected_content} is not a hash"
82
+ }
83
+ end
121
84
  end
122
85
 
123
- context 'when a schema is defined' do
124
- let(:schema) {
86
+ context 'with a file defining a hash' do
87
+ specify { expect(loader).to exist }
88
+
89
+ let(:expected_content) {
125
90
  {
126
- 'test' => TrueClass,
127
- 'users' => [
128
- [
129
- {
130
- 'first_name' => String,
131
- 'last_name' => String,
132
- 'address' => {
133
- 'address_line_1' => String,
134
- 'address_line_2' => [:optional, String, NilClass],
135
- 'city' => String,
136
- 'state' => String,
137
- 'post_code' => Integer,
138
- 'country' => String
139
- },
140
- 'email' => String
141
- }
142
- ]
91
+ 'test' => true,
92
+ 'users' => [
93
+ {
94
+ 'first_name' => 'Testy',
95
+ 'last_name' => 'Tester',
96
+ 'address' => {
97
+ 'address_line_1' => '1 Test Avenue',
98
+ 'address_line_2' => nil,
99
+ 'city' => 'Testville',
100
+ 'state' => 'TST',
101
+ 'post_code' => 1234,
102
+ 'country' => 'Testalia'
103
+ },
104
+ 'email' => 'testy.tester@test.com'
105
+ },
106
+ {
107
+ 'first_name' => 'Speccy',
108
+ 'last_name' => 'Speccer',
109
+ 'address' => {
110
+ 'address_line_1' => 'Unit 1',
111
+ 'address_line_2' => '42 Spec Street',
112
+ 'city' => 'Specwood',
113
+ 'state' => 'SPC',
114
+ 'post_code' => 5678,
115
+ 'country' => 'Specland'
116
+ },
117
+ 'email' => 'speccy.speccer@spec.com'
118
+ }
143
119
  ],
144
120
  'settings' => {
145
- 'remote_access' => TrueClass
121
+ 'remote_access' => true
146
122
  }
147
123
  }
148
124
  }
149
125
 
150
- before { loader.schema = schema }
126
+ specify 'deprecated `loaded_hash` still works' do
127
+ expect(loader.loaded_hash).to eq loader.content
128
+ end
129
+
130
+ specify { expect(content).to eq expected_content }
131
+
132
+ let(:content_obj) { loader.obj }
151
133
 
152
- specify { expect(loader).to be_valid }
153
- specify { expect(loader.error).to be_nil }
154
- specify { expect { loader.validate! }.not_to raise_error }
134
+ specify { expect(content_obj.test).to eq true }
135
+ specify { expect(content_obj.users[0].first_name).to eq 'Testy' }
136
+ specify { expect(content_obj.users[0].last_name).to eq 'Tester' }
137
+ specify { expect(content_obj.users[0].address.address_line_1).to eq '1 Test Avenue' }
138
+ specify { expect(content_obj.users[0].address.address_line_2).to eq nil }
139
+ specify { expect(content_obj.users[0].address.city).to eq 'Testville' }
140
+ specify { expect(content_obj.users[0].address.state).to eq 'TST' }
141
+ specify { expect(content_obj.users[0].address.post_code).to eq 1234 }
142
+ specify { expect(content_obj.users[0].address.country).to eq 'Testalia' }
143
+ specify { expect(content_obj.users[0].email).to eq 'testy.tester@test.com' }
144
+ specify { expect(content_obj.users[1].first_name).to eq 'Speccy' }
145
+ specify { expect(content_obj.users[1].last_name).to eq 'Speccer' }
146
+ specify { expect(content_obj.users[1].address.address_line_1).to eq 'Unit 1' }
147
+ specify { expect(content_obj.users[1].address.address_line_2).to eq '42 Spec Street' }
148
+ specify { expect(content_obj.users[1].address.city).to eq 'Specwood' }
149
+ specify { expect(content_obj.users[1].address.state).to eq 'SPC' }
150
+ specify { expect(content_obj.users[1].address.post_code).to eq 5678 }
151
+ specify { expect(content_obj.users[1].address.country).to eq 'Specland' }
152
+ specify { expect(content_obj.users[1].email).to eq 'speccy.speccer@spec.com' }
153
+ specify { expect(content_obj.settings.remote_access).to eq true }
155
154
 
156
- context 'when the schema is not matched' do
155
+ context 'when trying to modify the loaded hash' do
156
+ let(:new_user) { double('new user') }
157
+ specify 'the hash should be immutable' do
158
+ expect { content['users'] << new_user }
159
+ .to raise_error RuntimeError, "can't modify frozen Array"
160
+ expect(content['users']).not_to include new_user
161
+ end
162
+ end
163
+
164
+ context 'when trying to modify the content object' do
165
+ let(:new_user) { double('new user') }
166
+ specify 'the object should be immutable' do
167
+ expect { content_obj.users << new_user }
168
+ .to raise_error RuntimeError, "can't modify frozen Array"
169
+ expect(content_obj.users).not_to include new_user
170
+ end
171
+ end
172
+
173
+ context 'when no schema is defined' do
174
+ specify { expect(loader).to be_valid }
175
+ specify { expect(loader.error).to be_nil }
176
+ specify { expect { loader.validate! }.not_to raise_error }
177
+ end
178
+
179
+ context 'when the schema is not a hash' do
180
+ let(:schema) { 'not a hash' }
181
+ specify {
182
+ expect { loader.schema = schema }
183
+ .to raise_error ArgumentError, "#{schema} is not a hash"
184
+ }
185
+ end
186
+
187
+ context 'when a schema is defined' do
157
188
  let(:schema) {
158
189
  {
159
- 'users' => [
190
+ 'test' => TrueClass,
191
+ 'users' => [
160
192
  [
161
193
  {
162
- 'expected_attribute' => String
194
+ 'first_name' => String,
195
+ 'last_name' => String,
196
+ 'address' => {
197
+ 'address_line_1' => String,
198
+ 'address_line_2' => [:optional, String, NilClass],
199
+ 'city' => String,
200
+ 'state' => String,
201
+ 'post_code' => Integer,
202
+ 'country' => String
203
+ },
204
+ 'email' => String
163
205
  }
164
206
  ]
165
- ]
207
+ ],
208
+ 'settings' => {
209
+ 'remote_access' => TrueClass
210
+ }
166
211
  }
167
212
  }
168
213
 
169
- let(:expected_error) { '"users"[0]["expected_attribute"] is not present' }
170
- specify { expect(loader).not_to be_valid }
171
- specify { expect(loader.error).to eq expected_error }
172
- specify { expect { loader.validate! }.to raise_error Yamload::SchemaError, expected_error }
214
+ before do
215
+ loader.schema = schema
216
+ end
217
+
218
+ specify { expect(loader.schema).to eq schema }
219
+ specify { expect(loader).to be_valid }
220
+ specify { expect(loader.error).to be_nil }
221
+ specify { expect { loader.validate! }.not_to raise_error }
222
+
223
+ context 'when the schema is not matched' do
224
+ let(:schema) {
225
+ {
226
+ 'users' => [
227
+ [
228
+ {
229
+ 'expected_attribute' => String
230
+ }
231
+ ]
232
+ ]
233
+ }
234
+ }
235
+
236
+ let(:expected_error) { '"users"[0]["expected_attribute"] is not present' }
237
+ specify { expect(loader).not_to be_valid }
238
+ specify { expect(loader.error).to eq expected_error }
239
+ specify { expect { loader.validate! }.to raise_error Yamload::SchemaError, expected_error }
240
+ end
173
241
  end
174
- end
175
242
 
176
- context 'when defaults are defined' do
177
- let(:defaults) {
178
- {
179
- 'settings' => {
180
- 'remember_user' => false,
181
- 'remote_access' => false
243
+ context 'when the defaults object is not a hash' do
244
+ let(:defaults) { 'not a hash' }
245
+ specify {
246
+ expect { loader.defaults = defaults }
247
+ .to raise_error ArgumentError, "#{defaults} is not a hash"
248
+ }
249
+ end
250
+
251
+ context 'when defaults are defined' do
252
+ let(:defaults) {
253
+ {
254
+ 'settings' => {
255
+ 'remember_user' => false,
256
+ 'remote_access' => false
257
+ }
182
258
  }
183
259
  }
184
- }
185
260
 
186
- before do
187
- loader.defaults = defaults
188
- end
261
+ before do
262
+ loader.defaults = defaults
263
+ end
189
264
 
190
- specify { expect(config_obj.settings.remember_user).to eq false }
191
- specify { expect(config_obj.settings.remote_access).to eq true }
192
- end
265
+ specify { expect(loader.defaults).to eq defaults }
266
+ specify { expect(content_obj.settings.remember_user).to eq false }
267
+ specify { expect(content_obj.settings.remote_access).to eq true }
268
+ end
193
269
 
194
- context 'when reloading' do
195
- let(:original_hash) { loader.loaded_hash }
196
- before do
197
- original_hash
198
- loader.reload
270
+ context 'when reloading' do
271
+ let(:original_hash) { loader.content }
272
+ before do
273
+ original_hash
274
+ loader.reload
275
+ end
276
+ specify { expect(loader.content).not_to be original_hash }
199
277
  end
200
- specify { expect(loader.loaded_hash).not_to be original_hash }
201
278
  end
202
279
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yamload
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alessandro Berardi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-04 00:00:00.000000000 Z
11
+ date: 2015-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: anima
@@ -162,15 +162,29 @@ files:
162
162
  - ".rubocop.yml"
163
163
  - ".ruby-version"
164
164
  - ".travis.yml"
165
+ - CHANGELOG.md
165
166
  - Gemfile
166
167
  - LICENSE.txt
167
168
  - README.md
168
169
  - Rakefile
169
170
  - lib/yamload.rb
170
- - lib/yamload/hash_to_immutable_object.rb
171
+ - lib/yamload/conversion.rb
172
+ - lib/yamload/conversion/array.rb
173
+ - lib/yamload/conversion/hash.rb
174
+ - lib/yamload/conversion/object.rb
175
+ - lib/yamload/defaults.rb
176
+ - lib/yamload/defaults/hash.rb
171
177
  - lib/yamload/loader.rb
178
+ - lib/yamload/loading.rb
179
+ - lib/yamload/loading/yaml.rb
180
+ - lib/yamload/validation.rb
181
+ - lib/yamload/validation/hash.rb
182
+ - lib/yamload/validation/result.rb
172
183
  - lib/yamload/version.rb
184
+ - spec/conversion_spec.rb
185
+ - spec/fixtures/array.yml
173
186
  - spec/fixtures/empty.yml
187
+ - spec/fixtures/string.yml
174
188
  - spec/fixtures/test.yml
175
189
  - spec/loader_spec.rb
176
190
  - spec/spec_helper.rb
@@ -200,7 +214,10 @@ signing_key:
200
214
  specification_version: 4
201
215
  summary: YAML files loader
202
216
  test_files:
217
+ - spec/conversion_spec.rb
218
+ - spec/fixtures/array.yml
203
219
  - spec/fixtures/empty.yml
220
+ - spec/fixtures/string.yml
204
221
  - spec/fixtures/test.yml
205
222
  - spec/loader_spec.rb
206
223
  - spec/spec_helper.rb
@@ -1,40 +0,0 @@
1
- require 'facets/hash/rekey'
2
- require 'anima'
3
-
4
- module Yamload
5
- class HashToImmutableObject
6
- def initialize(hash)
7
- @hash = hash.rekey
8
- end
9
-
10
- def call
11
- immutable_objects_factory.new(converted_hash)
12
- end
13
-
14
- private
15
-
16
- def immutable_objects_factory
17
- anima = Anima.new(*@hash.keys)
18
- Class.new do
19
- include Adamantium
20
- include anima
21
- end
22
- end
23
-
24
- def converted_hash
25
- @hash.map.with_object({}) { |(key, value), hash|
26
- hash[key] = convert(value)
27
- }
28
- end
29
-
30
- def convert(value)
31
- if value.is_a?(Hash)
32
- self.class.new(value).call
33
- elsif value.is_a?(Array)
34
- value.map { |element| convert(element) }
35
- else
36
- value
37
- end
38
- end
39
- end
40
- end