iron-settings 1.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.
- data/.rspec +1 -0
- data/History.txt +9 -0
- data/LICENSE +20 -0
- data/README.rdoc +133 -0
- data/Version.txt +1 -0
- data/db/settings_migration.rb +23 -0
- data/lib/iron/rake_loader.rb +6 -0
- data/lib/iron/settings.rb +161 -0
- data/lib/iron/settings/builder.rb +72 -0
- data/lib/iron/settings/class_level.rb +122 -0
- data/lib/iron/settings/cursor.rb +170 -0
- data/lib/iron/settings/db_store.rb +57 -0
- data/lib/iron/settings/db_value.rb +33 -0
- data/lib/iron/settings/entry.rb +26 -0
- data/lib/iron/settings/group.rb +102 -0
- data/lib/iron/settings/instance_level.rb +98 -0
- data/lib/iron/settings/node.rb +46 -0
- data/lib/iron/settings/root.rb +15 -0
- data/lib/iron/settings/static_store.rb +85 -0
- data/lib/iron/settings/value_store.rb +88 -0
- data/lib/tasks/settings.rake +65 -0
- data/spec/samples/static-test +4 -0
- data/spec/samples/static-test-2 +1 -0
- data/spec/settings/builder_spec.rb +48 -0
- data/spec/settings/class_level_spec.rb +46 -0
- data/spec/settings/cursor_spec.rb +96 -0
- data/spec/settings/db_store_spec.rb +12 -0
- data/spec/settings/db_value_spec.rb +58 -0
- data/spec/settings/entry_spec.rb +34 -0
- data/spec/settings/group_spec.rb +35 -0
- data/spec/settings/instance_level_spec.rb +44 -0
- data/spec/settings/settings_spec.rb +61 -0
- data/spec/settings/static_store_spec.rb +46 -0
- data/spec/settings/value_store_spec.rb +55 -0
- data/spec/spec_helper.rb +50 -0
- metadata +114 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
describe Settings::DBValue do
|
2
|
+
|
3
|
+
before do
|
4
|
+
TestModel.delete_all
|
5
|
+
Settings::DBValue.delete_all
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'should save to the DB' do
|
9
|
+
setting = Settings::DBValue.new(:context => TestModel, :full_key => 'foo', :value => 'bar')
|
10
|
+
setting.save!
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should load from the DB' do
|
14
|
+
add_val(TestModel, 'unique', 'bar')
|
15
|
+
setting = Settings::DBValue.find_by_full_key('unique')
|
16
|
+
setting.should_not be_nil
|
17
|
+
setting.value.should == 'bar'
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should find all owned values via scope' do
|
21
|
+
model = TestModel.create!(:name => 'bobby')
|
22
|
+
add_vals(TestModel,
|
23
|
+
'alpha' => 'a',
|
24
|
+
'beta' => 'b'
|
25
|
+
)
|
26
|
+
add_vals(model,
|
27
|
+
'do' => 'a',
|
28
|
+
're' => 'b',
|
29
|
+
'mi' => 'c'
|
30
|
+
)
|
31
|
+
vals = Settings::DBValue.for_context(TestModel)
|
32
|
+
vals.count.should == 2
|
33
|
+
vals = Settings::DBValue.for_context(model)
|
34
|
+
vals.count.should == 3
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should reload values in the same state as they are saved' do
|
38
|
+
add_vals(TestModel,
|
39
|
+
'string' => 'stringy',
|
40
|
+
'int' => 501,
|
41
|
+
'sym' => :bob
|
42
|
+
)
|
43
|
+
Settings::DBValue.find_by_full_key('string').value.should be_a(String)
|
44
|
+
Settings::DBValue.find_by_full_key('int').value.should be_a(Fixnum)
|
45
|
+
Settings::DBValue.find_by_full_key('sym').value.should be_a(Symbol)
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_vals(context, val_map)
|
49
|
+
val_map.each_pair do |k, v|
|
50
|
+
add_val(context, k, v)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_val(context, key, val)
|
55
|
+
Settings::DBValue.create!(:context => context, :full_key => key, :value => val)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
describe Settings::Entry do
|
2
|
+
|
3
|
+
before do
|
4
|
+
@settings = Settings::Root.new
|
5
|
+
Settings::Builder.define(@settings) do
|
6
|
+
string('astring')
|
7
|
+
int('someint', 25)
|
8
|
+
symbol('procsym') { ('some_' + 'symbol').to_sym }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should know it is an entry' do
|
13
|
+
entry = @settings.find_entry('astring')
|
14
|
+
entry.should be_entry
|
15
|
+
entry.should_not be_group
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should know its type' do
|
19
|
+
@settings.find_entry('astring').type.should == :string
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should return undefined defaults as nil' do
|
23
|
+
@settings.find_entry('astring').default.should be_nil
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should return simple defaults' do
|
27
|
+
@settings.find_entry('someint').default.should == 25
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should save blocks as defaults' do
|
31
|
+
@settings.find_entry('procsym').default.should be_a(Proc)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
describe Settings::Group do
|
2
|
+
|
3
|
+
before do
|
4
|
+
@root = Settings::Root.new
|
5
|
+
end
|
6
|
+
|
7
|
+
it 'should know it is a group' do
|
8
|
+
@root.add_group('sub')
|
9
|
+
group = @root.find_group('sub')
|
10
|
+
group.should be_group
|
11
|
+
group.should_not be_entry
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should know its full key' do
|
15
|
+
fun = @root.add_group('fun')
|
16
|
+
times = fun.add_group('times')
|
17
|
+
fun.key.should == 'fun'
|
18
|
+
times.key.should == 'fun.times'
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should find child groups' do
|
22
|
+
@root.add_group('sub')
|
23
|
+
group = @root.find_group('sub')
|
24
|
+
group.should be_a(Settings::Group)
|
25
|
+
group.key.should == 'sub'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should find child entries' do
|
29
|
+
@root.add_entry('some_value', :string)
|
30
|
+
entry = @root.find_entry('some_value')
|
31
|
+
entry.should be_a(Settings::Entry)
|
32
|
+
entry.key.should == 'some_value'
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
describe Settings::InstanceLevel do
|
2
|
+
|
3
|
+
before do
|
4
|
+
Settings::DBValue.delete_all
|
5
|
+
@model = TestModel.new(:name => 'test')
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'should be available in all Objects' do
|
9
|
+
Object.should respond_to(:instance_settings)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should return a Builder instance' do
|
13
|
+
class Bob ; end
|
14
|
+
Bob.instance_settings.should be_a(Settings::Builder)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should not be available at class level' do
|
18
|
+
class Tim
|
19
|
+
instance_settings do
|
20
|
+
int('foo')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
Tim.respond_to?(:settings).should be_false
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should allow setting values for a specific model' do
|
27
|
+
@model.settings.some_num.should == 5
|
28
|
+
@model.settings.some_num = 10
|
29
|
+
@model.settings.some_num.should == 10
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should save pending settings values to the database when the model is saved' do
|
33
|
+
Settings::DBValue.find_by_full_key('some_num').should be_nil # Sanity check - not yet in DB
|
34
|
+
@model.settings.some_num = 20
|
35
|
+
Settings::DBValue.find_by_full_key('some_num').should be_nil # Setting value should not save
|
36
|
+
@model.save
|
37
|
+
Settings::DBValue.find_by_full_key('some_num').should_not be_nil # Saving model SHOULD
|
38
|
+
|
39
|
+
# Reset model state and verify settings load correct value
|
40
|
+
@model.settings_reset!
|
41
|
+
@model.settings.some_num.should == 20
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
describe Settings do
|
2
|
+
|
3
|
+
it 'should return the list of installed data types' do
|
4
|
+
Settings.data_types.should =~ [:int, :string, :bool, :symbol, :var]
|
5
|
+
end
|
6
|
+
|
7
|
+
it 'should correctly parse values that are of the right type' do
|
8
|
+
# Valid values
|
9
|
+
{
|
10
|
+
:int => 5,
|
11
|
+
:int => -1,
|
12
|
+
:int => '10',
|
13
|
+
:string => 'foo',
|
14
|
+
:string => '',
|
15
|
+
:string => nil,
|
16
|
+
:symbol => :f123_foo,
|
17
|
+
:bool => true,
|
18
|
+
:bool => false,
|
19
|
+
:bool => nil,
|
20
|
+
:int_list => [5,2,-13,'0'],
|
21
|
+
:string_list => [],
|
22
|
+
:string_list => ['a', 'b'],
|
23
|
+
:var => [1, :a, 'yo'],
|
24
|
+
:var => Time.now
|
25
|
+
}.each_pair do |type, val|
|
26
|
+
expect { Settings.parse(val, type) }.to_not raise_error
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should raise an error when parsing invalid values' do
|
31
|
+
# Invalid values
|
32
|
+
{
|
33
|
+
:int => 5.0,
|
34
|
+
:string => :symmish,
|
35
|
+
:string => [],
|
36
|
+
:symbol => 'stringish',
|
37
|
+
:bool => 'true',
|
38
|
+
:bool => 0,
|
39
|
+
:bool => 1,
|
40
|
+
:int_list => [0,:bob],
|
41
|
+
:int_list => 12
|
42
|
+
}.each_pair do |type, val|
|
43
|
+
expect { Settings.parse(val, type) }.to raise_error(ArgumentError)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should pick a nice settings timestamp file path' do
|
48
|
+
Settings.default_timestamp_file('SimpleTest').should == File.join(Dir.tmpdir, 'simple-test-settings.txt')
|
49
|
+
end
|
50
|
+
|
51
|
+
# Used below...
|
52
|
+
class ClassLevelClass
|
53
|
+
class_settings
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should provide a list of classes with class-level settings' do
|
57
|
+
Settings.classes.should be_a(Array)
|
58
|
+
Settings.classes.should include(ClassLevelClass)
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
describe Settings::StaticStore do
|
2
|
+
|
3
|
+
class StaticStoreTest
|
4
|
+
class_settings do
|
5
|
+
int('val1')
|
6
|
+
string('val2')
|
7
|
+
group('group1') do
|
8
|
+
symbol('val3')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
before do
|
14
|
+
@store = Settings::StaticStore.new(Settings::Root.new)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should save values' do
|
18
|
+
@store.set_value('foo.bar', 27)
|
19
|
+
@store.get_value('foo.bar').should == 27
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should accept paths' do
|
23
|
+
store = Settings::StaticStore.new(StaticStoreTest.settings.root, :file => SpecHelper.sample_path('static-test'))
|
24
|
+
store.paths.count.should == 1
|
25
|
+
store.paths.first.should == SpecHelper.sample_path('static-test')
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should need a reload on first init with one or more paths' do
|
29
|
+
store = Settings::StaticStore.new(StaticStoreTest.settings.root, :file => SpecHelper.sample_path('static-test'))
|
30
|
+
store.should be_need_reload
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should ignore missing files' do
|
34
|
+
store = Settings::StaticStore.new(StaticStoreTest.settings.root, :file => '/tmp/made-up')
|
35
|
+
store.load
|
36
|
+
store.get_value('val1').should be_nil
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should load all paths in order' do
|
40
|
+
store = Settings::StaticStore.new(StaticStoreTest.settings.root, :files => [SpecHelper.sample_path('static-test'), SpecHelper.sample_path('static-test-2')])
|
41
|
+
store.load
|
42
|
+
store.get_value('val1').should == 205
|
43
|
+
store.get_value('val2').should == 'dogbone'
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
describe Settings::ValueStore do
|
2
|
+
|
3
|
+
it 'should require_reload? after initialization' do
|
4
|
+
@store = Settings::ValueStore.new(Settings::Root.new)
|
5
|
+
@store.should be_need_reload
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'should always reload on reload == true' do
|
9
|
+
@store = Settings::ValueStore.new(Settings::Root.new, :reload => true)
|
10
|
+
@store.load
|
11
|
+
@store.should be_need_reload
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should never reload on reload == false' do
|
15
|
+
@store = Settings::ValueStore.new(Settings::Root.new, :reload => false)
|
16
|
+
@store.load
|
17
|
+
@store.should_not be_need_reload
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should reload after x seconds on reload == x' do
|
21
|
+
@store = Settings::ValueStore.new(Settings::Root.new, :reload => 1)
|
22
|
+
@store.load
|
23
|
+
@store.should_not be_need_reload
|
24
|
+
sleep 1
|
25
|
+
@store.should be_need_reload
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should reload on file mtime change on reload == "some path"' do
|
29
|
+
# Set test file path and create/update it
|
30
|
+
path = File.join(Dir.tmpdir, 'settings-test.txt')
|
31
|
+
FileUtils.touch(path)
|
32
|
+
|
33
|
+
# Set our store to reload based on that file
|
34
|
+
@store = Settings::ValueStore.new(Settings::Root.new, :reload => path)
|
35
|
+
@store.load
|
36
|
+
@store.should_not be_need_reload
|
37
|
+
|
38
|
+
# Sleep a bit (needed due to OSX mtime resolution of 1 second... grr)
|
39
|
+
sleep 1.1
|
40
|
+
FileUtils.touch(path)
|
41
|
+
@store.should be_need_reload
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should reload conditionally on reload == { some proc }' do
|
45
|
+
# Set reload to a lambda referencing a locally scoped variable, toggle variable and
|
46
|
+
# ensure need_reload? toggles as well...
|
47
|
+
doit = true
|
48
|
+
@store = Settings::ValueStore.new(Settings::Root.new, :reload => lambda { doit })
|
49
|
+
@store.load
|
50
|
+
@store.should be_need_reload
|
51
|
+
doit = false
|
52
|
+
@store.should_not be_need_reload
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Set up activerecord & in-memory SQLite DB
|
2
|
+
require 'active_record'
|
3
|
+
require 'sqlite3'
|
4
|
+
ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
|
5
|
+
|
6
|
+
# Uncomment to enable SQL logging during tests for debugging
|
7
|
+
# require 'logger'
|
8
|
+
# ActiveRecord::Base.logger = Logger.new(STDOUT)
|
9
|
+
|
10
|
+
# Require our library
|
11
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'iron', 'settings'))
|
12
|
+
|
13
|
+
# Run our migration to add DB value models to test DB
|
14
|
+
require_relative('../db/settings_migration')
|
15
|
+
SettingsMigration.migrate(:up)
|
16
|
+
|
17
|
+
# Run migration to create test model to use as settings owner
|
18
|
+
ActiveRecord::Migration.create_table :test_models do |t|
|
19
|
+
t.string 'name'
|
20
|
+
end
|
21
|
+
|
22
|
+
# Create test model class
|
23
|
+
class TestModel < ActiveRecord::Base
|
24
|
+
instance_settings do
|
25
|
+
int('some_num', 5)
|
26
|
+
int('no_default')
|
27
|
+
group('some_group') do
|
28
|
+
string('some_string')
|
29
|
+
group('subgrouper') do
|
30
|
+
symbol('yo', :baby)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Config RSpec options
|
37
|
+
RSpec.configure do |config|
|
38
|
+
config.color = true
|
39
|
+
config.add_formatter 'documentation'
|
40
|
+
config.backtrace_clean_patterns = [/rspec/]
|
41
|
+
end
|
42
|
+
|
43
|
+
module SpecHelper
|
44
|
+
|
45
|
+
# Helper to find sample file paths
|
46
|
+
def self.sample_path(file)
|
47
|
+
File.expand_path(File.join(File.dirname(__FILE__), 'samples', file))
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: iron-settings
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Rob Morris
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-09-05 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: iron-extensions
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.1.3
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.1.3
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '2.6'
|
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: '2.6'
|
46
|
+
description: Allows defining settings with defaults to be overridden in code or via
|
47
|
+
database values. Built to work well with Rails models.
|
48
|
+
email:
|
49
|
+
- rob@irongaze.com
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- lib/iron/rake_loader.rb
|
55
|
+
- lib/iron/settings/builder.rb
|
56
|
+
- lib/iron/settings/class_level.rb
|
57
|
+
- lib/iron/settings/cursor.rb
|
58
|
+
- lib/iron/settings/db_store.rb
|
59
|
+
- lib/iron/settings/db_value.rb
|
60
|
+
- lib/iron/settings/entry.rb
|
61
|
+
- lib/iron/settings/group.rb
|
62
|
+
- lib/iron/settings/instance_level.rb
|
63
|
+
- lib/iron/settings/node.rb
|
64
|
+
- lib/iron/settings/root.rb
|
65
|
+
- lib/iron/settings/static_store.rb
|
66
|
+
- lib/iron/settings/value_store.rb
|
67
|
+
- lib/iron/settings.rb
|
68
|
+
- lib/tasks/settings.rake
|
69
|
+
- db/settings_migration.rb
|
70
|
+
- spec/samples/static-test
|
71
|
+
- spec/samples/static-test-2
|
72
|
+
- spec/settings/builder_spec.rb
|
73
|
+
- spec/settings/class_level_spec.rb
|
74
|
+
- spec/settings/cursor_spec.rb
|
75
|
+
- spec/settings/db_store_spec.rb
|
76
|
+
- spec/settings/db_value_spec.rb
|
77
|
+
- spec/settings/entry_spec.rb
|
78
|
+
- spec/settings/group_spec.rb
|
79
|
+
- spec/settings/instance_level_spec.rb
|
80
|
+
- spec/settings/settings_spec.rb
|
81
|
+
- spec/settings/static_store_spec.rb
|
82
|
+
- spec/settings/value_store_spec.rb
|
83
|
+
- spec/spec_helper.rb
|
84
|
+
- LICENSE
|
85
|
+
- History.txt
|
86
|
+
- Version.txt
|
87
|
+
- README.rdoc
|
88
|
+
- .rspec
|
89
|
+
homepage: http://irongaze.com
|
90
|
+
licenses:
|
91
|
+
- MIT
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 1.9.2
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
none: false
|
104
|
+
requirements:
|
105
|
+
- - ! '>='
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
requirements: []
|
109
|
+
rubyforge_project:
|
110
|
+
rubygems_version: 1.8.24
|
111
|
+
signing_key:
|
112
|
+
specification_version: 3
|
113
|
+
summary: Application settings in code and database
|
114
|
+
test_files: []
|