2016-09-28 4 views
0

をフィールド名を読み値に設定するマクロ私はClojureのに新しいですし、このは、他のオブジェクトから

(defn transform 
    [entity] 
    (let [new-obj (doto (SomeObj.) 
       (.setField1 (:field-1 entity))) 
    new-obj)) 

のように見える、私が書いたいくつかのコードをリファクタリングしようとしている私が必要になります多くのオブジェクトを持っていますentityprototype例:(SomeObj.)、およびmapを受け入れるマクロを作成したいと考えています。ここで、キーはプロトタイプのフィールド名であり、値はentityから正しいフィールドを得るためのキーワードのベクトルです。それぞれkeyについては、(get-in map [value as a vector])の引数を使用して.set + keyNameを呼び出す必要があります。

私は、新しいエンティティごとにマッピングの設定を作成し、特別な場合にのみコードを書くことができます。これはマクロを使って可能ですか?

または、これをクロージャーで行うより慣れた方法があります。

答えて

1

はい、あなたは簡単にこのようなマクロでそれを行うことができます:

(defmacro map-to [type mappings entity] 
    `(doto (new ~type) 
    [email protected](map (fn [[field entity-field]] 
       `(~(symbol (str ".set" (clojure.string/capitalize field))) 
       (~entity-field ~entity))) 
      mappings))) 

これはあなたが必要とする正確なコードを生成します:

(map-to java.util.Date {date :dt minutes :mm hours :h} 
     {:dt 10 :mm 22 :h 12}) 

は以下に展開されます:

(doto 
    (new java.util.Date) 
    (.setDate (:dt {:dt 10, :mm 22, :h 12})) 
    (.setMinutes (:mm {:dt 10, :mm 22, :h 12})) 
    (.setHours (:h {:dt 10, :mm 22, :h 12}))) 

ここにいくつか注意してください:

1)新しい変数new-objを導入する必要はありません。dotoは操作対象のオブジェクトを返します。

2)マッピングはリテラルマップとして渡す必要があります。それ以外の場合は、.特殊フォームに渡すキーを取得できないためです。

3)entityマップが繰り返されていることがわかります。REPLで

(let [entity20047 {:dt 10, :mm 22, :h 12}] 
    (doto 
    (new java.util.Date) 
    (.setDate (:dt entity20047)) 
    (.setMinutes (:mm entity20047)) 
    (.setHours (:h entity20047)))) 

user> (map-to java.util.Date {date :dt minutes :mm hours :h} 
       {:dt 10 :mm 22 :h 12}) 

;;=> #inst "2016-09-10T09:22:48.867-00:00" 

user> (let [ent {:dt 10 :mm 22 :h 12}] 
     (map-to java.util.Date {date :dt minutes :mm hours :h} ent)) 

;;=> #inst "2016-09-10T09:22:48.899-00:00" 

(値は3時間早いので、今ではこのように拡大し

(defmacro map-to [type mappings entity] 
    (let [ent (gensym "entity")] 
    `(let [~ent ~entity] 
     (doto (new ~type) 
     [email protected](map (fn [[field entity-field]] 
        `(~(symbol (str ".set" (clojure.string/capitalize field))) 
        (~entity-field ~ent))) 
       mappings))))) 

:あなたは、マクロ内で結合別を導入することでこの問題を解決することができます私のタイムゾーン(gmt + 3)のために)

更新

get-in付き)ご希望の動作を取得するために、あなたは少しだけ、このマクロを変更することができます。REPLで

(defmacro map-to [type mappings entity] 
    (let [ent (gensym "entity")] 
    `(let [~ent ~entity] 
     (doto (new ~type) 
     [email protected](map (fn [[field entity-field]] 
        `(~(symbol (str ".set" (clojure.string/capitalize field))) 
        (get-in ~ent ~entity-field))) 
       mappings))))) 

(let [entity20094 {:date {:dt 10}, :time {:mm 22, :h 12}}] 
    (doto 
    (new java.util.Date) 
    (.setDate (get-in entity20094 [:date :dt])) 
    (.setMinutes (get-in entity20094 [:time :mm])) 
    (.setHours (get-in entity20094 [:time :h])))) 

user> (map-to java.util.Date {date [:date :dt] 
           minutes [:time :mm] 
           hours [:time :h]} 
       {:date {:dt 10} :time {:mm 22 :h 12}}) 

;;=> #inst "2016-09-10T09:22:41.935-00:00" 

はに展開マッピング関数の作成を自動化するもう1つのマクロを作ることができます:

あなたはクラスオブジェクトからメーカー名を生成する機能を必要とするすべての最初:

(defn make-name [c] 
    (->> c 
     .getName 
     (#(clojure.string/split % #"\.")) 
     (clojure.string/join "-") 
     (str "create-") 
     symbol)) 

user> (make-name java.util.Date) 
;;=> create-java-util-Date 

エンティティからインスタンスを作成するための関数を定義するマクロ:

(defmacro defmapper [type mappings] 
    `(defn ~(make-name type) [entity#] 
    (map-to ~type ~mappings entity#))) 

この1つは関数を作成し、つまり、エンティティを指定すると、それをクラスインスタンスに変換します。 REPLで

(defn create-java-util-Date [entity__20122__auto__] 
    (map-to 
    java.util.Date 
    {date [:date :dt], minutes [:time :mm], hours [:time :h]} 
    entity__20122__auto__)) 

(defmapper java.util.Date {date [:date :dt] 
          minutes [:time :mm] 
          hours [:time :h]}) 

は、次のように拡張されますそれはちょうど通常の関数です

user> (map create-java-util-Date 
      [{:date {:dt 10} :time {:mm 22 :h 12}} 
      {:date {:dt 11} :time {:mm 22 :h 12}} 
      {:date {:dt 12} :time {:mm 22 :h 12}}]) 

;;(#inst "2016-09-10T09:22:18.974-00:00" 
;; #inst "2016-09-11T09:22:18.974-00:00" 
;; #inst "2016-09-12T09:22:18.974-00:00") 
0

leetwinskiの答えはかなり良いです。すべてのゲッターとセッターを反映させ、正しい方法でそれらを公開する必要があります。 (http://tahto.github.io/lucidity/lucid-mind.html) を反射によって方法の世代を通じて

関連する問題