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 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