config_accessor 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +17 -1
- data/config_accessor.gemspec +1 -0
- data/lib/config_accessor.rb +1 -0
- data/lib/config_accessor/class_methods.rb +25 -50
- data/lib/config_accessor/config.rb +66 -0
- data/lib/config_accessor/instance_methods.rb +27 -18
- data/lib/config_accessor/version.rb +1 -1
- data/spec/config_accessor_spec.rb +27 -0
- metadata +54 -3
data/README.rdoc
CHANGED
@@ -29,6 +29,22 @@ Class-level configuration DSL
|
|
29
29
|
r.port # => 81
|
30
30
|
Remote.port # => 80
|
31
31
|
Remote.port = 82
|
32
|
+
|
33
|
+
# next expressions are equivalent
|
32
34
|
r.port # => 81
|
35
|
+
r.config[:port] # => 81
|
36
|
+
r.config["port"] # => 81
|
37
|
+
r.config.port # => 81
|
38
|
+
|
39
|
+
# It supports inheritance, subclasses cannot change superclasses configurations
|
40
|
+
Local.port # => 80
|
41
|
+
|
42
|
+
# You can do it with +configure+ method
|
43
|
+
Local.configure do
|
44
|
+
port 81
|
45
|
+
end
|
33
46
|
|
34
|
-
|
47
|
+
# or
|
48
|
+
Local.configure do |config|
|
49
|
+
config.port 81
|
50
|
+
end
|
data/config_accessor.gemspec
CHANGED
@@ -9,6 +9,7 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.email = ["amikhailov83@gmail.com"]
|
10
10
|
s.homepage = "https://github.com/take-five/config_accessor"
|
11
11
|
s.summary = %q{Class-level configurations}
|
12
|
+
s.description = File.read(File.expand_path('../README.rdoc', __FILE__))
|
12
13
|
|
13
14
|
s.rubyforge_project = "config_accessor"
|
14
15
|
|
data/lib/config_accessor.rb
CHANGED
@@ -17,7 +17,7 @@ module ConfigAccessor
|
|
17
17
|
raise ArgumentError, 'config accessors names expected' unless names.length > 0
|
18
18
|
|
19
19
|
raise ArgumentError, ':alias option should be set '\
|
20
|
-
|
20
|
+
'if and only if one argument supplied' if options.has_key?(:alias) &&
|
21
21
|
names.length != 1
|
22
22
|
|
23
23
|
names.each do |name|
|
@@ -30,66 +30,41 @@ module ConfigAccessor
|
|
30
30
|
|
31
31
|
# Defines an instance and a singleton method with (optionally) alias.
|
32
32
|
# The method parameter can be a <tt>Proc</tt>, a <tt>Method</tt> or an <tt>UnboundMethod</tt> object.
|
33
|
-
def define_config_method(symbol,
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
33
|
+
def define_config_method(symbol, ali=nil)
|
34
|
+
class_eval <<-CODE
|
35
|
+
class << self # class << self
|
36
|
+
def #{symbol}(*args, &block) # def port(*args, &block)
|
37
|
+
config.#{symbol}(*args, &block) # config.port(*args, &block)
|
38
|
+
end # end
|
39
|
+
alias #{symbol}= #{symbol} # alias port= port
|
40
|
+
#{"alias #{ali} #{symbol}" if ali} # alias inferred_port port
|
41
|
+
#{"alias #{ali}= #{symbol}" if ali} # alias inferred_port= port
|
42
|
+
end # end
|
43
|
+
|
44
|
+
def #{symbol}(*args, &block) # def port(*args, &block)
|
45
|
+
config.#{symbol}(*args, &block) # config.port(*args, &block)
|
46
|
+
end # end
|
47
|
+
alias #{symbol}= #{symbol} # alias port= port
|
48
|
+
#{"alias #{ali} #{symbol}" if ali} # alias inferred_port port
|
49
|
+
#{"alias #{ali}= #{symbol}" if ali} # alias inferred_port= port
|
50
|
+
CODE
|
49
51
|
end
|
50
52
|
|
51
53
|
private
|
52
54
|
# create universal config accessor
|
53
55
|
def define_config_accessor(name, options) #:nodoc:
|
54
|
-
|
55
|
-
|
56
|
-
t.to_proc
|
57
|
-
end
|
58
|
-
|
59
|
-
# create accessor
|
60
|
-
accessor = proc do |*args, &block|
|
61
|
-
if args.empty? && !block # reader
|
62
|
-
read_config_value(name)
|
63
|
-
else # writer
|
64
|
-
raise ArgumentError, 'Too many arguments (%s for 1)' % args.length if args.length > 1
|
65
|
-
|
66
|
-
val = args.first || block
|
67
|
-
# apply :transform
|
68
|
-
val = transform.call(val) if transform.is_a?(Proc)
|
69
|
-
|
70
|
-
# set
|
71
|
-
write_config_value(name, val)
|
72
|
-
end
|
56
|
+
if t = options.delete(:transform)
|
57
|
+
config.register_transformer(name, t)
|
73
58
|
end
|
74
59
|
|
75
|
-
# set default value
|
76
|
-
accessor.call(options.delete(:default)) if options.has_key?(:default)
|
77
|
-
|
78
60
|
# add method
|
79
|
-
define_config_method(name,
|
61
|
+
define_config_method(name, options.delete(:alias))
|
62
|
+
|
63
|
+
# set default value
|
64
|
+
send(name, options.delete(:default)) if options.has_key?(:default)
|
80
65
|
|
81
66
|
# return nil
|
82
67
|
nil
|
83
68
|
end
|
84
|
-
|
85
|
-
# create alias for both singleton and instance methods
|
86
|
-
def define_config_accessor_aliases(accessor_name, alias_name) #:nodoc:
|
87
|
-
class_eval <<-CODE
|
88
|
-
class << self
|
89
|
-
alias #{alias_name} #{accessor_name}
|
90
|
-
end
|
91
|
-
alias #{alias_name} #{accessor_name}
|
92
|
-
CODE
|
93
|
-
end
|
94
69
|
end
|
95
70
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module ConfigAccessor
|
2
|
+
class Config < Hash #:nodoc:
|
3
|
+
attr_reader :transformers
|
4
|
+
|
5
|
+
def initialize(parent)
|
6
|
+
super()
|
7
|
+
|
8
|
+
@transformers = parent.respond_to?(:transformers) ? parent.transformers.dup : {}
|
9
|
+
|
10
|
+
parent.each_pair do |k, v|
|
11
|
+
self[k] = try_duplicate(v)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Make a deep copy of Config
|
16
|
+
def dup
|
17
|
+
Config.new(self)
|
18
|
+
end
|
19
|
+
|
20
|
+
def register_transformer(name, proc)
|
21
|
+
raise TypeError, 'transformer must be Symbol, Method or Proc' unless proc.respond_to?(:to_proc)
|
22
|
+
|
23
|
+
@transformers[name.to_sym] = proc.to_proc
|
24
|
+
end
|
25
|
+
|
26
|
+
# indifferent access
|
27
|
+
def [](key)
|
28
|
+
super(key.to_sym)
|
29
|
+
end
|
30
|
+
|
31
|
+
# indifferent access
|
32
|
+
def fetch(key)
|
33
|
+
super(key.to_sym)
|
34
|
+
end
|
35
|
+
|
36
|
+
# indifferent access
|
37
|
+
def []=(key, value)
|
38
|
+
super(key.to_sym, value)
|
39
|
+
end
|
40
|
+
|
41
|
+
# uniform access
|
42
|
+
# c = Config.new
|
43
|
+
# c.port 80
|
44
|
+
# c.port # => 80
|
45
|
+
def access(key, *args, &block)
|
46
|
+
if args.empty? && !block
|
47
|
+
self[key]
|
48
|
+
else
|
49
|
+
raise ArgumentError, 'Too many arguments (%s for 1)' % args.length if args.length > 1
|
50
|
+
|
51
|
+
val = args.first || block
|
52
|
+
val = @transformers[key.to_sym].call(val) if @transformers[key.to_sym].respond_to?(:call)
|
53
|
+
|
54
|
+
self[key] = val
|
55
|
+
end
|
56
|
+
end
|
57
|
+
alias method_missing access
|
58
|
+
|
59
|
+
private
|
60
|
+
def try_duplicate(obj) #:nodoc:
|
61
|
+
duplicable = [NilClass, TrueClass, FalseClass, Numeric, Symbol, Class, Module].none? { |klass| obj.is_a?(klass) }
|
62
|
+
|
63
|
+
duplicable ? obj.dup : obj
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -1,33 +1,42 @@
|
|
1
1
|
module ConfigAccessor
|
2
2
|
# Both instance and class-level methods
|
3
3
|
module InstanceMethods
|
4
|
+
# Sexy notation:
|
5
|
+
# class Tank
|
6
|
+
# config_accessor :target
|
7
|
+
# end
|
8
|
+
#
|
9
|
+
# Tank.configure {
|
10
|
+
# target "localhost:80"
|
11
|
+
# }
|
12
|
+
#
|
13
|
+
# or (result is the same):
|
14
|
+
# Tank.configure do |conf|
|
15
|
+
# conf.target "localhost:80"
|
16
|
+
# end
|
4
17
|
def configure(&block)
|
5
18
|
raise ArgumentError, 'Block expected' unless block_given?
|
6
19
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
# Writes names configuration value
|
13
|
-
def write_config_value(name, value)
|
14
|
-
(@_config_accessors ||= {})[name.to_sym] = value
|
20
|
+
if block.arity == 1
|
21
|
+
yield(config)
|
22
|
+
else
|
23
|
+
config.instance_eval(&block)
|
24
|
+
end
|
15
25
|
end
|
16
26
|
|
17
|
-
#
|
18
|
-
def
|
19
|
-
|
27
|
+
# Direct access to configuration values
|
28
|
+
def config
|
29
|
+
if self.is_a?(Class)
|
30
|
+
@_config ||= ConfigAccessor::Config.new(parent_config)
|
31
|
+
else
|
32
|
+
@_config ||= self.class.config.dup
|
33
|
+
end
|
20
34
|
end
|
21
35
|
|
22
36
|
protected
|
23
|
-
|
24
|
-
def defined_config_accessors #:nodoc:
|
25
|
-
@_config_accessors ||= {}
|
26
|
-
end
|
27
|
-
|
28
|
-
def parent_config_accessors #:nodoc:
|
37
|
+
def parent_config #:nodoc:
|
29
38
|
superklass = (self.is_a?(Class) ? self : self.class).superclass
|
30
|
-
superklass.respond_to?(:
|
39
|
+
superklass.respond_to?(:config) ? superklass.config : {}
|
31
40
|
end
|
32
41
|
end
|
33
42
|
end
|
@@ -103,4 +103,31 @@ describe ConfigAccessor do
|
|
103
103
|
c.port "80"
|
104
104
|
c.port.should eq(80)
|
105
105
|
end
|
106
|
+
|
107
|
+
it "should be configurable through configure method" do
|
108
|
+
c = Class.new {
|
109
|
+
configurable!
|
110
|
+
config_accessor :port
|
111
|
+
}
|
112
|
+
|
113
|
+
c.configure {
|
114
|
+
port 80
|
115
|
+
}
|
116
|
+
c.port.should eq(80)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should be compatable with ActiveSupport::Configurable" do
|
120
|
+
c = Class.new {
|
121
|
+
configurable!
|
122
|
+
config_accessor :port
|
123
|
+
}
|
124
|
+
|
125
|
+
i = c.new
|
126
|
+
i.port = 80
|
127
|
+
|
128
|
+
i.config[:port].should eq(80)
|
129
|
+
|
130
|
+
i.configure { |conf| conf[:port] = 81 }
|
131
|
+
i.port.should eq(81)
|
132
|
+
end
|
106
133
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 3
|
9
|
+
version: 0.0.3
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Alexei Mikhailov
|
@@ -30,7 +30,57 @@ dependencies:
|
|
30
30
|
version: "0"
|
31
31
|
type: :development
|
32
32
|
version_requirements: *id001
|
33
|
-
description:
|
33
|
+
description: |-
|
34
|
+
== Synopsys
|
35
|
+
Class-level configuration DSL
|
36
|
+
|
37
|
+
== Installation
|
38
|
+
gem install config_accessor
|
39
|
+
|
40
|
+
== Examples
|
41
|
+
require 'config_accessor'
|
42
|
+
|
43
|
+
class Remote
|
44
|
+
configurable!
|
45
|
+
|
46
|
+
config_accessor :host, :default => "localhost"
|
47
|
+
config_accessor :port, :default => "80", :transform => :to_i
|
48
|
+
config_accessor :proxy_host, :proxy_port
|
49
|
+
end
|
50
|
+
|
51
|
+
class Local < Remote
|
52
|
+
config_accessor :l_port
|
53
|
+
end
|
54
|
+
|
55
|
+
Remote.host # => "localhost"
|
56
|
+
Remote.port # => 80
|
57
|
+
Remote.proxy_host # => nil
|
58
|
+
|
59
|
+
r = Remote.new
|
60
|
+
|
61
|
+
r.port = "81"
|
62
|
+
r.port # => 81
|
63
|
+
Remote.port # => 80
|
64
|
+
Remote.port = 82
|
65
|
+
|
66
|
+
# next expressions are equivalent
|
67
|
+
r.port # => 81
|
68
|
+
r.config[:port] # => 81
|
69
|
+
r.config["port"] # => 81
|
70
|
+
r.config.port # => 81
|
71
|
+
|
72
|
+
# It supports inheritance, subclasses cannot change superclasses configurations
|
73
|
+
Local.port # => 80
|
74
|
+
|
75
|
+
# You can do it with +configure+ method
|
76
|
+
Local.configure do
|
77
|
+
port 81
|
78
|
+
end
|
79
|
+
|
80
|
+
# or
|
81
|
+
Local.configure do |config|
|
82
|
+
config.port 81
|
83
|
+
end
|
34
84
|
email:
|
35
85
|
- amikhailov83@gmail.com
|
36
86
|
executables: []
|
@@ -48,6 +98,7 @@ files:
|
|
48
98
|
- config_accessor.gemspec
|
49
99
|
- lib/config_accessor.rb
|
50
100
|
- lib/config_accessor/class_methods.rb
|
101
|
+
- lib/config_accessor/config.rb
|
51
102
|
- lib/config_accessor/instance_methods.rb
|
52
103
|
- lib/config_accessor/version.rb
|
53
104
|
- spec/config_accessor_spec.rb
|