2015年10月02日

Ionic Framewark 入門

Ionic FramewarkはHTML5でモバイルアプリが作成できるnodeのフレームワーク。

利用方法としてはnpmでionic、cordova、ios-simをインストール。

sudo npm install -g ionic cordova ios-sim


ionicコマンドでプロジェクトを開始します

ionic start myproject blank


途中で「Create an ionic.io account to send Push Notifications and use the Ionic View app?」と聞かれるのでひとまずnoで。

作成が終了したらプロジェクト内に移動してionic serveで起動するとブラザでプロジェクトが確認できる。

cd myproject
ionic serve


初期表示画面のHTMLは「/myproject/www/index.html」にあるのでこちらを修正してリロードすれば修正内容が反映されている。

MacではiOSシミュレータで確認ができる。初回のみ「ionic platform add ios」でビルドすれば以下のコマンドでiOSシュミレータで確認出来ます。

ionic platform add ios//(初回のみ)
ionic build ios
ionic emulate ios


では、Ionic Framewarkを利用してTODO アプリを作っていきましょう。(参考:Ionic Book: Table of Contents - Ionic Framework)

index.htmlは初期時は以下のようになっています。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title></title>

<link href="lib/ionic/css/ionic.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">

<!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above
<link href="css/ionic.app.css" rel="stylesheet">
-->

<!-- ionic/angularjs js -->
<script src="lib/ionic/js/ionic.bundle.js"></script>

<!-- cordova script (this will be a 404 during development) -->
<script src="cordova.js"></script>

<!-- your app's js -->
<script src="js/app.js"></script>
</head>
<body ng-app="starter">

<ion-pane>
<ion-header-bar class="bar-stable">
<h1 class="title">Ionic Blank Starter</h1>
</ion-header-bar>
<ion-content>
</ion-content>
</ion-pane>
</body>
</html>


サイドメニューを表示する



まずは、body要素の内側を以下のように変更します。

<ion-side-menus>
<ion-side-menu-content></ion-side-menu-content>
<ion-side-menu side="left"></ion-side-menu>
</ion-side-menus>


そうするとドラッグ(スワイプ)でサイドメニューが表示されるようになります。

ヘッダーを表示する



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

<ion-side-menus>

<!-- Center content -->
<ion-side-menu-content>
<ion-header-bar class="bar-dark">
<h1 class="title">Todo</h1>
</ion-header-bar>
<ion-content>
</ion-content>
</ion-side-menu-content>

<!-- Left menu -->
<ion-side-menu side="left">
<ion-header-bar class="bar-dark">
<h1 class="title">Projects</h1>
</ion-header-bar>
</ion-side-menu>

</ion-side-menus>


メインコンテツ、サイドメニューそれぞれにヘッダーが表示されます。

リストを表示する



次に<ion-content>内を以下のように変更し、

<ion-content>
<ion-list>
<ion-item ng-repeat="task in tasks">
{{task.title}}
</ion-item>
</ion-list>
</ion-content>


body要素を以下のように変更し、

<body ng-app="starter" ng-controller="TodoCtrl">


app.jsを以下のように変更するとメインコンテツにリストが表示されます。

&angular.module('starter', ['ionic'])

.controller('TodoCtrl', function($scope) {
$scope.tasks = [
{ title: 'Collect coins' },
{ title: 'Eat mushrooms' },
{ title: 'Get high enough to grab the flag' },
{ title: 'Find the Princess' }
];
});


TODOを登録できるようにする



次にページ内のモーダルでTODOを登録できるように修正しましょう。index.html上に以下のHTMLをついきます。

<script id="new-task.html" type="text/ng-template">

<div class="modal">

<!-- Modal header bar -->
<ion-header-bar class="bar-secondary">
<h1 class="title">New Task</h1>
<button class="button button-clear button-positive" ng-click="closeNewTask()">Cancel</button>
</ion-header-bar>

<!-- Modal content area -->
<ion-content>

<form ng-submit="createTask(task)">
<div class="list">
<label class="item item-input">
<input type="text" placeholder="What do you need to do?" ng-model="task.title">
</label>
</div>
<div class="padding">
<button type="submit" class="button button-block button-positive">Create Task</button>
</div>
</form>

</ion-content>

</div>

</script>


更に<ion-header-bar>のh1の次に以下の様な<button>を追加します。

<ion-header-bar class="bar-dark">
<h1 class="title">Todo</h1>
<button class="button button-icon" ng-click="newTask()">
<i class="icon ion-compose"></i>
</button>
</ion-header-bar>


最後にapp.jsのcontroller('TodoCtrl')を以下のように変更しましょう。

angular.module('starter', ['ionic'])

.controller('TodoCtrl', function($scope, $ionicModal) {
// No need for testing data anymore
$scope.tasks = [];

// Create and load the Modal
$ionicModal.fromTemplateUrl('new-task.html', function(modal) {
$scope.taskModal = modal;
}, {
scope: $scope,
animation: 'slide-in-up'
});

// Called when the form is submitted
$scope.createTask = function(task) {
$scope.tasks.push({
title: task.title
});
$scope.taskModal.hide();
task.title = "";
};

// Open our new task modal
$scope.newTask = function() {
$scope.taskModal.show();
};

// Close the new task modal
$scope.closeNewTask = function() {
$scope.taskModal.hide();
};
});


こうすることで右上にボタンが表示され、そのボタンタップ時にモーダルが表示されモーダル内でTODOが登録できるようになります。

プロジェクトの追加



では、最後にプロジェクトで管理できるようにしましょう。

<ion-side-menu-content>を以下のように変更

<!-- Center content -->
<ion-side-menu-content>
<ion-header-bar class="bar-dark">
<button class="button button-icon" ng-click="toggleProjects()">
<i class="icon ion-navicon"></i>
</button>
<h1 class="title">{{activeProject.title}}</h1>
<!-- New Task button-->
<button class="button button-icon" ng-click="newTask()">
<i class="icon ion-compose"></i>
</button>
</ion-header-bar>
<ion-content scroll="false">
<ion-list>
<ion-item ng-repeat="task in activeProject.tasks">
{{task.title}}
</ion-item>
</ion-list>
</ion-content>
</ion-side-menu-content>


<ion-side-menu>を以下のように変更

 <!-- Left menu -->
<ion-side-menu side="left">
<ion-header-bar class="bar-dark">
<h1 class="title">Projects</h1>
<button class="button button-icon ion-plus" ng-click="newProject()">
</button>
</ion-header-bar>
<ion-content scroll="false">
<ion-list>
<ion-item ng-repeat="project in projects" ng-click="selectProject(project, $index)" ng-class="{active: activeProject == project}">
{{project.title}}
</ion-item>
</ion-list>
</ion-content>
</ion-side-menu>


最後にapp.jsを以下のように変更しましょう。

angular.module('starter', ['ionic'])
/**
* The Projects factory handles saving and loading projects
* from local storage, and also lets us save and load the
* last active project index.
*/
.factory('Projects', function() {
return {
all: function() {
var projectString = window.localStorage['projects'];
if(projectString) {
return angular.fromJson(projectString);
}
return [];
},
save: function(projects) {
window.localStorage['projects'] = angular.toJson(projects);
},
newProject: function(projectTitle) {
// Add a new project
return {
title: projectTitle,
tasks: []
};
},
getLastActiveIndex: function() {
return parseInt(window.localStorage['lastActiveProject']) || 0;
},
setLastActiveIndex: function(index) {
window.localStorage['lastActiveProject'] = index;
}
}
})

.controller('TodoCtrl', function($scope, $timeout, $ionicModal, Projects, $ionicSideMenuDelegate) {

// A utility function for creating a new project
// with the given projectTitle
var createProject = function(projectTitle) {
var newProject = Projects.newProject(projectTitle);
$scope.projects.push(newProject);
Projects.save($scope.projects);
$scope.selectProject(newProject, $scope.projects.length-1);
}


// Load or initialize projects
$scope.projects = Projects.all();

// Grab the last active, or the first project
$scope.activeProject = $scope.projects[Projects.getLastActiveIndex()];

// Called to create a new project
$scope.newProject = function() {
var projectTitle = prompt('Project name');
if(projectTitle) {
createProject(projectTitle);
}
};

// Called to select the given project
$scope.selectProject = function(project, index) {
$scope.activeProject = project;
Projects.setLastActiveIndex(index);
$ionicSideMenuDelegate.toggleLeft(false);
};

// Create our modal
$ionicModal.fromTemplateUrl('new-task.html', function(modal) {
$scope.taskModal = modal;
}, {
scope: $scope
});

$scope.createTask = function(task) {
if(!$scope.activeProject || !task) {
return;
}
$scope.activeProject.tasks.push({
title: task.title
});
$scope.taskModal.hide();

// Inefficient, but save all the projects
Projects.save($scope.projects);

task.title = "";
};

$scope.newTask = function() {
$scope.taskModal.show();
};

$scope.closeNewTask = function() {
$scope.taskModal.hide();
}

$scope.toggleProjects = function() {
$ionicSideMenuDelegate.toggleLeft();
};


// Try to create the first project, make sure to defer
// this by using $timeout so everything is initialized
// properly
$timeout(function() {
if($scope.projects.length == 0) {
while(true) {
var projectTitle = prompt('Your first project title:');
if(projectTitle) {
createProject(projectTitle);
break;
}
}
}
});

});


これで、完成。

Angularを触れるのが大前提のフレームワークですね。
posted by ねこまんま at 17:23 | Comment(0) | TrackBack(0) | Tool | 更新情報をチェックする

2015年09月30日

ES6の基本 - Generator

BabelでES6を始めるにあたってES6の基本文法とBabelがどのようにコンパイルするかを理解するためにまとめておきます。

Generator



function名の前に*(アスタリスク)をつけると実行時にGenerator Objectを生成します。Generator Objectのnextメソッドを実行するとyield宣言した箇所まで関数を実行します。yieldの値はvalueプロパティで取得できます。

// ジェネレータを返す関数を定義する
function *func(){
console.log(1,"in");
yield 1;
console.log(2,"in");
yield 2;
console.log(5,"in");
yield 5;
console.log("foo","in");
yield "foo";
console.log("last","in");
}

// ジェネレータを取得する
var gen = func();

console.log(gen.next().value,"out");
console.log(gen.next().value,"out");
console.log(gen.next().value,"out");
console.log(gen.next().value,"out");
console.log(gen.next().value,"out");


var marked0$0 = [func].map(regeneratorRuntime.mark);
function func() {
return regeneratorRuntime.wrap(function func$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
console.log(1, "in");
context$1$0.next = 3;
return 1;

case 3:
console.log(2, "in");
context$1$0.next = 6;
return 2;

case 6:
console.log(5, "in");
context$1$0.next = 9;
return 5;

case 9:
console.log("foo", "in");
context$1$0.next = 12;
return "foo";

case 12:
console.log("last", "in");

case 13:
case "end":
return context$1$0.stop();
}
}, marked0$0[0], this);
}

// ジェネレータを取得する
var gen = func();

console.log(gen.next().value, "out");
console.log(gen.next().value, "out");
console.log(gen.next().value, "out");
console.log(gen.next().value, "out");
console.log(gen.next().value, "out");


以下のように無名関数を利用して一気にGenerator Objectを生成することも可能です。

// ジェネレータを取得する
var gen = (function *(){
console.log(1,"in");
yield 1;
console.log(2,"in");
yield 2;
console.log(5,"in");
yield 5;
console.log("foo","in");
yield "foo";
console.log("last","in");
})();
タグ:BABEL ES6 generator
posted by ねこまんま at 13:27 | Comment(0) | TrackBack(0) | 関数/文法 | 更新情報をチェックする

2015年09月28日

ES6の基本 - Class

BabelでES6を始めるにあたってES6の基本文法とBabelがどのようにコンパイルするかを理解するためにまとめておきます。

Class



これまでnew functionなどを利用してclassを利用してきたがES6で他の言語と同等のclassが追加されました。

class Person{
//コンストラクタ
constructor(name) {
this.name = name;
}
//メソッド
say() {
return 'My Name is ' + this.name;
}
//静的メソッド
static say2(name) {
return '私の名前は' + name;
}
}
var taro = new Person('太郎');
console.log(taro.say());//My Name is 太郎
console.log(Person.say2("taro "));//私の名前はtaro

class Man extends Person{
isMan() {
return true;
}
}
class Woman extends Person{
isMan() {
return false;
}
}

var taro = new Man('太郎');
console.log(taro.isMan());//true
var hanako = new Woman('花子');
console.log(hanako.isMan());//false


var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var Person = (function () {
//コンストラクタ

function Person(name) {
_classCallCheck(this, Person);

this.name = name;
}

//メソッド

_createClass(Person, [{
key: 'say',
value: function say() {
return 'My Name is ' + this.name;
}

//静的メソッド
}], [{
key: 'say2',
value: function say2(name) {
return '私の名前は' + name;
}
}]);

return Person;
})();

var taro = new Person('太郎');
console.log(taro.say()); //My Name is 太郎
console.log(Person.say2("taro ")); //私の名前はtaro

var Man = (function (_Person) {
_inherits(Man, _Person);

function Man() {
_classCallCheck(this, Man);

_get(Object.getPrototypeOf(Man.prototype), 'constructor', this).apply(this, arguments);
}

_createClass(Man, [{
key: 'isMan',
value: function isMan() {
return true;
}
}]);

return Man;
})(Person);

var Woman = (function (_Person2) {
_inherits(Woman, _Person2);

function Woman() {
_classCallCheck(this, Woman);

_get(Object.getPrototypeOf(Woman.prototype), 'constructor', this).apply(this, arguments);
}

_createClass(Woman, [{
key: 'isMan',
value: function isMan() {
return false;
}
}]);

return Woman;
})(Person);

var taro = new Man('太郎');
console.log(taro.isMan()); //true
var hanako = new Woman('花子');
console.log(hanako.isMan()); //false


このようにconstructorで簡単にコンストラクタの設定ができ、staticで静的メソッドが定義できます。
タグ:ES6 BABEL class
posted by ねこまんま at 19:23 | Comment(0) | TrackBack(0) | 関数/文法 | 更新情報をチェックする

ES6の基本 - concise methods

BabelでES6を始めるにあたってES6の基本文法とBabelがどのようにコンパイルするかを理解するためにまとめておきます。

concise methods



ES6ではメソッドの定義が簡潔になりました。以下のように「:function」を省略できるようになっています。

var bar = {
fuga : "ok",
foo() {
console.log(this.fuga);
}
};

bar.foo();//ok


var bar = {
fuga: "ok",
foo: function foo() {
console.log(this.fuga);
}
};

bar.foo(); //ok
posted by ねこまんま at 18:30 | Comment(0) | TrackBack(0) | 関数/文法 | 更新情報をチェックする

2015年09月26日

ES6の基本 - Function Arguments

BabelでES6を始めるにあたってES6の基本文法とBabelがどのようにコンパイルするかを理解するためにまとめておきます。

可変長の引数



ES5以前では可変長の引数を受け取る際にargumentsを利用していましたが、ES6では任意の変数名で受け取ることができます。引数宣言時に.(ドット)を3つつなげて記述します。また、Arrayオブジェクトに変換されるためargumentsより使いやすいですね。

var bar = (...arg) => {
console.log(arg);//[1, 2, 3, 4]
console.log(Array.isArray(arg));//true
};

bar(1,2,3,4);


var bar = function bar() {
for (var _len = arguments.length, arg = Array(_len), _key = 0; _key < _len; _key++) {
arg[_key] = arguments[_key];
}

console.log(arg); //[1, 2, 3, 4]
console.log(Array.isArray(arg)); //true
};

bar(1, 2, 3, 4);


引数の初期値



関数の引数に初期値を設定できるようになりました。

function bar(foo = 3) {
console.log(foo);
}
bar(4);//4
bar();//3


function bar() {
var foo = arguments.length <= 0 || arguments[0] === undefined ? 3 : arguments[0];

console.log(foo);
}
bar(4); //4
bar(); //3


var bar = (foo = 3) => {
console.log(foo);
}
bar(4);//4
bar();//3


var bar = function bar() {
var foo = arguments.length <= 0 || arguments[0] === undefined ? 3 : arguments[0];

console.log(foo);
};
bar(4); //4
bar(); //3
posted by ねこまんま at 12:14 | Comment(0) | TrackBack(0) | 関数/文法 | 更新情報をチェックする

ES6の基本 - const

BabelでES6を始めるにあたってES6の基本文法とBabelがどのようにコンパイルするかを理解するためにまとめておきます。

const



constは定数宣言を行う構文です。定数は習慣的に大文字で宣言を行います。

BABELでは1回しか定数宣言されていない場合はvar宣言に変換を行います。

const FOO ="bar";
console.log(FOO);//bar


var FOO = "bar";
console.log(FOO);


しかし、2回以上同一の定数が宣言された場合コンパイルを行わずにエラーを出力します。

const FOO ="bar";
console.log(FOO);//bar
FOO ="bar2";
console.log(FOO);
タグ:ES6 CONST BABEL
posted by ねこまんま at 11:54 | Comment(0) | TrackBack(0) | 関数/文法 | 更新情報をチェックする

ES6の基本 - let

BabelでES6を始めるにあたってES6の基本文法とBabelがどのようにコンパイルするかを理解するためにまとめておきます。

let



letは局所的変数宣言でブロクスコープ内のみで有効な変数を宣言できます。
ブロック外で利用した場合は単純にvarと同じ機能を提供しますのでBabelはvarに変更してコンパイルを行います

let foo0 ="bar0";
console.log(foo0);//bar0
if(true){
foo0 = "bar00";
console.log(foo0);//bar00
}


var foo0 = "bar0";
console.log(foo0); //bar0
if (true) {
foo0 = "bar00";
console.log(foo0); //bar00
}


以下のようにブロック内でlet宣言をおこなってみましょう。Babelは大幅な変更を行います。

if(true){
let foo1 = "bar1";
console.log(foo1);//bar1
}
foo1 ="bar11";
console.log(foo1);//foo1 is not defined


if (true) {
var _foo1 = "bar1";
console.log(_foo1); //bar1
}
foo1 = "bar11";
console.log(foo1); //foo1 is not defined


このようにしてletを擬似的に再現しております。

また、以下のように同一ブロックで同じ変数をletした場合はBabelはエラーを返しコンパイルを行いません。

let foo0 ="bar0";
let foo0 ="bar00";


ブロックの階層が異なれば同一変数名でもlet宣言で定義が可能です。

let foo0 ="bar0";
if(true){
let foo0 ="bar00";
}


var foo0 = "bar0";
if (true) {
var _foo0 = "bar00";
}

タグ:ES6 BABEL Let
posted by ねこまんま at 05:09 | Comment(0) | TrackBack(0) | テクニック | 更新情報をチェックする

ES6の基本 - Arrow Function

BabelでES6を始めるにあたってES6の基本文法とBabelがどのようにコンパイルするかを理解するためにまとめておきます。

Arrow Function



代表的な省略構文であるArrow Function。

このようなコードが以下のように変換される。関数リテラルが無名関数ではなく有名関数で生成されているのに違和感ありですが。

var bar = () => {
console.log("ok");
};


var bar = function bar() {
console.log("ok");
};


引数を取った場合も一緒です。

var bar = (x) => {
console.log("ok");
};


var bar = function bar(x) {
console.log("ok");
};


引数の括弧は省略できる

var bar = x => {
console.log("ok");
};


var bar = function bar(x) {
console.log("ok");
};


return を行い1行しかない場合は場合は { }も省略できる

var bar = x => x+1;


var bar = function bar(x) {
return x + 1;
};


jQueryなどを利用している場合は以下のように記述するとfunctionの記述を省略できます。

$(()=>{
console.log("ok");
});


$(function () {
console.log("ok");
});
posted by ねこまんま at 03:56 | Comment(0) | TrackBack(0) | 関数/文法 | 更新情報をチェックする

2015年09月25日

gulpでbrowserifyとBabel

BabelはES6のimport/export構文をrequireに変換してしまうためそのままでは利用できない。

import { main } from './main';
console.log(main(1,2));


これを以下のように変換する

'use strict';

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _mainJs = require('./main');

var _mainJs2 = _interopRequireDefault(_mainJs);

console.log((0, _mainJs2['default'])(1, 2));


このままでは利用できないのでbrowserifyなどを利用してrequire()が利用できるようにしなくていけない。

調べてみるとbrowserifyとbabelifyというツールでコンパイルを行うらしい。

以下のコマンドでインストール。

sudo npm install browserify babelify vinyl-source-stream --save-dev


すると以下の様なタスクでコンパイルできる。

var gulp       = require('gulp'),
browserify = require("browserify"),
babelify = require("babelify"),
source = require("vinyl-source-stream");

gulp.task('babelify', function () {
browserify({
entries: "./babel/script.js",
extensions: [".js"]
})
.transform(babelify)
.bundle()
.on("error", function (err) { console.log("Error : " + err.message); })
.pipe(source("./script.js"))
.pipe(gulp.dest("./asset/js"));
});


babel単体だとグローバルに'use strict'が付与されるのが気になってたけどこれなら各名前空間のみに付与されるのでそれもよさ気だと思った。
posted by ねこまんま at 08:59 | Comment(0) | TrackBack(0) | gulp | 更新情報をチェックする

Babelをgulpで実行

久々に新規サイトを作成するのでBabelを利用してES6で書こうとしております。

BabelはES6をES5形式にコンパイルしてくれるAltJS。CoffeeScriptなど他のAltJSと比較すると学習コストが将来無駄にならない点がよい。

gulpの使い方は「タスクランナーgulp入門」などを参照に。Babelに関しては以下のgulp-babelをインストール。

sudo npm install gulp-babel --save-dev


以下のような設定を追加するとbabelディレクトリの内容をassets/jsディレクトリに書きだしてくれる。

var babel  = require('gulp-babel');

//babel
gulp.task('babel', function () {
gulp.src("/babel/*.js")
.pipe(babel())
.pipe(gulp.dest("/assets/js"));
});


ターミナルでは以下のコマンドを実行する。

gulp babel
posted by ねこまんま at 07:09 | Comment(0) | TrackBack(0) | gulp | 更新情報をチェックする

2015年08月11日

JavaScriptにおけるsetter/getter

JavaScriptにおけるsetter/getterはECMAScript5で追加されIE9以降の主要なブラウザで利用できる機能です。

続きを読む
posted by ねこまんま at 08:03 | Comment(0) | TrackBack(0) | テクニック | 更新情報をチェックする

プロパティの存在確認

以下のようにif分内に直接プロパティを指定して存在確認をするのはあまり上手な方法ではない。

var huga = {};
huga.hoge = "foo"
if(huga.hoge){
console.log("ok");
}else{
console.log("ng");
}
続きを読む
posted by ねこまんま at 07:38 | Comment(0) | TrackBack(0) | テクニック | 更新情報をチェックする

2015年03月28日

React.js入門1

なんとなくReact.jsを入門してみる。
続きを読む
posted by ねこまんま at 08:25 | Comment(0) | TrackBack(0) | React | 更新情報をチェックする

2015年02月22日

SUIT CSS

SUIT CSSはコンポーネントベースのCSS設計手法。ReactやEmber、 AngularといったコンポーネントベースのUI設計と相性がよい。個人的にはBEMよりこちらの設計手法のほうが使いやすい

SUIT CSSはCSSのclassの命名規則CSS preprocessor(プリプロセッサー)などで定義される。続きを読む
タグ:CSS suit
posted by ねこまんま at 07:30 | Comment(0) | TrackBack(0) | CSS | 更新情報をチェックする

2014年08月28日

RequireJS 入門

RequireJSはJavaScriptをモジュール開発するためのツール。

JavaScriptはグローバル領域が共通なので複数のモジュールを利用しようとするとバッティングするおそれがある。基本的にはグローバル使うな!となるが他のモジュールなどでどの名前空間を使うかなど色々決めなくてはいけない。

var Fuga = function() {};

var NameSpace = {}
NameSpace.Fuga = function() {}


RequireJSを使うとコレを簡単にすることができる。

まず、以下の様なディレクトリ構成だと考える。script/module/fuga.jsとscript/module/hoge.jsが独立したモジュールである

.
├── index.html
└── script
├── lib
│   ├── jquery-1.11.1.min.js
│   └── require.js
├── main.js
└── module
   ├── fuga.js
   └── hoge.js

まず、index.htmlではrequire.jsを読みこみ、data-main属性にメインのスクリプトのURLを記述する。

<script data-main="script/main.js" src="script/lib/require.js"></script>


まず、main.jsに以下のように設定してみよう。module/fuga.jsの読み込みが終了した際にコールバック関数が実行されます。

require([
'module/fuga'
], function(fuga) {
console.log(fuga.doo())
});


fuga.jsには以下のように記述してみる。

define({
bar:"fuga",
foo:"hoge",
doo:function(){
return this.bar+this.foo
}
});


main.jsがfuga.jsを読み込み、defineの定義内容はdefine()で指定ができる。これは以下のように記述することもできる。

define(function(){
var fuga = {
bar:"fuga",
foo:"hoge",
doo:function(){
return this.bar+this.foo
}
}
return fuga;
});


他のモジュールやライブラリを参照して利用したい場合は以下のように記述を行う。

define([
'lib/jquery-1.11.1.min'
], function(){
var fuga = {
bar:"fuga",
foo:"hoge",
doo:function(){
return this.bar+this.foo+$("body").text();
}
}
return fuga;
});
タグ:RequireJS
posted by ねこまんま at 17:54 | Comment(0) | TrackBack(0) | Tool | 更新情報をチェックする

2014年08月27日

GruntでJade

GruntでJadeをコンパイルしようとすると以下のようにする。ただこれだと対象ファイルが増えるたびに手動でGruntfile.jsにファイルを追加していかなくていはいけない。

module.exports = function(grunt) {
grunt.initConfig({
jade: {
compile: {
files: {
"dest/sample1.html": ["jade/sample1.jade"],
"dest/sample2.html": ["jade/sample2.jade"],
"dest/sample3.html": ["jade/sample3.jade"],
}
}
}
});
}


これは以下のようにするとjadeディレクトリ内のjadeファイルが更新された際にdestに書きだすようにすることができる。

module.exports = function(grunt) {
grunt.initConfig({
jade: {
compile: {
files: [ {
expand: true,
cwd: "jade/",
src: "*.jade",
dest: "dest/",
ext: ".html"
}]
}
}
});
}


あと、以下のように指定するとjadeディレクトリにある_xxx.jadeみないなアンスコ付きのファイルはコンパイル対象化から外すことができる。

module.exports = function(grunt) {
grunt.initConfig({
jade: {
compile: {
files: [ {
expand: true,
cwd: "jade/",
src: [
"*.jade",
"!_*.jade"
],
dest: "dest/",
ext: ".html"
}]
}
}
});
}


Gruntは面倒くさいので、みんなgulpを使おうよ。
タグ:Grunt JADE
posted by ねこまんま at 12:36 | Comment(0) | TrackBack(0) | Grunt | 更新情報をチェックする

jasmine-jqueryとkarmaでの自動テスト

jasmineのfixture(テスト用のHTMLをロードする奴)であるjasmine-jquerykarmaを利用しようとする色々とハマったのでメモ。

まず、karmaではconfigのfilesに指定したファイルしか読み込みが出来ない。そこでkarma.conf.jsのfilesにfixturesで読み込むURLを指定しなくてはいけない。オプションでincluded:falseを指定しないと初回で読み込まれてしまうので注意。

module.exports = function(config) {
config.set({
(中略)
files: [
'module/lib/jquery-1.11.1.js',
'module/lib/underscore-min.js',
'module/lib/backbone-min.js',
(中略)
{ pattern: 'fixtures/*.inc', included: false }
],
(中略)
});
}



また、filesで指定したファイルはbaseディレクトリ配下に読み込まれるので以下のようにKarma経由の場合(ポート番号9876)とブラウザ経由の場合でfixturesPathパスを変更する必要がある。

if(location.port==9876){
jasmine.getFixtures().fixturesPath = 'base/fixtures/';
}else{
jasmine.getFixtures().fixturesPath = '../fixtures/';
}


一番ハマったのは以下のコマンドでインストールした「karma-jasmine」のバージョンが古かったこと。jasmine-jqueryの組み合わせるとclearTimeoutがundefinedとか訳の分からないエラーが出てしまった

npm install karma-jasmine --save-dev


以下のコマンドじゃないと2系のkarma-jasmineはインストール出来ない

npm install karma-jasmine@2_0 --save-dev
posted by ねこまんま at 11:52 | Comment(0) | TrackBack(0) | test | 更新情報をチェックする

2014年08月08日

Selenium入門

Seleniumの使い方。

まずはRuby Gemをインストール

gem install selenium-webdriver


test.rbなど適当なファイル名で以下の内容を記述。

require "selenium-webdriver"
# Firefox用のドライバを使う
driver = Selenium::WebDriver.for :firefox
# Googleにアクセス
driver.navigate.to "http://google.com"
# `q`というnameを持つ要素を取得
element = driver.find_element(:name, 'q')
# `Hello WebDriver!`という文字を、上記で取得したinput要素に入力
element.send_keys "Hello WebDriver!"
# submitを実行する(つまり検索する)
element.submit
# 表示されたページのタイトルをコンソールに出力
puts driver.title
# テストを終了する(ブラウザを終了させる)
driver.quit


以下のコマンドでSeleniumが実行される。

ruby tesr.rb


参考:WebのUIテスト自動化 - Seleniumを使ってみる - Qiita

ちょっと上記のスクリプトだと表示が微妙だったが以下のコードのほうが良さげだった。

require "selenium-webdriver"
driver = Selenium::WebDriver.for :firefox
driver.get "http://www.google.co.jp/"
driver.find_element(:class,"gsfi").send_key "Selenium"
driver.find_element(:name,"btnK").submit
driver.close


参考:Ruby - Selenium 入門 - Qiita
タグ:Selenium
posted by ねこまんま at 15:08 | Comment(0) | TrackBack(0) | test | 更新情報をチェックする

Karmaでmocha + chaiのテストを自動化

Karma + を利用すればテストが自動化できるので自動化してみました。

ひとまずはkarmaをインストール。

npm install -g karma
karma --version


このままでは「-bash: karma: command not found」と怒られてしまうのでkarma-cliもインストール。

npm install -g karma-cli
karma --version


これでちゃんとバージョンが表示される。

Karma version: 0.12.21


まずは設定ファイルの作成。対話式で作成できます。

karma init


まず、

Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.

と利用するフレームワークについて聞かれる。QUnitJasmineMochaから選択できるのでひとまずはMochaを選択(tabか下ボタンで候補をきりかえられます)

次に、

Do you want to use Require.js ?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.

Require.jsを利用するかどうか聞かれます。「no」でEnter

Do you want to capture a browser automatically ?
Press tab to list possible options. Enter empty string to move to the next question.

次に、利用するブラウザを聞かれます。PhantomJSでヘッドレステストなんかもできますがターゲットブラウザ以外でテストするのもあれなのでChromeとFirefoxを選択。(WinならIEとかも指定するとよいかも)

What is the location of your source and test files ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.

次にスクリプトの場所が聞かれます。"js/*.js" or "test/**/*Spec.js"みたいにして読み込むファイルを指定しましょう。
ちなみにChaiやSinon.JS、jQueryなんかMocha以外にライブラリも指定しておく必要があります。

あと、mocha.run();を実行しているファイルを読み込んじゃうとテストが2回走ってしまいます。ここでハマった。

Should any of the files included by the previous patterns be excluded ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string to move to the next question.

swpファイルの出力場所が聞かれます。これは無視。

Do you want Karma to watch all the files and run the tests on change ?
Press tab to list possible options.

ファイルを監視するか聞かれるので「yes」。

これで「karma.conf.js」が作られます。

karma start


と打つと自動でブラウザが立ち上がりテストが開始されます。また、監視ファイルが更新された際にも再テストが自動で行われます。
タグ:KARMA mocha chai test
posted by ねこまんま at 11:26 | Comment(0) | TrackBack(0) | test | 更新情報をチェックする

2014年08月05日

Mocha+Chai+Sinon.JS 入門

最近注目のテストフレームワーク「Mocha」、軽量ですがアサーション機能すらありませんので別のライブラリを組み合わせて使うのが一般的。今回はアサーションライブラリとして「Chai」とテストタブル用にSinon.JSをチョイス。

Mocha



Mochaはnode.jsでも利用できますがクライアントJavaScriptではGitHubからダウンロードしたmocha.cssとmocha.jsを読み込みます、今回はChaiとSinon.JSを読み込んだ以下のテンプレートで解説をします。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="mocha.css">
</head>
<body>
<div id="mocha"></div>
<script src="mocha.js"></script>
<script src="chai.js"></script>
<script src="sinon-1.10.3.js"></script>
<script>
mocha.setup('bdd');
var expect = chai.expect;
window.onload = function() {
mocha.run();
};
</script>
<script src="テストするコード"></script>
<script src="テストコード"></script>
<div id="sandbox"></div>
</body>
</html>


以下の箇所ではmochaの設定でスタイルをBDD(ビヘイビア駆動開発 )にしてchaiのexpectを利用するようにしています。

mocha.setup('bdd');
var expect = chai.expect;
window.onload = function() {
mocha.run();
};



テストコードではJasmineと同じようにdescribe()で見出しをつけて記述していきます。

describe('テスト内容', function() {
//テストコード
});


describe()は入れ子でも利用できます。

describe('テスト内容1', function() {
describe('テスト内容1-1', function() {
//テストコード
});
describe('テスト内容1-2', function() {
//テストコード
});
});


テストはit()を利用して記述します。

describe('テスト内容', function() {
it("メソッドが存在する", function() {
//テストコード
});
});


実際のテストでは以下のアサーションメソッドを利用利します。

describe('テスト内容', function() {
it("aがおんなじ", function() {
expect("a").to.equal("a");
});
});


これでブラウザを見ればテストが成功しているのがわかります。

chaiの利用方法



mochaはBDDを利用する場合はJasmineと利用方法が変わらないので問題ないでしょう。chaiは少し利用方法が違います。

.toや.equalといったメソッドをチェーンさせながら記述します。

たとえば以下のように.notを追加することでaとbは同じじゃない、つまり異なることをテストしています。

describe('テスト内容', function() {
it("aとbがちがう", function() {
expect("a").to.not.equal("b");
});
});


参考:Expect / Should - Chai

Sinon.JSの使い方



Sinon.JSはテストダブルを行うことができます。

たとえば次のような謎のメソッドがあったとします。

var a = function(){
b();
}
var b = function(){
return "b";
}


a.b()のテストは以下の様な感じでできます。

it("bのテスト", function() {
var value = b();
expect(value).to.equal("b");
});


しかし、a()のテストはできません。a()ではb()が実行されているかテストしたいのです。Sinon.JSを利用すると以下のようにテストできます。

it("aのテスト", function() {
var spy = sinon.spy(window,'b');
a();
expect(spy.callCount).to.equal(1);
});
posted by ねこまんま at 21:41 | Comment(0) | TrackBack(0) | test | 更新情報をチェックする