yamload 0.0.6 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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