React初心者のためのreact-routerの使い方
2015年1月、React ConferenceにてReact.jsでネイティブアプリが作れるようになると発表された。この発表を受けて最近何かと話題のReact.jsだが、リリースされたのは2013年であり、2009年にリリースされたAngularJSや、2010年のBackbonejsなどの他のjavascriptフレームワークに比べると新しく、まだネットに日本語の情報があまりない。ということで、React.jsのデファクトルーティングライブラリであるreact-routerの使い方についてまとめてみた。
目次
1.そもそもreact-routerとは2. react-routerのインストール方法
3. react-routerの使用例
4. react-routerのコンポーネント一覧
5. Routerのrunメソッド
1. そもそもreact-routerとは
react-routerは、React.jsのデファクトのルーティングライブラリである。Ember.jsのrouting APIを基に作られていて、ヘッダー、サイドバー、フッターなどが複雑にネストされたビューのルーティングの設定を容易にしてくれる。なぜ容易になるのかというと、ビューのコンポーネント(ヘッダー、サイドバー、フッター等)のネスト構造と同じように、ルーティングの設定もネスト構造で定義することで、両者の関係性が単純化されるからである。
2. react-routerのインストール方法
それでは、さっそくreact-routerを触ってみよう。インストール方法はいたって簡単で、パソコンにnode.jsさえインストールされていれば、
npm install react-router
でインストールできる。
また、以下説明する例を試すにはjsファイルの先頭において以下をrequireしなければならない。
var Router = require('react-router'); var Route = Router.Route; var NotFoundRoute = Router.NotFoundRoute; var DefaultRoute = Router.DefaultRoute; var Link = Router.Link; var RouteHandler = Router.RouteHandler;
これは、CommonJSという、JavaScriptで様々なアプリケーションを作るための標準仕様に則っている。
3. react-routerの使用例
では、実際にどのように使われているのか。Dashboard・Inbox・Calenderの3つのページからなる、小さなWEBアプリケーションを例に説明する。以下の図が、そのアプリケーションのUIの設計図であり、全てのページで共通のヘッダーが使われるとする。
var Header = React.createClass({ render: function () { return ( <header> <ul> <li><a href="/">Dashboard</a></li> <li><a href="/inbox">Inbox</a></li> <li><a href="/calendar">Calendar</a></li> </ul> Logged in as Jane </header> ); } }); var DashboardRoute = React.createClass({ render: function () { return ( <div> <Header/> <Dashboard/> </div> ); } }); var InboxRoute = React.createClass({ render: function () { return ( <div> <Header/> <Inbox/> </div> ); } }); var CalendarRoute = React.createClass({ render: function () { return ( <div> <Header/> <Calendar/> </div> ); } }); otherRouter.route('/', function () { React.render(<DashboardRoute/>, document.body); }); otherRouter.route('/inbox', function () { React.render(<InboxRoute/>, document.body); }); otherRouter.route('/calendar', function () { React.render(<CalendarRoute/>, document.body); });
見て分かるように、この場合各コンポーネントで共通のヘッダーを呼びださなければならず、コードの重複が増えてしまう。
これに対しreact-routerを使うと、ルーティングを定義するコードは以下のようになる。
var Router = require('react-router'); var DefaultRoute = Router.DefaultRoute; var Link = Router.Link; var Route = Router.Route; var RouteHandler = Router.RouteHandler; var App = React.createClass({ render: function () { return ( <div> <header> <ul> <li><Link to="app">Dashboard</Link></li> <li><Link to="inbox">Inbox</Link></li> <li><Link to="calendar">Calendar</Link></li> </ul> Logged in as Jane </header> //<RouteHandler/>コンポーネントをrenderすることで、 //ルーティングで定義された全てのアクティブな子コンポーネントを呼び出す。 <RouteHandler/> </div> ); } }); //アプリケーションのurlの階層構造をネストされた<Route/>タグで定義し、 //各<Route/>のhandlerにReactのコンポーネントを紐付け、 //render時には、アクティブな<Route/>のhandlerがコンポーネントを呼び出し、 //それらが融合されて表出される。 var routes = ( <Route name="app" path="/" handler={App}> <Route name="inbox" handler={Inbox}/> <Route name="calendar" handler={Calendar}/> <DefaultRoute handler={Dashboard}/> </Route> ); //React RouterはリクエストされたURLに最も深くマッチする<Route/>を探し出し、 //その木構造の中での枝に含まれる全ての<Route/>をアクティブにする。 Router.run(routes, function (Handler) { React.render(<Handler/>, document.body); });
例として挙げたアプリケーションが単純なUIなため、コード量としてはさほど変わってはいないが、react-routerを使用することで、一箇所でルーティングを管理することが可能となり、ネストされたUIを作るときの生産性、保守性が上がる。また、ルーティングを設定しないとビューが表出されないことから、URLの構造を先に考えつつ開発を行う事ができる。これらのメリットは、UIが複雑になればなるほど役立つものとなる。
4. react-routerのコンポーネント一覧
2. Link
4. DefaultRoute
5. NotFoundRoute
6. Redirect
8. Navigation
1. RouteHandler
<RouteHandler/>は、その子要素のアクティブな <Route />のハンドラーを呼び出す。
2. Link
<Link >はtoオプションで指定したnameの <Route >のハンドラーを呼び出す。
// このようなルーティングの設定があった場合 <Route name="user" path="/users/:userId"/> // 以下のリンクコンポーネントで、上記の<Route/>のハンドラーを呼び出せる。 <Link to="user" params={{userId: "123"}}/>
paramsオプションに対しては、渡したいRouteのpathのダイナミックセグメントと一致するname,valueのオブジェクトを渡す。
//ダイナミックセグメントとは、pathの一部で:から始まるId等を指定している箇所のこと <Route name="user" path="/users/:userId"/> //このRouteの場合、:userIdの部分を示す。
3. Route
<Route handler={App}> <Route name="about" handler={About}/> //pathが指定されていない場合、pathはnameから自動的に補完される。 //この場合<Route name="about" path="about" handler={About}/>となる。 <Route name="users" handler={Users}> <Route name="user" handler={User} path="/user/:id"/> </Route> </Route>
<Route >はそのネスト構造によりアプリケーションのルーティングを定義しとhandlerでviewの階層構造を定義する。
<Route />のオプション
path: ドメイン以下のurl。未定義の場合、nameから定義され、nameも未定義の場合、デフォルトで'/'になる。
handler: routeがアクティブなときに呼び出されるコンポーネント。
4. DefaultRoute
<DefaultRoute>は親のrouteのpathと完全に一致する時アクティブになる。
<Routes> <Route path="/" handler={App}> <!-- pathが'/'の時、以下の<DefaultRoute handler={Home}/>がアクティブになる。 --> <DefaultRoute handler={Home}/> <Route name="about" handler={About}/> <Route name="users" handler={Users}> <Route name="user" handler={User} path="/user/:id"/> <!--pathが'/user'のとき、以下の <DefaultRoute handler={Home}/>がアクティブになる。--> <DefaultRoute name="users-index" handler={UsersIndex}/> </Route> </Route> </Routes>
5. NotFoundRoute
<lNotFoundRoute>は、リクエストされたurlが親の<Route/>のpathとマッチするのに、そのいずれの子<Route/>ともマッチしない場合にアクティブになる。エラーページの作成等に使える。
<Route path="/" handler={App}> <Route name="course" path="course/:courseId" handler={Course}> <Route name="course-dashboard" path="dashboard" handler={Dashboard}/> <!-- `/course/123/foo` というurlがリクエストされた場合、 以下の <NotFoundRoute />がアクティブになる。--> <NotFoundRoute handler={CourseRouteNotFound} /> </Route> <!-- `/flkjasdf`というurlがリクエストされた場合、 以下の <NotFoundRoute />がアクティブになる。 --> <NotFoundRoute handler={NotFound} /> </Route>
6. Redirect
<Redirect>は、fromオプションで設定したpathへのリクエストがあったときに、toオプションで指定したpathにリダイレクトするようにする。
<!-- 開発中にurlの名称等を変更したくなった場合に使える。 `/get-in-touch`というpathにリクエストを送ると 、 `/contact`というpathをrenderするようにする。 --> <Route handler={App}> <Route name="contact" handler={Contact}/> <Route name="about-user" path="about/:userId" handler={UserProfile}/> <Route name="course" path="course/:courseId"> <Route name="course-dashboard" path="dashboard" handler={Dashboard}/> <Route name="course-assignments" path="assignments" handler={Assignments}/> </Route> <!-- `/get-in-touch` -> `/contact` --> <Redirect from="get-in-touch" to="contact" /> </Route>
7. State
アクティブなparams,query,routeの情報を必要とするコンポーネントのためのmixin。ダイナミックセグメントを含む<Route>のhandlerが呼び出すコンポーネント等で使われる。
// route <Route name="user" path="user/:name" handler={User} /> // handler var User = React.createClass({ //mixinは、mixinsに配列で渡す。 mixins: [ Router.State ], render: function () { //アクティブなparamsの情報を取得している。 var name = this.getParams().name; return ( <div> <h1>{name}</h1> </div> ); } });
<State />のインスタンスメソッド
getParams: アクティブなparamsを返す。
getQuery: アクティブなqueryを返す。
5. Routerのrunメソッド
最後にRouterのrunメソッドについて紹介する。runメソッドは、リクエストされたURLとマッチするrouteを特定し、それらのhandlerに紐づくコンポーネントをラップし、コールバックにhandlerとstateを引き渡す。
Router.run(routes, [location,] callback(handler, state)) //第二引数のHistoryLocationによって、HTML5のhistory APIを使えるようになる //第三引数のコールバック関数である無名関数の第一引数はHandlerで、 //マッチした<Route/>の全てのコンポーネントがラップされている Router.run(routes, HistoryLocation, function (Handler) { // whenever the url changes, this callback is called again React.render(<Handler/>, document.body); });
参照URL
以上
AngularJSのディレクティブについて
明日AngularJSのハッカソン(AngularJS ハッカソン - AngularJS Japan User Group | Doorkeeper)に参加することになったので、AngularJSを一から勉強してみることにした。そこで今回は、AngularJSのディレクティブについて解説していきたい。
1. AngularJSのディレクティブとは
AngularJSのディレクティブとは、双方向バインドを実現するための仕組みである。
AngularJSで言うところの双方向バインドでは、具体的に以下の2つの処理が行われる。
1.Model(=scope)の変更をView(=DOM)へ反映する
2.Viewの変更時にscopeの値を変更する
具体的に、双方向バインドは、AngularJSのHTMLコンパイラが、DOM要素(クラス、属性、HTMLタグ、コメント等)からディレクティブを検出し、そこに対応するスクリプトを埋め込むことで実現される。
AngularJSには標準で組み込まれているディレクティブが多数ある。
例えば、テキストボックスの入力値とscope.nameを紐付ける場合、htmlに下記を記述するだけで、ユーザの入力値がscopeへ即時反映される。
<input type="text" ng-model="name" /> <span>{{name}}</span>
他にも、ngBind、 ngRepeat や ngClassなどのディレクティブがある。詳しくは、こちらの標準ディレクティブ一覧を参照して頂きたい。(https://docs.angularjs.org/api/ng/directive)
2. カスタムディレクティブ
実装したい機能が標準のディレクティブを使って出来ない場合は、自分で自由にディレクティブを作る事ができる。
これをカスタムディレクティブと言う。以下、作る際の注意点を二つ挙げる。
1.ディレクティブの定義には、angular.moduleのdirective関数を利用すること
2.ディレクティブをスクリプトファイルで定義する際はキャメルケースを用いるが、テンプレートで利用する時はハイフン繋ぎ(アンダースコア、コロンでも良い)にすること
以下、簡単なカスタムディレクティブを作ってみた。例としてあまり適切でないかもしれないが、下記を実行すれば、「初めてのディレクティブ」が表示されたはずだ。
//myModule.js var myModlue = angular.module('myModlue', []); myModule.directive('firstDirective', function(){ return { template: '<span>初めてのディレクティブ</span>' }; });
<!doctype html> <html ng-app="myModule"> <head> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="myModule.js"></script> </head> <body> <div first-directive></div> </body> </html>
3. ディレクティブのオプションでできることまとめ
カスタムディレクティブには、様々なオプションが用意されている。以下、それぞれのオプションでできることを一覧で示す。
なお、各オプションの詳しい解説は$compile | AngularJS 1.2 日本語リファレンス | js STUDIOを参照していただきたい。
1. ディレクティブ同士の優先順位を指定する
2. 新しいscopeをディレクティブ内で指定する
3. ひとつのディレクティブで複数のDOM要素を指定する
4. 指定するディレクティブよりpriorityの低いディレクティブの適用を外す
5. ディレクティブのscopeをcontrollerで使用可能にする
6. コントローラのコンストラクタ関数を定義する
7. 他のディレクティブで生成されたcontrollerを使う
8. ディレクティブがもつtemplate内で使えるディレクティブのcontrollerのエイリスを指定する
9. templateでのディレクティブの指定方法を定義する
10. ディレクティブがもつtemplateをhtml文字列として指定する
11. ディレクティブがもつtemplateをhtmlファイルのパスで指定する
12. templateでディレクティブを指定する際、子要素を挿入可能にする
13. scopeとtemplateを結びつける処理を指定する(compile)
14. scopeとtemplateを結びつける処理を指定する(link)
1. ディレクティブ同士の優先順位を指定する
priority: 10 //数字が大きければ大きいほど、優先度が高く先にコンパイルされる。
2. 新しいscopeをディレクティブ内で指定する
scope: true //true にすることで、新しいscopeが作成される scope: { //hashオブジェクトを渡すことで、親scopeを継承しない'隔離scope'が作成される myDirective: '=', // '='は、双方向バインディング onChange: '&', // '&'は、指定した値を親scopeのcontextで実行する title: '@' // '@'は、ローカル(このディレクティブの)scopeの値をDOMに追加する }
3. ひとつのディレクティブで複数のDOM要素を指定する
multiElement : true
4. 指定するディレクティブよりpriorityの低いディレクティブの適用を外す
terminal : true //priorityとともに使う。trueに設定した場合、そのディレクティブよりpriorityが小さいディレクティブは実行されない。
5. ディレクティブのscopeをcontrollerで使用可能にする
bindToController : true
”隔離scope"がディレクティブで作成されて、controllerAsが使われているとき、bindToController : trueにすると、その作成された"隔離scope"が、controllerAsで指定されたcontrollerがインスタンス化された時に使用可能になる。
6. コントローラのコンストラクタ関数を定義する
controller : function($scope) { var panes = $scope.panes = []; $scope.select = function(pane) { angular.forEach(panes, function(pane) { pane.selected = false; }); pane.selected = true; };
7. 他のディレクティブで生成されたcontrollerを使う
require : 'sample-directive' //ディレクティブ名を指定する
8. ディレクティブがもつtemplate内で使えるディレクティブのcontrollerのaliasを指定する
controllerAs : 'sample-controller'
9. templateでのディレクティブの指定方法を定義する
restrict : 'E' //E:タグ ex)<sample-directive> //他にも、 // A : 属性 ex) <div sample-directive> // C : クラス ex) <div class="sample-directive;"> // M : コメント ex) <!-- directive: sample-directive -->
10. ディレクティブがもつtemplateをhtml文字列として指定する
template : '<div red-on-hover>{{delete_str}}</div>'
11. ディレクティブがもつtemplateをhtmlファイルのパスで指定する
templateUrl : 'directive.html' //loadは非同期で行われる
12. templateでディレクティブを指定する際、子要素を挿入可能にする
transclude : true
13. scopeとtemplateを結びつける処理を指定する(compile)
compileと14のlinkオプションは同時に使うことが出来ない。詳しい使い方については、JavaScript - AngularJSのDirectiveを理解する. - Qiitaを参照頂きたい。
compile : function compile(tElement, tAttrs, transclude) { return { pre: function preLink(scope, iElement, iAttrs, controller) { ... }, post: function postLink(scope, iElement, iAttrs, controller) { ... } } }
14. scopeとtemplateを結びつける処理を指定する(link)
link : function link(scope, element, attrs) { var format, timeoutId; function updateTime() { element.text(dateFilter(new Date(), format)); } scope.$watch(attrs.myCurrentTime, function(value) { format = value; updateTime(); }); element.on('$destroy', function() { $interval.cancel(timeoutId); }); // start the UI update process; save the timeoutId for canceling timeoutId = $interval(function() { updateTime(); // update DOM }, 1000); }
参照URL
JavaScript - AngularJSのDirectiveを理解する. - Qiita
https://docs.angularjs.org/api/ng/service/$compile
https://docs.angularjs.org/guide/directive
JavaScript - AngularJSのdirectiveとは - Qiita
以上
jQueryのPromiseとDeferredとは
javascriptで非同期処理がからむと、コールバックが乱立してしまい、エラーに対する例外処理を正確に書くことが難しくなった経験はないだろうか。今回は、JavaScriptで複数の連続する非同期処理を上手く扱うためのjQueryの標準モジュールであるjQuery.Deferredについて、その概念や使い方を解説していく。
1. jQuery.Deferredとは
jQuery.Deferredは、複数の連続する非同期処理を単純で分かり易く記述できるようにすると述べたが、それはどのようにして行われるのか。それは、複数の非同期処理ひとつひとつにPromiseオブジェクトを割り当て、それを伝播させていくことで実現される。それでは、Promiseオブジェクトとは何か。Promiseオブジェクトは、割り当てられた非同期処理が成功しているか否か等の状態の情報を持つオブジェクトである。その状態の種類は3つあり、Deferred.state()で確認することができる。
Promiseオブジェクトの状態(Deferred.state()の返り値)
1. pending : 処理が未完了。
2. resolved : 処理が成功
3. rejected : 処理が失敗
Promiseオブジェクトは、生成されたときはpendingの状態で、割り当てられた処理が完了した際に、処理が成功した場合はresolved、処理が失敗した場合はrejectedに状態に変わる。そして、resolvedまたはrejectedの状態のPromiseオブジェクトがreturnされたときに実行されるコールバックは、以下の2つのメソッドで指定できる。(なお、Promiseオブジェクトの状態の変化やPromiseオブジェクトのreturnは、ajaxやGETjsonなどのjQueryの標準の非同期処理には実装されているが、それ以外の非同期処理においては、手動で実装しなければならない。)
1.状態がresolvedのPromiseオブジェクトがreturnされたときのコールバックは、.done()メソッドで呼び出される。
2.状態がrejectedのPromiseオブジェクトがreturnされたときのコールバックは、.fail()メソッドで呼び出される。
さて、Promiseオブジェクトは、Deferredオブジェクトに内包されるものである。よって、Deferredオブジェクトのresolveメソッドを呼び出すと、内包するPromiseオブジェクトの状態がresolvedになり、Deferredオブジェクトのrejectメソッドを呼び出すと、内包するPromiseオブジェクトの状態がrejectedになる。
//1秒後にHello!を出力するDeferred対応関数。必ずresolveする function delayHello() { var d = new $.Deferred; //Deferredオブジェクトを生成 setTimeout(function(){ console.log('Hello!'); d.resolve(); //Promiseオブジェクトの状態をresolvedに変更 }, 1000); return d.promise(); //Promiseオブジェクトをreturn } //1秒後にエラーを発生させるDeferred対応関数。必ずrejectする function delayError() { var d = new $.Deferred; //Defferedオブジェクトを生成 setTimeout(function(){ d.reject('Error!!'); //Promiseオブジェクトの状態をrejectedに変更 }, 1000); return d.promise(); //Promiseオブジェクトをreturn } //.done()と.fail() var promise = delayHello(); promise.done(function(){ /* resolvedで実行 */ }); //.done()メソッドによるコールバックが実行される promise.fail(function(e){ /* rejectedで実行 */ }); //本題とは、関係ないがfailのメソッドで定義されているコールバック(function(e){ /* rejectedで実行 */ })において、関数の引数に引き渡されているeは、Errorオブジェクトである。
2. .then()メソッドと.when()メソッド
ここで、jQuery.Deferredの一番の特徴とも言える、.then()と$.when()について解説する。これらは、複数の連続する非同期処理の連結、その例外処理を驚くほど簡単にする。まず、.then()メソッドは、Promiseオブジェクトを返す非同期処理に対して呼び出すことで、非同期処理が成功または失敗した場合の両方に対するコールバックを指定できる。.then()メソッドの第一引数に成功した場合、第二引数に失敗した場合の処理を記述する。この.then()メソッドは、.done()メソッドや.fail()メソッドと違い、新たにPromiseオブジェクトを生成し、returnするので、非同期処理を連続させことができる。
delayHello() .then(function(){ /* resolvedで実行 */ }, function(e){ /* rejectedで実行 */ } //↓これは直列連結の書き方として間違い delayHello().done(delayHello).done(delayHello); //1つめのdelayHello()がPromiseオブジェクトをreturnしたタイミングで、ふたつのコールバックが同時に呼び出されることになる
また、$.when()を使うことで、複数の非同期処理を並行して実行することができる。$.when()は、併記したDeferredの非同期処理の全てが、resolvedのPromiseオブジェクトをreturnした場合にのみ、statusがresolvedの新たなPromiseオブジェクトをreturnする。また、併記した非同期処理の中で、ひとつでもrejectedのPromiseオブジェクトをreturnするものがあった場合は、statusがrejectedの新たなPromiseオブジェクトをreturnする。$.when()も、新たにPromiseオブジェクトを生成し、returnしているので、非同期処理を連続して書くことができる。
$.when(delayHello(), delayHello(), delayHello()) .done(/*全てresolvedで実行*/); //全てresolvedのPromiseオブジェクトを返すので実行される
.then()と$.whenによる非同期処理の連結は、その処理の順番を容易に変えることができることも大きなメリットである。
3. 連続する非同期処理の関数化
jQuery.Deferredは、さらに連続する非同期処理を関数化してまとめることができる。これによって、コードの可読性が増し、また再利用が可能になる。
以下のように、複雑な処理も、関数化することによって格段に、読みやすく、再利用しやすいコードになる。
delayHello() .then(delayHello) .then(function(){ return $.when(delayHello(), delayHello(), delayHello()); }) .then(delayHello) .then(function(){ return $.when(delayHello(), delayHello(), delayHello()); }) .then(delayHello);
関数化すると、
//一部分だけ抜き出して関数化 function delayHelloParallel() { return $.when( delayHello(), delayHello(), delayHello() ) .then(delayHello); } //delayHelloParallelを使って少々簡略化 delayHello() .then(delayHello) .then(delayHelloParallel) .then(delayHelloParallel)
4. jQuery.Deferredを使う場面
それでは、どんな時にjQuery.Deferredを使うべきなのか。
それは、JavaScript の非同期処理を、順序立てて、連続して、複数回実行したいときである。
例えば、キーワードとして検索した住所の気象情報を取得する処理をjQueryで実装したい場合、
1. Yahoo!ジオコーダAPIで、キーワードとして検索した住所の緯度経度を取得し、
2. 1のAPIのレスポンスが返ってき次第、その緯度経度をもとに、気象情報APIで、気象情報を取得する
必要がある。この際、1の処理が終わってから、2の処理と、順番に処理を実行する必要があり、jQuery.Deferred を使うことで、この一連の処理を簡単に、分かり易く、書くことができる。
jQueryの代表的な非同期処理であるajaxについては、以下を参照して頂きたい。
参照URL
爆速でわかるjQuery.Deferred超入門 - Yahoo! JAPAN Tech Blog
jQueryのDeferredとPromiseで応答性の良いアプリをー基本編 | ゆっくりと…
結局jQuery.Deferredの何が嬉しいのか分からない、という人向けの小話 - Qiita
以上
RailsのPaperclipについてまとめてみた
Railsを使って、SNS上で共有された画像を活用したWEBサービスを作ろうと思い、画像ファイルの管理を担うgem、Paperclipについて調べてみた。
目次
1. Paperclipとは2. Paperclip::ClassMethods#has_attached_file
3. 添付ファイルに対するvalidation
4. 添付ファイルの削除、論理削除
5. s3の設定
6. Paperclipと他のgemの比較
1. Paperclipとは
thoughtbot/paperclip · GitHub
Paperclipは、RailsのActiveRecord用のライブラリである。Railsのアプリケーションで、ユーザーがアップロードした画像等のファイルについての煩雑な設定、すなわち、保存や削除などの設定を簡単に行うことができるようになる。
Paperclipは、ImageMagick(http://www.imagemagick.org/)と共に使うことを前提としていて、アップロードした画像を縦横比そのままでリサイズしたり、長方形の画像を、中央を起点にして正方形に切り取ったりすることができる。以下、Paperclipの使い方について説明する。
2. Paperclip::ClassMethods#has_attached_file
まず、migrationで生成した添付ファイルの保存先(attachment)へのパスをモデルに伝えるメソッド、has_attached_fileについて説明する。
has_attached_file( name, options = {} ) #以下はsample、モデルに記述する has_attached_file :sample_image, :url => "/:class/:attachment/:id/:style_:filename", :styles => { :normal => "100x100#", :large => '200x200#' }, :storage => :s3
このクラスメソッドは、前述のとおり、呼び出されたクラスに対して添付ファイルが保存されるべきパスを伝えるものである。添付ファイルを管理するPaperclip::Attachment オブジェクトを返す。Railsで、他のカラムに属性を定義するのと同じように添付ファイルについても、属性のような情報を定義できるようにした。新しいファイルがアップロードされると、サムネイルが作られ、saveメソッドが呼び出されることで保存される。以下、このメソッドのオプションでできることを一覧で示す。
has_attached_fileのoptionsでできること一覧
1. 添付ファイルを保存するパスを指定
2. 添付ファイルがない場合のデフォルト画像のパスを指定
3. サムネイルのスタイルの指定
4. デフォルト画像のスタイルの指定
5. ファイルを添付出来ない場合のコマンドラインエラーを表示するか否かの指定
6. どのように画像を変換するか指定
7. ファイルが保存されるストレージの指定
1. 添付ファイルを保存するパスを指定
:url => "/:class/:attachment/:id/:style_:filename"
urlオプションに対して、ファイルの保存先を絶対パスで指定。ドメインまで指定する必要はない。デフォルトは “/system/:attachment/:id/:style/:filename”.
また、ブラウザで表示するURLを指定することも可能。その場合、以下のようにURLをpathオプションに渡す。
path: "#{Rails.root}/public/system/:class/:id/:attachment/:style.:extension"
2. 添付ファイルがない場合のデフォルト画像のパスを指定
default_url: "/images/default_:style_avatar.png" User.new.avatar_url(:small) # => "/images/default_small_avatar.png"
default_urlオプションにパスを渡す。デフォルトは、“/:attachment/:style/missing.png”
3. サムネイルのスタイルの指定
:styles => { :normal => "100x100#", :large => '200x200#' }
このstylesオプションに、スタイル名がkeyで、指定したいスタイルがvalueのhashを渡す。スタイルの指定方法については、ImageMagickのサイトに記載されている(www.imagemagick.org/script/command-line-options.php#resize). ImageMagickのスタイルの設定に加え、さらに “#” オプションが追加されていて、 これは指定されたサイズに最大限fitするように画像をリサイズし、中央を起点に不要な部分を切り取るという設定である。(example: “50x50#”)、 デフォルトでは、サムネイルが生成されない.
4. デフォルトのURLで使われるサムネイルのスタイル名の指定
has_attached_file :avatar, :styles => { :normal => "100x100#", :large => '200x200#' }, :default_style => :normal user.avatar.url # => "/avatars/23/normal_me.png"
default_styleオプションに、stylesオプションで指定したスタイル名を渡す。つまるところ、ユーザーがファイルをアップロードする際に、スタイル名を指定しなかった場合に適用されるスタイルのスタイル名を指定できるオプションである。
5. ファイルを添付出来ない場合のコマンドラインエラーを表示するか否かの指定
whiny : false
whinyオプションに真偽値を渡す。デフォルトはtrue。コマンドラインエラーでアップロードファイルをpost_process出来ない場合、エラーを表示するか指定できる。
6. どのように画像を変換するか指定
has_attached_file :avatar, :styles => { :large => "300x300", :negative => "100x100" } :convert_options => { :all => "-strip", :negative => "-negate" }
convert_optionsオプションに、スタイル名がkeyで、画像の変換オプションがvalueのhashを渡す。画像の変換オプションの例として、'-strip' オプションは、画像からExif dataを取り除くために使われ、 “-depth 8”オプションは、ビット深度を指定するのに使われる。詳しくは、ImageMagickの convert documentation(ImageMagick: Command-line Tools: Convert) に記載。 allをkeyとするhashのvalueにoptionを指定することで、作成される全てのスタイルのサムネイルに適用するオプションを指定できる。
7. ファイルが保存されるストレージの指定
storage : :s3
storageオプションに、ストレージ名を渡す。 ストレージの選択肢としては 、:filesystem または、 :s3がある。 デフォルトは、 :filesystem.
3. 添付ファイルに対するvalidation
Paperclip::ClassMethods#validates_attachment_content_type
このクラスメソッドは、添付ファイルのファイル形式に対して、ActiveRecordのようなvalidationをかけることができる。
validates_attachment_content_type :sample_image, content_type: ["image/jpeg", "image/gif", "image/png"], message: 'ファイル形式が不正です。'
content_typeオプションに、許可するファイル形式を一つまたは、複数を配列で渡す。各typeは、String または a Regexpで指定可能. IEは、予期せぬタイプでファイルがアップロードされる場合があるから注意が必要。たとえば、JPEGはimage/pipeg、PNGはimage/x-pngでアップロードされるので、マッチする時気をつけよう。デフォルトは全てのタイプを許可。
messageオプションに、アップロードできないcontent type のfile がアップロードされた時に表示されるメッセージを渡す。
Paperclip::ClassMethods#validates_attachment_presence
このクラスメソッドは、添付ファイルの有無に対して、ActiveRecordのようなvalidationをかけることができる。
validates_attachment_presence :sample_image
Paperclip::ClassMethods#validates_attachment_size
このクラスメソッドは、添付ファイルのサイズに対して、ActiveRecordのようなvalidationをかけることができる。
validates_attachment_size :sample_image, :less_than=>1.megabyte
inオプションで、許可するファイルサイズの範囲を指定できる。
less_thanオプションで、許可する最大のファイルサイズを指定できる。
greater_thanオプションで、許可する最小のファイルサイズを指定できる。
messageオプションで、表示するエラーメッセージを指定できる。
添付ファイルの削除、論理削除
添付ファイルの削除
添付ファイルを削除するには、attributeをnilにして、saveすると削除される。
@user.avatar = nil @user.save
また、添付ファイルが紐づくオブジェクトが削除されると、添付ファイルも削除される。
添付ファイルの論理削除
acts_as_paranoid, paranoia 等の論理削除用のgemと連動して、論理削除が行えるように、Paperclipではオプションが用意されている。
has_attached_fileのメソッドのoptionで、preserve_filesをtrueにすれば、添付ファルの論理削除が正常に行われる.
has_attached_file :some_attachment, { :preserve_files => "true", }
s3の設定
アマゾンの S3 に保存する際は、まず aws-sdk というgem を使う。
#Gemfile に、以下の行を追加。 gem 'aws-sdk', '~> 1.5.7'
その後、has_attached_file のオプションで、s3を指定。詳しくは、Paperclip::Storage::S3 documentation(Module: Paperclip::Storage::S3 — Documentation for paperclip (4.2.0))。
Paperclipと他のgemの比較
Paperclipと他のgemの比較については、Paperclip と CarrierWave を結構マジメに比較してみた - 彼女からは、おいちゃんと呼ばれています
参照サイト:
Module: Paperclip::ClassMethods — Documentation for paperclip (2.3.8)
RailsでPaperclipを使ってみたメモ [俺の備忘録]
以上
jQueryのajaxについてまとめてみた
ユーザーにとって、ストレスレスで操作性の高いWEBアプリケーションを作るのにajaxは欠かせない技術である。今日は、jQueryのajaxについてまとめてみた。
1. そもそもajaxとは?
ajaxとは、WEBブラウザに実装されているJavaScriptのHTTP通信機能使って、Webページのリロードを伴わずにサーバーとデータのやりとりを行う処理である。ajaxは、asynchronous JavaScript + XMLの略である。asynchronousは「非同期」という意味であるが、ajaxは、非同期・同期両方の処理方式に対応していて、字面とは違い特徴は画面遷移を伴わない通信手段であるということに限定される。ユーザの操作と並行して、画面遷移なしでサーバと通信を行うことで、サーバの存在を感じさせないような、動的なWebアプリケーションを実現することができる。そんなajaxには、ブラウザの互換性やコードとデザインの分離を簡単に実現するいくつかのフレームワークがある。今日は、その中から、jQueryのajaxについて説明していきたい。jQueryでは、
$.ajax();
で、簡単にajax処理を実行することができる。
2. jQueryのajaxメソッドのオプションでできること一覧
jQueryのajaxメソッドは、複雑なajax処理を実装するための様々なオプションが設けられている。オプションは、以下のようにajaxメソッドの引数にhashで渡す。$.ajax({ type : 'GET', url : '/hoge/hoge.html', data: 'foo', dataType: 'json' });
まずは、このオプションでできることを紹介していきたい。以下、その一覧。
1. HTTPリクエストの種類を指定する
2. 送信するdataの内容を指定する
3. 処理方式を同期処理にする
4. サーバーから期待するレスポンスのデータ形式を指定する
5. ajaxリクエストを送る前のコールバックを指定する
6. HTTP認証が必要なときの設定を指定する
7. クロスドメイン処理を行う
8. 通信結果をcacheしない
1. HTTPリクエストの種類を指定する
//デフォルトは、'GET' type: 'POST' //HTTPリクエストの種類をpostにする
PUT, PATCH,DELETE等も指定することができるが、全てのブラウザでサポートされていないため、推奨されない。
2. 送信するdataの内容を指定する
//形式は、hash、string、またはarray. data : 'hoge'
3. 通信方式を同期通信にする
//デフォルトは、trueで、非同期通信。 async: false
そもそも同期通信とは、リクエストが送られている間、ブラウザ内で他のいかなるアクションも実行できない通信方式のことで、非同期通信とは、リクエストが送られている間も、他のアクションを実行できる通信方式のことである。
4. サーバーから期待するレスポンスのデータ形式を指定する
dataType : 'json' //併記して複数のデータ形式を期待することも可能 dataType : 'json text'
期待するレスポンスのデータ形式の種類としては、
・html
・text
・xml
・json
・script
・jsonp
が挙げられる。
この中で、jsonpとは、クロスドメインajax通信で扱うデータ形式で、取得するjsonを引数とする関数の形を取る。
//jsonデータ {'key' : 'value', 'key2' : 'value2'} //jsonpデータ(jsonを引数とする関数) getJSONP({'key' : 'value', 'key2' : 'value2'})
jsonpデータは、クロスドメインXMLHTTPRequestにおける、セキュリティーの問題を解決する。
5. ajaxリクエストを送る前のコールバックを指定する
beforeSend : function(jqXHRオブジェクト、settings) { 実行したい処理 }
例えば、9時〜17時の営業時間外の場合、ajaxの処理を実行しないようにするには、以下のように書ける。
// 通信前に時刻をチェックし、時間外である場合は通信を中止 beforeSend: function(xhr) { var d = new Date(); if (d.getHours() < 9 || d.getHours() > 17) { return false; } }
return false、または、jqXHR.abort()で、リクエストを中断することができる。
6. HTTP認証が必要な通信を行う
//usernameとpasswordの指定を行う username : 'user', password : 'password'
7. クロスドメイン処理を行う
//デフォルトはfalse crossDomain : true
クロスドメイン処理とは、その名の通り異なるドメイン間のXMLHTTPRequestのことである。
8. 通信結果をcacheしない
//デフォルトは、true cache : false
キャッシュとは、一度アクセスしたサイトのデータをブラウザで一時的に保管し、次回より同じページにアクセスした際の表示を速くする仕組みである。
ajax通信において、cacheをfalseにすべき処理とは一体どのようなものであろう。
3. jQueryのajax通信後の処理
次に、ajax通信後の処理について説明していきたい。jQueryでは、主に以下の3つのコールバックを用いて、ajax通信後の処理を指定する。
1. jqXHR.done(function( data, textStatus, jqXHR ) {});
→ajax通信成功後に呼び出される
2. jqXHR.fail(function( jqXHR, textStatus, errorThrown ) {});
→ajax通信失敗後に呼び出される
3. jqXHR.always(function( data|jqXHR, textStatus, jqXHR|errorThrown ) { });
→ajax通信完了後(成功後+失敗後)に呼び出される
ここで、jqXHRオブジェクトとは、XMLHTTPリクエストの通信の状態等の情報を持つオブジェクトのことである。
またtextStatusは、エラー情報を文字列化したものである。
以下、コールバックを使ったajax通信の例。
var jqxhr = $.ajax( "example.php" ) .done(function() { alert( "success" ); //通信が成功した場合、successを表示 }) .fail(function() { alert( "error" ); //通信が失敗した場合、errorを表示 }) .always(function() { alert( "complete" ); //通信が完了した場合、completeを表示 });
4. jQueryのDeffered
最後に、非同期処理を上手く扱うためのjQueryの標準モジュールであるjQuery.Defferedについて説明する。
これを使うことで、以下の3つの側面から非同期処理を扱いやすくなる。
1.同期処理を連結する際のコールバック地獄からの開放
2.エラー処理を上手く記述できる
3.一連の非同期処理を関数化して再利用しやすくできる
jQuery.Defferedについて詳しくは、以下の記事を参照して頂きたい。
参照サイト:
jQuery.ajax() | jQuery API Documentation
以上
Railsのコールバックについてまとめてみた
Railsのコールバックとは、一体何なんだろうか。今回は、Railsのコールバックの概要を解説する。
1.コールバックとは何か
Railsのコールバックは、オブジェクトの状態が変わるとき、すなわちオブジェクトが生成、更新、破壊されるときや、バリデーションを実行するときの前後に共通の処理を追加する仕組みである。これはモデル層で指定でき、処理を共通化することによって、モデルの一貫性を保て、またコード量を減らすことができる。
2. 2種類のコールバックの定義の仕方
モデルにて、コールバックを指定する方法は、2つある。
まず、最も一般的な方法がprivateでメソッドを切って、それを呼び出す方法。
class User < ActiveRecord::Base validates :login, :email, presence: true #validationの指定 before_validation :ensure_login_has_a_value #callbackの指定 protected def ensure_login_has_a_value if login.nil? self.login = email unless email.blank? end end end
次に、コールバックにブロックを渡す方法。
class User < ActiveRecord::Base validates :login, :email, presence: true before_create do self.name = login.capitalize if name.blank? end end
さらに、コールバックの定義を、オブジェクトのライフサイクル(オブジェクトの生成、更新、削除等)の中の一部の処理に関してのみ行うことができる。それは、以下のようにonオプションに指定する。
class User < ActiveRecord::Base before_validation :normalize_name, on: :create #createアクションにのみ、コールバックが適用される。 # :on に配列も引き渡せる。 after_validation :set_location, on: [ :create, :update ] protected def normalize_name self.name = self.name.downcase.titleize end def set_location self.location = LocationService.query(self) end end
3. 使用できるコールバック一覧
まず、オブジェクトのライフサイクル、すなわちオブジェクトの生成、更新、削除の各過程において使用可能なコールバックを、呼び出される順番に、以下並べる。
before_validation
↓
after_validation
↓
before_save
↓
around_save
↓
before_create
↓
around_create
↓
after_create
↓
after_save
before_validation
↓
after_validation
↓
before_save
↓
around_save
↓
before_update
↓
around_update
↓
after_update
↓
after_save
before_destroy
↓
around_destroy
↓
after_destroy
・オブジェクトがinitializeされたあと => after_initialize
オブジェクトに対して、newメソッドが呼ばれたときや、データベースからレコードがloadされたとき(データベースのレコードがインスタンス化されたとき)等、オブジェクトがinitializeされた後に処理を追加するコールバック。
・データベースからレコードがloadされたあと => after_find
ただし、after_initializeとafter_findが両方定義されているとき、after_findが先に呼び出される。
class User < ActiveRecord::Base after_initialize do |user| puts "after_initializeを呼び出しました" end after_find do |user| puts "after_findを呼び出しました" end end >> User.new after_initializeを呼び出しました => #<User id: nil> >> User.first after_findを呼び出しました after_initializeを呼び出しました => #<User id: 1>
・オブジェクトにtouchメソッドが呼び出されたあと => after_touch
オブジェクトのupdated_atカラムを現在時刻に更新するtouchメソッドが呼び出されるあとに処理を追加するコールバック。
参照URL:Active Record Callbacks — Ruby on Rails Guides
以上
Railsのmigarationについてまとめてみた
Railsでは、データベースにテーブルを作成したり、インデックスを追加したりするのに Active Record の Migration 機能を使う。今回は、Migrationについて、自分が分かりにくいと思った点を中心に解説していきたい。
目次
1. migrationとは何か2. rails migrationコマンド
3. change と up, down メソッドの使い分け
4. changeメソッドを使う動作と up, down メソッドを使う動作をひとつのファイルで行う
5. create_tableする際のcreated_at, updated_at カラムの生成方法
1.migrationとは何か
Active Record の migrationは、データベースの状態をRubyのDSLを用いて定義し、それらをcvsのようにversion管理するものである。すなわち、データベースへのテーブルやカラムの追加、削除を煩わしいSQL文ではなく、簡単なRubyのDSLで定義し、その内容をその経過とともに保存できるようにしたものである。そして、このRubyのDSLは、使用しているデータベースがMySQLでもSQLiteでも同じ記法で定義することができるため、データベースへのアプリケーションの依存を減らすことができる。Railsのmigrationでは、ひとつのmigrationファイルが、ひとつのversionに相当し、migrationファイルにデータベースの情報を定義していく。
2.rails migration コマンド
migrationファイルを作成する際には、rails migration コマンドを用いる。
bin/rails generate migration クラス名
コマンドで指定するクラス名には命名規則があり、それに従うとmigrationファイルの中身まで、コマンドで指定することができる。しかし、migration ファイルを生成した後に、そのファイルの中身を変更可能なため、そこまで意識する必要はない。
以下、rails migration コマンドと、それによって生成されるmigrationファイルの例を記述する。
#feedsテーブルを追加したい場合、ここではクラス名はCreateFeeds bin/rails generate migration CreateFeeds #生成されるファイル名 => db/migrate/年月日時分秒_create_feeds #生成されるファイルの中身 class CreateFeeds < ActiveRecord::Migration def change create_table :feeds do |t| t.timestamps end end end #feedsテーブルにcategory_id カラムを追加したい場合、ここではクラス名はAddCategoryIdToFeeds bin/rails generate migration AddCategoryIdToFeeds #生成されるファイル => db/migrate/年月日時分秒_add_category_id_to_feeds #生成されるファイルの中身 class AddCategoryIdToFeeds < ActiveRecord::Migration def change end end
3.change とup, downメソッドの使い分け
migrationクラスのオブジェクトには、change, up, downのメソッドがある。
まず、changeメソッドとup,downメソッドの使い分けの例を以下にあげる。
1.create_table をするとき ->changeメソッド
2.add_columnをするとき ->changeメソッド
3.remove_columnをするとき ->up, downメソッド
4.drop_tableをするとき ->up, downメソッド
ここで、changeメソッドを使うのは逆の動作が予測可能なときであり、up,downメソッドを使うのは逆の動作が予測不可能のときである。
したがって、create_tableやadd_columnのときは、changeメソッド、remove_columnやdrop_tableのときはup, downメソッドを用いる。
4.changeメソッドを使う動作と up, down メソッドを使う動作をひとつのMigrationファイルで行う
3.で、changeメソッドと、up, downメソッドの使い分けについて説明したが、ひとつのmigrationファイルでadd_columnやdrop_tableなど使うべきメソッドの違う複数の動作を行いたい場合は、changeメソッドでreversibleを用いると綺麗に定義できる。
#remove_columnなど、up, downメソッドで定義すべきものを、以下のようにreversible にブロックを渡す。 class ChangeMultipleColumsAndTables < ActiveRecord::Migration def change create_table :categories do |t| t.string :name end reversible do |dir| dir.up do remove_column :articles, :article_name end dir.down do add_column :articles, :article_name, :string end end add_column :users, :adress, :string rename_column :users, :email, :email_address end end
5.create_tableする際のcreated_at, updated_at カラムの生成方法
#create_tableに引き渡すブロック内でt.timestampと定義する class CreateCategories < ActiveRecord::Migration def change create_table :categories do |t| t.string :name t.text :description t.timestamps end end end
参照サイト:Active Record Migrations — Ruby on Rails Guides
以上