Rails AggregateRoot "on" handlers not detected as instance methods - ruby-on-rails

In Rails event store, AggregateRoot has DSL methods that result in blocks with this shape:
class X
extend AggregateRoot::OnDSL
class MyEvent < RailsEventStore::Event; end
on MyEvent do |event|
# Code
end
end
Inside that block, there's access to the class instance data. But sorbet don't know about it.
Is there a way to tell Sorbet that that block will be injected as an instance method?
I can mark every use of fields or methods as T.unsafe(), but that would remove the typing from it, and adding a cast makes the code harder to read

You can use T.bind to communicate to Sorbet that self has a particular type in a particular block:
class X
extend AggregateRoot::OnDSL
class MyEvent < RailsEventStore::Event; end
on MyEvent do |event|
T.bind(self, MyEvent)
# Code
end
end
More generally, you can add a shim which specifies the type of a DSL API like this, and specify the type the proc will be bound to, like: T.proc.bind(TheTypeOfSelfInTheBlock).params(...).returns(...).
In this case, it's a bit tricky for AggregateRoot::OnDSL.on, if not impossible, for two reasons:
on takes a *event_klasses. You can't be sure which event class triggered your block, so you don't statically know which of the event_klasses the event passed to your block will be.
The type of event_klasses should be event_klasses: T.class_of(RailsEventStore::Event). Even if it were one singular value, you would need the generics system to let you express that the block's argument is specifically the type of event_klasses (and not just any RailsEventStore::Event). I don't think that's currently possible.

Related

Ruby/Rails - force subclasses to override specific methods?

I'm wondering if there is a way to force a subclass to override a method from its parent method in either Ruby or Rails (in Java you would do this with an abstract class/method).
Let's say I have the following classes:
class Pet
end
class Dog < Pet
def collar_color
"red"
end
end
class Cat < Pet
end
dog = Dog.new
dog.collar_color
==> red
cat = Cat.new
cat.collar_color
==> NoMethodError
In this example I would never instantiate a Pet object, but it exists to serve as a way to collect common methods to common classes. But let's say I want to ensure that all subclasses of Pet override the collar_color method. Is there a way to do that? Could I achieve it through testing in some way? Assume I don't want a default defined in the parent class.
My real-life use case is a collection of classes that all have polymorphic ownership of another class. If I have a display page of the owned class, then one of the owner classes not having a method could leave me with a NoMethodError problem.
No, there is no way to enforce this.
I can guarantee you, whatever idea you can come up with, it will break in some way.
First off: doing this statically is out of the question. Determining whether a method is overridden or not is known to be equivalent to solving the Halting Problem.
So, you have to do it dynamically. But even that is going to be problematic.
For example: you could implement the inherited hook and check whether every class that inherits from Pet implements the method. But, that will prevent someone from inheriting their own abstract class. (Also, there is no guarantee when the inherited hook will run – it could run when the class is opened, i.e. before the methods are defined.)
Also, even if you can check that the method exists at the point where a class inherits Pet, the method can still be removed again later, so you don't get any guarantees. And, of course, they can just provide a dummy method, in order to get around your protection.
You could create default implementations of the methods that just raise an Exception, but there is no need to do that: if you don't create a default implementation, that will already raise a NoMethodError exception anyway. (If you do go down this route, do not use NotImplementedError. Instead, use a custom exception that inherits from RuntimeError.)
There are examples of this in the core library: for example, the Enumerable mixin depends on an abstract method each that must be implemented by subclasses. And the way this is handled is by simply documenting that fact:
Usage
To use module Enumerable in a collection class:
Include it:
include Enumerable
Implement method #each which must yield successive elements of the collection. The method will be called by almost any Enumerable method.
That is actually the way any type-related issues have been dealt with in Ruby since the beginning. Since Ruby does not have types, typing only happens in the programmer's head and type information is only written down in documentation.
There always were informal third-party type annotation languages that were used by various IDEs. More recently, two type annotation languages have been introduced: RBI, a third-party type annotation language used by the Sorbet type checker, and RBS, a type annotation language that is part of Ruby proper.
As far as I know, RBS has no way of expressing abstract methods, but RBI does:
class Pet
extend T::Sig
extend T::Helpers
interface!
sig {abstract.returns(String)}
def collar_color; end
end
This will give you a type error if there is object instantiated from a subclass that does not at some point in the inheritance chain define the method. But, of course, only if the user of the code actually type-checks the code using a type-checker like Sorbet. If the user of the code does not type-check it, they will not get a type error.
Ruby has relatively few keywords but it provides the basic building blocks to implement something that vaguely resembles abstract classes or methods.
In its simplest form you just raise an error in the parent "abstract" method:
class AbstractMethodError < StandardError
def initialize(klass, m)
super("Expected #{klass} to implement #{m}")
end
end
class Pet
def collar_color
raise AbstractMethodError.new(self.class, __method__)
end
end
class Cat < Pet
end
Cat.new.collar_color # Expected Cat to implement collar_color (AbstractMethodError)
__method__ is a magic variable that contains the name of the current method.
You can make this a bit more elegant by creating a class method that defines the "abstract method":
module Abstractions
def abstract_method(name)
define_method(name) do
raise AbstractMethodError.new(self.class, __method__)
end
end
end
class Pet
extend Abstractions
abstract_method :collar_color
end
However Ruby is a dynamic langauge and you don't have a compile time check so this will only give a slightly more obvious error message when the method is called. It doesn't actually give any guarentees that subclasses implement the method.
That is down to testing or using type checkers like Sorbet or RBS. In general it might be helpful when learning to forget everything you think you know about Object Oriented Programming and learn the Ruby way. It has a very different design philophy compared to Java - instead of abstract methods and interfaces you use duck typing to see if the object responds to that method.
Just define the default method implementation in the abstract class by raising Not implemented error or something. By doing that you also clarifies in your class design that when others / you want to inherit the Pet class they need to override collar_color method. Clarity is a good think and there is no benefit in not defining a default method in the abstract class.
Or if you want to achieve that by testing you can create a test case for Pet class that check if its descendants is defining their own collar_color method or not. I think Rails / Ruby 3.1 have .descendants methods defined or you can just google them.
# Pet_spec.rb
describe "descendants must implement collar_color" do
it "should not throw error" do
descendants = Pet.descendants
descendants.each do |descendant|
expect { descendant.new.collar_color }.to.not raise_error
end
end
end

What is the difference between instance&class method include&extend (Ruby, Rails)

What's the difference between class method and instance method.
I need to use some functions in a helper "RemoteFocusHelper" (under app/helpers/)
Then include the helper "RemoteFocusHelper" in the Worker module
But when I tried to call 'check_environment' (defined in RemoteFocusHelper),
It raised ""no method error"".
Instead of using "include", I used the "extend" and works.
I wonder know if it is correct that we can only use a class method when in a class method.
Is it possible to call a instance method in a class method ?
By the way,how does the rake resque:work QUEUE='*' know where to search the RemoteFocusHelper I didn't give it the file path.Is the rake command will trace all files under the Rails app?
automation_worker.rb
class AutomationWorker
#queue = :automation
def self.perform(task=false)
include RemoteFocusHelper
if task
ap task
binding.pry
check_environment
else
ap "there is no task to do"
end
end
end
The difference is the context where you're executing. Pretty much every tutorial will have include or extend under the class:
class Foo
include Thingy
end
class Bar
extend Thingy
end
This will get executed at the time the class is defined: self is Foo (or Bar) (of type Class). extend will thus dump the module contents into self - which creates class methods.
When you do it inside a method definition, self is the instance object (of type Foo or Bar). Thus the place where the module gets dumped into changes. Now if you extend (the module contents), it dumps them into what is now self - resulting in instance methods.
EDIT: It is also worth noting that because extend works on any instance object, it is defined on Object. However, since only modules and classes are supposed to be able to include stuff, include is an instance method of Module class (and, by inheritance, Class as well). As a consequence of this, if you try putting include inside a definition of an instance method, it will fail hard, since most things (including your AutomationWorker) are not descended from Module, and thus do not have access to the include method.

Calling a subclass method from a superclass

Preface: This is in the context of a Rails application. The question, however, is specific to Ruby.
Let's say I have a Media object.
class Media < ActiveRecord::Base
end
I've extended it in a few subclasses:
class Image < Media
def show
# logic
end
end
class Video < Media
def show
# logic
end
end
From within the Media class, I want to call the implementation of show from the proper subclass. So, from Media, if self is a Video, then it would call Video's show method. If self is instead an Image, it would call Image's show method.
Coming from a Java background, the first thing that popped into my head was 'create an abstract method in the superclass'. However, I've read in several places (including Stack Overflow) that abstract methods aren't the best way to deal with this in Ruby.
With that in mind, I started researching typecasting and discovered that this is also a relic of Java thinking that I need to banish from my mind when dealing with Ruby.
Defeated, I started coding something that looked like this:
def superclass_method
# logic
this_media = self.type.constantize.find(self.id)
this_media.show
end
I've been coding in Ruby/Rails for a while now, but since this was my first time trying out this behavior and existing resources didn't answer my question directly, I wanted to get feedback from more-seasoned developers on how to accomplish my task.
So, how can I call a subclass's implementation of a method from the superclass in Rails? Is there a better way than what I ended up (almost) implementing?
Good question, but you are making it too complicated. Keep in mind a few principles and it should all be clear...
The types will be resolved dynamically, so if a show exists anywhere in the object's class hierarchy at the moment it is actually called then Ruby will find it and call it. You are welcome to type in method calls to anything that may or may not exist in the future and it's legal ruby syntax and it will parse. You can type in an expression that includes a reference to this_will_never_be_implemented and no one will care unless it actually gets called.
Even in Java, there is only one actual object. Yes, you may have a method in the superclass that's calling a method, but it is an instance of the derived class (as well as an instance of the base class) and so you can count on the new show being called.
In a sense, every Ruby class is an abstract class containing stubs for every possible method that might be defined in the future. You can call anything without access qualifiers in the base class or derived class.
If you want a null superclass implementation, you may want to define one that does nothing or raises an exception.
Update: Possibly, I should have just said "call show like any other method" and left it at that, but having come this far I want to add: You can also implement show with Ruby's version of multiple inheritance: include SomeModule. Since you are obviously interested in Ruby's object model, you might implement your attribute with a mixin just for fun.
As you know having a superclass know about subclass functionality is a big no-no, which is why you wanted the abstract method.
What you want to do is define show in your superclass. Then you can call it in the superclass and the subclass will call its own version but the superclass won't throw an error.
class Media < ActiveRecord::Base
def show
# This method should be overloaded in a subclass
puts "Called from Media"
end
def do_something
show
end
end
class Image < Media
def show
puts "Called from Image"
end
end
class Video < Media
def show
puts "Called from Video"
end
end
i = Image.new
i.do_something
=> Called from Image
v = Video.new
v.do_something
=> Called from Video
Simple answer. Just call it. Ruby does not have compile-time checking so there is no one to complain that show isn't defined on Media. If #example is an instance of Image, then any call to #example.show will be sent to Image#show first, wherever it is made. Only if Image#show doesn't exist then the call will be passed on to Media, even if the call originated from code defined in Media
If you want to call show on self from within a method of Media, simply do it. However, make sure self responds to the method call.
class Media < ActiveRecord::Base
def foo
if self.respond_to?(:show)
self.show
else
... // *
end
end
...
end
To avoid the branch, implement show on Media, using the * as the body of show
class Media < ActiveRecord::Base
def foo
self.show
end
def show
...
end
end

Sample Ruby code, How is this abstracting things out?

Watching this video by Yehuda, and he gave this snippet about how Ruby helps you build better abstractions.
class FOWA
def self.is_fun
def fun?
true
end
end
is_fun
end
He was talking about, in ruby, how if you are repeating code in your class over and over again, you can abstract it out without having to think about things in terms of methods etc. And he said this was using a metaprogramming technique.
Can someone explain what this is?
It is a class method on FOWA (so its like a static method, you don't need an instance to call it), and this class method is really just wrapping another method that returns true.
And this is_fun class method is now being executed or what? not sure what the last line "is_fun" is doing?
http://vimeo.com/11679138
The is_fun call at the end of the class calls the static method. The static method then defines the fun? method inside of the FOWA class. Then, you can do this:
f = FOWA.new
f.fun?
If you take out the is_fun call at the end of the class, the fun? method doesn't get defined.
He mentioned that you wouldn't use it in this way, but the point is how easy it is to dynamically add a method to a class. You might use it like this if you wanted the method to be available in subclasses and you wouldn't call is_fun in FOWA, but you might in a subclass. It gets a little more interesting if you have a parameter for is_fun and the definition of fun? changes depending on that parameter.
This also leads right into modules because you can define a module with the same is_fun method and then just have your class extend the module and the methods in the module are available in the class. You would use this technique if you want your method to be available to more than just subclasses of FOWA.

Why does Sunspot change the `self` in the search DSL block?

I noticed (and verified in the sunspot code) the following behavior
class Foo < ActiveRecord::Base
def bar
search_str = "foo"
Boo.search do
keywords(search_str)
p self.id
p self
end
end
end
In the code above, the DSL block can access the variables defined in
the context. But the self inside block, points to an instance of
Sunspot::DSL::Search class (instead of an instance of Foo class.)
When I try to access self.id, instead of getting the id of a Foo
object; I get the id of a Sunspot::DSL::Search object.
I think Sunpot is doing some binding swapping/delegation magic in Util.instance_eval_or_call method.
I am curious why Sunspot does this and why there is no warning about
this behavior in documentation.
Edit:
The Sunspot search method can be found at this link
The code below will illustrate my point. In the method foo I have a block that behaves as expected. In the method bar, the block doesn't behave.
class Order < ActiveRecord::Base
def foo
p self.class.name # prints Order
# The `self` inside the block passed to the each method
# points to an object of type Order (as expected)
# This is the normal block behavior.
[1,2,3].each do |val|
p self.class.name # prints Order
end
end
def bar
p self.class.name # prints Order
# the `self` inside the block passed to the search method
# points to an object of type Sunspot::DSL::Search.
# This is NOT the normal block behavior.
Order.search do
keywords("hello")
p self.class.name # prints Sunspot::DSL::Search
end
end
Note2
I have located the code in Sunspot source tree that modifies the normal block behavior. My question is about the reason for rigging the binding like this.
Note3
Specifically, I found an issue while invoking the id method in side the block. The search method delegates the method invocation inside the block to the DSL object and if it doesn't find the method then call is re-delegated to the calling context. Search method strips all but the essential methods from the DSL object before registering delegation code. The id method is not stripped out. This is causing the problem. For all the other methods delegation works fine.
This behavior is not documented in the Sunspot method documentation.
Ok, I know how it works:
The magic is found in ContextBoundDelegate in util.rb.
It creates a blank slate delegator object.
The delegator forwards all method calls to the 'receiver'. In your example the 'receiver' is probably the object which contains the methods keywords and with and any_of and so on.
If a given method is not found in 'receiver' then it forwards the method call onto the 'context' object
The context object is the object that holds the bindings for the block.
You find the context object for a given block by doing this: eval('self', block.binding)
Rationale:
So the effect of all this is that the block not only gets access to the methods in the search object (a la instance_eval) but it also gets access to local methods in the calling scope of the block.
The block also, of course, gets access to local variables in the calling scope of the block, but this is just normal closure behaviour.
The block does not, however, get access to instance variables in the calling scope of the block.
The following code might be useful as it follows roughly the same idea but is much simpler and less sophisticated: Using methods from two different scopes?
Isn't it just an instance_eval? Unless you're talking about accessing instance variables from the calling context, this is normal closure behaviour.
I'm assuming the instance_eval (the change in self) is used to provide keywords and other related methods to the block.

Resources