ficus 0.0.5 → 2.0.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: 8e2f42373c4373ad219fa88df98d44ff83916f35
4
- data.tar.gz: aec31ba49eb8074354256fec29d444d4aaa8612a
3
+ metadata.gz: 484115640e759fe32eeefa0d4ec9b65982ad3510
4
+ data.tar.gz: 88ec5550e290697fff154936d6fc1174955d305d
5
5
  SHA512:
6
- metadata.gz: d6d56312dd96ae16ddad988fe555152c4c1361af4ab299495521f54da4dbb44d0af6fd77b53dcaa491fdd4c52108aa27da18f84df707402438968dd23ca37045
7
- data.tar.gz: 1d237e3b1bb559029b6f415af05f4575c103faf664f42fc9e45a3a6c7cb28a65185ed8725b1a235433397b71bfe7e097e75c35cc22bdadeabdb820738379ad87
6
+ metadata.gz: 09fc932f9eee192ae7155b944512f74e7ed083e4d04df176f25bc1137685e6910a03b910ebf4504a23ba5dbeb273e2be5dfe10ff30729fca967ff6c77216af49
7
+ data.tar.gz: 0ef2a6d334d58e138cd5542c5e219775e32b5f1fc61d2580c13a33ed8997e02669f35001d0f927e0b6202d6d0b7caaaa9661677cd213e7a4dbd72bb758a06387
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
+ .vendor
1
2
  .rspec
2
3
  vendor
3
4
  .idea
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.0
5
+
data/README.md CHANGED
@@ -1,10 +1,8 @@
1
1
  # Ficus
2
2
 
3
- A simple YAML configuration DSL that does runtime validation.
3
+ [![Build Status](https://travis-ci.org/drewfradette/ruby-ficus.png?branch=master)](https://travis-ci.org/drewfradette/ruby-ficus)
4
4
 
5
- ## Important Note
6
- This uses the [`recursive-open-struct`](https://github.com/aetherknight/recursive-open-struct) but due to gem missing a [crucial fix](https://github.com/aetherknight/recursive-open-struct/commit/0c16caa1b9a19d12e97829f02083f0b7d21f0100)
7
- I have simply added the latest version with the fix to the `lib`. When it's fixed in rubygems, I will add it as a gem dependency.
5
+ A simple YAML configuration DSL that does runtime validation.
8
6
 
9
7
  ## Installation
10
8
 
@@ -50,7 +48,9 @@ And now we can use Ficus to load the config and validate it at the same time.
50
48
  ```ruby
51
49
  require 'ficus'
52
50
 
53
- config = Ficus.load 'config.yml' do
51
+ ficus = Ficus.load_file 'config.yml'
52
+
53
+ ficus.validation do
54
54
  section 'section_1' do
55
55
  required 'key1'
56
56
  required 'key2'
@@ -75,13 +75,18 @@ config = Ficus.load 'config.yml' do
75
75
  end
76
76
  end
77
77
 
78
- config.section_1.key1 # value1
79
- config.section_1.key2 # value2
80
- config.section_1.key3 # value3
81
- config.section_2.key4 # value4
82
- config.not_defined # nil
78
+ if ficus.valid?
79
+ ficus.config['section_1']['key1'] # value1
80
+ ficus.config['section_1']['key2'] # value2
81
+ ficus.config['section_1']['key3'] # value3
82
+ ficus.config['section_2']['key4'] # value4
83
+ else
84
+ ficus.errors.each { |err| puts err.to_s }
85
+ end
83
86
  ```
84
87
 
88
+ For more examples, see the `examples` directory.
89
+
85
90
  ## Contributing
86
91
 
87
92
  1. Fork it
data/Rakefile CHANGED
@@ -1,3 +1,6 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
- RSpec::Core::RakeTask.new('spec')
3
+
4
+ task :default => :spec
5
+
6
+ RSpec::Core::RakeTask.new('spec')
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ficus'
3
+ require 'pp'
4
+
5
+ ficus = Ficus.load_file('sample.conf').validation do
6
+ section /^server\d?$/ do
7
+ required 'active'
8
+ optional 'state', 'pending'
9
+ end
10
+
11
+ section /^server[A-Z]?$/i do
12
+ required 'active'
13
+ optional 'state', 'running'
14
+ end
15
+ end
16
+
17
+ if ficus.valid?
18
+ pp ficus.config
19
+ else
20
+ puts "Invalid Configuration:"
21
+ ficus.errors.each {|err| puts err.to_s}
22
+ end
@@ -0,0 +1,12 @@
1
+ server1:
2
+ active: true
3
+
4
+ server2:
5
+ active: false
6
+
7
+ serverA:
8
+ active: false
9
+
10
+ serverB:
11
+ active: false
12
+
@@ -0,0 +1,5 @@
1
+ key1: value1
2
+ key2: value2
3
+
4
+ section1:
5
+ active: true
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ficus'
3
+ require 'pp'
4
+
5
+ ficus = Ficus.load_file('sample.conf').validation do
6
+
7
+ required 'key1'
8
+ required 'key2'
9
+
10
+ section 'section1' do
11
+ required 'active'
12
+ optional 'url', 'http://drewfradette.ca'
13
+ end
14
+
15
+ section 'section2', optional: true do
16
+ required 'key3'
17
+ end
18
+ end
19
+
20
+ if ficus.valid?
21
+ pp ficus.config
22
+ else
23
+ ficus.errors.each {|err| puts err.to_s}
24
+ end
@@ -0,0 +1,9 @@
1
+ web:
2
+ ip_address: 127.0.0.1
3
+ state: running
4
+
5
+ db:
6
+ ip_address: 127.0.0.2
7
+
8
+ cache:
9
+ ip_address: 127.0.0.3
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ficus'
3
+ require 'pp'
4
+
5
+ ficus = Ficus.load_file('sample.conf').validation do
6
+ # Note: The DSL is evaluated in order so be sure to
7
+ # declare your templates before you use them.
8
+ template :server do
9
+ required 'ip_address'
10
+ optional 'state', 'pending'
11
+ end
12
+
13
+ section 'web', template: :server
14
+ section 'db', template: :server
15
+ section 'cache', template: :server
16
+ end
17
+
18
+ if ficus.valid?
19
+ pp ficus.config
20
+ else
21
+ ficus.errors.each {|err| puts err.to_s}
22
+ end
@@ -0,0 +1,5 @@
1
+
2
+ active: true
3
+ size: 145
4
+ name: example
5
+ grep: Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ficus'
3
+ require 'pp'
4
+
5
+ ficus = Ficus.load_file('sample.conf').validation do
6
+ required 'active', :boolean
7
+ required 'size', :number
8
+ required 'name', :string
9
+
10
+ optional 'grep', 'lorem ipsum', /est qui dolorem/
11
+ end
12
+
13
+ if ficus.valid?
14
+ pp ficus.config
15
+ else
16
+ ficus.errors.each {|err| puts err.to_s}
17
+ end
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "ficus"
7
- spec.version = '0.0.5'
7
+ spec.version = '2.0.0'
8
8
  spec.authors = ["Drew Fradette"]
9
9
  spec.email = ["drew.fradette@gmail.com"]
10
10
  spec.description = 'A runtime validation configuration DSL'
@@ -20,7 +20,5 @@ Gem::Specification.new do |spec|
20
20
  spec.add_development_dependency "bundler", "~> 1.3"
21
21
  spec.add_development_dependency "rake"
22
22
  spec.add_development_dependency 'rspec'
23
- spec.add_development_dependency 'simplecov' if RUBY_VERSION >= '1.9'
24
-
25
- spec.add_dependency 'recursive-open-struct', '>= 0.4.5'
23
+ spec.add_development_dependency 'simplecov'
26
24
  end
@@ -1,7 +1,166 @@
1
1
  require 'yaml'
2
- require 'logger'
3
- require 'recursive-open-struct'
4
2
 
5
- require 'ficus/loader'
6
- require 'ficus/dsl'
7
- require 'ficus/exceptions'
3
+ class Ficus
4
+ class MissingValidation < StandardError; end
5
+
6
+ class << self
7
+ def load_file(filename)
8
+ Ficus.load(YAML.load_file(filename))
9
+ end
10
+
11
+ def load(data)
12
+ Ficus.new data
13
+ end
14
+ end
15
+
16
+ attr_accessor :config, :errors, :opts, :schema, :templates
17
+
18
+ TYPES = {
19
+ string: [String],
20
+ number: [Fixnum, Float],
21
+ boolean: [TrueClass, FalseClass],
22
+ array: [Array],
23
+ section: [Hash],
24
+ }
25
+
26
+ def initialize(config, opts = {})
27
+ self.config = config
28
+ self.opts = opts
29
+ self.templates = {}
30
+ self.errors = []
31
+ end
32
+
33
+ def validation(&block)
34
+ self.schema = block
35
+ return self
36
+ end
37
+
38
+ def valid?
39
+ raise MissingValidation.new('no validation block specified') if self.schema.nil?
40
+ self.errors = []
41
+ instance_eval(&self.schema)
42
+ return self.errors.empty?
43
+ end
44
+
45
+ def config
46
+ @config ||= {}
47
+ end
48
+
49
+ def template(name, &block)
50
+ self.templates[name] = block
51
+ end
52
+
53
+ def section(name, section_opts = {}, &block)
54
+ if sections(name).nil?
55
+ return if section_opts.fetch(:optional, false)
56
+ msg = (name.class == Regexp) ? "no matches in #{heritage}" : "undefined"
57
+ error heritage(name), msg
58
+ return
59
+ end
60
+
61
+ sections(name).each do |key|
62
+ # Ensure that the section is valid
63
+ if !self.valid_type?(self.get(key), :section)
64
+ error heritage(key), "must be section"
65
+ next
66
+ end
67
+
68
+ # Set the validation block
69
+ validation_block = block if block_given?
70
+ template_name = section_opts.fetch(:template, false)
71
+ if template_name
72
+ if !self.templates.key?(template_name)
73
+ error heritage(key), "undefined template #{template_name}"
74
+ next
75
+ end
76
+ validation_block = self.templates[template_name]
77
+ end
78
+
79
+ # Get to work
80
+ leaf = Ficus.new self.get(key), heritage: heritage(key)
81
+ leaf.validation(&validation_block) unless validation_block.nil?
82
+ if leaf.valid?
83
+ # Update the data for the leaf section as 'optional' can modify the data.
84
+ self.config[key] = leaf.config
85
+ else
86
+ # Update the errors raise by the leaf
87
+ self.errors += leaf.errors
88
+ end
89
+ end
90
+ end
91
+
92
+ def optional(name, default, type = nil)
93
+ value = exists?(name) ? get(name) : default
94
+ if !valid_type?(value, type)
95
+ error heritage(name), "must be #{type}"
96
+ else
97
+ self.config[name] = value
98
+ end
99
+ end
100
+
101
+ def required(name, type = nil)
102
+ if self.config.key?(name)
103
+ error heritage(name), "must be #{type}" unless self.valid_type? get(name), type
104
+ else
105
+ error heritage(name), "undefined"
106
+ end
107
+ end
108
+
109
+ protected
110
+
111
+ def error(name, message)
112
+ self.errors << Error.new(name, message)
113
+ end
114
+
115
+ def exists?(key)
116
+ self.config.key?(key)
117
+ end
118
+
119
+ def get(key)
120
+ self.config.fetch(key)
121
+ end
122
+
123
+ def heritage(postfix = nil)
124
+ if postfix.nil?
125
+ self.opts.fetch(:heritage, [])
126
+ else
127
+ self.opts.fetch(:heritage, []) + [postfix.to_s]
128
+ end
129
+ end
130
+
131
+ def sections(name)
132
+ # Collect the sections
133
+ if name == :all
134
+ self.config.keys
135
+ elsif name.class == Regexp
136
+ self.config.select{|k,v| k =~ name}.keys
137
+ elsif self.config.key?(name)
138
+ {name => self.config[name]}.keys
139
+ else
140
+ nil
141
+ end
142
+ end
143
+
144
+ # Ensure value is a valid type.
145
+ def valid_type?(value, type)
146
+ if type.nil?
147
+ return true
148
+ elsif type.class == Regexp
149
+ !!(type =~ value.to_s)
150
+ else
151
+ TYPES[type].include?(value.class)
152
+ end
153
+ end
154
+
155
+ class Error
156
+ attr_accessor :name, :message
157
+ def initialize(name, message)
158
+ self.name = name
159
+ self.message = message
160
+ end
161
+
162
+ def to_s
163
+ "#{name.join('.')}: #{message}"
164
+ end
165
+ end
166
+ end
@@ -1,145 +1,269 @@
1
1
  require 'spec_helper'
2
2
  require 'tempfile'
3
- require 'yaml'
3
+
4
4
  require 'ficus'
5
5
 
6
6
  describe Ficus do
7
- before :each do
8
- Ficus.logger = logger
9
- @config = {
10
- :general => {:key1=>'value1', :key2=>'value2', :key3=>'value3'},
11
- :misc => {
12
- :key4 => 'value4',
13
- :list => {:item1 => 'value1', :item2 => 'value2'}
14
- }
7
+ def config
8
+ {
9
+ 'general' => {'key1' => 'value1', 'key2' => 'value2', 'key3' =>'value3'},
10
+ 'misc' => {'key4' => 'value4', 'list' => {
11
+ 'item1' => 'value1',
12
+ 'item2' => 'value2'
13
+ }}
15
14
  }
16
15
  end
17
16
 
18
- it 'should load the config without any validation' do
19
- config_file do |config|
20
- config = Ficus.load(config)
21
- config.to_h.should eq @config
17
+ it 'will load the config from a string' do
18
+ ficus = Ficus.load(config)
19
+ expect(ficus.config).to eq config
20
+ end
21
+
22
+ it 'will load the config from a file' do
23
+ Tempfile.open('config.yml') do |file|
24
+ file.write config.to_yaml
25
+ file.close
26
+
27
+ ficus = Ficus.load_file file.path
28
+ expect(ficus.config).to eq config
22
29
  end
23
30
  end
24
31
 
25
- it 'should load the config with a warning about a missing section' do
26
- config_file do |config|
27
- config = Ficus.load(config) do
28
- section 'not_real', :optional => true
32
+ it 'will validate the config with an optional missing section' do
33
+ Ficus.load(config).tap do |ficus|
34
+ ficus.validation do
35
+ section 'not_real', optional: true
29
36
  end
30
- Ficus.warnings.size.should eq 1
37
+ expect(ficus.valid?).to eq true
31
38
  end
32
39
  end
33
40
 
34
- it 'should load the config with a error about a missing section' do
35
- config_file do |config|
36
- expect do
37
- config = Ficus.load(config) do
38
- section 'not_real'
39
- end
40
- end.to raise_error Ficus::ConfigError
41
+ it 'will fail to validate the config due to a missing section' do
42
+ Ficus.load(config).tap do |ficus|
43
+ ficus.validation do
44
+ section 'not_real'
45
+ end
41
46
 
42
- Ficus.errors.size.should eq 1
47
+ expect(ficus.valid?).to eq false
48
+ expect(ficus.errors.size).to eq 1
49
+ expect(ficus.errors.first.to_s).to match(/not_real/)
43
50
  end
44
51
  end
45
52
 
46
- it 'should load the config but fail to validate' do
47
- config_file do |config|
48
- expect do
49
- config = Ficus.load(config) do
50
- section 'general', :optional => true do
51
- required 'fake_param'
52
- end
53
+ it 'will fail to validate the config due to a required parameter' do
54
+ Ficus.load(config).tap do |ficus|
55
+ ficus.validation do
56
+ section 'general', optional: true do
57
+ required 'fake_param'
53
58
  end
54
- end.to raise_error Ficus::ConfigError
55
- Ficus.errors.size.should eq 1
59
+ end
60
+
61
+ expect(ficus.valid?).to eq false
62
+ expect(ficus.errors.size).to eq 1
63
+ expect(ficus.errors.first.to_s).to match(/general.fake_param/)
56
64
  end
57
65
  end
58
66
 
59
- it 'should load the config but fill in the optional value' do
60
- config_file do |config|
61
- config = Ficus.load(config) do
67
+ it 'will validate the config and fill in the optional value' do
68
+ Ficus.load(config).tap do |ficus|
69
+ ficus.validation do
62
70
  section 'general' do
63
71
  optional 'newparam', 'value2'
64
72
  end
65
73
  end
66
- @config.fetch(:general).fetch('newparam', nil).should eq nil
67
- config.general.newparam.should eq 'value2'
74
+
75
+ expect(ficus.valid?).to eq true
76
+ expect(ficus.config['general']['newparam']).to eq 'value2'
68
77
  end
69
78
  end
70
79
 
71
- it 'should validate conformant subsections' do
72
- hash = {
73
- :subsections => {
74
- :section1 => {:key1=>'value1'},
75
- :section2 => {:key1=>'value1'},
76
- }
77
- }
80
+ it 'will validate conformant subsections' do
81
+ hash = {'subsections' => {'section1' => {'key1' => 'value1'}, 'section2' => {'key1' => 'value1'}}}
78
82
 
79
- config_file(hash) do |config|
80
- config = Ficus.load(config) do
83
+ Ficus.load(hash).tap do |ficus|
84
+ ficus.validation do
81
85
  section 'subsections' do
82
86
  section /^section/ do
83
- required :key1
87
+ required 'key1'
84
88
  end
85
89
  end
86
90
  end
87
- config.subsections.section1.key1.should eq 'value1'
88
- config.subsections.section2.key1.should eq 'value1'
91
+
92
+ expect(ficus.valid?).to eq true
93
+ expect(ficus.config['subsections']['section1']['key1']).to eq 'value1'
94
+ expect(ficus.config['subsections']['section2']['key1']).to eq 'value1'
89
95
  end
90
96
  end
91
97
 
92
- it 'should invalidate nonconformant subsections' do
93
- hash = {
94
- :subsections => {
95
- :section1 => {:key1=>'value1'},
96
- :section2 => {},
97
- }
98
- }
98
+ it 'will fail to validate nonconformant subsections' do
99
+ hash = {'subsections' => {'section1' => {'key1' => 'value1'}, 'section2' => {}}}
99
100
 
100
- config_file(hash) do |config|
101
- expect do
102
- Ficus.load(config) do
103
- section 'subsections' do
104
- section /^section/ do
105
- required :key1
106
- end
101
+ Ficus.load(hash).tap do |ficus|
102
+ ficus.validation do
103
+ section 'subsections' do
104
+ section /^section/ do
105
+ required 'key1'
107
106
  end
108
107
  end
109
- end.to raise_error Ficus::ConfigError
108
+ end
109
+
110
+ expect(ficus.valid?).to eq false
111
+ expect(ficus.errors.size).to eq 1
112
+ expect(ficus.errors.first.to_s).to match(/subsections.section2.key1/)
110
113
  end
111
114
  end
112
115
 
113
- it 'should validate all subsections' do
114
- hash = {
115
- :subsections => {
116
- :section1 => {:key1=>'value1'},
117
- :section2 => {},
118
- }
119
- }
116
+ it 'will fail validate all subsections' do
117
+ hash = {'subsections' => {'section1' => {'key1' => 'value1'},'section2' => {}}}
120
118
 
121
- config_file(hash) do |config|
122
- expect do
123
- Ficus.load(config) do
124
- section 'subsections' do
125
- section :all do
126
- required :key1
127
- end
119
+ Ficus.load(hash).tap do |ficus|
120
+ ficus.validation do
121
+ section 'subsections' do
122
+ section :all do
123
+ required 'key1'
128
124
  end
129
125
  end
130
- end.to raise_error Ficus::ConfigError
126
+ end
127
+
128
+ expect(ficus.valid?).to eq false
129
+ expect(ficus.errors.size).to eq 1
130
+ expect(ficus.errors.first.to_s).to match(/subsections.section2.key1/)
131
+ end
132
+ end
133
+
134
+ it 'will validate based on a template' do
135
+ hash = {'section1' => {'key1' => 'value1'}}
136
+
137
+ Ficus.load(hash).tap do |ficus|
138
+ ficus.validation do
139
+ template 'template1' do
140
+ required 'key1'
141
+ end
142
+ section 'section1', template: 'template1'
143
+ end
144
+ expect(ficus.valid?).to eq true
145
+ expect(ficus.config['section1']['key1']).to eq 'value1'
146
+ end
147
+ end
148
+
149
+ it 'will fail to validate due to failing to conform to a template' do
150
+ hash = {'section1' => {}}
151
+
152
+ Ficus.load(hash).tap do |ficus|
153
+ ficus.validation do
154
+ template 'template1' do
155
+ required 'key1'
156
+ end
157
+ section 'section1', :template=>'template1'
158
+ end
159
+ expect(ficus.valid?).to eq false
160
+ expect(ficus.errors.size).to eq 1
161
+ expect(ficus.errors.first.to_s).to match(/section1.key1/)
131
162
  end
132
163
  end
133
164
 
134
- def config_file(hash=@config)
135
- Tempfile.open('config.yml') do |config|
136
- config.write hash.to_yaml
137
- config.close
138
- yield config.path
165
+ it 'will fail to validate a reference to an invalid template' do
166
+ hash = {'section1' => {}}
167
+
168
+ Ficus.load(hash).tap do |ficus|
169
+ ficus.validation do
170
+ section 'section1', template: 'template1'
171
+ end
172
+
173
+ expect(ficus.valid?).to eq false
174
+ expect(ficus.errors.first.to_s).to match(/undefined template template1/)
139
175
  end
140
176
  end
141
177
 
142
- def logger
143
- Logger.new(STDOUT).tap { |log| log.level = Logger::FATAL }
178
+ it 'will fail to validate due to a parameter definde as a section' do
179
+ hash = {'section1' => 'notarealsection'}
180
+
181
+ Ficus.load(hash).tap do |ficus|
182
+ ficus.validation do
183
+ section 'section1'
184
+ end
185
+
186
+ expect(ficus.valid?).to eq false
187
+ expect(ficus.errors.size).to eq 1
188
+ expect(ficus.errors.first.to_s).to match(/must be/i)
189
+ end
190
+ end
191
+
192
+ it 'will validate with type checking on optional parameters' do
193
+ hash = {'number' => 3}
194
+
195
+ Ficus.load(hash).tap do |ficus|
196
+ ficus.validation do
197
+ optional 'number', 1, :number
198
+ optional 'bool', true, :boolean
199
+ optional 'name', 'drew', :string
200
+ optional 'matcher', 'server5', /server\d/
201
+ end
202
+
203
+ expect(ficus.valid?).to eq true
204
+ end
205
+ end
206
+
207
+ it 'will fail to validate with type checking on optional parameters' do
208
+ hash = {}
209
+
210
+ Ficus.load(hash).tap do |ficus|
211
+ ficus.validation do
212
+ optional 'number', false, :number
213
+ optional 'bool', 'drew', :boolean
214
+ optional 'name', 123, :string
215
+ optional 'matcher', 'server5', /zzzserver\d/
216
+ end
217
+
218
+ expect(ficus.valid?).to eq false
219
+ expect(ficus.errors.size).to eq 4
220
+ end
221
+ end
222
+ it 'will validate with type checking on required parameters' do
223
+ hash = {'number' => 1, 'bool' => false,'name' => "drew"}
224
+
225
+ Ficus.load(hash).tap do |ficus|
226
+ ficus.validation do
227
+ required 'number', :number
228
+ required 'bool', :boolean
229
+ required 'name', :string
230
+ end
231
+
232
+ expect(ficus.valid?).to eq true
233
+ end
234
+ end
235
+
236
+ it 'will fail to validate due to type checking on required parameters' do
237
+ hash = {'number' => false, 'bool' => "nope",'name' => 1}
238
+
239
+ Ficus.load(hash).tap do |ficus|
240
+ ficus.validation do
241
+ required 'number', :number
242
+ required 'bool', :boolean
243
+ required 'name', :string
244
+ end
245
+
246
+ expect(ficus.valid?).to eq false
247
+ expect(ficus.errors.size).to eq 3
248
+ ficus.errors.each do |error|
249
+ expect(error.to_s).to match(/must be/)
250
+ end
251
+ end
252
+ end
253
+
254
+ it 'will raise an exception if no validation block is specified' do
255
+ Ficus.load({'key' => 'value'}).tap do |ficus|
256
+ expect(ficus.schema).to eq nil
257
+ expect{ficus.valid?}.to raise_error(::Ficus::MissingValidation)
258
+ end
259
+ end
260
+
261
+ it 'will allow you to chain the validation method' do
262
+ ficus = Ficus.load({'some' => 'data'}).validation do
263
+ required 'some', :string
264
+ end
265
+
266
+ expect(ficus.class).to eq Ficus
267
+ expect(ficus.schema).not_to be_nil
144
268
  end
145
269
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ficus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Drew Fradette
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-24 00:00:00.000000000 Z
11
+ date: 2014-08-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,20 +66,6 @@ dependencies:
66
66
  - - '>='
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: recursive-open-struct
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - '>='
74
- - !ruby/object:Gem::Version
75
- version: 0.4.5
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - '>='
81
- - !ruby/object:Gem::Version
82
- version: 0.4.5
83
69
  description: A runtime validation configuration DSL
84
70
  email:
85
71
  - drew.fradette@gmail.com
@@ -88,15 +74,21 @@ extensions: []
88
74
  extra_rdoc_files: []
89
75
  files:
90
76
  - .gitignore
77
+ - .travis.yml
91
78
  - Gemfile
92
79
  - LICENSE.txt
93
80
  - README.md
94
81
  - Rakefile
82
+ - examples/regexp/regexp.rb
83
+ - examples/regexp/sample.conf
84
+ - examples/simple/sample.conf
85
+ - examples/simple/simple.rb
86
+ - examples/templates/sample.conf
87
+ - examples/templates/templates.rb
88
+ - examples/typechecking/sample.conf
89
+ - examples/typechecking/typechecking.rb
95
90
  - ficus.gemspec
96
91
  - lib/ficus.rb
97
- - lib/ficus/dsl.rb
98
- - lib/ficus/exceptions.rb
99
- - lib/ficus/loader.rb
100
92
  - spec/ficus_spec.rb
101
93
  - spec/spec_helper.rb
102
94
  homepage: https://github.com/drewfradette/ruby-ficus
@@ -1,36 +0,0 @@
1
- class Ficus < RecursiveOpenStruct
2
- def section(name, args = {}, &block)
3
- sections(name).each do |s|
4
- s.parent = self.parent ? "#{self.parent}.#{name}" : name
5
- s.instance_eval(&block) if block_given?
6
- end
7
- rescue NoSection
8
- if args[:optional] == true
9
- Ficus.warning("Section #{name} is not defined")
10
- else
11
- Ficus.error("Section #{name} is not defined")
12
- end
13
- end
14
-
15
- def sections(name)
16
- if name == :all
17
- sections(/.*/)
18
- elsif name.is_a? Regexp
19
- matches = self.marshal_dump.keys
20
- matches.map { |k| self.send(k) unless k == :parent }.compact!
21
- else
22
- s = self.send(name)
23
- raise NoSection.new if s.nil?
24
- [s]
25
- end
26
- end
27
-
28
- def optional(name, default)
29
- self.send("#{name}=", default) if self.send(name).nil?
30
- end
31
-
32
- def required(name)
33
- prefix = self.parent ? "#{self.parent}." : nil
34
- Ficus.error "Option #{prefix}#{name} is not defined" if self.send(name).nil?
35
- end
36
- end
@@ -1,4 +0,0 @@
1
- class Ficus < RecursiveOpenStruct
2
- class ConfigError < StandardError; end
3
- class NoSection < StandardError; end
4
- end
@@ -1,38 +0,0 @@
1
- class Ficus < RecursiveOpenStruct
2
- class << self
3
- attr_accessor :verbose, :logger, :errors, :warnings
4
-
5
- # Load the configuration file and validate.
6
- def load(file, &block)
7
- @errors, @warnings, @config = [], [], nil
8
- config(file).instance_eval(&block) if block_given?
9
-
10
- warnings.each { |e| logger.warn e }
11
- errors.each { |e| logger.error e }
12
- raise ConfigError.new('Unable to start due to invalid settings') if errors.size > 0
13
- config(file)
14
- end
15
-
16
- def error(msg)
17
- @errors << msg
18
- end
19
-
20
- def warning(msg)
21
- @warnings << msg
22
- end
23
-
24
- protected
25
- def config(file)
26
- if @config.nil?
27
- yaml = YAML.load File.read(file)
28
- @config = Ficus.new(yaml, :recurse_over_arrays => true)
29
- end
30
- @config
31
- end
32
-
33
- def logger
34
- @logger ||= Logger.new(STDERR).tap { |l| l.level = Logger::WARN }
35
- end
36
- end
37
- end
38
-