23

2013/05

Backbone.js読書会1日目

Backbone.js読書会1日目

Webも徐々にGUIアプリのような振る舞いが求められていく世の中になってきたし、技術的に可能になってきたと。クライアントでそんな振る舞いをしてくれてるのが現状javascriptです。僕たちはどんどんjavascriiptを利用して多種多様な振る舞いをしていかなければいけないけど、現状のjQueryでばんばん書いていくのにも限界がきてるんじゃないか。そのとき、外国で話題になっていたフレームワークをほそぼそと勉強しはじめ、今や業務にまで導入できることに成功しました。(まだ、いろいろ課題はありますが。。。)

そんなときに、満を持して洋書よりも早く日本の書籍がでてしまったので早速基本に振り返るがてらみんなで知識深めようという目的で読書会はじめました。

「Backbone.js ガイドブック」

Backbone.jsガイドブック
Backbone.jsガイドブック

はじめは概要なのですらっと。

一つ話題になったのは、基本は「.extend」を利用してインスタンス用のプロパティを作成していくのが基本だけど、クラス用のプロパティってどこで利用するケースがあるかってこと。

var Parent = Backbone.View.extend(
  {
    instance_p: 1,
    instance_m: function(){ return 'instance_m';}
  },
  {
    class_p: 2,
    class_m: function(){ return 'instance_m';}
  }
    
);

サンプルみただけだとふーんですよね。僕もいまのところふーんです。

ケースとしてはクラスプロパティにテンプレートをぶち込んでおくのがよさげなのかしらね。例えば、Viewが管理している内部でモーダルを管理しているとき。モーダルは基本一つなのでクラスプロパティであらかじめ作成しておけば使い回しがきくと。$(‘modal’)とかで毎回DOMを操作しなくて済む。

ふと思った訳です。

var Parent = Backbone.View.extend(
  {
    instance_p: 1,
    instance_m: function(){ return 'instance_m';}
  },
  {
    modal_template: null,
    class_m: function(){ this.modal_template = $('#modal').html()}
  }
    
);

こんなかんじ。

あと、なんだかんだ実装しながら確認してたら時間かかってしまったので1章の半分くらいで終了。バタリ。

さ。来週もすすめていきますよ。

追記

洋書は今月末にでるようだ。

Developing Backbone.js Applications
Developing Backbone.js Applications

20

2013/05

欲求不満フルリニューアルかけた。 現在、自分で全て管理している「欲求不満(www.frustration.me)」ですが、数年前 にリリースを行い( http://blog.nakajijapan.net/?p=2685 )、デザインのリニューアル、 機能追加が粛々と気の向くままに開発していましたが、ついに3年目にしてソース・デザイン全て リニューアルをかけました。構想は去年の12月から徐々に着手し始めていまして、年初めにはリニューアル できるものかなと安易にかんがえていたけど(仕様そんなに難しくないしねーてきなのり)なかなか 大変で一部機能を削ぎ落しての妥協リリースになってしまいました。。。無念です。あと、これ要らねーだろう っていうやつは消してますけど。

見た目は今時風にしたかったので、それっぽくしました。あとは自分の開発モチベーションあげたいので言語をRubyに変更しました。

内部的には結構細かいことしてまして。

  • デザインをTwitterBootstrapをベースにした
  • ZendFramework(PHP)からRails(Ruby)に対応
  • jsのフレームワークにBackbone.jsを導入
  • APIの検索周りの処理をGem化
  • ユーザの画像一式を全てS3で管理
  • Paasを導入してsqale.jpで管理するようにした

的なことしています。

デザイン

なんか自分で眺めててあきないようにしたくて、いろんなサイトから参考にしましておこぼれちょうだいしてます。 デザイナ不在なので慣れないコーディングを全て自分やることになりましたが大変勉強になりました。あとTwitterBootstrapがなかったらもっとCSSのコーディングが大変になっていたしそこそこのデザインに鳴らなかったと思います。

スクリーンショット 2013-05-16 10.20.30

ちなみに初期のデザインはこれ

frustration

ちょっとは成長しました。。。

Paasはsqale.jp( http://sqale.jp )

スクリーンショット 2013-05-20 3.27.57

弊社サービスで運用しています。 http://sqale.jp で欲求不満を運用するようにしました。 インフラのことは何も考えず、ただひたすらコードをデプロイするだけなので一心不乱にコーディングができるところがすばらしいです。

今後やっていくこと

誰も使わなくても改善していきます!!!!10年使い続けられる物欲管理サービスになります!!

  • Backbone.jsをもっとフル活用する
    • もっと動きをjs側に処理を依存させる
    • よろしくない記述が多々あるのでリファクタリングに励みます。
  • 自分でもっと画像作れるようにする
    • cssでデザインするのには限界ですね
  • jsの動きがまだ見た目的によろしくない箇所が多々あるので修正を行っていきます。
  • 失われた機能を追加します。。。
13

2013/03

世の中にはごまんとそういうプラグイン的なものあるけど、自分用にごにょごにょしたいのと勉強がてら作成してみました。

用意するものとしては以下の通りです。

  • jquery.js(1.9.1)
  • TwitterBootStrap(1.3)
    • form.css(フォーム関係一式あれば大丈夫)
    • bootstrap.jp

html

テスト

css

.modal_conteiner {
  display: none;
  position: fixed;
  z-index: 1025;
  left: 50%;
  top: 50%;
  width: 550px;
  height: 350px;

  margin-left: -300px;
  margin-top: -150px;

  padding: 0px;
  background-color: white;
  border: 1px solid #aaa;
  box-shadow: 0px 0px 10px #333;
  border-radius: 6px;
  over-flow: auto;

}
.modal_conteiner .header{
  border-radius: 6px 6px 0 0;
  border-bottom: 1px solid #DDD;
  background-color: whitesmoke;
}
.modal_conteiner .header a.right{
  border-radius: 0 6px 0 0;
  position: absolute;
  z-index: 1;
  top:     0px;
  right:   0;
  bottom:  0;
  display: block;
  width:   60px;
  height:  60px;

  font-size: 20px;

  border-left: 1px solid rgba(34, 25, 25, 0.15);
  box-shadow: inset 0 1px 2px white;
  /* text-indent: -9999px; */
}
.modal_conteiner .header a:hover.right{
  background-color: #ddd;
}
.modal_conteiner .header a.right div{
  text-align: center;
  top: 10px;
  position: relative;
  font-size: 30px;
}
.modal_conteiner .header h2{
  height:  50px;
  margin: 0;
  padding: 10px 0 0 10px;
}

.modal_conteiner .body{
  margin: 10px;
  top: -10px;
  position: relative;
  padding-top: 10px
}
.modal_conteiner .body .btn-group > .btn {
  height: 50px;
}
.modal_conteiner .footer{

}
#modal_overlay {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1000;
  background-color: #ffffff;
  opacity: 0.85;
  filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=85);
  filter: alpha(opacity=80);
}
#modal_overlay.visible{
  display: none;
}

javascript

//----------------------------------------------
// モーダル画面の表示
//----------------------------------------------
(function($) {
  $.SimpleModal = function(element, options) {

      var defaults = {
        target: '',
        continue_overlay: false,
        close_callback: function() {}
      }
      var plugin = this;
      plugin.options = $.fn.extend(defaults, options);

      //---------------------------
      // init
      //---------------------------
      plugin.init = function() {
        _this = element;
        return plugin;
      }

      //---------------------------
      // close
      //---------------------------
      plugin.close = function() {
        // init
        options = plugin.options;

        var callback = function(){};
        if(options.continue_overlay == false) {
          callback = function(){
            $('#modal_overlay').fadeOut(1000, options.close_callback);
          }
        }
        else {
          callback = options.close_callback;
        }

        $(_this).animate({top: "+=300px", opacity: 0}, 500,
          function(){
            setTimeout(function(){
              callback();
            }, 1000);
          }
        )
        .animate({top: "-1000px"}, 0);

        return this;
      }

      //---------------------------
      // cancel
      //---------------------------
      plugin.cancel = function() {
        // init
        options = plugin.options;

        var callback = function(){
          $('#modal_overlay').fadeOut(1000, options.close_callback);
        }

        $(_this).animate({top: "-=300px", opacity: 0}, 500, callback).animate({top: "-1000px"}, 0);;
        console.log('delay');

        return this;
      }

      //---------------------------
      // open
      //---------------------------
      plugin.open = function() {
        // init
        options = plugin.options;

        $(this).css('opacity', 0);

        if(options.continue_overlay == false) {
          $('#modal_overlay').fadeIn(300, function(){
            $(_this).css({'display': 'block', top: '-100px'});
            $(_this).animate({top: "50%", opacity: 1}, 500);
          });
        }
        else {
          $(_this).css({'display': 'block', top: '-100px'});
          $(_this).animate({top: "50%", opacity: 1}, 500);
        }
      }

      plugin.init();
  }

  $.fn.SimpleModal = function(options) {
    var plugin = new $.SimpleModal(this, options);
    plugin.init();
    return plugin;
  }
})(jQuery);

まとめ

こんな感じで完成。

イメージはこんな感じになります。

デザインはTwitterBootstrapを利用してそれっぽい感じにしてみた。
動きはPintarest風に動かして上からでたり、したに隠れたり動くようにした。
callbackはまだCloseしたときにしか処理させてないけど、徐々に追加していこう。

あと連続してモーダルを表示するときは以下のようにすると実現できた。

function next() {

  // 表示させるHTML領域
  $('#create_fuman').SimpleModal({
    continue_overlay: true,
    close_callback: function(e){

      // closeしたときに新しいモーダルを表示させる
      $('#complete_fuman').SimpleModal({continue_overlay: true}).open();
    }
  }).close();
}

プラグインあるとそれを何も意識しないで利用しちゃうけど、自分でなんだかんだいいながら作るのものまた一興でした。

Ref

04

2013/02

Programing Practice

前回の続きです。今回はPart2のメモ。 実践編です、javascript特有の癖があるのでそれを考慮した上での実装方針がわかるようだ。

Event Handling

  • アプリケーションのロジックときり分けよう!
  • Don’t Pass the Event Object Around(略せなかった・・・)
    • アプリケーションロジックはイベントオブジェクトそのものに頼っては行けない
    • 処理の透過性が悪くなるし、テストしにくいんだよ! イベントオブジェクト周りを引数とかにして処理させないで!!っていういみだったのかも。

Avoid Null Comparisons

Detecting Reference Values

*どれもobjectが返るから注意

console.log(typeof {}); // "object"
console.log(typeof []); // "object"
console.log(typeof new Date()); // "object"
console.log(typeof new RegExp()); // "object"
console.log(typeof null); // "object"
  • チェックはインスタンスを見るといい
if (hoge instance Date) {
  // do something
}
if (hoge instance Error) {
  throw value;
}

Detecting Arrays

Array.isArray()ていう便利メソッドがあるけど対応しているブラウザがまだ最新なので 昔のブラウザを補完した実装がいいよね!

Separate Configration Data from Code

jsでもアプリ全体で利用する変数があると思うからそういった変数だとわかるように オブジェクトないで大文字で統一して実装するといいかも

var config = {
   PERSON_LIMIT : 2222
   TIME_LIMIT : 3333
}

if (num > config.PERSON_LIMIT) {
  // do something
}

Don’t Modify Objects You Don’t Own

最初から用意されているオブジェクトを変更しちゃうとまじでみんな混乱しちゃうよ! もちろん、チームで扱うこと考慮して。

rules

  • Don’t override methods
  • Don’t add new methods
  • Don’t remove existing methods.

// bad document.getElementById = function() { return null; } なんかまちがってもこんなことしないような気がするけど、逆言うとこんなことやったらみんなが混乱しちゃうね。

Browser Detection

ブラウザの見分け方いろいろあるけど、自分のアプリケーションでどちらを優先させるかで決定の方法を柔軟に 決めていった方がいいだろうね。

User-Agent Detection

// bad
if (navigator.userAgent.indexOf("MSIE") > -1) { 
  // it is Internet Explorer
} else {
  // it is not.
}

// good
if (isInternetExplorer8OrEarlier) { 
  // it is Internet Explorer
} else {
  // it is not.
}

Feature Detection

使えるメソッドでブラウザをきり分けするっていうのもあり。この機能はこのブラウザではできないよ!って切りけするときには便利かもしれない。

if (document.getElementById) { 
  // do something
}
07

2013/01

javascriptは以前からいろいろ実装していたものの、なんとは無しに実装していたが運用しやすいコードってどんなもんだろうな。どういう気概でやってるのだろうと思ってこの本を読んでみました。

だいたい大きく纏めると

  • Style Guidlines
  • Programming Practice
  • Automation

で構成されています。今回はその「Style Guildlines」についてぼやいていこうと思います。 だいたい言っていることは他の言語でも通ずるところがあったのでしっくり腹に落とし込めた気がします。あと、「Style Guidlines」に関してはおすすめだけじゃなくてなんでこういう書き方いいのかを説明されているので納得いくものがありました。「Programming Practice 」は結構知らないことが多々あったのでちょっと実用性があるかは疑問なものもあったけど勉強になったと思います。

Basic formatting

コードのインデントや変数名・関数名の決め方がこんな書き方がいいよという紹介から始まり、変数の型について説明されていた。

特にnullとundefinedで細かく説明されていたのでメモ。

null

  • 変数宣言の際には初期化しようね
var hoge = null;
  • 期待されていないオブジェクトだったりしたときはnullを返す
function getPerson(){
  if (condition) {
    return new Person();
  }
  else {
    return null;
  }
}

var person = getPerson();
if (person !== null) {
  doSomething();
}
  • 引数に設定(供給)されたかどうかをnullでチェックしないで! この関数に技術的に間違っているわけじゃないけどそういう想定で
    やるとあとで大変なことになるからやらんほうがいいのかな。
    (すこしわかってない)
function hoge(arg1, arg2) {
  if (arg2 != null) {
    doSomething();
  }
}

undefined

基本的にこれは混乱を招くものなので使わないようにしよう!と筆者は書いていた

var person;
typeof(person); // "undefined"
typeof(hoge);   // "undefined"

変数宣言したけど初期化してないからundefined、宣言してなくてもundefined。仮にエラーが発生しても宣言されていな変数と統一するべきだろう。 あとはこんな混乱が起きないように初期化しようって話

var person = null;
if (person === null) {
  do();     
}

Comments

コメントの書き方のおすすめなんかが説明されています。

Statements and Expressions

条件文を書くときの作法なんか書かれていて、基本的な者から筆者のおすすめやら各コーディング規約に準ずるとこんな書き方できるよっての書かれていてそれを採用してるときりがないので自分もしっくり書き方を紹介

// if,forの次は必ずspace
// ()内部は空白を入れない
if (condition) {  
  do();
} else {
  doElse();
}

for (var i = 0;i < 10; i++) {
  do();
}

switch (condition) {
  'hoge':
    // do something
    break;
  'moge':
    // do something
    break;
  default:
    // none
}

個人的には「if」は以下のように書きたい

if (condition) {
  do();
}
// ここにコメント入れられたりできて見やすい
else {
  doElse();
}

The loop

言わずと知れた構文だが、continueは利用しないほうが分かりやすくてバグの原因とならない

var values = [1,2,3,4,5], i;

for (i=0; values.length < 5; i++) {
  if (i != 2) {
    process(values[i]);
  }
}

The for-in loop

for-in は基本的にはオブジェクトのプロパティを走査するときに利用される。 利用するときはhasOwnPropertyを利用することを筆者はおすすめしている

var prop;
for (prop in object) {
  if (object.hasOwnProperty(prop)) {
    console.log(prop);
    console.log(object[prop]);
  }
}

単純な配列の走査はしないようにすすめている。これは数値的インデックスされたプロパティとして 走査されてしまい、本来値としてのデータとして扱われないのだ!なので先天的なエラーをお越しかねない。

Valiables, Functions, and Operators

まぁ、あたりまえのことなのですが最初に変数宣言はちゃんとしようぜ。 から、無名関数を宣言するときは最後の行でオブジェクトか無名かわからなくなるから 無名関数の場合は()でくくってね!

// Bad
var hoge = function() {
  alert('hohoho-');
}();

var hoge = (function() {
  alert('hohoho-');
}());

Primitive Wrapper Type

結論から言うと型としての扱い方がわからなくなるので利用しないことを進めている。


var name = 'hoge';
// このとき、処理の裏側では一時的にインスタンスが生成されている
// のでこの書き方をしてもエラーにはならないらしい
name.toUpperCase();
console.log(name.toUpperCase());


// ので以下のものはBad
var name = new String('hoge');
var num = new Number(20);
var bool = new Boolean(true);

次回は、「Programming Practice」をメモってみる。

01

2013/01

2013

あけましておめでとうございます。

今年はいいところも悪いところもガツガツとさらけ出していこうと思います!!

16

2012/12

Gemの公開をBumpを利用して公開してみる。

Bumpでやってみる.

以前はjewelerを使っていたけど、使ってみることにしました。 GitHubみるかぎりなんだかシンプルそう。

Bump
https://github.com/gregorym/bump

gem install bump

gemを作成しときます。

bundle gem api-bucket

ここで一式のGem公開に必要なファイルが作成されるので、 あとはごにょごにょ作り込む。

$ rake build 
api-bucket 0.0.1 built to pkg/api-bucket-0.0.1.gem

$ rake release 
api-bucket 0.0.1 built to pkg/api-bucket-0.0.1.gem
Tagged v0.0.1
Pushed git commits and tags
Pushed api-bucket 0.0.1 to rubygems.org

バージョンアップは

$ bump minor
[master aa84ae1] v0.1.0
 1 file changed, 1 insertion(+), 1 deletion(-)
Bump version 0.0.1 to 0.1.0

で勝手にコミットまでしてくれる。

あとは「rake build」でGemファイルを構築して「rake release」でGemにアップ。

うん

gemspecファイルとかよくわかんないものまでつくられることないからこっちのほうがシンプルでいいかも。

ということで、数日問題あって非公開していたapi-bucket( https://rubygems.org/gems/api-bucket )を公開しました。

11

2012/12

自作のライブラリをGemに公開するまでに行ったこと

欲求不満の利用しているライブラリを別に切り出してGemにしてみました。
欲求不満は自分が運営している言うなればソーシャルウィッシュリストサービス
なのですが自分がよく使うAPIを何個も使って商品を登録・管理しています。

欲求不満

しかし、apiはというものそれぞれの仕様でインターフェースが異なるので
商品情報を管理するときに大変面倒なので自前でクラスを作成して
インターフェースを統一させてます。そのい一部分をGemで切り出してみました。

今回使うにあたって簡単インストールできるjewelerを利用してみました。

gem install jeweler

まずは、あらかじめGitHubでレポジトリを作成しておきます。

その後、インストール処理。

jeweler --rspec --create-repo api-bucket                                                                         

[~/Programming/ruby/gem_test]
        create  .gitignore
        create  Rakefile
        create  Gemfile
        create  LICENSE.txt
        create  README.rdoc
        create  .document
        create  lib
        create  lib/api-bucket.rb
        create  spec
        create  spec/spec_helper.rb
        create  spec/api-bucket_spec.rb
        create  .rspec
Jeweler has prepared your gem in ./api-bucket
Jeweler has pushed your repo to http://github.com/nakajijapan/api-bucket

bundle install

rake version:write

rake gemspec

これでGemに必要な一式が入ります。

もろもろライブラリやらテストやらを実装したら、それをbuildして 作成されたGemファイルをRubyGemsにアップロードします。

gem build api-bucket.gemspec
gem push api-bucket-0.0.0.gem

Awasame!!!

これで、自分のライブラリが公開となります!

バージョンアップには

rake version:bump:major  # Bump the major version by 1
rake version:bump:minor  # Bump the a minor version by 1
rake version:bump:patch  # Bump the patch version by 1

なるもので上げつつ、以下のコマンドリリースです。

rake release

Committing api-bucket.gemspec
Pushing master to origin
Tagging v0.1.0
Pushing v0.1.0 to origin
Generated: api-bucket.gemspec
api-bucket.gemspec is valid.
WARNING:  description and summary are identical
  Successfully built RubyGem
  Name: api-bucket
  Version: 0.1.0
  File: api-bucket-0.1.0.gem
Executing "gem push ./pkg/api-bucket-0.1.0.gem":
gem push ./pkg/api-bucket-0.1.0.gem
Pushing gem to https://rubygems.org...
Successfully registered gem: api-bucket (0.1.0)

これで、勝手にバージョンアップの手順を踏んでくれるのと、GitHub,Gemにすかさず
アップロードしてくれます。便利。

というわけで

api-bucket

なるものを公開しました。よかったら使ってみてください!

Reference

02

2012/12

お待たせ致しました!!!(またせていない)

8月にお産で作成し、それ以降Applieの申請で3度もRejectされ4度目にしてなんとか
12月にして公開までありつけました。。。。4ヶ月もかかったよ。。

本当はうんこスタイルで出したかったのですが、やはり天下のAppleはそんなことはさせなかったようです。でもやはり悔しいところ はあったので少しでもうんこの面影を残そうと思ってなんどか申請しました。でもやっぱりだめ。もういっそのこと世界観をガラッと 変えてうんこを掃除するゲームからゴミを掃除するゲームに無理してしまいました。これはこれでいい感じに仕上がってはいるのですが やはりうんこにしたかった。
まぁ、リリースできたことで一安心です。
ここからダウンロードできるので是非くそゲーだと実感してください!

ちょとした経緯

一度目



Reason for reject

We found that your app contains content that many audiences would find objectionable, which is not in compliance with the App Store Review Guidelines.

Specifically, we noticed the purpose of the game is to clean out feces. Such apps are not appropriate for the App Store.

We encourage you to review your app content and evaluate whether you can modify the content to bring it into compliance with the Guidelines.


Appleへの挑戦状です。やっぱりだめでした。うんこはだめなんです。

二度目



Reason for reject

We found that your app contains content that many audiences would find objectionable, which is not in compliance with the App Store Review Guidelines.

Specifically, we noticed the purpose of the game is to clean out feces. Such apps are not appropriate for the App Store.

We encourage you to review your app content and evaluate whether you can modify the content to bring it into compliance with the Guidelines.


二度目の挑戦状です。やっぱりだめでした。
うんこは隠したんですけどね。。。(隠してない!)

三度目

もろもろ修正でアウト。(うんこ消すの忘れてたみたいです。)



Reason for reject

We found that your app contains content that many audiences would find objectionable, which is not in compliance with the App Store Review Guidelines.

Specifically, we noticed the purpose of the game is to clean out feces. Such apps are not appropriate for the App Store.

We encourage you to review your app content and evaluate whether you can modify the content to bring it into compliance with the Guidelines.


とほほ。。。

四度目


うんこに関連するもの全て排除。
ほっ。。。

関連記事

02

2012/12

今回Railsで起きた問題、ちと自分の中で迷ったの纏めておく。

PHPで運用していたものをRuby(Rails)にのせ変えようとしたときに置きました。
以前、前の仕様でCookieでカンマ区切りにして保存していた情報があったのですが
それを気にしないでRailsを導入していたら初期表示でサーバエラーになるという
問題が発生しました。

保存していたもの

document.cookie = "HOGE=1,,1,1"

どうもRack側で発生していることに気づきましてCookieの文字列を解析している箇所で「;」「,」の
二つでハッシュ化していることが原因だとわかりました。

      # According to RFC 2109:
      #   If multiple cookies satisfy the criteria above, they are ordered in
      #   the Cookie header such that those with more specific Path attributes
      #   precede those with less specific.  Ordering with respect to other
      #   attributes (e.g., Domain) is unspecified.
      Utils.parse_query(string, ';,').each { |k,v| hash[k] = Array === v ? v.first : v }
      @env["rack.request.cookie_string"] = string
      hash

Rackが悪いのでは?と思っていてけどRFCまわりをよくよく調査してみるとNAME,VALUEにセミコロン、コンマ、空白文字を
除いた文字列を指定すべきで、いれるときはエンコードすべき。あと、サーバは将来の互換性のためにカンマも受け入れるべきです。
てきなことが記述されていたので間違っていないことは確認しました。

とはいっても、サーバエラーにしてクッキー削除してから閲覧してねという訳にもいかないので一時凌ぎとして

コロンで解析しないようにしました。

修正したこと

# environment.rb

module Rack
  class Request
    def cookies
      hash   = @env["rack.request.cookie_hash"] ||= {}
      string = @env["HTTP_COOKIE"]

      return hash if string == @env["rack.request.cookie_string"]
      hash.clear

      Utils.parse_query(string, ';').each { |k,v| hash[k] = Array === v ? v.first : v}

      @env["rack.request.cookie_string"] = string

      hash
    rescue => error
      error.message.replace "cannot parse Cookie header: #{error.message}"
      raise
    end
  end
end

Reference