# Tool

## Property

```typescript
const tool = {
    install: 'function',
    handler: 'function', // required
    request: 'array',
    response: 'string',
}
```

### Install

第一次呼叫時執行，只會執行一次。

{% hint style="info" %}
Install只允許同步執行，若執意使用非同步請求可以參閱[Loader](https://packhouse-doc.metalsheep.com/utils#loader)。
{% endhint %}

```javascript
const tool = {
    install({ packhouse, store, include, utils, group }) { ... }
}
```

#### Packhouse

實例化的Packhouse對象，主要在於運用[Plugin](https://packhouse-doc.metalsheep.com/the-instance/plugin)。

#### Store

變數的容器，能在`handler`中被獲取。

```javascript
const tool = {
    install({ store }) {
        store.demo = 5
    },
    handler(self) {
        console.log(self.store.demo) // 5
    }
}
```

#### Include

引用其他的Tool必須經過`include`，該接口也能引用[其他Group](https://packhouse-doc.metalsheep.com/the-instance/group/..#mergers)或[Line](https://packhouse-doc.metalsheep.com/the-instance/group/line)。

```javascript
const group = {
   tools: {
      sum: {
         handler: (self, v1, v2) => self.success(v1 + v2)
      },
      double: {
         install({ include }) {
            // 參數一是引用名，第二個參數是tool name。
            include('sum').tool('sum')
         },
         handler(self, v1) {
            self.tool('sum')
                .action(v1, v1, (error, result) => {
                   self.success(result)
                })
         }
      }
   }
}
```

#### Group

與Group交換資料的管道，也意味著能與外部的參數進行溝通：

```javascript
const group = {
   install(group, options) {
      group.locale = options.locale
   },
   tools: {
      where: {
         install({ store, group }) {
            store.locale = group.locale
         },
         handler(self) {
            self.success(self.store.locale)
         }
      }
   }
}

packhouse.addGroup('locale', () => {
   return {
      data: group,
      options: {
         locale: '台灣'
      }
   }
})

packhouse.tool('locale/where').action((error, result) => {
   console.log(result) // '台灣'
})
```

#### Utils

可以使用Packhouse提供的工具組，詳情參照[Utils](https://packhouse-doc.metalsheep.com/the-instance/utils)。

## Handler

Tool被呼叫時的主執行續。

{% hint style="info" %}
如果整個`handler`的過程是同步的，那`action`的執行是完全同步的，但建議把所有的行為視為非同步。
{% endhint %}

```javascript
const tool = {
    handler(self, ...args) {
        self.success()
    }
}
```

### Self

主要的流程控制單元，固定在第一個參數上，以下是它的Property：

#### success

回傳成功結果

#### error

回傳錯誤結果

#### casting

回傳與驗證參數，詳情請參閱[Mold Casting](https://packhouse-doc.metalsheep.com/the-instance/mold#casting)。

#### line

引用`include`的[Line](https://packhouse-doc.metalsheep.com/the-instance/group/line)。

#### tool

引用`include`的Tool。

#### assess

驗證第一個參數是否為null或undefined，若不是則回傳第一個參數，若成立則回傳第二個參數。

> assess可以接受一個Callback，當第二個參數成立時可觸發並回傳該方法的結果。

```javascript
const group = {
   tools: {
      sum: {
         request: ['number', 'number'],
         handler: (self, v1, v2) => self.success(v1 + v2)
      },
      doubleAndToInt: {
         install({ include }) {
            include('sum').tool('sum')
         },
         handler(self, v1) {
            self.tool('sum')
                .action(v1, v1, self.assess(result => parseInt(result)))
         }
      }
   }
}

// ... 省略註冊行為

packhouse.tool('math/doubleAndToInt').action(10.2, (error, result) => {
   console.log(result) // 20
})

packhouse.tool('math/doubleAndToInt').action('10.2', (error, result) => {
   console.log(error != null) // true
})
```

### Lazy First

在Cloud Function的型態下，我們只要專注在單一功能上，意味著每次執行只要針對目的`require`所需的模塊即可。

{% hint style="info" %}
並不建議將`require`的行為建立在`install`的行為內，除了可讀性差之外也可能造成`this`指向錯誤或影響單元測試等行為。
{% endhint %}

```javascript
// 這是個糟糕的例子😢
let moment = require('moment')
const badTool = {
    handler(self) {
        self.success(moment())
    }
}
```

```javascript
// 這是個優良案例 🤣
const tool = {
    handler(self) {
        let moment = require('moment')
        self.success(moment())
    }
}
```

### Async

Install必須是同步的，但Handler允許非同步運作。

```javascript
const tool = {
    handler: async self => setTimeout(self.success, 100)
}
```

## Request & Response

指定參數與回傳值的型態與驗證對象，比對方法詳情請參照[Mold](https://packhouse-doc.metalsheep.com/the-instance/group/mold)。

```javascript
const tools = {
    sum: {
        request: ['number', 'number'],
        response: 'number',
        handler(self, v1, v2) {
            self.success(v1 + v2)
        }
    }
}
```
