【CentOS6】yum で最新の git を入れる
wing リポジトリを使う。
groonga+mroonga なども yum でインストール出来る様になるので便利。
epel リポジトリの導入
$ rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
wing リポジトリの導入
$ cd /etc/yum.repos.d/ $ wget http://wing-repo.net/wing/6/EL6.wing.repo
git のインストール
$ yum --enablerepo=wing install git
結果
============================================================================================================================================================== パッケージ アーキテクチャ バージョン リポジトリー 容量 ============================================================================================================================================================== 更新: git x86_64 2.4.1-1.el6_34.wing wing 8.2 M 依存性関連での更新をします。: perl-Git noarch 2.4.1-1.el6_34.wing wing 34 k
【gulp】タスク内で run-sequence を使うなら気をつけること
基本的にタスク内で run-sequence を使うなら、引数のコールバックを最後に指定するべき。
つまりこんな感じ。
gulp.task('hoge-fuga', function(callback) { runSequence('hoge', 'fuga', callback); // <- 引数 callback を最後に指定する });
こうしないと同期されない。
次の場合は処理の順番が保証されず、期待通りに動かないことがあるので注意したい。
gulp.task('hoge-fuga', function() { runSequence('hoge', 'fuga'); }); gulp.task('task', function() { runSequence('hoge-fuga', 'piyo'); });
上記だと、task
を実行した際、hoge-fuga
内のタスクが非同期で動き出し、後に続く piyo
がこれらの終わりを待たずに実行される。
処理の重さによっては、一番最後の piyo
が先に終わる場合もあり得る。
watch
とか、後に続く処理が無ければコールバックは無くても良い。
gulp-devtools であまり黒い画面を触らずに済ます
使う機会ないけどメモ。
個人的にローカルインストールで済ませたいので、npm の scripts プロパティに設定しておく。
▼ gulp-devtools のインストール
$ npm install gulp-devtools --save-dev
▼ package.json
{ "scripts": { "gulp-devtools": "gulp-devtools" } }
▼ gulpfile.js
var gulp = require('gulp'); ... module.exports = gulp;
後は以下を実行するだけ。
$ npm run gulp-devtools
以降は上記のコマンドを打つ時だけ黒い画面に触ることになる。
CakePHP 2.x でバリデート前にまとめてデータを加工する
最近業務で CakePHP を使い始めたので備忘録的な。
FuelPHP などに慣れていると、バリデートのルールと一緒にフィルタ処理したい衝動に駆られる。
こういった機構は CodeIgniter や Phalcon など実装されてたと思う(Phalcon はうろ覚え)
CakePHP においてバリデート時にフィルタを掛けるなら、beforeValidate というコールバックメソッドを用意してあげれば良いみたい。
汎用的に使おうと思ったらこんな感じ?
テスト環境:
CakePHP 2.6.2
PHP 5.6.6
▼ AppModel.php
<?php /** * Application model for Cake. * * This file is application-wide model file. You can put all * application-wide model-related methods here. * * @link http://cakephp.org CakePHP(tm) Project * @package app.Model * @since CakePHP(tm) v 0.2.9 */ App::uses('Model', 'Model'); /** * Application model for Cake. * * Add your application-wide methods in the class below, your models * will inherit them. * * @package app.Model */ class AppModel extends Model { /** * バリデート前に実行するフィルタ一覧 * * @var array */ public $filters = []; /** * バリデート前に実行するイベント * * @param array $options オプション * @return bool */ public function beforeValidate($options = []) { $result = parent::beforeValidate($options); if ($result && $this->filters) { array_walk($this->data[$this->name], function (&$value, $key) { if (!isset($this->filters[$key])) { return; } foreach ($this->filters[$key] as $filter) { if (is_string($filter)) { $value = call_user_func($filter, $value); } elseif (is_array($filter)) { $callback = array_shift($filter); array_unshift($filter, $value); $value = call_user_func_array($callback, $filter); } } }); } return $result; } }
あとは継承先で、$filters プロパティを用意して指定してあげるだけ。
<?php class Hoge extends AppModel { public $filters = [ 'title' => ['trim', [['self', 'upper']]], ]; /** * 小文字を大文字に変換します。 * * @param string $str 対象の文字列 * @return string 変換後の文字列 */ public static function upper($str) { return strtoupper($str); } }
この場合、title というデータにビルトイン関数 trim と、Hoge#upper を実行することになる。
Windows で Flow を触ってみる
追記: 2014/11/21 19:34
こんなページが出来てました。
http://www.ocamlpro.com/pub/ocpwin/flow-builds/
更にバージョンアップしているようです。
今巷で話題の flow を触ろうと思ったのに Windows 版が無い!と嘆いてたら、こんなコメントが投稿されてた。
Please provide Windows binaries · Issue #6 · facebook/flow · GitHub
Windows 用にビルドしてくれているみたいなので早速試してみる。
まずはファイルをダウンロード
http://www.ocamlpro.com/pub/ocpwin/flow-builds/flow-simple-windows-20141119.zip
zip を解凍すると中身はこんな感じ
. ├── flow32.exe ├── flow64.exe ├── lib │ ├── bom.js │ ├── core.js │ ├── dom.js │ └── react.js └── Readme.txt
次に環境変数 FLOWLIB に lib ディレクトリのパスを指定しておく。
$ export FLOWLIB=c:/cygwin/home/user/flow-simple-windows/lib
Cmd:
$ set FLOWLIB=c:/cygwin/home/user/flow-simple-windows/lib
後は実行するだけ。
とりあえず、以下のサンプルから 01_HelloWorld を試す。
https://github.com/facebook/flow/tree/master/examples
まずは失敗例
hello.js
/* @flow */ function foo(x) { return x*10; } foo("Hello, world!");
{ 01_HelloWorld } » ~/flow-simple-windows/flow64.exe single . C:\Users\hoge\.babun\cygwin\home\hoge\flow-master\examples\01_HelloWorld\./hello.js:7:5,19: string This type is incompatible with C:\Users\hoge\.babun\cygwin\home\hoge\flow-master\examples\01_HelloWorld\./hello.js:4:10,13: number Found 1 error
次に成功例
hello.js
/* @flow */ function foo(x) { return x*10; } // This is fine, because we're passing a number now foo(10);
{ 01_HelloWorld } » ~/flow-simple-windows/flow64.exe single . Found 0 errors
ちゃんと動いているっぽい。
興味のある方は試してみては。
※ただし自己責任でお願いします。
【wxPython】TextCtrl と StyledTextCtrl の change イベントの違い
TextCtrl だと EVT_TEXT を使う。
... tc_text = wx.TextCtrl(frame, wx.ID_ANY) tc_text.Bind(wx.EVT_TEXT, OnChange) ...
StyledTextCtrl では EVT_STC_CHANGE を使う。
import wx.stc as stc ... stc_text = wx.StyledTextCtrl(frame, wx.ID_ANY) stc_text.Bind(stc.EVT_STC_CHANGE, OnChange) ...
EVT_CHAR で監視とか必要なかった。
【wxPython】メニューバーを付ける
追記: 2014/11/20 21:41
調べてみたら、もっとたくさん ID 用意されてた。
SpecialIDs - wxPyWiki
あと、新たに ID を追加する場合は wx.NewId
で生成する方が良いようだ。
メニューを追加する際は ID が必要だが、一部は事前に用意されている。
- wx.ID_UNDO
- wx.ID_REDO
- wx.ID_CUT
- wx.ID_COPY
- wx.ID_PASTE
新たに ID を用意するなら、別にこれらの ID を必ず使う必要はない。
メニューの UI の更新に関しては、wx.EVT_UPDATE_UI イベントを使うと良い。
# -*- coding: utf-8 -*- import wxversion wxversion.select("3.0") import wx import wx.html2 import wx.stc as stc import markdown #============================================================================== # 定数群 #------------------------------------------------------------------------------ MENU_FILE_CLOSE = 1 #============================================================================== # 関数群 #------------------------------------------------------------------------------ def update(): timer.Start(milliseconds=100, oneShot=True) def update_preview(): # 変数 html が空文字だと SetPage で反映されないので、html 要素など加えておく html = """\ <!DOCTYPE html> <html lang="ja"> <head><meta charset="utf-8"></head> <body>%s</body> </html> """ % convert_str_to_html(text.GetValue()) browser.SetPage(html, "") def convert_str_to_html(str): codehilite = "codehilite(force_linenos=True, guess_lang=False, css_class=syntax)" return markdown.markdown(str, ["extra", codehilite]) def exit_app(): dialog = wx.MessageDialog(frame, u"ウィンドウを閉じますか?", u"確認", wx.OK|wx.CANCEL|wx.ICON_QUESTION) result = dialog.ShowModal() if result == wx.ID_OK: frame.Destroy() # イベントハンドラ #------------------------------------------------------------------------------ def close_handler(event): exit_app() def char_handler(event): update() event.Skip() def timer_handler(event): update_preview() event.Skip() def update_ui_handler(event): menu_edit.Enable(id=wx.ID_UNDO, enable=text.CanUndo()) menu_edit.Enable(id=wx.ID_REDO, enable=text.CanRedo()) menu_edit.Enable(id=wx.ID_CUT, enable=text.CanCut()) menu_edit.Enable(id=wx.ID_COPY, enable=text.CanCopy()) menu_edit.Enable(id=wx.ID_PASTE, enable=text.CanPaste()) def menu_handler(event): id = event.GetId() if id == MENU_FILE_CLOSE: exit_app() return elif id == wx.ID_UNDO: text.Undo() elif id == wx.ID_REDO: text.Redo() elif id == wx.ID_CUT: text.Cut() elif id == wx.ID_COPY: text.Copy() elif id == wx.ID_PASTE: text.Paste() update() #============================================================================== # 処理 #------------------------------------------------------------------------------ if __name__ == "__main__": app = wx.App() # 多重起動チェック instance_name = u"%s-%s" % (app.GetAppName(), wx.GetUserId()) instance = wx.SingleInstanceChecker(instance_name) if instance.IsAnotherRunning(): exit() frame = wx.Frame(None, wx.ID_ANY, "Markdown test", size=(800, 600)) menu_bar = wx.MenuBar() menu_file = wx.Menu() menu_file.Append(MENU_FILE_CLOSE, u"閉じる\tCtrl+Q") menu_bar.Append(menu_file, u"ファイル") menu_edit = wx.Menu() menu_edit.Append(wx.ID_UNDO, u"取り消し\tCtrl+Z") menu_edit.Append(wx.ID_REDO, u"やり直し\tCtrl+Shift+Z") menu_edit.AppendSeparator() menu_edit.Append(wx.ID_CUT, u"カット\tCtrl+X") menu_edit.Append(wx.ID_COPY, u"コピー\tCtrl+C") menu_edit.Append(wx.ID_PASTE, u"ペースト\tCtrl+V") menu_bar.Append(menu_edit, u"編集") frame.Bind(wx.EVT_MENU, menu_handler) frame.SetMenuBar(menu_bar) sw = wx.SplitterWindow(frame, -1, style=wx.SP_LIVE_UPDATE) pane1 = wx.Panel(sw, -1) text = stc.StyledTextCtrl(pane1, wx.ID_ANY) text.SetMarginType(1, stc.STC_MARGIN_NUMBER) text.SetMarginWidth(1, 30) text.StyleSetSpec(stc.STC_STYLE_LINENUMBER, "fore:#999999") text.SetLexer(stc.STC_LEX_MARKDOWN) text.Bind(wx.EVT_CHAR, char_handler) pane1_sizer = wx.BoxSizer() pane1_sizer.Add(text, 1, wx.EXPAND, 10) pane1.SetSizer(pane1_sizer) pane2 = wx.Panel(sw, -1) browser = wx.html2.WebView.New(pane2) pane2_sizer = wx.BoxSizer() pane2_sizer.Add(browser, 1, wx.EXPAND, 10) pane2.SetSizer(pane2_sizer) sw.SetSashGravity(0.5) sw.SplitVertically(pane1, pane2) timer = wx.Timer(frame) frame.Bind(wx.EVT_TIMER, timer_handler, timer) frame.Bind(wx.EVT_UPDATE_UI, update_ui_handler) frame.Bind(wx.EVT_CLOSE, close_handler) frame.Show() app.MainLoop()