聚會時間公告: 因應COSCUP 2011, Kalug 8月份休會一次

九月 4, 2015

小惡魔AppleBOY
AppleBOY
is about »

tag cloud

» Laravel Presenter 在 Controller 的使用

Laravel PHP Framework

Laravel Auto Presenter 是一套用在 view 裡的 decorates objects,搭配資料庫時,如果有需要組合或整合欄位來顯示相關資訊,這套就非常適合使用在 Laravel View 裡,如果不是透過 Laravel Auto Presenter,開發者也可以利用 Laravel Accessors & Mutators 來實現這方法,只是這要寫在 Model 層,寫法如下,此做法寫起來蠻亂的,而且也並不是每個地方都需要擴充這些欄位。

/**
 * The accessors to append to the model's array form.
 *
 * @var array
 */
protected $appends = [
    'is_twitter',
];

/**
 * Get the user's is_twitter flag.
 *
 * @param  string  $value
 * @return string
 */
public function getIsTwitterAttribute()
{
    return (bool) ($this->attributes['options'] & self::$OPTIONS['is_twitter']);
}

如果是使用 Laravel Auto Presenter 則寫法如下

<?php  namespace App\Models\Presenters;

use App\Models\User;
use Illuminate\Contracts\Support\Arrayable;
use McCool\LaravelAutoPresenter\BasePresenter;

class UserPresenter extends BasePresenter implements Arrayable
{
    public function __construct(User $resource)
    {
        $this->wrappedObject = $resource;
    }

    public function isTwitter()
    {
        return (bool) ($this->wrappedObject->info->options & self::$OPTIONS['is_twitter']);
    }

    /**
     * Get the instance as an array.
     *
     * @return array
     */
    public function toArray()
    {
        return [
            'id'        => $this->wrappedObject->id,
            'isTwitter' => $this->isTwitter(),
            'avatarUrl' => $this->avatarUrl(),
        ];
    }
}

但是使用在 Laravel View 裡面都可以正確拿到 isTwitter 欄位沒問題,如果要用在 RESTFul + 前端 ReactJS 的話,則要透過 Laravel Facades 來實現,加上 AutoPresenter::decorate 就可以在 Controller 內使用 AutoPresenter 了。

<?php

use McCool\LaravelAutoPresenter\Facades\AutoPresenter;

class MessageController extends Controller
{
    /**
     * @var Repository
     */
    private $user;

    public function __construct(UserRepository $user)
    {
        $this->user = $user;
    }

    public function index(Request $request)
    {
        $user = $this->user->with('info')->find(\Auth::id());
        $state = collect([
            'user' => AutoPresenter::decorate($user),
        ]);

        return $state;
    }
}

八月 11, 2015

小惡魔AppleBOY
AppleBOY
is about »

tag cloud

» Facebook React Jest 搭配 Webpack 測試

logo_og

Facebook React 就是要搭配 Webpack,Webpack 已經是前端開發的必備工具,要測試 React Component 就是要用 Facebook 開發的 Jest 框架,使用 Webpack 也許會搭配 Less or Sass Loader 讓每個 component 都可以獨立有 CSS 檔案。要在 JS 內直接引入 CSS 檔案寫法如下

import '!style!css!less!./Schedule.less';
import React, { Component } from 'react';

透過 webpack 就可以把這些 header 檔案 build 成單一檔案,但是遇到的問題是,如果搭配 Jest 測試,我們使用了 babel-jest 來做 script preprocessor 如下

{
  "devDependencies": {
    "babel-jest": "*",
    "jest-cli": "*"
  },
  "scripts": {
    "test": "jest"
  },
  "jest": {
    "scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
    "testFileExtensions": ["es6", "js"],
    "moduleFileExtensions": ["js", "json", "es6"]
  }
}

接著執行 npm test,可以看到會噴出 !style!css!less!./Schedule.less not found,原因就是 babel 看不懂 !style!css!less!,這是 webpack loader 特有的寫法,要解決這問題有兩種方式

  • 所有 Less 檔案全部寫在 Top 目錄內的 app.js
  • 改寫 script preprocessor

第一種方法就是把 less 檔案都獨立寫到最外層的 app.js 檔案,因為通常 app.js 幾乎不用寫測試,裡面只是單純的 initial component 而已,這是一種方法,但是我覺得不是很好,第二種做法是直接將 babel-jest 轉出來的程式碼將 less 部分抽掉,再跑測試。首先在主目錄建立 jest-script-preprocessor.js 內容如下,透過 Regular expression 將 .less 模組去掉。

var babelJest = require('babel-jest');

module.exports = {
  process: function(src, filename) {
    return babelJest.process(src, filename)
      .replace(/require\(\'[^\']+\.less\'\);/gm, '');
  }
};

然後修改 scriptPreprocessor

"jest": {
  "scriptPreprocessor": "<rootDir>/jest-script-preprocessor",
  "unmockedModulePathPatterns": [
    "<rootDir>/node_modules/react",
    "<rootDir>/node_modules/react-tools"
  ],
  "testFileExtensions": [
    "js",
    "jsx"
  ],
  "moduleFileExtensions": [
    "js",
    "json",
    "jsx"
  ]
}

最後建立 __tests__ 目錄,寫下第一個測試檔案

jest.dontMock('../index');

import React from 'react/addons';
let TestUtils = React.addons.TestUtils;
let About;

describe('About Test', () => {

  beforeEach(function() {
    About = require('../index');
  });

  it('should exists', function() {
    // Render into document
    let about = TestUtils.renderIntoDocument(<About />);
    expect(TestUtils.isCompositeComponent(about)).toBeTruthy();
  });
});

現在 Jest 也內建產生 coverage report 只要加上 --coverage 參數即可

Using Jest CLI v0.4.18
 PASS  src/components/About/__tests__/About.js (2.109s)
1 test passed (1 total)
Run time: 2.381s
------------|----------|----------|----------|----------|----------------|
File        |  % Stmts | % Branch |  % Funcs |  % Lines |Uncovered Lines |
------------|----------|----------|----------|----------|----------------|
 About/     |      100 |      100 |      100 |      100 |                |
  index.jsx |      100 |      100 |      100 |      100 |                |
------------|----------|----------|----------|----------|----------------|
All files   |      100 |      100 |      100 |      100 |                |
------------|----------|----------|----------|----------|----------------|

All reports generated

詳細資訊可以參考 Jest Issue 有網友發了 How to test with Jest when I’m using webpack 問題,有興趣的朋友們可以參考看看。

support:

biggo.com.tw

biggo.sg

A Django site.