webプロダクトいんふぉ

webの気になった情報を発信中!!

Rails標準のO/RマッパーであるActive Recordとは?

O/Rマッパー

Object/Relatinalの略で、リレーショナルデータベースとオブジェクト指向言語との橋渡しを受け持つライブラリです。

そもそも、アプリケーション(フロント)側で使用するオブジェクトモデルと、データベースが利用するリレーショナルモデルは決定的に構造が異なるため、フォームから受け取った値などをオブジェクトから一つ一つ取り出したりといった手順が必要でした。

単純な作業ではあるが、上記の手順などがアプリケーションコードの半分以上を占めていたと言われています。

このようなコードの冗長化を解消するために、オブジェクトモデルとリレーショナルモデルのミスマッチを解消するためのツールです。

例えば、usersテーブルみたいなのがあり、下記のようなカラムを用意してあげます。

  • id
  • email
  • tel
  • password

そうしたら、上記のカラムに対応するオブジェクトを用意すれば、

{id: 1

email: aaaa@aaa.com

tel: 000-0000-000

password: password}

このままデータベースに登録することができます。つまり、オブジェクトを分解してidはidカラムに対応しているというような設定が必要なくなります。

Acrive Record

Active Recordを利用することで、リレーショナルデータベース(Excelのような表形式のデータ)をあたかもオブジェクトであるかのように操作ができるようになります。

その上、基本的にはsqlコマンドを記述する必要がありません。sqlにはMySQLPostgreSQL毎に一部書き方が違いますが、O/Rマッパーは内部的にその差異を吸収してくれるので、接続先のデータベースを変更してもアプリケーションへの影響が最小限に抑えられます。

 

カルーセルスライダーを条件別にスライド位置を変える

ルーセルスライダーとは?

画像やコンテンツがスライドして別のコンテンツを切り替えて表示する方法
DEMO
Download

ライブラリとしては、良くslick.jsbxslider.jsが使われる
個人的にはslick.jsが使いやすい。カスタマイズする際、bxsliderはハマりやすい。その代わり、高機能。

今回はslick.jsを使用する。

slick-theme.cssは必須ではないですが、公式のdemoみたいな見た目にしたいのであれば入れた方がいいです。一応jQueryも読み込んでいます。

<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/jquery.slick/1.6.0/slick.css"/>
<link rel="stylesheet" type="text/css" href="css/slick-theme.css">
<script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
<script type="text/javascript" src="//cdn.jsdelivr.net/jquery.slick/1.6.0/slick.min.js"></script>

適当にヘッダー部分と、スライドコンテンツを作ります。

注意点としては、スライドさせたいコンテンツはdivタグで括ってあげることです。ulタグなどで括るとうまく動作しません。

<header id="header">
  <div id="header-inner">
    <h1>SlickSample</h1>
    <ul class="flR">
      <li><a href="#one">one</a></li>
      <li><a href="#two">two</a></li>
      <li><a href="#three">three</a></li>
    </ul>
  </div>
</header>

<div class="slide-wrapper slider">
  <div>
    <img src="img/slide_01.jpg" alt="">
  </div>
  <div>
    <img src="img/slide_02.jpg" alt="">
  </div>
  <div>
    <img src="img/slide_03.jpg" alt="">
  </div>
</div>

jsでslick.jsのメソッドを呼んでいます。中に書いている「accessibility」、「dots」などはオプションです。見た目や動きなどを変えたいときには、ここ(自分で書くjs)とslick-theme.cssをカスタムすることになります。

オプション一覧

オプション名 デフォルト 説明
accessibility bool true 左右のナビゲーションの有効/無効
autoplay bool false オートプレイの有効/無効
autoplaySpeed int 3000 スライドが切り替わる時間(ms)
dots bool false カレント表示のナビゲーション
arrows bool true 前へ/次へナビゲーションの有効/無効
initialSlide int 0 最初のスライド位置

オプションは他にも多々あるので、興味がある方は公式サイトを参考にしてみてください。settingsが該当箇所です。

$('.slide-wrapper').slick({
  accessibility: false,
  dots: true,
  arrows: false,
  autoplay: true,
  initialSlide: slide_int
});

先程、書いたjsの前に追記してあげます。ヘッダーメニューをクリックした時に、URLに付与された#***で条件判定してあげて、どのスライドを最初に出すかを選択してあげるjsです。

これで、メニューごとにスライドの初期位置を出しわけることが可能です。

var slide_int = 0;
$('.flR a').on('click', function(){
  location.reload()
});

var href = location.href.substr( location.href.indexOf("#") );
if(href == "#one") {
  slide_int = 0;
} else if(href == "#two") {
  slide_int = 1;
} else if(href == "#three") {
  slide_int = 2;
}

$('.slide-wrapper').slick({
  accessibility: false,
  dots: true,
  arrows: false,
  autoplay: true,
  initialSlide: slide_int
});

ES6で提供された主な仕様

ECMAScript6とは?

そもそもECMAScriptとは、標準化団体によって標準化された言語仕様で、JavaScriptが動作する際に、元となっている仕様です。

ES6で提供された新たな仕様(1部抜粋)

・class命令によるJava/C#ライクなクラス定義が可能に

・import/export命令によるコードのモジュール化に対応

・関数構文の改善

・let/const命令によるブロックスコープの導入

・for...of命令による値の列挙

イテレーター/ジェネレーターによる列挙可能なオブジェクトの操作が可能に

・promise、コレクション(Map/Set)/Proxyなどの組み込みオブジェクトを追加

・string/Number/Array/Objectなどの既存組み込みオブジェクトの拡充など

ブロックスコープを有効にする ー let命令

これまでのJavaScriptには、ブロックレベルのスコープはありませんでした。

※ES6を試してみたい方は、https://babeljs.io/repl/を利用ください

if (true) {
  var i = 1;
}
conosole.log(i); // 結果:1

ブロックスコープが有効であれば、結果は未定義エラーとなるはずですが、JavaScriptではブロックスコープの概念がないので、変数iはブロック外でも有効となり、結果は1です。そこで、ES6のlet命令を利用することでブロックスコープで有効な変数を宣言できます。

if (true) {
  let i = 1;
}
conosole.log(i); // 結果:エラー(i is not defined)

今度は、結果はエラーになります。let命令で宣言された変数はブロック外では無効になったためです。

let命令はスコープ内での変数の重複を認めていない(var命令は可)

ES6でもvar命令は宣言できますが、「スコープは最大限限定すべき」「重複チェックコンパイラーに任せられる」などの理由から、基本はlet命令を優先して利用することになると思います。

「let命令はスコープ内での変数の重複を認めていない」と書きましたが、どういうことかというと

switch(i) {
  case 0:
  let value = 'i:0';
  break;
  case 1:
  let value = 'i:1'; // 重複エラー
  break;
}

case1の時にも同じ変数名を定義しているので、エラーになってしまいます。

即時関数は利用しない

即時関数は、関数ブロックで疑似的にスコープを形成し、グローバルスコープの汚染を防ぐテクニックです。

(function(){
  // 関数の中身
})();

ES5までは当然の仕組みですが、ES6ではコード全体をブロックで括り、配下の変数をlet命令で宣言すれば、即時関数と同じ効果が得られるので、利用する必要がなくなります。

定数を宣言する ー const命令

const命令は、定数を宣言できます。PHPのfinalと同じです。

const data = 100;
data = 150; // エラー

ただし、const命令の定数は正しくは「再代入できない」であって、「変更できない」ではない点に注意です。

const data = [1, 2, 3];
data[0] = 10;
console.log(data); // 結果:[10,2,3]

配列は参照型なので、dataの参照先に再代入しているわけではなく、中身の要素だけを書き換えているため、constの制約にはかかりません。

const data = [1, 2, 3];
data = [10, 2, 3]; // エラー

配列そのもを変更した場合にはエラーになります。

文字列リテラルへの変数/改行の埋め込みを可能にする ー テンプレート文字列

テンプレート文字列を利用することでエスケープシーケンスで表現していた文字を、文字リテラルの中でそのまま表現できます。テンプレート文字列はクォート「'」「"」の代わりに、「`」(バッククォート)で括ります。

let str = `Hello
World`;
console.log(str); //Hello「改行」World

これまでであればエスケープシーケンスで表現していた改行文字を、文字列リテラルの中でそのまま表現できます。

また、${}の形式で、変数を文字列に埋め込むことも可能です。今までは、変数と文字は「+」演算子を使って連結する必要がありました。

let name = 'Tom';
console.log(`Hello,${name}!`); // Hello,Tom!

新たなデータ型Symbol

従来のNumber、String、Objectなどの型に加えて、新たにSymbolという型が追加されました。これは下手な説明より見た方がはやいです。

let hoge = Symbol('hoge');
let hoge2 = Symbol('hoge');
console.log(typeof hoge); // 結果:symbol
console.log(hoge.toString()); // 結果:symbol(hoge)
console.log(hoge === hoge2); // 結果:false

ここで注意するのは一点だけです。Symbolで生成したものは中身が同じであろうと、別物とみなされます。

また、シンボルでは文字列や数値への暗黙的な変換はできません。つまり、Symbolの型と文字列・数値の連結はできません。

ただし、boolean型、object型への変換は可能です。

これって実際どんな場面で活用するの?となると思います。

具体的な利用例をいくつか下記に挙げます。

①定数の値として利用する
var JAVASCRIPT = 0;
var RUBY = 1;
var PERL = 2;
var PYTHON = 3;

一般的に、このような定数は、JAVASCRIPTRUBYPERLなどを識別するための定数であって、割り当てられた0、1、2...といった値には意味がありません。ただ、これらの定数を利用するコードでは、定数、数値を利用してもエラーにはなりません。

コードの可読性を考えれば「0」で比較するのは望ましい状態ではありませんし、そもそも「var HOGE = 0;」のような定数が現れた時に、同じ値の定数が混在してしまうのはバグが混入する元です(枠割が似ていれば尚更)

そこで定数の値としてSymbolを利用します。

const JAVASCRIPT = Symbol();
const RUBY = Symbol();
const PERL = Symbol();
const PYTHON = Symbol();

Symbol命令で生成されたシンボルは、同名であってもユニークになります。つまり、定数JAVASCRIPTと等しいのは定数JAVASCRIPTのみです。

②非公開なプロパティを定義する

たとえば以下は、MyClassクラスの中で、privateなSECRETプロパティを定義する例です。

// SECRETプロパティの名前でシンボルで準備
const SECRET = Symbol();
class Myclass {
  constructor(secret) {
    this.data1 = 1;
    this.data2 = 2;
    // SECRETプロパティに値を設定
    this[SECRET] = secret;
  }
  // SECRETプロパティを利用したメソッド
  checkSecret(secret) {
    return this[SECRET] === secret;
  }
}
let c = new Myclass(12345);
// メソッド経由ではSECRETプロパティにアクセスできる
console.log(c.checkSecret(12345)); // true
// SECRETプロパティへの直接アクセスは不可
console.log(c.secret); // undifined
// オブジェクトのキー(プロパティ)を列挙
console.log(Object.keys(c)); // ["data1","data2"];
// オブジェクトのキー(プロパティ)を列挙
for (let k in c) {
  console.log(k); // data1、data2
}
// オブジェクトをJSON文字列に変換
console.log(JSON.stringify(c)); // {"data":1,"data2":2}

SECRETプロパティの名前をシンボルとして準備し、「this[SECRET]=~」でプロパティとして定義

シンボルSECRETの値は他からは判別できませんので、SECRETプロパティに直接アクセスすることはできません。for...in命令による列挙、JSON.stringifyメソッドで生成されたJSON文字列にも、シンボルで生成されたプロパティは現れてこない点に注目してください。

ただし、完全に隠ぺいできるわけではなく、getOwnPropertySymbolsメソッドを利用すると、シンボルプロパティにアクセスすることは可能です。

let idsym = Object.getOwngetOwnPropertySymbols(c)[0];
console.log(c[idsym]);

配列/オブジェクトから個々の要素を抽出する ー 分割代入

分割代入(destrucruting assignment)は、配列/オブジェクトを分解し、その要素/プロパティを個々の変数に展開するための構文

let [hoge, foo] = [15, 21];
console.log(hoge); // 15

「...」演算子を利用することで、残りの要素をまとめて配列として取り出すこともできます。

let [hoge, foo, ...other] = [10, 20, 30, 40, 50];
console.log(hoge); // 10
console.log(other); // [30,40,50]

オブジェクトのプロパティに割り当てることもできます。

let {hoge, foo} = {hoge:'ほげ', foo:"ふ~"};
console.log(hoge); // ほげ

入れ子になったプロパティに割り当てる

let data = {hoge: 'ほげ', foo: {piyo: 'ぴよ', goo: 'ぐう'}};
let {hoge, foo, foo: {piyo, goo}} = data;
console.log(hoge); // ほげ
console.log(foo); // {"piyo":"ぴよ","goo":"ぐう"}

指定されたプロパティが存在しなかった場合の為に、デフォルト値を用意も可能です。

let {hoge = 'ほげ', foo} = {foo: 'ふ~'};
console.log(hoge); // ほげ
宣言のない代入

今までは、宣言と代入をまとめて行っていますが、当たり前なんですけど変数宣言と代入は別々に行えます。

let hoge, foo;
[hoge, foo] = [15, 21];

ただし、オブジェクトの分割代入では、前後に()が必須です。なぜなら、左辺の{...}はブロックと見なされ、それ単体で文とすることができないからです。

let hoge, foo;
({hoge, foo} = {hoge:'ほげ', foo: 'ふ~'});

長くなってきたので、今回はここまで区切ります。

次回もES6について書いていきます。
(関数あたりのところを)

Ethereumについて学んでみる

Ethereum(イーサリアム)ってなに?

Etherreumはそもそも分散アプリケーションのためのプラットフォームです。(公式サイト)

分散アプリケーションと中央管理型

gitなどのバージョン管理システムでも、中央管理型と分離型とありますが、それと似たようなものです。

f:id:front-end-engineer:20160712113646p:plain

※中央管理型

具体例としては、インスタにアップロードした画像データはFacebook(開発元)が中央で一元管理することにより、サービスが成り立っています。これらのサービスを運営するにあたって、中央で管理するシステムが必須でした。

中央管理型の欠点として良く言われるのが、

・中央管理システムが何かしらの理由で機能しなくなったり、障害が発生した場合のデータ消失などのリスクが大きい

・私的企業が一手に個々のデータを握ることはプライバシーの観点から好ましくない

そこで、Ethereumは「ブロックチェーン」と呼ばれる技術を用いて、特別な管理者のいらないP2P※システム上で、様々なサービス(仮想通貨を扱ったりなど)を実現するためのプラットフォームを提供するものです。

※対等な関係にある端末間を相互に直接接続し、データを送受信する通信方式。Facebookのメッセなど

どういうことかというと、Facebookといったような、中央で管理する機関(企業)を必要とせずに同様のサービスを実現できるプラットフォームを提供します。

ブロックチェーンとは?

ブロックチェーンの技術を一言でいうと、「不正が難しい取引台帳」です。

ここでいう不正は、取引の改ざんや二重取引などです。

仮想通貨のビットコインを使って説明していきます。

f:id:front-end-engineer:20160712142103j:plain※取引改ざん

f:id:front-end-engineer:20160712142159j:plain※二重取引

二重取引のようにAさんが10コインを持っていると仮定して、Bさんに5コインを送金し、Cさんに2コイン送金したとします。

取引内容としては下記のようになり、取引を1つ1つ管理せずに、1つのまとまり(ブロック)として管理します。まとめ方は一定時間内に発生した取引毎です。

・取引:A→B

・取引:A→C

さらに、このブロックにハッシュを付与します。

※ハッシュ:メッセージを特定するための暗号化技術

・取引:A→B  ハッシュ

・取引:A→C

一定時間を過ぎた後に新たな取引があった場合には新規にブロックを生成します。

では、BさんからCさんへ送金・CさんからAさんへ送金があったとします。

・取引:B→C  ハッシュ

・取引:C→A

 この時(新規ブロックを生成時)に、ひとつ前のブロックのハッシュも含めます

ただし、ハッシュはブロックごとに異なります(当たり前ですが)。

この関係により各ブロックは時系列でつながります。

・取引:A→B  ハッシュ

・取引:A→C

 ↓

・取引:B→C  ハッシュ

・取引:C→A

 改ざんするには後続にあるブロックも改ざんしていく必要があります。

そうしないと整合性がとれないからです。

ブロックがどんどん鎖のようにつながっていく様子からブロックチェーンと名づけられました。

上記では「データ構造」について説明してきましたが、実はブロックチェーンにはもう一つ重要な仕組みがあります。それは「データの管理方法」です。

ブロックチェーンは、「データ構造」と「データの管理方法」で成り立ってます。

 次回はデータの管理方法について見て行きます。

ファイルアップロードをドラッグ&ドロップでも可能にする

ファイルアップロードするには?

フロント側のHTMLもしくはPHPファイルにフォームの記述をします。

<form action="" method="post" enctype="multipart/form-data">
  <input type="file" value="ファイルを選択" name="image">
  <input type="value" value="送信">
</form>

画像を扱う場合にはenctype部分が必須になります。他にもinputタグにaccept属性を指定することにより、画像の形式をpng、jpgのみ受け付けるようにすることなどもできますが、今回は必要最低限で説明していきます。

次に画像ファイルを受け取る裏側を書いていきます。詳しくは説明しないんですが、1行目のif文で画像データが問題なく送られているかチェックします。問題がなければ4・5行目でアップロードしたファイル名と拡張子を取得します
ex)test.png→$filename=test、$extension=png

$tmp_pathでアップロード画像が仮で置かれているパスを取得します。

$file_dir_pathでアップロードした画像をどこに置くかを選択。今回は同階層のuploadフォルダに格納するようにする。

$uniq_nameは同じファイル名でアップロードした場合は上書きされて古い画像が消えてしまうのを避けるために格納する際にユニークな名前をつけて画像が上書きされるのを避けます。

入れ子になった最初のif文でPOSTでアップロードされたファイルか確認。次のif文でアップロードされたファイルを新しい位置に移動する。

移動できたら、パーミッション(アクセス権限)を設定。所有者は読み書きOK、それ以外は読み込みだけOK

if (!isset($_FILES['image']['error']) || !is_int($_FILES['image']['error'])){
  echo "ファイルアップロードエラー";
} else {
  $file_name = $_FILES['image']['name'];
  $extension = pathinfo($file_name, PATHINFO_EXTENSION); //拡張子取得
  $tmp_path = $_FILES['image']['tmp_name'];
  $file_dir_path = "upload/";
  $uniq_name = date("YmdHis").md5(uniqid(microtime(),1)).session_id() . "." . $extension;

  if (is_uploaded_file($tmp_path)) {
    if(move_uploaded_file( $tmp_path, $file_dir_path . $uniq_name)) {
      chmod($file_dir_path . $uniq_name, 0644);
    } else {
      echo "Error:アップロードできませんでした。";
    }
  }
}

これで必要最低限のシステムはできました。

ファイルアップロードをドラッグ&ドロップでも可能にするには?

今回のゴールは下記のようなドラッグ&ドロップでもアップロードを可能にするシステムです。

f:id:front-end-engineer:20160708150321p:plain

簡単な話ではありますが、上記で作ったシステムとjsを使えば実装できます。

jQueryは使わずに生のjsで書いてみます。その前に、フロント部分の記述を少し変更します。

cssは各々で調整してください。

<form action="" method="post" enctype="multipart/form-data">
  <div id="drag-drop-area">
    <div class="drag-drop-inside">
      <p class="drag-drop-info">ここにファイルをドロップ</p>
      <p>または</p>
      <input type="file" value="ファイルを選択" name="image">
      <p class="drag-drop-buttons"><input id="fileInput" type="file" value="ファイルを選択" name="image"></p>
      <input type="value" value="送信">
    </div>
  </div>
</form>

fileAreaでドラッグ&ドロップの有効範囲を設定。fileInputにfile属性のinputタグを代入しておく。

dragoverイベントはドラッグしている要素がドロップ領域にある間に発生

dragleaveイベントはドラッグしている要素がドロップ領域から出たときに発生

dropイベントはドラッグしている要素がドロップ領域にドロップされたときに発生

その他にもドラッグ&ドロップのイベントはあります。もっと知りたい方は下記のリンクを参考にしてください。
https://app.codegrid.net/entry/dnd-api-1

files変数にドロップされた画像のデータ情報が入り、そのデータをfileInputに渡しているだけです。

これで完成です。

※ドロップした時点でアップロードしたい際には、changeイベントを使えば可能です

var fileArea = document.getElementById('drag-drop-area');
var fileInput = document.getElementById('fileInput');


fileArea.addEventListener('dragover', function(evt){
  evt.preventDefault();
  fileArea.classList.add('dragover');
});

fileArea.addEventListener('dragleave', function(evt){
    evt.preventDefault();
    fileArea.classList.remove('dragover');
});
fileArea.addEventListener('drop', function(evt){
    evt.preventDefault();
    fileArea.classList.remove('dragenter');
    var files = evt.dataTransfer.files;
    fileInput.files = files;
});