1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

base62.jsをTypedCoffeeScriptで書き直してみたメモ

Last updated at Posted at 2013-11-23

自作のモジュール、base62.js ver0.4.1をTypedCoffeeScriptで書き直してみました。

セットアップ

とりあえずTypedCoffeeScriptをインストールするので、package.jsonにtyped-coffee-scriptを追加します。ついでにnpm-scriptを実行するとコンパイルできるようにしておきます。

commit cbfc1b149e023cea1407f21cf56941cc0f216e3c
Author: sasaplus1 <sasaplus1@gmail.com>
Date:   Sat Nov 23 14:29:26 2013 +0900

    appended base62.tc.js

diff --git a/package.json b/package.json
index f434cfe..4de1497 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
   },
   "scripts": {
     "bower": "bower install",
+    "c": "tcoffee --bare --js --output ./base62.tc.js --input ./coffee/base62.coffee",
     "fix": "fixjsstyle --strict --nojsdoc -r ./lib -r ./test ./index.js",
     "lint": "gjslint --strict --nojsdoc -r ./lib -r ./test ./index.js",
     "mini": "uglifyjs ./lib/base62.js --comments -cm -d 'module=void 0' -r base62 -o ./base62.min.js",
@@ -24,6 +25,7 @@
     "expect.js": "~0.2",
     "mocha": "~1.14",
     "testem": "~0.5",
+    "typed-coffee-script": "~0.8",
     "uglify-js": "~2.4"
   }
 }
$ mkdir ./coffee
$ touch ./coffee/base62.coffee
$ npm i

これで確かセットアップは大丈夫。コンパイルするときは

$ npm run-script c

です。

コードを書く

coffee/base62.coffee

do (global :: Object = this) ->

  isInteger :: Number -> Boolean = Number.isInteger or (value :: Number) ->
    isFinite(value) and value is Math.floor(value)

  createConverter :: String -> Base62 = (table :: String) ->
    base62 :: Base62 = if arguments.length <= 0 then new Base62 else new Base62(table)
    #base62.createConverter :: String -> Base62 = createConverter
    ##base62.createConverter :: String -> Base62
    ##base62.createConverter = createConverter
    base62.createConverter = createConverter
    base62

  class Base62

    table_ :: String
    createConverter :: String -> Base62

    constructor: (table :: String) ->
      if arguments.length <= 0
        table = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
      else if table.length isnt 62
        throw new Error 'parameter must be 62 chars'
      @table_ = table

    decode: (str :: String) ->
      throw new Error "parameter is unsupported format: #{str}" if not /^-?[\da-zA-Z]+$/.test str

      isNegative :: Boolean = str[0] is '-'
      str = str.slice 1 if isNegative

      table :: String = @table_

      decodedNum :: Int = 0
      i :: Int = str.length
      len :: Int = str.length

      while i--
        decodedNum += table.indexOf(str[i]) * Math.pow 62, len - i - 1

      if isNegative then decodedNum *= -1 else decodedNum

    encode: (num :: Int) ->
      throw new Error "parameter is not an integer: #{num}" if not isInteger num

      return '0' if num is 0

      isNegative :: Boolean = num < 0
      num = Math.abs num

      table :: String = @table_
      encodedStr :: String = ''

      while num > 0
        encodedStr = table.charAt(num % 62) + encodedStr
        num = Math.floor num / 62

      if isNegative then "-#{encodedStr}" else encodedStr

  if module?
    module.exports ###:: Base62### = createConverter()
    if process.env.NODE_ENV is 'test'
      module.exports.Base62_ = Base62
      module.exports.isInteger_ = isInteger
  else
    global.base62 ###:: Base62### = createConverter()
    if Mocha?
      global.base62.Base62_ = Base62
      global.base62.isInteger_ = isInteger

  return

こんな感じでしょうか。勘違いしてるところもままあるような。

さすがはCoffeeScriptで、コード量がだいぶ削減されたと思います。
型の無い生活に慣れたので意外と辛かった……
あと初めてCoffeeScriptでクラスを書いたような。

コンパイルしてみる

$ npm run-script c

でコンパイルしたJavaScriptが以下です。

// Generated by CoffeeScript 0.8.3
(function (global) {
  var Base62, createConverter, isInteger;
  isInteger = Number.isInteger || function (value) {
    return isFinite(value) && value === Math.floor(value);
  };
  createConverter = function (table) {
    var base62;
    base62 = arguments.length <= 0 ? new Base62 : new Base62(table);
    base62.createConverter = createConverter;
    return base62;
  };
  Base62 = function () {
    function Base62(table) {
      if (arguments.length <= 0) {
        table = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
      } else if (table.length !== 62) {
        throw new Error('parameter must be 62 chars');
      }
      this.table_ = table;
    }
    Base62.prototype.decode = function (str) {
      var decodedNum, i, isNegative, len, table;
      if (!/^-?[\da-zA-Z]+$/.test(str))
        throw new Error('parameter is unsupported format: ' + str);
      isNegative = str[0] === '-';
      if (isNegative)
        str = str.slice(1);
      table = this.table_;
      decodedNum = 0;
      i = str.length;
      len = str.length;
      while (i--) {
        decodedNum += table.indexOf(str[i]) * Math.pow(62, len - i - 1);
      }
      if (isNegative) {
        return decodedNum *= -1;
      } else {
        return decodedNum;
      }
    };
    Base62.prototype.encode = function (num) {
      var encodedStr, isNegative, table;
      if (!isInteger(num))
        throw new Error('parameter is not an integer: ' + num);
      if (num === 0)
        return '0';
      isNegative = num < 0;
      num = Math.abs(num);
      table = this.table_;
      encodedStr = '';
      while (num > 0) {
        encodedStr = table.charAt(num % 62) + encodedStr;
        num = Math.floor(num / 62);
      }
      if (isNegative) {
        return '-' + encodedStr;
      } else {
        return encodedStr;
      }
    };
    return Base62;
  }();
  if ('undefined' !== typeof module && null != module) {
    module.exports = createConverter();
    if (process.env.NODE_ENV === 'test') {
      module.exports.Base62_ = Base62;
      module.exports.isInteger_ = isInteger;
    }
  } else {
    global.base62 = createConverter();
    if ('undefined' !== typeof Mocha && null != Mocha) {
      global.base62.Base62_ = Base62;
      global.base62.isInteger_ = isInteger;
    }
  }
}(this));

ユニットテスト

ついでにユニットテストに通してみたり。

commit cbfc1b149e023cea1407f21cf56941cc0f216e3c
Author: sasaplus1 <sasaplus1@gmail.com>
Date:   Sat Nov 23 14:29:26 2013 +0900

    appended base62.tc.js

diff --git a/test/test-base62.js b/test/test-base62.js
index 2e1dff3..8e3716d 100644
--- a/test/test-base62.js
+++ b/test/test-base62.js
@@ -2,7 +2,7 @@ var expect, base62;
 
 if (typeof module !== 'undefined') {
   expect = require('expect.js');
-  base62 = require('../');
+  base62 = require('../base62.tc.js');
 } else {
   expect = this.expect;
   base62 = this.base62;

例外が投げられるかどうかのテストは当然失敗しますが、それ以外はちゃんと通ってました。

$ npm ts

> base62.js@0.4.1 test /Users/sasaplus1/Repos/git/base62.coffee
> NODE_ENV=test mocha



  base62
    #createConverter()
      ✓ should return Base62 instance
    #decode()
      ✓ should throw error if parameter is not a integer
      1) should throw error if parameter type is not a string
      ✓ should convert to integer from base62 string
    #encode()
      ✓ should throw error if parameter is not a integer
      2) should throw error if parameter type is not a number
      ✓ should convert to base62 string from integer
    #Base62()
      3) should throw error if parameter is not a string
      ✓ should throw error if parameter is not 62 chars
      ✓ should has default table
      ✓ should set another table
    #isInteger()
      ✓ should return false if parameter is not a number
      ✓ should return false if parameter is not a finite number
      ✓ should return false if parameter is floating-point number
      ✓ should return true if parameter is integer

  index
    ✓ should export some functions


  13 passing (105ms)
  3 failing

  1) base62 #decode() should throw error if parameter type i  1) base62 #decode() should throw errn to throw an exception
      at Assertion.assert (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/expect.js/expect.js:99:13)
      at Assertion.throwError.Assertion.throwException (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/expect.js/expect.js:155:10)
      at Context.<anonymous> (/Users/sasaplus1/Repos/git/base62.coffee/test/test-base62.js:50:23)
      at Test.Runnable.run (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runnable.js:211:32)
      at Runner.runTest (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runner.js:358:10)
      at /Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runner.js:404:12
      at next (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runner.js:284:14)
      at /Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runner.js:293:7
      at next (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runner.js:237:23)
      at Object._onImmediate (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runner.js:261:5)
      at processImmediate [as _immediateCallback] (timers.js:330:15)

  2) base62 #encode() should throw error if parameter type is not a number:
     Error: expected {} to be an instance of TypeError
      at Assertion.assert (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/expect.js/expect.js:99:13)
      at Assertion.a.Assertion.an [as a] (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/expect.js/expect.js:261:12)
      at Function.a (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/expect.js/expect.js:480:17)
      at fn (/Users/sasaplus1/Repos/git/base62.coffee/test/test-base62.js:97:25)
      at Assertion.throwError.Assertion.throwException (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/expect.js/expect.js:136:9)
      at Context.<anonymous> (/Users/sasaplus1/Repos/git/base62.coffee/test/test-base62.js:100:25)
      at Test.Runnable.run (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runnable.js:211:32)
      at Runner.runTest (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runner.js:358:10)
      at /Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runner.js:404:12
      at next (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runner.js:284:14)
      at /Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runner.js:293:7
      at next (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runner.js:237:23)
      at Object._onImmediate (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runner.js:261:5)
      at processImmediate [as _immediateCallback] (timers.js:330:15)

  3) base62 #Base62() should throw error if parameter is not a string:
     Error: expected {} to be an instance of TypeError
      at Assertion.assert (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/expect.js/expect.js:99:13)
      at Assertion.a.Assertion.an [as a] (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/expect.js/expect.js:261:12)
      at Function.a (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/expect.js/expect.js:480:17)
      at fn (/Users/sasaplus1/Repos/git/base62.coffee/test/test-base62.js:130:25)
      at Assertion.throwError.Assertion.throwException (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/expect.js/expect.js:136:9)
      at Context.<anonymous> (/Users/sasaplus1/Repos/git/base62.coffee/test/test-base62.js:133:23)
      at Test.Runnable.run (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runnable.js:211:32)
      at Runner.runTest (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runner.js:358:10)
      at /Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runner.js:404:12
      at next (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runner.js:284:14)
      at /Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runner.js:293:7
      at next (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runner.js:237:23)
      at Object._onImmediate (/Users/sasaplus1/Repos/git/base62.coffee/node_modules/mocha/lib/runner.js:261:5)
      at processImmediate [as _immediateCallback] (timers.js:330:15)



npm ERR! weird error 3
npm ERR! not ok code 0

$ git show

commit cbfc1b149e023cea1407f21cf56941cc0f216e3c
Author: sasaplus1 <sasaplus1@gmail.com>
Date:   Sat Nov 23 14:29:26 2013 +0900

    appended base62.tc.js

diff --git a/base62.tc.js b/base62.tc.js
new file mode 100644
index 0000000..5d4500d
--- /dev/null
+++ b/base62.tc.js
@@ -0,0 +1,77 @@
+// Generated by CoffeeScript 0.8.3
+(function (global) {
+  var Base62, createConverter, isInteger;
+  isInteger = Number.isInteger || function (value) {
+    return isFinite(value) && value === Math.floor(value);
+  };
+  createConverter = function (table) {
+    var base62;
+    base62 = arguments.length <= 0 ? new Base62 : new Base62(table);
+    base62.createConverter = createConverter;
+    return base62;
+  };
+  Base62 = function () {
+    function Base62(table) {
+      if (arguments.length <= 0) {
+        table = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+      } else if (table.length !== 62) {
+        throw new Error('parameter must be 62 chars');
+      }
+      this.table_ = table;
+    }
+    Base62.prototype.decode = function (str) {
+      var decodedNum, i, isNegative, len, table;
+      if (!/^-?[\da-zA-Z]+$/.test(str))
+        throw new Error('parameter is unsupported format: ' + str);
+      isNegative = str[0] === '-';
+      if (isNegative)
+        str = str.slice(1);
+      table = this.table_;
+      decodedNum = 0;
+      i = str.length;
+      len = str.length;
+      while (i--) {
+        decodedNum += table.indexOf(str[i]) * Math.pow(62, len - i - 1);
+      }
+      if (isNegative) {
+        return decodedNum *= -1;
+      } else {
+        return decodedNum;
+      }
+    };
+    Base62.prototype.encode = function (num) {
+      var encodedStr, isNegative, table;
+      if (!isInteger(num))
+        throw new Error('parameter is not an integer: ' + num);
+      if (num === 0)
+        return '0';
+      isNegative = num < 0;
+      num = Math.abs(num);
+      table = this.table_;
+      encodedStr = '';
+      while (num > 0) {
+        encodedStr = table.charAt(num % 62) + encodedStr;
+        num = Math.floor(num / 62);
+      }
+      if (isNegative) {
+        return '-' + encodedStr;
+      } else {
+        return encodedStr;
+      }
+    };
+    return Base62;
+  }();
+  if ('undefined' !== typeof module && null != module) {
+    module.exports = createConverter();
+    if (process.env.NODE_ENV === 'test') {
+      module.exports.Base62_ = Base62;
+      module.exports.isInteger_ = isInteger;
+    }
+  } else {
+    global.base62 = createConverter();
+    if ('undefined' !== typeof Mocha && null != Mocha) {
+      global.base62.Base62_ = Base62;
+      global.base62.isInteger_ = isInteger;
+    }
+  }
+}(this));
diff --git a/coffee/base62.coffee b/coffee/base62.coffee
new file mode 100644
index 0000000..04cd6aa
--- /dev/null
+++ b/coffee/base62.coffee
@@ -0,0 +1,71 @@
+do (global :: Object = this) ->
+
+  isInteger :: Number -> Boolean = Number.isInteger or (value :: Number) ->
+    isFinite(value) and value is Math.floor(value)
+
+  createConverter :: String -> Base62 = (table :: String) ->
+    base62 :: Base62 = if arguments.length <= 0 then new Base62 else new Base62(table)
+    #base62.createConverter :: String -> Base62 = createConverter
+    ##base62.createConverter :: String -> Base62
+    ##base62.createConverter = createConverter
+    base62.createConverter = createConverter
+    base62
+
+  class Base62
+
+    table_ :: String
+    createConverter :: String -> Base62
+
+    constructor: (table :: String) ->
+      if arguments.length <= 0
+        table = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+      else if table.length isnt 62
+        throw new Error 'parameter must be 62 chars'
+      @table_ = table
+
+    decode: (str :: String) ->
+      throw new Error "parameter is unsupported format: #{str}" if not /^-?[\da-zA-Z]+$/.test str
+
+      isNegative :: Boolean = str[0] is '-'
+      str = str.slice 1 if isNegative
+
+      table :: String = @table_
+
+      decodedNum :: Int = 0
+      i :: Int = str.length
+      len :: Int = str.length
+
+      while i--
+        decodedNum += table.indexOf(str[i]) * Math.pow 62, len - i - 1
+
+      if isNegative then decodedNum *= -1 else decodedNum
+
+    encode: (num :: Int) ->
+      throw new Error "parameter is not an integer: #{num}" if not isInteger num
+
+      return '0' if num is 0
+
+      isNegative :: Boolean = num < 0
+      num = Math.abs num
+
+      table :: String = @table_
+      encodedStr :: String = ''
+
+      while num > 0
+        encodedStr = table.charAt(num % 62) + encodedStr
+        num = Math.floor num / 62
+
+      if isNegative then "-#{encodedStr}" else encodedStr
+
+  if module?
+    module.exports ###:: Base62### = createConverter()
+    if process.env.NODE_ENV is 'test'
+      module.exports.Base62_ = Base62
+      module.exports.isInteger_ = isInteger
+  else
+    global.base62 ###:: Base62### = createConverter()
+    if Mocha?
+      global.base62.Base62_ = Base62
+      global.base62.isInteger_ = isInteger
+
+  return
diff --git a/package.json b/package.json
index f434cfe..4de1497 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
   },
   "scripts": {
     "bower": "bower install",
+    "c": "tcoffee --bare --js --output ./base62.tc.js --input ./coffee/base62.coffee",
     "fix": "fixjsstyle --strict --nojsdoc -r ./lib -r ./test ./index.js",
     "lint": "gjslint --strict --nojsdoc -r ./lib -r ./test ./index.js",
     "mini": "uglifyjs ./lib/base62.js --comments -cm -d 'module=void 0' -r base62 -o ./base62.min.js",
@@ -24,6 +25,7 @@
     "expect.js": "~0.2",
     "mocha": "~1.14",
     "testem": "~0.5",
+    "typed-coffee-script": "~0.8",
     "uglify-js": "~2.4"
   }
 }
diff --git a/test/test-base62.js b/test/test-base62.js
index 2e1dff3..8e3716d 100644
--- a/test/test-base62.js
+++ b/test/test-base62.js
@@ -2,7 +2,7 @@ var expect, base62;
 
 if (typeof module !== 'undefined') {
   expect = require('expect.js');
-  base62 = require('../');
+  base62 = require('../base62.tc.js');
 } else {
   expect = this.expect;
   base62 = this.base62;

その他

そういえば文法よくわかってなくて、以下のように書いたら

  isInteger = (value :: Number) Number.isInteger or (value :: Number) ->
    isFinite(value) and value is Math.floor(value)

以下のように出力されました。

  isInteger = 3(Number.isInteger || function (value) {
    return isFinite(value) && value === Math.floor(value);
  });

3ってなんだろ……

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?