ficus 0.0.1

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.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.sw*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ficus.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 afradette
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,76 @@
1
+ # Ficus
2
+
3
+ A simple YAML configuration DSL that does runtime validation.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'ficus'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install ficus
18
+
19
+ ## Usage
20
+
21
+ Here is an example YAML config file:
22
+
23
+ ```yaml
24
+ # config.yml
25
+ ---
26
+ section_1:
27
+ key1: value1
28
+ key2: value2
29
+
30
+ section_2:
31
+ key4: value4
32
+
33
+ optional_section:
34
+ key5: value5
35
+ ```
36
+
37
+ And now we can use Ficus to load the config and validate it at the same time.
38
+
39
+ ```ruby
40
+ require 'ficus'
41
+
42
+ config = Ficus.load 'config.yml' do
43
+ section 'section_1' do
44
+ required 'key1'
45
+ required 'key2'
46
+
47
+ optional 'key3', 'value3'
48
+ end
49
+
50
+ section 'section_2' do
51
+ required 'key4'
52
+ end
53
+
54
+ section 'optional_section', :optional => true do
55
+ required 'key5'
56
+ end
57
+
58
+ section 'not_defined', :optional => true do
59
+ require 'key6'
60
+ end
61
+ end
62
+
63
+ config.section_1.key1 # value1
64
+ config.section_1.key2 # value2
65
+ config.section_1.key3 # value3
66
+ config.section_2.key4 # value4
67
+ config.not_defined # nil
68
+ ```
69
+
70
+ ## Contributing
71
+
72
+ 1. Fork it
73
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
74
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
75
+ 4. Push to the branch (`git push origin my-new-feature`)
76
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "ficus"
7
+ spec.version = '0.0.1'
8
+ spec.authors = ["Drew Fradette"]
9
+ spec.email = ["drew.fradette@gmail.com"]
10
+ spec.description = 'A runtime validation configuration DSL'
11
+ spec.summary = ''
12
+ spec.homepage = "https://github.com/drewfradette/ficus"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.3"
21
+ spec.add_development_dependency "rake"
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'
26
+ end
@@ -0,0 +1,47 @@
1
+ require 'yaml'
2
+ require 'recursive-open-struct'
3
+
4
+ class Ficus < RecursiveOpenStruct
5
+ class ConfigError < StandardError; end
6
+ class << self
7
+ attr_accessor :log, :verbose
8
+
9
+ def load(file, &block)
10
+ @log = []
11
+ yaml = YAML.load File.read(file)
12
+ config = Ficus.new(yaml, :recurse_over_arrays => true)
13
+ config.instance_eval(&block) if block_given?
14
+
15
+ errors = log.select{|v| v =~ /^\[ERR\]/}
16
+ if errors.size > 0
17
+ log.each{|v| puts v} if ENV['DEBUG']
18
+ raise ConfigError.new("Unable to start due to invalid settings")
19
+ end
20
+ config
21
+ end
22
+
23
+ def log(log = {})
24
+ @log << log
25
+ end
26
+ end
27
+
28
+ def section(name, args = {}, &block)
29
+ section = self.send(name)
30
+ if section.nil?
31
+ level = args[:optional] ? 'WARN' : 'ERR'
32
+ Ficus.log "[#{level}] Section #{name} is not defined"
33
+ else
34
+ section.parent = self.parent ? "#{self.parent}.#{name}" : name
35
+ section.instance_eval &block if block_given?
36
+ end
37
+ end
38
+
39
+ def optional(name, default)
40
+ self.send("#{name}=", default) if self.send(name).nil?
41
+ end
42
+
43
+ def required(name)
44
+ prefix = self.parent ? "#{self.parent}." : nil
45
+ Ficus.log "[ERR] Option #{prefix}#{name} is not defined" if self.send(name).nil?
46
+ end
47
+ end
@@ -0,0 +1,93 @@
1
+ require 'ostruct'
2
+
3
+ class RecursiveOpenStruct < OpenStruct
4
+ VERSION = "0.4.3"
5
+
6
+ def initialize(h=nil, args={})
7
+ @recurse_over_arrays = args.fetch(:recurse_over_arrays,false)
8
+ super(h)
9
+ @sub_elements = {}
10
+ end
11
+
12
+ def to_h
13
+ @table.dup.update(@sub_elements) do |k, oldval, newval|
14
+ if newval.kind_of?(self.class)
15
+ newval.to_h
16
+ elsif newval.kind_of?(Array)
17
+ newval.map { |a| a.kind_of?(self.class) ? a.to_h : a }
18
+ else
19
+ raise "Cached value of unsupported type: #{newval.inspect}"
20
+ end
21
+ end
22
+ end
23
+
24
+ def new_ostruct_member(name)
25
+ name = name.to_sym
26
+ unless self.respond_to?(name)
27
+ class << self; self; end.class_eval do
28
+ define_method(name) do
29
+ v = @table[name]
30
+ if v.is_a?(Hash)
31
+ @sub_elements[name] ||= self.class.new(v, :recurse_over_arrays => @recurse_over_arrays)
32
+ elsif v.is_a?(Array) and @recurse_over_arrays
33
+ @sub_elements[name] ||= recurse_over_array v
34
+ else
35
+ v
36
+ end
37
+ end
38
+ define_method("#{name}=") { |x| modifiable[name] = x }
39
+ define_method("#{name}_as_a_hash") { @table[name] }
40
+ end
41
+ end
42
+ name
43
+ end
44
+
45
+ def recurse_over_array array
46
+ array.map do |a|
47
+ if a.is_a? Hash
48
+ self.class.new(a, :recurse_over_arrays => true)
49
+ elsif a.is_a? Array
50
+ recurse_over_array a
51
+ else
52
+ a
53
+ end
54
+ end
55
+ end
56
+
57
+ def debug_inspect(io = STDOUT, indent_level = 0, recursion_limit = 12)
58
+ display_recursive_open_struct(io, @table, indent_level, recursion_limit)
59
+ end
60
+
61
+ def display_recursive_open_struct(io, ostrct_or_hash, indent_level, recursion_limit)
62
+
63
+ if recursion_limit <= 0 then
64
+ # protection against recursive structure (like in the tests)
65
+ io.puts ' '*indent_level + '(recursion limit reached)'
66
+ else
67
+ #puts ostrct_or_hash.inspect
68
+ if ostrct_or_hash.is_a?(self.class) then
69
+ ostrct_or_hash = ostrct_or_hash.marshal_dump
70
+ end
71
+
72
+ # We'll display the key values like this : key = value
73
+ # to align display, we look for the maximum key length of the data that will be displayed
74
+ # (everything except hashes)
75
+ data_indent = ostrct_or_hash \
76
+ .reject { |k, v| v.is_a?(self.class) || v.is_a?(Hash) } \
77
+ .max {|a,b| a[0].to_s.length <=> b[0].to_s.length}[0].to_s.length
78
+ # puts "max length = #{data_indent}"
79
+
80
+ ostrct_or_hash.each do |key, value|
81
+ if (value.is_a?(self.class) || value.is_a?(Hash)) then
82
+ io.puts ' '*indent_level + key.to_s + '.'
83
+ display_recursive_open_struct(io, value, indent_level + 1, recursion_limit - 1)
84
+ else
85
+ io.puts ' '*indent_level + key.to_s + ' '*(data_indent - key.to_s.length) + ' = ' + value.inspect
86
+ end
87
+ end
88
+ end
89
+
90
+ true
91
+ end
92
+
93
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ require 'tempfile'
4
+ require 'yaml'
5
+
6
+ require 'ficus'
7
+
8
+ describe Ficus do
9
+ before :each do
10
+ @config = {
11
+ :general => {:key1=>'value1',:key2=>'value2',:key3=>'value3'},
12
+ :misc => {
13
+ :key4 => 'value4',
14
+ :list => {:item1 => 'value1', :item2 => 'value2'}
15
+ }
16
+ }
17
+ end
18
+
19
+ it 'should load the config without any validation' do
20
+ config_file do |config|
21
+ config = Ficus.load(config)
22
+
23
+ config.to_h.should eq @config
24
+ end
25
+ end
26
+
27
+ it 'should load the config with a warning about a missing section' do
28
+ config_file do |config|
29
+ config = Ficus.load(config) do
30
+ section 'not_real', :optional => true
31
+ end
32
+ Ficus.log.count{|v| v =~ /^\[WARN\]/}.should eq 1
33
+ end
34
+ end
35
+
36
+ it 'should load the config with a error about a missing section' do
37
+ config_file do |config|
38
+ expect {
39
+ config = Ficus.load(config) do
40
+ section 'not_real'
41
+ end
42
+ }.to raise_error Ficus::ConfigError
43
+
44
+ Ficus.log.count{|v| v =~ /^\[ERR\]/}.should eq 1
45
+ end
46
+ end
47
+
48
+ it 'should load the config but fail to validate' do
49
+ config_file do |config|
50
+ expect {
51
+ config = Ficus.load(config) do
52
+ section 'general', :optional => true do
53
+ required 'fake_param'
54
+ end
55
+ end
56
+ }.to raise_error Ficus::ConfigError
57
+ Ficus.log.count{|v| v =~ /^\[ERR\]/}.should eq 1
58
+ end
59
+ end
60
+
61
+ it 'should load the config but fill in the optional value' do
62
+ config_file do |config|
63
+ config = Ficus.load(config) do
64
+ section 'general' do
65
+ optional 'newparam', 'value2'
66
+ end
67
+ end
68
+ @config.fetch(:general).fetch('newparam', nil).should eq nil
69
+ config.general.newparam.should eq 'value2'
70
+ end
71
+ end
72
+
73
+ def config_file
74
+ Tempfile.open('config.yml') do |config|
75
+ config.write @config.to_yaml
76
+ config.close
77
+
78
+ yield config.path
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,17 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), '..')
2
+
3
+ require 'rspec'
4
+
5
+ if RUBY_VERSION.to_f >= 1.9
6
+ puts 'Enabling coverage'
7
+ require 'simplecov'
8
+ SimpleCov.add_filter 'vendor'
9
+ SimpleCov.add_filter 'spec'
10
+ SimpleCov.start
11
+ end
12
+
13
+ RSpec.configure do |config|
14
+ require 'lib/ficus'
15
+ end
16
+
17
+
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ficus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Drew Fradette
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-10-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: simplecov
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: A runtime validation configuration DSL
79
+ email:
80
+ - drew.fradette@gmail.com
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - Gemfile
87
+ - LICENSE.txt
88
+ - README.md
89
+ - Rakefile
90
+ - ficus.gemspec
91
+ - lib/ficus.rb
92
+ - lib/recursive-open-struct.rb
93
+ - spec/ficus_spec.rb
94
+ - spec/spec_helper.rb
95
+ homepage: https://github.com/drewfradette/ficus
96
+ licenses:
97
+ - MIT
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ! '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ segments:
109
+ - 0
110
+ hash: 996828060778593471
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ! '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ segments:
118
+ - 0
119
+ hash: 996828060778593471
120
+ requirements: []
121
+ rubyforge_project:
122
+ rubygems_version: 1.8.25
123
+ signing_key:
124
+ specification_version: 3
125
+ summary: ''
126
+ test_files:
127
+ - spec/ficus_spec.rb
128
+ - spec/spec_helper.rb