pluginaweek-module_creation_helper 0.2.0

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