superclass_delegating_accessor 0.0.1
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 +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +76 -0
- data/Rakefile +1 -0
- data/lib/superclass_delegating_accessor.rb +5 -0
- data/lib/superclass_delegating_accessor/class.rb +1 -0
- data/lib/superclass_delegating_accessor/class/delegating_attributes.rb +41 -0
- data/lib/superclass_delegating_accessor/class/remove_method.rb +7 -0
- data/lib/superclass_delegating_accessor/version.rb +3 -0
- data/superclass_delegating_accessor.gemspec +24 -0
- data/test/delegating_attributes_test.rb +119 -0
- metadata +111 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2014 Akshay Vishnoi
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# SuperclassDelegatingAccessor
|
|
2
|
+
|
|
3
|
+
A Ruby utility that defines both class and instance accessors for class attributes. Creates private *_attr* and *_attr=* methods that can still be used if the public methods are overridden.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
Add this line to your application's Gemfile:
|
|
9
|
+
|
|
10
|
+
```shell
|
|
11
|
+
gem 'superclass_delegating_accessor', '~> 0.0.1'
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
And then execute:
|
|
15
|
+
|
|
16
|
+
```shell
|
|
17
|
+
$ bundle
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or install it yourself as:
|
|
21
|
+
|
|
22
|
+
```shell
|
|
23
|
+
$ gem install superclass_delegating_accessor
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
class Person
|
|
30
|
+
superclass_delegating_accessor :hair_colors
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
Person.hair_colors = [:brown, :black, :blonde, :red]
|
|
34
|
+
Person.hair_colors # => [:brown, :black, :blonde, :red]
|
|
35
|
+
Person.new.hair_colors # => [:brown, :black, :blonde, :red]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
If a subclass changes the value then that would also change the value for parent class. Similarly if parent class changes the value then that would change the value of subclasses too.
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
class Male < Person
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
Male.hair_colors << :blue
|
|
45
|
+
Person.hair_colors # => [:brown, :black, :blonde, :red, :blue]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
To opt out of the instance reader method, pass `instance_reader: false`.
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
class Person
|
|
52
|
+
cattr_accessor :hair_colors, instance_writer: false, instance_reader: false
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
Person.new.hair_colors = [:brown] # => NoMethodError
|
|
56
|
+
Person.new.hair_colors # => NoMethodError
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Or pass `instance_accessor: false`, to opt out both instance methods.
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
class Person
|
|
63
|
+
cattr_accessor :hair_colors, instance_accessor: false
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
Person.new.hair_colors = [:brown] # => NoMethodError
|
|
67
|
+
Person.new.hair_colors # => NoMethodError
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Contributing
|
|
71
|
+
|
|
72
|
+
1. Fork it
|
|
73
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
74
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
75
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
76
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'superclass_delegating_accessor/class/delegating_attributes'
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'superclass_delegating_accessor/class/remove_method'
|
|
2
|
+
|
|
3
|
+
class Class
|
|
4
|
+
def superclass_delegating_accessor(name, options = {})
|
|
5
|
+
# Create private _name and _name= methods that can still be used if the public
|
|
6
|
+
# methods are overridden.
|
|
7
|
+
_superclass_delegating_accessor("_#{name}", options)
|
|
8
|
+
|
|
9
|
+
# Generate the public methods name, name=, and name?.
|
|
10
|
+
# These methods dispatch to the private _name, and _name= methods, making them
|
|
11
|
+
# overridable.
|
|
12
|
+
singleton_class.send(:define_method, name) { send("_#{name}") }
|
|
13
|
+
singleton_class.send(:define_method, "#{name}?") { !!send("_#{name}") }
|
|
14
|
+
singleton_class.send(:define_method, "#{name}=") { |value| send("_#{name}=", value) }
|
|
15
|
+
|
|
16
|
+
# If an instance_reader is needed, generate public instance methods name and name?.
|
|
17
|
+
if options[:instance_reader] != false
|
|
18
|
+
define_method(name) { send("_#{name}") }
|
|
19
|
+
define_method("#{name}?") { !!send("#{name}") }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
# Take the object being set and store it in a method. This gives us automatic
|
|
25
|
+
# inheritance behavior, without having to store the object in an instance
|
|
26
|
+
# variable and look up the superclass chain manually.
|
|
27
|
+
def _stash_object_in_method(object, method, instance_reader = true)
|
|
28
|
+
singleton_class.remove_possible_method!(method)
|
|
29
|
+
singleton_class.send(:define_method, method) { object }
|
|
30
|
+
remove_possible_method!(method)
|
|
31
|
+
define_method(method) { object } if instance_reader
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def _superclass_delegating_accessor(name, options = {})
|
|
35
|
+
singleton_class.send(:define_method, "#{name}=") do |value|
|
|
36
|
+
_stash_object_in_method(value, name, options[:instance_reader] != false)
|
|
37
|
+
end
|
|
38
|
+
send("#{name}=", nil)
|
|
39
|
+
singleton_class.send(:private, name, "#{name}=")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'superclass_delegating_accessor/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "superclass_delegating_accessor"
|
|
8
|
+
spec.version = SuperclassDelegatingAccessor::VERSION
|
|
9
|
+
spec.authors = ["Akshay Vishnoi"]
|
|
10
|
+
spec.email = ["akshay.vishnoi@yahoo.com"]
|
|
11
|
+
spec.description = 'A Ruby utility that defines both class and instance accessors for class attributes. Creates private *_attr* and *_attr=* methods that can still be used if the public methods are overridden.'
|
|
12
|
+
spec.summary = 'A Ruby utility that defines both class and instance accessors for class attributes. Creates private *_attr* and *_attr=* methods that can still be used if the public methods are overridden.'
|
|
13
|
+
spec.homepage = 'https://github.com/akshay-vishnoi/superclass_delegating_accessor'
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files`.split($/)
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
|
22
|
+
spec.add_development_dependency "rake"
|
|
23
|
+
spec.add_development_dependency "minitest", "~> 5.3.1"
|
|
24
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
lib = File.expand_path('../../lib', __FILE__)
|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
|
|
4
|
+
require 'minitest/autorun'
|
|
5
|
+
require 'superclass_delegating_accessor'
|
|
6
|
+
|
|
7
|
+
module DelegatingFixtures
|
|
8
|
+
class Parent
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class Child < Parent
|
|
12
|
+
superclass_delegating_accessor :some_attribute
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class Mokopuna < Child
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class PercysMom
|
|
19
|
+
superclass_delegating_accessor :superpower
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class Percy < PercysMom
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class DelegatingAttributesTest < ::Minitest::Test
|
|
27
|
+
Assertion = Minitest::Assertion
|
|
28
|
+
include DelegatingFixtures
|
|
29
|
+
attr_reader :single_class, :single_class_instance
|
|
30
|
+
|
|
31
|
+
def setup
|
|
32
|
+
@single_class = Class.new(Object)
|
|
33
|
+
@single_class_instance = @single_class.new
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def test_simple_accessor_declaration
|
|
37
|
+
single_class.superclass_delegating_accessor :both
|
|
38
|
+
|
|
39
|
+
# `single_class` should have private accessor methods
|
|
40
|
+
assert single_class.private_methods.include?(:_both)
|
|
41
|
+
assert single_class.private_methods.include?(:_both=)
|
|
42
|
+
|
|
43
|
+
# `single_class` should have public accessor methods
|
|
44
|
+
assert single_class.public_methods.include?(:both)
|
|
45
|
+
assert single_class.public_methods.include?(:both=)
|
|
46
|
+
assert single_class.public_methods.include?(:both?)
|
|
47
|
+
|
|
48
|
+
# `single_class` should have public instance methods
|
|
49
|
+
assert single_class_instance.public_methods.include?(:both)
|
|
50
|
+
assert single_class_instance.public_methods.include?(:both?)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def test_simple_accessor_declaration_with_instance_reader_false
|
|
54
|
+
single_class.superclass_delegating_accessor :no_instance_reader, instance_reader: false
|
|
55
|
+
|
|
56
|
+
# `single_class` should have private accessor methods
|
|
57
|
+
assert single_class.private_methods.include?(:_no_instance_reader)
|
|
58
|
+
assert single_class.private_methods.include?(:_no_instance_reader=)
|
|
59
|
+
|
|
60
|
+
# `single_class` should have public accessor methods
|
|
61
|
+
assert single_class.public_methods.include?(:no_instance_reader)
|
|
62
|
+
assert single_class.public_methods.include?(:no_instance_reader=)
|
|
63
|
+
assert single_class.public_methods.include?(:no_instance_reader?)
|
|
64
|
+
|
|
65
|
+
# `single_class` should not have public instance methods
|
|
66
|
+
assert !single_class_instance.public_methods.include?(:no_instance_reader)
|
|
67
|
+
assert !single_class_instance.public_methods.include?(:no_instance_reader?)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def test_working_with_simple_attributes
|
|
71
|
+
single_class.superclass_delegating_accessor :both
|
|
72
|
+
|
|
73
|
+
single_class.both = "HMMM"
|
|
74
|
+
|
|
75
|
+
assert_equal "HMMM", single_class.both
|
|
76
|
+
assert_equal true, single_class.both?
|
|
77
|
+
|
|
78
|
+
assert_equal "HMMM", single_class.new.both
|
|
79
|
+
assert_equal true, single_class.new.both?
|
|
80
|
+
|
|
81
|
+
single_class.both = false
|
|
82
|
+
assert_equal false, single_class.both?
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def test_child_class_delegates_to_parent_but_can_be_overridden
|
|
86
|
+
parent = Class.new
|
|
87
|
+
parent.superclass_delegating_accessor :both
|
|
88
|
+
child = Class.new(parent)
|
|
89
|
+
parent.both = "1"
|
|
90
|
+
assert_equal "1", child.both
|
|
91
|
+
|
|
92
|
+
child.both = "2"
|
|
93
|
+
assert_equal "1", parent.both
|
|
94
|
+
assert_equal "2", child.both
|
|
95
|
+
|
|
96
|
+
parent.both = "3"
|
|
97
|
+
assert_equal "3", parent.both
|
|
98
|
+
assert_equal "2", child.both
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def test_delegation_stops_at_the_right_level
|
|
102
|
+
assert_nil Percy.superpower
|
|
103
|
+
assert_nil PercysMom.superpower
|
|
104
|
+
|
|
105
|
+
PercysMom.superpower = :heatvision
|
|
106
|
+
assert_equal :heatvision, Percy.superpower
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def test_delegation_stops_for_nil
|
|
110
|
+
Mokopuna.some_attribute = nil
|
|
111
|
+
Child.some_attribute = "1"
|
|
112
|
+
|
|
113
|
+
assert_equal "1", Child.some_attribute
|
|
114
|
+
assert_nil Mokopuna.some_attribute
|
|
115
|
+
ensure
|
|
116
|
+
Child.some_attribute = nil
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: superclass_delegating_accessor
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Akshay Vishnoi
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2014-03-19 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: bundler
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ~>
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '1.3'
|
|
22
|
+
type: :development
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ~>
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '1.3'
|
|
30
|
+
- !ruby/object:Gem::Dependency
|
|
31
|
+
name: rake
|
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
|
33
|
+
none: false
|
|
34
|
+
requirements:
|
|
35
|
+
- - ! '>='
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: '0'
|
|
38
|
+
type: :development
|
|
39
|
+
prerelease: false
|
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ! '>='
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: minitest
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
none: false
|
|
50
|
+
requirements:
|
|
51
|
+
- - ~>
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 5.3.1
|
|
54
|
+
type: :development
|
|
55
|
+
prerelease: false
|
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
+
none: false
|
|
58
|
+
requirements:
|
|
59
|
+
- - ~>
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: 5.3.1
|
|
62
|
+
description: A Ruby utility that defines both class and instance accessors for class
|
|
63
|
+
attributes. Creates private *_attr* and *_attr=* methods that can still be used
|
|
64
|
+
if the public methods are overridden.
|
|
65
|
+
email:
|
|
66
|
+
- akshay.vishnoi@yahoo.com
|
|
67
|
+
executables: []
|
|
68
|
+
extensions: []
|
|
69
|
+
extra_rdoc_files: []
|
|
70
|
+
files:
|
|
71
|
+
- .gitignore
|
|
72
|
+
- Gemfile
|
|
73
|
+
- LICENSE.txt
|
|
74
|
+
- README.md
|
|
75
|
+
- Rakefile
|
|
76
|
+
- lib/superclass_delegating_accessor.rb
|
|
77
|
+
- lib/superclass_delegating_accessor/class.rb
|
|
78
|
+
- lib/superclass_delegating_accessor/class/delegating_attributes.rb
|
|
79
|
+
- lib/superclass_delegating_accessor/class/remove_method.rb
|
|
80
|
+
- lib/superclass_delegating_accessor/version.rb
|
|
81
|
+
- superclass_delegating_accessor.gemspec
|
|
82
|
+
- test/delegating_attributes_test.rb
|
|
83
|
+
homepage: https://github.com/akshay-vishnoi/superclass_delegating_accessor
|
|
84
|
+
licenses:
|
|
85
|
+
- MIT
|
|
86
|
+
post_install_message:
|
|
87
|
+
rdoc_options: []
|
|
88
|
+
require_paths:
|
|
89
|
+
- lib
|
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
91
|
+
none: false
|
|
92
|
+
requirements:
|
|
93
|
+
- - ! '>='
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0'
|
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
97
|
+
none: false
|
|
98
|
+
requirements:
|
|
99
|
+
- - ! '>='
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: '0'
|
|
102
|
+
requirements: []
|
|
103
|
+
rubyforge_project:
|
|
104
|
+
rubygems_version: 1.8.25
|
|
105
|
+
signing_key:
|
|
106
|
+
specification_version: 3
|
|
107
|
+
summary: A Ruby utility that defines both class and instance accessors for class attributes.
|
|
108
|
+
Creates private *_attr* and *_attr=* methods that can still be used if the public
|
|
109
|
+
methods are overridden.
|
|
110
|
+
test_files:
|
|
111
|
+
- test/delegating_attributes_test.rb
|