classy 1.1.1 → 1.2.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/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.