• Stars
    star
    120
  • Rank 295,983 (Top 6 %)
  • Language
    CoffeeScript
  • License
    MIT License
  • Created over 12 years ago
  • Updated almost 11 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

method-combinators

tl;dr

This library gives you some handy function combinators you can use to make Method Decorators in CoffeeScript (click here for examples in JavaScript):

this.before =
  (decoration) ->
    (base) ->
      ->
        decoration.apply(this, arguments)
        base.apply(this, arguments)

this.after =
  (decoration) ->
    (base) ->
      ->
        decoration.call(this, __value__ = base.apply(this, arguments))
        __value__

this.around =
  (decoration) ->
    (base) ->
      (argv...) ->
        __value__ = undefined
        callback = =>
          __value__ = base.apply(this, argv)
        decoration.apply(this, [callback].concat(argv))
        __value__

this.provided =
  (condition) ->
    (base) ->
      ->
        if condition.apply(this, arguments)
          base.apply(this, arguments)

this.excepting =
  (condition) ->
    (base) ->
      ->
        unless condition.apply(this, arguments)
          base.apply(this, arguments)

The library is called "Method Combinators" because these functions are isomorphic to the combinators from Combinatorial Logic.

Back up the truck, Chuck. What's a Method Decorator?

A method decorator is a function that takes a function as its argument and returns a new function that is to be used as a method body. For example, this is a method decorator:

mustBeLoggedIn = (methodBody) ->
                   ->
                     if currentUser?.isValid()
                       methodBody.apply(this, arguments)

You use it like this:

class SomeControllerLikeThing

  showUserPreferences:
    mustBeLoggedIn ->
      #
      # ... show user preferences
      #

And now, whenever showUserPreferences is called, nothing happens unless currentUser?.isValid() is truthy. And you can reuse mustBeLoggedIn wherever you like. Since method decorators are based on function combinators, they compose very nicely, you can write:

triggersMenuRedraw = (methodBody) ->
                       ->
                         __rval__ = methodBody.apply(this, arguments)
                        @trigger('menu:redraww')
                        __rval__

class AnotherControllerLikeThing

  updateUserPreferences:
    mustBeLoggedIn \
    triggersMenuRedraw \
    ->
      #
      # ... save updated user preferences
      #

Fine. Method Decorators look cool. So what's a Method Combinator?

Method combinators are convenient function combinators for making method decorators. When writing decorators, the same few patterns tend to crop up regularly:

  1. You want to do something before the method's base logic is executed.
  2. You want to do something after the method's base logic is executed.
  3. You want to wrap some logic around the method's base logic.
  4. You only want to execute the method's base logic provided some condition is truthy.

Method combinators make these common kinds of method decorators extremely easy to write. Instead of:

mustBeLoggedIn = (methodBody) ->
                   ->
                     if currentUser?.isValid()
                       methodBody.apply(this, arguments)

triggersMenuRedraw = (methodBody) ->
                       ->
                         __rval__ = methodBody.apply(this, arguments)
                        @trigger('menu:redraww')
                        __rval__

We write:

mustBeLoggedIn = provided -> currentUser?.isValid()

triggersMenuRedraw = after -> @trigger('menu:redraww')

And they work exactly as we expect:

class AnotherControllerLikeThing

  updateUserPreferences:
    mustBeLoggedIn \
    triggersMenuRedraw \
    ->
      #
      # ... save updated user preferences
      #

The combinators do the rest!

Can I use this with Node's callback-oriented programming?

This library also provides method combinators that work in an asynchronous world.

So these are like RubyOnRails controller filters?

There are some differences. These are much simpler, which is in keeping with JavaScript's elegant style. For example, in Rails all of the filters can abort the filter chain by returning something falsy. The before and after decorators don't act as filters. Use provided if that's what you want.

More specifically:

  • None of the decorators you build with the method combinators change the arguments passed to the method. The before and around callbacks can execute code before the method body is executed, but only for side-effects.
  • The provided decorator will return void 0 if it evaluates to falsy or return whatever the method body returns. There's no other way to change the return value with provided
  • The around decorator will return void 0 if you don't call the passed callback. Otherwise, it returns whatever the method body would return. You can't change its arguments or the return value. You don't need to pass arguments to the callback. If you do, they will be ignored.

Is it any good?

Yes.

Can I install it with npm?

Yes: npm install method-combinators

Anything you left out?

Yes, there are some extra combinators that are useful for things like error handling and design-by-contract. Read the source for yourself. Or have a gander at these blog posts:

I'm writing a book called CoffeeScript Ristretto. Check it out!

Et cetera

Method Combinators was created by Reg "raganwald" Braithwaite. It is available under the terms of the MIT License. The retry and condition combinators were inspired by Michael Fairley's Ruby method_decorators.

More Repositories

1

javascript-allonge

Markdown source for the book "JavaScript Allongé"
JavaScript
758
star
2

presentations

Conference Talks and Proposals
751
star
3

allong.es

JavaScript
486
star
4

andand

The Maybe Monad in idiomatic Ruby
Ruby
302
star
5

JQuery-Combinators

The jQuery plugin with the academic name and the pragmatic methods
JavaScript
213
star
6

YouAreDaChef

Coffeescript/Javascript method combinations for Underscore projects
CoffeeScript
183
star
7

Katy

CoffeeScript and JavaScript Combinators
CoffeeScript
172
star
8

javascript-allonge-six

https://leanpub.com/javascriptallongesix
JavaScript
104
star
9

hashlife

JavaScript
58
star
10

oscin.es

n. pl. 1. (Zool.) Singing birds; a group of the Passeres, having numerous syringeal muscles, conferring musical ability
JavaScript
55
star
11

javascript-spessore

Manuscript for JavaScript Spessore
JavaScript
49
star
12

Underscore-Matchers-for-Jasmine

CoffeeScript
39
star
13

raganwald.github.com

raganwald.com jekyll source
HTML
31
star
14

cafeaulife

Gosper’s HashLife in CoffeeScript
CoffeeScript
30
star
15

wood_and_stones

Use your iPad as a Goban
JavaScript
29
star
16

combinators.info

JavaScript
23
star
17

jQuery-Predicates

.exists() and does_not_exist() for jQuery
JavaScript
16
star
18

ristrettolo.gy

jekyll source for http://ristrettolo.gy
JavaScript
10
star
19

FRACTRAN

Code to accompany “ Remembering John Conway's FRACTRAN, a ridiculous, yet surprisingly deep language”
JavaScript
10
star
20

supervis.es

CoffeeScript
9
star
21

string-lambdas

String lambdas for JavaScript and CoffeeScript
CoffeeScript
8
star
22

braythwayt.com

Reg Braithwaite's old weblog plus the odd non-technical blow hardiness
HTML
7
star
23

the-little-elixir-book

Fun with "The Little Elixir and OTP Guidebook"
Elixir
6
star
24

presentation_decks

Presentation decks from a few speaking engagements
6
star
25

raganwald.posterous.com

The content from my posterous blog, somewhat
5
star
26

mazerunner

Builds a maze in ES6 using Eller's Algorithm as described by Jamis Buck.
JavaScript
5
star
27

AMA

A lightweight “Ask me anything” repository inspired by @holman and @r00k
5
star
28

still-failing-still-learning-still-optimistic

3
star
29

stata

Read and write support for the Stata binary format
C
2
star
30

MacOSMendel

Java
1
star
31

shunting-yard

The collected shunting-yard code from raganwald.com
JavaScript
1
star
32

shunting-yard-dot-rb

A toy shunting yard implementation in Ruby
Ruby
1
star
33

kato

CoffeeScript-style OO for JavaScript
1
star