I'm trying to add a method to the Routes class (under the # this throws the error comment). But the compiler throws the resolved_type cannot be nil error. What does it mean? Is there a solution to define the home_controller__show method somewhere which gives me back the "/" String?
Thank you.
require "http"
Routes.new.draw do
get "/", HomeController.show
end
class Main
@@routes : Routes = Routes.new
def self.routes
@@routes
end
def self.routes=(value)
@@routes = value
end
end
class HomeController
def show
"this is show"
end
end
class Route
getter path
getter callback
def initialize(@path : String, &@callback : HTTP::Server::Context -> String)
end
end
class Routes
getter routes
def initialize
@routes = [] of Route
end
def draw
with self yield
Main.routes = self
end
macro get(route, mapping)
route = Route.new({{route}}) do |context|
{{mapping.receiver}}.new.{{mapping.name}}
end
# this throws the error
class Routes
def {{mapping.id.underscore.gsub(/\./, "__")}}
"/"
end
end
Main.routes.routes << route
end
end
Update: this is how I solved it:
require "http"
class Routes
getter routes
def initialize
@routes = [] of Route
end
macro get(route, mapping)
route = Route.new({{route}}) do |context|
{{mapping.receiver}}.new.{{mapping.name}}
end
def self.{{mapping.id.underscore.gsub(/\./, "__")}}
"/"
end
end
end
class Routes
get "/", HomeController.show
end
class Main
@@routes : Routes = Routes.new
def self.routes
@@routes
end
def self.routes=(value)
@@routes = value
end
end
class HomeController
def show
"this is show"
end
end
class Route
getter path
getter callback
def initialize(@path : String, &@callback : HTTP::Server::Context -> String)
end
end
pp Routes.home_controller__show
This setup cannot work. Routes#draw
is a method and its scope (via with self yield
) is an instance of Routes
, thus a runtime object. It's impossible to call a macro (Routes.get
) on a runtime object. Macros evaluate at compile time.
You can fix this by targeting the macro explicitly: Routes.get "/", HomeController.show
.
But then there'll be some other issues though. The macro must be defined before calling it, so you need to reorder the code accordingly.
Furthermore, the macro generates code that reopens a type. This code needs to be at the top-level scope and cannot bet inside a method (the code is in the block of Routes#draw
).
So basically you have some architectural flaws in the API and should reconsider. As possible paths forward, maybe you can make draw
a macro. Or maybe you don't need to use macros at all.