Middlemanの入れ子レイアウトでエラー undefined method `safe_concat' for "":String
slimでwrap_layout使おうとしてエラーった。- を = に。
gulpfileをCoffeeScriptで書く
メモ。
//gulpfile.js require('coffee-script/register'); require('./gulpfile.coffee');
//gulpfile.coffee gulp = require('gulp') gulp.task 'default', () -> console.log('gulp!')
参考。
普通にCoffeeScriptで書けるようになってた。
node-webkitでコールバックで書いた非同期処理をPromise使用に書き換えてみた
node-webkitで作ったアプリで、データロード処理と保存処理部分をそれぞれ書き換えてみた。
PromiseのライブラリはBluebirdを使ってみた。
あと、cheerioをDOMのパースに使用。
FTPは、jsftpを使用。
データロード処理の流れ(load)
1. FTPでデータ取得
2. FTPで取得したファイルを読み込み
3. 読み込んだHTMLをパースしてデータ取得
4. 入力フォームに取得したデータを反映
保存処理の流れ(save)
1. FTPで取得していたファイルを読み込み
2. 読み込んだHTMLをパースしてフォームに入力されたデータをHTMLに反映
3. フォームのデータが反映されたHTMLのデータを一時ファイルに書き込み
4. 書き込まれた一時ファイルをFTPでアップロード
5. アップロード完了後、別画面に遷移、完了通知などの処理
v1がコールバック版、v2がPromise版
2014/6/25追記:loadとsaveを呼び出している部分(index.coffee)も追加してみた。
File SystemのAPIはBluebirdのPromisificationという機能を使ってみた。
HTML部分の入力フォームやFTPの設定などはgistに上げてませんが、大体やってる流れは把握できるかと。
cheerioでパースすると、日本語が数値文字列参照になっているので、変換。
https://github.com/cheeriojs/cheerio/issues/466
Promiseについては以下が大変参考になりました。
node-webkitでTypeScriptを使ってはまるケース
node-webkitのJavaScript Contextでのrequireでのパス解決でハマる場合がある。
例えばこんなファイル構成で
- index.html
- js
- index.js(index.ts)
- fileUploader.js
各ファイルがこんな感じ。
<!-- index.html --> <!DOCTYPE html> <html> <head > <meta charset="utf-8" /> ................. <script src="js/index.js"></script> </body> </html>
//js/index.js .................. var fileUploader = require('./js/fileUploader'); ................. //↑node-webkit的に問題なし. //index.ts import fileUploader = require('./js/fileUploader'); //↑index.tsからだとrequire('./fileUploader')なのでエラー。
↑node-webkitのJavaScript Context(いわゆるブラウザの世界)でrequireをした場合、JavaScriptファイルからのパスではなく、そのJavaScriptを読み込んでいるhtmlからのパスになる。
参考:
回避策1
その場しのぎ的。requireをindex.htmlのインラインに持ってくる。
<!-- index.html --> <!DOCTYPE html> <html> <head > <meta charset="utf-8" /> ................. <script> //.................. var fileUploader = require('./js/fileUploader'); //................. </script> <script src=" </body> </html>
回避策2
構造を変える
- index.html
- index.js(index.ts)
- js
- fileUploader.js
<!-- index.html --> <!DOCTYPE html> <html> <head > <meta charset="utf-8" /> ................. <script src="index.js"></script> </body> </html>
うーむ。
requireしてnewして使用するnpmのパッケージにTypeScript用の型定義ファイルを書く
こういうの。
///<reference path='./typing/node.d.ts' /> ///<reference path='./typing/jsftp.d.ts' /> import JSFtp = require('jsftp'); .......................... var Ftp = new JSFtp({ host: connect.host, port: connect.port, user: connect.account, pass: connect.password }); ..........................
jsftp用に書いた型定義ファイル(簡略)
// ./typing/jsftp.d.ts /// <reference path="./node.d.ts" /> interface JSFtpOption { host?: string; port?: number; user?: string; pass?: string; .................... } declare module 'jsftp' { class Ftp { constructor(cfg?: JSFtpOption); put(from: string, to: string, callback?: (err: NodeJS.ErrnoException) => void): void; ....... } export = Ftp; }
参考
- https://github.com/borisyankov/DefinitelyTyped/blob/master/tspromise/tspromise.d.ts
これだとJSFTPOptionが他の型定義ファイルと重複してしまう可能性がないわけではない。
ちなみにexport = Ftpとしてやる必要がある*1ので、JSFTPOptionをmodule 'jsftp'の中に入れてexport JSFTPOptionとは出来ない。中に入れてexport付けないとコンパイル時にprivateだと怒られるし。
declare module 'jsftp' { class Ftp { constructor(cfg?: {host?:string;port?:number;user?:string;pass?: string}); put(from: string, to: string, callback?: (err: NodeJS.ErrnoException) => void): void; ........... } export = Ftp; }
オブジェクト型リテラルにすれば重複しないけど。 うーむ。
追記
@katahirado http://t.co/fQSZ6Qr7gU コレのmodule NodeJS みたいな感じで JSFTP専用モジュールに押し込んでしまうと重複しにくくて良いと思いますよ。 http://t.co/Lpl1CwFWfE
— わかめ@TypeScriptカッコガチ (@vvakame) 2014, 6月 16
おー、なるほど。
declare module JSFTP { export interface JSFtpOption { host?: string; port?: number; user?: string; pass?: string; ....................... } } declare module 'jsftp' { class Ftp { constructor(cfg?: JSFTP.JSFtpOption); put(from: string, to: string, callback?: (err: NodeJS.ErrnoException) => void): void; ............................................. } export = Ftp; }
このJSFTPモジュールは非インスタンス化モジュールで、型定義のネームスペースにしか存在しないので、変数空間を汚さないと。
ネームスペースを噛ませているので重複しにくくなりました。
勉強になりました。
ありがとうございます。
更に追記
@vvakame ありがとうございます。非インスタンス化モジュール…。なるほど、そんな方法が。
— Yuichi Katahira (@katahirado) 2014, 6月 16
あー・・・。TypeScriptリファレンス Ver.1.0対応の6-4-4インスタンス化・非インスタンス化モジュールに書いてあった。
読破したはずなのに覚えてなかった・・・。
node-webkitでNode contextからGUIのAPIを使う
メニュー出したりとかデスクトップアプリ特有のネイティブのアレをNode側から使いたい。
1.require時に引数として渡す
//index.js var gui = require('nw.gui'); //↓コレ var fileUploader = require('fileUploader')(gui);
例なので全部渡しちゃってるけど必要な項目だけに絞って渡した方がいいと思う。
2.window.require('nw.gui')する
//fileUploader.js var fs = require('fs'); var gui = window.require('nw.gui');
Window.globalとかGlobal.windowとかになっている話はこちらを参照。
node-webkitの場合、windowにもrequireメソッドがあって、実装はというとこんな
function (name) { if (name == 'nw.gui') return nwDispatcher.requireNwGui(); return global.require(name);}
見てそのまま。
nw.guiはwindowのrequireを呼ばないといけないと。
contextが混在しているのはささっと呼べて便利といえば便利だけど、サンドボックス機構があったほうがメンテナンス的には幸せなのかもしれない。
以上。
参考
grunt-slim
メモ
Gruntfile.coffee
...................................... slim: pretty: options: pretty: true files: [ expand: true cwd: 'src' src: ['{,*/}*.slim'] dest: 'app' ext: '.html' ] ...................................... grunt.loadNpmTasks('grunt-slim')
圧縮されるのはアレなのでpretty: trueにした。
参考:grunt-slim