I am struggle on migration one of our small go project to use kemal + avaram.
for avoid duplicate code, i try extract some common code (which in rails, we treat as before_action) into macro,
Maybe i do things wrong, it that is it, please point it out the correct way, anyway, let me show one example:
# define a macro here for used in several blocks
private macro get_user(env)
open_id = env.params.url["open_id"]
user = UserQuery.new.open_id(open_id).first # <= i define a user variable in macro.
if user.nil?
env.response.status_code = 400
next "Error"
end
{% debug %}
end
post path.admin_update_user do |env|
get_user(env) # <= use macro here, the user can be visited in ECR template.
# ...
end
post path.admin_update_user do |env|
get_user(env) # <= use macro here too
# i try to use `user` object here, but no luck.
UpdateUser.update!(user, env.params)
end
As you can see, i get compile time error, like this:
Error: undefined method 'user' for top-level
If you declared 'user' in a suffix if, declare it in a regular if for this to work.
If the variable was declared in a macro it's not visible outside it)
So, use user outside macro is not supported, right?
What is the correct way to do things like this? as you can see, what i want is just a
before_action like things, which can return early if user is nil.
I think the gist of the problem is the macro isn’t expanded yet when the compiler encounters the update method call.
Doing something like this seems to work:
get_user(env)
{% begin %}
UpdateUser.update!(user, env.params)
{% end %}
Tho technically speaking using a macro in this context actually results in more code in the program. Could probably just use a method for this.
EDIT: A method might be a bit tricky with how kemal handles error handling. I.e. you need to exit early from the block, which you can’t do from outside of it…
EDIT2: You also dont really need to pass env to the macro, as you’re not using that parameter, nor would it work the way you’re thinking.
require "kemal"
class UserQuery
def open_id(id)
pp id
[1, 2, 3]
end
end
module UpdateUser
def self.update!(u, params)
pp u
end
end
# define a macro here for used in several blocks
private macro get_user(env)
open_id = env.params.url["open_id"]
user = UserQuery.new.open_id(open_id).first # <= i define a user variable in macro.
if user.nil?
env.response.status_code = 400
next "Error"
end
end
post "/bar/:open_id" do |env|
get_user(env) # <= use macro here too
{% begin %}
# i try to use `user` object here, but no luck.
UpdateUser.update!(user, env.params)
{% end %}
end
Kemal.run
it not works for me because my code is included in a macro, like this:
module AdminUserController
macro included
# my code is here.
macro get_user
open_id = env.params.url["open_id"]
user = UserQuery.new.open_id(open_id).first
if !user.nil?
env.response.status_code = 400
next "Error"
end
user
end
get ... do
\{% begin %}
...
\{% end }
end
end
end
Here’s an advice that will save you a lot of trouble: don’t use macros. You don’t need them. I’m sure of it. Please rewrite your code in a different way. For example get_user could return User or nil, and respond with an error in case of nil. Then you use it like this:
get_user(env) || next "Error"
I recommend not using macros at all in the beginning. Macros are mostly used for library code, not for application code.