sexy_settings 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 ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .idea/*
3
+ .bundle
4
+ Gemfile.lock
5
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in sexy_settings.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # Sexy Settings
2
+
3
+ Flexible specifying of application settings
4
+
5
+ ## What is Sexy Settings?
6
+
7
+ It is Ruby library for flexible specifying of application settings different ways:
8
+
9
+ * from YAML file(default and custom settings)
10
+ * from command line
11
+
12
+ ### What's new in 0.0.1
13
+
14
+ Init version with base functionality
15
+
16
+ ## Getting Started
17
+
18
+ ### Prerequisites
19
+
20
+ It was tested with Ruby 1.9.2, but assumes it should work with other Ruby version as well
21
+ ### Installation
22
+
23
+ > gem install sexy_settings
24
+
25
+ ### Configuration
26
+
27
+ Create 2 configuration files, for default and custom settings. For instance,
28
+
29
+ ```
30
+ config\
31
+ default.yaml
32
+ overwritten.yaml
33
+ ```
34
+
35
+ Place next code to boot executable ruby file:
36
+
37
+ ```ruby
38
+ require 'sexy_settings'
39
+
40
+ SexySettings.configure do |config|
41
+ config.path_to_default_settings = File.expand_path("config.yaml", File.join(File.dirname(__FILE__), '..', 'config')) # 'default.yml' by default
42
+ config.path_to_custom_settings = File.expand_path("overwritten.yaml", File.join(File.dirname(__FILE__), '..', 'config')) # 'custom.yml' by default
43
+ config.path_to_project = File.dirname(__FILE__) # '.' by default
44
+ config.env_variable_with_options = 'OPTIONS' # 'OPTS' by default
45
+ cmd_line_option_delimiter = '$$$' # ',' by default
46
+ end
47
+ ```
48
+
49
+ Specify shortcut method for Settings object:
50
+
51
+ ```ruby
52
+ def settings
53
+ SexySettings::Base.instance()
54
+ end
55
+ ```
56
+
57
+ ### Using
58
+
59
+ There are 4 possible values of settings
60
+ The priority ranks with respect to the setting places are as follows:
61
+
62
+ > **command line** < **custom** < **default** < **nil**_(in case setting was not specified anywhere)_
63
+
64
+ Thus, specifying some setting in command line will override the same setting value specified in <_default config file_> or <_custom config file_>
65
+
66
+ Example:
67
+
68
+ _default.yaml_
69
+
70
+ ```yaml
71
+ foo: bar
72
+ foo1: default ${foo}
73
+ foo2: default value
74
+ ```
75
+
76
+ _overwritten.yaml_
77
+
78
+ ```yaml
79
+ foo1: custom ${foo}
80
+ ```
81
+
82
+ Set environment variable:
83
+
84
+ > OPTIONS="foo2=10$$$foo3=:hi$$$foo4=true"
85
+
86
+ ```ruby
87
+ puts settings.foo # returns 'bar'
88
+ puts settings.foo1 # returns 'custom foo'
89
+ puts settings.foo2 # returns 10
90
+ puts settings.foo3 # returns :hi
91
+ puts settings.foo4 # returns true
92
+ ```
93
+
94
+
95
+ ## Hints
96
+
97
+ * Add <_default config file_> under version control system
98
+ * Add <_custom config file_> to ignore list
99
+ * Use command line with using Environment Variable for quick specifying setting in your Continuous Integration System
100
+ * Use next code for output all settings as pretty formatted text:
101
+
102
+ ```ruby
103
+ puts settings.as_formatted_text
104
+ ```
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ desc 'Spec all functionality of gem'
4
+ task :spec_all do
5
+ system("rspec spec/*/")
6
+ end
@@ -0,0 +1,6 @@
1
+ require 'sexy_settings/version'
2
+ require 'sexy_settings/configuration'
3
+ require 'sexy_settings/base'
4
+ require 'sexy_settings/core'
5
+
6
+
@@ -0,0 +1,113 @@
1
+ require 'singleton'
2
+ require 'yaml'
3
+
4
+ module SexySettings
5
+ class Base
6
+ include Singleton
7
+ attr_reader :default, :custom, :command_line, :all
8
+
9
+ # Priorities: command_line > custom.yml > default.yml > nil
10
+ def initialize
11
+ config = SexySettings.configuration
12
+ @default = load_settings(config.path_to_default_settings)
13
+ @custom = load_settings(config.path_to_custom_settings)
14
+ @all = @default.merge(@custom)
15
+ @command_line = {}
16
+ unless ENV[config.env_variable_with_options].nil?
17
+ ENV[config.env_variable_with_options].split(config.cmd_line_option_delimiter).each{|opt_line| parse_setting(opt_line) if opt_line }
18
+ end
19
+ @all.merge!(@command_line)
20
+ @all.each_pair{|k, v| @all[k] = get_compound_value(v)}
21
+ end
22
+
23
+ def as_formatted_text(which=:all)
24
+ props_list = case which
25
+ when :all then @all
26
+ when :custom then @custom
27
+ when :default then @default
28
+ else ''
29
+ end.to_a
30
+ max_key_size = props_list.map{|el| el.first.to_s.size}.max
31
+ res = []
32
+ title = "##{' '*20}#{which.to_s.capitalize} Settings#{' '*21}#"
33
+ sharp_line = '#'*title.size
34
+ res << sharp_line
35
+ res << title
36
+ res << sharp_line
37
+ res << ''
38
+ res += props_list.map{|el| "#{indent}#{el[0]}#{indent + indent(max_key_size - el[0].to_s.size)}=#{indent}#{el[1]}"}.sort
39
+ res << ''
40
+ res.join("\n")
41
+ end
42
+
43
+ private
44
+
45
+ def load_settings(path)
46
+ File.exists?(path) ? YAML::load_file(path) : {}
47
+ end
48
+
49
+ def indent(space_count=nil)
50
+ " "*(space_count.nil? ? 2 : space_count)
51
+ end
52
+
53
+ # Parse the compound setting.
54
+ # Parts of this config_parser must be defined earlier.
55
+ # You can define an option as option=${another_option_name}/something
56
+ def get_compound_value(value)
57
+ if /\$\{(.*?)\}/.match(value.to_s)
58
+ var = /\$\{(.*?)\}/.match(value.to_s)[1]
59
+ exist_var = @all[var]
60
+ raise ArgumentError, "Did you define this setting '#{var}' before?" if exist_var.nil?
61
+ value["${#{var}}"] = exist_var.to_s if var
62
+ get_compound_value(value)
63
+ end
64
+ value
65
+ end
66
+
67
+ def method_missing(name, *args)
68
+ if name.to_s =~ /=$/
69
+ @all[name.to_s] = args[0] if @all.has_key?(name.to_s)
70
+ else
71
+ @all[name.to_s]
72
+ end
73
+ end
74
+
75
+ # Try to convert a value from String to other types (int, boolean, float)
76
+ # Return the value as String if all tries have failed.
77
+ # If the value is not of String type, return it as is.
78
+ def try_convert_value(value)
79
+ if value.class == String
80
+ if /^[0-9]+$/.match(value) #int
81
+ value.to_i
82
+ elsif /^[0-9]+(\.)[0-9]*$/.match(value) #float
83
+ value.to_f
84
+ elsif (value.downcase == 'true') #boolean
85
+ true
86
+ elsif (value.downcase == 'false') #boolean
87
+ false
88
+ elsif /^:(.+)$/.match(value)
89
+ $1.to_sym
90
+ else
91
+ value # can't parse, return String
92
+ end
93
+ else # value is not String, return it as is
94
+ value
95
+ end
96
+ end
97
+
98
+ # Try to parse the setting's line
99
+ # Delimiter is "=", ignores double quotes in the start and end positions
100
+ def parse_setting(line)
101
+ raise ArgumentError, "Invalid pair: #{line}. Expected: name=value" unless line["="]
102
+ param, value = line.split(/\s*=\s*/, 2)
103
+ var_name = param.chomp.strip
104
+ value = value.chomp.strip if value
105
+ new_value = ''
106
+ if (value)
107
+ new_value = (value =~ /^["](.*)["]$/) ? $1 : value
108
+ end
109
+ new_value = try_convert_value(new_value)
110
+ @command_line[var_name] = new_value
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,21 @@
1
+ module SexySettings
2
+ class Configuration
3
+ DEFAULT_OPTIONS = {
4
+ :path_to_default_settings => "default.yml",
5
+ :path_to_custom_settings => "custom.yml",
6
+ :path_to_project => '.',
7
+ :env_variable_with_options => 'OPTS',
8
+ :cmd_line_option_delimiter => ','
9
+ }
10
+ DEFAULT_OPTIONS.keys.each{|option| attr_writer option}
11
+
12
+ def initialize
13
+ DEFAULT_OPTIONS.keys.each do |method|
14
+ self.class.send(:define_method, method) do
15
+ self.instance_variable_get("@#{method}") || DEFAULT_OPTIONS[method]
16
+ end
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,27 @@
1
+ module SexySettings
2
+ # Used internally to ensure examples get reloaded between multiple runs in
3
+ # the same process.
4
+ def self.reset
5
+ self.configuration.class::DEFAULT_OPTIONS.keys.each{|key| self.configuration.send("#{key}=", nil)}
6
+ end
7
+
8
+ # Returns the global configuration object
9
+ def self.configuration
10
+ @configuration ||= SexySettings::Configuration.new
11
+ end
12
+
13
+ # Yields the global configuration object
14
+ #
15
+ # == Examples
16
+ #
17
+ # SexySettings.configure do |config|
18
+ # config.env_variable_with_options = 'OPTIONS'
19
+ # end
20
+ def self.configure
21
+ if block_given?
22
+ yield configuration
23
+ else
24
+ self.configuration
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ module SexySettings
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "sexy_settings/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "sexy_settings"
7
+ s.version = SexySettings::VERSION
8
+ s.authors = ["Roman Parashchenko"]
9
+ s.email = ["romikoops1@gmail.com"]
10
+ s.homepage = "https://github.com/romikoops/sexy_settings"
11
+ s.summary = %q{Flexible specifying of application settings}
12
+ s.description = %q{Library for flexible specifying of application settings different ways}
13
+ s.rubyforge_project = "sexy_settings"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+ s.add_development_dependency 'rspec'
20
+ end
@@ -0,0 +1,3 @@
1
+ default_property: default DEFAULT value
2
+ overwritten_property: default OVERWRITTEN value
3
+ console_property: default CONSOLE value
@@ -0,0 +1,2 @@
1
+ overwritten_property: overwritten OVERWRITTEN value
2
+ console_property: overwritten CONSOLE value
@@ -0,0 +1,120 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Base' do
4
+ before :all do
5
+ SexySettings.configure do |config|
6
+ config.path_to_default_settings = File.expand_path("config.yaml", File.join(File.dirname(__FILE__), '..', '_config'))
7
+ config.path_to_custom_settings = File.expand_path("overwritten.yaml", File.join(File.dirname(__FILE__), '..', '_config'))
8
+ config.path_to_project = File.dirname(__FILE__)
9
+ config.env_variable_with_options = 'OPTIONS'
10
+ end
11
+ if ENV.has_key?(SexySettings.configuration.env_variable_with_options)
12
+ @original_options = ENV[SexySettings.configuration.env_variable_with_options]
13
+ else
14
+ @original_options = nil
15
+ end
16
+ ENV[SexySettings.configuration.env_variable_with_options] = "console_property=console CONSOLE value"
17
+ @settings ||= settings
18
+ end
19
+
20
+ after :all do
21
+ @original_options = ENV[SexySettings.configuration.env_variable_with_options]
22
+ unless @original_options
23
+ ENV[SexySettings.configuration.env_variable_with_options] = @original_options
24
+ end
25
+ end
26
+
27
+ it "should be singleton object" do
28
+ SexySettings::Base.respond_to?(:instance).should be_true
29
+ SexySettings::Base.instance.is_a?(SexySettings::Base).should be_true
30
+ end
31
+
32
+ it "should have getter for default setting" do
33
+ @settings.respond_to?(:default).should be_true
34
+ expected_default_settings = {
35
+ "default_property" => "default DEFAULT value",
36
+ "overwritten_property" => "default OVERWRITTEN value",
37
+ "console_property" => "default CONSOLE value"
38
+ }
39
+ @settings.default.should == expected_default_settings
40
+ end
41
+
42
+ it "should have getter for custom setting" do
43
+ @settings.respond_to?(:default).should be_true
44
+ expected_custom_settings = {
45
+ "overwritten_property" => "overwritten OVERWRITTEN value",
46
+ "console_property" => "overwritten CONSOLE value"
47
+ }
48
+ @settings.custom.should == expected_custom_settings
49
+ end
50
+
51
+ it "should have getter for all setting" do
52
+ @settings.respond_to?(:default).should be_true
53
+ expected_all_settings = {
54
+ "default_property" => "default DEFAULT value",
55
+ "overwritten_property" => "overwritten OVERWRITTEN value",
56
+ "console_property" => "console CONSOLE value"
57
+ }
58
+ @settings.all.should == expected_all_settings
59
+ end
60
+
61
+ it "should return specified pretty formatted settings for output" do
62
+ expected = <<-eos
63
+ #######################################################
64
+ # All Settings #
65
+ #######################################################
66
+
67
+ console_property = console CONSOLE value
68
+ default_property = default DEFAULT value
69
+ overwritten_property = overwritten OVERWRITTEN value
70
+ eos
71
+ @settings.as_formatted_text.should == expected
72
+ end
73
+
74
+ context "command line" do
75
+ before :all do
76
+ SexySettings.configure.env_variable_with_options = 'OPTS'
77
+ ENV['OPTS'] = "string=Test, int=1, float=1.09, boolean_true=true, boolean_false=false, symbol=:foo, reference = ${string}"
78
+ @clone_settings = settings.class.clone.instance
79
+ end
80
+
81
+ after :all do
82
+ SexySettings.configure.env_variable_with_options = 'OPTIONS'
83
+ end
84
+
85
+ it "should convert command line string value to String type" do
86
+ @clone_settings.string.should == 'Test'
87
+ end
88
+
89
+ it "should convert command line integer value to Fixnum type" do
90
+ @clone_settings.int.should == 1
91
+ @clone_settings.int.class.should == Fixnum
92
+ end
93
+
94
+ it "should convert command line float value to Float type" do
95
+ @clone_settings.float.should == 1.09
96
+ @clone_settings.float.class.should == Float
97
+ end
98
+
99
+ it "should convert command line true value to TrueClass type" do
100
+ @clone_settings.boolean_true.should be_true
101
+ end
102
+
103
+ it "should convert command line false value to FalseClass type" do
104
+ @clone_settings.boolean_false.should be_false
105
+ @clone_settings.boolean_false.class.should == FalseClass
106
+ end
107
+
108
+ it "should convert command line symbol value to Symbol type" do
109
+ @clone_settings.symbol.should == :foo
110
+ end
111
+
112
+ it "should replace command line reference to correct value" do
113
+ @clone_settings.reference == 'Test'
114
+ end
115
+ end
116
+ end
117
+
118
+ def settings
119
+ SexySettings::Base.instance()
120
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Configuration' do
4
+ before :all do
5
+ @expected_opts = {
6
+ :path_to_default_settings => "default.yml",
7
+ :path_to_custom_settings => "custom.yml",
8
+ :path_to_project => '.',
9
+ :env_variable_with_options => 'OPTS',
10
+ :cmd_line_option_delimiter => ','
11
+ }
12
+ @config = SexySettings::Configuration.new
13
+ end
14
+
15
+ it "should have correct default options" do
16
+ SexySettings::Configuration.constants.should include(:DEFAULT_OPTIONS)
17
+ SexySettings::Configuration::DEFAULT_OPTIONS.should == @expected_opts
18
+ end
19
+
20
+ it "should have setters for all options" do
21
+ @expected_opts.keys.each{|key| @config.respond_to?("#{key}=").should be_true}
22
+ end
23
+
24
+ it "should return last value of specified option" do
25
+ new_value = 'fake'
26
+ @expected_opts.keys.each{|key| @config.send("#{key}=", new_value)}
27
+ @expected_opts.keys.each{|key| @config.send(key).should == new_value}
28
+ end
29
+
30
+ it "should return default value of specified option" do
31
+ config = SexySettings::Configuration.new
32
+ @expected_opts.keys.each{|key| config.send(key).should == @expected_opts[key]}
33
+ end
34
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Core' do
4
+ it "should have ability to reset settings" do
5
+ new_delim = '#@#'
6
+ old_delim = nil
7
+ SexySettings.configure do |config|
8
+ old_delim = config.cmd_line_option_delimiter
9
+ config.cmd_line_option_delimiter = new_delim
10
+ end
11
+ SexySettings.configuration.cmd_line_option_delimiter.should ==(new_delim)
12
+ SexySettings.reset
13
+ SexySettings.configuration.cmd_line_option_delimiter.should ==(old_delim)
14
+ end
15
+
16
+ it "should return the same configuration object each time" do
17
+ config = SexySettings.configuration
18
+ config.is_a?(SexySettings::Configuration).should be_true
19
+ config.object_id.should == SexySettings.configuration.object_id
20
+ end
21
+
22
+ it "should have ability to configure Configuration object with block" do
23
+ SexySettings.configure do |config|
24
+ config.is_a?(SexySettings::Configuration).should be_true
25
+ end
26
+ end
27
+
28
+ it "should have ability to configure Configuration object without block" do
29
+ SexySettings.configure.is_a?(SexySettings::Configuration).should be_true
30
+ end
31
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Version' do
4
+ it "should contains VERSION constant with correct format" do
5
+ SexySettings.constants.should include(:VERSION)
6
+ SexySettings::VERSION.should match(/^\d+\.\d+\.\d+$/)
7
+ end
8
+ end
@@ -0,0 +1 @@
1
+ require 'sexy_settings'
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sexy_settings
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Roman Parashchenko
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-08 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &26780748 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *26780748
25
+ description: Library for flexible specifying of application settings different ways
26
+ email:
27
+ - romikoops1@gmail.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - .gitignore
33
+ - Gemfile
34
+ - README.md
35
+ - Rakefile
36
+ - lib/sexy_settings.rb
37
+ - lib/sexy_settings/base.rb
38
+ - lib/sexy_settings/configuration.rb
39
+ - lib/sexy_settings/core.rb
40
+ - lib/sexy_settings/version.rb
41
+ - sexy_settings.gemspec
42
+ - spec/_config/config.yaml
43
+ - spec/_config/overwritten.yaml
44
+ - spec/sexy_settings/base_spec.rb
45
+ - spec/sexy_settings/configuration_spec.rb
46
+ - spec/sexy_settings/core_spec.rb
47
+ - spec/sexy_settings/version_spec.rb
48
+ - spec/spec_helper.rb
49
+ homepage: https://github.com/romikoops/sexy_settings
50
+ licenses: []
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubyforge_project: sexy_settings
69
+ rubygems_version: 1.8.11
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: Flexible specifying of application settings
73
+ test_files: []