関数型プログラミング(？？)でストレージ操作

2015/02/16 追記
[続き](http://qiita.com/youkaiantena/items/201eb8f96ccf315e74d6)みたいなのを書きました。

**「関数型プログラミングってカッコイイ！！！」**と思ったので、勉強し始めました。
ちょっと勉強してみたところで何か書いてみたくなったので、ストレージ管理のModelを書いてみました。

_※ 本Modelを使用する際はオブジェクト指向的に、内部処理のみ関数型プログラミングのつもりで書いています。_

が、しかし

「これ、関数型プログラミングとは言えないんじゃないか？？」という指摘を多々頂いたので、Qiitaに公開してどこがどうダメなのかを教えていただけたらな、と思いました。

ライブラリには[Lo-Dash v2.4.1](https://lodash.com/ "Lo-Dash")を使用しています。

皆様、宜しくお願い致します。

```JavaScript:storage.js
/**
 * ストレージ管理
 */

module.exports = (function(){
  /**
   *
   * @param type local|session|cookie
   * @constructor
   */
  function StorageModel(type){
    this.myStorages = [
      {
        'type':      'local',
        'available': isSupport(localStorage),
        'set':       setLocalStorageItem,
        'get':       getLocalStorageItem,
        'remove':    removeLocalStorageItem
      },
      {
        'type':      'session',
        'available': isSupport(sessionStorage),
        'set':       setSessionStorageItem,
        'get':       getSessionStorageItem,
        'remove':    removeSessionStorageItem
      },
      {
        'type':      'cookie',
        'available': isSupport(document.cookie),
        'set':       setCookie,
        'get':       getCookie,
        'remove':    removeCookie
      }
    ];

    // 使用するストレージ
    this.myStorage = decideStorage(findStorageFromMap(type, this.myStorages), this.myStorages);
  }

  /**
   * ストレージへの保存
   *
   * @param {String} key
   * @param {String} value
   * @param {int} expireDays
   */
  StorageModel.prototype.setItem = function(key, value, expireDays){
    this.myStorage.set.apply(this, [key, value, expireDays]);
  };

  /**
   * ストレージからの取得
   *
   * @param {String} key
   * @return {mixed}
   */
  StorageModel.prototype.getItem = function(key){
    return this.myStorage.get.apply(this, [key]);
  };

  /**
   * ストレージからの削除
   *
   * @param {String} key
   */
  StorageModel.prototype.removeItem = function(key){
    this.myStorage.remove.apply(this, [key]);
  };

  /**
   * 指定されたストレージ機能をサポートしているか否か
   *
   * @param {Object} storage localStorage|sessionStorage|document.cookie
   * @returns {boolean}
   */
  function isSupport(storage){
    return (_.isUndefined(storage)) ? false : true;
  }

  /**
   * ストレージの取得
   *
   * @param {String} type local|session|cookie
   * @param {Array} storages
   * @returns ストレージ
   */
  function findStorageFromMap(type, storages){
    return _.find(storages, function(st){
      return st.type == type;
    });
  }

  /**
   * 使用ストレージタイプの決定
   * ただし、localStorage|sessionStorageが存在しない場合はcookieになる
   *
   * @param {Object} localStorage|sessionStorage|document.cookie
   * @returns {Object} local|session|cookie
   */
  function decideStorage(storage, storages){
    return (_.isUndefined(storage) === false && storage.available === true) ? storage : _.find(storages, function(st){ return st.type == 'cookie'; });
  }

  /**
   * localStorageへの保存
   *
   * @param {String} key
   * @param {String} value
   * @return localStorage
   */
  function setLocalStorageItem(key, value, expireDays){
    localStorage.setItem(key, JSON.stringify(value));

    return localStorage;
  }

  /**
   * sessionStorageへの保存
   *
   * @param {String} key
   * @param {String} value
   * @return sessionStorage
   */
  function setSessionStorageItem(key, value, expireDays){
    sessionStorage.setItem(key, JSON.stringify(value));

    return sessionStorage;
  }

  /**
   * Cookieへの保存
   *
   * @param {String} key
   * @param {String} value
   * @param {int} expireDays
   * @return document.cookie
   */
  function setCookie(key, value, expireDays){
    document.cookie = generateCookieData(key, value, expireDays, generateCookieExpireDateString);

    return document.cookie;
  }

  /**
   * Cookie保存用文字列の生成
   *
   * @param {String} key
   * @param {String} value
   * @param {int} expire
   * @param {function} generator
   */
  function generateCookieData(key, value, expire, generator){
    return encodeURIComponent(key) + '=' + encodeURIComponent(value) + generator.call(null, expire);
  }

  /**
   * Cookieの保存期間文字列生成
   *
   * @param {int} expireDays
   * @param {String} utc
   */
  function generateCookieExpireDateString(expireDays){
    if (_.isUndefined(expireDays)) {
      return '';
    }

    var current = new Date();
    var date = new Date(current.getTime() + (60 * 60 * 24 * 1000 * expireDays));

    return '; expires=' + date.toUTCString();
  }

  /**
   * Cookie削除の為の保存期間文字列生成
   *
   * @param {String} utc
   */
  function generateCookieRemoveDateString(){
    return '; expires=Thu, 01 Jan 1970 00:00:00 GMT';
  }

  /**
   * localStorageから取得
   *
   * @param {String} key
   * @return {mixed}
   */
  function getLocalStorageItem(key){
    return JSON.parse(localStorage.getItem(key));
  }

  /**
   * sessionStorageから取得
   *
   * @param {String} key
   * @return {mixed}
   */
  function getSessionStorageItem(key){
    return JSON.parse(sessionStorage.getItem(key));
  }

  /**
   * Cookieから取得
   *
   * @param {String} key
   * @return {mixed}
   */
  function getCookie(key){
    // Cookie情報を;で区切って配列化
    // その後、=で区切って、{key: value}の形に生成
    // 返ってきた値から、keyを使用して値を取得する
    return (function(list){
      return (hasCookie(key, list)) ? decodeURIComponent(list[key]) : null;
    })(convertCookieToArray());
  }

  /**
   * Cookie情報の配列化
   *
   * @return {Array}
   */
  function convertCookieToArray(){
    return _.reduce(_.toArray(document.cookie.split(';')), function(result, cookie){
      var data = cookie.split('=');
      var cookieKey   = data[0],
          cookieValue = data[1];

      result[cookieKey.trim()] = cookieValue;

      return result;
    }, {});
  }

  /**
   * 指定されたCookieが存在するか否か
   *
   * @param {String} key
   * @param {Array} cookies
   * @return {boolean} 存在する場合:true|存在しない場合:false
   */
  function hasCookie(key, cookies){
    return _.has(cookies, key);
  }

  /**
   * localStorageから削除
   *
   * @param {String} key
   */
  function removeLocalStorageItem(key){
    localStorage.removeItem(key);

    return localStorage;
  }

  /**
   * sessionStorageから削除
   *
   * @param {String} key
   */
  function removeSessionStorageItem(key){
    sessionStorage.removeItem(key);

    return sessionStorage;
  }

  /**
   * Cookieから削除
   *
   * @param {String} key
   */
  function removeCookie(key){
    if (hasCookie(key, convertCookieToArray())) {
      document.cookie = generateCookieData(key, '', undefined, generateCookieRemoveDateString);
    }

    return document.cookie;
  }

  return StorageModel;
})();
```

```javascript:app.js
var storage = require('./storage');

var StLocal   = new storage('local'),
    StSession = new storage('session'),
    StCookie  = new storage('cookie');

StLocal.setItem('foo', '1234abcd');
StSession.setItem('foo', 'hijk5678');
StCookie.setItem('foo', '90opqrstu');

console.log(StLocal.getItem('foo');   // 1234abcd
console.log(StSession.getItem('foo'); // hijk5678
console.log(StCookie.getItem('foo');  // 90opqrstu
```