Conversation
cf42ec7 to
998bf60
Compare
998bf60 to
59c4bd9
Compare
983378f to
d29b4c1
Compare
| x.report("check then include") do |times| | ||
| i = 0 | ||
| while (i += 1) < times | ||
| C1.include(M1) unless C1.include?(M1) | ||
| end | ||
| end | ||
|
|
||
| x.report("just include") do |times| | ||
| i = 0 | ||
| while (i += 1) < times | ||
| C2.include(M2) | ||
| end |
There was a problem hiding this comment.
Is this a fair comparison when the check include will only include once across the whole run? Wouldn't it be better to create a class, include it then re-include it?
There was a problem hiding this comment.
Ah, this was me toying with making it so calling abstract lazily does the correct module inclusions in the right places. You probably have multiple abstract methods per class, so I was just curious how repeated attempts to reinclude the same module would work.
d29b4c1 to
783b606
Compare
59c4bd9 to
b6129c3
Compare
lib/type_toolkit.rb
Outdated
| mod.extend(TypeToolkit::Interface) | ||
| mod.extend(TypeToolkit::DSL) | ||
| mod.extend(TypeToolkit::MethodDefRecorder) | ||
| mod.extend(TypeToolkit::HasAbstractMethods) |
There was a problem hiding this comment.
TODO: follow-up on @vinistock's suggestion to merge these modules into one.
I only had these split because I was moving around which pieces should go where, and that just made it easier during development.
However, this raises a new question. Some people might not want the method_missing? behaviour, and are okay with just hitting NoMethodError instead. In that case, what's the best way to make this behaviour configurable?
#!/usr/bin/ruby
$config = {
nicer_error_messages_for_abstract_methods: true
}
class YourClass; end
# Approach 1: Conditionally define method
module TypeToolKit
# Some method needed under all configurations
def f1; end
def f2; end
def f3; end
if $config[:nicer_error_messages_for_abstract_methods]
def method_missing
# magic to raise a nicer message for abstract methods
end
end
end
YourClass.include(TypeToolKit)
# Approach 2: Unconditionally define method on an optional module
module TypeToolKit
# Some method needed under all configurations
def f1; end
def f2; end
def f3; end
end
module TypeToolKit::NicerErrorMessages
def method_missing
# magic to raise a nicer message for abstract methods
end
end
YourClass.include(TypeToolKit)
YourClass.include(TypeToolKit::NicerErrorMessages) if $config[:nicer_error_messages_for_abstract_methods]There was a problem hiding this comment.
Vini and I paired on this, and these modules can't me combined as much as I thought.
TypeToolkit::Interfaceneeds to standalone, because its purpose is to define anincludedhook that only applies to interfaces, and not any classes that use interfacesDSLandMethodDefRecorderare the only pair that could be combined, but I found it useful to be able to haveMethodDefRecorderisolated for testingHasAbstractMethodsneeds to appear on both an interface and implementers of the interface
This separation does also happen to help with making the method_missing behaviour optional in the future, if we ever want to do that.
bbd649e to
2a1f136
Compare
276e63a to
738e534
Compare
276e63a to
4b7e80e
Compare
2a1f136 to
a66d3fb
Compare
a66d3fb to
860b7f9
Compare
860b7f9 to
4d64604
Compare
4d64604 to
f744212
Compare
| # an unimplemented abstract method would usually raise a `NoMethodError`. This module uses `method_missing` to | ||
| # to raise `AbstractMethodNotImplementedError` instead. | ||
| # @requires_ancestor: Kernel | ||
| module AbstractClassMethodReceiver |
There was a problem hiding this comment.
Abstract class methods in the up-stack PR
... which I realize aren't tested.
| c = self.class #: as Class[top] & HasAbstractMethods | ||
|
|
||
| if c.abstract_method_declared?(method_name) | ||
| raise AbstractMethodNotImplementedError, "Abstract method ##{method_name.inspect} was never implemented." |
There was a problem hiding this comment.
The instance method error uses method_name.inspect here, but the class method version (line 23) doesn't. With .inspect, Symbol output includes a : prefix — so you'd get Abstract method #:send_notification was never implemented. which looks odd. Should we be consistent and drop the .inspect?
There was a problem hiding this comment.
Good eye, I intended to drop the .inspect.
Should test this message, too.
| # - An inherited concrete implementation of an abstract method | ||
| # - The error case of calling an unimplemented abstract method | ||
|
|
||
| ############################################# Results ############################################# |
There was a problem hiding this comment.
That's fine, I'd rather have the results checked in here than pasted around some random Slack conversations or GitHub gists
| # frozen_string_literal: true | ||
|
|
||
| class Module | ||
| def interface! |
There was a problem hiding this comment.
Shouldn't this be under DSL?
There was a problem hiding this comment.
Ah okay I see how DSL gets appended later. Unfortunate.
There was a problem hiding this comment.
Yeah I guess there's two parts to the DSL. I don't want to pollute your class with the .abstract macro if you don't opt-in with abstract! or interface!
04decd8 to
50786d5
Compare
f744212 to
4eb6da3
Compare
9d75579 to
66131d6
Compare
50786d5 to
d870047
Compare
66131d6 to
d460f28
Compare

Interfaces are modules that define abstract methods, which must be implemented by the classes that implement the interface.
Example usage: