class_attr 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,11 @@
1
+ if RUBY_VERSION >= "1.9"
2
+ require 'duvet'
3
+ Duvet.start :filter => 'class_attr/lib'
4
+ end
5
+
6
+ require 'rspec'
7
+ require 'class_attr'
8
+
9
+ RSpec.configure do |config|
10
+ config.color_enabled = true
11
+ end
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