Roman2K-class-inheritable-attributes 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2008-2009 Roman Le Négrate
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.mdown ADDED
@@ -0,0 +1,56 @@
1
+ # Thread-safe class inheritable attributes
2
+
3
+ [GitHub repository](https://github.com/Roman2K/class-inheritable-attributes).
4
+
5
+ ## Installation
6
+
7
+ $ gem install Roman2K-class-inheritable-attributes -s http://gems.github.com/
8
+
9
+ ## Features
10
+
11
+ Allows for defining class-level attributes whose values are:
12
+
13
+ * inherited by subclasses,
14
+ * stored in a thread safe manner,
15
+ * accessible via calls to `super`,
16
+ * stored in a memory efficient manner (the registry shrinks itself on `nil` values).
17
+
18
+ ## Example usage
19
+
20
+ require 'class_inheritable_attributes'
21
+
22
+ class Resource
23
+ class_inheritable_attr_accessor :site, :timeout
24
+
25
+ def self.timeout
26
+ super || 10
27
+ end
28
+
29
+ def self.timeout=(seconds)
30
+ super(seconds.to_i)
31
+ end
32
+ end
33
+
34
+ Resource.timeout # => 10
35
+ Resource.timeout = "5"
36
+ Resource.timeout # => 5
37
+
38
+ class AccountingResource < Resource
39
+ self.site = "http://account.example.com"
40
+ end
41
+
42
+ class Balance < AccountingResource
43
+ end
44
+
45
+ # Child's value defaults to parent's
46
+ AccountingResource.site # => "http://account.example.com"
47
+ Balance.site # => "http://account.example.com"
48
+
49
+ # Child can set its own value
50
+ Balance.site = "http://balance.account.example.com"
51
+ AccountingResource.site # => "http://account.example.com"
52
+ Balance.site # => "http://balance.account.example.com"
53
+
54
+ ## Credits
55
+
56
+ Written by [Roman Le Négrate](http://roman.flucti.com) ([contact](mailto:roman.lenegrate@gmail.com)). Released under the MIT license: see the `LICENSE` file.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "echoe"
2
+
3
+ Echoe.new('class-inheritable-attributes', '0.1.1') do |p|
4
+ p.description = "Thread-safe class inheritable attributes"
5
+ p.url = "https://github.com/Roman2K/class-inheritable-attributes"
6
+ p.author = "Roman Le Négrate"
7
+ p.email = "roman.lenegrate@gmail.com"
8
+ p.ignore_pattern = "*.gemspec"
9
+ p.development_dependencies = ["mocha"]
10
+ p.rdoc_options = %w(--main README.mdown --inline-source --line-numbers --charset UTF-8)
11
+ end
@@ -0,0 +1,35 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{class-inheritable-attributes}
5
+ s.version = "0.1.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Roman Le N\303\251grate"]
9
+ s.date = %q{2009-03-17}
10
+ s.description = %q{Thread-safe class inheritable attributes}
11
+ s.email = %q{roman.lenegrate@gmail.com}
12
+ s.extra_rdoc_files = ["lib/class_inheritable_attributes.rb", "LICENSE", "README.mdown"]
13
+ s.files = ["lib/class_inheritable_attributes.rb", "LICENSE", "Manifest", "Rakefile", "README.mdown", "test/class_inheritable_attributes_test.rb", "class-inheritable-attributes.gemspec"]
14
+ s.has_rdoc = true
15
+ s.homepage = %q{https://github.com/Roman2K/class-inheritable-attributes}
16
+ s.rdoc_options = ["--main", "README.mdown", "--inline-source", "--line-numbers", "--charset", "UTF-8"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{class-inheritable-attributes}
19
+ s.rubygems_version = %q{1.3.1}
20
+ s.summary = %q{Thread-safe class inheritable attributes}
21
+ s.test_files = ["test/class_inheritable_attributes_test.rb"]
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 2
26
+
27
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
28
+ s.add_development_dependency(%q<mocha>, [">= 0"])
29
+ else
30
+ s.add_dependency(%q<mocha>, [">= 0"])
31
+ end
32
+ else
33
+ s.add_dependency(%q<mocha>, [">= 0"])
34
+ end
35
+ end
@@ -0,0 +1,82 @@
1
+ module ClassInheritableAttributes
2
+ class << self
3
+ def eval_in_accessor_module(klass, code)
4
+ mod = klass.instance_eval { @_inheritable_attribute_accessors ||= Module.new }
5
+ klass.extend(mod)
6
+ mod.module_eval(code)
7
+ end
8
+
9
+ def fetch_value(klass, attribute)
10
+ each_parent(klass) do |parent|
11
+ if values = registry[parent] and values.key?(attribute)
12
+ return values[attribute]
13
+ elsif parent.instance_variable_defined?("@#{attribute}")
14
+ return parent.instance_variable_get("@#{attribute}")
15
+ end
16
+ end
17
+ return nil
18
+ end
19
+
20
+ def store_value(klass, attribute, value)
21
+ if Thread.current == Thread.main
22
+ if value.nil?
23
+ klass.instance_eval do
24
+ remove_instance_variable("@#{attribute}") if instance_variable_defined?("@#{attribute}")
25
+ end
26
+ else
27
+ klass.instance_variable_set("@#{attribute}", value)
28
+ end
29
+ else
30
+ registry[klass] ||= {}
31
+ if value.nil?
32
+ registry[klass].delete(attribute)
33
+ registry.delete(klass) if registry[klass].empty?
34
+ else
35
+ registry[klass][attribute] = value
36
+ end
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def each_parent(klass)
43
+ while klass < Object
44
+ yield klass
45
+ klass = klass.superclass
46
+ end
47
+ end
48
+
49
+ def registry
50
+ Thread.current[ClassInheritableAttributes.name] ||= {}
51
+ end
52
+ end
53
+
54
+ def class_inheritable_attr_reader(*attributes)
55
+ attributes.each do |attribute|
56
+ ClassInheritableAttributes.eval_in_accessor_module(self, <<-EOS)
57
+ def #{attribute}
58
+ #{ClassInheritableAttributes}.fetch_value(self, :#{attribute})
59
+ end
60
+ EOS
61
+ end
62
+ end
63
+
64
+ def class_inheritable_attr_writer(*attributes)
65
+ attributes.each do |attribute|
66
+ ClassInheritableAttributes.eval_in_accessor_module(self, <<-EOS)
67
+ def #{attribute}=(value)
68
+ #{ClassInheritableAttributes}.store_value(self, :#{attribute}, value)
69
+ end
70
+ EOS
71
+ end
72
+ end
73
+
74
+ def class_inheritable_attr_accessor(*attributes)
75
+ class_inheritable_attr_reader(*attributes)
76
+ class_inheritable_attr_writer(*attributes)
77
+ end
78
+ end
79
+
80
+ Class.class_eval do
81
+ include ClassInheritableAttributes
82
+ end
@@ -0,0 +1,85 @@
1
+ require 'test/unit'
2
+ require 'mocha'
3
+
4
+ require 'class_inheritable_attributes'
5
+
6
+ class ClassInheritableAttributesTest < Test::Unit::TestCase
7
+ def setup
8
+ @class = Class.new { class_inheritable_attr_accessor :foo }
9
+ end
10
+
11
+ def test_initial_value
12
+ assert_equal nil, @class.foo
13
+ end
14
+
15
+ def test_thread_overrideability
16
+ @class.foo = :bar
17
+ assert_equal :over, Thread.new { @class.foo = :over; @class.foo }.value
18
+ assert_equal :bar, @class.foo
19
+ end
20
+
21
+ def test_inheritability
22
+ child = Class.new(@class)
23
+
24
+ @class.foo = :bar
25
+ assert_equal :bar, child.foo
26
+
27
+ @class.foo = :bar
28
+ child.foo = :other
29
+ assert_equal :other, child.foo
30
+ assert_equal :bar, @class.foo
31
+
32
+ # Setting a value to nil, in the main thread
33
+ @class.foo = :bar
34
+ child.foo = nil
35
+ assert_equal :bar, child.foo
36
+ assert_equal :bar, @class.foo
37
+
38
+ # Setting a value that wasn't previously assigned to nil, in the main thread
39
+ @class.foo = :bar
40
+ other_child = Class.new(@class)
41
+ other_child.foo = nil
42
+ assert_equal :bar, other_child.foo
43
+
44
+ # Setting a value to nil, in a separate thread
45
+ @class.foo = :bar
46
+ result = Thread.new { child.foo = nil; child.foo }.value
47
+ assert_equal :bar, result
48
+
49
+ @class.foo = :bar
50
+ child.foo = :other
51
+ assert_equal :in_thread, Thread.new { child.foo = :in_thread; child.foo }.value
52
+ assert_equal :other, child.foo
53
+ assert_equal :bar, @class.foo
54
+
55
+ @class.foo = :bar
56
+ child.foo = :other
57
+ @class.foo = :changed
58
+ assert_equal :other, child.foo
59
+ assert_equal :changed, @class.foo
60
+ end
61
+
62
+ def test_housekeeping
63
+ Thread.new do
64
+ assert_equal 0, ClassInheritableAttributes.instance_eval { registry }.size
65
+ @class.foo = nil
66
+ assert_equal 0, ClassInheritableAttributes.instance_eval { registry }.size
67
+ @class.foo = :bar
68
+ assert_equal 1, ClassInheritableAttributes.instance_eval { registry }.size
69
+ @class.foo = nil
70
+ assert_equal 0, ClassInheritableAttributes.instance_eval { registry }.size
71
+ end.join
72
+ end
73
+
74
+ def test_attribute_methods_are_defined_in_ancestors
75
+ c = @class
76
+ def c.foo
77
+ super || :default
78
+ end
79
+
80
+ assert_equal :default, c.foo
81
+
82
+ c.foo = :explicit
83
+ assert_equal :explicit, c.foo
84
+ end
85
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: Roman2K-class-inheritable-attributes
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - "Roman Le N\xC3\xA9grate"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-17 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: mocha
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: Thread-safe class inheritable attributes
26
+ email: roman.lenegrate@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - lib/class_inheritable_attributes.rb
33
+ - LICENSE
34
+ - README.mdown
35
+ files:
36
+ - lib/class_inheritable_attributes.rb
37
+ - LICENSE
38
+ - Manifest
39
+ - Rakefile
40
+ - README.mdown
41
+ - test/class_inheritable_attributes_test.rb
42
+ - class-inheritable-attributes.gemspec
43
+ has_rdoc: true
44
+ homepage: https://github.com/Roman2K/class-inheritable-attributes
45
+ post_install_message:
46
+ rdoc_options:
47
+ - --main
48
+ - README.mdown
49
+ - --inline-source
50
+ - --line-numbers
51
+ - --charset
52
+ - UTF-8
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ version:
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "1.2"
66
+ version:
67
+ requirements: []
68
+
69
+ rubyforge_project: class-inheritable-attributes
70
+ rubygems_version: 1.2.0
71
+ signing_key:
72
+ specification_version: 2
73
+ summary: Thread-safe class inheritable attributes
74
+ test_files:
75
+ - test/class_inheritable_attributes_test.rb