bolts 0.1.0dev1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/bolts.rb +3 -0
- data/lib/bolts/application.rb +69 -0
- data/lib/bolts/settings.rb +7 -0
- data/lib/bolts/settings/container.rb +89 -0
- data/lib/bolts/settings/definition.rb +21 -0
- data/lib/bolts/settings/errors.rb +7 -0
- data/lib/bolts/settings/mixin.rb +58 -0
- data/lib/bolts/settings/registry.rb +104 -0
- data/lib/bolts/version.rb +3 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/unit/application_spec.rb +60 -0
- data/spec/unit/settings/container_spec.rb +68 -0
- data/spec/unit/settings/definition_spec.rb +35 -0
- data/spec/unit/settings/registry_spec.rb +95 -0
- metadata +80 -0
data/lib/bolts.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
module Bolts
|
2
|
+
class Application
|
3
|
+
|
4
|
+
# @!attribute [r] name
|
5
|
+
# @return [String]
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
# @!attribute [r] initializers
|
9
|
+
# @return [Array<Object>] A list of initializers to run before
|
10
|
+
# application startup
|
11
|
+
attr_reader :initializers
|
12
|
+
|
13
|
+
# @!attribute [r] finalizers
|
14
|
+
# @return [Array<Object>] A list of finalizers to run after application
|
15
|
+
# completion
|
16
|
+
attr_reader :finalizers
|
17
|
+
|
18
|
+
# @!attribute [r] error_handlers
|
19
|
+
# @return [Array<Object>] A list of erro handles to run in case of
|
20
|
+
# application errors.
|
21
|
+
attr_reader :error_handlers
|
22
|
+
|
23
|
+
def initialize(name)
|
24
|
+
@name = name
|
25
|
+
|
26
|
+
@main = nil
|
27
|
+
|
28
|
+
@initializers = []
|
29
|
+
@finalizers = []
|
30
|
+
|
31
|
+
@error_handlers = []
|
32
|
+
end
|
33
|
+
|
34
|
+
# Run the main body of the application
|
35
|
+
#
|
36
|
+
# This method takes a block that actually runs under the application,
|
37
|
+
# performs all initialization steps, runs the block, performs finalization
|
38
|
+
# steps, and runs error handlers if an error was raised.
|
39
|
+
#
|
40
|
+
# @return [void]
|
41
|
+
def run!
|
42
|
+
@initializers.each { |action| action.call(self) }
|
43
|
+
@main.call(self)
|
44
|
+
@finalizers.each { |action| action.call(self) }
|
45
|
+
rescue => e
|
46
|
+
@error_handlers.each { |action| action.call(self) }
|
47
|
+
raise e
|
48
|
+
end
|
49
|
+
|
50
|
+
# Add a new action to run on app initialization
|
51
|
+
def on_initialize(&block)
|
52
|
+
@initializers << block
|
53
|
+
end
|
54
|
+
|
55
|
+
# Add a new action to run on app finalization
|
56
|
+
def on_finalize(&block)
|
57
|
+
@finalizers << block
|
58
|
+
end
|
59
|
+
|
60
|
+
# Add a new action to run on app error
|
61
|
+
def on_error(&block)
|
62
|
+
@error_handlers << block
|
63
|
+
end
|
64
|
+
|
65
|
+
def on_run(&block)
|
66
|
+
@main = block
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'bolts/settings'
|
2
|
+
|
3
|
+
# Defines a collection for application settings
|
4
|
+
#
|
5
|
+
# This implements a hierarchical interface to application settings. Containers
|
6
|
+
# can define an optional parent container that will be used for default options
|
7
|
+
# if those options aren't set on the given container.
|
8
|
+
class Bolts::Settings::Container
|
9
|
+
|
10
|
+
# @!attribute [r] valid_keys
|
11
|
+
# @return [Set<Symbol>] All valid keys defined on the container or parent container.
|
12
|
+
attr_accessor :valid_keys
|
13
|
+
|
14
|
+
# @param parent [Bolts::Settings::Container] An optional parent container
|
15
|
+
def initialize(parent = nil)
|
16
|
+
@parent = parent
|
17
|
+
|
18
|
+
@valid_keys = Set.new
|
19
|
+
@settings = {}
|
20
|
+
end
|
21
|
+
|
22
|
+
# Look up a value in the container. The lookup checks the current container,
|
23
|
+
# and then falls back to the parent container if it's given.
|
24
|
+
#
|
25
|
+
# @param key [Symbol] The lookup key
|
26
|
+
#
|
27
|
+
# @return [Object, nil] The retrieved value if present.
|
28
|
+
#
|
29
|
+
# @raise [Bolts::Settings::Container::InvalidKey] If the looked up key isn't
|
30
|
+
# a valid key.
|
31
|
+
def [](key)
|
32
|
+
validate_key! key
|
33
|
+
|
34
|
+
if @settings[key]
|
35
|
+
@settings[key]
|
36
|
+
elsif @parent and (pkey = @parent[key])
|
37
|
+
@settings[key] = pkey
|
38
|
+
pkey
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Set a value on the container
|
43
|
+
#
|
44
|
+
# @param key [Symbol] The lookup key
|
45
|
+
# @param value [Object] The value to store in the container
|
46
|
+
#
|
47
|
+
# @raise [Bolts::Settings::Container::InvalidKey] If the looked up key isn't
|
48
|
+
# a valid key.
|
49
|
+
def []=(key, value)
|
50
|
+
validate_key! key
|
51
|
+
|
52
|
+
@settings[key] = value
|
53
|
+
end
|
54
|
+
|
55
|
+
# Define a valid container key
|
56
|
+
#
|
57
|
+
# @note This should only be used by {#Bolts::Settings::ClassSettings}
|
58
|
+
#
|
59
|
+
# @param key [Symbol]
|
60
|
+
# @return [void]
|
61
|
+
def add_valid_key(key)
|
62
|
+
@valid_keys.add(key)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Determine if a key is a valid setting.
|
66
|
+
#
|
67
|
+
# @param key [Symbol]
|
68
|
+
#
|
69
|
+
# @return [true, false]
|
70
|
+
def valid_key?(key)
|
71
|
+
if @valid_keys.include?(key)
|
72
|
+
true
|
73
|
+
elsif @parent and @parent.valid_key?(key)
|
74
|
+
@valid_keys.add(key)
|
75
|
+
true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def validate_key!(key)
|
82
|
+
unless valid_key?(key)
|
83
|
+
raise InvalidKey, "Key #{key} is not a valid key"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# @api private
|
88
|
+
class InvalidKey < StandardError; end
|
89
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'bolts/settings'
|
2
|
+
|
3
|
+
class Bolts::Settings::Definition
|
4
|
+
|
5
|
+
attr_reader :name
|
6
|
+
attr_reader :desc
|
7
|
+
attr_reader :default
|
8
|
+
|
9
|
+
attr_writer :value
|
10
|
+
|
11
|
+
def initialize(name, desc, default)
|
12
|
+
|
13
|
+
@name = name
|
14
|
+
@desc = desc
|
15
|
+
@default = default
|
16
|
+
end
|
17
|
+
|
18
|
+
def value
|
19
|
+
(! @value.nil?) ? @value : @default
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'bolts/settings'
|
2
|
+
|
3
|
+
module Bolts::Settings::Mixin
|
4
|
+
|
5
|
+
def self.included(klass)
|
6
|
+
klass.send(:include, InstanceMethods)
|
7
|
+
klass.send(:extend, ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
|
12
|
+
# @return [Bolts::Settings::Container] A settings container for the given instance.
|
13
|
+
def settings
|
14
|
+
@settings ||= Bolts::Settings::Container.new(self.class.settings)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
|
20
|
+
# Bind to a setting registry so that global settings can change this class.
|
21
|
+
#
|
22
|
+
# @param registry [Bolts::Setting::Registry]
|
23
|
+
# @param name [String] The name of the setting to bind to
|
24
|
+
# @param block [Proc] An optional block to execute when the setting changes
|
25
|
+
def bind_setting(registry, name)
|
26
|
+
def_setting_attr(name)
|
27
|
+
|
28
|
+
registry.bind_watcher(self, name)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Define a setting and optional default on the extending class.
|
32
|
+
#
|
33
|
+
# @param key [Symbol]
|
34
|
+
# @param default [Object]
|
35
|
+
#
|
36
|
+
# @return [void]
|
37
|
+
def def_setting_attr(key, default = nil)
|
38
|
+
defaults.add_valid_key(key)
|
39
|
+
defaults[key] = default if default
|
40
|
+
end
|
41
|
+
|
42
|
+
# A singleton settings container for storing immutable default configuration
|
43
|
+
# on the extending class.
|
44
|
+
#
|
45
|
+
# @return [Bolts::Settings::Container]
|
46
|
+
def defaults
|
47
|
+
@defaults ||= Bolts::Settings::Container.new
|
48
|
+
end
|
49
|
+
|
50
|
+
# A singleton settings container for storing manual setting configurations
|
51
|
+
# on the extending class.
|
52
|
+
#
|
53
|
+
# @return [Bolts::Settings::Container]
|
54
|
+
def settings
|
55
|
+
@settings ||= Bolts::Settings::Container.new(defaults)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'bolts/settings'
|
2
|
+
require 'bolts/settings/definition'
|
3
|
+
|
4
|
+
class Bolts::Settings::Registry
|
5
|
+
|
6
|
+
attr_reader :definitions
|
7
|
+
|
8
|
+
attr_reader :watchers
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@definitions = []
|
12
|
+
|
13
|
+
@watchers = Hash.new { |h, k| h[k] = [] }
|
14
|
+
end
|
15
|
+
|
16
|
+
# Create a new setting definition
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# reg.define_setting(
|
20
|
+
# 'my-setting',
|
21
|
+
# :desc => 'setting description goes here',
|
22
|
+
# :default => 'default value',
|
23
|
+
# )
|
24
|
+
#
|
25
|
+
# @param name [String] The setting name
|
26
|
+
# @param opts [Hash]
|
27
|
+
#
|
28
|
+
# @param opts [String] :desc The setting description
|
29
|
+
# @param opts [Object] :default
|
30
|
+
def define_setting(name, options)
|
31
|
+
definition = Bolts::Settings::Definition.new(name, options[:desc], options[:default])
|
32
|
+
@definitions << definition
|
33
|
+
end
|
34
|
+
|
35
|
+
# Look up the definition with the given name.
|
36
|
+
#
|
37
|
+
# @param name [String]
|
38
|
+
# @return [Bolts::Setting::Definition, nil]
|
39
|
+
def definition(name)
|
40
|
+
@definitions.find { |definition| name == definition.name }
|
41
|
+
end
|
42
|
+
|
43
|
+
# Apply settings from a hash
|
44
|
+
#
|
45
|
+
# @param hash [Hash]
|
46
|
+
# @return [Hash] The hash with the processed values removed
|
47
|
+
def set_values(hash)
|
48
|
+
ret = hash.dup
|
49
|
+
|
50
|
+
hash.each_pair do |name, value|
|
51
|
+
if set_value(name, value)
|
52
|
+
ret.delete(name)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
ret
|
57
|
+
end
|
58
|
+
|
59
|
+
# Set a definition value
|
60
|
+
#
|
61
|
+
# @param name [String]
|
62
|
+
# @param value [Object]
|
63
|
+
#
|
64
|
+
# @return [true, false] If the setting could be applied
|
65
|
+
def set_value(name, value)
|
66
|
+
if (setting_def = self.definition(name))
|
67
|
+
setting_def.value = value
|
68
|
+
true
|
69
|
+
else
|
70
|
+
false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
alias :[]= :set_value
|
75
|
+
|
76
|
+
# Retrieve a definition value
|
77
|
+
#
|
78
|
+
# @param name [String]
|
79
|
+
#
|
80
|
+
# @return [Object]
|
81
|
+
def get_value(name)
|
82
|
+
if (setting_def = self.definition(name))
|
83
|
+
setting_def.value
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
alias :[] :get_value
|
88
|
+
|
89
|
+
# @param name [String] The setting name to bind to
|
90
|
+
# @param obj [#[]=] An object that can have properties set
|
91
|
+
def bind_watcher(name, settable)
|
92
|
+
@watchers[name] << settable
|
93
|
+
end
|
94
|
+
|
95
|
+
def notify_watchers
|
96
|
+
@watchers.each_pair do |name, list|
|
97
|
+
setting_def = self.definition(name)
|
98
|
+
|
99
|
+
list.each do |watcher|
|
100
|
+
watcher[name] = setting_def.value
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'bolts/application'
|
4
|
+
|
5
|
+
describe Bolts::Application do
|
6
|
+
let(:sequence) { [] }
|
7
|
+
subject do
|
8
|
+
described_class.new('test').tap do |app|
|
9
|
+
app.on_run { sequence << :main }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "with an initializer" do
|
14
|
+
it "hands the application to the initializer" do
|
15
|
+
subject.on_initialize { |app| expect(app).to eq subject }
|
16
|
+
subject.run!
|
17
|
+
end
|
18
|
+
|
19
|
+
it "runs the initializer before the main action" do
|
20
|
+
subject.on_initialize { sequence << :initializer }
|
21
|
+
subject.run!
|
22
|
+
|
23
|
+
expect(sequence).to eq [:initializer, :main]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "with a finalizer" do
|
28
|
+
it "hands the application to the finalizer" do
|
29
|
+
subject.on_finalize { |app| expect(app).to eq subject }
|
30
|
+
subject.run!
|
31
|
+
end
|
32
|
+
|
33
|
+
it "runs the finalizer after the main action" do
|
34
|
+
subject.on_finalize { sequence << :finalizer }
|
35
|
+
subject.run!
|
36
|
+
expect(sequence).to eq [:main, :finalizer]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "with an error handler" do
|
41
|
+
it "hands the application to the error handler" do
|
42
|
+
subject.on_error { |app| expect(app).to eq subject }
|
43
|
+
subject.on_run { raise "kaboom!" }
|
44
|
+
expect { subject.run! }.to raise_error StandardError, /kaboom!/
|
45
|
+
end
|
46
|
+
|
47
|
+
it "runs the error handler if an exception was raised" do
|
48
|
+
subject.on_error { sequence << :error }
|
49
|
+
subject.on_run { sequence << :main; raise "kaboom!" }
|
50
|
+
expect { subject.run! }.to raise_error StandardError, /kaboom!/
|
51
|
+
expect(sequence).to eq [:main, :error]
|
52
|
+
end
|
53
|
+
|
54
|
+
it "doesn't run the error handler on normal application exit" do
|
55
|
+
subject.on_error { sequence << :error }
|
56
|
+
subject.run!
|
57
|
+
expect(sequence).to eq [:main]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'bolts/settings'
|
4
|
+
|
5
|
+
describe Bolts::Settings::Container do
|
6
|
+
|
7
|
+
describe 'validating keys' do
|
8
|
+
it 'can add new valid keys' do
|
9
|
+
subject.add_valid_key(:v)
|
10
|
+
subject[:v]
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'can check if a key is valid' do
|
14
|
+
subject.add_valid_key(:v)
|
15
|
+
expect(subject.valid_key?(:v)).to be_true
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'can list all valid keys' do
|
19
|
+
subject.add_valid_key(:v)
|
20
|
+
subject.add_valid_key(:w)
|
21
|
+
|
22
|
+
expect(subject.valid_keys).to include :v
|
23
|
+
expect(subject.valid_keys).to include :w
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'specifying settings' do
|
28
|
+
it 'fails if a setting application uses an invalid key' do
|
29
|
+
expect { subject[:invalid] = 'fail' }.to raise_error Bolts::Settings::Container::InvalidKey
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'can look up values that it sets' do
|
33
|
+
subject.add_valid_key :v
|
34
|
+
subject[:v] = 'set'
|
35
|
+
expect(subject[:v]).to eq 'set'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'looking up settings' do
|
40
|
+
before do
|
41
|
+
subject.add_valid_key :v
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'fails if a setting lookup uses an invalid key' do
|
45
|
+
expect { subject[:invalid] }.to raise_error Bolts::Settings::Container::InvalidKey
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'returns nil if a key is valid but no setting is present' do
|
49
|
+
expect(subject[:v]).to be_nil
|
50
|
+
end
|
51
|
+
|
52
|
+
describe 'with a parent container' do
|
53
|
+
let(:parent) { described_class.new.tap { |p| p.add_valid_key :v } }
|
54
|
+
subject { described_class.new(parent) }
|
55
|
+
|
56
|
+
it 'uses its setting over a parent value' do
|
57
|
+
subject[:v] = 'child'
|
58
|
+
parent[:v] = 'parent'
|
59
|
+
expect(subject[:v]).to eq 'child'
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'falls back to the parent value if it does not have a value' do
|
63
|
+
parent[:v] = 'parent'
|
64
|
+
expect(subject[:v]).to eq 'parent'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'bolts/settings/definition'
|
3
|
+
|
4
|
+
describe Bolts::Settings::Definition do
|
5
|
+
|
6
|
+
subject do
|
7
|
+
described_class.new(
|
8
|
+
'test',
|
9
|
+
'description here',
|
10
|
+
'default value'
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "has a name" do
|
15
|
+
expect(subject.name).to eq 'test'
|
16
|
+
end
|
17
|
+
|
18
|
+
it "has a description" do
|
19
|
+
expect(subject.desc).to eq 'description here'
|
20
|
+
end
|
21
|
+
|
22
|
+
it "has a default value" do
|
23
|
+
expect(subject.default).to eq 'default value'
|
24
|
+
end
|
25
|
+
|
26
|
+
it "prefers an explicit value over a default value" do
|
27
|
+
subject.value = 'explicit!'
|
28
|
+
expect(subject.value).to eq 'explicit!'
|
29
|
+
end
|
30
|
+
|
31
|
+
it "uses a default value if no explicit value is specified" do
|
32
|
+
subject.value = nil
|
33
|
+
expect(subject.value).to eq 'default value'
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'bolts/settings/registry'
|
3
|
+
|
4
|
+
describe Bolts::Settings::Registry do
|
5
|
+
|
6
|
+
it "can define new settings" do
|
7
|
+
subject.define_setting('something', :desc => 'a setting definition!')
|
8
|
+
expect(subject.definitions).to have(1).items
|
9
|
+
expect(subject.definition('something')).to be_a_kind_of Bolts::Settings::Definition
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "applying settings" do
|
13
|
+
before do
|
14
|
+
subject.define_setting('partycat', :desc => 'party cat loves to party')
|
15
|
+
subject.define_setting('partydog', :desc => 'party dog also loves to party')
|
16
|
+
end
|
17
|
+
|
18
|
+
it "applies settings from a hash" do
|
19
|
+
settings = {
|
20
|
+
'partycat' => 'parties!',
|
21
|
+
'partydog' => 'we love parties!',
|
22
|
+
}
|
23
|
+
|
24
|
+
subject.set_values(settings)
|
25
|
+
|
26
|
+
expect(subject.definition('partycat').value).to eq 'parties!'
|
27
|
+
expect(subject.definition('partydog').value).to eq 'we love parties!'
|
28
|
+
end
|
29
|
+
|
30
|
+
it "can directly retrieve a definition value" do
|
31
|
+
settings = {
|
32
|
+
'partycat' => 'parties!',
|
33
|
+
'partydog' => 'we love parties!',
|
34
|
+
}
|
35
|
+
|
36
|
+
subject.set_values(settings)
|
37
|
+
|
38
|
+
expect(subject.get_value('partycat')).to eq 'parties!'
|
39
|
+
expect(subject.get_value('partydog')).to eq 'we love parties!'
|
40
|
+
end
|
41
|
+
|
42
|
+
it "removes used settings from the returned hash" do
|
43
|
+
input = {
|
44
|
+
'partycat' => 'parties!',
|
45
|
+
'partydog' => 'we love parties!',
|
46
|
+
'partysnake' => 'partysnake hates parties'
|
47
|
+
}
|
48
|
+
|
49
|
+
output = subject.set_values(input)
|
50
|
+
|
51
|
+
expect(output).to have(1).items
|
52
|
+
expect(output).to include('partysnake' => 'partysnake hates parties')
|
53
|
+
end
|
54
|
+
|
55
|
+
it "doesn't modify the hash in place" do
|
56
|
+
input = {
|
57
|
+
'partycat' => 'parties!',
|
58
|
+
'partydog' => 'we love parties!',
|
59
|
+
'partysnake' => 'partysnake hates parties'
|
60
|
+
}
|
61
|
+
|
62
|
+
subject.set_values(input)
|
63
|
+
|
64
|
+
expect(input).to eq input
|
65
|
+
end
|
66
|
+
|
67
|
+
it "applies a single setting and value" do
|
68
|
+
subject.set_value('partycat', 'parties!')
|
69
|
+
expect(subject.definition('partycat').value).to eq 'parties!'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "binding values" do
|
74
|
+
let(:container) { double('settings container') }
|
75
|
+
|
76
|
+
before do
|
77
|
+
subject.define_setting('partycat', :desc => 'party cat loves to party')
|
78
|
+
subject.set_value('partycat', 'parties!')
|
79
|
+
end
|
80
|
+
|
81
|
+
it "can bind objects to settings" do
|
82
|
+
subject.bind_watcher('partycat', container)
|
83
|
+
|
84
|
+
expect(subject.watchers).to include('partycat' => [container])
|
85
|
+
end
|
86
|
+
|
87
|
+
it "can notify bound objects" do
|
88
|
+
subject.bind_watcher('partycat', container)
|
89
|
+
|
90
|
+
expect(container).to receive(:[]=).with('partycat', 'parties!')
|
91
|
+
|
92
|
+
subject.notify_watchers
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bolts
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0dev1
|
5
|
+
prerelease: 5
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Adrien Thebo
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-12-22 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.14.0
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 2.14.0
|
30
|
+
description:
|
31
|
+
email: adrien@somethingsinistral.net
|
32
|
+
executables: []
|
33
|
+
extensions: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
files:
|
36
|
+
- lib/bolts/settings.rb
|
37
|
+
- lib/bolts/settings/errors.rb
|
38
|
+
- lib/bolts/settings/registry.rb
|
39
|
+
- lib/bolts/settings/container.rb
|
40
|
+
- lib/bolts/settings/mixin.rb
|
41
|
+
- lib/bolts/settings/definition.rb
|
42
|
+
- lib/bolts/version.rb
|
43
|
+
- lib/bolts/application.rb
|
44
|
+
- lib/bolts.rb
|
45
|
+
- spec/spec_helper.rb
|
46
|
+
- spec/unit/settings/registry_spec.rb
|
47
|
+
- spec/unit/settings/container_spec.rb
|
48
|
+
- spec/unit/settings/definition_spec.rb
|
49
|
+
- spec/unit/application_spec.rb
|
50
|
+
homepage: http://github.com/adrienthebo/bolts
|
51
|
+
licenses:
|
52
|
+
- Apache 2.0
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ! '>'
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.3.1
|
69
|
+
requirements: []
|
70
|
+
rubyforge_project:
|
71
|
+
rubygems_version: 1.8.23
|
72
|
+
signing_key:
|
73
|
+
specification_version: 3
|
74
|
+
summary: Bolts is a foundation for writing applications
|
75
|
+
test_files:
|
76
|
+
- spec/unit/settings/registry_spec.rb
|
77
|
+
- spec/unit/settings/container_spec.rb
|
78
|
+
- spec/unit/settings/definition_spec.rb
|
79
|
+
- spec/unit/application_spec.rb
|
80
|
+
has_rdoc:
|