Skip to content
This repository

A smarter client-side for ClojureScript

branch: master

Fetching latest commit…

Octocat-spinner-32-eaf2f5

Cannot retrieve the latest commit at this time

Octocat-spinner-32 .gitignore
Octocat-spinner-32 LICENSE.html
Octocat-spinner-32 README.mkd
Octocat-spinner-32 project.clj
README.mkd

shoreleave

A smarter client-side in ClojureScript

Shoreleave is a collection of integrated libraries that focuses on:

  • Security
  • Idiomatic interfaces
  • Common client-side strategies
  • HTML5 capabilities
  • ClojureScript's advantages

Installing and using

You can pull all the pieces of Shoreleave into your project by adding the following to your project.clj

(defproject ...

  :dependencies [[org.clojure/clojure "1.4.0"]
                 [shoreleave "0.3.0"]
                 [shoreleave/shoreleave-remote-ring "0.3.0"]
                 ...
                 ]
...
)

Individual utilies can be pulled in with [shoreleave/UTILITY "0.3.0"]. See the specific repo/artifact names below.

Overview

Shoreleave has support for the following:

Common client-side interactions

  • An idiomatic interface to cookies
  • An idiomatic interface to browser history (with extended support for HTML5 History API)
  • HTML Blob construction and on-demand asset building
  • An idiomatic interface to browser storage (Local Storage, Session Storage. App Cache coming soon)
  • Common auxiliary functions out-of-the-box to handle query strings, browser-repl, hash strings, and CLJS/JS interop
  • and more...

A remotes package

Shoreleave's remotes package includes XHR, Pooled-XHR, JSONP, and HTTP-RPC capabilities.

CSRF protection is built in if your Clojure server is using the ring-anti-forgery middleware. See shoreleave-baseline for anti-forgery details.

The HTTP-RPC allows for exposing a server-side namespace as a client-side API, via a single server-side call, remote-ns.

A typical Compojure setup might look like:

(ns baseline.handler
  (:require [compojure.handler :as handler]
            [baseline.routes :as routes]
            [baseline.config :refer [config]]
            [ring.middleware.gzip]
            [ring.middleware.file-info]
            [ring.middleware.anti-forgery]
            [ring.middleware.session.cookie :refer [cookie-store]]
            [shoreleave.middleware.rpc]
            [hiccup.middleware]))

(def app routes/all-routes)

(defn get-handler [app]
  (-> app
    (shoreleave.middleware.rpc/wrap-rpc)
    (ring.middleware.anti-forgery/wrap-anti-forgery)
    (ring.middleware.gzip/wrap-gzip)
    (handler/site {:session {:cookie-name "baseline"
                             :store (cookie-store {:key (config :session-secret)})
                             ;:store (cookie-store)
                             :cookie-attrs {:max-age (config :session-max-age-seconds)
                                            :http-only true}}})
    (ring.middleware.file-info/wrap-file-info)
    (hiccup.middleware/wrap-base-url)))

(def war-handler (get-handler app))

With a routes file that might look like this:

(ns baseline.routes
  (:require [compojure.core :as c-core :refer [defroutes
                                               GET POST PUT DELETE
                                               HEAD OPTIONS PATCH
                                               ANY]]
            [compojure.route :as c-route]
            [shoreleave.middleware.rpc :refer [remote-ns]]
            ;; Controllers
            [baseline.controllers.site :as cont-site]
            ;; Public APIs
            [baseline.controllers.api]))

;; Remote APIs exposed
;; -------------------
(remote-ns 'baseline.controllers.api :as "api")

;; Controller routes, ROA oriented
;; -------------------------------
(defroutes site
  (GET "/" {session :session} (cont-site/index session))
  (GET "/test" [] (cont-site/test-shoreleave)))

;; Core system routes
;; ------------------
(defroutes app-routes
  (c-route/resources "/")
  (c-route/not-found "404 Page not found."))

;; The top-level collection of all routes
;; --------------------------------------
(def all-routes (c-core/routes site
                               app-routes))

You can also define single "global" rpc functions:

(defremote ping []
  (do
    (println "Pinged by client!")
    "PONG - from the server"))

See the full example in the shoreleave-baseline project

A pub/sub abstraction (and implementations)

Why would I ever want to use this?

Shoreleave's pub/sub system enables you to completely decouple parts of your app and declaratively bind them together. New features and functionalities can be built by composing pre-existing services/publishables.

Additionally you can express cross-cutting functionality (like logging or metrics reporting) as a service.

Reactive ClojureScript

This gives you the heart of Reactive JavaScript (RxJS), without the additional verbs (both a benefit and a tradeoff). It's often most beneficial to use DOM listeners as entry-points into the pub/sub system.


Shoreleave's pub/sub system is built upon two protocols: "brokers" and "publishables"

Out of the box, Shoreleave allows you to publish funtions, atoms, web workers, and anything that implements (str ...)/.toString, as topics. The simple pub/sub bus has very little overhead, but operates synchronously. You can trade-off some performance for an async bus, the event pubsub bus.

(In-progress) The cross document bus has a small amount of overhead, but allows you to publish and subscribe from/to functions that live in other web workers or windows (in-browser concurrency for "free").

In most cases, the simple bus is the best choice.

Common external API support

Shoreleave has JSONP-wrapped support for external APIs including:

  • Google Maps
  • DuckDuckGo Zero-Click
  • (Coming soon) Wikipedia and Alpha

An enhanced ClojureScript experience

  • The ability to create embedded web workers

Where's the code?

There is a starter project that you can use as template, similar to how ClojureScript:One worked. It's also a great source to see how to build out Shoreleave+Compojure projects.

Shoreleave is a big project and has been split up into smaller pieces within the Shoreleave organization:

Deprecated

What's new in the latest version?

The latest version is 0.3.0

  • Example application and template project, shoreleave-baseline
  • Finalized protocols and support for browser web storage (localStorage, sessionStorage)
  • Ring middleware updated and moved to be the standard Shoreleave server-side component
  • Dropped Noir support
  • Updated remotes support for better error handling
  • Improved pub/sub handling of various flow combinations
  • subscribe-> added the pub/sub bus procotol - allows threading of flows
  • Replaced deprecated CLJS protocol usage
  • Code migrated from this project to CLJS proper
  • removed jQuery support from remotes
  • Updated all interfaces to their HTML5 finalized specs
  • bug fixes and performance improvements
  • Added more documentation

Plays well with others

Shoreleave makes no assumptions about other libraries you might be using in your app.

I have found it to pair particularly well with Enfocus

Examples and usage

Please the github doc pages (ie: Marginalia docs) above for library specifics.

There is a community-contributed demo app thanks to the hard work of Robert Stuttaford

Google Group and General Help

There is a Shoreleave CLJS Google Group. Please feel free to post all questions and general comments there.

Additionally, you can ping ohpauleez in #clojure on Freenode.

License

Copyright (C) 2012-2013 Paul deGrandis

Distributed under the Eclipse Public License, the same as Clojure.

Something went wrong with that request. Please try again.