<commit_before>(ns
    #^{:author "<NAME>"
       :doc "
A library for defining entities and aggregates.  Entities correspond to a mongo entity.
Aggregates correspond to a an embedded entity.

Example:

  (defaggregate Address [:street :city :state :zip])

  (defaggregate Phone [:area-code :number]

  (defentity Person
    [:first-name 
     :last-name
     :address {:type Address}
     :phones {:type :list :of Phone}])
"}
  karras.entity
  (:require [karras.collection :as c])
  (:use karras.sugar
        [clojure.contrib.def :only [defnk defalias defvar]]
        [clojure.contrib.str-utils2 :only [lower-case]]
        inflections))

(defrecord EntitySpec
  [record-class
   is-entity?
   fields
   collection-name])

(defonce entity-specs (atom {}))

(defn entity-spec
  "Lookup the EntitySpec for a given type"
  [type]
  (get @entity-specs type))

(defn entity-spec-get
  "Lookup the EntitySpec value for given key of the given type."
  [type key & [default]]
  (get (entity-spec type) key default))

(defn entity-spec-get-in
  "Lookup the EntitySpec value for given key of the given type."
  [type path]
  (get-in (entity-spec type) path))

(defn parse-fields [ks]
  (loop [attrs ks
         results {}]
    (if (empty? attrs)
      results
      (let [[x y & zs] attrs]
        (cond
         (and (keyword? x) (map? y)) (recur zs (assoc results x y))
         (and (keyword? x) (nil? y)) (recur zs  (assoc results x {}))
         (keyword? x)                (recur (cons y zs) (assoc results x {}))
         :else (throw (IllegalArgumentException. (str x " is not a keyword or a map"))))))))

(defn- assoc-meta-cache [v]
  (with-meta v (merge {:cache (atom nil)} (meta v))))

(declare make)
(defmulti convert
  "Multimethod used to convert a entity.
   Takes in a field-spec and a val and returns the conver
   Dispatches off of :type key of first arg.
   :default returns the value unmodified"
  :type)

(defmethod convert :list
  [field-spec vals]
  (map #(convert (assoc field-spec :type (:of field-spec)) %) vals))

(defmethod convert :reference
  [field-spec val]
  (assoc-meta-cache (make (:of field-spec) val :no-defaults)))

(defmethod convert :references
  [field-spec vals]
  (assoc-meta-cache (map #(make (:of field-spec) % :no-defaults) vals)))

(defmethod convert :default
  [_ val]
  val)

(defn make
  "Converts a hashmap to the supplied type."
  [#^Class type hmap & [no-defaults]]
  (let [fields (-> type entity-spec :fields)
        has-key? (fn [e k]
                   (try (some #{k} (keys e))
                        (catch Exception _ nil)))
        make-default? #(and (nil? no-defaults) (:default %))]
    (reduce (fn [entity [k field-spec]]
              (cond
               (has-key? entity k)   (assoc entity k (convert field-spec (k hmap)))
               (make-default? field-spec) (assoc entity k (:default field-spec))
               :otherwise            entity))
            (with-meta (merge (.newInstance type) hmap) (meta hmap))
            fields)))

(defn- default-collection-name [classname]
  (pluralize (lower-case (last (.split (str classname) "\\.")))))

(defn- make-mongo-type [classname is-entity? fields type-fns]
  `(do
     (defrecord ~classname [])
     (swap! entity-specs assoc
            ~classname
            (EntitySpec. ~classname
                      ~is-entity?
                      ~(parse-fields fields)
                      ~(if is-entity?
                         (default-collection-name classname)
                         nil)))
     (defmethod convert ~classname [field-spec# val#]
                (make (:type field-spec#) val#))
     ~@(map (fn [f] `(-> ~classname ~f)) type-fns)))

(defmacro defentity
  "Defines an entity that corresponds to a collection."
  [classname [& fields] & type-fns]
  (make-mongo-type classname true fields type-fns))
<commit_msg>refactored karras.entity/make<commit_after>(ns
    #^{:author "<NAME>"
       :doc "
A library for defining entities and aggregates.  Entities correspond to a mongo entity.
Aggregates correspond to a an embedded entity.

Example:

  (defaggregate Address [:street :city :state :zip])

  (defaggregate Phone [:area-code :number]

  (defentity Person
    [:first-name 
     :last-name
     :address {:type Address}
     :phones {:type :list :of Phone}])
"}
  karras.entity
  (:require [karras.collection :as c])
  (:use karras.sugar
        [clojure.contrib.def :only [defnk defalias defvar]]
        [clojure.contrib.str-utils2 :only [lower-case]]
        inflections))

(defrecord EntitySpec
  [record-class
   is-entity?
   fields
   collection-name])

(defonce entity-specs (atom {}))

(defn entity-spec
  "Lookup the EntitySpec for a given type"
  [type]
  (get @entity-specs type))

(defn entity-spec-get
  "Lookup the EntitySpec value for given key of the given type."
  [type key & [default]]
  (get (entity-spec type) key default))

(defn entity-spec-get-in
  "Lookup the EntitySpec value for given key of the given type."
  [type path]
  (get-in (entity-spec type) path))

(defn parse-fields [ks]
  (loop [attrs ks
         results {}]
    (if (empty? attrs)
      results
      (let [[x y & zs] attrs]
        (cond
         (and (keyword? x) (map? y)) (recur zs (assoc results x y))
         (and (keyword? x) (nil? y)) (recur zs  (assoc results x {}))
         (keyword? x)                (recur (cons y zs) (assoc results x {}))
         :else (throw (IllegalArgumentException. (str x " is not a keyword or a map"))))))))

(defn- assoc-meta-cache [v]
  (with-meta v (merge {:cache (atom nil)} (meta v))))

(declare make)
(defmulti convert
  "Multimethod used to convert a entity.
   Takes in a field-spec and a val and returns the conver
   Dispatches off of :type key of first arg.
   :default returns the value unmodified"
  :type)

(defmethod convert :list
  [field-spec vals]
  (map #(convert (assoc field-spec :type (:of field-spec)) %) vals))

(defmethod convert :reference
  [field-spec val]
  (assoc-meta-cache (make (:of field-spec) val :no-defaults)))

(defmethod convert :references
  [field-spec vals]
  (assoc-meta-cache (map #(make (:of field-spec) % :no-defaults) vals)))

(defmethod convert :default
  [_ val]
  val)

(defn- convert-field [entity field-key field-spec]
  (if (contains? entity field-key)
    (assoc entity field-key (convert field-spec (field-key entity)))
    entity))

(defn- fill-defaults [entity field-key field-spec disable-defaults]
  (if (and (not disable-defaults) (:default field-spec))
    (assoc entity field-key (:default field-spec))
    entity))

(defn make
  "Converts a hashmap to the supplied type."
  [#^Class type hmap & [no-defaults]]
  (let [make-field (fn [entity [field-key field-spec]]
                     (-> entity
                         (convert-field field-key field-spec)
                         (fill-defaults field-key field-spec no-defaults)))
        initial (with-meta (merge (.newInstance type) hmap) (meta hmap))
        fields  (-> type entity-spec :fields)]
    (reduce make-field initial fields)))

(defn- default-collection-name [classname]
  (pluralize (lower-case (last (.split (str classname) "\\.")))))

(defn- make-mongo-type [classname is-entity? fields type-fns]
  `(do
     (defrecord ~classname [])
     (swap! entity-specs assoc
            ~classname
            (EntitySpec. ~classname
                      ~is-entity?
                      ~(parse-fields fields)
                      ~(if is-entity?
                         (default-collection-name classname)
                         nil)))
     (defmethod convert ~classname [field-spec# val#]
                (make (:type field-spec#) val#))
     ~@(map (fn [f] `(-> ~classname ~f)) type-fns)))

(defmacro defentity
  "Defines an entity that corresponds to a collection."
  [classname [& fields] & type-fns]
  (make-mongo-type classname true fields type-fns))

{'fraction_non_alphanumeric': 0.13440996948118006, 'fraction_numerical': 0.000254323499491353, 'mean_word_length': 2.5685117967332123, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 10, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '881834', 'n_tokens_mistral': 2600, 'n_tokens_neox': 2523, 'n_words': 819}