defaultable 0.0.3 → 0.0.4
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/defaultable.gemspec +1 -1
- data/lib/defaultable/serialization.rb +25 -0
- data/lib/defaultable/settings.rb +98 -0
- data/lib/defaultable/version.rb +1 -1
- data/lib/defaultable.rb +2 -81
- data/spec/defaultable_spec.rb +101 -62
- data/spec/serialization_spec.rb +67 -0
- metadata +13 -9
data/defaultable.gemspec
CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = Defaultable::VERSION
|
17
17
|
|
18
|
-
gem.add_dependency("activesupport", "3.0")
|
18
|
+
gem.add_dependency("activesupport", ">= 3.0")
|
19
19
|
|
20
20
|
gem.add_development_dependency("rspec", "~> 2.7")
|
21
21
|
gem.add_development_dependency("pry")
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Defaultable
|
2
|
+
class Serialization
|
3
|
+
class_attribute :settings_class
|
4
|
+
|
5
|
+
# Called to deserialize data to ruby object.
|
6
|
+
def load(data)
|
7
|
+
if data
|
8
|
+
obj = YAML.load(data)
|
9
|
+
|
10
|
+
raise TypeError, "Deserialized object is not of type #{self.class.settings_class.name}. Got #{obj.class}" unless obj.is_a?(self.class.settings_class)
|
11
|
+
|
12
|
+
# The reason we do the following is because defaults may have changed
|
13
|
+
# after the point of serialization. To avoid not having them, we instantiate the class
|
14
|
+
# again, we the previous values. This prevents not having data.
|
15
|
+
obj.class.new(obj.as_hash)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Called to convert from ruby object to serialized data.
|
20
|
+
def dump(obj)
|
21
|
+
raise TypeError, "Serialization failed: Object is not of type #{self.class.settings_class.name}." unless obj.is_a?(self.class.settings_class)
|
22
|
+
obj.to_yaml if obj
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Defaultable
|
2
|
+
class Settings
|
3
|
+
class_attribute :defaults
|
4
|
+
attr_accessor :root_key, :parent
|
5
|
+
|
6
|
+
def initialize(hash=nil, root_key=nil, parent=nil, skip_defaults=false)
|
7
|
+
@table = {}
|
8
|
+
|
9
|
+
self.root_key = root_key if root_key
|
10
|
+
self.parent = parent if parent
|
11
|
+
|
12
|
+
if !skip_defaults && !self.class.defaults.nil?
|
13
|
+
recursive_hash_assignment self.class.defaults
|
14
|
+
end
|
15
|
+
|
16
|
+
if !hash.nil? && hash.kind_of?(Hash)
|
17
|
+
recursive_hash_assignment hash
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def method_missing(message, *args, &block)
|
22
|
+
# puts "Message: #{message}", "Args: #{args}", "\n"
|
23
|
+
|
24
|
+
if !(message.to_s =~ /^(.*)=$/) && @table.has_key?(message.to_s)
|
25
|
+
@table[message.to_s]
|
26
|
+
elsif message.to_s =~ /^(.*)\?$/
|
27
|
+
@table.has_key?(message.to_s.gsub(/\?$/, ''))
|
28
|
+
elsif message.to_s =~ /^(.*)=$/
|
29
|
+
key = message.to_s.gsub(/=$/, '')
|
30
|
+
value = args.first
|
31
|
+
|
32
|
+
if value.kind_of?(Defaultable::Settings)
|
33
|
+
value.parent = self
|
34
|
+
value.root_key = key
|
35
|
+
end
|
36
|
+
|
37
|
+
@table[key] = value
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def as_hash
|
42
|
+
@table.inject({}) do |hash, (key, val)|
|
43
|
+
if val.kind_of?(Defaultable::Settings)
|
44
|
+
val = val.as_hash
|
45
|
+
else
|
46
|
+
val
|
47
|
+
end
|
48
|
+
|
49
|
+
hash[key] = val
|
50
|
+
hash
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def has_parent?
|
55
|
+
!!self.parent
|
56
|
+
end
|
57
|
+
|
58
|
+
def recursive_hash_assignment(hash)
|
59
|
+
hash.each do |key, value|
|
60
|
+
if value.kind_of?(Hash)
|
61
|
+
key_aware_assignment(key, value)
|
62
|
+
else
|
63
|
+
self.send("#{key}=", value)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def key_aware_assignment(key, hash)
|
69
|
+
if self.send("#{key}?")
|
70
|
+
instance = self.send(key)
|
71
|
+
|
72
|
+
if instance.kind_of?(Defaultable::Settings)
|
73
|
+
instance.recursive_hash_assignment(hash)
|
74
|
+
self.send("#{key}=", instance)
|
75
|
+
else
|
76
|
+
self.send("#{key}=", hash)
|
77
|
+
end
|
78
|
+
else
|
79
|
+
self.send("#{key}=", self.class.new(hash, key, self, true))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class << self
|
84
|
+
def set_defaults(settings, env=nil)
|
85
|
+
case settings
|
86
|
+
when Hash
|
87
|
+
self.defaults = settings
|
88
|
+
when String
|
89
|
+
yaml = YAML.load_file(settings)
|
90
|
+
yaml = yaml[env] if env
|
91
|
+
self.defaults = yaml
|
92
|
+
else
|
93
|
+
raise ArgumentError, "You must supply a hash or a file name for default settings"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/lib/defaultable/version.rb
CHANGED
data/lib/defaultable.rb
CHANGED
@@ -3,85 +3,6 @@ require 'yaml'
|
|
3
3
|
require 'active_support/core_ext/class/attribute'
|
4
4
|
|
5
5
|
module Defaultable
|
6
|
-
|
7
|
-
|
8
|
-
attr_accessor :root_key, :parent
|
9
|
-
|
10
|
-
def initialize(hash=nil, root_key=nil, parent=nil)
|
11
|
-
@table = {}
|
12
|
-
|
13
|
-
if root_key
|
14
|
-
self.root_key = root_key
|
15
|
-
end
|
16
|
-
|
17
|
-
if parent
|
18
|
-
self.parent = parent
|
19
|
-
end
|
20
|
-
|
21
|
-
if !hash.nil? && hash.kind_of?(Hash)
|
22
|
-
hash.each do |key, value|
|
23
|
-
if value.kind_of?(Hash)
|
24
|
-
send("#{key}=", Defaultable::Settings.new(value, key, self))
|
25
|
-
else
|
26
|
-
self.send("#{key}=", value)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def method_missing(message, *args, &block)
|
33
|
-
# puts "Message: #{message}", "Args: #{args}", "\n"
|
34
|
-
|
35
|
-
if !(message.to_s =~ /^(.*)=$/) && @table.has_key?(message.to_s)
|
36
|
-
@table[message.to_s]
|
37
|
-
elsif message.to_s =~ /^(.*)=$/
|
38
|
-
@table[message.to_s.gsub(/=$/, '')] = args.first
|
39
|
-
elsif !(message.to_s =~ /^(.*)=$/)
|
40
|
-
# We're looking for a default
|
41
|
-
default_settings = self.class.defaults
|
42
|
-
|
43
|
-
# If we have a parent, we need to traverse to the top of all of the method calls (root_keys), then go back down them
|
44
|
-
if self.parent
|
45
|
-
root_keys = []
|
46
|
-
current_parent = self
|
47
|
-
while current_parent.has_parent? do
|
48
|
-
root_keys << current_parent.root_key
|
49
|
-
current_parent = current_parent.parent
|
50
|
-
end
|
51
|
-
|
52
|
-
root_keys.reverse.each do |key|
|
53
|
-
default_settings = default_settings.send(key)
|
54
|
-
end
|
55
|
-
|
56
|
-
return default_settings.send(message)
|
57
|
-
# If don't have a parent, just send the message to the defaults outright
|
58
|
-
else
|
59
|
-
default_settings.send(message)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def as_hash
|
65
|
-
@table
|
66
|
-
end
|
67
|
-
|
68
|
-
def has_parent?
|
69
|
-
!!self.parent
|
70
|
-
end
|
71
|
-
|
72
|
-
class << self
|
73
|
-
def set_defaults(settings, env=nil)
|
74
|
-
case settings
|
75
|
-
when Hash
|
76
|
-
self.defaults = self.new(settings)
|
77
|
-
when String
|
78
|
-
yaml = YAML.load_file(settings)
|
79
|
-
yaml = yaml[env] if env
|
80
|
-
self.defaults = self.new(yaml)
|
81
|
-
else
|
82
|
-
raise ArgumentError, "You must supply a hash or a file name for default settings"
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
6
|
+
autoload :Serialization, 'defaultable/serialization'
|
7
|
+
autoload :Settings, 'defaultable/settings'
|
87
8
|
end
|
data/spec/defaultable_spec.rb
CHANGED
@@ -33,93 +33,110 @@ describe Defaultable::Settings do
|
|
33
33
|
setting.parent.child.should eq('Rob')
|
34
34
|
end
|
35
35
|
|
36
|
-
it "should have
|
37
|
-
Defaultable::Settings.
|
38
|
-
|
36
|
+
it "should have a question mark method for keys" do
|
37
|
+
setting = Defaultable::Settings.new(:foo => 'bar')
|
38
|
+
setting.foo?.should be_true
|
39
39
|
end
|
40
40
|
|
41
|
-
|
42
|
-
|
41
|
+
describe "Defaults" do
|
42
|
+
it "should have default settings" do
|
43
|
+
Defaultable::Settings.set_defaults File.expand_path('../', __FILE__) + '/test.yml'
|
44
|
+
Defaultable::Settings.defaults.should be_kind_of(Hash)
|
45
|
+
end
|
43
46
|
|
44
|
-
setting
|
45
|
-
|
46
|
-
end
|
47
|
+
it "should have a default setting for a key" do
|
48
|
+
Defaultable::Settings.set_defaults File.expand_path('../', __FILE__) + '/test.yml'
|
47
49
|
|
48
|
-
|
49
|
-
|
50
|
+
setting = Defaultable::Settings.new
|
51
|
+
setting.grandparent.should be_kind_of Defaultable::Settings
|
52
|
+
end
|
50
53
|
|
51
|
-
setting
|
52
|
-
|
53
|
-
|
54
|
+
it "should have another key for another setting" do
|
55
|
+
Defaultable::Settings.set_defaults File.expand_path('../', __FILE__) + '/test.yml'
|
56
|
+
|
57
|
+
setting = Defaultable::Settings.new
|
58
|
+
setting.grandparent.child.grandchild1.should eq('robert')
|
59
|
+
end
|
54
60
|
|
55
|
-
|
56
|
-
|
61
|
+
it "should set a key but still have defaults" do
|
62
|
+
Defaultable::Settings.set_defaults File.expand_path('../', __FILE__) + '/test.yml'
|
57
63
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
64
|
+
setting = Defaultable::Settings.new({
|
65
|
+
:grandparent => {
|
66
|
+
:child => {
|
67
|
+
:grandchild3 => 'hurdur'
|
68
|
+
}
|
62
69
|
}
|
63
|
-
}
|
64
|
-
})
|
70
|
+
})
|
65
71
|
|
66
|
-
|
67
|
-
|
68
|
-
|
72
|
+
setting.grandparent.child.grandchild3.should eq('hurdur')
|
73
|
+
setting.grandparent.child.grandchild2.should eq('brian')
|
74
|
+
end
|
69
75
|
|
70
|
-
|
71
|
-
|
76
|
+
it "should be able to overwrite a default" do
|
77
|
+
Defaultable::Settings.set_defaults File.expand_path('../', __FILE__) + '/test.yml'
|
72
78
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
79
|
+
setting = Defaultable::Settings.new({
|
80
|
+
:grandparent => {
|
81
|
+
:child => {
|
82
|
+
:grandchild3 => 'hurdur'
|
83
|
+
}
|
77
84
|
}
|
78
|
-
}
|
79
|
-
})
|
85
|
+
})
|
80
86
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
87
|
+
setting.grandparent.child.grandchild3.should eq('hurdur')
|
88
|
+
setting.grandparent.child.grandchild2 = 'drpepper'
|
89
|
+
setting.grandparent.child.grandchild2.should eq('drpepper')
|
90
|
+
end
|
85
91
|
|
86
|
-
|
87
|
-
|
92
|
+
it "should be able to set a key in the middle of defaults" do
|
93
|
+
Defaultable::Settings.set_defaults File.expand_path('../', __FILE__) + '/test.yml'
|
88
94
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
95
|
+
setting = Defaultable::Settings.new({
|
96
|
+
:grandparent => {
|
97
|
+
:child => {
|
98
|
+
:grandchild3 => 'hurdur'
|
99
|
+
}
|
93
100
|
}
|
94
|
-
}
|
95
|
-
})
|
101
|
+
})
|
96
102
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
103
|
+
setting.grandparent.child.grandchild3.should eq('hurdur')
|
104
|
+
setting.grandparent.someotherkey = 'saweet'
|
105
|
+
setting.grandparent.someotherkey.should eq('saweet')
|
106
|
+
end
|
101
107
|
|
102
|
-
|
103
|
-
|
108
|
+
it "should be able to set a hash for defaults" do
|
109
|
+
Defaultable::Settings.set_defaults :child => 'sxephil'
|
104
110
|
|
105
|
-
|
106
|
-
|
107
|
-
|
111
|
+
setting = Defaultable::Settings.new
|
112
|
+
setting.child.should eq('sxephil')
|
113
|
+
end
|
108
114
|
|
109
|
-
|
110
|
-
|
115
|
+
it "should accept a filename with an environment" do
|
116
|
+
Defaultable::Settings.set_defaults File.expand_path('../', __FILE__) + '/env_test.yml', 'development'
|
111
117
|
|
112
|
-
|
113
|
-
|
118
|
+
setting = Defaultable::Settings.new
|
119
|
+
setting.setting_key.should eq('somevalue')
|
120
|
+
end
|
114
121
|
end
|
115
122
|
|
116
|
-
|
117
|
-
|
118
|
-
|
123
|
+
describe "Extendable" do
|
124
|
+
it "should be extendable" do
|
125
|
+
class DummySetting < Defaultable::Settings
|
126
|
+
set_defaults :movie => 'Iron Man'
|
127
|
+
end
|
128
|
+
|
129
|
+
setting = DummySetting.new
|
130
|
+
setting.movie.should eq('Iron Man')
|
119
131
|
end
|
120
132
|
|
121
|
-
|
122
|
-
|
133
|
+
it "should return settings the same class of the extension for detauls" do
|
134
|
+
class DummySetting < Defaultable::Settings
|
135
|
+
set_defaults :movie => 'Iron Man'
|
136
|
+
end
|
137
|
+
|
138
|
+
DummySetting.defaults.should be_a Hash
|
139
|
+
end
|
123
140
|
end
|
124
141
|
|
125
142
|
describe "Hashes" do
|
@@ -139,4 +156,26 @@ describe Defaultable::Settings do
|
|
139
156
|
setting.as_hash['name'].should eq('Robert')
|
140
157
|
end
|
141
158
|
end
|
159
|
+
|
160
|
+
describe "Defaults" do
|
161
|
+
before(:each) do
|
162
|
+
class DummySetting < Defaultable::Settings
|
163
|
+
set_defaults :movie => {:name => 'Iron Man' }
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should have a key from defaults on initialization" do
|
168
|
+
setting = DummySetting.new
|
169
|
+
setting.movie?.should be_true
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should have a hash with a length from defaults on initialization" do
|
173
|
+
setting = DummySetting.new
|
174
|
+
setting.as_hash.length.should eq(1)
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should mash defaults together with new settings" do
|
178
|
+
setting = DummySetting.new(:movie => { :genre => 'asdf' })
|
179
|
+
end
|
180
|
+
end
|
142
181
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Defaultable::Serialization do
|
4
|
+
before(:each) do
|
5
|
+
class DummySetting < Defaultable::Settings; end
|
6
|
+
Defaultable::Serialization.settings_class = DummySetting
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should set a class for settings" do
|
10
|
+
Defaultable::Serialization.settings_class.should eq(DummySetting)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should serialize a settings object" do
|
14
|
+
encoder = Defaultable::Serialization.new
|
15
|
+
settings = DummySetting.new
|
16
|
+
|
17
|
+
settings.foo = 'bar'
|
18
|
+
serialized = encoder.dump(settings)
|
19
|
+
serialized.should be_a String
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should deserialize a settings object back to a kind of Defaultable::Settings" do
|
23
|
+
encoder = Defaultable::Serialization.new
|
24
|
+
settings = DummySetting.new
|
25
|
+
|
26
|
+
settings.foo = 'bar'
|
27
|
+
serialized = encoder.dump(settings)
|
28
|
+
|
29
|
+
encoder.load(serialized).should be_kind_of Defaultable::Settings
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should throw an exception when the class doesn't match the settings class on load" do
|
33
|
+
encoder = Defaultable::Serialization.new
|
34
|
+
|
35
|
+
lambda { encoder.load('adfghjsfjdhg') }.should raise_error TypeError
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should throw an exception when the class doesn't match the settings class on dump" do
|
39
|
+
encoder = Defaultable::Serialization.new
|
40
|
+
|
41
|
+
lambda { encoder.dump('adfghjsfjdhg'.to_yaml) }.should raise_error TypeError
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should load with keys still in tact" do
|
45
|
+
encoder = Defaultable::Serialization.new
|
46
|
+
settings = DummySetting.new
|
47
|
+
|
48
|
+
settings.foo = 'bar'
|
49
|
+
serialized = encoder.dump(settings)
|
50
|
+
unserialized = encoder.load(serialized)
|
51
|
+
|
52
|
+
unserialized.foo.should eq('bar')
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should load defaults in after the fact" do
|
56
|
+
encoder = Defaultable::Serialization.new
|
57
|
+
settings = DummySetting.new
|
58
|
+
|
59
|
+
settings.foo = 'bar'
|
60
|
+
serialized = encoder.dump(settings)
|
61
|
+
|
62
|
+
DummySetting.set_defaults :bobby => 'tables'
|
63
|
+
|
64
|
+
unserialized = encoder.load(serialized)
|
65
|
+
unserialized.bobby?.should be_true
|
66
|
+
end
|
67
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: defaultable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,22 +9,22 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-12-
|
12
|
+
date: 2011-12-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
16
|
-
requirement: &
|
16
|
+
requirement: &70278791694720 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - ! '>='
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: '3.0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70278791694720
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &70278791693100 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '2.7'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70278791693100
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: pry
|
38
|
-
requirement: &
|
38
|
+
requirement: &70278791692600 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70278791692600
|
47
47
|
description: Defaultable is an extendable class to allow easy method chaining for
|
48
48
|
settings along with defaults.
|
49
49
|
email:
|
@@ -59,9 +59,12 @@ files:
|
|
59
59
|
- Rakefile
|
60
60
|
- defaultable.gemspec
|
61
61
|
- lib/defaultable.rb
|
62
|
+
- lib/defaultable/serialization.rb
|
63
|
+
- lib/defaultable/settings.rb
|
62
64
|
- lib/defaultable/version.rb
|
63
65
|
- spec/defaultable_spec.rb
|
64
66
|
- spec/env_test.yml
|
67
|
+
- spec/serialization_spec.rb
|
65
68
|
- spec/spec_helper.rb
|
66
69
|
- spec/test.yml
|
67
70
|
homepage: https://github.com/bobbytables/defaultable
|
@@ -91,5 +94,6 @@ summary: Settings made easy.
|
|
91
94
|
test_files:
|
92
95
|
- spec/defaultable_spec.rb
|
93
96
|
- spec/env_test.yml
|
97
|
+
- spec/serialization_spec.rb
|
94
98
|
- spec/spec_helper.rb
|
95
99
|
- spec/test.yml
|