classy 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -11,6 +11,10 @@ sub-subclasses, etc), and Aliasable lets you refer to classes via symbols
11
11
  class Parent
12
12
  extend Aliasable
13
13
  extend SubclassAware
14
+ extend Templatable
15
+
16
+ templatable_attr :awesomeness, :temperature
17
+ awesomeness :fairly
14
18
 
15
19
  aka :pop
16
20
  end
@@ -23,8 +27,9 @@ sub-subclasses, etc), and Aliasable lets you refer to classes via symbols
23
27
  aka :kid2
24
28
  end
25
29
 
26
- Parent.find(:kid1) # => ChildA
27
- Parent.subclasses # => [ ChildA, ChildB ]
30
+ Parent.find(:kid1) # => ChildA
31
+ Parent.subclasses # => [ ChildA, ChildB ]
32
+ Parent.new.awesomeness # => :fairly
28
33
 
29
34
  More extensive documentation and example code can be found in the RDoc for each
30
35
  module (available online at http://rdoc.info/projects/djspinmonkey/classy) or
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.1
1
+ 1.2.0
@@ -4,17 +4,10 @@
4
4
  # a given class hierarchy. Possible uses for this include friendlier DSLs or
5
5
  # additional layers of dynamic abstraction when specifying classes.
6
6
  #
7
- # Note: As mentioned, this module keeps its identity map in a class variable,
8
- # @@classy_aliases, on the extending class. This could concievably lead to
9
- # namespace conflicts and strange bugs in the unlikely event that this variable
10
- # is used for anything else. Later versions may implement a hash of identity
11
- # maps as a class variable on the Aliasable module itself, but for reasons of
12
- # complexity and performance, that has not been done at this time.
13
- #
14
7
  # ==Example
15
8
  #
16
9
  # class ParentClass
17
- # extend Aliasable
10
+ # include Aliasable
18
11
  # aka :pop
19
12
  # end
20
13
  #
@@ -25,54 +18,124 @@
25
18
  # Parent.find(:pop) # => ParentClass
26
19
  # Parent.find(:kid) # => AliasedSubclass
27
20
  #
28
- # More complex usage examples can be found in the aliasable_spec.rb file.
21
+ # It is also possible to include Aliasable from a model, which will then track
22
+ # aliases of classes which include that module.
23
+ #
24
+ # == Example
25
+ #
26
+ # module Meta
27
+ # include Aliasable
28
+ # end
29
+ #
30
+ # class AliasedClass
31
+ # include Meta
32
+ #
33
+ # aka :klass
34
+ # end
35
+ #
36
+ # Meta.find(:klass) # => AliasedClass
37
+ #
38
+ # More complex usage examples can be found in the spec file.
39
+ #
40
+ # NOTE: This defines a class variable, @@classy_aliases, on any class or module
41
+ # that includes Aliasable (or any class that includes a module including
42
+ # Aliasable).
43
+ #
44
+ # ANOTHER NOTE: As always, if you define your own .included methods, be sure to
45
+ # call super.
29
46
  #
30
47
  module Aliasable
31
-
32
- def self.extended (klass) #:nodoc:
33
- klass.class_exec do
34
- class_variable_set(:@@classy_aliases, {})
35
- end
36
- end
37
-
38
- # When passed a class, just returns it. When passed a symbol that is an
39
- # alias for a class, returns that class.
48
+ # Handle a module or class including Aliasable.
40
49
  #
41
- # ParentClass.find(AliasedSubclass) # => AliasedSubclass
42
- # ParentClass.find(:kid) # => AliasedSubclass
50
+ # :nodoc:
43
51
  #
44
- def find (klass)
45
- return klass if klass.kind_of? Class
46
- class_variable_get(:@@classy_aliases)[klass] or raise ArgumentError, "Could not find alias #{klass}"
52
+ def self.included( mod )
53
+ mod.extend ControllingClassMethods
54
+ mod.extend UniversalClassMethods
55
+ mod.extend AliasingClassMethods if mod.kind_of? Class # If mod is a Class, the aliased classes get the class methods via inheritance.
56
+ mod.send :class_variable_set, :@@classy_aliases, Hash.new
57
+ super
47
58
  end
48
59
 
49
- # Forget all known aliases. Mainly useful for testing purposes.
60
+ # Methods for the class or module that directly includes Aliasable.
50
61
  #
51
- def forget_aliases
52
- class_variable_get(:@@classy_aliases).clear
62
+ module ControllingClassMethods
63
+ # Handle a class including a module that has included Aliasable. Since the
64
+ # contolling module has extended this module, this method ends up being
65
+ # called when the controlling module is included.
66
+ #
67
+ # As a minor side effect, an instance method named #included ends up on any
68
+ # class that directly includes Aliasable. If you know an elegant way to
69
+ # avoid this, I welcome pull requests. :-)
70
+ #
71
+ # :nodoc:
72
+ #
73
+ def included( klass )
74
+ klass.extend AliasingClassMethods
75
+ klass.extend UniversalClassMethods
76
+
77
+ # Hoo boy. We need to set the @@classy_aliases class variable in the
78
+ # including class to point to the same actual hash object that the
79
+ # @@classy_aliases variable on the controlling module points to. When
80
+ # everything is class based, this is done automatically, since
81
+ # sub-classes share class variables.
82
+ #
83
+ klass.send(:class_variable_set, :@@classy_aliases, self.send(:class_variable_get, :@@classy_aliases))
84
+
85
+ super
86
+ end
87
+
88
+ # When passed a class, just returns it. When passed a symbol that is an
89
+ # alias for a class, returns that class.
90
+ #
91
+ # ParentClass.find(AliasedSubclass) # => AliasedSubclass
92
+ # ParentClass.find(:kid) # => AliasedSubclass
93
+ #
94
+ def find( nick )
95
+ return nick if nick.kind_of? Class
96
+ aliases[nick]
97
+ end
98
+
99
+ # Forget all known aliases. Mainly useful for testing purposes.
100
+ #
101
+ def forget_aliases
102
+ aliases.clear
103
+ end
104
+
53
105
  end
54
106
 
55
- # Specifies a symbol (or several) that a given framework might be known
56
- # by.
57
- #
58
- # class AnotherClass
59
- # aka :kid2, :chunky_bacon
60
- # ...
61
- # end
107
+ # Methods for the classes that get aliased.
62
108
  #
63
- def aka (*names)
64
- names.each do |name|
65
- raise ArgumentError, "Called aka with an alias that is already taken." if class_variable_get(:@@classy_aliases).include? name
66
- class_variable_get(:@@classy_aliases)[name] = self
109
+ module AliasingClassMethods
110
+ # Specifies a symbol (or several) that a given framework might be known
111
+ # by.
112
+ #
113
+ # class AnotherClass
114
+ # aka :kid2, :chunky_bacon
115
+ # ...
116
+ # end
117
+ #
118
+ def aka( *nicks )
119
+ nicks.each do |nick|
120
+ raise ArgumentError, "Called aka with an alias that is already taken." if aliases.include? nick
121
+ aliases[nick] = self
122
+ end
67
123
  end
68
124
  end
69
125
 
70
- # Return a hash of known aliases to Class objects
126
+ # Methods for both the controlling class/module and the aliased classes.
71
127
  #
72
- # ParentClass.aliases # => { :pop => ParentClass, :kid => AliasedSubclass, :kid2 => AnotherClass, :chunky_bacon => AnotherClass }
73
- #
74
- def aliases
75
- class_variable_get(:@@classy_aliases).dup
128
+ module UniversalClassMethods
129
+ # Return a hash of known aliases to Class objects.
130
+ #
131
+ # DANGER DANGER: This is the actual hash used internally by Aliasable, not a
132
+ # dup. If you mess with it, you might asplode things.
133
+ #
134
+ # ParentClass.aliases # => { :pop => ParentClass, :kid => AliasedSubclass, :kid2 => AnotherClass, :chunky_bacon => AnotherClass }
135
+ # ParentClass.aliases[:thing] = "BOOM" # This will end in tears.
136
+ #
137
+ def aliases
138
+ send :class_variable_get, :@@classy_aliases
139
+ end
76
140
  end
77
-
78
141
  end
@@ -28,23 +28,19 @@ require 'set'
28
28
  #
29
29
  # == Warning
30
30
  #
31
- # This module defines an inherited() class method on the extending class to
32
- # keep track of subclasses. Unfortunately, if this method is later re-defined,
33
- # this inherited() method is lost and subclass tracking will break. In order
34
- # to work around this, constructions like the following might be necessary:
35
- #
36
- # class ChildC < Parent
37
- #
38
- # class << self; alias :old_inherited :inherited end
39
- # def self.inherited(sub)
40
- # old_inherited(sub)
41
- # # ...your inherited() code...
42
- # end
31
+ # This module defines an inherited() class method. If the extending class
32
+ # defines its own inherited() method without calling super, this inherited()
33
+ # method is lost and subclass tracking will break. In order to work around
34
+ # this, make sure your inherited() method calls super. Like this:
43
35
  #
36
+ # class YourAwesomeClass
37
+ #
38
+ # def self.inherited
39
+ # # ...your awesome logic
40
+ # super # <-- This is important.
44
41
  # end
45
42
  #
46
- # This is not considered an acceptable long-term state of affairs - hopefully
47
- # in future versions of this module, this work around will not be necessary.
43
+ # end
48
44
  #
49
45
  module SubclassAware
50
46
 
@@ -57,12 +53,9 @@ module SubclassAware
57
53
  # Add the inheriting class to the list of subclasses. Not intended to be
58
54
  # called directly.
59
55
  #
60
- # TODO: Find a way for self.inherited on the extended class not to blow
61
- # this away without requiring a bunch of alias chain hoops to be jumped
62
- # through, as described above.
63
- #
64
56
  def inherited(sub)
65
57
  class_exec { class_variable_get(:@@classy_subclasses).add sub }
58
+ super
66
59
  end
67
60
 
68
61
  # Return an array of all known subclasses (and sub-subclasses, etc) of this
@@ -24,6 +24,8 @@
24
24
  # # Instances can override the defaults.
25
25
  # doodad.awesomeness = nil
26
26
  # doodad.temperature = :cool
27
+ # doodad.awesomeness # => nil
28
+ # doodad.temperature # => :cool
27
29
  #
28
30
  # == Note
29
31
  #
@@ -37,6 +39,9 @@ module Templatable
37
39
  # (Required to distinguish between variables explicitly set to nil and those
38
40
  # left as nil by default.)
39
41
  #
42
+ # @private
43
+ # :nodoc:
44
+ #
40
45
  @@ever_been_set = Hash.new { |hash, key| hash[key] = {} }
41
46
 
42
47
  # Defines one or more templatable attrs, which will add instance methods
@@ -2,11 +2,10 @@ require File.join(File.dirname(__FILE__), 'spec_helper')
2
2
 
3
3
  describe "Aliasable" do
4
4
 
5
- # TODO: It would be better to tear these down and rebuild them before each
6
- # test, since some of the tests add or remove aliases.
7
5
  before :all do
6
+ Base.forget_aliases if Object.const_defined? "Base"
8
7
  class Base
9
- extend Aliasable
8
+ include Aliasable
10
9
  aka :base
11
10
  end
12
11
 
@@ -18,6 +17,21 @@ describe "Aliasable" do
18
17
  aka :childB
19
18
  end
20
19
 
20
+ Meta.forget_aliases if Object.const_defined? "Meta"
21
+ module Meta
22
+ include Aliasable
23
+ end
24
+
25
+ class IncluderA
26
+ include Meta
27
+ aka :incA
28
+ end
29
+
30
+ class IncluderB
31
+ include Meta
32
+ aka :incB
33
+ end
34
+
21
35
  end
22
36
 
23
37
  describe ".aliases" do
@@ -29,6 +43,13 @@ describe "Aliasable" do
29
43
  :childB => ChildB
30
44
  })
31
45
  end
46
+
47
+ it "should work on modules as well as classes" do
48
+ Meta.aliases.should eql({
49
+ :incA => IncluderA,
50
+ :incB => IncluderB
51
+ })
52
+ end
32
53
  end
33
54
 
34
55
  describe ".aka" do
@@ -46,11 +67,15 @@ describe "Aliasable" do
46
67
  Base.find(:first_child).should equal ChildA
47
68
  Base.find(:childB).should equal ChildB
48
69
  end
70
+
71
+ it "should work on modules as well as bases" do
72
+ Meta.find(:incA).should equal IncluderA
73
+ end
49
74
  end
50
75
 
51
76
  it "should keep aliases of different class hierarchies separate" do
52
77
  class AnotherBase
53
- extend Aliasable
78
+ include Aliasable
54
79
  end
55
80
 
56
81
  lambda {
@@ -76,6 +101,11 @@ describe "Aliasable" do
76
101
  Base.forget_aliases
77
102
  Base.aliases.should be_empty
78
103
  end
104
+
105
+ it "should work for modules as well as classes" do
106
+ Meta.forget_aliases
107
+ Meta.aliases.should be_empty
108
+ end
79
109
  end
80
110
 
81
111
  end
@@ -8,13 +8,10 @@ describe "SubclassAware" do
8
8
  class ParentA
9
9
  extend SubclassAware
10
10
 
11
- # This is kind of a pain in the butt, but it's what you need to do if you
12
- # define your own self.inherited to keep from blowing away the one
13
- # SubclassAware sets up. There's a todo in subclass_aware.rb about this.
14
- class << self; alias :old_inherited :inherited end
11
+ # Make sure you call super in any self.inherited() methods!
15
12
  def self.inherited(sub)
16
- old_inherited(sub)
17
- # do your stuff here
13
+ # ...your stuff...
14
+ super # <-- This is important
18
15
  end
19
16
  end
20
17
 
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: classy
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ hash: 31
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 2
9
+ - 0
10
+ version: 1.2.0
5
11
  platform: ruby
6
12
  authors:
7
13
  - John Hyland
@@ -9,19 +15,25 @@ autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2010-01-12 00:00:00 -05:00
18
+ date: 2011-10-27 00:00:00 -04:00
13
19
  default_executable:
14
20
  dependencies:
15
21
  - !ruby/object:Gem::Dependency
16
22
  name: rspec
17
- type: :development
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
20
26
  requirements:
21
27
  - - ">="
22
28
  - !ruby/object:Gem::Version
29
+ hash: 13
30
+ segments:
31
+ - 1
32
+ - 2
33
+ - 9
23
34
  version: 1.2.9
24
- version:
35
+ type: :development
36
+ version_requirements: *id001
25
37
  description: Classy is a collection of metaprogramming-heavy modules which you can extend in order to give various capabilities to your Ruby classes. For example, SubclassAware lets a class know about all of its subclasses (and sub-subclasses, etc), and Aliasable lets you refer to classes via symbols (useful for creating friendly DSLs).
26
38
  email: github@djspinmonkey.com
27
39
  executables: []
@@ -57,21 +69,27 @@ rdoc_options:
57
69
  require_paths:
58
70
  - lib
59
71
  required_ruby_version: !ruby/object:Gem::Requirement
72
+ none: false
60
73
  requirements:
61
74
  - - ">="
62
75
  - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 0
63
79
  version: "0"
64
- version:
65
80
  required_rubygems_version: !ruby/object:Gem::Requirement
81
+ none: false
66
82
  requirements:
67
83
  - - ">="
68
84
  - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
69
88
  version: "0"
70
- version:
71
89
  requirements: []
72
90
 
73
91
  rubyforge_project:
74
- rubygems_version: 1.3.5
92
+ rubygems_version: 1.6.2
75
93
  signing_key:
76
94
  specification_version: 3
77
95
  summary: A collection of modules to enhance the capabilities of Ruby classes in various ways.