fun_with_configurations 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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: