active_factory 0.1.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.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 [name of plugin creator]
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.
@@ -0,0 +1,17 @@
1
+ MIT-LICENSE
2
+ README.rdoc
3
+ Rakefile
4
+ init.rb
5
+ lib/active_factory.rb
6
+ lib/generators/active_factory/install/USAGE
7
+ lib/generators/active_factory/install/install_generator.rb
8
+ lib/generators/active_factory/install/templates/active_factory.rb
9
+ lib/generators/active_factory/install/templates/define_factories.rb
10
+ lib/hash_struct.rb
11
+ spec/active_factory_define_spec.rb
12
+ spec/active_factory_spec.rb
13
+ spec/active_record_environment_spec.rb
14
+ spec/define_lib_factories.rb
15
+ spec/spec_helper.rb
16
+ spec/support/active_record_environment.rb
17
+ Manifest
@@ -0,0 +1,113 @@
1
+ == ActiveFactory
2
+
3
+ <em>A fixture replacement library. With it your specs will become more declarative, uniform and terse.</em>
4
+
5
+ <b>Feedback will be highly appreciated</b>
6
+
7
+ * {Google group}[http://groups.google.com/group/active_factory]
8
+ * {Factory Definition Reference}[https://github.com/tarasevich/active_factory/wiki/Factory-Definition-Reference]
9
+ * {Models Clause Reference}[https://github.com/tarasevich/active_factory/wiki/Models-Clause-Reference]
10
+
11
+ == Introduction
12
+
13
+ ActiveFactory allows you declaratively define
14
+ which objects you want to have in a database for your spec.
15
+ You can also define associations between the objects and
16
+ redefine default values.
17
+ Additionally ActiveFactory automatically defines accessor methods for the objects.
18
+ The scope of the methods is limited to current spec. So they will not affect you other specs.
19
+
20
+ it "Task.incomplete returns only incomplete tasks" do
21
+ models { project - tasks({:complete => 0}, {:complete => 1}) }
22
+
23
+ project.tasks.incomplete.should == [tasks[0]]
24
+ end
25
+
26
+ it "project displays incomplete tasks" do
27
+ models { my - project - task(:complete => 0) }
28
+
29
+ visit project_path(project)
30
+
31
+ page.should have_content task.title
32
+ end
33
+
34
+ These specs require the following configuration:
35
+
36
+ class ActiveFactory::Define
37
+
38
+ factory :my, :class => User do
39
+ username "my_name"
40
+ password "my_password"
41
+
42
+ before_save do
43
+ model.save!
44
+ context.emulate_sign_in model
45
+ end
46
+ end
47
+
48
+ factory :project do
49
+ title { "Project #{index} title" }
50
+ due { Time.now }
51
+ end
52
+
53
+ factory :task do
54
+ title { "Task #{index} title" }
55
+ end
56
+ end
57
+
58
+ In the configuration you specify default attribute values for an object,
59
+ and in a specific test you may reassign the values for the needs of the test.
60
+ Optional block after_build specifies actions that should be done
61
+ after object was initialized but before saving it.
62
+ If you want to create by the same factory several objects with different values,
63
+ you may use blocks.
64
+ Method index in those blocks returns index of the object being created.
65
+
66
+ == Installation
67
+
68
+ ActiveFactory requires Rails 3 and RSpec 2.
69
+
70
+ rails plugin install git@github.com:tarasevich/active_factory.git
71
+ rails g active_factory:install
72
+
73
+ Now you are ready to add you factories spec/define_factories.rb
74
+ and use models {} block in your specs.
75
+
76
+ <em>Copyright (c) 2010-2011 Alexey Tarasevich, released under the MIT license</em>
77
+
78
+ == Some Examples
79
+
80
+ Consider we have factory declarations from introduction secion.
81
+
82
+ describe ProjectController do
83
+
84
+ it "creates a new project" do
85
+ models { my ; project_ }
86
+
87
+ post :create, :project => project_
88
+
89
+ Project.all.map(&:title).should == [project_[:title]]
90
+ end
91
+ ...
92
+
93
+ In previous example factory +my+ in +models+ block causes to emulate login.
94
+ +project_+ declares corresponding method inside the spec.
95
+ Syntax with uderscore indicates that it's should be just hash
96
+ with values taken from the factory declaration.
97
+ In our case it will be <code>{:title => "Project 0 title"}</code>
98
+
99
+ it "modifies a project's title" do
100
+ models { my - project }
101
+
102
+ put :update, :id => project.id, :project => project_(:title => "New Title")
103
+
104
+ project.reload.title.should == "New Title"
105
+ end
106
+
107
+ In this example we again use +my+ to create a user and log in with it.
108
+ Additionally we create +Project+ instance using +project+ factory
109
+ and define method +project+ locally for the spec.
110
+ Finally we use minus sign to create association between user +my+ and +project+.
111
+ Active factory iterates through associations of +User+ model and chooses the one that
112
+ has +Project+ on the other side.
113
+ <code>project_(h)</code> is just syntax sugar for <code>project_.merge(h)</code>
@@ -0,0 +1,41 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/rdoctask'
4
+ require 'rspec/core/rake_task'
5
+ require 'echoe'
6
+
7
+ desc 'Default: run specs.'
8
+ task :default => :spec
9
+
10
+ desc "Run specs"
11
+ RSpec::Core::RakeTask.new do |t|
12
+ t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
13
+ # Put spec opts in a file named .rspec in root
14
+ end
15
+
16
+ desc "Generate code coverage"
17
+ RSpec::Core::RakeTask.new(:coverage) do |t|
18
+ t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
19
+ t.rcov = true
20
+ t.rcov_opts = ['--exclude', 'spec']
21
+ end
22
+
23
+ Echoe.new('active_factory', '0.1.0') do |p|
24
+ p.description = "Fixtures replacement with sweet syntax"
25
+ p.url = "http://github.com/tarasevich/active_factory"
26
+ p.author = "Alexey Tarasevich"
27
+ p.ignore_pattern = ["tmp/*", "script/*"]
28
+ p.development_dependencies = []
29
+ end
30
+
31
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
32
+
33
+
34
+ desc 'Generate documentation for the active_factory plugin.'
35
+ Rake::RDocTask.new(:rdoc) do |rdoc|
36
+ rdoc.rdoc_dir = 'rdoc'
37
+ rdoc.title = 'ActiveFactory'
38
+ rdoc.options << '--line-numbers' << '--inline-source'
39
+ rdoc.rdoc_files.include('README.rdoc')
40
+ rdoc.rdoc_files.include('lib/**/*.rb')
41
+ end
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{active_factory}
5
+ s.version = "0.1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Alexey Tarasevich"]
9
+ s.date = %q{2011-04-21}
10
+ s.description = %q{Fixtures replacement with sweet syntax}
11
+ s.email = %q{}
12
+ s.extra_rdoc_files = ["README.rdoc", "lib/active_factory.rb", "lib/generators/active_factory/install/USAGE", "lib/generators/active_factory/install/install_generator.rb", "lib/generators/active_factory/install/templates/active_factory.rb", "lib/generators/active_factory/install/templates/define_factories.rb", "lib/hash_struct.rb"]
13
+ s.files = ["MIT-LICENSE", "README.rdoc", "Rakefile", "init.rb", "lib/active_factory.rb", "lib/generators/active_factory/install/USAGE", "lib/generators/active_factory/install/install_generator.rb", "lib/generators/active_factory/install/templates/active_factory.rb", "lib/generators/active_factory/install/templates/define_factories.rb", "lib/hash_struct.rb", "spec/active_factory_define_spec.rb", "spec/active_factory_spec.rb", "spec/active_record_environment_spec.rb", "spec/define_lib_factories.rb", "spec/spec_helper.rb", "spec/support/active_record_environment.rb", "Manifest", "active_factory.gemspec"]
14
+ s.homepage = %q{http://github.com/tarasevich/active_factory}
15
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Active_factory", "--main", "README.rdoc"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = %q{active_factory}
18
+ s.rubygems_version = %q{1.3.7}
19
+ s.summary = %q{Fixtures replacement with sweet syntax}
20
+
21
+ if s.respond_to? :specification_version then
22
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
23
+ s.specification_version = 3
24
+
25
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
26
+ else
27
+ end
28
+ else
29
+ end
30
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'active_factory' if Rails.env == 'test'
@@ -0,0 +1,480 @@
1
+ require 'hash_struct'
2
+
3
+ module ActiveFactory
4
+ # should be included in specs
5
+ module API
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ attr_accessor :_active_factory_context_extension
10
+
11
+ after do
12
+ _active_factory_context_extension.try :undo
13
+ self._active_factory_context_extension = nil
14
+ end
15
+ end
16
+
17
+ module ClassMethods
18
+ def factory_attributes model_name, index = 0
19
+ Define.
20
+ factories_hash[model_name].
21
+ attributes_for(index)
22
+ end
23
+ end
24
+
25
+ # methods available in specs
26
+ module InstanceMethods
27
+ def models &define_graph
28
+ not _active_factory_context_extension or raise "cannot use models twice in an example"
29
+
30
+ context = self
31
+ factories_hash = Define.factories_hash
32
+ containers_hash = Hash.new { |this, name|
33
+ factory = factories_hash[name]
34
+ this[name] = Container.new(name, factory, context)
35
+ }
36
+ linking_context = LinkingContext.new factories_hash.keys, containers_hash, context
37
+ self._active_factory_context_extension = ContextExtension.new
38
+
39
+ linking_context.instance_eval &define_graph
40
+ containers_hash.values.each &:before_save
41
+ containers_hash.values.each &:save
42
+ _active_factory_context_extension.extend_test_context containers_hash, context
43
+ nil
44
+ end
45
+ end
46
+ end
47
+
48
+ class FactoryDSL
49
+ def initialize
50
+ @attribute_expressions = {}
51
+ end
52
+
53
+ def prefer_associations *assoc_symbols
54
+ @prefer_associations = assoc_symbols
55
+ end
56
+
57
+ def after_build &callback
58
+ @after_build = callback
59
+ end
60
+
61
+ def before_save &callback
62
+ @before_save = callback
63
+ end
64
+
65
+ def method_missing method, *args, &expression
66
+ if args.many? or args.any? and block_given?
67
+ raise "should be either block or value: #{method} #{args.inspect[1..-2]}"
68
+ end
69
+ @attribute_expressions[method.to_sym] = expression || proc { args[0] }
70
+ end
71
+ end
72
+
73
+ # the class that should be "extended" to define models
74
+ class Define
75
+ @@factories = {}
76
+
77
+ def self.factory name, options = {}, &block
78
+ model_class = options[:class]
79
+ if parent_sym = options[:parent]
80
+ parent = @@factories[parent_sym] or raise "undefined parent factory #{parent_sym}"
81
+ end
82
+
83
+ @@factories[name] = FactoryDSL.new.instance_eval {
84
+ instance_eval(&block)
85
+ Factory.new name, parent, model_class,
86
+ @prefer_associations, @attribute_expressions, @after_build, @before_save
87
+ }
88
+ end
89
+
90
+ def self.factories_hash
91
+ @@factories
92
+ end
93
+ end
94
+
95
+ # defines methods that can be used in a model definition
96
+ # model - the model under construction
97
+ # index - index of the model in the factory
98
+ # context - spec context where the models {} block was evaluated
99
+ class CreationContext < Struct.new :index, :context, :model
100
+ alias i index
101
+ end
102
+
103
+ # creates instances of the given model class
104
+ class Factory < Struct.new :name, :parent, :model_class,
105
+ :prefer_associations, :attribute_expressions, :after_build, :before_save
106
+ def initialize name, parent, *overridable
107
+ @overridable = parent ? parent.merge_overridable(overridable) : overridable
108
+ super(name, parent, *@overridable)
109
+ self.attribute_expressions =
110
+ parent.attribute_expressions.merge(self.attribute_expressions) if parent
111
+
112
+ name.is_a? Symbol or raise "factory name #{name.inspect} must be symbol"
113
+ self.model_class ||= (@overridable[0] = Kernel.const_get(name.to_s.capitalize))
114
+ end
115
+
116
+ def merge_overridable overridable
117
+ overridable.zip(@overridable).
118
+ map { |his, my| his or my }
119
+ end
120
+
121
+ def attributes_for index
122
+ context = CreationContext.new(index)
123
+ attrs = attribute_expressions.map { |a, e| [a, context.instance_eval(&e)] }
124
+ Hash[attrs]
125
+ end
126
+
127
+ def apply_after_build index, context, model
128
+ if after_build
129
+ CreationContext.new(index, context, model).
130
+ instance_eval(&after_build)
131
+ end
132
+ end
133
+
134
+ def apply_before_save index, context, model
135
+ if before_save
136
+ CreationContext.new(index, context, model).
137
+ instance_eval(&before_save)
138
+ end
139
+ end
140
+ end
141
+
142
+ class ContainerEntry
143
+ attr_reader :model, :attrs
144
+
145
+ def initialize index, factory, context
146
+ @index = index
147
+ @factory = factory
148
+ @attrs = HashStruct[factory.attributes_for(index)]
149
+ @context = context
150
+ end
151
+
152
+ def merge hash
153
+ @attrs = @attrs.merge hash
154
+ end
155
+
156
+ def build
157
+ unless @model
158
+ @model = @factory.model_class.new
159
+ @attrs.each_pair { |k,v| @model.send "#{k}=", v }
160
+
161
+ @factory.apply_after_build @index, @context, @model
162
+ end
163
+ end
164
+
165
+ def before_save
166
+ if @model and not @saved
167
+ @factory.apply_before_save @index, @context, @model
168
+ end
169
+ end
170
+
171
+ def save
172
+ if @model and not @saved
173
+ @model.save!
174
+ @saved = true
175
+ end
176
+ end
177
+ end
178
+
179
+ # keeps collection of created instances of the given model class
180
+ class Container
181
+ attr_accessor :entries
182
+ attr_reader :name, :factory
183
+
184
+ def initialize name, factory, context
185
+ @name = name
186
+ @factory = factory
187
+ @context = context
188
+ @entries = [].freeze
189
+ end
190
+
191
+ def create count
192
+ dup_with add_entries count
193
+ end
194
+
195
+ def singleton
196
+ if @entries.none?
197
+ add_entries 1
198
+ elsif @entries.many?
199
+ raise "Multiple instances were declared for model :#{@name}."+
200
+ "Use <#{@name.to_s.pluralize}> to access them"
201
+ end
202
+ self
203
+ end
204
+
205
+ def zip_merge *hashes
206
+ @entries.size == hashes.size or raise
207
+
208
+ @entries.zip(hashes) { |entry, hash|
209
+ entry.merge hash
210
+ }
211
+ self
212
+ end
213
+
214
+ def build
215
+ @entries.each &:build
216
+ self
217
+ end
218
+
219
+ def make_linker
220
+ Linker.new self
221
+ end
222
+
223
+ def before_save
224
+ @entries.each &:before_save
225
+ end
226
+
227
+ def save
228
+ @entries.each &:save
229
+ end
230
+
231
+ def attrs
232
+ @entries.map &:attrs
233
+ end
234
+
235
+ def objects
236
+ @entries.map &:model
237
+ end
238
+
239
+ private
240
+
241
+ def dup_with entries
242
+ that = clone
243
+ that.entries = entries
244
+ that
245
+ end
246
+
247
+ def add_entries count
248
+ size = @entries.size
249
+ added = (size...size+count).
250
+ map { |i| ContainerEntry.new i, factory, @context }
251
+ @entries = (@entries + added).freeze
252
+ added
253
+ end
254
+ end
255
+
256
+ # provides syntax to create associations between models
257
+ class Linker
258
+ def initialize container, use_association = nil
259
+ @container = container
260
+ @use_association = use_association
261
+
262
+ @entries = container.entries
263
+ @model_class = container.factory.model_class
264
+ @prefer_associations = container.factory.prefer_associations
265
+ end
266
+
267
+ attr_accessor :entries, :model_class
268
+
269
+ def - that
270
+ case that
271
+ when Linker
272
+ associate that
273
+ that
274
+ when Symbol
275
+ Linker.new @container, that
276
+ else
277
+ raise "cannot associate with #{that.inspect}"
278
+ end
279
+ end
280
+
281
+ private
282
+
283
+ def associate linker
284
+ ar = get_association linker.model_class
285
+
286
+ case ar.macro
287
+ when :has_many, :has_and_belongs_to_many
288
+ assoc_entries = proc { |e, e2|
289
+ e.model.send(ar.name) << e2.model
290
+ }
291
+
292
+ if entries.one? or linker.entries.one?
293
+ entries.each { |e|
294
+ linker.entries.each { |e2|
295
+ assoc_entries[e, e2]
296
+ }
297
+ }
298
+
299
+ elsif entries.size == linker.entries.size
300
+ entries.zip(linker.entries) { |e, e2|
301
+ assoc_entries[e, e2]
302
+ }
303
+
304
+ else
305
+ raise "when linking models, they should be one of this: 1-n, n-1, n-n (e.i. equal number)"
306
+ end
307
+
308
+ when :belongs_to, :has_one
309
+ assoc_entries = proc { |e, e2|
310
+ e.model.send :"#{ar.name}=", e2.model
311
+ }
312
+
313
+ if linker.entries.one?
314
+ entries.each { |e|
315
+ assoc_entries[e, linker.entries.first]
316
+ }
317
+
318
+ elsif entries.size == linker.entries.size
319
+ entries.zip(linker.entries) { |e, e2|
320
+ assoc_entries[e, e2]
321
+ }
322
+
323
+ else
324
+ raise "exactly one instance of an object should be assigned to belongs_to association: #{@container.name} - #{linker.instance_variable_get(:@container).try :name}"
325
+ end
326
+ end
327
+ end
328
+
329
+ def get_association with_class
330
+ if @use_association
331
+ @model_class.reflect_on_association(@use_association) or
332
+ raise "No association #{@use_association.inspect} found for #{@model_class}"
333
+ else
334
+ find_association with_class
335
+ end
336
+ end
337
+
338
+ def find_association with_class
339
+ assocs = @model_class.reflect_on_all_associations.find_all { |assoc|
340
+ assoc.class_name == with_class.name
341
+ }
342
+
343
+ if assocs.none?
344
+ raise "Trying to link, but no association found from #{@model_class} to #{with_class}"
345
+
346
+ elsif assocs.one?
347
+ assocs.first
348
+
349
+ elsif assocs.many?
350
+ resolved = assocs.select { |assoc| @prefer_associations.member? assoc.name }
351
+ resolved.one? or
352
+ raise "Ambiguous associations: #{assocs.map(&:name).inspect} of #{@model_class} to #{with_class}. prefer_associations=#{@prefer_associations.inspect}"
353
+
354
+ resolved.first
355
+ end
356
+ end
357
+
358
+ end
359
+
360
+ # provides methods that refer models in models {} block
361
+ class LinkingContext
362
+ def initialize model_names, containers_hash, context
363
+ @context = context
364
+ h = containers_hash
365
+
366
+ obj_class_eval do
367
+ model_names.each { |name|
368
+
369
+ define_method name do |*args|
370
+ not args.many? or raise "0 or 1 arguments expected, got: #{args.inspect}"
371
+
372
+ if args.none?
373
+ h[name].singleton
374
+ else
375
+ h[name].singleton.zip_merge(args[0])
376
+
377
+ end.build.make_linker
378
+ end
379
+
380
+ define_method :"#{name.to_s.pluralize}" do |*args|
381
+
382
+ if args.none?
383
+ h[name]
384
+
385
+ elsif args[0].is_a? Fixnum and args.one?
386
+ h[name].create(args[0])
387
+
388
+ elsif args.all? { |arg| arg.is_a? Hash }
389
+ h[name].create(args.size).zip_merge(*args)
390
+
391
+ else
392
+ raise "expected no args, or single integer, or several hashes, got: #{args.inspect}"
393
+
394
+ end.build.make_linker
395
+ end
396
+
397
+ define_method :"#{name}_" do
398
+ h[name].singleton.make_linker
399
+ end
400
+
401
+ define_method :"#{name.to_s.pluralize}_" do |count|
402
+ h[name].create(count).make_linker
403
+ end
404
+ }
405
+ end
406
+ end
407
+
408
+ private
409
+
410
+ def obj_class_eval &block
411
+ class << self
412
+ self
413
+ end.class_eval &block
414
+ end
415
+
416
+ def method_missing *args, &block
417
+ if block
418
+ @context.send *args, &block
419
+ else
420
+ @context.send *args
421
+ end
422
+ end
423
+ end
424
+
425
+ # introduces models' names in a spec's context
426
+ class ContextExtension
427
+ def undo
428
+ @undo_define_methods[] if @undo_define_methods
429
+ @undo_define_methods = nil
430
+ end
431
+
432
+ def extend_test_context containers_hash, context
433
+ mrg = proc {|args, hash|
434
+ if args.none?
435
+ hash
436
+ elsif args.one? and args[0].is_a? Hash
437
+ hash.merge args[0]
438
+ else
439
+ raise "Only has is valid argument, but *args=#{args.inspect}"
440
+ end
441
+ }
442
+
443
+ method_defs =
444
+ containers_hash.map { |name, container| [
445
+ name, proc { container.singleton.objects[0] },
446
+ :"#{name}_", proc { |*args| mrg[args, container.singleton.attrs[0]] },
447
+ :"#{name.to_s.pluralize}", proc { container.objects },
448
+ :"#{name.to_s.pluralize}_", proc { container.attrs }
449
+ ] }.
450
+ flatten.each_slice(2)
451
+
452
+ @undo_define_methods =
453
+ define_methods_with_undo context, method_defs
454
+ end
455
+
456
+ private
457
+
458
+ def define_methods_with_undo model, method_defs
459
+ old_methods = model.methods.map &:to_sym
460
+
461
+ overridden_methods, new_methods =
462
+ method_defs.
463
+ map(&:first).
464
+ partition { |name| old_methods.include? name.to_sym }
465
+
466
+ overridden_methods.map! { |name| [name, model.method(name)] }
467
+
468
+ define_method, undef_method = class << model
469
+ [method(:define_method), method(:undef_method)]
470
+ end
471
+
472
+ method_defs.each{ |n,b| define_method[n,b] }
473
+
474
+ lambda {
475
+ overridden_methods.each{ |n,b| define_method[n,b] }
476
+ new_methods.each &undef_method
477
+ }
478
+ end
479
+ end
480
+ end