clafamatt 1.0.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.
@@ -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