• Stars
    star
    284
  • Rank 140,526 (Top 3 %)
  • Language
    JavaScript
  • Created almost 13 years ago
  • Updated almost 13 years ago

Reviews

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

Repository Details

A nice little library for writing and implementing web application behavior in javascript that looks like English.

Whenever.js

Whenever is a javascript library that lets you write your application logic in a format that reads like English. You define your definitions by using a simple expression chain, then hook it up in the background using jQuery, zepto, Prototype, mootools, dojo et al. jQuery support ships out of the box.

Whenever helps you to organise your javascripts in a clean and tidy way, and keeps your implementation logic separate from your behavioral logic.

NB: This is not a testing library. cucumber provided the inspiration for the syntax and separation of specification/implementation, but you should be able to use whenever.js to write actual apps.

Example

For example, take the following:

whenever('Click Me!').is('clicked').then('Change the text to "Clicked!"')

By itself, this does nothing, but it very clearly describes what will happen.

It's easy to hook up. First, 'Click Me!' should map to an element:

whenever.definitions.add({
  'Click Me!': 'a.click-me'
})

clicked is automatically mapped to the click event.

Finally, 'Change the text to "Clicked!"' should be hooked up to a function:

whenever.actions.add({
  'Change the text to "Clicked!"': function(){
    $(this).text("Clicked!")
  }
})

Or you can do this with a RegExp for re-use:

whenever.actions.add({
  'Change the text to "([^"]*)"': function(value){
    $(this).text(value)
  }
})

That's it!

Conditions

Goodbye to nested if statements! Add conditionals:

whenever('Click Me!')
  .is   ('clicked')
  .given('one and one make two')
  .then ('Change the text to "Clicked!"')

And implement:

whenever.conditions.add({
  'one and one make two': function(){
    return 1+1 === 2
  }
})

Usefully, the jQuery object is passed along:

whenever('Click Me!')
  .is   ('clicked')
  .given('the text of this is "Something"')
  .then ('Change the text to "Clicked!"')

And implement:

whenever.conditions.add({
  'the text of this is "Something"': function(){
    return $(this).text() === 'Something'
  }
})

or, again, you can use a RegExp:

whenever.conditions.add({
  'the text of this is "([^"]*)"': function(value){
    return $(this).text() === value
  }
})

Chaining

You can chain conditions and actions:

whenever('Click Me!')
  .is   ('clicked')
  .given('the text of this is "Something"')
    .and('Some other condition')
    .and('Another condition')
  .then ('Change the text to "Clicked!"')
    .and('Do something else')
    .and('Do another thing')

That's it!

Supported events

At the moment, the following actions are supported:

'blurred': 'blur',
'clicked':'click',
'focussed':'focus',
'hovered over':'mouseenter',
'hovered out of':'mouseout',
'loaded': 'load',
'ready': 'ready',
'submitted':'submit',
'changed': 'change'

Installation

Whenever.js needs a DOM library in order to bind actions to events. Out of the box, it comes with support for jQuery, but as long as you have an underlying library that supports binding events to elements, you can probably use it.

If your DOM library supports jQuery syntax (eg. Zepto), you can just replace jQuery with that globally. eg. with Zepto:

var jQuery = $;

Alternatively, you can implement your own whenever.bind_function_to_event and whenever.unbind_function_to_event function. Here's what they look like for jQuery:

whenever.unbind_function_to_event = function(selector, event, action){
  return jQuery(document).ready(function(){
    jQuery(document).undelegate(selector, event, action);
  })
}

whenever.bind_function_to_event = function(selector, event, action){
  return jQuery(document).ready(function(){
    if(event === 'ready' || event === 'load')
    {
      action.apply(selector)
    }
    else
    {
      return jQuery(document).delegate(selector, event, action);
    }
  });
}

or for Prototype:

whenever.unbind_function_to_event = function(selector, event, action){
  document.stopObserving(selector, event, action)
}

whenever.bind_function_to_event = function(selector, event, action){
  if(event === 'ready' || event === 'load')
  {
    document.observe('dom:loaded', function(){
      action.apply(document)
    })
  }
  else
  {
    document.on(event, selector, function(_event, element){
      action.apply(element)
    })
  }
}

After including your DOM library of choice, just add the whenever.js script to your project, eg. for jQuery:

<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="whenever.js"></script>

You might prefer to place your behavior, definitions and actions in separate files, or all in the one file.

A Note on Expressiveness

Whenever is an attempt to help writing expressive javascript. There are two motivations here:

  • Code that can be read quickly without trudging through logic
  • Code that clearly expresses the intent of what the programmer wants to achieve

Consider the example above, which is a bit misleading:

whenever('Click Me!').is('clicked').then('Change the text to "Clicked!"')

This is a trite example, which serves to explain the basic workings of Whenever, but it's not necessarily a great example, since it doesn't encapsulate very much, and doesn't explain much more than the equivalent jQuery would:

$('a#click-me').click(function(){ $(this).html('Clicked!') })

It does remove a lot of the cruft, but it might be argued that it doesn't add a lot of meaning vs. the straight jQuery.

Riffing on this a little:

whenever('Click Me!').is('clicked').then('show the user that they clicked')

This is (probably) better, but maybe a bit abstract. It does get away from the danger of writing code in English, which completely misses the point. Consider:

whenever('Click Me!').is('clicked').then('add the "display" class')

Basically, when it gets to this, it's almost identical to the jQuery, and tells us nothing about why we would want to add the display class, or what that means.

$('a#click-me').click(function(){ $(this).addClass('display') })

It inherently shows what the code does but not what the overall intent was.

The point is, whenever is an exercise in writing clean code and not necessarily writing code in English for its own sake. Like everything, it's a fine balance.

More Repositories

1

eyeballs.js

A lightweight MVC framework for building fast, tidy javascript web apps
JavaScript
336
star
2

configurable_engine

A Rails gem for storing app configuration data in your database, with a config file to fall back on.
Ruby
116
star
3

paths_of_glory

An achievement system generator for Rails
Ruby
79
star
4

stuffing

A Rails plugin to read and write CouchDB documents via Activerecord
Ruby
53
star
5

behavior

A library for storing Rails app configuration in the database
Ruby
20
star
6

twitter2campfire

Publish a Summize Twitter search feed to Campfire
Ruby
20
star
7

paulconf

19
star
8

realex

A Ruby class for interfacing with www.realexpayments.com
Ruby
19
star
9

shortcode-url

A really simple Rails plugin to generate a unique code on a field for TinyURL style URLS
Ruby
14
star
10

merge_translation

A plugin to merge YAML translation files in Rails 2.2
Ruby
12
star
11

url_field

A simple ActiveRecord plugin to correctly format a URL in the database whether the user enters "http://" or not
Ruby
12
star
12

shakespeare

A simple drop-in CMS for Rails
Ruby
10
star
13

formatted-dates

A simple acts_as style plugin for easily formatting of dates within Rails models
Ruby
10
star
14

address_engine

Ruby
9
star
15

phone_codes

International phone dialling codes for options_for_select
Ruby
8
star
16

town-crier

Sends a message to Twitter every hour.
Ruby
6
star
17

cukemin

A Rails Admin Controller / View Scaffold Generator that eschews specs in favour of cukes and is cut exactly to my liking.
Ruby
6
star
18

ruby-ireland-23-sept-2014

5
star
19

can_has

A simple ActiveRecord plugin to add a lovely little can_view, can_edit, can_delete permissions system in simple use cases
5
star
20

meview

A sample eyeballs.js localstorage web app
JavaScript
5
star
21

neofuturists_presenter

Ruby
4
star
22

big_decimal_price

BigDecimal.new('10.9').to_s #=> '10.90'
Ruby
4
star
23

kalipso

Ruby
4
star
24

couchy

A simple HTML template -> CouchDB document translator
Ruby
4
star
25

design-article-site

HTML/CSS to go along with an article I wrote for dotMobi
JavaScript
4
star
26

feelings

A tumble-log written in Rails using CouchDB as a data-store
Ruby
4
star
27

audit

A Rails plugin to save changes to an ActiveRecord model
Ruby
3
star
28

exceptional-php

PHP Client for geteceptional.com
PHP
3
star
29

ruby-ireland-schedule-app

Ruby
3
star
30

eyeballs_rails

Rails helpers for eyeballs.js
Ruby
2
star
31

couchpop

Upload a directory to CouchDB
Ruby
2
star
32

whenever.js-lightning-talk

a lightning talk about http://github.com/paulca/whenever.js
Ruby
2
star
33

paul-is-awesome

Ruby
2
star
34

paulca.github.com

2
star
35

blog-example

Ruby
1
star
36

ruby-ireland-schedule-api

Ruby
1
star
37

uninitialized_constant_example

Example of Rails 3 bug
Ruby
1
star
38

jaysus

Local / Remote persistence for JSON APIs / Local Store
Ruby
1
star
39

rails31rubyireland

Presentation to Ruby Ireland on August 30 for Rails 3.1 release party!
Ruby
1
star
40

db_context

Simple Rails plugin for switching context to a different database
Ruby
1
star
41

ubimport

Ruby
1
star
42

sanity-nuxt-events

Events with Nuxt.js
Vue
1
star
43

ucd-rails-intro

Ruby
1
star