最新の投稿

django-blog-2
カテゴリー:django 作成日:2024年6月13日12:29

djangoでBlog その2

今回(djangoでBlog その1の続き)はdjango-breezeを使って

djnago + inertia.js + react + tailwind.css + viteをubuntu22.04LTSに簡単に構築する方法

完成するとこんな感じです

django-breezeを使えるようにする

django-breeze-githubサイト

mkdir djb-react && cd djb-react
pyenv local 3.8.10

nodenv local 18.16.1

python -m venv venv
source venv/bin/activate

pipを最新版にする
(venv) pip install --upgrade pip
(venv) pip install django-breeze

(venv) django-breeze startproject conf . //「.」を忘れないように

(venv) django-breeze startapp app


react 又は vue3が選択可能のようです

今回はreactを選択

(venv) django-breeze create-app react 
vueを選択する場合は
(venv) django-breeze create-app vue3
とします

conf/settings.pyに以下設定

INSTALLED_APPS = [
  #..............
  'django_breeze',
  'app'
  #..............
]

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

USE_I18N = True

USE_TZ = True

# axios 設定追加 今回はaxiosは使わないが一応設定しとく

CSRF_HEADER_NAME = 'HTTP_X_XSRF_TOKEN'
CSRF_COOKIE_NAME = 'XSRF-TOKEN'


npm install も忘れずに行ってください

ここまででほぼ設定まで完了しています


src/main.jsx ここにはdjangoとreactをinetertia.jsを使って連結する内容が書かれています。

src/index.cssにはtailwind cssが書かれています。

@tailwind base;
@tailwind components;
@tailwind utilities;



ただreactでimportする時src以下を@/で置き換える為 vite.config.jsに以下を追加

resolve: {
    resolve: {
      extensions: [".js", ".jsx", ".json"],
    },
  // ここから
    alias: {
      '@': resolve(__dirname, './src')
    },
 // ここまで追加
},


/src/Layout/MyLayoutを
import MyLayout from '@/Layout/MyLayout';
とできます。
階層が深くなった時にも '../../MyLayout'いや'../../../MyLayout'だったかな?
と悩まずにすみます。

後tailwindは多機能な感じで良いのですが少し楽をしたいのでflowbite flowbite-reactを入れます

npm install flowbite flowbite-react

tailwind.config.jsをflowbite対応に書き換えます

/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./src/index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
    'node_modules/flowbite-react/**/*.{js,jsx,ts,tsx}',
    'node_modules/flowbite/**/*.{js,jsx,ts,tsx}',
  ],
  theme: {
    extend: {},
  },
  plugins: [
    /** forms, **/
    require('flowbite/plugin'),
  ],
};

それとreactでiconを使いたいので lucide-react 日付を日本語で表示したいので momentをインストール

npm install moment lucide-react

djangoには marshmallowをインストール
databaseをjsonへ変換したりvalidation機能もあります

(venv)pip install marshmallow



1.データを作ります app/models.py

from django.db import models
from django.core import validators
from django.utils.timezone import now

class Item(models.Model):

    SEX_CHOICES = (
        (1, '男性'),
        (2, '女性'),
    )

    name = models.CharField(
        verbose_name='名前',
        max_length=200,
    )
    age = models.IntegerField(
        verbose_name='年齢',
        validators=[validators.MinValueValidator(1)],
        blank=True
    )
    sex = models.IntegerField(
        verbose_name='性別',
        choices=SEX_CHOICES,
        default=1
    )
    memo = models.TextField(
        verbose_name='備考',
        max_length=300,
        blank=True
    )
    created_at = models.DateTimeField(
        verbose_name='登録日',
        default=now
    )

    # 管理サイト上の表示設定
    def __str__(self):
        return self.name

    class Meta:
        verbose_name = 'アイテム'
        verbose_name_plural = 'アイテム'

ここで気をつける事は

from django.utils.timezone import now

created_at = models.DateTimeField(
       verbose_name='登録日',
        default=now
)



登録日は now関数を使う事です auto_now_add=Trueauto_now=True にすると後で日付を書き換える事ができないからです。

2. djangoに登録します adminのユーザとパスワードも作成します

(venv)python manage.py makemigrations
(Venv)python manage.py migrate

## admin User, Passwordを作る
(venv)python manage.py createsuperuser エンターキーを叩くとUser,Email,passwordを聞いてきます。
例:
User :demo
Email: demo@email.com
Password: password!!11AA
Password確認: password!!11AA
Passwordは先頭がアルファベットで大文字小文字を各1個以上数値も含ませ8文字以上との制約があります。 

3.conf/urls.pyを修正します

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include("app.urls")),
]

4.app/urls.pyを作成して編集します

from django.urls import path

from . import views

app_name = 'app'

urlpatterns = [
    path("", views.index, name="index"),
    path("create/", views.create, name="create"),
    path("store/", views.store, name="store"),
    path("edit/<int:id>", views.edit, name="edit"),
    path("update/<int:id>", views.update, name="update"),
    path("delete/<int:id>", views.delete, name="delete"),
    path("show/<int:id>", views.show, name="show"),
]

5.続いて app/views.pyを編集していきます

from django.shortcuts import redirect
from inertia import render, share
from .models import Item
from .serializers import ItemSchema  # <-serializers.pyこのあと作ります
from marshmallow import ValidationError

def index(request):
  obj = Item.objects.order_by('-created_at')
  # auth_bool = request.user.is_superuser
  return render(request, 'Items/Index', props={
    'items': obj,
  })

def create(request):
  return render(request, 'Items/Create', {})

def show(request, id):
    obj = Item.objects.get(id=id)
    return render(request, 'Items/Show', props={'item': obj})

def store(request):
  if request.method == 'POST':
    try:
      schema = ItemSchema()
      data = schema.loads(request.body)
      obj = Item.objects.create(**data)
    except ValidationError as err:
      share(request, error="Exists errors on form")
      share(request, error=err.messages)
    else:
      share(request, success=f"Item {obj.name} created")
      return redirect("app:index")

def edit(request, id):
  obj = Item.objects.get(id=id)
  return render(request, 'Items/Edit', props={'item': obj})

def update(request, id):
  obj = Item.objects.get(id=id)
  schema = ItemSchema()
  param = schema.loads(request.body)
  obj.name = param["name"]
  obj.age = param["age"]
  obj.sex = param["sex"]
  obj.memo = param["memo"]
  obj.save()
  #obj.objects.filter(id=id).update(**data)
  share(request, success=f"Item {obj.name} update")
  return redirect("app:index")

def delete(request, id):
  obj = Item.objects.get(id=id)
  obj.delete()
  share(request, success="Item Deleted")
  return redirect("app:index")

6. app/serializers.pyを作成し編集します

from marshmallow import Schema, fields, validate

class ItemSchema(Schema):
    id = fields.Int()
    name = fields.Str(validate=validate.Length(min=1))
    age = fields.Int()
    sex = fields.Int()
    memo = fields.Str()
    created_at = fields.DateTime()



7. src/pagesにItemsフォルダーをつくりItems/Index.jsxを作ります

mkdir -p src/pages/Items
touch src/pages/Items/Index.jsx
touch src/pages/Items/Create.jsx
touch src/pages/Items/Edit.jsx
touch src/pages/Items/Show.jsx



8. /src/pages/Items/Index.jsxを編集します

import { useState, useEffect } from 'react';
import { router, Link } from '@inertiajs/react';
import MyLayout from '@/Layout/MyLayout';
import moment from 'moment';


export default function Index({items}){

    const [prePage] = useState(6)

    const [totalPage] = useState(items.length)

    const [currentPage, setCurrentPage] = useState(1)

    const nextPage = () => {
        if (currentPage !== Math.ceil(items.length / prePage)) {
            setCurrentPage(currentPage+1)
        }
    }

    const prevPage = () => {
        if(currentPage !== 1){
            setCurrentPage(currentPage-1)
        }
    }

    const gotoPage = (page) => {
        setCurrentPage(page)
    }

    const [lcount] = useState(Math.ceil(totalPage / prePage))

    const pageNumbers = [];
    for (let i=1; i<Math.ceil(totalPage/prePage)+1; i++){
        pageNumbers.push(i);
    }

    function deletePost( id ) {
        if (confirm(`No.${id}を削除してよろしいですか`)) {
            router.delete(`/delete/${id}`);
        }
    }

    function dateFormat(data) {
        return  moment(data).format('YYYY年MM月DD日 HH:mm')
    }

    function get_sex_display(sex) { return (sex === 1) ? '男性' : '女性' }

    return (
        <>
        <MyLayout>
            <div className="flex flex-wrap columns-2 md:columns-3 lg:columns-4">
            { items.slice((currentPage -1) * prePage, currentPage * prePage).map( (item) => (
            <div key={item.id}   className="mt-4 max-w-sm rounded border-gray-400 overflow-hidden shadow-xl">
                <div className="p-4 w-72">
                <h2 className="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
                    <p>{item.name}</p>
                </h2>
                <p  className="font-normal text-gray-700 dark:text-gray-400">
                        年齢 {item.age} 歳
                </p>
                <p className="font-normal text-gray-700 dark:text-gray-400">
                    性別 { get_sex_display(item.sex) }
                </p>
                <p className="font-normal text-gray-700 dark:text-gray-400">
                    備考 { item.memo }
                </p>
                <p className="font-normal text-gray-700 dark:text-gray-400">
                    登録日 {dateFormat(item.created_at)}
                </p>
                <div className="px-6 pt-4 pb-2">
                    <Link href={`/show/${item.id}`} className="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2">
                            確認
                    </Link>
                    <Link href={`/edit/${item.id}`} className="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2">
                            編集
                    </Link>
                    <button onClick={() => deletePost(item.id)} className="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-red-700 mr-2 mb-2">
                            削除
                    </button>
               </div>
            </div>
            </div>
            ))}
            </div>
            <br />
            <nav aria-label="Page navigation example">
            <ul className="flex items-center  -space-x-px h-8 text-sm">
                <li className="flex">
                  { currentPage > 1 ? (
                     <button onClick={prevPage} className="flex items-center justify-center px-3 h-8 ms-0 leading-tight text-gray-500 bg-white border border-e-0 border-gray-300 rounded-s-lg hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">
                      <span className="sr-only">Previous</span>
                      <svg className="w-2.5 h-2.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 6 10">
                       <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 1 1 5l4 4"/>
                     </svg>
                     </button>
                       ) : (<p></p>)
                    }
                </li>
                <li className="flex">
                        {pageNumbers.map((number) => (
                            <button onClick={() => gotoPage(number)}
                            className={
                                currentPage === number
                                ? "z-10 flex items-center justify-center px-3 h-8 leading-tight text-blue-600 border border-blue-300 bg-blue-50 hover:bg-blue-100 hover:text-blue-700 dark:border-gray-700 dark:bg-gray-700 dark:text-white"
                                : "flex items-center justify-center px-3 h-8 leading-tight text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white"
                            }
                        >{number}</button>
                        ))}
                </li>
                <li className="flex">
                    { currentPage < lcount ? (
                    <button onClick={nextPage} className="flex items-center justify-center px-3 h-8 leading-tight text-gray-500 bg-white border border-gray-300 rounded-e-lg hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">
                        <span className="sr-only">Next</span>
                        <svg className="w-2.5 h-2.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 6 10">
                        <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 9 4-4-4-4"/>
                        </svg>
                    </button>
                        ) : (<p></p>)
                    }
                </li>
            </ul>
            </nav>
        </MyLayout>
        </>
    )
}

9. src/Layout/MyLayout.jsx作成編集

import React from "react";
import { Navigation, Header } from './index';


export default function MyLayout({children}) {
    return  (
        <>
        <div className="flex flex-col h-screen bg-gray-100">
            <Header />
            <div className="flex flex-grow bg-gray-100">
              <div className="w-2/12">
                <Navigation />
              </div>
              <div className="w-10/12 m-2 bg-gray-100 overflow-y-auto">
               {children}
              </div>
            </div>
        </div>
        </>
    )
}

10. src/Layout/Header.jsx, src/Layout/Navigation.jsx src/Layout/index.jsx作成
src/Layout/Header.jsx

import { Link } from '@inertiajs/react';
import {headerMenus} from '../icons';

export const Header = () => {
    return (
        <div className="h-12 bg-gray-100 text-yellow-900 flex items-center mx-4 pl-3 px-4">
        <ul className="mx4">
            {headerMenus.map((menu, index) => (
                <li className="mb-1 group" key={index}>
                    <Link href={menu.link} className="flex font-semibold items-center py-1 px-4 text-gray-900 hover:bg-red-500 hover:text-gray-100 rounded-md group-[.active]:bg-gray-800 group-[.active]:text-white group-[.selected]:bg-gray-950 group-[.selected]:text-gray-100">
                        <menu.icon  className="mr-1 size-[24px] text-indigo-500"></menu.icon>{menu.label}
                    </Link>
                </li>
            ))}
        </ul>
        </div>
    );
}

src/Layout/Navigation.jsx

import { Link } from '@inertiajs/react';
import { naviMenus } from '../icons';

export const Navigation = () =>{
    return (
        <nav className="bg-gray-100 mx-auto p-4 text-black-600 flex flex-col items-center">
        <ul className="mx4">
            {naviMenus.map((menu, index) => (
                <li className="mb-1 group" key={index}>
                    <Link href={menu.link} className="flex font-semibold items-center py-1 px-4 text-gray-900 hover:bg-gray-950 hover:text-gray-100 rounded-md group-[.active]:bg-gray-800 group-[.active]:text-white group-[.selected]:bg-gray-950 group-[.selected]:text-gray-100">
                        <menu.icon  className="mr-1 size-[24px] text-green-300"></menu.icon>{menu.label}
                    </Link>
                </li>
            ))}
        </ul>
        </nav>
    );
};

src/Layout/index.jsx

export * from './Header';
export * from './Navigation';
export * from './MyLayout';

11. Icon関係のファイルを作成します

mkdir src/icons
touch src/icons/hraderMenus.jsx
touch src/icons/navMenus.jsx
touch src/icons/index.jsx

src/icons/headerMenus.jsx

import {Home} from 'lucide-react';

export const headerMenus = [
    {
        link: '/',
        label: 'ホーム',
        icon: Home,
    },
];

src/icons/naviMenus.jsx

import {Home, Plus} from 'lucide-react';

export const naviMenus = [
    {
        link: '/',
        label: 'ホーム',
        icon: Home,
    },
    {
        link: '/create',
        label: '新規',
        icon: Plus,
    },
]

src/icons/index.jsx

export * from './headerMenus';
export * from './naviMenus';

12. app/admin.py編集します

from django.contrib import admin

# Register your models here.
from .models import Item


@admin.register(Item)
class ItemAdmin(admin.ModelAdmin):

    class Meta:
        verbose_name = 'ユーザ'
        verbose_name_plural = 'ユーザ'

vscode ターミナルで npm run dev
もう一つターミナルを開いて python manage.py runserver
127.0.0.1:8000/adminで10個位サンプルを入れてください。


django-blog-1
カテゴリー:django 作成日:2024年5月24日15:55

djangoでblog その1

djangoでblogを作る方法
条件
マークダウンを使いたい
tailwindcssを使いたい
vueかreactを使いたい
なるべく簡単に作りたい  


まず開発環境

1.ubuntu 22.04LTS or 24.04LTS //proxmox ve 8内で作業する為省略
2.pyenv pythonのバージョンを管理
3.nodenv nodejsのバージョンを管理


pyenv インストール

sudo apt update
sudo apt install build-essential libffi-dev libssl-dev zlib1g-dev liblzma-dev libbz2-dev \
  libreadline-dev libsqlite3-dev libopencv-dev tk-dev git

gitでpyenv clone

git clone https://github.com/pyenv/pyenv.git ~/.pyenv

echo '' >> ~/.bashrc
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(pyenv init --path)"' >> ~/.bashrc
source ~/.bashrc

pyenv でインスール可能なバージョン

pyenv install --list

pyenv で 3.10.8をインストール

pyenv install 3.10.8
pyenv install 3.7.0

pyenv で現在インストールされているリスト

pyenv versions

pyenv の使うバージョンを全体で指定

pyenv global 3.10.8

pyenv フォルダ別にバージョン指定

cd /home/folder
pyenv local 3.7.0

nodenv インストール

pyenvの使い方によく似ています
git clone https://github.com/nodenv/nodenv.git ~/.nodenv

echo 'export PATH="$HOME/.nodenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(nodenv init -)"' >> ~/.bashrc

nodejsバージョン一覧を取得用 node-build インストール

git clone https://github.com/nodenv/node-build.git $(nodenv root)/plugins/node-build

source ~/.bashrc

nodenvでインストールできる一覧

nodenv install --list

nodenv でインストール

nodenv install 18.16.1
nodenv install 16.14.0

nodenv で現在インストールされているリスト

nodenv versions

nodenv の使うバージョンを全体で指定

nodenv global 18.16.1

nodenv フォルダ別にバージョン指定

cd /home/folder
nodenv local 16.14.0
pyenv local 又は nodenv localを指定すると フォルダーに .python-version .node-versionが記録される

.python-versionの中身
3.10.8

.node-versionの中身
16.14.0



CAを使って無公開サイトの作成
カテゴリー:未分類 作成日:2015年7月29日22:09

openssl で認証キーでプライベートサイトを作る

openssl で認証キーを作る場合CentOS6を使って作成
ubuntu14.04lts vinelinux などで作成しようとしたがエラー多発でうまくいかない。
参考サイト で作ったCAデレクトリーを運用中の/etc/nginxにコピー

nginxでのssl設定

server {
       listen 443 ssl;

       ssl on;

       ssl_certificate /etc/nginx/CA/certs/cert.crt;
       ssl_certificate_key /etc/nginx/CA/private/cert.key;

       ssl_session_cache shared:SSL:1m;
       ssl_session_timeout  5m;

       ssl_ciphers HIGH:!aNULL:!MD5;
       ssl_prefer_server_ciphers on;

       ssl_client_certificate /etc/nginx/CA/cacert.pem;
       ssl_verify_client on;
       ssl_verify_depth   1;

       location / {
             proxy_pass https://10.0.3.200;
             proxy_http_version 1.1;
             proxy_set_header Upgrade $http_upgrade;
             proxy_set_header Connection "upgrade";
       }

       location /owncloud {
             proxy_pass https://10.0.3.201/owncloud;
             proxy_http_version 1.1;
             proxy_set_header Upgrade $http_upgrade;
             proxy_set_header Connection "upgrade";
       }
}


Meteorでメール配備
カテゴリー:Meteor 作成日:2015年4月11日22:08

Meteorでコメントがあったり、BBSで投稿があった場合サイトの管理者(ここでは私)はその事を知りたいと思いました。
そこで、投稿があったらメールで管理者に知らせる為にメール送信機能を組み込みました。
参考サイト Meteorで、メール送信
いつものようにパッケージを組み込む

meteor add email

そして server/mailserver.jsへ自分が使っているsmtpサーバの情報を入力する。

これはGmailの設定例です。
Meteor.startup(function () {

    process.env.MAIL_URL="smtp://メールアドレス:パスワード@smtp.gmail.com:587";
});

このサイトで使ってるBBSの例




client/template/bbs/bbs.html

<template name="bbscomment">
    <span id="labeltitle">お名前<input type="text"   id="bbs_name" value="{{cookie_bbs_name}}"/></span>
    <span id="labeltitle">パスワード<input type="password"  id="bbs_password" value="{{cookie_bbs_password}}"/></span>

    <textarea id="bbscomment_message" class="form-control" rows="6" placeholder="パスワードを入力すると自分のコメントを編集できます。マークダウン使えますが、htmlタグ制限があります 。Aboutを見て下さい。"></textarea>

    <span id="labeltitle">{{randomtext}}</span>  左の数値を入力して下さい  <input type="text" id="random_id" />
    <hr />
    <button id="btn_comment" class="btn btn-info">追加</button>  
</template>

client/template/bbs/bbs.js

Template.bbs.events({
    'click #btn_comment': function(event, template){
        var md5str;
        var randomst = String($('#random_id').val());
        if(randomst !== String(Session.get('bbs_random_text'))) return;
        var bbs_name = $('#bbs_name').val();
        var bbs_password = $('#bbs_password').val();
        var bbs_comment = $('#bbscomment_message').val();
        var mid = Session.get('bbs_mid');
        // パスワードが空白でなければ、 Crypt を使って Base64で暗号化
        if(bbs_password !==''){
             md5str = CryptoJS.MD5(bbs_password).toString(CryptoJS.enc.Base64);
        } else {
            md5str ='';
        }
        if(bbs_name==='' || bbs_comment==='') return;
       //コメントのインサート
        Meteor.call('bbsCommentInsert', mid, bbs_name, md5str, bbs_comment,
            function(err, result){
                if(!err){  // エラーがなければ
                    $('#random_id').val('');
                    $('#bbs_name').val('');
                    $('#bbs_password').val('');
                    $('#bbscomment_message').val('');
                    if(bbs_password !== '') {
                        // クッキーをサイトルートで一年間セット
                        Cookie.set('_cookie_bbs_name', bbs_name, {
                            path: '/',
                            expires: 365
                        });
                        Cookie.set('_cookie_bbs_password', bbs_password, {
                            path: '/',
                            expires: 365
                        });

                    };

                   // メールの送信はここから
                    var maintitle =NewMainBbs.findOne({mid:Session.get('bbs_mid')}).maintitle;
                    var to = 'kiyo@yosiki.org';  
                    var from = 'kiyo@yosiki.org';
                    var subject = 'kiyoSite からメール';
                    var mes = maintitle +' '+bbs_name+ '様からコメントがありました。';
                    // Meteor server methodsのsendEmailをコール
                    Meteor.call('sendEmail', to, from, subject, mes,
                        function (err, result) {
                            if (err) {
                                console.log('メールの送信に失敗');
                            } else {
                                console.log('メール送信 完了');
                            }

                        });

server/server.js

Meteor.methods({
    //メールの送信
    'sendEmail': function(to, from, subject, text){
        check([to, from, subject, text], [String]);
        // 同じクライアントからの、ほかのメソッド呼び出しが
        // メール送信完了を待つことなく実行が開始されるように
        // 設定します。
        this.unblock();
        Email.send({
            to: to,
            from: from,
            subject: subject,
            text: text
        });
    }
});


Meteorの起動スクリプト Upstart
カテゴリー:Meteor 作成日:2015年4月9日22:07

起動スクリプトを調べていたら、 Upstart を使ってお手軽 daemon 化と言う方法があるようだ。

ルール

/etc/init/ に拡張子 .confにして保存する。
description "(説明を書く)"
author "(あなたの名前) <(メアド@ドメイン)>"
start on runlevel [(Run Level)]
stop on runlevel [(Run Level)]
expect fork (デーモンの場合は必要)
chdir (作業ディレクトリ)
exec (起動コマンド)
respawn (実行中のジョブが異常終了すると再実行するよう指定)

SysVinit(従来の)方法に比べて簡単で起動が早いらしい。

ubuntuでは Upstart方式に変えていく方針らしい。

実験してみた。

#kiyo-site nginx port:xxxx deamon 
description "kiyo-site --- meteor deamon"

start on runlevel [2345]
stop on runlevel [!2345]

expect fork
respawn
chdir /usr/lib/bundle_kiyo
exec sh /usr/lib/bundle_kiyo/start.sh

$ sudo initctl reload-configuration
再起動

ちゃんと起動した。