From 6bf125d77cfb7f2991c068002c6a4ff5a0e4beb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles?= Date: Wed, 9 Oct 2024 09:23:13 -0300 Subject: [PATCH 1/5] test(route_node): add test cases for route merging functionality - Added `test_merge` to verify merging of identical routes. - Added `test_merge_variables_in_different_routes` to test merging with variable routes. --- test/lib/lennarb/test_route_node.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/lib/lennarb/test_route_node.rb b/test/lib/lennarb/test_route_node.rb index 76a4e06..e5a6073 100644 --- a/test/lib/lennarb/test_route_node.rb +++ b/test/lib/lennarb/test_route_node.rb @@ -58,5 +58,22 @@ def test_different_variables_in_common_nested_routes assert_equal({ id: '24' }, params) end + + def test_merge + router = Lennarb::RouteNode.new + router.add_route(['posts'], 'GET', proc { 'List of posts' }) + router.add_route(['posts', ':id'], 'GET', proc { |id| "Post #{id}" }) + + router.merge!(router) + end + + def test_merge_variables_in_different_routes + router = Lennarb::RouteNode.new + router.add_route(['posts'], 'GET', proc { 'List of posts' }) + router.add_route(['posts', ':id'], 'GET', proc { |id| "Post #{id}" }) + router.add_route(['posts', ':id', 'comments'], 'GET', proc { |id| "Comments of post #{id}" }) + + router.merge!(router) + end end end From c506db2b236de0113b468dfc73a7fe1e52a52c8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles?= Date: Wed, 9 Oct 2024 09:24:10 -0300 Subject: [PATCH 2/5] feat(route_node): add merge method --- lib/lennarb/route_node.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/lennarb/route_node.rb b/lib/lennarb/route_node.rb index 210953d..e0c4e4e 100644 --- a/lib/lennarb/route_node.rb +++ b/lib/lennarb/route_node.rb @@ -68,5 +68,17 @@ def match_route(parts, http_method, params: {}) [nil, nil] end + + # Merge the other RouteNode into the current one + # + # @parameter other [RouteNode] The other RouteNode to merge into the current one + # + # @return [void] + # + def merge!(other) + self.static_children.merge!(other.static_children) + self.dynamic_children.merge!(other.dynamic_children) + self.blocks.merge!(other.blocks) + end end end From b3a81ce3d7c28a1ff6c770fd6d9872188244fd00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles?= Date: Wed, 9 Oct 2024 09:25:56 -0300 Subject: [PATCH 3/5] feat(base_app): add tests for mount hooks and plugin execution - Added `test_mount_hooks_must_be_executed` to verify execution of before and after hooks. - Added `test_mount_routes_with_plugins_must_be_executed` to ensure plugin methods are executed correctly --- test/lib/lennarb/application/test_base.rb | 64 ++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/test/lib/lennarb/application/test_base.rb b/test/lib/lennarb/application/test_base.rb index 50cc4f2..9e58470 100644 --- a/test/lib/lennarb/application/test_base.rb +++ b/test/lib/lennarb/application/test_base.rb @@ -28,7 +28,39 @@ def self.setup(base_class, *_args) Lennarb::Plugin.register(:test_plugin, TestPlugin) + class UsersController < Lennarb::Application::Base + plugin :test_plugin + + before do |req, res| + req['x-users-controller-before-hook'] = 'Users Controller Before Hook' + end + + after do |req, res| + req['x-users-controller-after-hook'] = 'Users Controller After Hook' + end + + get '/users/test' do |_req, res| + res.status = 200 + res.html('Users Controller Response') + end + + get '/users/plugin' do |_req, res| + res.status = 200 + res.html(test_plugin_method) + end + end + + class PostsController < Lennarb::Application::Base + get '/posts/test' do |_req, res| + res.status = 200 + res.html('Posts Controller Response') + end + end + class MyApp < Lennarb::Application::Base + mount UsersController + mount PostsController + plugin :test_plugin test_plugin_class_method @@ -71,6 +103,20 @@ class MyApp < Lennarb::Application::Base def app = MyApp.run! + def test_users_controller + get '/users/test' + + assert_predicate last_response, :ok? + assert_equal 'Users Controller Response', last_response.body + end + + def test_posts_controller + get '/posts/test' + + assert_predicate last_response, :ok? + assert_equal 'Posts Controller Response', last_response.body + end + def test_get get '/' @@ -121,6 +167,22 @@ def test_after_hooks assert_equal 'After Hook', last_request.env['x-after-hook'] end + def test_mount_hooks_must_be_executed + get '/users/test' + + assert_equal 'Before Hook', last_request.env['x-before-hook'] + assert_equal 'After Hook', last_request.env['x-after-hook'] + assert_equal 'Users Controller Before Hook', last_request.env['x-users-controller-before-hook'] + assert_equal 'Users Controller After Hook', last_request.env['x-users-controller-after-hook'] + end + + def test_mount_routes_with_plugins_must_be_executed + get '/users/plugin' + + assert_predicate last_response, :ok? + assert_equal 'Plugin Method Executed', last_response.body + end + def test_enviroment ENV['LENNARB_ENV'] = 'test' @@ -160,7 +222,7 @@ def call(env) = @app.call(env) def test_middlewares MyApp.use(MockedMiddleware) - assert_includes MyApp.middlewares, [Lennarb::Application::TestBase::MockedMiddleware, [], nil] + assert_includes MyApp._middlewares, [Lennarb::Application::TestBase::MockedMiddleware, [], nil] end end end From db94e272f0dd889a7d5e2d6ba781cb811e0c923d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles?= Date: Wed, 9 Oct 2024 09:27:11 -0300 Subject: [PATCH 4/5] feat(bas_app): Implement controller mounting and hook merging logic - Added `_applications` method to store mounted controllers. - Implemented `mount` method to add controllers to the applications list and log the mounting action. - Enhanced merging logic to include routes and hooks from mounted controllers --- lib/lennarb.rb | 12 ++++++++++ lib/lennarb/application/base.rb | 40 ++++++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/lib/lennarb.rb b/lib/lennarb.rb index 2e7e2b1..3641848 100644 --- a/lib/lennarb.rb +++ b/lib/lennarb.rb @@ -115,6 +115,18 @@ def plugin(plugin_name, *, &) @_applied_plugins << plugin_name end + # Merge the other RouteNode into the current one + # + # @parameter other [RouteNode] The other RouteNode to merge into the current one + # + # @return [void] + # + def merge!(other) + raise "Expected a Lennarb instance, got #{other.class}" unless other.is_a?(Lennarb) + + @_root.merge!(other._root) + end + private # Add a route diff --git a/lib/lennarb/application/base.rb b/lib/lennarb/application/base.rb index 303d4d5..d3a3506 100644 --- a/lib/lennarb/application/base.rb +++ b/lib/lennarb/application/base.rb @@ -32,9 +32,10 @@ class Base # # @returns [Base] # - class << self def inherited(subclass) + super + _applications << subclass subclass.instance_variable_set(:@_route, Lennarb.new) subclass.instance_variable_set(:@_middlewares, []) subclass.instance_variable_set(:@_global_after_hooks, []) @@ -51,9 +52,23 @@ def match(...) = @_route.match(...) def patch(...) = @_route.patch(...) def delete(...) = @_route.delete(...) def options(...) = @_route.options(...) - + # @returns [Array] middlewares - def middlewares = @_middlewares + # + def _middlewares = @_middlewares ||= [] + + # @returns [Array] applications + # + def _applications = @_applications ||= [] + + # Mount a controller + # + # @parameter [Class] controller + # + def mount(controller_class) + _applications << controller_class + puts "Mounted controller: #{controller_class}" + end # Use a middleware # @@ -117,10 +132,26 @@ def run! use Rack::Head use Rack::ContentLength - middlewares.each do |(middleware, args, block)| + _middlewares.each do |(middleware, args, block)| stack.use(middleware, *args, &block) end + _applications.each do |app| + app_route = app.instance_variable_get(:@_route) + + app_after_hooks = app.instance_variable_get(:@_after_hooks) + app_before_hooks = app.instance_variable_get(:@_before_hooks) + global_after_hooks = app.instance_variable_get(:@_global_after_hooks) + global_before_hooks = app.instance_variable_get(:@_global_before_hooks) + + @_route.merge!(app_route) + @_before_hooks.merge!(app_before_hooks) + @_after_hooks.merge!(app_after_hooks) + + @_global_before_hooks.concat(global_before_hooks) + @_global_after_hooks.concat(global_after_hooks) + end + stack.run ->(env) do catch(:halt) do execute_hooks(@_before_hooks, env, :before) @@ -137,7 +168,6 @@ def run! end @_route.freeze! - stack.to_app end From 5c317ad89034ebf3defed11e7bce56614999ccff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles?= Date: Wed, 9 Oct 2024 09:43:05 -0300 Subject: [PATCH 5/5] docs: update getting-stated --- guides/getting-started/readme.md | 51 +++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/guides/getting-started/readme.md b/guides/getting-started/readme.md index 4f3b876..43de44e 100644 --- a/guides/getting-started/readme.md +++ b/guides/getting-started/readme.md @@ -44,7 +44,7 @@ end run app ``` -## Use `Lennarb::Application::Base` class +## How to use `Lennarb::Application::Base` class You can also use the `Lennarb::Application::Base` class to define your app: @@ -116,6 +116,55 @@ After that, you can run your app with the `rackup` command: $ rackup ``` +## Mount route controllers + +You can use the `mount` method to mount route controllers: + +Create a route controller, in this example, we'll create a `UsersController`, and define your routes: + +```ruby +# ../whatwever/users.rb + +require 'lennarb' + +class UsersController < Lennarb::Application::Base + get '/users' do |req, res| + res.html 'List of users' + end +end +``` + +Now, you can use the `mount` method to mount the `UsersController` on your app: + +```ruby +# config.ru + +require 'lennarb' +require_relative './whatwever/users' + +class Application < Lennarb::Application::Base + mount UsersController +end +``` + +Completed! You can now execute your application using distinct controllers. + +🚨 **IMPORTANT:** The `mount` method does not modify the hooks of the mounted controller. This means that the hooks of the mounted controller will not be executed in the context of the main application. + +```ruby +# ../whatwever/users.rb + +require 'lennarb' + +class UsersController < Lennarb::Application::Base + before do |req, res| + puts 'UsersController before' # This will be executed in global context + end +end +``` + +We recommend you to use the `before` and `after` methods to define callbacks in the main application or specific routes. Ex. `before('/users')` will be executed only in the `UsersController` routes. + ## Hooks You can use the `before` and `after` methods to define callbacks: