config_accessor 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/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+ gemspec
3
+
4
+ group :development do
5
+ gem 'rspec'
6
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Alexey Mikhaylov
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,34 @@
1
+ == Synopsys
2
+ Class-level configuration DSL
3
+
4
+ == Installation
5
+ gem install config_accessor
6
+
7
+ == Examples
8
+ require 'config_accessor'
9
+
10
+ class Remote
11
+ configurable!
12
+
13
+ config_accessor :host, :default => "localhost"
14
+ config_accessor :port, :default => "80", :transform => :to_i
15
+ config_accessor :proxy_host, :proxy_port
16
+ end
17
+
18
+ class Local < Remote
19
+ config_accessor :l_port
20
+ end
21
+
22
+ Remote.host # => "localhost"
23
+ Remote.port # => 80
24
+ Remote.proxy_host # => nil
25
+
26
+ r = Remote.new
27
+
28
+ r.port = "81"
29
+ r.port # => 81
30
+ Remote.port # => 80
31
+ Remote.port = 82
32
+ r.port # => 81
33
+
34
+ Local.port # => 80
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "config_accessor/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "config_accessor"
7
+ s.version = ConfigAccessor::VERSION
8
+ s.authors = ["Alexei Mikhailov"]
9
+ s.email = ["amikhailov83@gmail.com"]
10
+ s.homepage = "https://github.com/take-five/config_accessor"
11
+ s.summary = %q{Class-level configurations}
12
+
13
+ s.rubyforge_project = "config_accessor"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ # specify any dependencies here; for example:
21
+ s.add_development_dependency "rspec"
22
+ # s.add_runtime_dependency "rest-client"
23
+ end
@@ -0,0 +1,95 @@
1
+ module ConfigAccessor
2
+ module ClassMethods
3
+ # config_accessor(symbol...) => nil
4
+ #
5
+ # Declare a named config attribute for this class
6
+ # Allowed options:
7
+ # * :transform - Symbol, Proc or Method that must be applied to configuration value before set
8
+ # * :default - Default value for this config section
9
+ # * :alias - alias name for configuration section
10
+ def config_accessor(*names)
11
+ # extract options
12
+ options = names.last.is_a?(Hash) ? names.pop : {}
13
+
14
+ # convert strings to symbols
15
+ names.map!(&:to_sym)
16
+
17
+ raise ArgumentError, 'config accessors names expected' unless names.length > 0
18
+
19
+ raise ArgumentError, ':alias option should be set '\
20
+ 'if and only if one argument supplied' if options.has_key?(:alias) &&
21
+ names.length != 1
22
+
23
+ names.each do |name|
24
+ define_config_accessor(name, options)
25
+ end
26
+
27
+ nil
28
+ end
29
+ alias config_accessors config_accessor
30
+
31
+ # Defines an instance and a singleton method with (optionally) alias.
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
49
+ end
50
+
51
+ private
52
+ # create universal config accessor
53
+ 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
73
+ end
74
+
75
+ # set default value
76
+ accessor.call(options.delete(:default)) if options.has_key?(:default)
77
+
78
+ # add method
79
+ define_config_method(name, accessor, options.delete(:alias))
80
+
81
+ # return nil
82
+ nil
83
+ 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
+ end
95
+ end
@@ -0,0 +1,33 @@
1
+ module ConfigAccessor
2
+ # Both instance and class-level methods
3
+ module InstanceMethods
4
+ def configure(&block)
5
+ raise ArgumentError, 'Block expected' unless block_given?
6
+
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
15
+ end
16
+
17
+ # Reads configuration value (supports inheritance)
18
+ def read_config_value(name)
19
+ parent_config_accessors.merge(@_config_accessors || {})[name.to_sym]
20
+ end
21
+
22
+ protected
23
+ # inheritable config accessors array
24
+ def defined_config_accessors #:nodoc:
25
+ @_config_accessors ||= {}
26
+ end
27
+
28
+ def parent_config_accessors #:nodoc:
29
+ superklass = (self.is_a?(Class) ? self : self.class).superclass
30
+ superklass.respond_to?(:defined_config_accessors) ? superklass.defined_config_accessors : {}
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module ConfigAccessor
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,22 @@
1
+ require "config_accessor/version"
2
+ require "config_accessor/class_methods"
3
+ require "config_accessor/instance_methods"
4
+
5
+ module ConfigAccessor
6
+ def configurable
7
+ extend(ClassMethods)
8
+
9
+ extend(InstanceMethods)
10
+ include(InstanceMethods)
11
+ end
12
+ alias configurable! configurable
13
+
14
+ def try_duplicate(obj) #:nodoc:
15
+ duplicable = [NilClass, TrueClass, FalseClass, Numeric, Symbol, Class, Module].none? { |klass| obj.is_a?(klass) }
16
+
17
+ duplicable ? obj.dup : obj
18
+ end
19
+ module_function :try_duplicate
20
+ end
21
+
22
+ Object.extend(ConfigAccessor)
@@ -0,0 +1,106 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ describe ConfigAccessor do
4
+ it "should be configurable" do
5
+ c = Class.new {
6
+ configurable!
7
+
8
+ config_accessor :a
9
+ }
10
+ c.should respond_to(:a)
11
+ c.new.should respond_to(:a)
12
+
13
+ c.new.should_not respond_to(:config_accessor)
14
+ end
15
+
16
+ it "should be configurable in subclasses" do
17
+ c = Class.new {
18
+ configurable!
19
+
20
+ config_accessor :a
21
+ }
22
+ d = Class.new(c) {}
23
+
24
+ d.should respond_to(:a)
25
+ d.new.should respond_to(:a)
26
+ end
27
+
28
+ it "should be able to set configuration values" do
29
+ c = Class.new {
30
+ configurable!
31
+
32
+ config_accessor :port
33
+ port 80
34
+ }
35
+ d = Class.new(c) {}
36
+ instance = c.new
37
+
38
+ c.port.should eq(80)
39
+ instance.port.should eq(80)
40
+ d.port.should eq(80)
41
+
42
+ instance.port 81
43
+ d.port.should eq(80)
44
+ end
45
+
46
+ it "should not mutate class configuration through instance" do
47
+ c = Class.new {
48
+ configurable!
49
+
50
+ config_accessor :port, :ary
51
+ port 80
52
+ ary []
53
+ }
54
+ instance = c.new
55
+
56
+ instance.port 81
57
+ instance.ary << 1
58
+
59
+ instance.port.should eq(81)
60
+ instance.ary.should eq([1])
61
+
62
+ c.port.should eq(80)
63
+ c.ary.should eq([])
64
+
65
+ c.port 82
66
+ instance.port.should eq(81)
67
+ end
68
+
69
+ it "should be able to set default values" do
70
+ c = Class.new {
71
+ configurable!
72
+
73
+ config_accessor :port, :default => 80
74
+ }
75
+ c.port.should eq(80)
76
+ end
77
+
78
+ it "should be able to define aliases" do
79
+ c = Class.new {
80
+ configurable!
81
+
82
+ config_accessor :port, :alias => :inferred_port, :default => 80
83
+ }
84
+ c.inferred_port.should eq(80)
85
+ end
86
+
87
+ it "should accept blocks as values" do
88
+ c = Class.new {
89
+ configurable!
90
+
91
+ config_accessor :transformer
92
+ transformer { 22 }
93
+ }
94
+ c.transformer.should be_a_kind_of(Proc)
95
+ end
96
+
97
+ it "should transform values" do
98
+ c = Class.new {
99
+ configurable!
100
+
101
+ config_accessor :port, :transform => :to_i
102
+ }
103
+ c.port "80"
104
+ c.port.should eq(80)
105
+ end
106
+ end
@@ -0,0 +1,5 @@
1
+ require "rubygems"
2
+ require "bundler"
3
+ Bundler.setup
4
+
5
+ require "config_accessor"
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: config_accessor
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 2
9
+ version: 0.0.2
10
+ platform: ruby
11
+ authors:
12
+ - Alexei Mikhailov
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-12-10 00:00:00 +06:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :development
32
+ version_requirements: *id001
33
+ description:
34
+ email:
35
+ - amikhailov83@gmail.com
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files: []
41
+
42
+ files:
43
+ - .gitignore
44
+ - Gemfile
45
+ - LICENSE
46
+ - README.rdoc
47
+ - Rakefile
48
+ - config_accessor.gemspec
49
+ - lib/config_accessor.rb
50
+ - lib/config_accessor/class_methods.rb
51
+ - lib/config_accessor/instance_methods.rb
52
+ - lib/config_accessor/version.rb
53
+ - spec/config_accessor_spec.rb
54
+ - spec/test_helper.rb
55
+ has_rdoc: true
56
+ homepage: https://github.com/take-five/config_accessor
57
+ licenses: []
58
+
59
+ post_install_message:
60
+ rdoc_options: []
61
+
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ requirements: []
81
+
82
+ rubyforge_project: config_accessor
83
+ rubygems_version: 1.3.7
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Class-level configurations
87
+ test_files: []
88
+