designudge
vol.120

2016-Jan-14 アドベントカレンダー特集2

Cover

今回のカバーも、初詣に行ったときのショットです。

Story Permalink

前回、アドベントカレンダー向けに書いていたものをまとめて1日目から5日目まで掲載したのですが、今回もその続きです。

6日目から10日目までを掲載します。

僕は技術的な部分ではClojureとElectronにすごく可能性を感じていて、今回もそんな感じ一色のマガジンとなっています。

次々号あたりで通常運転に戻ると思います。

行灯

今回のカバーも、前号に続き初詣のときのもの。

田舎の小さな神社で、地元の人に大事にされています。

石畳の脇に立てられた行灯が社まで続く、印象的な境内です。

descjop で遊ぼう day 6 : Electronがうまくダウンロードできないとき

descjop (Electron + Clojure) Advent Calendar 2015 - Adventarの6日目です。

Story Permalink

descjopを使おうと思って、Electronのダウンロードのところで詰まってしまうことがあります。

Electronがダウンロードし展開し終わったよという

Done, without errors.

が表示されず、そのままunzip状態で止まってしまう現象です。

こうなってしまうと、次に同様の作業をやった場合でもElectronが中途半端な状態で展開されてしまい、Electronのビルドから先のステップに進めなくなってしまいます。

なぜかというと、一回ダウンロードしたElectronを再利用するように出来ているためです。

こうなったときは、キャッシュされているElectronを消去してやることで再実行することが出来、無事Electronのダウンロードを終えることが出来ます。

download-atom-shell-task.coffee · atom/grunt-download-electronでは、次のように定義されています。

  grunt.registerTask TaskName, 'Download electron',  ->
    @requiresConfig "#{TaskName}.version", "#{TaskName}.outputDir"
    {version, outputDir, downloadDir, symbols, rebuild, apm, token, appDir} = grunt.config TaskName
    downloadDir ?= path.join os.tmpdir(), 'downloaded-electron'
    symbols ?= false
    rebuild ?= true
    apm ?= getApmPath()
    version = "v#{version}"
    versionDownloadDir = path.join(downloadDir, version)
    appDir ?= process.cwd()
 
    done = @async()
    ... 以下略

つまり、ダウンロードを一時的にされるディレクトリは、os.tmpdir()つまりOSのテンポラリディレクトリに展開されるようになっています。

ただ、OSのテンポラリディレクトリを探すのがかったるいというケースもあるでしょう。

gruntfileの修正による対応

そこで、./Gruntfile.jsを少し修正します。

現状の./Gruntfile.jsは以下のようになっていると思います。

module.exports = function(grunt) {
 
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    "download-electron": {
        version: "0.35.0",
        outputDir: "./electron",
        rebuild: true
    }
  });
 
  grunt.loadNpmTasks('grunt-download-electron');
 
};

これに、

module.exports = function(grunt) {
 
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    "download-electron": {
        version: "0.35.0",
        outputDir: "./electron",
        rebuild: true,
        downloadDir: ".electron-download" // 追加します
    }
  });
 
  grunt.loadNpmTasks('grunt-download-electron');
 
};

のように、downloadDirというオプションを指定します。

このようにすると、カレントディレクトリ上にElectronのキャッシュがダウンロードされます。

.electron-downloadというフォルダが出来ているのが見えましたよね。

以上、Electronがダウンロードうまくできないときのチップスでした。

descjop で遊ぼう day 7 : Hello Worldを出力してみるには

descjop (Electron + Clojure) Advent Calendar 2015 - Adventarの7日目です。

Story Permalink

昨日は、ついにdescjopを使ってデフォルトのアプリケーションが起動しました。

Hello Worldも出たし言うことはないのですが、何かソースをいじった上でHello Worldを表示させてみたいものです。

ということで、コードを編集して、Hello Worldと表示させてみましょう。

./app/index.htmlをエディタで開きます。

<!DOCTYPE html>
<html>
  <head>
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    We are using node.js <script>document.write(process.version)</script>
    and Electron(atom-shell) <script>document.write(process.versions['electron'])</script>.
  </body>
</html>

上記のコードを、下記のように書き換えて、アプリケーションを起動してみましょう。

<!DOCTYPE html>
<html>
  <head>
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    <p>Hello World!</p>
  </body>
</html>

すると、画面にはHello World!と表示されるはずです。

どこからか「バカヤロー!!」という声が聞こえてきそうです。

HTMLを編集したんだから表示が変わって当たり前ですよね。「もっとこう、動的に変えるようなやつをやりたいんだ!」というご要望もごもっともですが、実はここは結構重要なポイントです。

helloworld-site-view

例えば、./src/helloworld/core.cljs(.on app "ready" ...)の部分を下記のように変えてみましょう。

  ; ready listener
   (.on app "ready"
     (fn []
       (reset! *win* (BrowserWindow. (clj->js {:width 800 :height 600})))
 
       ;; when no optimize comment out
       ;; (.loadUrl @*win* (str "file://" (.resolve path (js* "__dirname") "../index.html")))
       (.loadUrl @*win* "https://designudge.org/ja/")
 
       ;; when no optimize uncomment
       ;; (.loadUrl @*win* (str "file://" (.resolve path (js* "__dirname") "../../../index.html")))
 
       (.on @*win* "closed" (fn [] (reset! *win* nil))))))

変えた部分は、ロードするHTMLをファイルではなく、別のドメインのサイトにしている部分です。

するとどう表示されるかというと、サイトが表示されるはずです。

このブラウザウインドウは独立して普通のHTMLと同様にJavaScriptを読み込むことが出来ます。

つまり、動的にHello Worldを表示させたい場合は、HTML上で読み込むためのJavaScriptを出力させればいいわけです。

現在は、Electron用のClojureScriptのみがJavaScriptとして出力されていますが、ブラウザから読み込む用のプロジェクトを作成すれば良いのです。

ちなみに、デフォルトプロジェクトでは、ブラウザ向けのClojureScriptは用意してありません。

今から作成していっても良いのですが、最初からブラウザ向けのClojureScriptプロジェクトが用意されているテンプレートを利用しましょう。

descjopで、ブラウザ向けのClojureScriptプロジェクトを含んでいるプロジェクトは、OmベースのテンプレートとReagentベースのテンプレートの2つになります。

全体理解を深めるために、Hello Worldプロジェクトは置いておいて、こちらのプロジェクトを使ってみましょう。

descjop で遊ぼう day 8 : Om basedテンプレートを使ってみる

descjop (Electron + Clojure) Advent Calendar 2015 - Adventarの8日目です。

Story Permalink

さて、前回前々々回で、デフォルトテンプレートを使ったHelloWorldプロジェクトを生成してみました。

前回すこし話をしましたが、動的にHelloWorldをやるには、デフォルトプロジェクトよりも、ビルド設定が進んでいるOm basedプロジェクトやReagent basedプロジェクトのほうが手っ取り早いです。デフォルトプロジェクトは本当にまっさらな感じなので。

ということで、今回はOm based テンプレートからプロジェクトを生成し、実行してみましょう。

Om basedプロジェクトやReagent basedプロジェクトは、デフォルトテンプレートと構造が異なるだけでなく、実行方法も異なります。

1. プロジェクトの生成とNode.jsモジュールのインストール

Om basedテンプレートの利用方法は、デフォルトテンプレートと同様ですが、最後に+omとつけます。

$ lein new descjop helloworld_om_based +om 

すると、下記のように出力されます。

$ lein new descjop helloworld_om_based +om 
 
Generating fresh descjop +om project.

これでプロジェクトが出来ました。

helloworld_om_basedプロジェクトのディレクトリに移動しておきます。

$ cd helloworld_om_based

続いて、Node.jsモジュール各種をプロジェクト内へインストールします。

$ npm install

すると、下記のような表示が出て、node_modulesディレクトリに各種モジュールが配置されます。

$ npm install
 
grunt@0.4.5 node_modules/grunt
├── which@1.0.9
...
└── js-yaml@2.0.5 (esprima@1.0.4, argparse@0.1.16)
 
grunt-download-electron@2.1.2 node_modules/grunt-download-electron
├── progress@1.1.2
...
└── github-releases@0.2.1 (minimatch@0.2.12, optimist@0.4.0, prettyjson@0.8.1, request@2.27.0)

続いて、Electronのダウンロードをしましょう。これもデフォルトテンプレートと同様の手順です

$ grunt download-electron
 
Running "download-electron" task
downloading [===================] 100% 0.0s
 
Done, without errors.

2. ClojureScriptのコンパイル

まずは、ClojureScript用のexternの解決をするために下記を実行します。

$ lein externs > app/js/externs.js

ここもデフォルトテンプレートと同様です。

さて、続いてClojureScriptのコンパイルをします。

ここからすこしデフォルトテンプレートと変わってきます。

$ lein cljsbuild once
 
Compiling ClojureScript.
Compiling "app/js/cljsbuild-main.js" from ["src"]...
Successfully compiled "app/js/cljsbuild-main.js" in 14.819 seconds.
Compiling "app/js/front.js" from ["src_front"]...
Successfully compiled "app/js/front.js" in 8.773 seconds.

lein cljsbuild onceを実行すると、2つのビルドが走ったのがわかるとおもいます。

一つは普通のElectron起動用のClojurescriptで、もう一つはsrc_frontというディレクトリにあるClojureScriptファイルがコンパイルされて、app/js/front.jsとして出力されています。

src_frontは、ブラウザウインドウ内に表示されるHTML上から呼び出されるフロントエンド用のJavaScriptを出力するためのプロジェクトで、Omによるプログラムが記述されています。

3. Figwheelサーバの起動

これで、コンパイルは完了し、実行可能状態になったわけですが、もう一つ手順があります。

まずは、別の新しいターミナルウインドウを開き、helloworld_om_basedプロジェクトのディレクトリに移動します。

$ cd helloworld_om_basedのあるディレクトリ

続いて下記コマンドを入力します。

$ lein trampoline figwheel frontend

すると、このように画面に表示されます。

$ lein trampoline figwheel frontend

Figwheel: Starting server at http://localhost:3449
Focusing on build ids: frontend
Compiling "app/js/front.js" from ["src_front"]...
Successfully compiled "app/js/front.js" in 8.435 seconds.
Started Figwheel autobuilder
 
Launching ClojureScript REPL for build: frontend
Figwheel Controls:
          (stop-autobuild)                ;; stops Figwheel autobuilder
          (start-autobuild [id ...])      ;; starts autobuilder focused on optional ids
          (switch-to-build id ...)        ;; switches autobuilder to different build
          (reset-autobuild)               ;; stops, cleans, and starts autobuilder
          (reload-config)                 ;; reloads build config and resets autobuild
          (build-once [id ...])           ;; builds source one time
          (clean-builds [id ..])          ;; deletes compiled cljs target files
          (fig-status)                    ;; displays current state of system
          (add-dep [org.om/om "0.8.1"]) ;; add a dependency. very experimental
  Switch REPL build focus:
          :cljs/quit                      ;; allows you to switch REPL to another build
    Docs: (doc function-name-here)
    Exit: Control+C or :cljs/quit
 Results: Stored in vars *1, *2, *3, *e holds last exception object
Prompt will show when figwheel connects to your application

これで準備が整いました。

Hello om world

4. アプリの起動

先ほどのコンパイルをやっていたターミナルウインドウに戻り、Electronを起動しましょう。

OSXの場合は下記で起動します。

$ ./electron/Electron.app/Contents/MacOS/Electron app

これで、「Hello om world!」と出力されていれば成功です。

明日はいよいよボタンをつけてみます。

descjop で遊ぼう day 9 : Om basedテンプレートにボタンをつける

descjop (Electron + Clojure) Advent Calendar 2015 - Adventarの9日目です。

Story Permalink

さて、今日はOm basedテンプレートから作ったhelloworld_om_basedプロジェクトにボタンでもつけていってみましょうか。

ようやくアプリ製作ぽくなってきましたね。

さて、アプリケーションの表示部分を変更するには、どこのファイルをいじればいいのでしょうか。

正解はsrc_front / helloworld_om_based_om / core.cljsです。

ボタンなどを作るには、ここのClojurescriptファイルを修正します。

ボタンを置き、Alertを出してみる

Omは、独特な考え方でアプリケーションの状態を変化させていきます。

なので、その部分とは別に、まずは構造を変えてみるところからやっていきましょう。

まず、ちょっとDOMの構造を変えるところからやってみます。

現状、src_front / helloworld_om_based_om / core.cljsのコードは下記の通りになっているはずです。

(ns helloworld_om_based-om.core
  (:require [om.core :as om :include-macros true]
            [om.dom :as dom :include-macros true]
            [figwheel.client :as fw :include-macros true]))
 
(enable-console-print!)
 
(fw/watch-and-reload
 :websocket-url   "ws://localhost:3449/figwheel-ws"
 :jsload-callback 'mount-root)
 
(defonce app-state (atom {:message "Hello om world!"}))
 
(defn mount-root []
  (om/root
   (fn [state owner]
     (reify om/IRender
       (render [_]
         (dom/h1 nil (:message state)))))
   app-state
   {:target (. js/document
               (getElementById "app"))}))
 
(defn init! []
  (mount-root))
 
(init!)

このうち、

(defn mount-root []
  (om/root
   (fn [state owner]
     (reify om/IRender
       (render [_]
         (dom/h1 nil (:message state)))))
   app-state
   {:target (. js/document
               (getElementById "app"))}))

の部分が、画面に出力されるコンポーネントを定義しているところなのですが、ここにdom/h1という関数があるのがわかるとおもいます。

これは、DOMツリーを構築するための関数でここでは、h1タグを生成しています。h1があるので、他にもdivとかそういうHTMLタグに相当するものがあります。

実際は、om.dom/h1は、React.DOM.h1が呼び出されます。

他にも下記のHTML要素がサポートされています。

a abbr address area article aside audio b base bdi bdo big blockquote body br
button canvas caption cite code col colgroup data datalist dd del details dfn
dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5
h6 head header hr html i iframe img input ins kbd keygen label legend li link
main map mark menu menuitem meta meter nav noscript object ol optgroup option
output p param picture pre progress q rp rt ruby s samp script section select
small source span strong style sub summary sup table tbody td textarea tfoot th
thead time title tr track u ul var video wbr

それでは、上記のコードをちょっと変えてみましょう。

下記のように変更します。

(defn mount-root []
  (om/root
   (fn [state owner]
     (reify om/IRender
       (render [_]
         (dom/div nil
                  (dom/h1 nil (:message state))
                  (dom/button #js {:onClick (fn [e]
                                              (js/alert "pressed!"))}
                              "Hello")))))
   app-state
   {:target (. js/document
               (getElementById "app"))}))

dom/h1dom/divでくるみ、dom/h1の下にdom/buttonを配置しました。

dom/buttonは、ボタンです。

で、ここにon-click時のイベントとして"pressed!"というアラートが出るようにしています。

pressed

ここで、再びElectronを起動するか、もしくはすでに起動中のElectronをみると、ボタンが表示されており、クリックすると"pressed!"とアラートが出るでしょう。

明日も続きを書きます。

descjop で遊ぼう day 10 : Om basedテンプレートにボタンをつけるその2

descjop (Electron + Clojure) Advent Calendar 2015 - Adventarの10日目です。

Story Permalink

続けてhelloworld_om_basedプロジェクトにボタンをつけて動的な処理をつけていきます。

前回の続きなので、今から読んだ方は前回のところまで作業を終えておいてください。

さて、ボタンを押すと、JavaScriptのアラートが出たのですが、まだそれだけです。

続いて、ボタンを押したら見出しが変わるようにしてみましょうか。

(js/alert "pressed!")は、一旦コメントアウトして、(om/transact! state :message (fn [] "World."))を追加します。

追加しおえたコードは、下記のとおりとなります。

(defn mount-root []
  (om/root
   (fn [state owner]
     (reify om/IRender
       (render [_]
         (dom/div nil
                  (dom/h1 nil (:message state))
                  (dom/button #js {:onClick (fn [e]
                                              ;; (js/alert "pressed!")
                                              (om/transact! state :message (fn [] "World.")))
                                   :className "press"}
                              "Hello")))))
   app-state
   {:target (. js/document
               (getElementById "app"))}))

transact!というのは、ざっくりと言うと、アプリケーションの状態を書き換える関数で、

(defn transact!
  ([cursor f] ...)
  ([cursor korks f] ...)
  ([cursor korks f tag]) ...)

こんな風に利用できることが、APIマニュアルに書かれています。

今回の使用例でいうと、

(om/transact! カーソル マップのキーワード 実行される関数)

という形で書いています。

ここで、カーソルという概念が出てきました。カーソルというのは、アプリケーション全体の状態のうち、コンポーネントが管理するべきアプリケーションの状態の一部です。

カーソルが活躍するのはコンポーネントを使ってアプリケーションを構築しはじめたときです。

現時点では、アプリケーションの状態くらいにとらえておいてよいでしょう。

Omでは、アプリケーションを作成する際に、アプリケーションの状態を管理するマップを用意して、それを渡すという状態の管理方法を使っています。

ここで、コード全体を見渡してみます。

(defonce app-state (atom {:message "Hello om world!"}))
 
(defn mount-root []
  (om/root
   (fn [state owner]
     (reify om/IRender
       (render [_]
         (dom/div nil
                  (dom/h1 nil (:message state))
                  (dom/button #js {:onClick (fn [e]
                                              ;; (js/alert "pressed!")
                                              (om/transact! state :message (fn [] "World.")))
                                   :className "press"}
                              "Hello")))))
   app-state
   {:target (. js/document
               (getElementById "app"))}))
 
(defn init! []
  (mount-root))

まず、(defonce app-state (atom {:message "Hello om world!"}))というところで、最初の状態を定義しています。

app-stateがアプリケーションの状態を指しているのです。

mount-root関数は今まで取り上げてきた通りですが、内容としてはom/rootを実行した結果を返します。

om/rootは、Omアプリケーションを作る際に必要なアプリケーションのルートで、実際にはコンポーネントなどを用意してそれらを組み合わせることでOmアプリケーションとして機能させます。

今回は直接OmアプリケーションのRoot内でいろいろやっていますが。

root関数は、こんな風に定義されており、

(defn root
  ([f value options] ...))

今回は、app-stateを第2引数に渡しています。

app-stateは、IRenderIRenderStateのインスタンスを返す関数として定義されるfに対して、ここではstateという名前で渡ってくるように定義しています。

それによって、(:message state)は、ここでは(:message @app-state)と同様の結果が得られるのです。

Omでは、アプリケーションの状態の変化に応じて、仮想DOMを自動で更新することができます。

例えば下記のようにコードを書き換えてみましょう。

(defn mount-root []
  (om/root
   (fn [state owner]
     (reify om/IRender
       (render [_]
         (dom/div nil
                  (dom/h1 nil (:message state))
                  (dom/p {:className "my-message"} (:message state))
                  (dom/button #js {:onClick (fn [e]
                                              ;; (js/alert "pressed!")
                                              (om/transact! state :message (fn [] "World.")))
                                   :className "press"}
                              "Hello")))))
   app-state
   {:target (. js/document
               (getElementById "app"))}))

h1の下にpタグを出力する(dom/p {:className "my-message"} (:message state))を置きました。

実行すると、ボタンを押した際にh1要素とp要素両方が変わっているのがわかるはずです。

編集後記

Story Permalink

Greativeでは、1/4から業務開始をしているのですが、正月休みが明けて1週間経ちました。

そして、今週末また3連休です。

結構このコンボでリズムが狂ってしまう人っているのではないでしょうか。

次号はもう一回だけ、アドベントカレンダー特集をやります。

次は11日目から15日目まで。なぜそこで特集が終わるのかというと、そこまでで力尽きてしまったからです。

通常運転をしつつ、続きを書いていきます。

Colophon

編集長
Greative GK. 原一浩 ( kara_d )
製版システム
Clojure / Compojure / Ring / Enlive / markdown-clj / Jetty / MySQL
Share this magazine!
Follow designudge

FREE

Magazine Archives

vol.122

2016-Feb-01

Cover

台湾に行ってきました。

今回のカバーは、台湾にて撮影したものです。

自治体Webデザイントレンドこぼれ話 : ここだよマップ

CSS Niteの年末イベントShiftでやっているセッションにデザイントレンドというのがあります。

descjop で遊ぼう day 16 : Windows版のメニューを表示する

2015年の振り返り的な一人レトロスペクティブ

そういえば、まだ2015年の振り返りをマガジンに掲載していませんでした

編集後記

vol.121

2016-Jan-14

Cover

今回のカバーは、、、UFO?

自治体Webデザイントレンドこぼれ話 : 予告

CSS Niteの年末イベントShiftでやっているセッションにデザイントレンドというのがあります。

descjop で遊ぼう day 11 : Omのコンポーネントで遊ぼう

descjop で遊ぼう day 12 : Omのコンポーネントで遊ぼうその2

descjop で遊ぼう day 13 : Clojure ワークショップでTAしてきた話

descjop で遊ぼう day 14 : アプリケーションメニューを作ろう

descjop で遊ぼう day 15 : descjop 0.5.3をリリースしました

編集後記

vol.120

2016-Jan-14

Cover

今回のカバーも、初詣に行ったときのショットです。

descjop で遊ぼう day 6 : Electronがうまくダウンロードできないとき

descjop で遊ぼう day 7 : Hello Worldを出力してみるには

descjop で遊ぼう day 8 : Om basedテンプレートを使ってみる

descjop で遊ぼう day 9 : Om basedテンプレートにボタンをつける

descjop で遊ぼう day 10 : Om basedテンプレートにボタンをつけるその2

編集後記

vol.119

2016-Jan-10

Cover

今回のカバーは、初詣に行ったときのショット。

「descjop で遊ぼう」について

「descjop で遊ぼう」というのは年末にやったアドベントカレンダーのシリーズなのですが、前段としてまずはその解説をば。

descjop で遊ぼう day 1 : はじめに

descjop で遊ぼう day 2 : 3つのテンプレート

descjop で遊ぼう day 3 : 環境構築をしよう

descjop で遊ぼう day 4 : Helpを見てみよう

descjop で遊ぼう day 5 : デフォルトアプリケーションのビルドと起動

編集後記

vol.118

2015-Oct-22

Cover

今回のカバーは、Stack Overflow DevDaysのイベントをお手伝いしたときの窓からのショット。

Vaqum Web Design Review

創刊以来延々と続くWebサイトレビュー

ミカヅキClojure : ゼロからはじめるClojure入門第2回

Clojureっていうじつに面白いプログラミング言語をとりあげていきます

インフォメーション

おすすめのイベント・勉強会情報を紹介

編集後記

vol.117

2015-Oct-07

Cover

今回は散歩道からのショット

Editor’s Picks

designudgeで扱っている内容に近いジャンルのリソースのうち気になったものなど

U.S. Web Design Standardsを見てみる

今話題となっているU.S. Web Design Standardsの周辺情報など見てみました

Bootstrap 4徹底攻略 (3)

Bootstrap 4のアルファ版がでているので使おう

インフォメーション

おすすめのイベント・勉強会情報を紹介

編集後記

vol.116

2015-Sep-28

Cover

今回は帰省した際に通った三島からのショットです

Vaqum Web Design Review

創刊以来延々と続くWebサイトレビュー

みそじ過ぎからの英語再学習

40を目前に今までサボっていた英語学習をはじめました

Bootstrap 4徹底攻略 (2)

Bootstrap 4のアルファ版がでているので使おう

ミカヅキClojure

Clojureっていうじつに面白いプログラミング言語をとりあげていきます

インフォメーション

おすすめのイベント・勉強会情報を紹介

編集後記

vol.115

2015-Sep-13

Cover

前回と同じ開発合宿のショットから一つ

Vaqum Web Design Review

創刊以来延々と続くWebサイトレビュー

開発合宿へ行こう! : Python mini hack-a-thon 夏山合宿 2015

先週末に行ってきた開発合宿について書きました。

ミカヅキClojure

Clojureっていうじつに面白いプログラミング言語をとりあげていきます

インフォメーション

おすすめのイベント・勉強会情報を紹介

編集後記