Possibility of routing prefixed routes automatically

12 views
Skip to first unread message

Janko Marohnić

unread,
Mar 21, 2021, 2:12:45 PMMar 21
to Rodauth
Hi Jeremy,

I wanted to ask whether you would be open for a pull request that would make `r.rodauth` automatically route routes with a path prefix, without the need for `r.on "<prefix>"`. It would of course keep backwards compatibility for apps using `r.on "<prefix>"`.

The reason I would like to make this change is because e.g. `r.on "auth"` will by default not allow the main app to define its own "auth/*" routes when the Rodauth app is used as a middleware, unless the `r.on` block explicitly marks the request as not handled, either using the pass plugin or by throwing `:next` as you had suggested. While both solutions work, they do require the user to know more about Roda routing, and I'm trying to find alternatives which would minimize the learning curve.

Updating the documentation would definitely help, but I thought it would be convenient in general if we could remove this extra step, so that all that's needed to add a path prefix is changing the `prefix` setting. Personally, I'm often forgetting to update the `r.rodauth` call after setting a `prefix`, so I definitely see an opportunity here for improved ergonomics :)

Let me know what you think.

Kind regards,
Janko

Jeremy Evans

unread,
Mar 21, 2021, 4:39:48 PMMar 21
to rod...@googlegroups.com
This would have to be added as a separate method that required the prefix.  If it wasn't added as a separate method, then you would have:

route do |r|
  r.on "auth" do
    r.rodauth
  end
end

Respond to both /auth/login and /auth/auth/login (for example), which I don't want.

Thanks,
Jeremy

Janko Marohnić

unread,
Mar 21, 2021, 5:58:10 PMMar 21
to Rodauth
I came up with the following implementation, which checks whether any `r.on` block has already been used by comparing `r.remaining_path` with `r.path_info`, and if it hasn't it does it internally, and calls pass from the pass plugin to break out.

diff --git a/lib/rodauth.rb b/lib/rodauth.rb
index 7f86828..d08bc6c 100644
--- a/lib/rodauth.rb
+++ b/lib/rodauth.rb
@@ -28,6 +28,8 @@ module Rodauth
       app.plugin :flash unless opts[:flash] == false
       app.plugin :h
     end
+
+    app.plugin :pass

   end

   def self.configure(app, opts={}, &block)
@@ -359,7 +361,14 @@ module Rodauth

   module RequestMethods
     def rodauth(name=nil)
-      scope.rodauth(name).route!
+      if scope.rodauth(name).prefix && remaining_path == path_info
+        on scope.rodauth(name).prefix[1..-1] do
+          scope.rodauth(name).route!
+          pass
+        end
+      else
+        scope.rodauth(name).route!
+      end

     end
   end
 end


This implementation wouldn't route "/auth/auth/login" from your example. I'm not sure whether it has other flaws I'm not aware of.

Kind regards,
Janko

Jeremy Evans

unread,
Mar 22, 2021, 12:31:25 AMMar 22
to rod...@googlegroups.com
Implementation-wise, it requires loading the pass plugin, and I don't want rodauth to require that.

I don't want magical behavior for r.rodauth, even with a implementation that didn't require loading the pass plugin,. I don't want behavior that tries to guess what the user's intention was and behaves differently in different contexts, sometimes using prefixes and sometimes not.  I want consistent behavior in all cases.

If you are against a separate method for this, I would be open to a separate configuration option to enable support which would always use the prefix in r.rodauth.

Alternatively, you could just override r.rodauth in rodauth-rails for the behavior you want.

Thanks,
Jeremy

Janko Marohnić

unread,
Mar 22, 2021, 12:39:58 PMMar 22
to Rodauth
Thanks for the feedback, I agree now that it's too much guesswork which could potentially backfire.

I'm not against a separate method, I was just looking if there was a safe way to have r.rodauth handle both, so that the user doesn't need to make any edits other than the `prefix` option. 

However, even if I used a different method, I wouldn't know how to implement it without the pass plugin in a way that's backwards compatible. The `#handle_*` methods rely on being called inside `r.on`, and I don't know how to break out of `r.on` without the pass plugin. The only way I know how to avoid `r.on` would be to change `#handle_*` methods to route `r.is "#{prefix}#{send(route_meth)}"` instead, but that obviously wouldn't be compatible with existing apps that use `r.on`.

Having reviewed my options, I think it's easiest for rodauth-rails to load the pass plugin and recommend calling `r.pass` in the docs. It could change `r.rodauth`, but I don't want to diverge too much from Rodauth's API. I just wanted to first attempt to simplify this in Rodauth, so that non-Rails users also benefit from it, but I think calling `r.pass` is not bad at all. No need to guard people from Roda's routing API, as long as it's documented.

Kind regards,
Janko
Reply all
Reply to author
Forward
0 new messages