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.
@@ -0,0 +1,12 @@
1
+ describe Settings::DBStore do
2
+
3
+ before do
4
+ @store = Settings::DBStore.new(Settings::Root.new, Settings)
5
+ end
6
+
7
+ it 'should save values' do
8
+ @store.set_value('tim', :f00)
9
+ @store.get_value('tim').should == :f00
10
+ end
11
+
12
+ end
@@ -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
@@ -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: []