pluginaweek-module_creation_helper 0.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/CHANGELOG.rdoc ADDED
@@ -0,0 +1,26 @@
1
+ == master
2
+
3
+ == 0.2.0 / 2008-12-14
4
+
5
+ * Remove the PluginAWeek namespace
6
+
7
+ == 0.1.0 / 2008-07-06
8
+
9
+ * Add support for specifying the parent class/module in the name of the class/module being created
10
+ * Don't depend on active_support
11
+
12
+ == 0.0.4 / 2008-05-05
13
+
14
+ * Updated documentation
15
+
16
+ == 0.0.3 / 2007-09-18
17
+
18
+ * Convert dos newlines to unix newlines
19
+
20
+ == 0.0.2 / 2007-02-02
21
+
22
+ * Released as a gem
23
+
24
+ == 0.0.1 / 2007-01-04
25
+
26
+ * Initial release
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006-2009 Aaron Pfeifer
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.rdoc ADDED
@@ -0,0 +1,101 @@
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
+ API
9
+
10
+ * http://api.pluginaweek.org/module_creation_helper
11
+
12
+ Bugs
13
+
14
+ * http://pluginaweek.lighthouseapp.com/projects/13281-module_creation_helper
15
+
16
+ Development
17
+
18
+ * http://github.com/pluginaweek/module_creation_helper
19
+
20
+ Source
21
+
22
+ * git://github.com/pluginaweek/module_creation_helper.git
23
+
24
+ == Description
25
+
26
+ Creating modules and classes at runtime isn't the easiest and most intuitive
27
+ process. Although often used for anonymous classes, there are many times where
28
+ you will want to associate a runtime class with an actual name.
29
+
30
+ Traditionally, you would create new classes like so:
31
+
32
+ c = Class.new # => #<Class:0x480e388>
33
+ Object.const_set('Foo', c) # => Foo
34
+
35
+ Although this isn't very hard, there are two problems:
36
+ (1) It's a repetitive process that should be DRYed.
37
+ (2) Callbacks that are invoked while the class is being created do not know the
38
+ name of the class.
39
+
40
+ To understand the second problem, consider the following:
41
+
42
+ class Foo
43
+ def self.inherited(base)
44
+ puts "inherited class: #{base}, name: #{base.name}"
45
+ end
46
+ end
47
+
48
+ When a class inherits from Foo, Ruby will invoke the +inherited+ callback. For
49
+ example,
50
+
51
+ c = Class.new(Foo)
52
+ # inherited class: #<Class:0x47fb92c>, name:
53
+ # => #<Class:0x47fb92c>
54
+
55
+ As you can see from output in this example, since the class has not yet been
56
+ assigned to a constant, it is anonymous and does not yet have a name.
57
+
58
+ To address these issues, the functionality is encapsulated into a new method,
59
+ <tt>Module#create</tt>. Since the method is defined in Module, it is also available to
60
+ Class since Class inherits from Module.
61
+
62
+ == Usage
63
+
64
+ === Creating new classes/modules
65
+
66
+ Using the same example as before,
67
+
68
+ c = Class.create('Bar', :superclass => Foo)
69
+ # inherited class: Bar, name: Bar
70
+ # => Bar
71
+
72
+ As you can see, the name of the class is now available during the +inherited+
73
+ callback and is automatically assigned to the 'Bar' constant in Object.
74
+
75
+ === Specifying the parent class/module
76
+
77
+ In addition to specifying the superclass, you can also specify the parent
78
+ module/class like so:
79
+
80
+ c = Class.create('Bar', :superclass => Foo, :parent => MyModule)
81
+ # inherited class: MyModule::Bar, name: MyModule::Bar
82
+ # => MyModule::Bar
83
+
84
+ === Defining class/module methods
85
+
86
+ As you normally could when creating a new class, you can provide an additional
87
+ 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"
98
+
99
+ == Dependencies
100
+
101
+ None.
data/Rakefile ADDED
@@ -0,0 +1,96 @@
1
+ require 'rake/testtask'
2
+ require 'rake/rdoctask'
3
+ require 'rake/gempackagetask'
4
+ require 'rake/contrib/sshpublisher'
5
+
6
+ spec = Gem::Specification.new do |s|
7
+ s.name = 'module_creation_helper'
8
+ s.version = '0.2.0'
9
+ s.platform = Gem::Platform::RUBY
10
+ s.summary = 'Adds a helper method for creating new modules and classes at runtime'
11
+ s.description = s.summary
12
+
13
+ s.files = FileList['{lib,test}/**/*'] + %w(CHANGELOG.rdoc init.rb LICENSE Rakefile README.rdoc)
14
+ s.require_path = 'lib'
15
+ s.has_rdoc = true
16
+ s.test_files = Dir['test/**/*_test.rb']
17
+
18
+ s.author = 'Aaron Pfeifer'
19
+ s.email = 'aaron@pluginaweek.org'
20
+ s.homepage = 'http://www.pluginaweek.org'
21
+ s.rubyforge_project = 'pluginaweek'
22
+ end
23
+
24
+ desc 'Default: run all tests.'
25
+ task :default => :test
26
+
27
+ desc "Test the #{spec.name} plugin."
28
+ Rake::TestTask.new(:test) do |t|
29
+ t.libs << 'lib'
30
+ t.test_files = spec.test_files
31
+ t.verbose = true
32
+ end
33
+
34
+ begin
35
+ require 'rcov/rcovtask'
36
+ namespace :test do
37
+ desc "Test the #{spec.name} plugin with Rcov."
38
+ Rcov::RcovTask.new(:rcov) do |t|
39
+ t.libs << 'lib'
40
+ t.test_files = spec.test_files
41
+ t.rcov_opts << '--exclude="^(?!lib/)"'
42
+ t.verbose = true
43
+ end
44
+ end
45
+ rescue LoadError
46
+ end
47
+
48
+ desc "Generate documentation for the #{spec.name} plugin."
49
+ Rake::RDocTask.new(:rdoc) do |rdoc|
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = spec.name
52
+ rdoc.template = '../rdoc_template.rb'
53
+ rdoc.options << '--line-numbers' << '--inline-source'
54
+ rdoc.rdoc_files.include('README.rdoc', 'CHANGELOG.rdoc', 'LICENSE', 'lib/**/*.rb')
55
+ end
56
+
57
+ desc 'Generate a gemspec file.'
58
+ task :gemspec do
59
+ File.open("#{spec.name}.gemspec", 'w') do |f|
60
+ f.write spec.to_ruby
61
+ end
62
+ end
63
+
64
+ Rake::GemPackageTask.new(spec) do |p|
65
+ p.gem_spec = spec
66
+ p.need_tar = true
67
+ p.need_zip = true
68
+ end
69
+
70
+ desc 'Publish the beta gem.'
71
+ task :pgem => [:package] do
72
+ Rake::SshFilePublisher.new('aaron@pluginaweek.org', '/home/aaron/gems.pluginaweek.org/public/gems', 'pkg', "#{spec.name}-#{spec.version}.gem").upload
73
+ end
74
+
75
+ desc 'Publish the API documentation.'
76
+ task :pdoc => [:rdoc] do
77
+ Rake::SshDirPublisher.new('aaron@pluginaweek.org', "/home/aaron/api.pluginaweek.org/public/#{spec.name}", 'rdoc').upload
78
+ end
79
+
80
+ desc 'Publish the API docs and gem'
81
+ task :publish => [:pgem, :pdoc, :release]
82
+
83
+ desc 'Publish the release files to RubyForge.'
84
+ task :release => [:gem, :package] do
85
+ require 'rubyforge'
86
+
87
+ ruby_forge = RubyForge.new.configure
88
+ ruby_forge.login
89
+
90
+ %w(gem tgz zip).each do |ext|
91
+ file = "pkg/#{spec.name}-#{spec.version}.#{ext}"
92
+ puts "Releasing #{File.basename(file)}..."
93
+
94
+ ruby_forge.add_release(spec.rubyforge_project, spec.name, spec.version, file)
95
+ end
96
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'module_creation_helper'
@@ -0,0 +1,74 @@
1
+ module ModuleCreationHelper
2
+ module Extensions #:nodoc:
3
+ # Adds helper methods for easily generating new modules/classes
4
+ module Module
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
18
+ # when using Class#create. Default is Object.
19
+ # * <tt>:parent</tt> - The class/module that contains this module.
20
+ # Default is Object.
21
+ #
22
+ # == Examples
23
+ #
24
+ # Module.create('Foo') # => Foo
25
+ # Module.create('Bar', :parent => Foo) # => Foo::Bar
26
+ # Class.create('Waddle') # => Waddle
27
+ # Class.create('Widdle', :parent => Waddle) # => Waddle::Widdle
28
+ # Class.create('Woddle', :superclass => Waddle::Widdle, :parent => Waddle) # => Waddle::Woddle
29
+ # Waddle::Woddle.superclass # => Waddle::Widdle
30
+ #
31
+ # == Setting the parent
32
+ #
33
+ # Rather than setting the parent directly using the +parent+ configuration
34
+ # option, you can specify it in the actual name of the class like so:
35
+ #
36
+ # Module.create('Foo::Bar') # => Foo::Bar
37
+ #
38
+ # This has the same effect as the following:
39
+ #
40
+ # Module.create('Bar', :parent => Foo)
41
+ def create(name, options = {}, &block)
42
+ # Validate the provided options
43
+ invalid_options = options.keys - [:superclass, :parent]
44
+ raise ArgumentError, "Unknown key(s): #{invalid_options.join(", ")}" unless invalid_options.empty?
45
+
46
+ # Validate usage of :superclass option
47
+ raise ArgumentError, 'Modules cannot have superclasses' if options[:superclass] && self.to_s == 'Module'
48
+
49
+ options = {:superclass => Object, :parent => Object}.merge(options)
50
+ parent = options[:parent]
51
+ superclass = options[:superclass]
52
+
53
+ if superclass != Object
54
+ superclass = " < ::#{superclass}"
55
+ else
56
+ superclass = ''
57
+ end
58
+
59
+ mod = parent.class_eval <<-end_eval
60
+ #{self.to_s.downcase} #{name}#{superclass}
61
+ self
62
+ end
63
+ end_eval
64
+
65
+ mod.class_eval(&block) if block_given?
66
+ mod
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ Module.class_eval do
73
+ extend ModuleCreationHelper::Extensions::Module
74
+ end
@@ -0,0 +1 @@
1
+ require 'module_creation_helper/extensions/module'
@@ -0,0 +1,230 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ module Ford
4
+ end
5
+
6
+ class Vehicle
7
+ class << self
8
+ attr_accessor :test_name
9
+ attr_accessor :test_inspect
10
+ attr_accessor :test_to_s
11
+ end
12
+
13
+ def self.inherited(base)
14
+ self.test_name = base.name
15
+ self.test_inspect = base.inspect
16
+ self.test_to_s = base.to_s
17
+ end
18
+ end
19
+
20
+ class ModuleCreationHelperTest < Test::Unit::TestCase
21
+ def test_should_raise_exception_if_invalid_option_specified
22
+ assert_raise(ArgumentError) {Class.create(nil, :invalid => true)}
23
+ end
24
+ end
25
+
26
+ class ClassTest < Test::Unit::TestCase
27
+ def setup
28
+ @car = Class.create('Car')
29
+ end
30
+
31
+ def test_should_have_object_as_superclass
32
+ assert_equal Object, @car.superclass
33
+ end
34
+
35
+ def test_should_have_object_as_parent
36
+ assert Object.const_defined?('Car')
37
+ end
38
+
39
+ def teardown
40
+ Object.send(:remove_const, 'Car')
41
+ end
42
+ end
43
+
44
+ class ClassWithSuperclassTest < Test::Unit::TestCase
45
+ def setup
46
+ @car = Class.create('Car', :superclass => Vehicle)
47
+ end
48
+
49
+ def test_should_inherit_from_superclass
50
+ assert_equal Vehicle, @car.superclass
51
+ end
52
+
53
+ def test_should_have_object_as_parent
54
+ assert Object.const_defined?('Car')
55
+ end
56
+
57
+ def test_should_evaluate_name_as_name
58
+ assert_equal 'Car', Vehicle.test_name
59
+ end
60
+
61
+ def test_should_evaluate_inspect_as_name
62
+ assert_equal 'Car', Vehicle.test_inspect
63
+ end
64
+
65
+ def test_should_evaluate_to_s_as_name
66
+ assert_equal 'Car', Vehicle.test_to_s
67
+ end
68
+
69
+ def teardown
70
+ Object.send(:remove_const, 'Car')
71
+ end
72
+ end
73
+
74
+ class ClassWithParentTest < Test::Unit::TestCase
75
+ def setup
76
+ @car = Class.create('Car', :parent => Ford)
77
+ end
78
+
79
+ def test_should_have_object_as_superclass
80
+ assert_equal Object, @car.superclass
81
+ end
82
+
83
+ def test_should_be_nested_within_parent
84
+ assert Ford.const_defined?('Car')
85
+ end
86
+
87
+ def teardown
88
+ Ford.send(:remove_const, 'Car')
89
+ end
90
+ end
91
+
92
+ class ClassWithInferredParentTest < Test::Unit::TestCase
93
+ def setup
94
+ @car = Class.create('Ford::Car')
95
+ end
96
+
97
+ def test_should_have_object_as_superclass
98
+ assert_equal Object, @car.superclass
99
+ end
100
+
101
+ def test_should_be_nested_within_parent
102
+ assert Ford.const_defined?('Car')
103
+ end
104
+
105
+ def teardown
106
+ Ford.send(:remove_const, 'Car')
107
+ end
108
+ end
109
+
110
+ class ClassWithInferredParentAndConfiguredParenTest < Test::Unit::TestCase
111
+ def setup
112
+ Module.create('Car', :parent => Ford)
113
+ @part = Class.create('Car::Part', :parent => Ford)
114
+ end
115
+
116
+ def test_should_have_object_as_superclass
117
+ assert_equal Object, @part.superclass
118
+ end
119
+
120
+ def test_should_be_nested_within_parent
121
+ assert Ford::Car.const_defined?('Part')
122
+ end
123
+
124
+ def teardown
125
+ Ford::Car.send(:remove_const, 'Part')
126
+ Ford.send(:remove_const, 'Car')
127
+ end
128
+ end
129
+
130
+ class ClassWithSuperclassAndParentTest < Test::Unit::TestCase
131
+ def setup
132
+ Vehicle.test_name = nil
133
+ Vehicle.test_inspect = nil
134
+ Vehicle.test_to_s = nil
135
+
136
+ @car = Class.create('Car', :superclass => Vehicle, :parent => Ford)
137
+ end
138
+
139
+ def test_should_inherit_from_superclass
140
+ assert_equal Vehicle, @car.superclass
141
+ end
142
+
143
+ def test_should_be_nested_within_parent
144
+ assert Ford.const_defined?('Car')
145
+ end
146
+
147
+ def test_should_evaluate_name_as_name
148
+ assert_equal 'Ford::Car', Vehicle.test_name
149
+ end
150
+
151
+ def test_should_evaluate_inspect_as_name
152
+ assert_equal 'Ford::Car', Vehicle.test_inspect
153
+ end
154
+
155
+ def test_should_evaluate_to_s_as_name
156
+ assert_equal 'Ford::Car', Vehicle.test_to_s
157
+ end
158
+
159
+ def teardown
160
+ Ford.send(:remove_const, 'Car')
161
+ end
162
+ end
163
+
164
+ class ClassWithDynamicSuperclassTest < Test::Unit::TestCase
165
+ def setup
166
+ @car = Class.create('Car')
167
+ @convertible = Class.create('Convertible', :superclass => @car)
168
+ end
169
+
170
+ def test_should_inherit_from_superclass
171
+ assert_equal @car, @convertible.superclass
172
+ end
173
+
174
+ def teardown
175
+ Object.send(:remove_const, 'Convertible')
176
+ Object.send(:remove_const, 'Car')
177
+ end
178
+ end
179
+
180
+ class ClassWithCustomMethodsTest < Test::Unit::TestCase
181
+ def setup
182
+ @car = Class.create('Car', :superclass => Vehicle) do
183
+ def self.color
184
+ 'red'
185
+ end
186
+ end
187
+ end
188
+
189
+ def test_should_evaluate_methods
190
+ assert_equal 'red', Car.color
191
+ end
192
+
193
+ def teardown
194
+ Object.send(:remove_const, 'Car')
195
+ end
196
+ end
197
+
198
+ class ModuleTest < Test::Unit::TestCase
199
+ def setup
200
+ @autopilot = Module.create('Autopilot')
201
+ end
202
+
203
+ def test_should_have_object_as_parent
204
+ assert Object.const_defined?('Autopilot')
205
+ end
206
+
207
+ def teardown
208
+ Object.send(:remove_const, 'Autopilot')
209
+ end
210
+ end
211
+
212
+ class ModuleWithSuperclassTest < Test::Unit::TestCase
213
+ def test_should_raise_an_exception
214
+ assert_raise(ArgumentError) {Module.create(nil, :superclass => Object)}
215
+ end
216
+ end
217
+
218
+ class ModuleWithParentTest < Test::Unit::TestCase
219
+ def setup
220
+ @autopilot = Module.create('Autopilot', :parent => Ford)
221
+ end
222
+
223
+ def test_should_be_nested_within_parent
224
+ assert Ford.const_defined?('Autopilot')
225
+ end
226
+
227
+ def teardown
228
+ Ford.send(:remove_const, 'Autopilot')
229
+ end
230
+ end
@@ -0,0 +1,4 @@
1
+ require 'test/unit'
2
+
3
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
4
+ require File.dirname(__FILE__) + '/../init'
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pluginaweek-module_creation_helper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Aaron Pfeifer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-08 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Adds a helper method for creating new modules and classes at runtime
17
+ email: aaron@pluginaweek.org
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib/module_creation_helper
26
+ - lib/module_creation_helper/extensions
27
+ - lib/module_creation_helper/extensions/module.rb
28
+ - lib/module_creation_helper.rb
29
+ - test/module_creation_helper_test.rb
30
+ - test/test_helper.rb
31
+ - CHANGELOG.rdoc
32
+ - init.rb
33
+ - LICENSE
34
+ - Rakefile
35
+ - README.rdoc
36
+ has_rdoc: true
37
+ homepage: http://www.pluginaweek.org
38
+ post_install_message:
39
+ rdoc_options: []
40
+
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ requirements: []
56
+
57
+ rubyforge_project: pluginaweek
58
+ rubygems_version: 1.2.0
59
+ signing_key:
60
+ specification_version: 2
61
+ summary: Adds a helper method for creating new modules and classes at runtime
62
+ test_files:
63
+ - test/module_creation_helper_test.rb