app-config 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,24 @@
1
+ *.gem
2
+ *.rbc
3
+ *.swp
4
+ *.tmproj
5
+ *~
6
+ .DS_Store
7
+ .\#*
8
+ .bundle
9
+ .config
10
+ .yardoc
11
+ Gemfile.lock
12
+ InstalledFiles
13
+ \#*
14
+ _yardoc
15
+ coverage
16
+ doc/
17
+ lib/bundler/man
18
+ pkg
19
+ rdoc
20
+ spec/reports
21
+ test/tmp
22
+ test/version_tmp
23
+ tmp
24
+ tmtags
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format=nested
3
+ --backtrace
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Dan Sosedoff
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,113 @@
1
+ = AppConfig
2
+
3
+ AppConfig is a library to manage your (web) application dynamic settings with flexible access and configuration strategy.
4
+
5
+ Primary datasource for AppConfig is an ActiveRecord model.
6
+
7
+ == Installation
8
+
9
+ via rubygems:
10
+
11
+ gem install app-config
12
+
13
+ via github:
14
+
15
+ git clone git://github.com/sosedoff/app-config.git
16
+ cd app-config
17
+ gem build
18
+ gem install app-config-x.y.z.gem
19
+
20
+ == Supported platforms
21
+
22
+ - Ruby 1.8.7
23
+ - Ruby REE 1.8.7
24
+ - Ruby 1.9.2
25
+
26
+ == Data Formats
27
+
28
+ You can use following formats:
29
+ - String
30
+ - Boolean
31
+ - Array
32
+ - Hash
33
+
34
+ String format is a default format. Everything is a string by default.
35
+
36
+ Boolean format is just a flag, values 'true', 'on', 'yes', 'y', '1' are equal to True. Everything else is False.
37
+
38
+ Array format is a multiline text which is transformed into array. Each evelemnt will be trimmed. Empty strings are ignored.
39
+
40
+ Hash format is special key-value string, "foo: bar, user: username", which is transformed into Hash instance.
41
+ Only format "keyname: value, keyname2: value2" is supported. No nested hashes allowed.
42
+
43
+ == Usage
44
+
45
+ AppConfig is designed to work with ActiveRecord model. Only ActiveRecord >= 3.0.0 is supported.
46
+
47
+ By default model "Setting" will be used as a data source.
48
+
49
+ Default migration:
50
+
51
+ class CreateSettings < ActiveRecord::Migration
52
+ def self.up
53
+ create_table :settings do |t|
54
+ t.string :keyname, :null => false, :limit => 64
55
+ t.string :name, :null => false, :limit => 64
56
+ t.text :value, :null => false
57
+ t.string :value_format, :limit => 64, :default => "string"
58
+ t.string :description, :limit => 512, :null => false
59
+ t.timestamps
60
+ end
61
+ end
62
+
63
+ def self.down
64
+ drop_table :settings
65
+ end
66
+ end
67
+
68
+ Now, configure:
69
+
70
+ AppConfig.configure
71
+
72
+ If your settings model has a different schema, you can redefine columns:
73
+
74
+ AppConfig.configure(
75
+ :model => Setting, # define your model as a source
76
+ :key => 'KEYNAME_FIELD', # field that contains name
77
+ :format => 'FORMAT_FIELD', # field that contains key format
78
+ :value => 'VALUE_FIELD', #field that contains value data
79
+ )
80
+
81
+ Load all settings somewhere in your application. In Rails it should be initializer file.
82
+
83
+ AppConfig.load
84
+
85
+ Configuration in Rails 3: (you can put this into environment/ENV or application.rb)
86
+ Make sure your application does not have any critical parts depending on AppConfig at startup.
87
+
88
+ config.after_initialize do
89
+ AppConfig.configure(:model => Setting)
90
+ AppConfig.load if Setting.table_exists?
91
+ end
92
+
93
+ AppConfig gives you 3 ways to access variables:
94
+
95
+ AppConfig.my_setting # method-like
96
+ AppConfig[:my_setting] # hash-like by symbol key
97
+ AppConfig['my_setting'] # hash-like by string key
98
+
99
+ You can define settings items manually. NOTE: THESE KEYS WILL BE REMOVED ON RELOAD/LOAD.
100
+
101
+ AppConfig.set('KEYNAME, 'VALUE', 'FORMAT')
102
+
103
+ Everytime you change your settings on the fly, use reload:
104
+
105
+ AppConfig.reload
106
+
107
+ Cleanup everything:
108
+
109
+ AppConfig.flush
110
+
111
+ == Copyright
112
+
113
+ Copyright (c) 2011 Dan Sosedoff.
@@ -0,0 +1,9 @@
1
+ require 'bundler'
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:test) do |t|
5
+ t.pattern = 'spec/*_spec.rb'
6
+ t.verbose = false
7
+ end
8
+
9
+ task :default => :test
@@ -0,0 +1,27 @@
1
+ require File.expand_path('../lib/app-config/version', __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "app-config"
5
+ s.version = AppConfig::VERSION.dup
6
+ s.summary = "Configurable application settings"
7
+ s.description = "Flexible and simple configuration settings for your Rails/Sinatra applications."
8
+ s.homepage = "http://github.com/sosedoff/app-config"
9
+ s.authors = ["Dan Sosedoff"]
10
+ s.email = ["dan.sosedoff@gmail.com"]
11
+ s.license = "MIT"
12
+
13
+ s.add_development_dependency 'rake', '~> 0.8'
14
+ s.add_development_dependency 'rspec', '~> 2.6'
15
+ s.add_development_dependency 'simplecov', '~> 0.4'
16
+ s.add_development_dependency 'sqlite3', '~> 1.3'
17
+
18
+ s.add_runtime_dependency 'activerecord', '~> 3.0.0'
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
23
+ s.require_paths = ["lib"]
24
+
25
+ s.platform = Gem::Platform::RUBY
26
+ s.required_rubygems_version = Gem::Requirement.new('>= 1.3.6') if s.respond_to? :required_rubygems_version=
27
+ end
@@ -1,4 +1,5 @@
1
- require 'app-config/version'
1
+ require 'active_record'
2
+
2
3
  require 'app-config/errors'
3
4
  require 'app-config/processor'
4
5
  require 'app-config/app-config'
@@ -1,7 +1,22 @@
1
1
  module AppConfig
2
2
  extend AppConfig::Processor
3
3
 
4
- FORMATS = ['string', 'array', 'hash', 'boolean']
4
+ FORMATS = ['string', 'array', 'hash', 'boolean'].freeze
5
+ RESTRICTED_KEYS = [
6
+ 'id',
7
+ 'to_s',
8
+ 'configure',
9
+ 'load',
10
+ 'flush',
11
+ 'reload',
12
+ 'keys',
13
+ 'empty?',
14
+ 'method_missing',
15
+ 'exist?',
16
+ 'source_model',
17
+ 'configuration'
18
+ ].freeze
19
+
5
20
  @@options = {}
6
21
  @@records = {}
7
22
 
@@ -13,6 +28,11 @@ module AppConfig
13
28
  @@options[:format] = opts[:format] || 'value_format'
14
29
  end
15
30
 
31
+ # Returns a configuration options
32
+ def self.configuration
33
+ @@options
34
+ end
35
+
16
36
  # Load and process application settings
17
37
  def self.load
18
38
  @@records = fetch
@@ -31,7 +51,7 @@ module AppConfig
31
51
 
32
52
  # Manually set (or add) a key
33
53
  def self.set_key(keyname, value, format='string')
34
- raise InvalidKeyName, "Invalid key name: #{keyname}" if self.respond_to?(keyname)
54
+ raise InvalidKeyName, "Invalid key name: #{keyname}" if RESTRICTED_KEYS.include?(keyname)
35
55
  @@records[keyname] = process(value, format)
36
56
  end
37
57
 
@@ -60,12 +80,25 @@ module AppConfig
60
80
  @@records.key?(key)
61
81
  end
62
82
 
83
+ # Returns class that defined as source
84
+ def self.source_model
85
+ @@options[:model]
86
+ end
87
+
63
88
  protected
64
89
 
90
+ # Checks the column structure of the source model
91
+ def self.check_structure
92
+ klass = @@options[:model]
93
+ keys = [@@options[:key], @@options[:value], @@options[:format]]
94
+ return (keys - klass.column_names).empty?
95
+ end
96
+
65
97
  # Fetch data from model
66
98
  def self.fetch
67
99
  raise InvalidSource, 'Model is not defined!' if @@options[:model].nil?
68
100
  raise InvalidSource, 'Model was not found!' unless @@options[:model].superclass == ActiveRecord::Base
101
+ raise InvalidSource, 'Model fields are invalid!' unless check_structure
69
102
 
70
103
  records = {}
71
104
 
@@ -1,3 +1,3 @@
1
1
  module AppConfig
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.1.1'.freeze
3
3
  end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'AppConfig' do
4
+ before :all do
5
+ establish_connection
6
+ init_settings_table
7
+
8
+ Setting.create(:keyname => 'foo', :value => 'bar', :value_format => 'string')
9
+ AppConfig.configure
10
+ end
11
+
12
+ it 'should validate default source fields' do
13
+ ['keyname', 'value', 'value_format'].each do |c|
14
+ AppConfig.source_model.column_names.include?(c).should == true
15
+ end
16
+ end
17
+
18
+ it 'should have no configuration options' do
19
+ AppConfig.empty?.should == true
20
+ end
21
+
22
+ it 'should raise InvalidKeyName when setting value of a restricted key' do
23
+ keys = AppConfig::RESTRICTED_KEYS
24
+ keys.each do |k|
25
+ proc { AppConfig.set_key(k, 'foo') }.should raise_error AppConfig::InvalidKeyName
26
+ end
27
+ end
28
+
29
+ it 'should load setting item manually' do
30
+ AppConfig.set_key('email_notifications', 'noreply@foo.com')
31
+ AppConfig.exist?('email_notifications').should == true
32
+ end
33
+
34
+ it 'should provide hash-like and object-like access to options' do
35
+ AppConfig.set_key('foo', 'bar')
36
+ AppConfig['foo'].should == 'bar'
37
+ AppConfig[:foo].should == 'bar'
38
+ AppConfig.foo.should == 'bar'
39
+ end
40
+
41
+ it 'should return nil if option does not exist' do
42
+ AppConfig[:foobar].should == nil
43
+ AppConfig['foobar'].should == nil
44
+ AppConfig.foobar.should == nil
45
+ end
46
+
47
+ it 'should erase all settings' do
48
+ AppConfig.flush
49
+ AppConfig.empty?.should == true
50
+ end
51
+
52
+ it 'should load data from database' do
53
+ AppConfig.load
54
+ AppConfig.keys.size.should == 1
55
+ AppConfig.foo.should == 'bar'
56
+ end
57
+
58
+ it 'should reload data' do
59
+ s = Setting.find_by_keyname('foo')
60
+ s.update_attribute('value', 'TEST')
61
+
62
+ AppConfig.reload
63
+ AppConfig.keys.size.should == 1
64
+ AppConfig.foo.should == 'TEST'
65
+ end
66
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Processor' do
4
+ include AppConfig::Processor
5
+
6
+ it 'should raise InvalidType expection on invalid data type' do
7
+ proc { process('foo', 'bar') }.should raise_error AppConfig::InvalidType
8
+ end
9
+
10
+ it 'should return an empty object if data is nil' do
11
+ process(nil, 'string').should == ''
12
+ process(nil, 'array').should == []
13
+ process(nil, 'boolean').should == false
14
+ process(nil, 'hash').should == {}
15
+ end
16
+
17
+ it 'should process and trim string data' do
18
+ data = " Foo bar \r\n "
19
+ result = process(data, 'string')
20
+
21
+ result.should be_a_kind_of String
22
+ result.empty?.should_not be true
23
+ result.should == 'Foo bar'
24
+ end
25
+
26
+ it 'should process and trim array data' do
27
+ data = "str1\r\nstr2\r\nstr3 \r\n\r\n"
28
+ target = ['str1', 'str2', 'str3']
29
+
30
+ result = process(data, 'array')
31
+ result.should be_a_kind_of Array
32
+ result.size.should == 3
33
+ result.should == target
34
+ end
35
+
36
+ it 'should process empty array data' do
37
+ result = process("\r\n", 'array')
38
+ result.should be_a_kind_of Array
39
+ result.size.should == 0
40
+ end
41
+
42
+ it 'should process hash data' do
43
+ data = "key: value1, key2: value2, key3: hello world, key 4: text data ,"
44
+ result = process(data, 'hash')
45
+
46
+ result.should be_a_kind_of Hash
47
+ result.empty?.should_not == true
48
+ result.size.should == 4
49
+ result.keys.should include 'key 4'
50
+ result['key 4'].should == 'text data'
51
+ end
52
+
53
+ it 'should process a boolean data' do
54
+ ['true', 'on', 'yes', 'y', '1'].each { |v| process(v, 'boolean').should == true }
55
+ ['', 'no', 'n', '0', 'other'].each { |v| process(v, 'boolean').should == false }
56
+ end
57
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'AppConfig' do
4
+ before :all do
5
+ establish_connection
6
+ init_custom_table
7
+
8
+ Item.create(:name => 'foo', :data => 'bar', :fmt => 'string')
9
+ end
10
+
11
+ it 'should raise InvalidSource error if source model is not defined' do
12
+ AppConfig.configure
13
+ proc { AppConfig.load }.should raise_error AppConfig::InvalidSource
14
+ end
15
+
16
+ it 'should raise InvalidSource error if source model was not found' do
17
+ AppConfig.configure(:model => FakeModel)
18
+ proc { AppConfig.load }.should raise_error AppConfig::InvalidSource
19
+ end
20
+
21
+ it 'should validate custom source fields' do
22
+ AppConfig.configure(
23
+ :model => Item,
24
+ :key => 'name',
25
+ :value => 'data',
26
+ :format => 'fmt'
27
+ )
28
+
29
+ proc { AppConfig.load }.should_not raise_error AppConfig::InvalidSource
30
+
31
+ AppConfig.configure(
32
+ :model => Item,
33
+ :key => 'fake_field',
34
+ :value => 'data',
35
+ :format => 'fmt'
36
+ )
37
+
38
+ proc { AppConfig.load }.should raise_error AppConfig::InvalidSource
39
+ end
40
+
41
+ it 'should load data from custom model' do
42
+
43
+ AppConfig.configure(
44
+ :model => Item,
45
+ :key => 'name',
46
+ :value => 'data',
47
+ :format => 'fmt'
48
+ )
49
+
50
+ AppConfig.load
51
+ AppConfig.exist?('foo').should == true
52
+ AppConfig.foo.should == 'bar'
53
+ end
54
+ end
@@ -0,0 +1,44 @@
1
+ $:.unshift File.expand_path("../..", __FILE__)
2
+
3
+ require 'simplecov'
4
+ SimpleCov.start do
5
+ add_group 'AppConfig', 'lib/app-config'
6
+ end
7
+
8
+ require 'app-config'
9
+
10
+ class Setting < ActiveRecord::Base
11
+ validates_presence_of :keyname, :value, :value_format
12
+ validates_uniqueness_of :keyname
13
+ end
14
+
15
+ class Item < ActiveRecord::Base ; end
16
+
17
+ class FakeModel ; end
18
+
19
+ def establish_connection
20
+ ActiveRecord::Base.establish_connection(
21
+ :adapter => "sqlite3",
22
+ :database => ":memory:"
23
+ )
24
+ end
25
+
26
+ def init_settings_table
27
+ ActiveRecord::Schema.define do
28
+ create_table :settings do |t|
29
+ t.string :keyname
30
+ t.string :value
31
+ t.string :value_format
32
+ end
33
+ end
34
+ end
35
+
36
+ def init_custom_table
37
+ ActiveRecord::Schema.define do
38
+ create_table :items do |t|
39
+ t.string :name
40
+ t.string :data
41
+ t.string :fmt
42
+ end
43
+ end
44
+ end
metadata CHANGED
@@ -1,13 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: app-config
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
5
4
  prerelease:
6
- segments:
7
- - 0
8
- - 1
9
- - 0
10
- version: 0.1.0
5
+ version: 0.1.1
11
6
  platform: ruby
12
7
  authors:
13
8
  - Dan Sosedoff
@@ -15,27 +10,67 @@ autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
12
 
18
- date: 2011-02-11 00:00:00 -06:00
13
+ date: 2011-06-15 00:00:00 -05:00
19
14
  default_executable:
20
15
  dependencies:
21
16
  - !ruby/object:Gem::Dependency
22
- name: activerecord
17
+ name: rake
23
18
  prerelease: false
24
19
  requirement: &id001 !ruby/object:Gem::Requirement
25
20
  none: false
26
21
  requirements:
27
- - - ">="
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: "0.8"
25
+ type: :development
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ~>
34
+ - !ruby/object:Gem::Version
35
+ version: "2.6"
36
+ type: :development
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: simplecov
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: "0.4"
47
+ type: :development
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: sqlite3
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ version: "1.3"
58
+ type: :development
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: activerecord
62
+ prerelease: false
63
+ requirement: &id005 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ~>
28
67
  - !ruby/object:Gem::Version
29
- hash: 7
30
- segments:
31
- - 3
32
- - 0
33
- - 0
34
68
  version: 3.0.0
35
69
  type: :runtime
36
- version_requirements: *id001
37
- description: Flexible and simple configuration settings for your Rails/Sinatra applications
38
- email: dan.sosedoff@gmail.com
70
+ version_requirements: *id005
71
+ description: Flexible and simple configuration settings for your Rails/Sinatra applications.
72
+ email:
73
+ - dan.sosedoff@gmail.com
39
74
  executables: []
40
75
 
41
76
  extensions: []
@@ -43,11 +78,22 @@ extensions: []
43
78
  extra_rdoc_files: []
44
79
 
45
80
  files:
81
+ - .gitignore
82
+ - .rspec
83
+ - Gemfile
84
+ - LICENSE.txt
85
+ - README.rdoc
86
+ - Rakefile
87
+ - app-config.gemspec
46
88
  - lib/app-config.rb
47
- - lib/app-config/version.rb
89
+ - lib/app-config/app-config.rb
48
90
  - lib/app-config/errors.rb
49
91
  - lib/app-config/processor.rb
50
- - lib/app-config/app-config.rb
92
+ - lib/app-config/version.rb
93
+ - spec/config_spec.rb
94
+ - spec/processor_spec.rb
95
+ - spec/source_spec.rb
96
+ - spec/spec_helper.rb
51
97
  has_rdoc: true
52
98
  homepage: http://github.com/sosedoff/app-config
53
99
  licenses:
@@ -62,25 +108,22 @@ required_ruby_version: !ruby/object:Gem::Requirement
62
108
  requirements:
63
109
  - - ">="
64
110
  - !ruby/object:Gem::Version
65
- hash: 3
66
- segments:
67
- - 0
68
111
  version: "0"
69
112
  required_rubygems_version: !ruby/object:Gem::Requirement
70
113
  none: false
71
114
  requirements:
72
115
  - - ">="
73
116
  - !ruby/object:Gem::Version
74
- hash: 3
75
- segments:
76
- - 0
77
- version: "0"
117
+ version: 1.3.6
78
118
  requirements: []
79
119
 
80
120
  rubyforge_project:
81
- rubygems_version: 1.4.1
121
+ rubygems_version: 1.6.2
82
122
  signing_key:
83
123
  specification_version: 3
84
124
  summary: Configurable application settings
85
- test_files: []
86
-
125
+ test_files:
126
+ - spec/config_spec.rb
127
+ - spec/processor_spec.rb
128
+ - spec/source_spec.rb
129
+ - spec/spec_helper.rb