CrossClj

1.1.8 docs

SourceDocs



RECENT
    VARS
    -root-namespace
    add-property!
    add-type!
    apply-property-fns
    apply-type-fns
    defmodel
    do-post-select
    do-pre-insert
    do-pre-update
    fully-qualified-symbol
    ICreateFromMap
    ifn-invoke-forms
    IModel
    IModelDefaults
    invoke-model
    invoke-model-or-instance
    method-forms-map
    model?
    property-fns
    root-namespace
    set-root-namespace!
    type-fns

    « Index of all namespaces of this project

    The defmodel macro, used to define Toucan models, and
    the IModel protocol and default implementations, which implement Toucan model functionality.
    (add-property! k & {:keys [insert update select]})
    Define a new model property and set the functions used to implement its functionality.
    See documentation for more details.
    
      (add-property! :timestamped?
        :insert (fn [obj _]
                  (let [now (java.sql.Timestamp. (System/currentTimeMillis))]
                    (assoc obj :created-at now, :updated-at now)))
        :update (fn [obj _]
                  (assoc obj :updated-at (java.sql.Timestamp. (System/currentTimeMillis)))))
    (add-type! k & {:keys [in out]})
    Add a new type mapping for type named by key K. Supply mappings for the functions that should prepare value
    when it goes :in to the database, and for when it comes :out.
    
      ;; add a :json type (using Cheshire) will serialize objects as JSON
      ;; going into the DB, and deserialize JSON coming out from the DB
      (add-type! :json
        :in  json/generate-string
        :out #(json/parse-string % keyword))
    Private
    (apply-property-fns context obj)
    Private
    (apply-type-fns obj direction)
    Apply the appropriate type-fns for OBJ.
    
    macro
    (defmodel model table-name)(defmodel model docstr? table-name)
    Define a new "model". Models encapsulate information and behaviors related to a specific table in the application
    DB, and have their own unique record type.
    
    defmodel defines a backing record type following the format <model>Instance. For example, the class associated
    with User is <root-namespace>.user/UserInstance. (The root namespace defaults to models but can be configured
    via set-root-namespace!)
    
    This class is used for both the titular model (e.g. User) and for objects that are fetched from the DB. This means
    they can share the IModel protocol and simplifies the interface somewhat; functions like types work on either
    the model or instances fetched from the DB.
    
       (defmodel User :user_table)  ; creates class UserInstance and DB model User
    
       (db/select User, ...)  ; use with toucan.db functions. All results are instances of UserInstance
    
    The record type automatically extends IModel with IModelDefaults, but you can override specific methods, or
    implement other protocols, by passing them to defmodel, the same way you would with defrecord.
    
       (defmodel User :user_table
         IModel
         (hydration-keys [_]
           [:user])
         (properties [_]
           {:timestamped true})
         (pre-insert [user]
           user))
    
     This is equivalent to:
    
       (extend (class User)             ; it's somewhat more readable to write `(class User)` instead of UserInstance
         IModel (merge IModelDefaults
                        {...}))
    
     Finally, the model itself is invokable. Calling with no args returns *all* values of that object; calling with a
    single arg can be used to fetch a specific instance by its integer ID.
    
       (Database)                       ; return a seq of *all* Databases (as instances of DatabaseInstance)
       (Database 1)                     ; return Database 1
    (do-post-select model obj)
    Don't call this directly! Apply internal functions like post-select when an object is retrieved from the DB.
    
    (do-pre-insert model obj)
    Don't call this directly! Apply functions like pre-insert before inserting an object into the DB.
    
    (do-pre-update model obj)
    Don't call this directly! Apply internal functions like pre-update before updating an object in the DB.
    
    Private
    (fully-qualified-symbol s)
    Make a symbol fully qualified by resolving it as a var in the current namespace.
    
    conj        ;;=> clojure.core/conj
    str/join    ;;=> clojure.string/join
    foo.bar/baz ;;=> foo.bar/baz 
    protocol
    Used by internal functions like do-post-select.
    
    Private
    (map-> klass m)
    Convert map M to instance of record type KLASS.
    
    Private
    (ifn-invoke-forms)
    Macro helper, generates
    
    (~'invoke [this#]
     (invoke-model-or-instance this#))
    (~'invoke [this# id#]
     (invoke-model-or-instance this# id#))
    (~'invoke [this# arg1# arg2#]
     (invoke-model-or-instance this# arg1# arg2#))
    ,,,
    protocol
    The IModel protocol defines the various methods that are used to provide custom behavior for various models.
    
    This protocol contains the various methods model classes can optionally implement. All methods have a default
    implementation provided by IModelDefaults; new models created with the defmodel macro automatically implement
    this protocol using those default implementations. To override one or more implementations, use extend and
    merge your custom implementations with IModelDefaults:
    
      (defmodel MyModel)
    
      (extend (class MyModel)
        IModel
        (merge IModelDefaults {...}))
    (default-fields this)
    Return a sequence of keyword field names that should be fetched by default when calling
    select or invoking the model (e.g., `(Database 1)`).
    (hydration-keys this)
    The hydration-keys method can be overrode to specify the keyword field names that should be hydrated
    as instances of this model. For example, User might include :creator, which means hydrate will
    look for :creator_id or :creator-id in other objects to find the User ID, and fetch the Users
    corresponding to those values.
    (post-insert this)
    Gets called by insert! with an object that was newly inserted into the database.
    This provides an opportunity to trigger specific logic that should occur when an object is inserted or modify the
    object that is returned. The value returned by this method is returned to the caller of insert!. The default
    implementation is identity.
    
      (post-insert [user]
        (assoc user :newly-created true))
    
      (post-insert [user]
        (add-user-to-magic-perm-groups! user)
        user)
    (post-select this)
    Called on the results from a call to select and similar functions. Default implementation doesn't do anything,
       but you can provide custom implementations to do things like remove sensitive fields or add dynamic new ones.
    
    For example, let's say we want to add a :name field to Users that combines their :first-name and :last-name:
    
        (defn- post-select [user]
          (assoc user :name (str (:first-name user) " " (:last-name user))))
    
    Then, when we select a User:
    
        (User 1) ; -> {:id 1, :first-name "Cam", :last-name "Saul", :name "Cam Saul"}
    (post-update this)
    Gets called by update! with an object that was successfully updated in the database.
    This provides an opportunity to trigger specific logic that should occur when an object is updated.
    The value returned by this method is not returned to the caller of update!. The default
    implementation is nil (not invoked).
    
    Note: This method is *not* invoked when calling update! with a honeysql-form form.
    
      (post-update [user]
        (audit-user-updated! user)
    (pre-delete this)
    Called by delete! for each matching object that is about to be deleted.
       Implementations can delete any objects related to this object by recursively calling delete!, or do
       any other cleanup needed, or check some preconditions that must be fulfilled before deleting an object.
    
    The output of this function is ignored.
    
          (pre-delete [{database-id :id :as database}]
            (delete! Card :database_id database-id)
            ...)
    (pre-insert this)
    Gets called by insert! immediately before inserting a new object.
    This provides an opportunity to do things like encode JSON or provide default values for certain fields.
    
        (pre-insert [query]
          (let [defaults {:version 1}]
            (merge defaults query))) ; set some default values
    (pre-update this)
    Called by update! before DB operations happen. A good place to set updated values for fields like updated-at,
    or to check preconditions.
    (properties this)
    Return a map of properties of this model. Properties can be used to implement advanced behavior across many
       different models; see the documentation for more details. Declare a model's properties as such:
    
         (properties [_] {:timestamped? true})
    
    Define functions to handle objects with those properties using add-property!:
    
        (add-property! :timestamped?
          :insert (fn [obj] (assoc obj :created-at (new-timestamp), :updated-at (new-timestamp)))
          :update (fn [obj] (assoc obj :updated-at (new-timestamp))))
    (types this)
    Return a map of keyword field names to their types for fields that should be serialized/deserialized in a special
      way. Values belonging to a type are sent through an input function before being inserted into the DB, and sent
      through an output function on their way out. :keyword is the only type enabled by default; you can add more by
      calling add-type!:
    
        (add-type! :json, :in json/generate-string, :out json/parse-string)
    
    Set the types for a model like so:
    
        ;; convert :category to a keyword when it comes out of the DB; convert back to a string before going in
        (types [_] {:category :keyword})
    Default implementations for IModel methods.
    
    Private
    (invoke-model model)(invoke-model model id)(invoke-model model k v & more)
    Fetch an object with a specific ID or all objects of type ENTITY from the DB.
    
    (invoke-model Database)           -> seq of all databases
    (invoke-model Database 1)         -> Database w/ ID 1
    (invoke-model Database :id 1 ...) -> A single Database matching some key-value args
    (invoke-model-or-instance obj & args)
    Check whether OBJ is an model (e.g. Database) or an object from the DB; if an model,
    call invoked-model; otherwise call get.
    Private
    (method-forms-map forms)
    Take in forms as passed to defrecord or extend-type (protocol or interface
    name followed by method definitions), and return a map suitable for use with
    extend.
    
      (IFn (invoke [this] this)) ;;=> {IFn {:invoke (fn [this] this)}}
    (model? model)
    Is model a valid toucan model?
    
    (root-namespace)
    Fetch the parent namespace for all Toucan models.
    
    (set-root-namespace! new-root-namespace)
    Set the root namespace where all models are expected to live.
    
       (set-root-namespace! 'my-project.models)
    
    In this example, Toucan would look for a model named UserFollow in the namespace my-project.models.user-follow.