Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
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的なやつとか