fun_with_configurations 0.0.1 → 0.0.2

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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.2
@@ -3,8 +3,34 @@ module FunWith
3
3
  class KeyError < Exception
4
4
  end
5
5
 
6
- class Config < Hash
7
- def initialize( &block )
6
+ class ChainError < Exception
7
+ end
8
+
9
+ module ConfigOverriddenMethods
10
+ def self.override_method( sym )
11
+ eval <<-EOS
12
+ def #{sym}( *args, &block )
13
+ self.method_missing(:sym, *args, &block)
14
+ end
15
+
16
+ def #{sym}=( *args, &block )
17
+ self.method_missing( :#{sym}, *args, &block )
18
+ end
19
+ EOS
20
+ end
21
+
22
+ for sym in [:test]
23
+ override_method( sym )
24
+ end
25
+ end
26
+
27
+ class Config
28
+ include ConfigOverriddenMethods
29
+
30
+ def initialize( key_to_self = nil, parent = nil, &block )
31
+ @key_to_self = key_to_self
32
+ @parent = parent
33
+ @config_vars = {}
8
34
  self.instance_exec( &block ) if block_given?
9
35
  end
10
36
 
@@ -12,10 +38,12 @@ module FunWith
12
38
  method = method.to_s.gsub( /=$/, '' ).to_sym
13
39
 
14
40
  if block_given?
15
- self[method] = Config.new unless self[method].is_a?(Config)
41
+ self[method] = Config.new(method, self) unless self[method].is_a?(Config)
16
42
  self[method].instance_exec( &block )
17
- elsif args.length > 0
43
+ elsif args.length == 1
18
44
  self[method] = args.first
45
+ elsif args.length > 1
46
+ self[method] = args
19
47
  else
20
48
  self[method]
21
49
  end
@@ -24,20 +52,72 @@ module FunWith
24
52
  def []( sym )
25
53
  sym = sym.to_sym if sym.is_a?(String)
26
54
  self.class.key_check( sym )
27
- super( sym )
55
+ @config_vars[ sym ]
28
56
  end
29
57
 
30
58
  def []=( sym, val )
31
59
  sym = sym.to_sym if sym.is_a?(String)
32
60
  self.class.key_check( sym )
33
- super( sym, val )
61
+ @config_vars[ sym ] = val
62
+ end
63
+
64
+ # Say you had a configuration that had multiple entries, and you wanted to select from
65
+ # among them at runtime. Example:
66
+ # config:
67
+ # important_folder:
68
+ # development: "/this/directory",
69
+ # test: "/that/directory",
70
+ # production: "~/another/directory"
71
+ #
72
+ # You could do config.important_folder[environment] every time you want to access that setting.
73
+ # Or you can do config.important_folder.promote_configuration(:development) and have the
74
+ # development subconfiguration replace the important_folder: configuration
75
+ #
76
+ # You can promote a sub-sub-sub-config by sending an array of symbols. But I hope it
77
+ # never comes to that.
78
+ def promote_configuration( *keys )
79
+ replace_with = self.try.config_method_chain_result( keys )
80
+ if replace_with.success?
81
+ @parent[@key_to_self] = replace_with.config
82
+ else
83
+ raise ChainError.new( "config failed to promote_configuration #{keys.inspect}" )
84
+ end
34
85
  end
35
86
 
36
87
  def self.key_check( sym )
37
- @reserved_symbols ||= Hash.instance_methods
88
+ @reserved_symbols ||= Config.instance_methods - self.fwc_overridden_methods
89
+
38
90
  raise KeyError.new("#{sym} is not a symbol") unless sym.is_a?(Symbol)
39
91
  raise KeyError.new("#{sym} is reserved for use by Hash") if @reserved_symbols.include?( sym )
40
92
  end
93
+
94
+ def try( *keys )
95
+ t = TryObject.new( self )
96
+
97
+ for key in keys
98
+ t[key]
99
+ end
100
+
101
+ t
102
+ end
103
+
104
+ def self.from_hash( hash )
105
+ config = self.new
106
+
107
+ for k, v in hash
108
+ config.send( k, v.is_a?( Hash ) ? self.from_hash( v ) : v )
109
+ end
110
+
111
+ config
112
+ end
113
+
114
+ def self.fwc_overridden_methods
115
+ ConfigOverriddenMethods.instance_methods.grep( /[^=]$/ )
116
+ end
117
+
118
+ def fwc_overridden_methods
119
+ self.class.fwc_overridden_methods
120
+ end
41
121
  end
42
122
  end
43
123
  end
@@ -1,11 +1,19 @@
1
1
  class Object
2
2
  def install_fwc_config( config = nil, &block )
3
3
  extend FunWith::Configurations::Configurable
4
- self.config = config || FunWith::Configurations::Config.new( &block )
4
+ self.config = config || FunWith::Configurations::Config.new( nil, &block )
5
5
  self.config
6
6
  end
7
7
 
8
8
  def install_fwc_config_from_file( filename )
9
9
  install_fwc_config( eval( File.read( filename ) ) ) # TODO: Has to be a better way than eval(). Dangerous.
10
10
  end
11
+
12
+ def install_fwc_config_from_hash( hash )
13
+ install_fwc_config( FunWith::Configurations::Config.from_hash( hash ) )
14
+ end
15
+
16
+ def install_fwc_config_from_yaml( yaml_string )
17
+ install_fwc_config_from_hash( YAML.load( yaml_string ) )
18
+ end
11
19
  end
@@ -0,0 +1,65 @@
1
+ module FunWith
2
+ module Configurations
3
+ class TryResult
4
+ attr_accessor :config
5
+ def initialize( config, success )
6
+ @config = config
7
+ @success = success
8
+ end
9
+
10
+ def success?
11
+ @success
12
+ end
13
+ end
14
+
15
+ class TryObject
16
+ # takes a Config object
17
+ def initialize( config )
18
+ @config = config
19
+ @success = true
20
+ @leaf = false
21
+ end
22
+
23
+ def method_missing( method, *args )
24
+ follow_config_method_chain( method )
25
+ end
26
+
27
+ def [] method_key
28
+ follow_config_method_chain( method_key.to_sym )
29
+ end
30
+
31
+ def follow_config_method_chain( method )
32
+ if @success == true
33
+ if @config.is_a?(Config) && @config.has_key?(method)
34
+ @config = @config[method]
35
+ unless @config.is_a?(Config)
36
+ @leaf = true
37
+ end
38
+ elsif @leaf
39
+ @leaf = false # declare unsuccessful on next call.
40
+ else
41
+ @success = false
42
+ end
43
+ end
44
+
45
+ self
46
+ end
47
+
48
+ def config_method_chain_result( *keys )
49
+ keys = keys.flatten
50
+
51
+ if success?
52
+ keys.each do |k|
53
+ follow_config_method_chain(k)
54
+ end
55
+ end
56
+
57
+ TryResult.new( @config, success? )
58
+ end
59
+
60
+ def success?
61
+ @success
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,5 +1,13 @@
1
1
  require 'fun_with_files'
2
+ module FunWith
3
+ module Configurations
4
+ end
5
+ end
6
+
7
+ FunWith::Files::RootPath.rootify( FunWith::Configurations, __FILE__.fwf_filepath.dirname.up )
8
+
2
9
  require_relative File.join( "fun_with", "configurations", "config" )
3
10
  require_relative File.join( "fun_with", "configurations", "configurations" )
4
11
  require_relative File.join( "fun_with", "configurations", "object" )
12
+ require_relative File.join( "fun_with", "configurations", "try_object" )
5
13
 
@@ -0,0 +1,12 @@
1
+ servers:
2
+ puppymonster:
3
+ commands:
4
+ - flippy
5
+ - snorty
6
+ - shorty
7
+
8
+ services:
9
+ mysql: yep
10
+ memcached: sure
11
+ oauth: definitely
12
+ twitter: no
@@ -25,6 +25,16 @@ class TestFunWithConfigurations < Test::Unit::TestCase
25
25
  e :f
26
26
  f :g
27
27
  end
28
+
29
+ s do
30
+ t do
31
+ u do
32
+ v do
33
+ w :x
34
+ end
35
+ end
36
+ end
37
+ end
28
38
  end
29
39
  end
30
40
 
@@ -66,29 +76,118 @@ class TestFunWithConfigurations < Test::Unit::TestCase
66
76
  assert_equal nil, @obj.config[:c=]
67
77
  end
68
78
 
69
- should "gripe when given a string" do
79
+ should "gripe when given a hash" do
70
80
  assert defined?( FunWith::Configurations::KeyError )
71
81
  assert_raises FunWith::Configurations::KeyError do |err|
72
- @obj.config["c"]
82
+ @obj.config[{c: "hey"}]
73
83
  end
74
84
  end
75
85
 
76
86
  should "gripe when given a reserved method as a key" do
77
87
  assert defined?( FunWith::Configurations::KeyError )
78
88
  assert_raises FunWith::Configurations::KeyError do |err|
79
- @obj.config[:keys] = ["22461", "80129"]
89
+ @obj.config[:clone] = ["22461", "80129"]
90
+ end
91
+
92
+ assert_raises FunWith::Configurations::KeyError do |err|
93
+ @obj.config.clone = ["22461", "80129"]
80
94
  end
81
95
  end
96
+
97
+ should "pass tests for try() function" do
98
+ assert_equal true, @obj.config.try.a.success?
99
+ assert_equal true, @obj.config.try.a.b.success?
100
+ assert_equal false, @obj.config.try.a.b.c.success?
101
+ assert_equal false, @obj.config.try.a.b.c.d.e.f.g.h.success?
102
+ assert_equal true, @obj.config.try.c.d.success?
103
+ assert_equal true, @obj.config.try[:c]["d"].success? # alternate traversal mechanism
104
+ end
105
+
106
+ should "have parents" do
107
+ assert_kind_of FunWith::Configurations::Config, @obj.config.c.instance_variable_get(:@parent)
108
+ assert_kind_of FunWith::Configurations::Config, @obj.config.s.t.u.instance_variable_get(:@parent).instance_variable_get(:@parent)
109
+ end
110
+
111
+ should "promote subconfiguration" do
112
+ assert_equal :x, @obj.config.s.t.u.v.w
113
+ @obj.config.s.t.u.v.promote_configuration(:w)
114
+ assert_equal :x, @obj.config.s.t.u.v
115
+ end
116
+
117
+ should "promote sub-subconfiguration" do
118
+ assert_equal :x, @obj.config.s.t.u.v.w
119
+ @obj.config.s.t.u.promote_configuration(:v,:w)
120
+ assert_equal :x, @obj.config.s.t.u
121
+ end
82
122
  end
83
123
 
84
124
  should "successfully create configuration manually" do
85
125
  @obj = Object.new
86
126
  @obj.extend( FunWith::Configurations::Configurable )
87
- @obj.config = FunWith::Configurations::Config.new do
127
+ @obj.config = FunWith::Configurations::Config.new( nil ) do
88
128
  betelgeuse "red"
89
129
  rigel "blue-white"
90
130
  end
91
131
 
92
132
  assert_equal "blue-white", @obj.config.rigel
93
133
  end
134
+
135
+ context "testing overridden methods|" do
136
+ setup do
137
+ @obj = Object.new
138
+ @obj.install_fwc_config do
139
+ malaprop :sym
140
+ blocky do
141
+ fish :guts
142
+ test :has_caused_problems
143
+ end
144
+ end
145
+ end
146
+
147
+ should "override test" do
148
+ assert @obj.config.blocky.respond_to?(:test)
149
+ assert_equal :has_caused_problems, @obj.config.blocky.test
150
+ end
151
+ end
152
+
153
+ should "hold multiple arguments as arrays" do
154
+ @obj = Object.new
155
+ @obj.install_fwc_config do
156
+ my_array 1,2,3,nil,5
157
+ my_array2 [1,2,3,nil,5]
158
+ end
159
+
160
+ assert_equal @obj.config.my_array, @obj.config.my_array2
161
+ end
162
+
163
+ context "testing from_hash" do
164
+ setup do
165
+ @obj = Object.new
166
+ @obj.install_fwc_config_from_hash({
167
+ :calista => "Flockhart",
168
+ :jude => "Law"
169
+ })
170
+
171
+ @obj2 = Object.new
172
+ @obj2.install_fwc_config_from_hash({
173
+ :a => {
174
+ :b => {
175
+ :c => {
176
+ :d => "e"
177
+ }
178
+ }
179
+ }
180
+ })
181
+
182
+ @obj3 = Object.new
183
+ yaml = FunWith::Configurations.root("test", "data", "config.yml").read
184
+ @obj3.install_fwc_config_from_yaml( yaml )
185
+ end
186
+
187
+ should "have successfully created configs" do
188
+ assert_equal "Law", @obj.config.jude
189
+ assert_equal "e", @obj2.config.a.b.c.d
190
+ assert_equal "definitely", @obj3.config.servers.puppymonster.services.oauth
191
+ end
192
+ end
94
193
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fun_with_configurations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-30 00:00:00.000000000 Z
12
+ date: 2013-05-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fun_with_files
@@ -119,7 +119,9 @@ files:
119
119
  - ./lib/fun_with/configurations/config.rb
120
120
  - ./lib/fun_with/configurations/configurations.rb
121
121
  - ./lib/fun_with/configurations/object.rb
122
+ - ./lib/fun_with/configurations/try_object.rb
122
123
  - ./lib/fun_with_configurations.rb
124
+ - ./test/data/config.yml
123
125
  - ./test/helper.rb
124
126
  - ./test/test_fun_with_configurations.rb
125
127
  - Gemfile
@@ -142,7 +144,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
142
144
  version: '0'
143
145
  segments:
144
146
  - 0
145
- hash: 2567250153676636570
147
+ hash: 4158279720265668030
146
148
  required_rubygems_version: !ruby/object:Gem::Requirement
147
149
  none: false
148
150
  requirements: