Skip to content

Commit

Permalink
Merge pull request #65 from aristotelesbr/development
Browse files Browse the repository at this point in the history
Main < Development
  • Loading branch information
aristotelesbr authored Oct 9, 2024
2 parents a4fd7c6 + 5c317ad commit ad72326
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 7 deletions.
51 changes: 50 additions & 1 deletion guides/getting-started/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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:
Expand Down
12 changes: 12 additions & 0 deletions lib/lennarb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
40 changes: 35 additions & 5 deletions lib/lennarb/application/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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, [])
Expand All @@ -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
#
Expand Down Expand Up @@ -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)
Expand All @@ -137,7 +168,6 @@ def run!
end

@_route.freeze!

stack.to_app
end

Expand Down
12 changes: 12 additions & 0 deletions lib/lennarb/route_node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
64 changes: 63 additions & 1 deletion test/lib/lennarb/application/test_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 '/'

Expand Down Expand Up @@ -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'

Expand Down Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions test/lib/lennarb/test_route_node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit ad72326

Please sign in to comment.