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 +20 -0
- data/README +97 -0
- data/Rakefile +79 -0
- data/init.rb +1 -0
- data/lib/module_creation_helper.rb +64 -0
- data/test/module_creation_helper_test.rb +132 -0
- data/test/test_helper.rb +6 -0
- metadata +52 -0
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
|
data/test/test_helper.rb
ADDED
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
|
+
|