All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pallet.extensions.clj Maven / Gradle / Ivy

There is a newer version: 0.6.0
Show newest version
(ns pallet.extensions
  (:use [clojure.contrib.def :only (name-with-attributes)])
  (:require pallet.resource.filesystem-layout
            [clojure.contrib.condition :as condition]
            [clojure.contrib.macro-utils :as macro]))

;; ### Pallet Extensions
;;
;; We start with a few extensions to pallet's crate writing
;; facilities. These may or may not make it into pallet proper; we use
;; them here for demonstration, and as an example of the flexibility
;;that a `phase-fn` with arguments might afford.


;; Here's the macro we've been waiting for. Pallet makes heavy use of
;; threading to build up its requests; each phase accepts an argument
;; vector, binds locals, and either directly modify the request, or
;; threads it through more primitive subphases. `-->` layers various
;; flow control constructs onto `->`, allowing for more natural
;; expressions within the body of the thread. For example:
;;
;;    (--> 10
;;         (for [x (range 10)]
;;            (+ x)))
;;    ;=> 55

(defmacro -->
  "Similar to `clojure.core/->`, but includes symbol macros  for `when`,
  `let` and `for` commands on the internal threading
  expressions. Future iterations will include more symbol macro
  bindings."
  [& forms]
  `(macro/symbol-macrolet
    [~'when pallet.thread-expr/when->
     ~'for pallet.thread-expr/for->
     ~'let pallet.thread-expr/let->
     ~'binding pallet.thread-expr/binding->
     ~'expose-request-as pallet.thread-expr/arg->]
    (-> ~@forms)))

;; #### Phase Macros
;;
;; The `-->` macro above opens the door for a more abstract way to
;; write phases. By capturing the pattern of a function that threads
;; its first argument through the rest of its forms, we can simplify
;; phase function definitions, while making their signatures clearer
;; to the user. (`(def-phase-fn some-phase ...)` presents a simpler
;; signal than `(defn some-phase [arg ...] (-> arg ...))`; the first
;; is a crate function, the second may not be.)
;;
;; Additionally, by controlling the way in which request threading
;; occurs, we gain the ability to insert checks between every form in
;; passed in to the phase function. `check-session` is a simple test
;;that makes sure that the session exists, and is a map.

(defn check-session
  "Function that can check a session map to ensure it is a valid part of
   phase definition. It returns the session map.

   On failure, the function will print the phase through which the
   session passed prior to crashing. It is like that this phase
   introduced the fault into the session; to aid in debugging, make
   sure to use `phase-fn` and `def-phase-fn` to build phases."
  [session form]
  (if (and session (map? session))
    session
    (condition/raise
     :type :invalid-session
     :message
     (str
      "Invalid session map in phase.\n"
      (format "session is %s\n" session)
      (format "Problem probably caused by subphase:\n  %s\n" form)
      "Check for non crate functions, improper crate functions, or
      problems in threading the session map in your phase
      definition. A crate function is a function that takes a session
      map and other arguments, and returns a modified session
      map. Calls to crate functions are often wrapped in a threading
      macro, -> or pallet.phase/phase-fn, to simplify chaining of the
      session map argument."))))

(defmacro phase-fn
  "Composes a phase function from a sequence of phases by threading an
 implicit phase session parameter through each. Each phase will have
 access to the parameters passed in through `phase-fn`'s argument
 vector. thus,

    (phase-fn [filename]
         (file filename)
         (file \"/other-file\"))

   is equivalent to:

   (fn [session filename]
     (-> session
         (file filename)
         (file \"/other-file\")))
  
   with a number of verifications on the session map performed after
   each phase invocation."
  ([argvec] (phase-fn argvec identity))
  ([argvec subphase & left]
     `(fn [session# ~@argvec]
        (--> session#
             ~subphase
             (check-session ~(str subphase))
             ~@(when left
                 [`((phase-fn ~argvec ~@left) ~@argvec)])))))

(defmacro def-phase-fn
  "Binds a `phase-fn` to the supplied name."
  [name & rest]
  (let [[name [argvec & body]]
        (name-with-attributes name rest)]
    `(def ~name
       (phase-fn ~argvec ~@body))))

;; `phase` is deprecated by pallet 0.5.0 in favor of `phase-fn` with
;; no argument vector... I do have to say, though, in a world where
;; `phase-fn` DOES have an argument vector, it becomes nice at the top
;; level to be able to compose various phases without that empty
;; argument vector, like so:
;;
;;    (phase
;;      (java/java :jdk)
;;      hadoop/install)
;;
;; rather than
;;
;;    (phase-fn []
;;      (java/java :jdk)
;;      hadoop/install)
;;
;; So, to see how it looks, and for backwards compatibility, we
;; provide `phase`.

(defmacro phase [& forms]
  `(phase-fn [] ~@forms))




© 2015 - 2024 Weber Informatics LLC | Privacy Policy