npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

foraker

v0.0.4

Published

Simple, drop-in controllers that support filters, promises, subclassing, and more.

Downloads

8

Readme

foraker NPM version Build Status Dependency Status Coverage percentage

Foraker provides a simple, drop-in controller that support filters, promises, subclassing, and more. It's easy to integrate into existing Express / middleware based apps, follows the "do one thing well" principle, and is thoroughly tested.

Note: this is alpha software, and still undergoing changes. Contributions welcome, consumers beware :wink:

Install

$ npm install --save foraker

Basic Usage

// controllers/posts.js
import Controller from 'foraker';

export default Controller.extend({
  create(req, res) {
    // You can return promises from an action handler. Rejected promises will
    // call next(rejectionValue).
    return createNewPostRecord(req.body)
      .then((newPost) => {
        res.json(newPost);
      });
  }
});

Express / Connect Integration

Controllers are stand alone code - you need to wire them up to your Express (or otherwise Connect-compatible) routing layer to let them actually handle requests:

// app.js
import PostController from './controllers/posts';

// Controller singleton
let posts = new PostController();

app.post('/posts', posts.action('create'));

Filters

Filters operate much like Rails filters: they allow you to run code before or after an action handler runs.

Filters can be applied to specific actions, are inherited from superclasses, and can be skipped. They receive the req and res just like a regular action handler, and if they throw an error or send a response, the request handling stops there (i.e. later filters or actions are not run).

Here's a basic example of a controller with a single action (update), and a single before filter that applies to all actions on the controller which will authenticate the user:

// controllers/posts.js
import Controller from 'foraker';

export default Controller.extend({

  filters() {
    // Run the authenticate function (that is defined on this controller) before
    // any action for this controller runs.
    this.before('authenticate');
  },

  update(req, res) {
    /* update the post record ... */
  },

  authenticate(req, res) {
    if (req.headers['Authorization'] !== 'Secret Password') {
      // Errors throw will be caught, and passed into next()
      throw new Error('Unauthorized!');
    }
  }

});

In this more complex example, the filters are applied selectively using the only and except options:

  filters() {
    // The `only` option acts like a whitelist, so the notifyAuthor filter is
    // only run for the update action. `except` acts like a blacklist,
    // preventing the filter from running on specific actions.
    this.after('notifyAuthor', { only: 'update' });
  }

You can also pass the filter method directly in, rather than referencing it by name. This is useful if the filter method isn't a method defined on the controller class itself:

import Controller from 'foraker';
import authenticate from '../filters/authenticate';

export default Controller.extend({
  filters() {
    // If the filter is a string, it's assumed to be a method defined on the
    // controller itself. Alternatively, you can pass in a function directly:
    this.before(authenticate);
  }

Subclassing

Foraker Controllers use core-object to provide inheritance functionality. You can extend the base Controller class via Controller.extend, which will then be available on your subclass as well:

import Controller from 'foraker';

let BaseController = Controller.extend({ /* ... */ });
let SubclassedController = BaseController.extend({ /* ... */ });

core-object also provides convenient super functionality:

import Controller from 'foraker';

let NameController = Controller.extend({
  getName() {
    return 'Dave';
  }
});

let PoliteNameController = NameController.extend({
  getName() {
    return 'Mr. ' + this._super();
  }
});

Controllers with filters are intelligent about how they handle subclasses. Child classes will run the parent class filters as well:

import Controller from 'foraker';

let ApplicationController = Controller.extend({
  filters() {
    this.before('authenticate');
  },
  authenticate(req, res) {
    if (!req.user) {
      throw new Error('Unauthorized!');
    }
  }
});

let BooksController = ApplicationController.extend({
  create() {
    // ApplicationController.authenticate will run before this action does
  }
});

Parent class before filters will run prior to the child class before filters, and parent class after filters will run following the child class after filters.

License

MIT © Dave Wasmer