module_creation_helper 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
+