module_creation_helper 0.0.2

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/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2005-2006 Aaron Pfeifer & Neil Abraham
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 ADDED
@@ -0,0 +1,97 @@
1
+ = module_creation_helper
2
+
3
+ module_creation_helper adds a helper method for creating new modules and classes
4
+ at runtime.
5
+
6
+ == Resources
7
+
8
+ Wiki
9
+
10
+ * http://wiki.pluginaweek.org/Module_creation_helper
11
+
12
+ Announcement
13
+
14
+ * http://www.pluginaweek.org/2007/01/04/10-from-anonymous-to-named-and-back-again-a-tale-of-modules-and-classes/
15
+
16
+ Source
17
+
18
+ * http://svn.pluginaweek.org/trunk/plugins/ruby/module/module_creation_helper
19
+
20
+ Development
21
+
22
+ * http://dev.pluginaweek.org/browser/trunk/plugins/ruby/module/module_creation_helper
23
+
24
+ == Description
25
+
26
+ Creating modules and classes at runtime isn't the easiest and most intuitive
27
+ process that Ruby could have provided. Although often used for anonymous
28
+ classes, there are many times where you will want to associate a runtime class
29
+ with an actual name.
30
+
31
+ Normally, you would create new classes and associate them with a name like so:
32
+
33
+ >> c = Class.new
34
+ => #<Class:0x480e388>
35
+ >> Object.const_set('Foo', c)
36
+ => Foo
37
+
38
+ Although this isn't very hard, there are two problems:
39
+ (1) It's a repetitive process that should be DRYed in the same way that chaining
40
+ methods has been dried.
41
+ (2) Callbacks that are invoked while the class is being created do not have
42
+ access to the name of the class.
43
+
44
+ To understand the second problem, consider the following:
45
+
46
+ class Foo
47
+ def self.inherited(base)
48
+ puts "inherited class: #{base}, name: #{base.name}"
49
+ end
50
+ end
51
+
52
+ When a class inherits from Foo, Ruby will invoke the +inherited+ callback. For
53
+ example,
54
+
55
+ >> c = Class.new(Foo)
56
+ inherited class: #<Class:0x47fb92c>, name:
57
+ => #<Class:0x47fb92c>
58
+
59
+ As you can see from output in this example, since the class has not yet been
60
+ assigned to a constant, it is anonymous and does not yet have a name.
61
+
62
+ To address these two issues, the functionality is encapsulated into a new method,
63
+ Module#create. Since the method is defined in Module, it is also available to
64
+ Class since Class inherits from Module.
65
+
66
+ Rather than defining new modules/classes using Module#new/Class#new, this plugin
67
+ uses class_eval on the object's parent. This allows names to be immediately
68
+ associated with the class/module regardless of whether it is currently being
69
+ evaluated or has already been evaluated.
70
+
71
+ Using the same example as before,
72
+
73
+ >> c = Class.create('Bar', :superclass => Foo)
74
+ inherited class: Bar, name: Bar
75
+ => Bar
76
+
77
+ As you can see, the name of the class is now available during the +inherited+
78
+ callback and is automatically assigned to the 'Bar' constant in Object. In
79
+ addition to specifying the superclass, you can also specify the parent
80
+ module/class like so:
81
+
82
+ >> c = Class.create('Bar', :superclass => Foo, :parent => MyModule)
83
+ inherited class: MyModule::Bar, name: MyModule::Bar
84
+ => MyModule::Bar
85
+
86
+ Finally, as you normally could when creating a new class, you can provide an
87
+ additional block that defines the body of the class. For example,
88
+
89
+ >> c = Class.create('Bar', :superclass => Foo, :parent => MyModule) do
90
+ ?> def say_hello
91
+ ?> 'hello'
92
+ ?> end
93
+ ?> end
94
+ inherited class: MyModule::Bar, name: MyModule::Bar
95
+ => Bar
96
+ >> Bar.new.say_hello
97
+ => "hello"
data/Rakefile ADDED
@@ -0,0 +1,79 @@
1
+ require 'rake/testtask'
2
+ require 'rake/rdoctask'
3
+ require 'rake/gempackagetask'
4
+ require 'rake/contrib/sshpublisher'
5
+
6
+ PKG_NAME = 'module_creation_helper'
7
+ PKG_VERSION = '0.0.2'
8
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
9
+ RUBY_FORGE_PROJECT = 'pluginaweek'
10
+
11
+ desc 'Default: run unit tests.'
12
+ task :default => :test
13
+
14
+ desc 'Test the module_creation_helper plugin.'
15
+ Rake::TestTask.new(:test) do |t|
16
+ t.libs << 'lib'
17
+ t.pattern = 'test/**/*_test.rb'
18
+ t.verbose = true
19
+ end
20
+
21
+ desc 'Generate documentation for the module_creation_helper plugin.'
22
+ Rake::RDocTask.new(:rdoc) do |rdoc|
23
+ rdoc.rdoc_dir = 'rdoc'
24
+ rdoc.title = 'ModuleCreationHelper'
25
+ rdoc.options << '--line-numbers' << '--inline-source'
26
+ rdoc.rdoc_files.include('README')
27
+ rdoc.rdoc_files.include('lib/**/*.rb')
28
+ end
29
+
30
+ spec = Gem::Specification.new do |s|
31
+ s.name = PKG_NAME
32
+ s.version = PKG_VERSION
33
+ s.platform = Gem::Platform::RUBY
34
+ s.summary = 'Adds a helper method for creating new modules and classes at runtime.'
35
+
36
+ s.files = FileList['{lib,tasks,test}/**/*'].to_a + %w(init.rb MIT-LICENSE Rakefile README)
37
+ s.require_path = 'lib'
38
+ s.autorequire = 'module_creation_helper'
39
+ s.has_rdoc = true
40
+ s.test_files = Dir['test/**/*_test.rb']
41
+
42
+ s.author = 'Aaron Pfeifer and Neil Abraham'
43
+ s.email = 'info@pluginaweek.org'
44
+ s.homepage = 'http://www.pluginaweek.org'
45
+ end
46
+
47
+ Rake::GemPackageTask.new(spec) do |p|
48
+ p.gem_spec = spec
49
+ p.need_tar = true
50
+ p.need_zip = true
51
+ end
52
+
53
+ desc 'Publish the beta gem'
54
+ task :pgem => [:package] do
55
+ Rake::SshFilePublisher.new('pluginaweek@pluginaweek.org', '/home/pluginaweek/gems.pluginaweek.org/gems', 'pkg', "#{PKG_FILE_NAME}.gem").upload
56
+ end
57
+
58
+ desc 'Publish the API documentation'
59
+ task :pdoc => [:rdoc] do
60
+ Rake::SshDirPublisher.new('pluginaweek@pluginaweek.org', "/home/pluginaweek/api.pluginaweek.org/#{PKG_NAME}", 'rdoc').upload
61
+ end
62
+
63
+ desc 'Publish the API docs and gem'
64
+ task :publish => [:pdoc, :release]
65
+
66
+ desc 'Publish the release files to RubyForge.'
67
+ task :release => [:gem, :package] do
68
+ require 'rubyforge'
69
+
70
+ ruby_forge = RubyForge.new
71
+ ruby_forge.login
72
+
73
+ %w( gem tgz zip ).each do |ext|
74
+ file = "pkg/#{PKG_FILE_NAME}.#{ext}"
75
+ puts "Releasing #{File.basename(file)}..."
76
+
77
+ ruby_forge.add_release(RUBY_FORGE_PROJECT, PKG_NAME, PKG_VERSION, file)
78
+ end
79
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'module_creation_helper'
@@ -0,0 +1,64 @@
1
+ module PluginAWeek #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module Module #:nodoc:
4
+ module ModuleCreationHelper
5
+ # Creates a new module with the specified name. This is essentially the
6
+ # same as actually defining the module like so:
7
+ #
8
+ # module NewModule
9
+ # end
10
+ #
11
+ # or as a class:
12
+ #
13
+ # class NewClass < SuperKlass
14
+ # end
15
+ #
16
+ # Configuration options:
17
+ # <tt>superclass</tt> - The class to inherit from. This only applies when using Class#create. Default is Object.
18
+ # <tt>parent</tt> - The class/module that contains this module. Default is Object.
19
+ #
20
+ # Examples:
21
+ #
22
+ # Module.create('Foo') # => Foo
23
+ # Module.create('Bar', :parent => Foo) # => Foo::Bar
24
+ # Class.create('Waddle') # => Waddle
25
+ # Class.create('Widdle', :parent => Waddle) # => Waddle::Widdle
26
+ # Class.create('Woddle', :superclass => Waddle::Widdle, :parent => Waddle) # => Waddle::Woddle
27
+ # Waddle::Woddle.superclass # => Waddle::Widdle
28
+ def create(name, options = {}, &block)
29
+ options.assert_valid_keys(
30
+ :superclass,
31
+ :parent
32
+ )
33
+ raise ArgumentError, 'Modules cannot have superclasses' if options[:superclass] && self.to_s == 'Module'
34
+
35
+ options.reverse_merge!(
36
+ :superclass => ::Object,
37
+ :parent => ::Object
38
+ )
39
+ parent = options[:parent]
40
+ superclass = options[:superclass]
41
+
42
+ if superclass != ::Object
43
+ superclass = " < ::#{superclass}"
44
+ else
45
+ superclass = ''
46
+ end
47
+
48
+ parent.class_eval <<-end_eval
49
+ #{self.to_s.downcase} #{name}#{superclass}
50
+ end
51
+ end_eval
52
+
53
+ mod = parent.const_get(name)
54
+ mod.class_eval(&block) if block_given?
55
+ mod
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ class ::Module #:nodoc:
63
+ extend PluginAWeek::CoreExtensions::Module::ModuleCreationHelper
64
+ end
@@ -0,0 +1,132 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ module TestParent
4
+ end
5
+
6
+ class TestSuperclass
7
+ cattr_accessor :test_name
8
+ cattr_accessor :test_inspect
9
+ cattr_accessor :test_to_s
10
+
11
+ def self.inherited(base)
12
+ self.test_name = base.name
13
+ self.test_inspect = base.inspect
14
+ self.test_to_s = base.to_s
15
+ end
16
+ end
17
+
18
+ class ModuleCreationHelperTest < Test::Unit::TestCase
19
+ def setup
20
+ TestSuperclass.test_name = nil
21
+ TestSuperclass.test_inspect = nil
22
+ TestSuperclass.test_to_s = nil
23
+ end
24
+
25
+ def test_no_options_for_class
26
+ klass = Class.create('Foo')
27
+ assert_equal Object, klass.superclass
28
+ assert_equal Object, klass.parent
29
+ assert Object.const_defined?('Foo')
30
+ end
31
+
32
+ def test_no_options_for_module
33
+ mod = Module.create('FooMod')
34
+ assert_equal Object, mod.parent
35
+ assert Object.const_defined?('FooMod')
36
+ end
37
+
38
+ def test_invalid_option
39
+ assert_raise(ArgumentError) {Class.create(nil, :invalid => true)}
40
+ end
41
+
42
+ def test_superclass_for_module
43
+ assert_raise(ArgumentError) {Module.create(nil, :superclass => Object)}
44
+ end
45
+
46
+ def test_superclass
47
+ klass = Class.create('Bar', :superclass => TestSuperclass)
48
+ assert_equal TestSuperclass, klass.superclass
49
+ assert_equal Object, klass.parent
50
+ assert Object.const_defined?('Bar')
51
+ end
52
+
53
+ def test_parent_for_class
54
+ klass = Class.create('Baz', :parent => TestParent)
55
+ assert_equal Object, klass.superclass
56
+ assert_equal TestParent, klass.parent
57
+ assert TestParent.const_defined?('Baz')
58
+ end
59
+
60
+ def test_parent_for_module
61
+ mod = Module.create('BazMod', :parent => TestParent)
62
+ assert_equal TestParent, mod.parent
63
+ assert TestParent.const_defined?('BazMod')
64
+ end
65
+
66
+ def test_superclass_and_parent
67
+ klass = Class.create('Biz', :superclass => TestSuperclass, :parent => TestParent)
68
+ assert_equal TestSuperclass, klass.superclass
69
+ assert_equal TestParent, klass.parent
70
+ assert TestParent.const_defined?('Biz')
71
+ end
72
+
73
+ def test_name_before_evaluated
74
+ klass = Class.create('Waddle', :superclass => TestSuperclass)
75
+ assert_equal 'Waddle', TestSuperclass.test_name
76
+ end
77
+
78
+ def test_inspect_before_evaluated
79
+ klass = Class.create('Widdle', :superclass => TestSuperclass)
80
+ assert_equal 'Widdle', TestSuperclass.test_inspect
81
+ end
82
+
83
+ def test_to_s_before_evaluated
84
+ klass = Class.create('Wuddle', :superclass => TestSuperclass)
85
+ assert_equal 'Wuddle', TestSuperclass.test_to_s
86
+ end
87
+
88
+ def test_name_before_evaluated_with_parent
89
+ klass = Class.create('Waddle', :superclass => TestSuperclass, :parent => TestParent)
90
+ assert_equal 'TestParent::Waddle', TestSuperclass.test_name
91
+ end
92
+
93
+ def test_inspect_before_evaluated_with_parent
94
+ klass = Class.create('Widdle', :superclass => TestSuperclass, :parent => TestParent)
95
+ assert_equal 'TestParent::Widdle', TestSuperclass.test_inspect
96
+ end
97
+
98
+ def test_to_s_before_evaluated_with_parent
99
+ klass = klass = Class.create('Wuddle', :superclass => TestSuperclass, :parent => TestParent)
100
+ assert_equal 'TestParent::Wuddle', TestSuperclass.test_to_s
101
+ end
102
+
103
+ def test_subclass_of_dynamic_class
104
+ klass = Class.create('Foobar')
105
+ subclass = Class.create('Foobaz', :superclass => klass)
106
+
107
+ assert_equal klass, subclass.superclass
108
+ assert_equal 'Foobaz', subclass.name
109
+ assert_equal 'Foobaz', subclass.inspect
110
+ assert_equal 'Foobaz', subclass.to_s
111
+ end
112
+
113
+ def test_with_block
114
+ klass = Class.create('ClassWithBlock', :superclass => TestSuperclass) do
115
+ def self.say_hello
116
+ 'hello'
117
+ end
118
+ end
119
+ assert_equal 'hello', ClassWithBlock.say_hello
120
+ end
121
+
122
+ def test_nested_class_with_superclass_with_same_name
123
+ klass = Class.create('Employee')
124
+ nested_class = Class.create('Employee', :superclass => klass, :parent => TestParent)
125
+ assert_equal klass, nested_class.superclass
126
+ end
127
+
128
+ private
129
+ def klass_with_id(klass)
130
+ "#<Class:0x#{(klass.object_id * 2).to_s(16)}>"
131
+ end
132
+ end
@@ -0,0 +1,6 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+ require 'active_support'
4
+
5
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
6
+ require File.dirname(__FILE__) + '/../init'
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.0
3
+ specification_version: 1
4
+ name: module_creation_helper
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.0.2
7
+ date: 2007-02-02 00:00:00 -05:00
8
+ summary: Adds a helper method for creating new modules and classes at runtime.
9
+ require_paths:
10
+ - lib
11
+ email: info@pluginaweek.org
12
+ homepage: http://www.pluginaweek.org
13
+ rubyforge_project:
14
+ description:
15
+ autorequire: module_creation_helper
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Aaron Pfeifer and Neil Abraham
31
+ files:
32
+ - lib/module_creation_helper.rb
33
+ - test/test_helper.rb
34
+ - test/module_creation_helper_test.rb
35
+ - init.rb
36
+ - MIT-LICENSE
37
+ - Rakefile
38
+ - README
39
+ test_files:
40
+ - test/module_creation_helper_test.rb
41
+ rdoc_options: []
42
+
43
+ extra_rdoc_files: []
44
+
45
+ executables: []
46
+
47
+ extensions: []
48
+
49
+ requirements: []
50
+
51
+ dependencies: []
52
+