Skip to content

Use a class for the application routes#1116

Merged
waiting-for-dev merged 1 commit intomainfrom
waiting_for_dev/routes_as_class
Jul 6, 2021
Merged

Use a class for the application routes#1116
waiting-for-dev merged 1 commit intomainfrom
waiting_for_dev/routes_as_class

Conversation

@waiting-for-dev
Copy link
Copy Markdown
Member

@waiting-for-dev waiting-for-dev commented Jun 25, 2021

We move the definition of routes as a block provided to
Hanami::Application.routes to its own class
Hanami::Application::Routes.

Users need to create a subclass and define the routes through a block
applied to the .routes class method. As before, the hanami application
captures the context of the block and forwards it to
Hanami::Application::Router for its evaluation.

E.g.:

require "hanami/application/routes"

module MyApp
  class Routes < Hanami::Application::Routes
    define do
      slice :main, at: "/" do
        root to: "home.show"
      end
    end
  end
end

For consistency with settings, we move the routes path configuration to
the main Hanami::Configuration class.

Copy link
Copy Markdown
Member

@jodosha jodosha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@waiting-for-dev LGTM 👍

Copy link
Copy Markdown
Member

@solnic solnic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@waiting-for-dev could we have the DSL at the class level so that there's no need to use routes block within the class?

@waiting-for-dev
Copy link
Copy Markdown
Member Author

waiting-for-dev commented Jun 28, 2021

@waiting-for-dev could we have the DSL at the class level so that there's no need to use routes block within the class?

I agree that it'd look nicer. In that case, probably the simplest solution involves letting the user inherit from Hanami::Application::Router and moving those scope, slice, etc., methods to the class level. So the capture of the block of routes and the router wouldn't be in separated classes anymore. Are we ok with that?

@waiting-for-dev
Copy link
Copy Markdown
Member Author

@waiting-for-dev could we have the DSL at the class level so that there's no need to use routes block within the class?

@solnic, I've been looking into it, and it's not that easy. We are now just capturing the block and forwarding it to a Hanami::Application::Router instance, which executes it in its context. We need to provide a block to a method for that, as we can't just capture the class definition context.

As to using Hanami::Application::Router directly, it's not easy if we want users to interact with it at the class level (in contrast to an instance of it). Take the scope method, it ends up calling super, which belongs to hanami/router, so we can't just move those methods at the class level. A workaround could redefine those methods (scope, slice, etc.) at the class level to build an AST so that later they are executed in the same order at the instance level. We must be sure that it's worth the effort, though. Thoughts?

@solnic
Copy link
Copy Markdown
Member

solnic commented Jun 29, 2021

@waiting-for-dev isn't it possible to create a router instance at a class level and forward DSL methods to it?

@waiting-for-dev waiting-for-dev force-pushed the waiting_for_dev/routes_as_class branch from 9dc292a to dac4992 Compare June 29, 2021 11:41
@waiting-for-dev
Copy link
Copy Markdown
Member Author

@waiting-for-dev isn't it possible to create a router instance at a class level and forward DSL methods to it?

Yup, you're right. We can do it. Please, take a look at the last commit.

However, I feel a bit uneasy moving such a thing to the global state. WDYT?

If you think it's a better idea, I'll polish everything and add tests to it.

Copy link
Copy Markdown
Member

@jodosha jodosha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@waiting-for-dev The code has been changed since my last review. 🙂

I'm concerned that we're complicating the loading process to save us from the ugly routes block.


Remember that we're doing this change not because it's needed but because of syntax consistency with application settings.

It's fine to have consistency, but if that has to come at the cost of complexity, we should stop and reflect.

I agree that routes is ugly, but better having it, than complicating our lives. 😃

/cc @solnic

Comment thread lib/hanami/application/routes.rb Outdated
),
**Hanami.application.configuration.router.options,
) do
use Hanami.application[:rack_monitor]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@waiting-for-dev Please remove those middleware from here and add them to the Rack middleware stack of the application.

In depth explanation. We can have three nested layers of Rack middleware stacks. Here's them listed from the outermost to the innermost:

  • config.ru
  • Application
  • Router

The less stack we setup the better, for the app performance. When adding default middleware to the stack, we should target Application.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's food for another PR 🙂

Comment thread lib/hanami/application/routes.rb Outdated
# @since 2.0.0
class Routes
def self.router
@router ||= Router.new(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@waiting-for-dev ||= isn't thread-safe, is this code executed within a context that guarantees thread-safety?

This configuration heavily depends on global state of Hanami.application. How do we ensure that all these components are ready when we're instantiating the router? Shouldn't this be the role of the caller instead?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a concern in the new implementation

@jodosha jodosha added this to the v2.0.0 milestone Jun 30, 2021
@waiting-for-dev
Copy link
Copy Markdown
Member Author

@waiting-for-dev The code has been changed since my last review.

Yeah, @jodosha, the last commit is more like a POC so that we can see how it'd look if we take it that way. I agree with your concerns, so I think that something like a routes method (we could rename it) is the less worst option here.

@solnic
Copy link
Copy Markdown
Member

solnic commented Jul 1, 2021

Can we then rename it to define? The combination of Routes class name and then routes block method name is not 🧊 🙂

We move the definition of routes as a block provided to
`Hanami::Application.routes` to its own class
`Hanami::Application::Routes`.

Users need to create a subclass and define the routes through a block
applied to the `.define` class method. As before, the hanami application
captures the context of the block and forwards it to
`Hanami::Application::Router` for its evaluation.

E.g.:

```ruby

require "hanami/application/routes"

module MyApp
  class Routes < Hanami::Application::Routes
    define do
      slice :main, at: "/" do
        root to: "home.show"
      end
    end
  end
end
```

For consistency with settings, we move the routes path configuration to
the main `Hanami::Configuration` class.
@waiting-for-dev waiting-for-dev force-pushed the waiting_for_dev/routes_as_class branch from dac4992 to cbfc025 Compare July 1, 2021 17:06
@waiting-for-dev
Copy link
Copy Markdown
Member Author

Can we then rename it to define? The combination of Routes class name and then routes block method name is not

Hey @solnic, I agree with you. It's done now. I'm still leaving the .routes method for the reading operation, though, so we no call it .define 🙃

@jodosha, could you please review it again? @timriley I'd also like to have your eyes on it 🙂

Comment thread spec/new_integration/web_app_spec.rb
Copy link
Copy Markdown
Member

@jodosha jodosha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@waiting-for-dev Thank you very much! LGTM 👍

Copy link
Copy Markdown
Member

@timriley timriley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Can't wait to see what we can do with this object now that we have an actual class backing it.

@waiting-for-dev waiting-for-dev merged commit 4d476fa into main Jul 6, 2021
@waiting-for-dev waiting-for-dev deleted the waiting_for_dev/routes_as_class branch July 6, 2021 11:01
waiting-for-dev added a commit to hanami/hanami-2-application-template that referenced this pull request Jul 7, 2021
waiting-for-dev added a commit to hanami/hanami-2-application-template that referenced this pull request Jul 19, 2021
…f CSRFProtection module (#33)

* Adapt to routes being configured from a regular class

See hanami/hanami#1116

* Remove copy of upstream Hanami::Action::CSRFProtection module

We use dry-inflector as inflector for Zeitwerk.

As of dry-inflector 0.2.0, it didn’t support CSRF as an acronym. Zeitwerk was
looking for `CsrfProtection`, so we had to temporarily add a module with
this exact inflection in this repository.

As of dry-inflector 0.2.1, it added that CSRF acronym.

That means now Zeitwerk is able to look for `CSRFProtection` module from
hanami-controller. As per this change, we can remove `CsrfProtection` from
the app template.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants