clafamatt 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,119 @@
1
+ clafamatt
2
+ by Avdi Grimm
3
+ http://clafamatt.rubyforge.org
4
+
5
+ == DESCRIPTION:
6
+
7
+ ClaFamAtt (CLAss FAMily ATTributes) gives you class inheritable attributes
8
+ without all that tedious mucking about with +@inheritable_attributes+.
9
+
10
+ == FEATURES
11
+
12
+ * Fully Spec'd
13
+ * reader/writer methods are cleanly partitioned in dynamically created modules
14
+ * No extra class variables needed for bookkeeping
15
+ * Reader/writer methods are defined once and only once no matter how many classes
16
+ inherit them
17
+ * Family inheritable attributes can be defined in modules
18
+ * Will not pollute the global namespace.
19
+
20
+ == SYNOPSIS:
21
+
22
+ module Shared
23
+ include Clafamatt::Macros
24
+
25
+ class_family_accessor :foo
26
+ end
27
+
28
+ class Parent
29
+ include Shared
30
+
31
+ end
32
+
33
+ class Child < Parent
34
+ class_family_accessor :bar
35
+ end
36
+
37
+ Shared.foo # => nil
38
+ Parent.foo # => nil
39
+ Child.foo # => nil
40
+
41
+ Shared.foo = "klaatu"
42
+ Shared.foo # => "klaatu"
43
+ Parent.foo # => "klaatu"
44
+ Child.foo # => "klaatu"
45
+
46
+ Parent.foo = "nikto"
47
+ Shared.foo # => "klaatu"
48
+ Parent.foo # => "nikto"
49
+ Child.foo # => "nikto"
50
+
51
+ Child.foo = "barada"
52
+ Shared.foo # => "klaatu"
53
+ Parent.foo # => "nikto"
54
+ Child.foo # => "barada"
55
+
56
+ Child.bar = "klaatu"
57
+ Child.bar # => "klaatu"
58
+ Parent.bar # =>
59
+ # ~> -:38: undefined method `bar' for Parent:Class (NoMethodError)
60
+
61
+ == RATIONALE:
62
+
63
+ Clafamatt sprung from a desire to have ActiveSupport-style class inheritable
64
+ attributes without having all of ActiveSupport come with it. It is also an
65
+ attempt to implement class-inheritable attributes in the cleanest way possible.
66
+
67
+ In contrast to the ActiveSupport version, which writes reader/writer methods
68
+ directly into the class being extended, Clafamatt writes it's readers and
69
+ writers into a dynamically created module which is then inserted into the class'
70
+ (or module's) singleton class. Among other benefits, this enables Clafamatt to
71
+ transparently support defining inheritable attributes in either classes or
72
+ modules.
73
+
74
+ The ActiveSupport version keeps inheritable attribute values in a class-level
75
+ @inheritable_attributes variable, which it copies whenever a new class is
76
+ inherited. Clafamatt stays closer to Ruby conventions by storing each
77
+ attribute's value in a correspondingly-named class instance variable - so
78
+ e.g. <code>Foo.bar = 42</code> will set the <code>@bar</code> instance variable.
79
+ This is consistent with how Ruby's built-in attr_* macros behave. Beyond the
80
+ variables used to store values, Clafamatt needs *zero* class instance variables
81
+ for internal bookeeping. It also doesn't copy variables on inheritance;
82
+ instead, it simply re-uses values from farther up the inheritance chain until an
83
+ attribute is explicitly set.
84
+
85
+ Finally, Clafamatt is only there when you ask for it, by including
86
+ Clafamatt::Macros. It will not pollute the global namespace.
87
+
88
+ == REQUIREMENTS:
89
+
90
+ * Ruby!
91
+
92
+ == INSTALL:
93
+
94
+ sudo gem install clafamatt
95
+
96
+ == LICENSE:
97
+
98
+ (The MIT License)
99
+
100
+ Copyright (c) 2008 Avdi Grimm
101
+
102
+ Permission is hereby granted, free of charge, to any person obtaining
103
+ a copy of this software and associated documentation files (the
104
+ 'Software'), to deal in the Software without restriction, including
105
+ without limitation the rights to use, copy, modify, merge, publish,
106
+ distribute, sublicense, and/or sell copies of the Software, and to
107
+ permit persons to whom the Software is furnished to do so, subject to
108
+ the following conditions:
109
+
110
+ The above copyright notice and this permission notice shall be
111
+ included in all copies or substantial portions of the Software.
112
+
113
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
114
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
115
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
116
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
117
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
118
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
119
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,4 @@
1
+ == 1.0.0 / 2008-12-14
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
@@ -0,0 +1,24 @@
1
+ #README.rdoc#
2
+ History.txt
3
+ Manifest.txt
4
+ README.rdoc
5
+ Rakefile
6
+ bin/clafamatt
7
+ examples/synopsys.rb
8
+ lib/clafamatt.rb
9
+ spec/clafamatt_spec.rb
10
+ spec/spec_helper.rb
11
+ tasks/ann.rake
12
+ tasks/bones.rake
13
+ tasks/gem.rake
14
+ tasks/git.rake
15
+ tasks/manifest.rake
16
+ tasks/notes.rake
17
+ tasks/post_load.rake
18
+ tasks/rdoc.rake
19
+ tasks/rubyforge.rake
20
+ tasks/setup.rb
21
+ tasks/spec.rake
22
+ tasks/svn.rake
23
+ tasks/test.rake
24
+ test/test_clafamatt.rb
@@ -0,0 +1,119 @@
1
+ clafamatt
2
+ by Avdi Grimm
3
+ http://clafamatt.rubyforge.org
4
+
5
+ == DESCRIPTION:
6
+
7
+ ClaFamAtt (CLAss FAMily ATTributes) gives you class inheritable attributes
8
+ without all that tedious mucking about with +@inheritable_attributes+.
9
+
10
+ == FEATURES
11
+
12
+ * Fully Spec'd
13
+ * reader/writer methods are cleanly partitioned in dynamically created modules
14
+ * No extra class variables needed for bookkeeping
15
+ * Reader/writer methods are defined once and only once no matter how many classes
16
+ inherit them
17
+ * Family inheritable attributes can be defined in modules
18
+ * Will not pollute the global namespace.
19
+
20
+ == SYNOPSIS:
21
+
22
+ module Shared
23
+ include Clafamatt::Macros
24
+
25
+ class_family_accessor :foo
26
+ end
27
+
28
+ class Parent
29
+ include Shared
30
+
31
+ end
32
+
33
+ class Child < Parent
34
+ class_family_accessor :bar
35
+ end
36
+
37
+ Shared.foo # => nil
38
+ Parent.foo # => nil
39
+ Child.foo # => nil
40
+
41
+ Shared.foo = "klaatu"
42
+ Shared.foo # => "klaatu"
43
+ Parent.foo # => "klaatu"
44
+ Child.foo # => "klaatu"
45
+
46
+ Parent.foo = "nikto"
47
+ Shared.foo # => "klaatu"
48
+ Parent.foo # => "nikto"
49
+ Child.foo # => "nikto"
50
+
51
+ Child.foo = "barada"
52
+ Shared.foo # => "klaatu"
53
+ Parent.foo # => "nikto"
54
+ Child.foo # => "barada"
55
+
56
+ Child.bar = "klaatu"
57
+ Child.bar # => "klaatu"
58
+ Parent.bar # =>
59
+ # ~> -:38: undefined method `bar' for Parent:Class (NoMethodError)
60
+
61
+ == RATIONALE:
62
+
63
+ Clafamatt sprung from a desire to have ActiveSupport-style class inheritable
64
+ attributes without having all of ActiveSupport come with it. It is also an
65
+ attempt to implement class-inheritable attributes in the cleanest way possible.
66
+
67
+ In contrast to the ActiveSupport version, which writes reader/writer methods
68
+ directly into the class being extended, Clafamatt writes it's readers and
69
+ writers into a dynamically created module which is then inserted into the class'
70
+ (or module's) singleton class. Among other benefits, this enables Clafamatt to
71
+ transparently support defining inheritable attributes in either classes or
72
+ modules.
73
+
74
+ The ActiveSupport version keeps inheritable attribute values in a class-level
75
+ @inheritable_attributes variable, which it copies whenever a new class is
76
+ inherited. Clafamatt stays closer to Ruby conventions by storing each
77
+ attribute's value in a correspondingly-named class instance variable - so
78
+ e.g. <code>Foo.bar = 42</code> will set the <code>@bar</code> instance variable. This is
79
+ consistent with how Ruby's built-in attr_* macros behave. Beyond the variables
80
+ used to store values, Clafamatt needs *zero* class instance variables for
81
+ internal bookeeping. It also doesn't copy variables on inheritance; instead, it
82
+ simply re-uses values from farther up the inheritance chain until an attribute
83
+ is explicitly set.
84
+
85
+ Finally, Clafamatt is only there when you ask for it, by including
86
+ Clafamatt::Macros. It will not pollute the global namespace.
87
+
88
+ == REQUIREMENTS:
89
+
90
+ * Ruby!
91
+
92
+ == INSTALL:
93
+
94
+ sudo gem install clafamatt
95
+
96
+ == LICENSE:
97
+
98
+ (The MIT License)
99
+
100
+ Copyright (c) 2008 Avdi Grimm
101
+
102
+ Permission is hereby granted, free of charge, to any person obtaining
103
+ a copy of this software and associated documentation files (the
104
+ 'Software'), to deal in the Software without restriction, including
105
+ without limitation the rights to use, copy, modify, merge, publish,
106
+ distribute, sublicense, and/or sell copies of the Software, and to
107
+ permit persons to whom the Software is furnished to do so, subject to
108
+ the following conditions:
109
+
110
+ The above copyright notice and this permission notice shall be
111
+ included in all copies or substantial portions of the Software.
112
+
113
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
114
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
115
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
116
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
117
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
118
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
119
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,46 @@
1
+ # Look in the tasks/setup.rb file for the various options that can be
2
+ # configured in this Rakefile. The .rake files in the tasks directory
3
+ # are where the options are used.
4
+
5
+ begin
6
+ require 'bones'
7
+ Bones.setup
8
+ rescue LoadError
9
+ load 'tasks/setup.rb'
10
+ end
11
+
12
+ ensure_in_path 'lib'
13
+ require 'clafamatt'
14
+
15
+ task :default => 'spec:run'
16
+
17
+ PROJ.name = 'clafamatt'
18
+ PROJ.authors = 'Avdi Grimm'
19
+ PROJ.email = 'avdi@avdi.org'
20
+ PROJ.url = 'http://clafamatt.rubyforge.org'
21
+ PROJ.version = Clafamatt::VERSION
22
+ PROJ.rubyforge.name = 'clafamatt'
23
+ PROJ.readme_file = 'README.rdoc'
24
+
25
+ # Uncomment to disable warnings
26
+ # PROJ.ruby_opts = []
27
+
28
+ # RSpec
29
+ PROJ.spec.opts << '--color'
30
+
31
+ # RDoc
32
+ PROJ.rdoc.include << '\.rdoc$'
33
+
34
+ # Email Announcement
35
+ PROJ.ann.email[:from] = 'avdi@avdi.org'
36
+ PROJ.ann.email[:to] = 'ruby-talk@ruby-lang.org'
37
+ PROJ.ann.email[:server] = 'smtp.gmail.com'
38
+ PROJ.ann.email[:domain] = 'avdi.org'
39
+ PROJ.ann.email[:port] = 587
40
+ PROJ.ann.email[:acct] = 'avdi.grimm'
41
+ PROJ.ann.email[:authtype] = :plain
42
+
43
+ # Notes
44
+ PROJ.notes.extensions << '.rdoc'
45
+
46
+ # EOF
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(
4
+ File.join(File.dirname(__FILE__), %w[.. lib clafamatt]))
5
+
6
+ # Put your code here
7
+
8
+ # EOF
@@ -0,0 +1,38 @@
1
+ require File.expand_path('../lib/clafamatt', File.dirname(__FILE__))
2
+
3
+ module Shared
4
+ include Clafamatt::Macros
5
+
6
+ class_family_accessor :foo
7
+ end
8
+
9
+ class Parent
10
+ include Shared
11
+
12
+ end
13
+
14
+ class Child < Parent
15
+ class_family_accessor :bar
16
+ end
17
+
18
+ Shared.foo # =>
19
+ Parent.foo # =>
20
+ Child.foo # =>
21
+
22
+ Shared.foo = "klaatu"
23
+ Shared.foo # =>
24
+ Parent.foo # =>
25
+ Child.foo # =>
26
+
27
+ Parent.foo = "nikto"
28
+ Shared.foo # =>
29
+ Parent.foo # =>
30
+ Child.foo # =>
31
+
32
+ Child.foo = "barada"
33
+ Parent.foo # =>
34
+ Child.foo # =>
35
+
36
+ Child.bar = "klaatu"
37
+ Child.bar # =>
38
+ Parent.bar # =>
@@ -0,0 +1,168 @@
1
+ module Clafamatt
2
+
3
+ # :stopdoc:
4
+ VERSION = '1.0.0'
5
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
6
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
7
+
8
+ # Returns the version string for the library.
9
+ #
10
+ def self.version
11
+ VERSION
12
+ end
13
+
14
+ # Returns the library path for the module. If any arguments are given,
15
+ # they will be joined to the end of the libray path using
16
+ # <tt>File.join</tt>.
17
+ #
18
+ def self.libpath( *args )
19
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
20
+ end
21
+
22
+ # Returns the lpath for the module. If any arguments are given,
23
+ # they will be joined to the end of the path using
24
+ # <tt>File.join</tt>.
25
+ #
26
+ def self.path( *args )
27
+ args.empty? ? PATH : ::File.join(PATH, args.flatten)
28
+ end
29
+
30
+ # Utility method used to rquire all files ending in .rb that lie in the
31
+ # directory below this file that has the same name as the filename passed
32
+ # in. Optionally, a specific _directory_ name can be passed in such that
33
+ # the _filename_ does not have to be equivalent to the directory.
34
+ #
35
+ def self.require_all_libs_relative_to( fname, dir = nil )
36
+ dir ||= ::File.basename(fname, '.*')
37
+ search_me = ::File.expand_path(
38
+ ::File.join(::File.dirname(fname), dir, '*', '*.rb'))
39
+
40
+ Dir.glob(search_me).sort.each {|rb| require rb}
41
+ end
42
+ # :startdoc:
43
+
44
+ # This might be better named SingletonModule. A MacroModule acts as a
45
+ # container for singleton methods attached to a given class or module. This
46
+ # way we can share singleton methods across arbitrary classes and modules.
47
+ #
48
+ # MacroModule also ensures that the class being "decorated" with a MacroModule
49
+ # of its own will also inherit all of its ancestors' macros.
50
+ class MacroModule < Module
51
+ def self.find_or_create_for(klass)
52
+ mod = self.new(klass)
53
+ extend_klass = lambda do
54
+ mod.extend_class!
55
+ mod
56
+ end
57
+ klass_singleton = class << klass; self; end
58
+ klass_singleton.ancestors.find(extend_klass){|a|
59
+ a == mod
60
+ }
61
+ end
62
+ def self.find_all_for(klass)
63
+ klass_singleton = class << klass; self; end
64
+ klass_singleton.ancestors.grep(MacroModule)
65
+ end
66
+ def initialize(decorated_class)
67
+ @decorated_class = decorated_class
68
+ end
69
+
70
+ def ==(other)
71
+ self.class == other.class && self.decorated_class == other.decorated_class
72
+ end
73
+
74
+ def inspect
75
+ "#<Clafamatt::MacroModule:#{@decorated_class}:(#{self.instance_methods(false).join(", ")})>"
76
+ end
77
+
78
+ def copy_ancestor_macro_modules!
79
+ @decorated_class.ancestors.each do |ancestor|
80
+ macro_modules = self.class.find_all_for(ancestor)
81
+ macro_modules.each do |mm|
82
+ @decorated_class.extend(mm)
83
+ end
84
+ end
85
+ end
86
+
87
+ def extend_class!
88
+ copy_ancestor_macro_modules!
89
+ @decorated_class.extend(self)
90
+ end
91
+ attr_reader :decorated_class
92
+ end
93
+
94
+ module Macros
95
+ def self.append_features(other)
96
+ other.extend(ClassMethods)
97
+ super(other)
98
+ end
99
+
100
+ module ClassMethods
101
+ def class_family_reader(*symbols)
102
+ symbols.each do |name|
103
+ ivar = '@' + name.to_s
104
+ define_macro(name) do ||
105
+ # Explicitly checking for variable definition avoids warnings.
106
+ if instance_variable_defined?(ivar)
107
+ instance_variable_get(ivar)
108
+ # super() doesn't work the way we need in singleton class world
109
+ elsif can_pass?(name)
110
+ pass(name)
111
+ else
112
+ nil
113
+ end
114
+ end
115
+ end
116
+ end
117
+ def class_family_writer(*symbols)
118
+ symbols.each do |name|
119
+ setter = name.to_s + '='
120
+ ivar = '@' + name.to_s
121
+ define_macro(setter) do |new_value|
122
+ instance_variable_set(ivar, new_value)
123
+ end
124
+ end
125
+ end
126
+ def class_family_accessor(*symbols)
127
+ class_family_reader(*symbols)
128
+ class_family_writer(*symbols)
129
+ end
130
+
131
+ private
132
+
133
+ def append_features(other)
134
+ other.extend(ClassMethods)
135
+ cmm = clafamatt_macro_module
136
+ other.extend(cmm)
137
+ super(other)
138
+ end
139
+
140
+ def define_macro(name, &block)
141
+ cmm = clafamatt_macro_module
142
+ cmm.module_eval do
143
+ define_method(name, &block)
144
+ end
145
+ end
146
+
147
+ def clafamatt_macro_module
148
+ MacroModule.find_or_create_for(self)
149
+ end
150
+
151
+ def next_ancestor_responding_to(method)
152
+ ancestors.slice(1..-1).detect{|a| a.respond_to?(method)}
153
+ end
154
+
155
+ def can_pass?(method)
156
+ next_ancestor_responding_to(method)
157
+ end
158
+
159
+ def pass(method, *args, &block)
160
+ next_ancestor_responding_to(method).send(method, *args, &block)
161
+ end
162
+ end
163
+ end
164
+ end # module Clafamatt
165
+
166
+ Clafamatt.require_all_libs_relative_to(__FILE__)
167
+
168
+ # EOF