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

schmackbone

v3.0.0

Published

jQuery-less, Promise-interfaced models based on BackboneJS

Downloads

2,826

Readme

                       __      __
                      /\ \    /\ \                                   __
           __      ___\ \ \/'\\ \ \____    ___     ___      __      /\_\    ____
         /'__`\   /'___\ \ , < \ \ '__`\  / __`\ /' _ `\  /'__`\    \/\ \  /',__\
   SCHM-/\ \ \.\_/\ \__/\ \ \\`\\ \ \ \ \/\ \ \ \/\ \/\ \/\  __/  __ \ \ \/\__, `\
        \ \__/.\_\ \____\\ \_\ \_\ \_,__/\ \____/\ \_\ \_\ \____\/\_\_\ \ \/\____/
         \/__/\/_/\/____/ \/_/\/_/\/___/  \/___/  \/_/\/_/\/____/\/_/\ \_\ \/___/
                                                                    \ \____/
                                                                     \/___/
 (_'______________________________________________________________________________'_)
 (_.——————————————————————————————————————————————————————————————————————————————._)

Schmackbone.js

Schmackbone is a lighter, modernized fork of the established MV-library Backbone, with the View-logic, jQuery, and Router removed along with its support for legacy browsers. So it's really just an M-library to handle your RESTful API requests.

Its ajax requests are now native and promise-based, and its components are modularized so you can use what you need and tree-shake the rest.

Why?

While creating a Backbone view layer feels a little 2012, its Models and Collections are actually a very light and easy abstraction for interacting with REST APIs. Additionally, its basic Events module make it a cinch to pub/sub your model changes to your actual view layer, for example, your React Components. This allows for some really elegant abstractions without the heaviness of a full-blown state manager like Redux. You can keep your UI-state local and your data-state (the 'state' of the data that is represented in your API).

This is how the resourcerer library employs Schmackbone.

Practical Differences to Backbone

Underscore methods are now opt-in

Backbone automatically adds the bulk of the underscore library methods to its Model/Collection prototypes. But nearly all of them are unnecessary these days. Schmackbone leaves these out by default. However, if you want to add these back, just import the add_underscore_methods script to your application:

// your-app.js
import 'schmackbone/add_underscore_methods';

(Note that in order to access the add_underscore_methods script, you'll need a bundler (or other module loading system) that recognizes the exports property in package.json. For webpack, this means upgrading to v5.)

Modularized

// before, with webpack shimming via https://github.com/webpack-contrib/imports-loader:
import Backbone from 'backbone';

// after:
import * as Schmackbone from 'schmackbone';
// or
import {Model} from 'schmackbone';

Model and Collection are native Javascript classes

// before:
var MyModel = Model.extend({url: () => '/foo'});

// after:
class MyModel extends Model {
  url = () => '/foo'
}

Reserved instance properties now static properties

Due to the way instance properties are instantiated in subclasses (not until after the superclass has been instantiated), many of the reserved instance properties in Schmackbone have been moved to static properties:

// before:
var MyModel = Model.extend({
  defaults: {
    one: 1,
    two: 2
  },
  
  cidPrefix: 'm',
  
  idAttribute: '_id',
  
  url: () => '/foo'
});


// after:
class MyModel extends Model {
  static defaults = {
    one: 1,
    two: 2
  }
  
  static cidPrefix = 'm'
  
  static idAttribute = '_id'
  
  url() => '/foo'
});

Notes:

  • For Models, defaults, idAttribute, and cidPrefix are static properties. defaults can optionally be a static function.
  • For Collections, model and comparator properties are now static, but if they need to be overridden in an instance, they can do so via the options object in instantiation:
class MyCollection extends Collection {
  static comparator = 'created_at'
  static model = MyModel
}

const overrideCollection = new MyCollection([], {comparator: 'updated_at', model: MyModel2});
  • url (Model/Collection) and urlRoot (Model) remain instance properties, as they (1) often depend on the instance and (2) are not utilized during instantiation

Requests have a Promise interface

All Schmackbone request methods use window.fetch under the hood and so now have a Promise interface, instead of accepting jQuery-style success and error options.

// before:
todoModel.save({name: 'Giving This Todo A New Name'}, {
  success: (model, response, options) => notify('Todo save succeeded!'),
  error: (model, response, options) => notify('Todo save failed :/'),
  complete: () => saveAttempts = saveAttempts + 1
});

// after
todoModel.save({name: 'Giving This Todo A New Name'})
    .then(([model, response, options]) => notify('Todo save succeeded!'))
    .catch(([model, response, options]) => notify('Todo save failed :/'))
    .then(() => saveAttempts = saveAttempts + 1);

Note a couple important consequences:

  1. Since Promises can only resolve a single value, the callback parameters are passed via an array that can be destructured.
  2. All requests must have a .catch attached, even if the rejection is swallowed. Omitting one risks an uncaught Promise rejection exception if the request fails.
  3. The .create method no longer returns the added model; it returns the promise instead.

qs Dependency

While jQuery was no longer necessary, we could not replace it entirely with native javascript: we added query string stringifying functionality via the small-footprint qs library.

setAjaxPrefilter

Schmackbone offers one hook into its fetch requests: setAjaxPrefilter. It allows you to alter the options object passed to window.fetch before any requests are made. Use this hook to pass custom headers like auth headers, or a custom global error handler:

import {setAjaxPrefilter} from 'schmackbone';

// usage:
// @param {object} options object
// @return {object} modified options object
const ajaxPrefilter = (options={}) => ({
  ...options,
  // if you want to default all api requests to json
  contentType: 'application/json',
  error: (response) => {
    if (response.status === 401) {
      // refresh auth token logic
    } else if (response.status === 429) {
      // do some rate-limiting retry logic
    }

    return options.error(response);
  },
  headers: {
    ...options.headers,
    Authorization: `Bearer ${localStorage.getItem('super-secret-auth-token')}`
  }
});

setAjaxPrefilter(ajaxPrefilter);

By default, the ajaxPrefilter function is set to the identity function.

Misc

  • Note that Schmackbone uses ES2015 in its source and does no transpiling—including import/export (Local babel configuration is for testing, only). Unlike Backbone, whose legacy monolith used UMD syntax and could be used directly in the browser, Schmackbone can be used only in modern browsers via the type="module" script MIME-type or via a bundler like webpack that handles module transforms.

This means that if you're not babelifying your node_modules folder, you'll need to make an exception for this package, ie:

// webpack.config.js
module: {
  rules: [{
    test: /\.jsx?$/,
    exclude: /node_modules\/(?!(schmackbone))/,
    use: {loader: 'babel-loader?cacheDirectory=true'}
  }]
}