bogus 0.0.2 → 0.0.3.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/.gitignore +2 -0
  2. data/Gemfile.lock +14 -2
  3. data/Guardfile +5 -0
  4. data/README.md +7 -249
  5. data/bogus.gemspec +3 -1
  6. data/features/.nav +21 -0
  7. data/features/authors.md +42 -0
  8. data/{CHANGELOG.md → features/changelog.md} +9 -2
  9. data/features/configuration_options.feature +1 -2
  10. data/features/{contract_tests_mocks.feature → contract_tests/contract_tests_mocks.feature} +4 -1
  11. data/features/{contract_tests_spies.feature → contract_tests/contract_tests_spies.feature} +2 -0
  12. data/features/{contract_tests_stubs.feature → contract_tests/contract_tests_stubs.feature} +14 -0
  13. data/features/contract_tests/readme.md +26 -0
  14. data/features/{return_value_contracts.feature → contract_tests/return_value_contracts.feature} +6 -2
  15. data/features/{anonymous_doubles.feature → fakes/anonymous_doubles.feature} +24 -8
  16. data/features/{fake_objects.feature → fakes/fake_objects.feature} +39 -4
  17. data/features/fakes/global_fake_configuration.feature +88 -0
  18. data/features/fakes/readme.md +11 -0
  19. data/features/fakes/replacing_classes.feature +148 -0
  20. data/features/getting_started.md +36 -0
  21. data/features/license.md +9 -0
  22. data/features/readme.md +173 -0
  23. data/features/safe_stubbing/argument_matchers.feature +30 -0
  24. data/features/safe_stubbing/readme.md +35 -0
  25. data/features/{safe_mocking.feature → safe_stubbing/safe_mocking.feature} +14 -0
  26. data/features/{safe_stubbing.feature → safe_stubbing/safe_stubbing.feature} +18 -4
  27. data/features/{spies.feature → safe_stubbing/spies.feature} +19 -4
  28. data/features/step_definitions/rspec_steps.rb +14 -5
  29. data/features/support/env.rb +4 -0
  30. data/lib/bogus.rb +2 -0
  31. data/lib/bogus/adds_recording.rb +11 -7
  32. data/lib/bogus/any_args.rb +7 -0
  33. data/lib/bogus/anything.rb +11 -0
  34. data/lib/bogus/contract_not_fulfilled.rb +17 -10
  35. data/lib/bogus/copies_classes.rb +2 -6
  36. data/lib/bogus/creates_fakes_with_stubbed_methods.rb +40 -0
  37. data/lib/bogus/double.rb +28 -8
  38. data/lib/bogus/ensures_all_interactions_satisfied.rb +36 -0
  39. data/lib/bogus/fake.rb +6 -0
  40. data/lib/bogus/fake_configuration.rb +69 -0
  41. data/lib/bogus/fakes_classes.rb +21 -0
  42. data/lib/bogus/has_overwritten_methods.rb +24 -0
  43. data/lib/bogus/have_received_matcher.rb +63 -0
  44. data/lib/bogus/injector.rb +36 -14
  45. data/lib/bogus/interaction.rb +33 -17
  46. data/lib/bogus/makes_substitute_methods.rb +15 -0
  47. data/lib/bogus/mocking_dsl.rb +31 -0
  48. data/lib/bogus/multi_stubber.rb +15 -0
  49. data/lib/bogus/not_all_expectations_satisfied.rb +27 -0
  50. data/lib/bogus/overwriten_classes.rb +15 -0
  51. data/lib/bogus/overwrites_classes.rb +2 -2
  52. data/lib/bogus/overwrites_methods.rb +42 -0
  53. data/lib/bogus/proxies_method_calls.rb +23 -0
  54. data/lib/bogus/proxy_class.rb +25 -16
  55. data/lib/bogus/public_methods.rb +36 -11
  56. data/lib/bogus/record_interactions.rb +3 -9
  57. data/lib/bogus/recording_proxy.rb +5 -0
  58. data/lib/bogus/registers_created_fakes.rb +2 -1
  59. data/lib/bogus/resets_overwritten_classes.rb +14 -0
  60. data/lib/bogus/resets_stubbed_methods.rb +12 -0
  61. data/lib/bogus/responds_to_everything.rb +11 -0
  62. data/lib/bogus/rspec.rb +7 -0
  63. data/lib/bogus/rspec_adapter.rb +17 -0
  64. data/lib/bogus/rspec_extensions.rb +8 -20
  65. data/lib/bogus/shadow.rb +60 -0
  66. data/lib/bogus/verifies_contracts.rb +5 -1
  67. data/lib/bogus/verifies_stub_definition.rb +5 -0
  68. data/lib/bogus/version.rb +1 -1
  69. data/lib/tracks_existence_of_test_doubles.rb +11 -0
  70. data/spec/bogus/adds_recording_spec.rb +46 -10
  71. data/spec/bogus/anything_spec.rb +13 -0
  72. data/spec/bogus/copies_classes_spec.rb +4 -3
  73. data/spec/bogus/creates_fakes_with_stubbed_methods_spec.rb +121 -0
  74. data/spec/bogus/double_spec.rb +63 -20
  75. data/spec/bogus/ensures_all_interactions_satisfied_spec.rb +43 -0
  76. data/spec/bogus/fake_configuration_spec.rb +99 -0
  77. data/spec/bogus/fakes_classes_spec.rb +46 -0
  78. data/spec/bogus/have_received_matcher_spec.rb +56 -0
  79. data/spec/bogus/interaction_spec.rb +18 -7
  80. data/spec/bogus/interactions_repository_spec.rb +42 -0
  81. data/spec/bogus/makes_substitute_methods_spec.rb +24 -0
  82. data/spec/bogus/mocking_dsl_spec.rb +234 -7
  83. data/spec/bogus/multi_stubber_spec.rb +31 -0
  84. data/spec/bogus/overwriten_classes_spec.rb +27 -0
  85. data/spec/bogus/overwrites_classes_spec.rb +2 -2
  86. data/spec/bogus/overwrites_methods_spec.rb +107 -0
  87. data/spec/bogus/proxy_class_spec.rb +6 -0
  88. data/spec/bogus/record_interactions_spec.rb +3 -4
  89. data/spec/bogus/registers_created_fakes_spec.rb +8 -0
  90. data/spec/bogus/resets_overwritten_classes_spec.rb +26 -0
  91. data/spec/bogus/resets_stubbed_methods_spec.rb +16 -0
  92. data/spec/bogus/shadow_spec.rb +182 -0
  93. data/spec/bogus/verifies_contracts_spec.rb +9 -3
  94. data/spec/bogus/verifies_stub_definition_spec.rb +4 -0
  95. data/spec/spec_helper.rb +3 -0
  96. data/spec/support/fake_creator_of_fakes.rb +15 -0
  97. data/spec/support/sample_fake.rb +13 -0
  98. data/spec/tracks_existence_of_test_doubles_spec.rb +26 -0
  99. metadata +105 -32
  100. data/lib/bogus/creates_anonymous_stubs.rb +0 -27
  101. data/lib/bogus/invocation_matcher.rb +0 -27
  102. data/lib/bogus/rr_proxy.rb +0 -5
  103. data/spec/bogus/invocation_matcher_spec.rb +0 -26
@@ -0,0 +1,36 @@
1
+ ## Installation
2
+
3
+ To install Bogus, all you need to do is add:
4
+
5
+ gem "bogus"
6
+
7
+ to your Gemfile.
8
+
9
+ ## Configuration
10
+
11
+ In your `spec_helper.rb`, require bogus:
12
+
13
+ require 'bogus/rspec'
14
+
15
+ Bogus will hook into RSpec on it's own, but if you want to be explicit about the used mock framework, you can put this in your `spec_helper.rb`:
16
+
17
+ RSpec.configure do |c|
18
+ # already done by requiring bogus/rspec
19
+ c.mock_with Bogus::RSpecAdapter
20
+ end
21
+
22
+ And configure it to look for classes in your namespace:
23
+
24
+ Bogus.configure do |c|
25
+ c.search_modules << My::Namespace
26
+ end
27
+
28
+ You will probably also want to create a configuration file for your fakes (for example `spec/support/fakes.rb`):
29
+
30
+ Bogus.fakes do
31
+ # fakes go here
32
+ end
33
+
34
+ and require it in your gem file:
35
+
36
+ require_relative 'support/fakes.rb'
@@ -0,0 +1,9 @@
1
+ # The MIT License (MIT)
2
+
3
+ Copyright (c) 2012-2013 Adam Pohorecki
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,173 @@
1
+ Bogus is a library that aims to reduce the risks associated with isolated unit testing.
2
+
3
+ ## The problem
4
+
5
+ It is not uncommon to encounter code like this in isolated unit tests:
6
+
7
+ it "returns the average score" do
8
+ scores = stub(get: [5, 9])
9
+ students = stub(all: ["John", "Mary"])
10
+
11
+ report_card = ReportCard.new(scores, students)
12
+
13
+ report_card.average_score.should == 7
14
+ end
15
+
16
+ _NOTE: In the above example we use mocha syntax, but this patten is common
17
+ to all the major mocking frameworks_
18
+
19
+ The test above not only ensures that `ReportCard` can calculate the average score for all the students, but it also specifies how this class will interact with it's collaborators and what those collaborators will be.
20
+
21
+ This style of testing enables us to practice so called *programming by wishful thinking*. We can implement `ReportCard` and get it to work before it's collaborators are implemented. This way we can design our system top-down and only implement what we need. This is a Very Good Thing(tm).
22
+
23
+ However, the same freedom that comes from not having to implement the collaborators, can quickly turn against us. Once implement `ReportCard`, what test will tell us that `Scores` and `Students` are not implemented yet?
24
+
25
+ The kind of stubbing that you see in the example above requires us to write integrated or end-to-end test *just to make sure that the objects we implemented fit together*. This is a problem, because those tests can be quite slow and hard to set up in a way that covers all the integration points between our objects.
26
+
27
+ Another problem is that it's quite likely that `Students` and `Scores` will be collaborating with more objects than the `ReportCard`. If so, you will find yourself typing code like this over and over again:
28
+
29
+ students = stub(all: ["John", "Mary"])
30
+
31
+ This is duplication. It is also a problem, because if you decide to change the interface of the `Students` class, suddenly you will have to remember every place you created that stub and fix it. Your tests won't help you, because they don't have any idea what the `Students` interface should look like.
32
+
33
+ ## The solution
34
+
35
+ Bogus makes your test doubles more assertive. They will no longer be too shy to tell you: "Hey, you are stubbing the wrong thing!".
36
+
37
+ Let's reexamine our previous example, this time Bogus-style:
38
+
39
+ it "returns the average score" do
40
+ students = fake(:students, get: [5, 9])
41
+ scores = fake(:scores, all: ["John", "Mary"])
42
+
43
+ report_card = ReportCard.new(scores, students)
44
+
45
+ report_card.average_score.should == 7
46
+ end
47
+
48
+ Can you spot the difference? Not much, huh?
49
+
50
+ However, the code above will not only make sure that the `ReportCard` works, it will also make sure that classes `Students` and `Scores` exist and have an interface that matches what you stub on them.
51
+
52
+ ## DRY out your fake definitions
53
+
54
+ By now you know how Bogus can help you with making sure that your stubs match the interface of your production classes. However, the attentive reader has probably spotted a problem with the solution above already. If all you need to change in your tests is the word `stub` to `fake` and give it some name, then how is that any better when it comes to scattering the fake interface definition all over your specs?
55
+
56
+ The answer is: it's just slightly better. The reason it's better is that you only need to ever stub methods that return meaningful values. Let us explain.
57
+
58
+ ### Tell-don't-ask methods
59
+
60
+ Let's say we have a `PushNotifier` class:
61
+
62
+ class PushNotifier
63
+ def notify_async(messages)
64
+ end
65
+ end
66
+
67
+ Now if you test an object that collaborates with our `PushNotifier`, you would do something like this:
68
+
69
+ fake(:push_notifier)
70
+
71
+ it "send push notifications when comment is added" do
72
+ comment_adder = CommentAdder.new(push_notifier)
73
+
74
+ comment_adder.add("Hello world!")
75
+
76
+ push_notifier.should have_received.notify_async("Comment: 'Hello world!' added.")
77
+ end
78
+
79
+ While not really impressive, this feature is worth mentioning because it will eliminate a lot of the mocking and stubbing from your tests.
80
+
81
+ ### Global fake configuration
82
+
83
+ Bogus also has a solution for DRYing stubbing of the methods that return values. All you need to do is to provide a reasonable default return value for those methods in the global fake configuration.
84
+
85
+ Bogus.fakes do
86
+ fake(:students) do
87
+ all []
88
+ end
89
+
90
+ fake(:scores) do
91
+ get []
92
+ end
93
+ end
94
+
95
+ Now you will only need to stub those methods when you actually care about their return value, which is exactly what we want.
96
+
97
+ ## Contract tests
98
+
99
+ Bogus is not the only mocking library to implement fakes and safe mocking. However, it is the first library to implement the concept of contract tests [as defined by J. B. Rainsberger][contracts].
100
+
101
+ Let's start with an example:
102
+
103
+ stub(scores).get(["John", "Mary"]) { [5, 9] }
104
+
105
+ We already know that Bogus makes sure for us that the `Scores` class exists, has a `get` method and that this method can be called with one argument.
106
+
107
+ It would also be nice to know that the input/output that we stub makes sense for this method.
108
+
109
+ Contract tests are an idea, that whenever we stub `Scores#get` with argument `["John", "Mary"]` to return the value `[5, 9]`, we should add a test for the Scores class, that calls method `get` with those same arguments and have the same return value.
110
+
111
+ A contract test like that could look like this:
112
+
113
+ it "returns scrores for students" do
114
+ scores = Scores.new(redis)
115
+ scores.add("John", 5)
116
+ scores.add("Mary", 9)
117
+
118
+ scores.get(["John", "Mary"]).should == [5, 9]
119
+ end
120
+
121
+ Obviously Bogus won't be able to write those tests for you. However it can remind you if you forget to add one.
122
+
123
+ To add contract test verification, the only thing you need to do is add the line:
124
+
125
+ verify_contract(:students)
126
+
127
+ to your tests for `Students` class.
128
+
129
+ ## Not only for people who use Dependency Injection
130
+
131
+ The examples above all have one thing in common: they assume that your code has some way of injecting dependencies. We believe it should, and that's why we wrote [Dependor][dependor].
132
+
133
+ However, we are aware that this practice is not very common in the Ruby community. That's why Bogus supports replacing classes with fakes.
134
+
135
+ Let's assume, that you have production code like this:
136
+
137
+ class PushNotifier
138
+ def self.notify_async(message)
139
+ # ...
140
+ end
141
+ end
142
+
143
+ class CommentAdder
144
+ def self.add(parent, comment)
145
+ PushNotifier.notify_async("comment added")
146
+ # ...
147
+ end
148
+ end
149
+
150
+ You can test it easily, with all the benefits of fakes, safe stubbing and contracts:
151
+
152
+ describe CommentAdder do
153
+ fake_class(PushNotifier)
154
+
155
+ it "should send a push notification" do
156
+ CommentAdder.add("the user", "the comment")
157
+
158
+ PushNotifier.should have_received.notify_async("comment added")
159
+ end
160
+ end
161
+
162
+ describe PushNotifier do
163
+ verify_contract(:push_notifier)
164
+
165
+ it "notifies about comments asynchronously" do
166
+ PushNotifier.notify_async("comment added")
167
+
168
+ # ...
169
+ end
170
+ end
171
+
172
+ [contracts]: http://www.infoq.com/presentations/integration-tests-scam
173
+ [dependor]: https://github.com/psyho/dependor
@@ -0,0 +1,30 @@
1
+ Feature: Argument matchers
2
+
3
+ Bogus supports some argument matchers for use, when you don't really care about exact equality of arguments passed in or spied on.
4
+
5
+ Background:
6
+ Given a file named "foo.rb" with:
7
+ """ruby
8
+ class Catalog
9
+ def self.books_by_author_and_title(author, title)
10
+ end
11
+ end
12
+ """
13
+
14
+ Scenario: Stubbing methods with any arguments
15
+ Then the following test should pass:
16
+ """ruby
17
+ stub(Catalog).books_by_author_and_title(any_args) { :some_book }
18
+
19
+ Catalog.books_by_author_and_title("Mark Twain", "Tom Sawyer").should == :some_book
20
+ """
21
+
22
+ Scenario: Stubbing methods with some wildcard arguments
23
+ Then the following test should pass:
24
+ """ruby
25
+ stub(Catalog).books_by_author_and_title("Mark Twain", anything) { :twains_book }
26
+
27
+ Catalog.books_by_author_and_title("Mark Twain", "Tom Sawyer").should == :twains_book
28
+ Catalog.books_by_author_and_title("Mark Twain", "Huckleberry Finn").should == :twains_book
29
+ """
30
+
@@ -0,0 +1,35 @@
1
+ Bogus let's you stub methods on any object, not just fakes, which makes this feature usable even in scenarios when you don't follow the Inversion of Control Principle.
2
+
3
+ When you stub/mock a method in Bogus, it will automatically ensure that:
4
+
5
+ 1. The method actually exists
6
+ 2. It can take the number of arguments you passed
7
+
8
+ Those of you familiar with RR stubbing syntax, will fill right at home with Bogus:
9
+
10
+ stub(object).method_name(*arguments) { return_value }
11
+
12
+ One key difference is for when you want to stub a method for any arguments:
13
+
14
+ # RR syntax
15
+ stub(object).method_name { return_value }
16
+
17
+ # Bogus syntax
18
+ stub(object).method_name(any_args) { return_value }
19
+
20
+ One other, quite important thing, is that Bogus does not erase the method signature when stubbing:
21
+
22
+ class Library
23
+ def self.checkout(book)
24
+ end
25
+ end
26
+
27
+ The following would be OK in RR:
28
+
29
+ stub(Library).checkout { "foo" }
30
+ Library.checkout("a", "b") # returns "foo"
31
+
32
+ But not in Bogus:
33
+
34
+ stub(Library).checkout(any_args) { "foo" }
35
+ Library.checkout("a", "b") # raises an error
@@ -1,5 +1,19 @@
1
1
  Feature: Safe mocking
2
2
 
3
+ In Bogus you normally use the following pattern to make sure right messages were sent between the tested object and it's collaborators:
4
+
5
+ 1. Stub the collaborator method in let or before
6
+ 2. In one test ensure that the tested method returns the right thing
7
+ 3. In another test use `have_received` to ensure that the method on collaborator was called with right arguments.
8
+
9
+ However, there are cases when the more general stub in let or before is not enough. Then, we can use mocking to reduce the amount of code written.
10
+
11
+ The syntax for mocking is:
12
+
13
+ mock(object).method_name(*args) { return_value }
14
+
15
+ You can only mock methods that actually exist on an object. It will also work with methods that the object `responds_to?`, but (obviously) without being able to check the method signature.
16
+
3
17
  Background:
4
18
  Given a file named "foo.rb" with:
5
19
  """ruby
@@ -1,9 +1,10 @@
1
1
  Feature: Safe stubbing
2
2
 
3
- Most Ruby test double libraries let you stub methods that don't exist.
4
- Bogus is different in this respect: not only does it not allow stubbing
5
- methods that don't exist, it also ensures that the number of arguments
6
- you pass to those methods matches the method definition.
3
+ Most Ruby test double libraries let you stub methods that don't exist. Bogus is different in this respect: not only does it not allow stubbing methods that don't exist, it also ensures that the number of arguments you pass to those methods matches the method definition.
4
+
5
+ The stubbing syntax is:
6
+
7
+ stub(object).method_name(arg1, arg2, ...) { return_value }
7
8
 
8
9
  Background:
9
10
  Given a file named "foo.rb" with:
@@ -60,3 +61,16 @@ Feature: Safe stubbing
60
61
  end
61
62
  end
62
63
  """
64
+
65
+ Scenario: Stubbing methods multiple times
66
+ Then spec file with following content should fail:
67
+ """ruby
68
+ describe Library do
69
+ it "stubbing with too many arguments" do
70
+ library = Library.new
71
+
72
+ stub(library).checkout("some book") { :bought }
73
+ stub(library).checkout("book", "and one argument too many") { :whatever }
74
+ end
75
+ end
76
+ """
@@ -1,9 +1,8 @@
1
1
  Feature: Spies
2
2
 
3
- Object Oriented Programming is all about messages sent between the objects.
4
- If you follow principles like "Tell, Don't Ask", you will enable yourself
5
- to combine Bogus's powerful feature of faking objects with it's ability
6
- to verify object interactions.
3
+ Object Oriented Programming is all about messages sent between the objects. If you follow principles like "Tell, Don't Ask", you will enable yourself to combine Bogus's powerful feature of faking objects with it's ability to verify object interactions.
4
+
5
+ Typically, stubbing libraries force you to first stub a method, so that you can later make sure it was called. However, if you use fakes, Bogus lets you verify that a method was called (or not) without stubbing it first.
7
6
 
8
7
  Background:
9
8
  Given a file named "foo.rb" with:
@@ -76,3 +75,19 @@ Feature: Spies
76
75
  end
77
76
  end
78
77
  """
78
+
79
+ Scenario: Spying on previously stubbed methods
80
+ Then spec file with following content should pass:
81
+ """ruby
82
+ describe Student do
83
+ fake(:library)
84
+
85
+ it "studies using books from library" do
86
+ stub(library).checkout("Moby Dick") { "checked out" }
87
+
88
+ library.checkout("Moby Dick").should == "checked out"
89
+
90
+ library.should have_received.checkout("Moby Dick")
91
+ end
92
+ end
93
+ """
@@ -6,13 +6,8 @@ Given /^a spec file named "([^"]*)" with:$/ do |file_name, string|
6
6
  Given a file named "#{file_name}" with:
7
7
  """ruby
8
8
  require 'bogus/rspec'
9
-
10
9
  require_relative 'foo'
11
10
 
12
- RSpec.configure do |config|
13
- config.mock_with :rr
14
- end
15
-
16
11
  #{string}
17
12
  """
18
13
  }
@@ -64,3 +59,17 @@ Then /^spec file with following content should fail:$/ do |string|
64
59
  Then the specs should fail
65
60
  }
66
61
  end
62
+
63
+ Then /^the following test should pass:$/ do |string|
64
+ steps %Q{
65
+ When I run spec with the following content:
66
+ """ruby
67
+ describe Bogus do
68
+ specify do
69
+ #{string}
70
+ end
71
+ end
72
+ """
73
+ Then all the specs should pass
74
+ }
75
+ end
@@ -1 +1,5 @@
1
1
  require 'aruba/cucumber'
2
+
3
+ Before('@known_bug') do
4
+ pending("This scenario fails because of a known bug")
5
+ end
data/lib/bogus.rb CHANGED
@@ -2,7 +2,9 @@ require 'dependor'
2
2
 
3
3
  require_relative 'bogus/takes'
4
4
  require_relative 'bogus/record_interactions'
5
+ require_relative 'bogus/proxies_method_calls'
5
6
  require_relative 'bogus/rspec_extensions'
7
+ require_relative 'bogus/rspec_adapter'
6
8
 
7
9
  all_files = Dir[File.expand_path('../**/*.rb', __FILE__)]
8
10
  all_files = all_files.reject{|f| f.include?('bogus/rspec') }.sort
@@ -1,11 +1,15 @@
1
- class Bogus::AddsRecording
2
- extend Bogus::Takes
1
+ module Bogus
2
+ class AddsRecording
3
+ extend Takes
3
4
 
4
- takes :converts_name_to_class, :create_proxy_class, :overwrites_classes
5
+ takes :converts_name_to_class, :create_proxy_class, :overwrites_classes,
6
+ :overwritten_classes
5
7
 
6
- def add(name)
7
- klass = converts_name_to_class.convert(name)
8
- new_klass = create_proxy_class.call(name, klass)
9
- overwrites_classes.overwrite(klass, new_klass)
8
+ def add(name, klass = nil)
9
+ klass ||= converts_name_to_class.convert(name)
10
+ new_klass = create_proxy_class.call(name, klass)
11
+ overwrites_classes.overwrite(klass.name, new_klass)
12
+ overwritten_classes.add(klass.name, klass)
13
+ end
10
14
  end
11
15
  end