class_attr 0.1.0
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/LICENSE +20 -0
- data/README.md +71 -0
- data/lib/class_attr.rb +184 -0
- data/spec/class_attr_spec.rb +167 -0
- data/spec/spec_helper.rb +11 -0
- metadata +69 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Joshua Hawxwell
|
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.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# ClassAttr
|
2
|
+
|
3
|
+
Adds `#class_attr_accessor` (and reader/writer) and `#inheritable_class_attr_accessor` (and
|
4
|
+
reader/writer of course) to Class.
|
5
|
+
|
6
|
+
class Polygon
|
7
|
+
class_attr_accessor :sides
|
8
|
+
end
|
9
|
+
|
10
|
+
Polygon.sides = 5
|
11
|
+
Polygon.sides #=> 5
|
12
|
+
|
13
|
+
class Square < Polygon
|
14
|
+
end
|
15
|
+
|
16
|
+
Square.sides #=> nil
|
17
|
+
|
18
|
+
|
19
|
+
class InheritablePolygon
|
20
|
+
inheritable_class_attr_accessor :sides
|
21
|
+
end
|
22
|
+
|
23
|
+
Polygon.sides = 4
|
24
|
+
|
25
|
+
class NewSquare < Polygon
|
26
|
+
end
|
27
|
+
|
28
|
+
Square.sides #=> 4
|
29
|
+
|
30
|
+
You can provide default values using a hash with :default set for the last value, or if just
|
31
|
+
creating one accessor/reader/writer add `=> defaultvalue` to the end, this example should
|
32
|
+
make it more clear:
|
33
|
+
|
34
|
+
class Person
|
35
|
+
class_attr_accessor :name => 'John Doe'
|
36
|
+
inheritable_class_attr_accessor :arms, :legs, :default => 2
|
37
|
+
inheritable_class_attr_accessor :fingers, :toes, :default => 5
|
38
|
+
end
|
39
|
+
|
40
|
+
class Agent < Person
|
41
|
+
@name = "James Bond"
|
42
|
+
end
|
43
|
+
|
44
|
+
Person.name #=> "John Doe"
|
45
|
+
Person.arms #=> 2
|
46
|
+
Person.toes #=> 5
|
47
|
+
Agent.name #=> "James Bond"
|
48
|
+
Agent.legs #=> 2
|
49
|
+
|
50
|
+
|
51
|
+
### Install
|
52
|
+
|
53
|
+
(sudo) gem install class_attr
|
54
|
+
|
55
|
+
|
56
|
+
### Use
|
57
|
+
|
58
|
+
require 'class_attr'
|
59
|
+
|
60
|
+
|
61
|
+
## Thanks
|
62
|
+
|
63
|
+
- <http://railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/>
|
64
|
+
For originally demystifying the class instance variable thing.
|
65
|
+
- <http://www.raulparolari.com/Rails/class_inheritable> For ideas on how to implement
|
66
|
+
class level attribute accessors which inherit values.
|
67
|
+
|
68
|
+
|
69
|
+
## Copyright
|
70
|
+
|
71
|
+
Copyright (c) 2010 Joshua Hawxwell. See LICENSE for details.
|
data/lib/class_attr.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
class Class
|
2
|
+
|
3
|
+
# Defines a method that allows you to read an instance variable set at the
|
4
|
+
# class level. Also defines an _instance_ method that reads the same thing.
|
5
|
+
# Comparable to #attr_reader for the class level.
|
6
|
+
#
|
7
|
+
# So in summary defines: .var (which gets @var) and
|
8
|
+
# #var (which gets self.class.var)
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
#
|
12
|
+
# class Polygon
|
13
|
+
# class_attr_reader :sides
|
14
|
+
#
|
15
|
+
# def self.is_a=(shape)
|
16
|
+
# case shape
|
17
|
+
# when 'triangle' then @sides = 3
|
18
|
+
# when 'square' then @sides = 4
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# Polygon.sides #=> nil
|
24
|
+
# Polygon.is_a 'triangle'
|
25
|
+
# Polygon.sides #=> 3
|
26
|
+
# t = Polygon.new
|
27
|
+
# t.sides #=> 3
|
28
|
+
#
|
29
|
+
def class_attr_reader(*args)
|
30
|
+
names, default = separate_argument_list_and_default(args)
|
31
|
+
names.each do |name|
|
32
|
+
class_eval <<-EOS
|
33
|
+
def self.#{name}
|
34
|
+
@#{name}
|
35
|
+
end
|
36
|
+
|
37
|
+
def #{name}
|
38
|
+
self.class.send(:#{name})
|
39
|
+
end
|
40
|
+
EOS
|
41
|
+
self.instance_variable_set("@#{name}", default)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Defines a method for to write to an instance varaible set at the class
|
46
|
+
# level. Comparable to #attr_writer for the class level.
|
47
|
+
#
|
48
|
+
# So in summary defines: .var= (which sets @var)
|
49
|
+
#
|
50
|
+
# @example
|
51
|
+
#
|
52
|
+
# class Polygon
|
53
|
+
# class_attr_writer :sides
|
54
|
+
#
|
55
|
+
# def self.rectangle?; @sides == 4; end
|
56
|
+
# def self.triangle?; @sides == 3; end
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# Polygon.rectangle? #=> false
|
60
|
+
# Polygon.sides = 4
|
61
|
+
# Polygon.rectangle? #=> true
|
62
|
+
# Polygon.sides = 3
|
63
|
+
# Polygon.triangle? #=> true
|
64
|
+
#
|
65
|
+
def class_attr_writer(*args)
|
66
|
+
names, default = separate_argument_list_and_default(args)
|
67
|
+
names.each do |name|
|
68
|
+
class_eval <<-EOS
|
69
|
+
def self.#{name}=(obj)
|
70
|
+
@#{name} = obj
|
71
|
+
end
|
72
|
+
EOS
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Executes #class_attr_reader and #class_attr_writer.
|
77
|
+
#
|
78
|
+
# @see #class_attr_reader
|
79
|
+
# @see #class_attr_writer
|
80
|
+
#
|
81
|
+
# @example
|
82
|
+
#
|
83
|
+
# class Polygon
|
84
|
+
# class_attr_accessor :sides
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
# Polygon.sides #=> nil
|
88
|
+
# Polygon.sides = 5
|
89
|
+
# Polygon.sides #=> 5
|
90
|
+
# pentagon = Polygon.new
|
91
|
+
# pentagon.sides #=> 5
|
92
|
+
#
|
93
|
+
def class_attr_accessor(*names)
|
94
|
+
class_attr_reader(*names)
|
95
|
+
class_attr_writer(*names)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Creates a class and instance method to read the class level variable(s)
|
99
|
+
# with the name(s) provided. If no value has been set it attempts to use
|
100
|
+
# the value of the superclass.
|
101
|
+
#
|
102
|
+
# @see #class_attr_reader
|
103
|
+
#
|
104
|
+
def inheritable_class_attr_reader(*args)
|
105
|
+
names, default = separate_argument_list_and_default(args)
|
106
|
+
names.each do |name|
|
107
|
+
class_eval <<-EOS
|
108
|
+
def self.#{name}
|
109
|
+
if @#{name}
|
110
|
+
@#{name}
|
111
|
+
elsif superclass.respond_to? :#{name}
|
112
|
+
@#{name} ||= superclass.#{name}
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def #{name}
|
117
|
+
self.class.send(:#{name})
|
118
|
+
end
|
119
|
+
EOS
|
120
|
+
self.instance_variable_set("@#{name}", default)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# The same as #class_attr_writer.
|
125
|
+
def inheritable_class_attr_writer(*args)
|
126
|
+
class_attr_writer(*args)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Executes #inheritable_class_attr_reader and #inheritable_class_attr_writer.
|
130
|
+
#
|
131
|
+
# @see #inheritable_class_attr_reader
|
132
|
+
# @see #inheritable_class_attr_writer
|
133
|
+
#
|
134
|
+
# @example
|
135
|
+
#
|
136
|
+
# class Polygon
|
137
|
+
# inheritable_class_attr_accessor :sides, :angles
|
138
|
+
# self.angles = [90]
|
139
|
+
# end
|
140
|
+
#
|
141
|
+
# class Triangle < Polygon
|
142
|
+
# self.sides = 3
|
143
|
+
# self.angles = [100, 35, 45]
|
144
|
+
# end
|
145
|
+
#
|
146
|
+
# class Rectangle < Polygon
|
147
|
+
# self.sides = 4
|
148
|
+
# self.angles << 60 << 100 << 110
|
149
|
+
# end
|
150
|
+
#
|
151
|
+
# class Square < Rectangle
|
152
|
+
# # should get 4 sides from rectange
|
153
|
+
# self.angles = [90] * 4
|
154
|
+
# end
|
155
|
+
#
|
156
|
+
# Polygon.sides #=> nil
|
157
|
+
# Triangle.sides #=> 3
|
158
|
+
# Rectangle.sides #=> 4
|
159
|
+
# Rectangle.angles #=> [90, 60, 100, 110]
|
160
|
+
# Square.sides #=> 4
|
161
|
+
# Square.angles #=> [90, 90, 90, 90]
|
162
|
+
#
|
163
|
+
def inheritable_class_attr_accessor(*names)
|
164
|
+
inheritable_class_attr_reader(*names)
|
165
|
+
inheritable_class_attr_writer(*names)
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
# @param args [Array]
|
172
|
+
# @return [Array[Array, Object]]
|
173
|
+
#
|
174
|
+
def separate_argument_list_and_default(args)
|
175
|
+
if args.size == 1 && args.first.is_a?(Hash)
|
176
|
+
[[args.first.keys[0]], args.first.values[0]]
|
177
|
+
elsif args.last.is_a?(Hash)
|
178
|
+
[args[0..-2], args.last[:default]]
|
179
|
+
else
|
180
|
+
[args, nil]
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Object do
|
4
|
+
|
5
|
+
describe "#class_attr_reader" do
|
6
|
+
subject { Class.new { class_attr_reader :test } }
|
7
|
+
|
8
|
+
it "defines a read method for the class" do
|
9
|
+
subject.should respond_to :test
|
10
|
+
end
|
11
|
+
|
12
|
+
it "defines a read method for instances of the class" do
|
13
|
+
subject.new.should respond_to :test
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#class_attr_writer" do
|
18
|
+
subject { Class.new { class_attr_writer :test } }
|
19
|
+
|
20
|
+
it "defines a write method for the class" do
|
21
|
+
subject.should respond_to :test=
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#class_attr_accessor" do
|
26
|
+
subject { Class.new { class_attr_accessor :test } }
|
27
|
+
|
28
|
+
it "defines a read method for the class" do
|
29
|
+
subject.should respond_to :test
|
30
|
+
end
|
31
|
+
|
32
|
+
it "defines a write method for the class" do
|
33
|
+
subject.should respond_to :test=
|
34
|
+
end
|
35
|
+
|
36
|
+
it "defines a read method for instances of the class" do
|
37
|
+
subject.new.should respond_to :test
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# The subclass thing was starting to annoy me...
|
42
|
+
context "When there is a subclass of a class with class_attrs" do
|
43
|
+
|
44
|
+
let(:sup) { Class.new { class_attr_accessor :test } }
|
45
|
+
let(:sub) { Class.new(sup) }
|
46
|
+
|
47
|
+
it "has the methods of the superclass" do
|
48
|
+
sub.should respond_to :test
|
49
|
+
sub.should respond_to :test=
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "calling the writer method on the subclass" do
|
53
|
+
it "should not alter the value on the superclass" do
|
54
|
+
sub.test = "changed"
|
55
|
+
sub.test.should == "changed"
|
56
|
+
sup.test.should_not == "changed"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "calling the writer method on the superclass" do
|
61
|
+
it "should not alter the value on the subclass" do
|
62
|
+
sup.test = "changed again"
|
63
|
+
sup.test.should == "changed again"
|
64
|
+
sub.test.should_not == "changed again"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
# Inheritable class attributes
|
71
|
+
|
72
|
+
describe "#inheritable_class_attr_reader" do
|
73
|
+
subject { Class.new { inheritable_class_attr_reader :test } }
|
74
|
+
|
75
|
+
it "defines a read method for the class" do
|
76
|
+
subject.should respond_to :test
|
77
|
+
end
|
78
|
+
|
79
|
+
it "defines a read method for instances of the class" do
|
80
|
+
subject.new.should respond_to :test
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#inheritable_class_attr_writer" do
|
85
|
+
subject { Class.new { inheritable_class_attr_writer :test } }
|
86
|
+
|
87
|
+
it "defines a write method for the class" do
|
88
|
+
subject.should respond_to :test=
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "#inheritable_class_attr_accessor" do
|
93
|
+
subject { Class.new { inheritable_class_attr_accessor :test } }
|
94
|
+
|
95
|
+
it "defines a read method for the class" do
|
96
|
+
subject.should respond_to :test
|
97
|
+
end
|
98
|
+
|
99
|
+
it "defines a write method for the class" do
|
100
|
+
subject.should respond_to :test=
|
101
|
+
end
|
102
|
+
|
103
|
+
it "defines a read method for instances of the class" do
|
104
|
+
subject.new.should respond_to :test
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context "When there is a subclass of a class with inheritable_class_attrs" do
|
109
|
+
|
110
|
+
let(:sup) { Class.new { inheritable_class_attr_accessor :test } }
|
111
|
+
let(:sub) { Class.new(sup) }
|
112
|
+
|
113
|
+
it "has the methods of the superclass" do
|
114
|
+
sub.should respond_to :test
|
115
|
+
sub.should respond_to :test=
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "calling the writer method on the subclass" do
|
119
|
+
it "should not alter the value on the superclass" do
|
120
|
+
sub.test = "changed"
|
121
|
+
sub.test.should == "changed"
|
122
|
+
sup.test.should_not == "changed"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "calling the writer method on the superclass" do
|
127
|
+
it "should alter the value on the subclass if not set" do
|
128
|
+
sup.test = "changed it"
|
129
|
+
sup.test.should == "changed it"
|
130
|
+
sub.test.should == "changed it"
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should not alter the value on the subclass if set" do
|
134
|
+
sub.test = "already set"
|
135
|
+
sup.test = "change it"
|
136
|
+
sup.test.should == "change it"
|
137
|
+
sub.test.should == "already set"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
describe "#separate_argument_list_and_default" do
|
144
|
+
def call_it(*args)
|
145
|
+
Class.send(:separate_argument_list_and_default, *args)
|
146
|
+
end
|
147
|
+
|
148
|
+
context "when given a list of arguments with a default" do
|
149
|
+
it "returns an array with the arguments and a default value" do
|
150
|
+
call_it([:one, :two, :three, {:default => 0}]).should == [[:one, :two, :three], 0]
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context "when given an argument and a default" do
|
155
|
+
it "returns an array with the arguments and a default value" do
|
156
|
+
call_it([{:one => 1}]).should == [[:one], 1]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context "when given just arguments" do
|
161
|
+
it "returns and array with the arguments and nil" do
|
162
|
+
call_it([:one, :two, :three]).should == [[:one, :two, :three], nil]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: class_attr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Joshua Hawxwell
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-01-29 00:00:00 +00:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: " Provides attr_accessor style methods for easily creating methods for\n working with class level instance variables. Allows you to set variables\n to be inheritable or not, and have default values.\n"
|
22
|
+
email: m@hawx.me
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- README.md
|
31
|
+
- LICENSE
|
32
|
+
- lib/class_attr.rb
|
33
|
+
- spec/class_attr_spec.rb
|
34
|
+
- spec/spec_helper.rb
|
35
|
+
has_rdoc: false
|
36
|
+
homepage: http://github.com/hawx/class_attr
|
37
|
+
licenses: []
|
38
|
+
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
none: false
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
segments:
|
50
|
+
- 0
|
51
|
+
version: "0"
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
requirements: []
|
61
|
+
|
62
|
+
rubyforge_project:
|
63
|
+
rubygems_version: 1.3.7
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: attr_accessor for class level instance variables.
|
67
|
+
test_files:
|
68
|
+
- spec/class_attr_spec.rb
|
69
|
+
- spec/spec_helper.rb
|