Slide 1

Slide 1 text

Hiccup Data Structure!! Lisp Meet Up presented by Shibuya.lisp #55 超変換!!

Slide 2

Slide 2 text

–Johnny Appleseed ʠ͜͜ʹҾ༻Λೖྗ͍ͯͩ͘͠͞ɻʡ –Yusuke Godai “こんなやつらのために、 これ以上誰かの涙は⾒たくない! 皆に笑顔でいて欲しいんです! だから⾒ててください!俺の…変⾝!! ”

Slide 3

Slide 3 text

–Johnny Appleseed ʠ͜͜ʹҾ༻Λೖྗ͍ͯͩ͘͠͞ɻʡ –@ayato-p “HTML Formのために、 これ以上誰かの涙は⾒たくない! Hiccupユーザーに笑顔でいて欲しいんです! だから⾒ててください!俺の…変換!! ”

Slide 4

Slide 4 text

あやぴー •ayato-p@github •Cybozu Startups, Inc. •Clojureエンジニア •Webアプリを作るお仕事 •仮面ライダーと言えばクウ○世代 •ガンダムと言えばS○ED世代

Slide 5

Slide 5 text

今日の話

Slide 6

Slide 6 text

今日話すこと •そもそもHiccupとは •HTMLフォームを書くのが面倒だということ •Hiccupデータの変換について •ClojureScriptでマクロ展開するときハマった話 •「仮面ライダーク○ガ」の素晴らしさについて

Slide 7

Slide 7 text

今日話すこと •そもそもHiccupとは •HTMLフォームを書くのが面倒だということ •Hiccupデータの変換について •ClojureScriptでマクロ展開するときハマった話 •「仮面ライダーク○ガ」の素晴らしさについて

Slide 8

Slide 8 text

Hiccup

Slide 9

Slide 9 text

そもそもHiccupとは •HTMLをClojureのデータ構造で表現するDSL •ベクタをタグ、マップを属性として表現する •Clojureコミュニティでは人気(?) •沢山のライブラリがHiccup-likeなデータでの表現を
 サポートしている •例) Enlive, Reagent, Rum, Instaparse, etc

Slide 10

Slide 10 text

そもそもHiccupとは [:a#mybtn.button {:href "..."} "..."] タグベクター: ベクタで表現したHTMLタグ

Slide 11

Slide 11 text

そもそもHiccupとは [:a#mybtn.button {:href "..."} "..."] タグ名: CSSセレクターのように書ける

Slide 12

Slide 12 text

そもそもHiccupとは [:a#mybtn.button {:href "..."} "..."] 属性: マップで表現

Slide 13

Slide 13 text

そもそもHiccupとは [:a#mybtn.button {:href "..."} "..."] コンテンツ: タグベクターや文字列を書ける

Slide 14

Slide 14 text

これであなたも ✨Hiccupマスター✨

Slide 15

Slide 15 text

HTMLフォームを 書くのは面倒だ

Slide 16

Slide 16 text

HTMLフォームは面倒 •フォームのポスト処理に失敗した場合、入力されていた 値を入力された状態にして再描画しなければいけない •利用しているCSSフレームワークのルールにそって、
 エラーが適切に表示されるようにしなければならない •全てのHTMLフォームを全く同じように表示したくない ことがある •例) ログインフォーム、グリッドスタイル

Slide 17

Slide 17 text

[:div.form-group [:label {:for "input-email"} "Email address"] [:input#input-email.form-control {:type :email :name :email :class (when (contains? errors :email) "is-invalid") :value (:email values) :placeholer "Enter email"}] [:div.invalid-feedback (:email errors)] [:small.form-text.text-muted "We'll never share your email with anyone else."]] Bootstrap4の場合

Slide 18

Slide 18 text

[:div.form-group [:label {:for "input-email"} "Email address"] [:input#input-email.form-control {:type :email :name :email :class (when (contains? errors :email) "is-invalid") :value (:email values) :placeholer "Enter email"}] [:div.invalid-feedback (:email errors)] [:small.form-text.text-muted "We'll never share your email with anyone else."]] Bootstrap4の場合

Slide 19

Slide 19 text

Slide 20

Slide 20 text

どうにかしたい… 1.諦める 2.小さい関数で面倒なところをラップする
 (hiccup.formみたいな) 3.Formativeみたいなフォーム用ライブラリを使う
 (jkk/formative) 4.まだ見たこと無い解決方法

Slide 21

Slide 21 text

OOPな言語のWAFだと… •フォームに値をマッピングするのに •フォームオブジェクトをつくる •ORMのモデルオブジェクトをつかう •エラー用クラスを付与するのに •カスタムタグをつくる •エラー時に固定のCSSクラスを付与する

Slide 22

Slide 22 text

–Johnny Appleseed ʠ͜͜ʹҾ༻Λೖྗ͍ͯͩ͘͠͞ɻʡ –Yusuke Godai “求められた気がしたんです。 あの蜘蛛みたいな奴と戦えって!”

Slide 23

Slide 23 text

–Johnny Appleseed ʠ͜͜ʹҾ༻Λೖྗ͍ͯͩ͘͠͞ɻʡ –@ayato-p “求められた気がしたんです。 あのVerbose Hiccupと戦えって!”

Slide 24

Slide 24 text

Kuuga

Slide 25

Slide 25 text

Kuugaとは •Pure Clojureにしか依存していない •ユーザーが自由に •任意のタグやクラスに対して変換ルールを定義できて •マクロ展開時に変換することもできて •ClojureScriptもサポートしている •Hiccupのようなデータを変換するライブラリ •https://github.com/ayato-p/kuuga

Slide 26

Slide 26 text

変換ルールを書く-その) (defmethod growing/transform-by-tag :input [_ options tag-vector] (let [[tagkw tagopts contents] (tool/parse-tag-vector tag-vector)] `[~tagkw (update-input-opts ~options ~tagopts) ~@contents]))

Slide 27

Slide 27 text

変換ルールを書く-その* (defmethod growing/transform-by-class :form-group [_ options tag-vector] (let [[tagkw tagopts contents] (tool/parse-tag-vector tag-vector) contents (reduce (fn [contents' tagvec'] (let [[tk to _] (tool/parse-tag-vector tagvec') [_ t] (tool/parse-tag-keyword tk)] (cond-> (conj contents' tagvec') (= t "input") (conj `(invalid-fb ~options ~to))))) [] contents))] `[~tagkw ~tagopts ~@contents]))

Slide 28

Slide 28 text

こうすると…

Slide 29

Slide 29 text

これを… [:div.form-group [:label {:for "input-email"} "Email address"] [:input#input-email.form-control {:type :email :name :email :placeholder "Enter email"}] [:small.form-text.text-muted "We'll never share your email with anyone else."]]

Slide 30

Slide 30 text

こんな感じで展開できる [:div.form-group [:label {:for "input-email"} "Email address"] [:input#input-email.form-control {:type :email :name :email :class (when (contains? errors :email) "is-invalid") :value (:email values) :placeholder "Enter email"}] [:div.invalid-feedback (:email errors)] [:small.form-text.text-muted "We'll never share your email with anyone else."]] ※イメージです

Slide 31

Slide 31 text

Kuugaのメリット •マクロ展開時に変換できる •なので実行時の変換コストがない •特別なタグ/記法を覚える必要がない •どのくらい変換ルールを書くかもユーザー次第

Slide 32

Slide 32 text

Kuugaのデメリット •特にない •マクロ展開時に変換するルールを書くのが少し難しい •実行時に変換する方法は速度があまり出ない

Slide 33

Slide 33 text

KuugaとClojureScript

Slide 34

Slide 34 text

Kuugaは… •マクロ展開時にHiccupデータを変換できる •拡張ルールをマルチメソッドで記述する •ClojureScriptをサポートしている

Slide 35

Slide 35 text

ClojureScriptでのマクロ •ClojureScriptでマクロは書けない •セルフホストはとりあえず忘れる •マクロ展開はClojureの環境が利用される •展開後のフォームに含まれるものはClojureScript上に
 なければならない

Slide 36

Slide 36 text

問題. •マクロ展開時に利用されるマルチメソッドはClojureの
 環境で定義されていなければならない

Slide 37

Slide 37 text

問題. •マクロ展開時に利用されるマルチメソッドはClojureの
 環境で定義されていなければならない •ClojureScriptのビルド時に変換ルールを読み込んで おかないといけない

Slide 38

Slide 38 text

解決策 •cljsbuildなどの既存のツールに任意のClojure libを
 読み込む仕組みがなさそう? •自前でスクリプトを書いて、その中で任意のlibを
 読み込みClojureScriptのビルドAPIを直接叩く •lein-execはプロジェクトの環境を引き継げる •Bootは元々そういう用途で使えるので便利 https://github.com/ayato-p/kuuga/blob/master/examples/cljs/script/cljsbuild.clj

Slide 39

Slide 39 text

まとめ •Kuugaを使うとわりとハッピーになれる(気がした) •マクロ展開時に分かっている部分を変換するコンセプト を適用できるところは他にもある気がする •SQL Builder的なやつとか