appsent 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.
- data/.gitignore +11 -0
- data/.rspec +1 -0
- data/.travis.yml +5 -0
- data/.yardopts +2 -0
- data/Gemfile +4 -0
- data/README.md +40 -0
- data/Rakefile +13 -0
- data/appsent.gemspec +28 -0
- data/features/README.md +40 -0
- data/features/step_definitions/helper_steps.rb +7 -0
- data/features/support/env.rb +1 -0
- data/features/usage_example.feature +85 -0
- data/lib/appsent.rb +72 -0
- data/lib/appsent/config_file.rb +71 -0
- data/lib/appsent/config_value.rb +56 -0
- data/lib/appsent/tools.rb +18 -0
- data/lib/appsent/version.rb +3 -0
- data/spec/dsl_spec.rb +72 -0
- data/spec/fixtures/database.yml +4 -0
- data/spec/fixtures/simple_config.yml +3 -0
- data/spec/fixtures/simple_config_with_just_type.yml +4 -0
- data/spec/internal_api/config_file_spec.rb +74 -0
- data/spec/internal_api/config_value_spec.rb +114 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/tools_spec.rb +35 -0
- metadata +177 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
APPSENT - awesome config management solution [](http://travis-ci.org/kucaahbe/appsent)
|
2
|
+
================================================================================================================================================
|
3
|
+
|
4
|
+
Description
|
5
|
+
-----------
|
6
|
+
|
7
|
+
In every application (especially big apps, where a lot of people working on it) you need
|
8
|
+
to have many config files, where settings for different environments (development, test, and production as minimum),
|
9
|
+
and without some settings there application will fail, and sometime it takes long time to find where was mistake.
|
10
|
+
This gem provides easy way to handle loading different config files, validations for necessary config values, in a such way:
|
11
|
+
your application will not start until you fill all needed settings, and when you done you will have access to every your in a convenient way.
|
12
|
+
|
13
|
+
Usage
|
14
|
+
-----
|
15
|
+
|
16
|
+
Initialize application with config requirements:
|
17
|
+
|
18
|
+
require 'appsent'
|
19
|
+
AppSent.init(:path => 'config', :env => ENV['RACK_ENV']) do
|
20
|
+
|
21
|
+
# Hash-based config:
|
22
|
+
mongo_db_config do
|
23
|
+
host :type => String, :example => 'localhost', :desc => 'Host to connect to MongoDB'
|
24
|
+
port :type => Fixnum
|
25
|
+
pool_size :type => Fixnum
|
26
|
+
timeout :type => Fixnum
|
27
|
+
end
|
28
|
+
|
29
|
+
# Array-based config(TODO:block non-required):
|
30
|
+
exception_notification_recipients :type => Array
|
31
|
+
|
32
|
+
my_simple_config # config without special requirements, stupid but it can do so, why not?
|
33
|
+
end
|
34
|
+
|
35
|
+
Access to config values performs in a such way:
|
36
|
+
|
37
|
+
AppSent::MONGO_DB_CONFIG['host'] #=>'localhost'
|
38
|
+
AppSent::EXCEPTION_NOTIFICATION_RECIPIENTS #=>[...]
|
39
|
+
|
40
|
+
TODO: more doc
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'cucumber'
|
4
|
+
require 'cucumber/rake/task'
|
5
|
+
Bundler::GemHelper.install_tasks
|
6
|
+
|
7
|
+
task :default => :spec
|
8
|
+
|
9
|
+
RSpec::Core::RakeTask.new(:spec)
|
10
|
+
|
11
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
12
|
+
t.cucumber_opts = "--format pretty"
|
13
|
+
end
|
data/appsent.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "appsent/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "appsent"
|
7
|
+
s.version = AppSent::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["kucaahbe"]
|
10
|
+
s.email = ["kucaahbe@ukr.net"]
|
11
|
+
s.homepage = "http://github.com/kucaahbe/appsent"
|
12
|
+
s.summary = %q{config management solution}
|
13
|
+
s.description = s.summary
|
14
|
+
|
15
|
+
s.rubyforge_project = "appsent"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_development_dependency 'yard'
|
23
|
+
s.add_development_dependency 'BlueCloth'
|
24
|
+
s.add_development_dependency 'RedCloth'
|
25
|
+
s.add_development_dependency 'rspec', '>= 2.0.0'
|
26
|
+
s.add_development_dependency 'aruba'
|
27
|
+
s.add_development_dependency 'rake'
|
28
|
+
end
|
data/features/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
APPSENT - awesome config management solution [](http://travis-ci.org/kucaahbe/appsent)
|
2
|
+
================================================================================================================================================
|
3
|
+
|
4
|
+
Description
|
5
|
+
-----------
|
6
|
+
|
7
|
+
In every application (especially big apps, where a lot of people working on it) you need
|
8
|
+
to have many config files, where settings for different environments (development, test, and production as minimum),
|
9
|
+
and without some settings there application will fail, and sometime it takes long time to find where was mistake.
|
10
|
+
This gem provides easy way to handle loading different config files, validations for necessary config values, in a such way:
|
11
|
+
your application will not start until you fill all needed settings, and when you done you will have access to every your in a convenient way.
|
12
|
+
|
13
|
+
Usage
|
14
|
+
-----
|
15
|
+
|
16
|
+
Initialize application with config requirements:
|
17
|
+
|
18
|
+
require 'appsent'
|
19
|
+
AppSent.init(:path => 'config', :env => ENV['RACK_ENV']) do
|
20
|
+
|
21
|
+
# Hash-based config:
|
22
|
+
mongo_db_config do
|
23
|
+
host :type => String, :example => 'localhost', :desc => 'Host to connect to MongoDB'
|
24
|
+
port :type => Fixnum
|
25
|
+
pool_size :type => Fixnum
|
26
|
+
timeout :type => Fixnum
|
27
|
+
end
|
28
|
+
|
29
|
+
# Array-based config(TODO:block non-required):
|
30
|
+
exception_notification_recipients :type => Array
|
31
|
+
|
32
|
+
my_simple_config # config without special requirements, stupid but it can do so, why not?
|
33
|
+
end
|
34
|
+
|
35
|
+
Access to config values performs in a such way:
|
36
|
+
|
37
|
+
AppSent::MONGO_DB_CONFIG['host'] #=>'localhost'
|
38
|
+
AppSent::EXCEPTION_NOTIFICATION_RECIPIENTS #=>[...]
|
39
|
+
|
40
|
+
TODO: more doc
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'aruba/cucumber'
|
@@ -0,0 +1,85 @@
|
|
1
|
+
Feature: Usage example
|
2
|
+
As ruby developer
|
3
|
+
|
4
|
+
I want my application to start only when all config files exist and have right values
|
5
|
+
|
6
|
+
and in this case I am using appsent
|
7
|
+
|
8
|
+
Background:
|
9
|
+
Given a file named "my_app.rb" with:
|
10
|
+
"""
|
11
|
+
require 'appsent'
|
12
|
+
|
13
|
+
AppSent.init :path => 'config', :env => 'production' do
|
14
|
+
mongodb do
|
15
|
+
host :type => String, :example => 'localhost', :desc => 'Host to connect to MongoDB'
|
16
|
+
port :type => Fixnum, :example => 27017, :desc => 'MongoDB port'
|
17
|
+
pool_size :type => Fixnum
|
18
|
+
timeout :type => Fixnum
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
puts 'All stuff work!'
|
23
|
+
"""
|
24
|
+
|
25
|
+
Scenario: config does not exists
|
26
|
+
When I run `ruby my_app.rb`
|
27
|
+
Then the output should contain:
|
28
|
+
"""
|
29
|
+
missing config file 'config/mongodb.yml'
|
30
|
+
"""
|
31
|
+
|
32
|
+
Scenario: Config has no environment(or config is wrong yml file(FIXME))
|
33
|
+
When I add file named "config/mongodb.yml"
|
34
|
+
And I run `ruby my_app.rb`
|
35
|
+
Then the output should contain:
|
36
|
+
"""
|
37
|
+
config file 'config/mongodb.yml' has no 'production' environment
|
38
|
+
"""
|
39
|
+
|
40
|
+
Scenario: required parameteres do not specified
|
41
|
+
When I write to "config/mongodb.yml" with:
|
42
|
+
"""
|
43
|
+
production:
|
44
|
+
optional_value: temp
|
45
|
+
"""
|
46
|
+
And I run `ruby my_app.rb`
|
47
|
+
Then the output should contain:
|
48
|
+
"""
|
49
|
+
wrong config file 'config/mongodb.yml':
|
50
|
+
host: localhost # does not exists(Host to connect to MongoDB), String
|
51
|
+
port: 27017 # does not exists(MongoDB port), Fixnum
|
52
|
+
pool_size: # does not exists, Fixnum
|
53
|
+
timeout: # does not exists, Fixnum
|
54
|
+
"""
|
55
|
+
|
56
|
+
Scenario: Some parameter is wrong
|
57
|
+
When I write to "config/mongodb.yml" following:
|
58
|
+
"""
|
59
|
+
production:
|
60
|
+
host: 100500
|
61
|
+
port: 27017
|
62
|
+
pool_size: 1
|
63
|
+
timeout: 5
|
64
|
+
"""
|
65
|
+
And I run `ruby my_app.rb`
|
66
|
+
Then the output should contain:
|
67
|
+
"""
|
68
|
+
wrong config file 'config/mongodb.yml':
|
69
|
+
host: 100500 # wrong type,should be String(Host to connect to MongoDB)
|
70
|
+
"""
|
71
|
+
|
72
|
+
Scenario: All config present and have right values
|
73
|
+
When I write to "config/mongodb.yml" following:
|
74
|
+
"""
|
75
|
+
production:
|
76
|
+
host: 'somehost.com'
|
77
|
+
port: 27017
|
78
|
+
pool_size: 1
|
79
|
+
timeout: 5
|
80
|
+
"""
|
81
|
+
And I run `ruby my_app.rb`
|
82
|
+
Then the output should contain:
|
83
|
+
"""
|
84
|
+
All stuff work!
|
85
|
+
"""
|
data/lib/appsent.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'appsent/tools'
|
2
|
+
require 'appsent/config_value'
|
3
|
+
require 'appsent/config_file'
|
4
|
+
|
5
|
+
class AppSent
|
6
|
+
|
7
|
+
class ConfigPathNotSet < ArgumentError; end
|
8
|
+
class EnvironmentNotSet < ArgumentError; end
|
9
|
+
class BlockRequired < StandardError; end
|
10
|
+
class Error < LoadError; end
|
11
|
+
|
12
|
+
@@config_path = nil
|
13
|
+
@@environment = nil
|
14
|
+
@@config_files = []
|
15
|
+
|
16
|
+
def self.init opts={},&block
|
17
|
+
raise ConfigPathNotSet unless opts[:path]
|
18
|
+
raise EnvironmentNotSet unless opts[:env]
|
19
|
+
raise BlockRequired unless block_given?
|
20
|
+
|
21
|
+
caller_filename = caller.first.split(':').first
|
22
|
+
@@config_path = File.expand_path(File.join(File.dirname(caller_filename),opts[:path]))
|
23
|
+
@@environment = opts[:env]
|
24
|
+
|
25
|
+
settings = self.new
|
26
|
+
settings.instance_exec(&block)
|
27
|
+
if settings.all_valid?
|
28
|
+
settings.load!
|
29
|
+
else
|
30
|
+
raise AppSent::Error, settings.full_error_message
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.config_files
|
35
|
+
@@config_files
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.config_path
|
39
|
+
@@config_path
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize
|
43
|
+
@configs=[]
|
44
|
+
end
|
45
|
+
|
46
|
+
def all_valid?
|
47
|
+
@configs.ask_all? { |conf_file| conf_file.valid? }
|
48
|
+
end
|
49
|
+
|
50
|
+
def load!
|
51
|
+
@configs.each do |config|
|
52
|
+
self.class.const_set(config.constantized,config.data)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def full_error_message
|
57
|
+
error_description = ''
|
58
|
+
@configs.each do |config|
|
59
|
+
error_description += config.error_message+"\n" unless config.valid?
|
60
|
+
end
|
61
|
+
"failed to load some configuration files\n\n"+error_description
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def method_missing config, args={}, &block
|
67
|
+
config = config.to_s
|
68
|
+
@@config_files << config
|
69
|
+
@configs << ConfigFile.new(@@config_path,config,@@environment,args[:type],&block)
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
class AppSent
|
2
|
+
class ConfigFile
|
3
|
+
attr_reader :data
|
4
|
+
|
5
|
+
CONFIG_NOT_FOUND_ERROR_MSG = "missing config file '%s'"
|
6
|
+
ENVIRONMENT_NOT_FOUND_ERROR_MSG = "config file '%s' has no '%s' environment"
|
7
|
+
WRONG_CONFIG_ERROR_MSG = "wrong config file '%s':\n"
|
8
|
+
|
9
|
+
def initialize config_dir, config_file_name, environment, type, &block
|
10
|
+
@config_dir, @config_file_name, @environment, @type, @block = config_dir, config_file_name, (environment && environment.to_sym), type, block
|
11
|
+
|
12
|
+
@type ||= Hash
|
13
|
+
raise "params #{@type} and block given" if block_given? and not @type==Hash
|
14
|
+
@path_to_config = File.join(@config_dir,@config_file_name+'.yml')
|
15
|
+
end
|
16
|
+
|
17
|
+
def valid?
|
18
|
+
return @checked if defined?(@checked)
|
19
|
+
|
20
|
+
yaml_data = YAML.load_file(@path_to_config)
|
21
|
+
if yaml_data.is_a?(Hash)
|
22
|
+
yaml_data.symbolize_keys!
|
23
|
+
else
|
24
|
+
# yaml is not valid YAML file, TODO change error message
|
25
|
+
@self_error_msg = ENVIRONMENT_NOT_FOUND_ERROR_MSG % [relative_path_to_config,@environment]
|
26
|
+
return @checked = false
|
27
|
+
end
|
28
|
+
|
29
|
+
@data = yaml_data[@environment]
|
30
|
+
|
31
|
+
@checked = if @data.instance_of?(@type)
|
32
|
+
@data.symbolize_keys! if @type==Hash
|
33
|
+
if @block
|
34
|
+
self.instance_exec(&@block)
|
35
|
+
@self_error_msg = WRONG_CONFIG_ERROR_MSG % relative_path_to_config
|
36
|
+
options.ask_all? { |option| option.valid? }
|
37
|
+
else
|
38
|
+
true
|
39
|
+
end
|
40
|
+
else
|
41
|
+
false
|
42
|
+
end
|
43
|
+
rescue Errno::ENOENT
|
44
|
+
@self_error_msg = CONFIG_NOT_FOUND_ERROR_MSG % relative_path_to_config
|
45
|
+
@checked = false
|
46
|
+
end
|
47
|
+
|
48
|
+
def options
|
49
|
+
@options ||= []
|
50
|
+
end
|
51
|
+
|
52
|
+
def constantized
|
53
|
+
@config_file_name.upcase
|
54
|
+
end
|
55
|
+
|
56
|
+
def error_message
|
57
|
+
@self_error_msg += options.map { |o| o.valid? ? nil : o.error_message }.compact.join("\n")
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def relative_path_to_config
|
63
|
+
@path_to_config.gsub(Dir.pwd+File::SEPARATOR,'')
|
64
|
+
end
|
65
|
+
|
66
|
+
def method_missing option, opts={}, &block
|
67
|
+
self.options << ConfigValue.new(option.to_s, opts[:type], @data[option.to_sym], opts[:desc], opts[:example], &block)
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
class AppSent
|
2
|
+
class ConfigValue
|
3
|
+
attr_reader :parameter, :data_type, :data, :description, :example
|
4
|
+
attr_accessor :nesting
|
5
|
+
|
6
|
+
WRONG_DATA_TYPE_PASSED_MSG = "data type should be ruby class!"
|
7
|
+
VALUE_NOT_EXISTS_MSG = "does not exists"
|
8
|
+
VALUE_WRONG_TYPE_MSG = "wrong type,should be %s"
|
9
|
+
FULL_ERROR_MESSAGE = "%s: %s # %s%s%s"
|
10
|
+
|
11
|
+
# data => it's an actual data of parameter
|
12
|
+
def initialize parameter, data_type, data, description, example, &block
|
13
|
+
@parameter, @data_type, @data, @description, @example = (parameter.to_sym rescue parameter), data_type, data, description, example
|
14
|
+
|
15
|
+
@data_type ||= Hash
|
16
|
+
raise WRONG_DATA_TYPE_PASSED_MSG unless @data_type.is_a?(Class)
|
17
|
+
raise "params #{@data_type} and block given" if block_given? and not @data_type==Hash
|
18
|
+
|
19
|
+
@block = block
|
20
|
+
@nesting = 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid?
|
24
|
+
return @checked if defined?(@checked)
|
25
|
+
@checked = if data.instance_of?(data_type)
|
26
|
+
if @block
|
27
|
+
data.symbolize_keys!
|
28
|
+
self.instance_exec(&@block)
|
29
|
+
child_options.any? { |option| option.valid? }
|
30
|
+
else
|
31
|
+
true
|
32
|
+
end
|
33
|
+
else
|
34
|
+
false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def child_options
|
39
|
+
@options ||= []
|
40
|
+
end
|
41
|
+
|
42
|
+
def error_message
|
43
|
+
desc = (description and "(#{description})")
|
44
|
+
actual_error_msg = (data ? VALUE_WRONG_TYPE_MSG % [data_type] : VALUE_NOT_EXISTS_MSG)
|
45
|
+
optional_type = (data ? '' : ', '+data_type.inspect)
|
46
|
+
' '*(self.nesting+1)+FULL_ERROR_MESSAGE % [parameter, (data or example), actual_error_msg, desc, optional_type]
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def method_missing option, opts={}
|
52
|
+
self.child_options << self.class.new(option.to_s, opts[:type], data[option.to_sym], opts[:desc], opts[:example])
|
53
|
+
self.child_options.last.nesting+=(self.nesting+1)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Hash
|
2
|
+
# fix bug here
|
3
|
+
unless self.method_defined?(:symbolize_keys!)
|
4
|
+
def symbolize_keys!
|
5
|
+
self.keys.each { |key| self[(key.to_sym rescue key) || key] = self.delete(key) }
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Array
|
11
|
+
def ask_all? &block
|
12
|
+
result = []
|
13
|
+
self.each do |el|
|
14
|
+
result << block.call(el)
|
15
|
+
end
|
16
|
+
result.all?
|
17
|
+
end
|
18
|
+
end
|
data/spec/dsl_spec.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "AppSent.init" do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@right_params = { :path => 'fixtures', :env => 'test' }
|
7
|
+
@fixtures_path = File.expand_path(File.join(File.dirname(__FILE__),'fixtures'))
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should require config path" do
|
11
|
+
@right_params.delete(:path)
|
12
|
+
expect { AppSent.init(@right_params) do; end }.to raise_exception(AppSent::ConfigPathNotSet)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should require environment variable" do
|
16
|
+
@right_params.delete(:env)
|
17
|
+
expect { AppSent.init(@right_params) do; end }.to raise_exception(AppSent::EnvironmentNotSet)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should require block" do
|
21
|
+
expect { AppSent.init(@right_params) }.to raise_exception(AppSent::BlockRequired)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should save config path to @@config_path" do
|
25
|
+
AppSent.init(@right_params) do; end
|
26
|
+
AppSent.config_path.should eq(@fixtures_path)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should save environment to @@environment" do
|
30
|
+
AppSent.init(@right_params) do; end
|
31
|
+
AppSent.send(:class_variable_get,:@@environment).should eq('test')
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should save array of configs to @@configs" do
|
35
|
+
expect {
|
36
|
+
AppSent.init(@right_params) do
|
37
|
+
config1
|
38
|
+
config2
|
39
|
+
config3
|
40
|
+
end
|
41
|
+
}.to raise_exception(AppSent::Error)
|
42
|
+
AppSent.config_files.should eq(%w(config1 config2 config3))
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should create corresponding constants with values" do
|
46
|
+
AppSent.init(@right_params) do
|
47
|
+
simple_config
|
48
|
+
database do
|
49
|
+
username :type => String
|
50
|
+
password :type => String
|
51
|
+
port :type => Fixnum
|
52
|
+
end
|
53
|
+
|
54
|
+
simple_config_with_just_type :type => Array
|
55
|
+
end
|
56
|
+
|
57
|
+
AppSent::SIMPLE_CONFIG.should eq({:a=>1, :b=>'2'})
|
58
|
+
AppSent::DATABASE.should eq({:username => 'user', :password => 'pass', :port => 100500})
|
59
|
+
AppSent::SIMPLE_CONFIG_WITH_JUST_TYPE.should eq([1,2,3])
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'AppSent.new' do
|
65
|
+
|
66
|
+
subject { AppSent.new }
|
67
|
+
|
68
|
+
%w(all_valid? load! full_error_message).each do |method|
|
69
|
+
it { should respond_to(method) }
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AppSent::ConfigFile do
|
4
|
+
|
5
|
+
subject { described_class }
|
6
|
+
|
7
|
+
before :each do
|
8
|
+
@params = ['/path/to/config','config_name',:environment, Hash]
|
9
|
+
@fake_config_filename = '/path/to/config/config_name.yml'
|
10
|
+
end
|
11
|
+
|
12
|
+
context ".new" do
|
13
|
+
|
14
|
+
%w(valid? options constantized error_message).each do |method|
|
15
|
+
it { subject.new(*@params).should respond_to(method)}
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should raise exception if type is not hash and block given" do
|
19
|
+
block = lambda {}
|
20
|
+
@params[-1] = Array
|
21
|
+
expect { subject.new(*@params,&block) }.to raise_exception(/params Array and block given/)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
context "#valid?" do
|
27
|
+
|
28
|
+
context "should be true" do
|
29
|
+
|
30
|
+
it "if config exists and environment presence(without type)(no values)" do
|
31
|
+
YAML.should_receive(:load_file).once.with(@fake_config_filename).and_return('environment' => {:a=>100500})
|
32
|
+
subject.new(*@params).should be_valid
|
33
|
+
end
|
34
|
+
|
35
|
+
it "if config exists and environment presence(with type specified)(no values)" do
|
36
|
+
@params[-1]=Array
|
37
|
+
YAML.should_receive(:load_file).once.with(@fake_config_filename).and_return(:environment => [1,2,3])
|
38
|
+
subject.new(*@params).should be_valid
|
39
|
+
end
|
40
|
+
|
41
|
+
it "if config exists and environment presence(with values)" do
|
42
|
+
values_block = lambda {
|
43
|
+
value :type => String
|
44
|
+
}
|
45
|
+
YAML.should_receive(:load_file).once.with(@fake_config_filename).and_return('environment' => {:value=>'100500'})
|
46
|
+
subject.new(*@params,&values_block).should be_valid
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
context "should be false" do
|
52
|
+
|
53
|
+
it "if config does not exists" do
|
54
|
+
YAML.should_receive(:load_file).once.with(@fake_config_filename).and_raise(Errno::ENOENT)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "if environment does not presence in config" do
|
58
|
+
YAML.should_receive(:load_file).once.with(@fake_config_filename).and_return('other_environment' => {:a=>100500})
|
59
|
+
end
|
60
|
+
|
61
|
+
it "if wrong type" do
|
62
|
+
@params[-1] = Array
|
63
|
+
YAML.should_receive(:load_file).once.with(@fake_config_filename).and_return(:environment => 123)
|
64
|
+
end
|
65
|
+
|
66
|
+
after :each do
|
67
|
+
subject.new(*@params).should_not be_valid
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AppSent::ConfigValue do
|
4
|
+
|
5
|
+
subject { described_class }
|
6
|
+
|
7
|
+
before :each do
|
8
|
+
@params = [ 'param_name', String, 'some string', 'description string', 'example string' ]
|
9
|
+
end
|
10
|
+
|
11
|
+
context ".new" do
|
12
|
+
|
13
|
+
%w(valid? child_options error_message).each do |method|
|
14
|
+
it { subject.new(*@params).should respond_to(method)}
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should raise exception if unsupported type passed" do
|
18
|
+
expect { subject.new('parameter','asd','data',nil,nil) }.to raise_exception(/data type should be ruby class!/)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should raise exception if type is not hash and block given" do
|
22
|
+
block = lambda {}
|
23
|
+
@params[1] = Array
|
24
|
+
expect { subject.new(*@params,&block) }.to raise_exception(/params Array and block given/)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
context "#valid?" do
|
30
|
+
|
31
|
+
context "should return false" do
|
32
|
+
|
33
|
+
it "if entry does not presence in config file" do
|
34
|
+
subject.new('paramname',String,nil,nil,nil).should_not be_valid
|
35
|
+
end
|
36
|
+
|
37
|
+
it "if data in config file has wrong type" do
|
38
|
+
subject.new('paramname',String,2,nil,nil).should_not be_valid
|
39
|
+
end
|
40
|
+
|
41
|
+
it "if child value is not valid" do
|
42
|
+
@params[1]=Hash
|
43
|
+
@params[2]={:value => 100500}
|
44
|
+
values_block = lambda {
|
45
|
+
value :type => String
|
46
|
+
}
|
47
|
+
subject.new(*@params,&values_block).should_not be_valid
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
context "should return true" do
|
53
|
+
|
54
|
+
it "if entry presence and has right type" do
|
55
|
+
subject.new(*@params).should be_valid
|
56
|
+
end
|
57
|
+
|
58
|
+
it "if valid itself and child values valid too" do
|
59
|
+
@params[1]=Hash
|
60
|
+
@params[2]={'value' => 'some data'}
|
61
|
+
values_block = lambda {
|
62
|
+
value :type => String
|
63
|
+
}
|
64
|
+
subject.new(*@params,&values_block).should be_valid
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
context "#error_message" do
|
72
|
+
|
73
|
+
context "should generate correct error message when no data" do
|
74
|
+
|
75
|
+
it "with full description" do
|
76
|
+
subject.new('database',String,nil,'Database name','localhost').error_message.should eq(" database: localhost # does not exists(Database name), String")
|
77
|
+
end
|
78
|
+
|
79
|
+
it "without example value" do
|
80
|
+
subject.new('database',String,nil,'Database name',nil).error_message.should eq(" database: # does not exists(Database name), String")
|
81
|
+
end
|
82
|
+
|
83
|
+
it "without description" do
|
84
|
+
subject.new('database',String,nil,nil,'localhost').error_message.should eq(" database: localhost # does not exists, String")
|
85
|
+
end
|
86
|
+
|
87
|
+
it "without example and description" do
|
88
|
+
subject.new('database',String,nil,nil,nil).error_message.should eq(" database: # does not exists, String")
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
context "should generate correct error message when no data is of wrong type" do
|
94
|
+
|
95
|
+
it "with full description" do
|
96
|
+
subject.new('database',String,20,'Database name','localhost').error_message.should eq(" database: 20 # wrong type,should be String(Database name)")
|
97
|
+
end
|
98
|
+
|
99
|
+
it "without example value" do
|
100
|
+
subject.new('database',String,20,'Database name',nil).error_message.should eq(" database: 20 # wrong type,should be String(Database name)")
|
101
|
+
end
|
102
|
+
|
103
|
+
it "without description" do
|
104
|
+
subject.new('database',String,20,nil,'localhost').error_message.should eq(" database: 20 # wrong type,should be String")
|
105
|
+
end
|
106
|
+
|
107
|
+
it "without example and description" do
|
108
|
+
subject.new('database',String,20,nil,nil).error_message.should eq(" database: 20 # wrong type,should be String")
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rspec/core'
|
2
|
+
|
3
|
+
if defined?(SimpleCov)
|
4
|
+
SimpleCov.start do
|
5
|
+
add_group 'Main', '/lib/'
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'appsent'
|
10
|
+
|
11
|
+
# Requires supporting files with custom matchers and macros, etc,
|
12
|
+
# in ./support/ and its subdirectories.
|
13
|
+
Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.mock_with :rspec
|
17
|
+
end
|
data/spec/tools_spec.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Array do
|
4
|
+
|
5
|
+
context "#ask_all?" do
|
6
|
+
|
7
|
+
let(:string_receiver) { double(String) }
|
8
|
+
let(:fixnum_receiver) { double(Fixnum) }
|
9
|
+
let(:good_mock_array) { [string_receiver,string_receiver,string_receiver] }
|
10
|
+
let(:bad_mock_array) { [string_receiver,fixnum_receiver,string_receiver] }
|
11
|
+
let(:good) { ['a','b','c'] }
|
12
|
+
let(:bad) { ['a',100,'c'] }
|
13
|
+
|
14
|
+
it "should ask all elements" do
|
15
|
+
string_receiver.should_receive(:class).exactly(3).times.and_return(String)
|
16
|
+
good_mock_array.ask_all? { |e| e.class == String }.should be_true
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should ask all elements" do
|
20
|
+
string_receiver.should_receive(:class).twice.and_return(String)
|
21
|
+
fixnum_receiver.should_receive(:class).once.and_return(Fixnum)
|
22
|
+
bad_mock_array.ask_all? { |e| e.class == String }.should be_false
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should return true" do
|
26
|
+
good.ask_all? { |e| e.class == String }.should be_true
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should return false" do
|
30
|
+
bad.ask_all? { |e| e.class == String }.should be_false
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: appsent
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- kucaahbe
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-07-29 00:00:00 +03:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :development
|
33
|
+
name: yard
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :development
|
47
|
+
name: BlueCloth
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
hash: 3
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
type: :development
|
61
|
+
name: RedCloth
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
prerelease: false
|
65
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
hash: 15
|
71
|
+
segments:
|
72
|
+
- 2
|
73
|
+
- 0
|
74
|
+
- 0
|
75
|
+
version: 2.0.0
|
76
|
+
type: :development
|
77
|
+
name: rspec
|
78
|
+
version_requirements: *id004
|
79
|
+
- !ruby/object:Gem::Dependency
|
80
|
+
prerelease: false
|
81
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
hash: 3
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
version: "0"
|
90
|
+
type: :development
|
91
|
+
name: aruba
|
92
|
+
version_requirements: *id005
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
prerelease: false
|
95
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
hash: 3
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
version: "0"
|
104
|
+
type: :development
|
105
|
+
name: rake
|
106
|
+
version_requirements: *id006
|
107
|
+
description: config management solution
|
108
|
+
email:
|
109
|
+
- kucaahbe@ukr.net
|
110
|
+
executables: []
|
111
|
+
|
112
|
+
extensions: []
|
113
|
+
|
114
|
+
extra_rdoc_files: []
|
115
|
+
|
116
|
+
files:
|
117
|
+
- .gitignore
|
118
|
+
- .rspec
|
119
|
+
- .travis.yml
|
120
|
+
- .yardopts
|
121
|
+
- Gemfile
|
122
|
+
- README.md
|
123
|
+
- Rakefile
|
124
|
+
- appsent.gemspec
|
125
|
+
- features/README.md
|
126
|
+
- features/step_definitions/helper_steps.rb
|
127
|
+
- features/support/env.rb
|
128
|
+
- features/usage_example.feature
|
129
|
+
- lib/appsent.rb
|
130
|
+
- lib/appsent/config_file.rb
|
131
|
+
- lib/appsent/config_value.rb
|
132
|
+
- lib/appsent/tools.rb
|
133
|
+
- lib/appsent/version.rb
|
134
|
+
- spec/dsl_spec.rb
|
135
|
+
- spec/fixtures/database.yml
|
136
|
+
- spec/fixtures/simple_config.yml
|
137
|
+
- spec/fixtures/simple_config_with_just_type.yml
|
138
|
+
- spec/internal_api/config_file_spec.rb
|
139
|
+
- spec/internal_api/config_value_spec.rb
|
140
|
+
- spec/spec_helper.rb
|
141
|
+
- spec/tools_spec.rb
|
142
|
+
has_rdoc: true
|
143
|
+
homepage: http://github.com/kucaahbe/appsent
|
144
|
+
licenses: []
|
145
|
+
|
146
|
+
post_install_message:
|
147
|
+
rdoc_options: []
|
148
|
+
|
149
|
+
require_paths:
|
150
|
+
- lib
|
151
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
152
|
+
none: false
|
153
|
+
requirements:
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
hash: 3
|
157
|
+
segments:
|
158
|
+
- 0
|
159
|
+
version: "0"
|
160
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ">="
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
hash: 3
|
166
|
+
segments:
|
167
|
+
- 0
|
168
|
+
version: "0"
|
169
|
+
requirements: []
|
170
|
+
|
171
|
+
rubyforge_project: appsent
|
172
|
+
rubygems_version: 1.6.2
|
173
|
+
signing_key:
|
174
|
+
specification_version: 3
|
175
|
+
summary: config management solution
|
176
|
+
test_files: []
|
177
|
+
|