ironfan 4.12.3 → 5.0.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,78 @@
1
+ require 'spec_helper'
2
+ require 'gorillib/diff'
3
+ require_relative '../spec_helper/dummy_diff_drawer.rb'
4
+
5
+ describe Gorillib::DiffFormatter do
6
+ it 'writes the indices of displayed elements for both sides' do
7
+ TestDrawer.diffing_objs([1], [1,2]) do |drawer|
8
+ drawer.should_receive(:display_indices).with(1,1)
9
+ end
10
+ end
11
+ it 'adds objects correctly on the right' do
12
+ TestDrawer.diffing_objs([1], [1,2]) do |drawer|
13
+ drawer.should_receive(:display_add).with(nil, 2)
14
+ end
15
+ end
16
+ it 'labels objects correctly on the right' do
17
+ TestDrawer.diffing_objs([1], [1,2]) do |drawer|
18
+ # meaning it displayed the name of the right object
19
+ drawer.should_receive(:b_right)
20
+ end
21
+ end
22
+ it 'adds objects correctly on the left' do
23
+ TestDrawer.diffing_objs([1,2], [1]) do |drawer|
24
+ drawer.should_receive(:display_add).with(2, nil)
25
+ end
26
+ TestDrawer.diffing_objs([1,2], [1]) do |drawer|
27
+ # meaning it displayed the name of the left object
28
+ drawer.should_receive(:b_left)
29
+ end
30
+ end
31
+ it 'lists keys only on the left correctly' do
32
+ TestDrawer.diffing_objs({a: 1, b: 2}, {a: 1}) do |drawer|
33
+ drawer.should_receive(:only_left_key).with(:b)
34
+ end
35
+ end
36
+ it 'lists keys only on the right correctly' do
37
+ TestDrawer.diffing_objs({a: 1}, {a: 1, b: 2}) do |drawer|
38
+ drawer.should_receive(:only_right_key).with(:b)
39
+ end
40
+ end
41
+ it 'explains when elements differ' do
42
+ TestDrawer.diffing_objs(1, 2) do |drawer|
43
+ drawer.should_receive(:display_noteql_atoms).with(1,2)
44
+ end
45
+ end
46
+ it 'explains when elements are of different classes' do
47
+ TestDrawer.diffing_objs([], {}) do |drawer|
48
+ drawer.should_receive(:display_hetero).with([],{})
49
+ end
50
+ end
51
+ it 'initializes itself every time it diffs a new pair of objects object' do
52
+ diff_formatter = TestDrawer.diff_formatter
53
+
54
+ diff_formatter.should_receive :setup
55
+ diff_formatter.display_diff(nil, nil)
56
+ diff_formatter.should_receive :setup
57
+ diff_formatter.display_diff(nil, nil)
58
+ end
59
+
60
+ it 'correctly diffs complex objects' do
61
+
62
+ # These aren't incredibly descriptive, but they are nice for
63
+ # bug-hunting.
64
+
65
+ TestDrawer.transform_ltor(1,2)
66
+ TestDrawer.transform_ltor([1], [1,2])
67
+ TestDrawer.transform_ltor({a: 1}, {a: 1, b: 2})
68
+ TestDrawer.transform_ltor([1,2], [1])
69
+ TestDrawer.transform_ltor({a: 1, b: 2}, {a: 1})
70
+ TestDrawer.transform_ltor({a: 1, b: 2}, {a: 1, c: 3})
71
+ TestDrawer.transform_ltor({a: [1]}, {a: [1,2]})
72
+ TestDrawer.transform_ltor({a: [1,2]}, {a: [1]})
73
+ TestDrawer.transform_ltor({a: [1]}, {a: [1,2]})
74
+ TestDrawer.transform_ltor({a: [1]}, {a: [1,2]})
75
+ TestDrawer.transform_ltor({a: [1], b: 3, c: {a: 1}, d: []}, {a: [1,2], c: [7,8], d: {a: 2}})
76
+ TestDrawer.transform_ltor([], [[1,2]])
77
+ end
78
+ end
@@ -0,0 +1,115 @@
1
+ require 'spec_helper'
2
+ require 'ironfan'
3
+
4
+ describe Ironfan::Dsl do
5
+ context 'when joining requirement pairs' do
6
+ subject{ Ironfan::Dsl.new }
7
+
8
+ def test_join(cn1, cn2)
9
+ name = 'foo'
10
+ subject.join_req(Ironfan::Plugin::CookbookRequirement.new(name: name, constraint: cn1),
11
+ Ironfan::Plugin::CookbookRequirement.new(name: name, constraint: cn2)).constraint
12
+ end
13
+
14
+ it 'joins =,= requirements when valid' do
15
+ test_join('= 1.0.0', '= 1.0.0').should == '= 1.0.0'
16
+ end
17
+ it 'raises when =,= requirements not valid' do
18
+ expect{ test_join('= 1.0.0', '= 2.0.0') }.to raise_error
19
+ end
20
+ it 'chooses the more restrictive constraint for >= , >= requirements' do
21
+ test_join('>= 1.0.0', '>= 2.0.0').should == '>= 2.0.0'
22
+ test_join('>= 2.0.0', '>= 1.0.0').should == '>= 2.0.0'
23
+ end
24
+ it 'chooses the more restrictive constraint for ~> , ~> requirements' do
25
+ test_join('~> 1.0', '~> 1.2').should == '~> 1.2'
26
+ test_join('~> 1.2', '~> 1.0').should == '~> 1.2'
27
+ end
28
+ it 'raises when ~> , ~> requirements not valid' do
29
+ expect{ test_join('~> 1.0', '~> 1.0.0') }.to raise_error
30
+ expect{ test_join('~> 1.0', '~> 2.0') }.to raise_error
31
+ expect{ test_join('~> 2.0', '~> 1.0') }.to raise_error
32
+ end
33
+ it 'joins =, >= requirements when valid' do
34
+ test_join('= 1.1.0', '>= 1.1.0').should == '= 1.1.0'
35
+ test_join('>= 1.1.0', '= 1.1.0').should == '= 1.1.0'
36
+ end
37
+ it 'raises when =, >= requirements not valid' do
38
+ expect{ test_join('= 1.0.0', '>= 1.1.0') }.to raise_error
39
+ expect{ test_join('>= 1.1.0', '= 1.0.0') }.to raise_error
40
+ end
41
+ it 'joins =, ~> requirements when valid' do
42
+ test_join('= 1.1.0', '~> 1.0').should == '= 1.1.0'
43
+ test_join('~> 1.0', '= 1.1.0').should == '= 1.1.0'
44
+ end
45
+ it 'raises when =, ~> requirements not valid' do
46
+ expect{ test_join('= 2.0.0', '~> 1.0') }.to raise_error
47
+ expect{ test_join('~> 1.0', '= 2.0.0') }.to raise_error
48
+ end
49
+ it 'joins >=, ~> requirements when valid' do
50
+ test_join('>= 1.1.0', '~> 1.1.2').should == '~> 1.1.2'
51
+ test_join('~> 1.1.2', '>= 1.1.0').should == '~> 1.1.2'
52
+ end
53
+ it 'raises when >=, ~> requirements not valid' do
54
+ expect{ test_join('>= 1.1.3', '~> 1.1.2') }.to raise_error
55
+ expect{ test_join('~> 1.1.2', '>= 1.1.3') }.to raise_error
56
+ end
57
+ end
58
+
59
+ context 'when aggregating requirements' do
60
+ before(:each) do
61
+ Ironfan::Dsl.class_eval{ @@testing = true }
62
+
63
+ Ironfan::Dsl::Component.template(%w[foo]) do
64
+ cookbook_req 'a', '>= 1.2.3'
65
+ def project(*_) end
66
+ end
67
+ Ironfan::Dsl::Component.template(%w[bar]) do
68
+ cookbook_req 'a', '>= 1.2.5'
69
+ def project(*_) end
70
+ end
71
+ Ironfan::Dsl::Component.template(%w[baz]) do
72
+ cookbook_req 'a', '>= 1.2.7'
73
+ def project(*_) end
74
+ end
75
+ Ironfan.realm(:test) do
76
+ cluster(:just_foo) do
77
+ foo
78
+ end
79
+ cluster(:foo_bar) do
80
+ foo; bar
81
+ end
82
+ cluster(:nest_foo_bar) do
83
+ foo
84
+ facet(:bar) do
85
+ bar
86
+ end
87
+ end
88
+ cluster(:baz) do
89
+ baz
90
+ end
91
+ end
92
+ end
93
+
94
+ after(:each) do
95
+ [:Foo, :Bar, :Baz].each{|x| Ironfan::Dsl::Component.send(:remove_const, x)}
96
+ end
97
+
98
+ it 'correctly merges version requirements in simple cases' do
99
+ Ironfan.realm(:test).tap do |realm|
100
+ realm.cluster(:just_foo).cookbook_reqs['a'].should == '>= 1.2.3'
101
+ realm.cluster( :foo_bar).cookbook_reqs['a'].should == '>= 1.2.5'
102
+ end
103
+ end
104
+
105
+ it 'correctly handles nested versions requirements' do
106
+ Ironfan.realm(:test).tap do |realm|
107
+ realm.cluster(:nest_foo_bar).tap do |cluster|
108
+ cluster .cookbook_reqs['a'].should == '>= 1.2.5'
109
+ cluster.facet(:bar).cookbook_reqs['a'].should == '>= 1.2.5'
110
+ end
111
+ realm.cookbook_reqs['a'].should == '>= 1.2.7'
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require 'ironfan'
3
+
4
+ describe Ironfan::Dsl::MachineManifest do
5
+ context 'it disregards whether values are strings or symbols' do
6
+ before(:each) do
7
+ Ironfan::Dsl::Component.template(%w[baz bif]) do
8
+ collection :bams, Symbol
9
+ magic :pow, Whatever
10
+ end
11
+ end
12
+
13
+ def comparable_manifest(hsh)
14
+ Ironfan::Dsl::MachineManifest.receive(components: [Ironfan::Dsl::Component::BazBif.new(hsh)]).to_comparable
15
+ end
16
+
17
+ it 'when those values are contained in an array of within its serialization' do
18
+ comparable_manifest(bam: [:a, :b, :c]).should == comparable_manifest(bam: %w[a b c])
19
+ comparable_manifest(pow: [:a, :b, :c]).should == comparable_manifest(pow: %w[a b c])
20
+ end
21
+ it 'when those values are contained in hash within its serialization' do
22
+ comparable_manifest(pow: {a: 'a'}).should == comparable_manifest(pow: {'a' => :a})
23
+ end
24
+ it 'correctly stores the chef environment' do
25
+ node = Chef::Node.json_create('chef_environment' => 'buzi', 'recipes' => [])
26
+ Ironfan::Dsl::MachineManifest.from_remote(nil, nil, nil, node, nil, nil, nil, nil, nil).environment.to_s.should == 'buzi'
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,138 @@
1
+ require_relative '../spec_helper.rb'
2
+
3
+ describe MyPlugin do
4
+ subject { MyPlugin }
5
+
6
+ it 'should respond to template' do
7
+ subject.should(respond_to(:template))
8
+ end
9
+
10
+ context 'when templatizing plugins' do
11
+ after(:each) do
12
+ uncreate_plugin(subject, Foo)
13
+ end
14
+ before(:each) do
15
+ subject.template(%w[baz bif])
16
+ end
17
+
18
+ it 'should create them as classes within itself' do
19
+ subject.constants.should(include(:BazBif))
20
+ end
21
+ it 'should create them as subclasses of itself' do
22
+ subject.const_get(:BazBif).should(be < subject)
23
+ end
24
+ it 'should allow customizing the classes' do
25
+ uncreate_plugin(subject, Foo)
26
+ subject.template(%w[baz bif]){ def bam() end }
27
+ subject.const_get(:BazBif).new.should(respond_to(:bam))
28
+ end
29
+ it 'should register methods on the specified class' do
30
+ Foo.new.should(respond_to(:baz_bif))
31
+ end
32
+ it 'should wire those methods to the plugin_hook method of itself' do
33
+ foo = Foo.new
34
+ subject.should_receive(:plugin_hook).with(foo, {}, :baz, :baz_bif)
35
+ foo.baz_bif
36
+ end
37
+ it 'should allow the user to create a custom class that will also be registered' do
38
+ Foo.registry[:baz_bif].should(be(subject.const_get(:BazBif)))
39
+ end
40
+ end
41
+ end
42
+
43
+ describe Ironfan::Dsl::Component do
44
+ before(:each) do
45
+ Ironfan::Dsl::Component.template(%w[baz bif]) do
46
+ include Ironfan::Dsl::Component::Announcement
47
+
48
+ magic :bam, Symbol, node_attr: 'a.b.c', default: nil
49
+
50
+ def project(compute)
51
+ compute.role(:called_from_project, compute) unless bam
52
+ end
53
+ end
54
+ end
55
+
56
+ after(:each) do
57
+ uncreate_plugin(Ironfan::Dsl::Component, Ironfan::Dsl::Compute)
58
+ end
59
+
60
+ it 'should have its project method called by the plugin_hook' do
61
+ Ironfan.cluster(:foo) do
62
+ should_receive(:role).with(:called_from_project, self)
63
+ baz_bif
64
+ end
65
+ end
66
+
67
+ it 'should merge its node attribute to create a node' do
68
+ node = Chef::Node.new
69
+ node.set['a'] = {'b' => {'c' => :baz}}
70
+ Ironfan.cluster(:foo) do
71
+ baz_bif{ bam(:baz) }.to_node.to_hash.should == node.to_hash
72
+ end
73
+ end
74
+ it 'should be instantiable from a node object' do
75
+ node = Chef::Node.new
76
+ node.set['a'] = {'b' => {'c' => :baz}}
77
+ Ironfan::Dsl::Compute.registry[:baz_bif].from_node(node).bam.should == :baz
78
+ end
79
+
80
+ it 'should remember all of its node attributes' do
81
+ Ironfan.cluster(:foo) do
82
+ component = baz_bif{ bam(:baz) }
83
+ component.to_node.to_hash.should ==
84
+ Ironfan::Dsl::Compute.registry[:baz_bif].from_node(component.to_node).to_node.to_hash
85
+ end
86
+ end
87
+
88
+ context 'when announcing' do
89
+ before (:each) do
90
+ def make_plugin(name, server_b)
91
+ Ironfan::Dsl::Component.template([name, server_b ? 'server' : 'client']) do
92
+ include Ironfan::Dsl::Component::Announcement if server_b
93
+ include Ironfan::Dsl::Component::Discovery if not server_b
94
+
95
+ if server_b
96
+ def project(compute)
97
+ end
98
+ else
99
+ def project(compute)
100
+ set_discovery compute, [announce_name]
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ def make_plugin_pair(name) make_plugin(name, true); make_plugin(name, false); end
107
+
108
+ make_plugin_pair(:bam); make_plugin_pair(:pow)
109
+ end
110
+
111
+ it 'automatically discovers announcements within realms' do
112
+ Ironfan.realm(:wap) do
113
+ cluster(:foo){ bam_client; pow_server }
114
+ cluster(:bar){ bam_server; pow_client }
115
+
116
+ cluster(:foo).cluster_role.override_attributes[:discovers].should == {bam: :wap_bar}
117
+ cluster(:bar).cluster_role.override_attributes[:discovers].should == {pow: :wap_foo}
118
+ end
119
+ end
120
+
121
+ it 'automatically configures security groups during discovery' do
122
+ Ironfan.realm(:wap) do
123
+ cloud(:ec2)
124
+
125
+ cluster(:foo){ bam_client; pow_server }
126
+ cluster(:bar){ bam_server; pow_client }
127
+ end.resolve!
128
+
129
+ rspec = self
130
+
131
+ foo_group = Ironfan.realm(:wap).cluster(:foo).cloud(:ec2).security_group('wap_foo')
132
+ bar_group = Ironfan.realm(:wap).cluster(:bar).cloud(:ec2).security_group('wap_bar')
133
+
134
+ foo_group.group_authorized.should include('wap_bar')
135
+ bar_group.group_authorized.should include('wap_foo')
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,137 @@
1
+ require 'spec_helper'
2
+
3
+ require 'ironfan'
4
+
5
+ describe Ironfan::Dsl::Realm do
6
+ before(:each) do
7
+ (Chef::Config[:ec2_image_info] ||= {}).merge!({
8
+ %w[us-east-1 64-bit ebs ironfan-precise ] =>
9
+ { :image_id => 'ami-29fe7640', :ssh_user => 'bam', :bootstrap_distro => "ubuntu12.04-ironfan", },
10
+ })
11
+
12
+ Ironfan.realm(:foo) do
13
+ environment :bif
14
+
15
+ cloud(:ec2) do
16
+ flavor 'm1.xlarge'
17
+ image_name 'ironfan-precise'
18
+ end
19
+
20
+ cluster(:bar) do
21
+ cluster_role.override_attributes(a: 1)
22
+ facet(:baz) do
23
+ instances 1
24
+ role :blah
25
+ facet_role.override_attributes(b: 1)
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ def manifest
32
+ Ironfan.cluster(:foo_bar).facets[:baz].server(0).to_machine_manifest
33
+ end
34
+
35
+ it 'should choose its own name as its default environment' do
36
+ Ironfan.realm(:bar).environment.to_s.should == 'bar'
37
+ end
38
+
39
+ it 'should choose the widest possible cookbook contraints to satisfy all plugins' do
40
+ Ironfan::Dsl::Component.template(%w[bam pow]) do
41
+ cookbook_req 'bif', '>= 1.0.0'
42
+ def project(_) end
43
+ end
44
+
45
+ Ironfan::Dsl::Component.template(%w[jam wam]) do
46
+ cookbook_req 'bif', '>= 2.0.0'
47
+ def project(_) end
48
+ end
49
+
50
+ Ironfan.realm(:qux) do
51
+ cluster(:cuz) do
52
+ facet(:lix) do
53
+ bam_pow
54
+ jam_wam
55
+ end
56
+ end
57
+ end.cookbook_reqs['bif'].should == '>= 2.0.0'
58
+ end
59
+
60
+ it 'should complain when no cookbook constraints can satisfy all plugins' do
61
+ Ironfan::Dsl::Component.template(%w[bam pow]) do
62
+ cookbook_req 'bif', '~> 1.0.0'
63
+ def project(_) end
64
+ end
65
+
66
+ Ironfan::Dsl::Component.template(%w[jam wam]) do
67
+ cookbook_req 'bif', '>= 2.0.0'
68
+ def project(_) end
69
+ end
70
+
71
+ Ironfan.realm(:qux) do
72
+ cluster(:cuz) do
73
+ facet(:lix) do
74
+ bam_pow
75
+ jam_wam
76
+ end
77
+ end
78
+ end
79
+
80
+ expect{ Ironfan.realm(:qux).cookbook_reqs }.to raise_error
81
+ end
82
+
83
+ it 'should create clusters that can be referenced later' do
84
+ x = self
85
+ Ironfan.realm :xx do
86
+ cluster(:bar).should(x.be(cluster(:bar)))
87
+ end
88
+ end
89
+
90
+ it 'should create clusters that can be edited later' do
91
+ Ironfan.realm :xy do
92
+ cluster(:baz)
93
+ cluster(:baz){ facet :bif }
94
+ end
95
+
96
+ Ironfan.cluster(:xy_baz).facets.to_a.should_not(be_empty)
97
+ Ironfan.cluster(:xy_baz).servers.to_a.first.should_not(be_nil)
98
+ end
99
+
100
+ it 'should create clusters with names prefixed by its own' do
101
+ Ironfan.cluster(:foo_bar).should_not(be_nil)
102
+ end
103
+
104
+ it 'should create clusters with machines' do
105
+ Ironfan.cluster(:foo_bar).facets[:baz].server(0).should_not(be_nil)
106
+ end
107
+
108
+ it 'should create clusters with attributes correctly applied' do
109
+ manifest.cluster_override_attributes.should == {a: 1}
110
+ manifest.facet_override_attributes.should == {b: 1}
111
+ manifest.run_list.should == %w[role[blah] role[foo_bar-cluster] role[foo_bar-baz-facet]]
112
+ end
113
+
114
+ it 'should create clusters with the correct ssh user' do
115
+ manifest.flavor.should == 'm1.xlarge'
116
+ manifest.ssh_user.should == 'bam'
117
+ end
118
+
119
+ it 'should save cloud properties to be shared among all clusters within the realm' do
120
+ # We need to resolve before the cloud settings come through
121
+ Ironfan.realm(:foo).clusters[:foo_bar].resolve.facets[:baz].servers.to_a.first.to_machine_manifest.flavor.should == 'm1.xlarge'
122
+
123
+ # Ironfan.cluster will do the resolution for us.
124
+ manifest.flavor.should == 'm1.xlarge'
125
+ end
126
+
127
+ it 'should save an environment to be shared among all clusters within the realm' do
128
+ # We need to resolve before the cloud settings come through
129
+ Ironfan.realm(:foo).clusters[:foo_bar].resolve.facets[:baz].environment.should == :bif
130
+
131
+ # The server manifest should contain the environment.
132
+ manifest.environment.should == :bif
133
+
134
+ # Ironfan.cluster will do the resolution for us.
135
+ Ironfan.cluster(:foo_bar).facets[:baz].environment.should == :bif
136
+ end
137
+ end
@@ -0,0 +1,94 @@
1
+ require 'gorillib/diff'
2
+ require 'stringio'
3
+
4
+ module TransformLeftToRight
5
+ attr_reader :left
6
+
7
+ DELETE_ME = Object.new
8
+
9
+ def display_key_header(key)
10
+ @left_lineage << [@left, key]
11
+ @left = @left[key]
12
+
13
+ @right_lineage << @right
14
+ @right = @right[key]
15
+ end
16
+
17
+ def only_left_key(_)
18
+ @left = DELETE_ME
19
+ end
20
+
21
+ def only_right_key(key)
22
+ @left = @right
23
+ end
24
+
25
+ def display_add(this, other)
26
+ if this.nil?
27
+ @left.insert(@ix, other)
28
+ @ix += 1
29
+ else
30
+ @left.delete_at(@ix)
31
+ end
32
+ end
33
+
34
+ def display_hetero(this, other)
35
+ @ix += 1
36
+ @left = @right
37
+ end
38
+
39
+ def display_noteql_items(*args)
40
+ @in_array = true
41
+ super(*args)
42
+ @in_array = false
43
+ end
44
+
45
+ def display_noteql_atoms(this, other)
46
+ @ix += 1
47
+ @left = @right
48
+ end
49
+
50
+ def decrease_indentation
51
+ tmp = @left
52
+ @left,key = @left_lineage.pop
53
+
54
+ if tmp.equal?(DELETE_ME)
55
+ @left.delete(key)
56
+ else
57
+ @left[key] = tmp
58
+ end
59
+
60
+ @right = @right_lineage.pop
61
+ end
62
+
63
+ def increase_indentation() end
64
+ def indent() end
65
+
66
+ def display_indices(ixl, ixr)
67
+ @ix = ixr
68
+ end
69
+ end
70
+
71
+ class TestDrawer
72
+ include Gorillib::DiffDrawerMethods
73
+ include TransformLeftToRight
74
+
75
+ def initialize this = nil, other = nil
76
+ @left_lineage = [@left = this]
77
+ @right_lineage = [@right = other]
78
+ @ix = 0
79
+ end
80
+
81
+ def self.diffing_objs this, other
82
+ diff_formatter.display_diff(this, other)
83
+ end
84
+
85
+ def self.diff_formatter(drawer = nil)
86
+ Gorillib::DiffFormatter.new(stream: StringIO.new, drawer: drawer)
87
+ end
88
+
89
+ def self.transform_ltor this, other
90
+ drawer = new(this, other)
91
+ Gorillib::DiffFormatter.new(drawer: drawer).display_diff(this, other)
92
+ drawer.left.should == other
93
+ end
94
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  $:.unshift File.expand_path('../../lib', __FILE__)
2
2
  require 'chef'
3
3
  require 'chef/knife'
4
+ require 'ironfan'
4
5
  require 'fog'
5
6
  Fog.mock!
6
7
  Fog::Mock.delay = 0
@@ -36,3 +37,17 @@ end
36
37
  require 'chef_zero/server'
37
38
  server = ChefZero::Server.new(port: 4000)
38
39
  server.start_background
40
+
41
+ def uncreate_plugin(plugin_class, target_class)
42
+ target_class.registry.clear
43
+ plugin_class.instance_eval{ remove_const :BazBif }
44
+ end
45
+
46
+ class Foo
47
+ end
48
+
49
+ class MyPlugin
50
+ include Ironfan::Plugin::Base; register_with Foo
51
+
52
+ def self.plugin_hook(*_) end
53
+ end