0.3.1 docs





    Jul 26, 2017



    Index of all namespaces

    « Project + dependencies

    A puny map to redis persistency layer

    A tiny redis based ORM,
    Caveat: crud+index operations are not atomic, introducing lua procedures will fix that.
    Misc id names of all functions, args etc..
    Support for migration that must happen outside interceptors like index add/delete etc..
    The README below is fetched from the published project artifact. Some relative links may be broken.


    Puny is a tiny layer for persisting Clojure maps into Redis hashes, it aims to reduce boilerplate and to enable end to end life cycle management of entities from validation to data migration and indexing.

    It includes support for:

    • Complete generated CRUD API.
    • Automatic keys and id management (both generated and internal to the entity).
    • Automatic Validation checks.
    • Declaring indexes.
    • Attaching persisted metadata like versioning etc..
    • Defining interceptors on CRUD functions enabling support for security checks, automatic migration etc..

    Puny is based on carmine, a big thank you to Peter its author.

    Build Status


     [puny "0.3.1"]

    Walk through

    Defining an entity with a generated id:

    (require '[puny.core :as p])
    (p/entity foo)        
    ; validated-{entity} must be defined, it will be called upon add and update
    (defn validate-foo [foo] {})

    Basic CRUD:

    (let [id (add-foo {:bar 1})]
      (get-foo id) ;=> {:bar 1}
      (foo-exists? id) ;=> truthy
      (update-foo id {:bar 2}) 
      (get-foo id) ;=> {:bar 2}
      (delete-foo id)
      (foo-exists? id) ;=> falsey

    An entity with a internal id property:

    (p/entity user :id name)        
    (defn validate-user [user] {})
    (add-user {:name "me"})]
    (get-user "me") ;=> {:name "me"}

    Fail fast functions:

    (p/entity planet :id name :indices [life])
    (defn validate-planet [planet] {})
    (add-planet {:name "lunar" :life "false"})
    (planet-exists! "lunar"); => true
    (planet-exists! "foo"); => (throws ExceptionInfo (is-type? :puny.test.core/missing-planet))
    (delete-planet! "foo"); => (throws ExceptionInfo (is-type? :puny.test.core/missing-planet))

    Fast retrieval of all entity keys:

     (p/entity phone :id number :indices [type])
     (defn validate-phone [p] {})
     (add-phone {:number 1234 :type "android"})
     (add-phone {:number 1235 :type "iOS"}) 
     (all-phones); => '("1234" "1235")

    Defining indexes:

    (p/entity car :id license :indices [color])        
    (defn validate-car [car] {})
    (add-car {:license 123 :color "black"}) ;=> 123
    (get-car-index :color "black") ;=> ["123"]

    Metadata and hooks (automatic versioning):

    (def current-version 2)
    (declare update-car)
    (defn upgrade-car [f & args] 
        (let [res (apply f args) version (-> res meta :version)]
          (if (and (map? res) (or (nil? version) (> current-version version )))
              (update-car (assoc res :tires "big")) (apply f args))
    ; each entity that is read gets update automaticly, versioning info is set in metadata
    (p/entity {:version current-version} car :id license 
        :indices [color] :intercept {:read [upgrade-car]})
    (defn validate-car [car] {})
    (add-car {:license 123 :color "black"}) => 123
    ; we set the version back to 1 to trigger update
    (wcar (car/hset (car-id 123) :meta {:version 1}))
    ; the entity gets updated on the fly
    (:tires (get-car 123)) ;=> {:license 123 :color "black" :tires "big"}


    Puny is not transactional, some underlying operations might span multiple operations, in case where consistency and atomicity are important its recommended to use locks on entity ids.

    Copyright and license

    Copyright [2013] [Ronen Narkis]

    Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

    Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.