# Container

Container 是一種分類模式,可能是一個資料庫、也有可能是一種雲端服務等的資料群組的載體,最主要的功用即是裝載 Model。

// 定義好 Model 結構
const profileModel = {
    body: {
        name: []
    }
}
// 將 Model 註冊進 Container
const userContainer = {
    models: {
        profile: profileModel
    }
}
// 將 Container 註冊進 Alas
const alas = new Alas({
    containers: {
        user: userContainer
    }
})
// 指定實體化 user container 的 profile model
const profile = alas.make('user', 'profile').$init({
    name: 'james'
})
console.log(profile.name) // james

該設計本身是很重要的部分是專案的遷移,在複數專案指向同一個 API 的時候,這些 Model 只要跟著 Container 複製到另一個專案或是託管至套件管理工具( 如 npm ),就可以建立一種隨開即用的開發模式。

# 與外部的資料交換

既然 Container 允許我們跨專案複製,意味著必須要有外部接口提供個專案的自定義環境,以下範例是假如我們必須為每位 User 注入專案所在的地區:

const profileModel = {
    body: {
        name: [],
        location: []
    },
    init(self, { name }) {
        return {
            name,
            // self.$config 指向 container config
            location: self.$config.location
        }
    }
}
const userContainer = {
    models: {
        profile: profileModel
    },
    // 在註冊 Core 之前,可以定義預設的 Config 數值
    config: {
        location: 'us'
    },
    // 在註冊進入 Core 之後就會執行 install 並獲取註冊端的 option
    install(core, config, options) {
        if (options.location) {
            config.location = options.location
        }
    }
}
const alas = new Alas({
    containers: {
        // 使用 Array 作為參數可以提供 options 給 install 的對象
        user: [userContainer, {
            location: 'tw'
        }]
    }
})
const profile = alas.make('user', 'profile').$init({
    name: 'james'
})
console.log(profile.location) // tw

# 指定實作對象

Interface 參數的目的是在位於這個 Container 內的 Model 必須實作那些方法,如果沒有實作,將在註冊時發出錯誤並註冊失敗:

const profileModel = {
    body: {
        name: []
    }
}
const userContainer = {
    models: {
        profile: profileModel
    },
    // 指定底下 Model 必須實作指定的參數或名稱,目前涵蓋以下四種結構
    interface: {
        body: ['name', 'age'],
        views: [],
        methods: [],
        loaders: []
    }
}
const alas = new Alas({
    containers: {
        // 這段程式碼會在執行期間擲出錯誤,因為 body 沒有 age 屬性
        user: userContainer
    }
})

# 共用規則與語系

在 Container 可以定義自己的驗證規則與語系,使轉移專案更加方便。

const profileModel = {
    body: {
        // 會員資料的名稱只能是英文
        name: ['onlyEnglish']
    }
}
const container = {
    models: {
        profile: profileModel
    },
    // 定義驗證規則
    rules: {
        onlyEnglish: (model, value) => {
            if (/^[a-zA-Z]+$/.test(value)) {
                return true
            } else {
                return model.$meg('onlyEnglish')
            }
        }
    },
    // 定義自有的語系
    locales: {
        'zh-tw': {
            onlyEnglish: '只能是英文'
        },
        'en-us': {
            onlyEnglish: 'Only English'
        }
    }
}
const alas = new Alas({
    containers: {
        user: userContainer
    }
})
// 顯示的語系隨著 Core 本身定義顯示
alas.setLocale('tw')
const profile = alas.make('user', 'profile').$init({
    name: '詹姆士'
})
console.log(profile.$validateBy('name')) // 只能是英文
const containerOptions = {
    models: { ... }
}
const alas = new Alas({
    container: {
        myContainer: containerOptions
    }
})

# Types

import Alas, { ContainerStructure, ContainerOptions, ModelStructure } from 'alas'
type UserContainerStructure = ContainerStructure<{
    models: {
        [key: string]: ModelStructure
    }
}>

const userOptions: ContainerOptions<UserContainerStructure> = {
    models: {
        user: { ... }
    }
}

type Containers = {
    user: UserContainerStructure
}

const alas = new Alas<Containers>({
    containers: {
        user: userOptions
    }
})

# Options

# models

Container 底下所有的 Model。

  • type: { [key: string]: ModelOptions }
  • required: false
const myModel = {
    body: {
        attr: []
    }
}
const containerOptions = {
    models: {
        myModel
    }
}

# config

提供給底下 Model 統一參照的物件。

  • type: { [key: string]: any }
  • required: false
const myModel = {
    body: {
        amount: []
    },
    views: {
        amount: self => self.amount * self.$config.exchangeRate
    }
}
const containerOptions = {
    config: {
        exchangeRate: 2
    }
}

# install

與外部進行資料交換的方法。

  • type: (core: Alas, config: ContainerOptionsConfig, options: any) => any
  • required: false
const containerOptions = {
    install(core, config, options) {}
}

# rules

定義專屬規則。

  • type: { [key: string]: RuleHandler }
  • required: false
const myModel = {
    body: {
        amount: ['int']
    }
}
const containerOptions = {
    rules: {
        int(model, value) {
            return value.toString().match('.') ? '必須為整數' : true
        }
    }
}

# locales

定義專屬語系。

  • type: { [key: string]: { [locale: string]: string } }
  • required: false
const myModel = {
    body: {
        amount: ['int']
    },
    views: {
        amount: self => self.amount + self.$meg('amount')
    }
}
const containerOptions = {
    locales: {
        'en-us': {
            'amount': '金額'
        }
    }
}