embla 0.2.3

  • README.md
  • CHANGELOG.md
  • Installing
  • Versions
  • 54

Embla

Embla is a powerful but simple server side application framework for Dart.

Usage

Install like so:

# pubspec.yaml
dependencies:
  embla: any
> pub get

Embla scripts can be run directly with dart my_script.dart, but for development we can use the Embla CLI:

> pub global activate embla
# Add pub's binaries to PATH, to be able to omit "pub run" (Also: https://github.com/dart-lang/pub/issues/1204)
> PATH=$PATH:~/.pub-cache/bin
> embla start

Currently, embla start will look for a bin/server.dart file and start the app. If you make changes to your project files, the app will automatically restart.

Overview

Here's an example of a super simple Embla app.

export 'package:embla/bootstrap.dart';
import 'package:embla/http.dart';

get embla => [
  new HttpBootstrapper(
    pipeline: pipe(() => 'Hello world!')
  )
];

This application starts a server, and responds with "Hello world!" on every request. Looks weird? Let's figure out what's going on.

Bootstrapping

Instead of the good old main function, Embla requires a getter called embla in the main entry point script. The actual main function will be provided by bootstrap.dart.

export 'package:embla/bootstrap.dart';

get embla => [];

If we were to run the above script, we would get an empty Dart process that did nothing, and would close on Ctrl+C.

To hook into the application, we can add Bootstrappers to the embla function. HttpBootstrapper comes out of the box if we just import 'package:embla/http.dart'. Each bootstrapper should be instantiated in the embla function, and any configuration needed is passed through the constructor.

HTTP Pipeline

It just so happens the HttpBootstrapper takes a named pipeline parameter, that represents the request/response pipeline for the server.

To create a pipeline, we use the pipe helper provided by embla/http.dart. A pipeline consists of a series of Middleware. Embla wraps Shelf for this.

import 'dart:async';

export 'package:embla/bootstrap.dart';
import 'package:embla/http.dart';

get embla => [
  new HttpBootstrapper(
    pipeline: pipe(
      MyMiddleware
    )
  )
];

class MyMiddleware extends Middleware {
  Future<Response> handle(Request request) {
    // Pass along to the next middleware
    return super.handle(request);
  }
}

The pipe allows for different formats for Middleware. You can pass in a Shelf Middleware directly, or the Type of a middleware class. It also supports passing in a Function, which will be converted to a route handler.

Routing

Routes are nothing more than conditional paths in the pipeline. Here's an example:

pipeline: pipe(

  MiddlewareForAllRoutes,

  Route.get('/', () => 'Hello world'),

  Route.all('subroutes/*',
    MiddlewareForAllRoutesInSubroutes,

    Route.get('', () => 'Will be reached by GET /subroutes'),

    Route.put('action', () => 'Will be reached by PUT /subroutes/action'),

    Route.get('another',
      SpecialMiddlewareForThisRoute,
      () => 'Will be reached by PUT /subroutes/another'
    ),

    Route.get('deeper/:wildcard',
      ({String wildcard}) => 'GET /subroutes/deeper/$wildcard'
    )
  ),

  () => 'This will be reached by request not matching the routes above'
)

Controller

In Embla, controllers are also middleware. They are collections of routes, after all. The controllers use annotations to declare routes.

export 'package:embla/bootstrap.dart';
import 'package:embla/http.dart';
import 'package:embla/http_annotations.dart';

get embla => [new HttpBootstrapper(pipeline: pipe(MyController))];

class MyController extends Controller {
  /// GET /action  ->  'Response'
  @Get() action() {
    return 'Response';
  }

  /// POST /endpoint  ->  302 /
  @Post('endpoint') methodName() {
    return redirect('/action');
  }
}

Since controllers are middleware too, we can easily route our controllers to endpoints like this:

Route.all('pages/*', PagesController)

Custom Bootstrappers

Bootstrappers hook into the initialization and deinitialization of the application. Creating one is super simple.

export 'package:embla/bootstrap.dart';
import 'package:embla/application.dart';

get embla => [new MyBootstrapper()];

class MyBootstrapper extends Bootstrapper {
  @Hook.init
  init() {
    print('Initializing the application!');
  }
}

Changelog

0.3.0

  • Added an --isolates flag, that allows the application to be multiplexed over multiple isolates. It defaults to 1.
> embla start --isolates 3
> embla start -i 3
> dart bin/server.dart -i 3

0.2.2

  • Fixed a bug where routing stars didn't require a slash to follow:
Route.get('path/*')
// no longer matches "/path_and_more"
// only "/path/and_more"
  • Fixed a bug where the UrlEncodedInputParser didn't actually decode the URL component:
key=value -> { 'key': 'value' }

// before 0.2.2
key=value%20with%20spaces -> { 'key': 'value%20with%20spaces' }
// after 0.2.2
key=value%20with%20spaces -> { 'key': 'value with spaces' }

0.2.1

Adds a new ForwarderMiddleware that acts as a proxy to another server.

Also adds a PubMiddleware which combines the StaticFilesMiddleware and the new ForwarderMiddleware to forward to Pub serve in dev mode, and to the build directory in production mode.

pipe(
  // Middleware preceding the PubMiddleware will now be available on the same server
  // that deals with transformers and stuff!
  Route.get('special-endpoint', () => 'Hello from server!'),
  PubMiddleware  
)
> pub serve
# In another tab
> APP_ENV=development embla start

Pre 0.2

An empty Embla app is an empty getter called embla in the script, with an export statement.

export 'package:embla/bootstrap.dart';
get embla => [];

The getter should return a List<Bootstrapper>.

import 'package:embla/application.dart';
export 'package:embla/bootstrap.dart';

List<Bootstrapper> get embla => [];

Bootstrappers attach listeners to application lifetime hooks.

import 'package:embla/application.dart';
export 'package:embla/bootstrap.dart';

get embla => [
  new MyBootstrapper()
];

class MyBootstrapper extends Bootstrapper {
  @Hook.init
  init() {
    print('Starting the application up!');
  }

  @Hook.exit
  exit() {
    print('Shutting the application down!');
  }
}

Methods in a bootstrapper can use Dependency Injection to inject classes. Since Embla uses a stateless IoC container, adding bindings to the container returns a new instance. To push the new bindings into the application, the bootstrappers can return the new container in any of its methods.

The container itself is available from the Bootstrapper superclass.

class AddsBindingsBootstrapper extends Bootstrapper {
  @Hook.bindings
  bindings() {
    return container.bind(SomeAbstractClass, to: SomeConcreteClass);
  }
}

The hooks, as well as the container, is documented in doc comments.

HTTP Pipeline

The basic Embla library comes with an HttpBootstrapper, which takes some configuration as named parameters. One of which is the pipeline parameter, expecting a Pipeline.

The pipe helper creates a Pipeline from one or more Middleware:

import 'package:embla/http.dart';
export 'package:embla/bootstrap.dart';

get embla => [
  new HttpBootstrapper(
    pipeline: pipe(
      SomeMiddleware
    )
  )
];

There are some middleware that comes out-of-the-box, for routing as well as for some common tasks like removing trailing slashes from URLs, parsing the request body, or handling errors thrown in the pipeline.

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  embla: "^0.2.3"

2. Install it

You can install packages from the command line:

with pub:


$ pub get

Alternatively, your editor might support pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:embla/application.dart';

import 'package:embla/bootstrap.dart';

import 'package:embla/container.dart';

import 'package:embla/http.dart';

import 'package:embla/http_annotations.dart';

import 'package:embla/http_basic_middleware.dart';
        
Version Uploaded Documentation Archive
0.2.3 Sep 16, 2016 Go to the documentation of embla 0.2.3 Download embla 0.2.3 archive
0.2.2 Mar 10, 2016 Go to the documentation of embla 0.2.2 Download embla 0.2.2 archive
0.2.1 Mar 10, 2016 Go to the documentation of embla 0.2.1 Download embla 0.2.1 archive
0.2.0 Mar 8, 2016 Go to the documentation of embla 0.2.0 Download embla 0.2.0 archive
0.1.13 Mar 4, 2016 Go to the documentation of embla 0.1.13 Download embla 0.1.13 archive
0.1.12 Mar 4, 2016 Go to the documentation of embla 0.1.12 Download embla 0.1.12 archive
0.1.11 Feb 25, 2016 Go to the documentation of embla 0.1.11 Download embla 0.1.11 archive
0.1.10 Feb 24, 2016 Go to the documentation of embla 0.1.10 Download embla 0.1.10 archive
0.1.9 Feb 24, 2016 Go to the documentation of embla 0.1.9 Download embla 0.1.9 archive
0.1.8 Feb 24, 2016 Go to the documentation of embla 0.1.8 Download embla 0.1.8 archive

All 17 versions...

Analysis

We analyzed this package on Apr 23, 2018, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.0.0-dev.49.0
  • pana: 0.10.6

Scores

Popularity:
Describes how popular the package is relative to other packages. [more]
38 / 100
Health:
Code health derived from static analysis. [more]
93 / 100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
38 / 100
Overall score:
Weighted score of the above. [more]
54
Learn more about scoring.

Platforms

Detected platforms: other

Platform components identified in package: io, isolate, mirrors.

Suggestions

  • Fix analysis and formatting issues.

    Analysis or formatting checks reported 3 errors 83 hints.

    Strong-mode analysis of lib/src/container.dart failed with the following error:

    line: 103 col: 12
    The return type 'CurriedFunction' isn't a 'Function', as defined by the method 'curry'.

    Strong-mode analysis of lib/src/http/pipeline.dart failed with the following error:

    line: 51 col: 13
    The type 'Iterable<Middleware>' implied by the 'yield' expression must be assignable to 'Iterable<((Request) → dynamic) → (Request) → dynamic>'.

    Similar analysis of the following files failed:

    • lib/src/http/routing.dart (error)
    • lib/application.dart (hint)
    • lib/bootstrap.dart (hint)
    • lib/container.dart (hint)
    • lib/http.dart (hint)
    • lib/http_annotations.dart (hint)
    • lib/http_basic_middleware.dart (hint)
    • lib/src/http/context.dart (hint)
    • lib/src/http/error_template.dart (hint)
    • lib/src/http/http_exceptions.dart (hint)
    • lib/src/http/middleware/conditional_middleware.dart (hint)
    • lib/src/http/middleware/error_handler_middleware.dart (hint)
    • lib/src/http/middleware/forwarder_middleware.dart (hint)
    • lib/src/http/middleware/handler_middleware.dart (hint)
    • lib/src/http/middleware/input_parser_middleware.dart (hint)
    • lib/src/http/middleware/logger_middleware.dart (hint)
    • lib/src/http/middleware/pub_middleware.dart (hint)
    • lib/src/http/middleware/remove_trailing_slash_middleware.dart (hint)
    • lib/src/http/middleware/static_files_middleware.dart (hint)
    • lib/src/http/response_maker.dart (hint)
    • lib/src/http/route_expander.dart (hint)
    • lib/src/util/concat.dart (hint)
    • lib/src/util/trace_formatting.dart (hint)
  • Package is pre-v1 release.

    While there is nothing inherently wrong with versions of 0.*.*, it usually means that the author is still experimenting with the general direction API.

  • Maintain an example.

    Create a short demo in the example/ directory to show how to use this package. Common file name patterns include: main.dart, example.dart or you could also use embla.dart.

  • Use analysis_options.yaml.

    Rename old .analysis_options file to analysis_options.yaml.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=1.12.0 <2.0.0
shelf ^0.6.4 0.6.8 0.7.2
shelf_static ^0.2.3 0.2.7
stack_trace ^1.6.0 1.9.2
watcher ^0.9.7 0.9.7+7
Transitive dependencies
async 1.13.3 2.0.6
charcode 1.1.1
collection 1.14.9
convert 2.0.1
http_parser 3.1.1
mime 0.9.6
path 1.5.1
source_span 1.4.0
stream_channel 1.6.5
string_scanner 1.0.2
typed_data 1.1.5
Dev dependencies
quark any
test any