embla 0.2.3

  • README.md
  • CHANGELOG.md
  • Installing
  • Versions
  • 0

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.

Use this package as an executable

1. Install it

You can install the package from the command line:


$ pub global activate embla

2. Use it

The package has the following executables:


$ embla

Use this package as a library

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...

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

The package version is not analyzed, because it does not support Dart 2. Until this is resolved, the package will receive a health and maintenance score of 0.

Analysis issues and suggestions

Support Dart 2 in pubspec.yaml.

The SDK constraint in pubspec.yaml doesn't allow the Dart 2.0.0 release. For information about upgrading it to be Dart 2 compatible, please see https://www.dartlang.org/dart-2#migration.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=1.12.0 <2.0.0