config_accessor 0.0.2 → 0.0.3

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/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
- Local.port # => 80
47
+ # or
48
+ Local.configure do |config|
49
+ config.port 81
50
+ end
@@ -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
 
@@ -1,4 +1,5 @@
1
1
  require "config_accessor/version"
2
+ require "config_accessor/config"
2
3
  require "config_accessor/class_methods"
3
4
  require "config_accessor/instance_methods"
4
5
 
@@ -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
- 'if and only if one argument supplied' if options.has_key?(:alias) &&
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, method, ali=nil)
34
- define_method(symbol, method)
35
- define_singleton_method(symbol, method)
36
- define_config_accessor_aliases(symbol, ali.to_sym) if ali
37
- end
38
-
39
- # Clone class-level configuration for each instance
40
- def new(*args, &block) #:nodoc:
41
- instance = super(*args, &block)
42
-
43
- # clone class-level variables
44
- defined_config_accessors.each do |name, val|
45
- instance.write_config_value(name, ConfigAccessor.try_duplicate(val))
46
- end
47
-
48
- instance
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
- transform = if t = options.delete(:transform)
55
- raise TypeError, 'transformer must be Symbol, Method or Proc' unless t.respond_to?(:to_proc)
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, accessor, options.delete(:alias))
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
- self.instance_eval(&block)
8
-
9
- self
10
- end
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
- # Reads configuration value (supports inheritance)
18
- def read_config_value(name)
19
- parent_config_accessors.merge(@_config_accessors || {})[name.to_sym]
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
- # inheritable config accessors array
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?(:defined_config_accessors) ? superklass.defined_config_accessors : {}
39
+ superklass.respond_to?(:config) ? superklass.config : {}
31
40
  end
32
41
  end
33
42
  end
@@ -1,3 +1,3 @@
1
1
  module ConfigAccessor
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  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
- - 2
9
- version: 0.0.2
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