Hatena::Grouperlang

檜山正幸のErlang未確認情報 RSSフィード

2009-04-16 (木)

pubファイルによるアプリケーション・メタ情報記述

| 11:03

システム的には、.appファイルがアプリケーションを記述するが、より一般的にアプリケーションに関するメタ情報を記述する形式にpubファイルがある。

supported syntaxe:

{author, {"Name", "EMail", {Year,Month,Day}}}.
{author, {"Name", "EMail"}}.

{packager, {"Name", "EMail"}}.
This is an optional field.
If packager field is not there, the author is declared as packager.

{category, ["Category"]}.
Category name must match one of the category listed on CEAN web site.
If you feel that a category is missing, you can set it into the .pub file, 
but please drop me a mail in that case.
You can also provide several category.

{name, "PackageName"}.

{vsn, "Version"}.
vsn can be a.b.c x.y string etc...
    1.2a > 1.2
    beta > alpha
    alpha > 1.0
note: package directory will be PackageName-Version

{depends, ["Package1","Package2",...]}.
Dependency is a list of package name.
Package name can include version:
    "stdlib" means requires stdlib any version
    "=stdlib-1.14.2" means requires stdlib version 1.14.2
    ">stdlib-1.14.2" means requires stdlib version newer than 1.14.2
    "<stdlib-1.14.2" means requires stdlib version older than 1.14.2
Note: No version checking by now. only "package" syntaxe is working.

{keywords, ["Word1","Word2",...]}.
These keywords will be used by the search engine.

{summary, "Summary"}.
Package description in one sentence, without trailing dot.

{abstract, "Long Description"
 "Can use several lines. "
 "and <i>can</i> use HTML tags."}.
Detailed package description.

{home, "http://www.your-web-site.org"}.

{sources, {git, "git://www.site.org/project"}}.
{sources, {svn, "http://svn.site.org/project/trunk"}}.
{sources, {cvs, ":pserver:anonymous@cvs.site.org:/cvsroot/project"}}.
{sources, {tar, "http://www.erlang.org/download/otp_src_R11B-2.tar.gz"}}.
{sources, {zip, "http://www.site.org/package.zip"}}.
{sources, {erl, "http://www.site.org/package.erl"}}.
Note: if archive name includes package version, you must check that it matchs the vsn field.

{configure, "arguments"}.
This is an optional field.
Arguments given to configure script at compile time.

If your package does not include a LICENCE file on root directory, then the default LICENCE from
CEAN root directory is effective (ERLANG PUBLIC LICENSE).

ということです。以下にpubファイルの例をいくつか。

[追記]PHPのパッケージ記述形式

他のプログラミング言語でもそれぞれあるんだろうな、おそらく。

[/追記]

{name, "ermake"}.
{vsn, {2,0}}.
{summary, "Make in Erlang"}.
{author, "Joe Armstrong", "joe@cslab.ericsson.se", "981131"}.
{keywords, ["make","topological","sort","transitive","closure"]}.
{needs, []}.
{abstract,"This is a simple implementation of make in Erlang.
This version supports 1) Macros, 2) Include files, 3) suffix rules, and
4) file dependencies"}.

{name, "depcheck"}.
{vsn, {1,0}}.
{summary, "A program that checks validity of external references"}.
{author, "Peter Andersson", "peppe@erlang.ericsson.se", "991029"}.
{keywords, ["exref","xref","dependency"]}.
{needs, []}.
{abstract,"Depcheck is a program for finding incorrect module dependencies. "
  "Given a source module, "
  "it will attempt to locate the dependency modules and "
  "validate the function calls. "
  "The dependency modules are scanned syntactically, "
  "not parsed. "
  "(The source module needs to be compilable and is analysed using exref)."
}.

{name, "ccviewer"}.
{vsn, {1,1}}.
{summary, "Web-based source code browser"}.
{author, "Ulf Wiger", "ulf.wiger@ericsson.com", "20000908"}.
{keywords, ["html", "http", "source", "browser"]}.
{needs, [{compiler, "2.1"}]}.
{abstract, 
 "Implements an web-based source code browser. "
 "CCviewer is designed to provide a web-based interface to a ClearCase "
 "repository of Erlang source code. It provides hypertext linking of source "
 "code, and statics cross-references analysis. With modest changes, it "
 "should work against other version control systems."}.

{name, "sgte"}.
{author, {"S.G. Consulting", "pacini@sgconsulting.it"}}.
{packager, {"Filippo Pacini", "pacini@sgconsulting.it"}}.
{category, ["text","web"]}.
{vsn, "0.7.1"}.
{depends, ["kernel","stdlib"]}.
{keywords, ["template engine","web","template","code generation",
            "xml", "dynamic content"]}.
{summary, "Simple Template Engine"}.
{abstract, "sgte is a simple Erlang template engine for generating structured "
           "output (html web pages, xml, code, sql, csv files, emails, etc...)"
           "It's based on "
           "<a href="http://www.stringtemplate.org/">String Template</a>."}.
{home, "http://code.google.com/p/sgte/"}.
{sources, {svn, "http://sgte.googlecode.com/svn/trunk/"}}.

2009-03-28 (土)

スケルトンの自動挿入

| 12:22

EDoc対応のスケルトンは次で示した。

OPTのオフィシャルアプリケーションを構成するモジュールのネーミングはほぼ習慣化している。(「ネーミング」参照。)

であるなら、ファイル名が *_server.erl ならgen_serverスケルトン、*_sup.erl ならsupervisorスケルトンを自動挿入すりゃいんじゃないの。なんで今までやってなかったの? アホッ>自分。

;; 自動的に挿入

;; 以下のディレクトリ指定で、'/'は必須、ないとダメ
(setq auto-insert-directory "~/lib/code-templates/") 
(auto-insert-mode 1)
(setq auto-insert-query nil) ;; デフォルトは 'function
(add-to-list 'auto-insert-alist
	     '("\\.erl$" . "erlang.erl"))
(add-to-list 'auto-insert-alist
	     '("\\.hrl$" . "erlang.hrl"))
(add-to-list 'auto-insert-alist
	     '("_server\\.erl$" . "gen_server_skelton.erl"))
(add-to-list 'auto-insert-alist
	     '("_sup\\.erl$" . "supervisor_skelton.erl"))

2009-03-13 (金)

JohnettaJohnetta2011/09/20 10:08There's a terrific aonumt of knowledge in this article!

idihtrvmidihtrvm2011/09/20 22:58Ae3uCQ <a href="http://olosettxchao.com/">olosettxchao</a>

sngxpjsngxpj2011/09/21 03:11ayOhXp , [url=http://zdziauunxamo.com/]zdziauunxamo[/url], [link=http://wtbahyieqovf.com/]wtbahyieqovf[/link], http://ozvgbmmbijrb.com/

ndincwbndincwb2011/09/25 02:04ON6kwd <a href="http://kcwkfgrvmnmy.com/">kcwkfgrvmnmy</a>

rjbiggyerjbiggye2011/10/03 22:37JsPlJf , [url=http://cqbwbbmunyno.com/]cqbwbbmunyno[/url], [link=http://fbfwknufklze.com/]fbfwknufklze[/link], http://qgontgbvollb.com/

MargarethMargareth2012/12/26 17:53Keep on wriitng and chugging away!

aeduguecaeduguec2012/12/28 17:05Tw7ofP , [url=http://yxsisfncoryo.com/]yxsisfncoryo[/url], [link=http://zdhwawfdimqa.com/]zdhwawfdimqa[/link], http://vvwqnyvdmkze.com/

hrrcbgrjvwhrrcbgrjvw2014/12/03 18:13majbwfsmboh, <a href="http://www.vaunvrflwj.com/">xopycnkhum</a> , [url=http://www.rxvsuexcfd.com/]xhxjttevzg[/url], http://www.ubizlzcddr.com/ xopycnkhum

2009-01-30 (金)

分散アプリケーションの運用

| 16:22

分散アプリケーションの、failover、takeoverとは、それぞれ後継ぎ、お引っ越しってことだ。

後継ぎ:

お引っ越し:


(中断、後で続き書くだろう)

[追記]うーーー、中断したままだ。いったん中止。続きがあるなら別エントリーで。基本文書は:

[/追記]

2009-01-29 (木)

分散アプリケーションへの一歩目

| 08:58

分散helloの実験を始めた。とりあえずの印象では、作るより配備や運用が大変そうだなー。分散アプリケーションのリリースアップとかって手作業でできるんか? 配備運用のサポートってないのかな?

それはともかく、分散アプリケーションはどのノードで実行されているかを意識しなくてよい。これはいい点なのだが、実際の稼働ノードを知りたいときに苦労する。global:whereis_name/1 でサーバー名からPIDを取れるが、PIDを見てもノードがわからない。自ノードプロセスは0からはじまるが、他のノードは数値を見てもサッパリ?? PIDから所在ノード名を返す関数ってあったか?

以下に実験用の分散(?)hello。

[追記]なんだよもう、「はてな」って空のブラケットも消えるのか? 変な制限が多すぎ! 空白を入れて回避したが、、、[/追記]

サーバー

%%% -*- coding: utf-8 -*-

%%%-------------------------------------------------------------------
%%% @author Your Name <your@mail.address>
%%% @doc Module Description
%%%
-module(dhello_server).

-behaviour(gen_server).

%% API
-export([start_link/0, start/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).

%% @type void() = ok | void | none

%% @type state() = #state{}
%%
-record(state, {}).

-define(SERVER, ?MODULE).
-define(SCOPE, global).

%%====================================================================
%% API
%%====================================================================

%% @spec start_link() -> {ok,Pid} | ignore | {error,Error}
%%       where 
%%         Pid = pid()
%%         Error = term()
%% 
%% @doc Starts the server
start_link() ->
  gen_server:start_link({?SCOPE, ?SERVER}, ?MODULE, [ ], [ ]).

%% @doc サーバースタンドアローンでスタートさせる. 
%% @spec () -> {ok,Pid} | ignore | {error,Error}
start() ->
  gen_server:start({?SCOPE, ?SERVER}, ?MODULE, [ ], [ ]).

%%====================================================================
%% gen_server callbacks
%%====================================================================

%% @private
%% @spec init(Args) -> {ok, State} |
%%                     {ok, State, Timeout} |
%%                     ignore |
%%                     {stop, Reason}
%%        where
%%          Args = term()
%%          State = #state{}
%%          Timeout = integer() | infinity
%%          Reason = term()
%%
%% @doc Initiates the server
init(_Args) ->
  {ok, #state{}}.

%% @private
%% @spec handle_call(Request, From, State) -> {reply, Reply, State} |
%%                                            {reply, Reply, State, Timeout} |
%%                                            {noreply, State} |
%%                                            {noreply, State, Timeout} |
%%                                            {stop, Reason, Reply, State} |
%%                                            {stop, Reason, State}
%%       where
%%         Request = term()
%%         From = {pid(), reference()}
%%         State = #state{}
%%         Timeout = integer() | infinity
%%         Reason = term()
%% 
%% @doc Handling call messages
handle_call(get_hello, _From, State) ->
  Reply = get_hello(),
  {reply, Reply, State};
handle_call({get_hello, Whom}, _From, State) ->
  Reply = get_hello(Whom),
  {reply, Reply, State};
%% 以下、実験のため;sleep/1, crush/0
handle_call({sleep, MilliSec}, _From, State) ->
  timer:sleep(MilliSec),
  Reply = ok,
  {reply, Reply, State};
handle_call(crush, _From, _State) ->
  erlang:error(crush);
%% その他のcall
handle_call(Request, From, State) ->
  error_logger:warning_msg("unexpected call: call-requet=~p, from=~p~n", 
                            [Request, From]),
  Reply = {error, not_supported},
  {reply, Reply, State}.

%% @private
%% @spec handle_cast(Msg, State) -> {noreply, State} |
%%                                  {noreply, State, Timeout} |
%%                                  {stop, Reason, State}
%%       where
%%         Msg = term()
%%         State = #state{}
%%         Timeout = integer() | infinity
%%         Reason = term()
%%
%% @doc Handling cast messages
handle_cast(stop, State) ->
  {stop, normal, State};
handle_cast(_Msg, State) ->
  error_logger:warning_msg("unexpected cast: cast-messaget=~p~n", 
                            [_Msg]),
  {noreply, State}.

%% @private
%% @spec handle_info(Info, State) -> {noreply, State} |
%%                                   {noreply, State, Timeout} |
%%                                   {stop, Reason, State}
%%       where
%%         Info = term()
%%         State = #state{}
%%         Timeout = integer() | infinity
%%         Reason = term()
%% @doc Handling all non call/cast messages
handle_info(_Info, State) ->
  {noreply, State}.

%% @private
%% @spec terminate(Reason, State) -> void()
%%       where
%%         Reason = normal | shutdown | term()
%%         State = #state{}
%%
%% @doc This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
terminate(_Reason, _State) ->
  ok.

%% @private
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%%       where
%%         OldVsn = term() | {down, term()}
%%         State = #state{}
%%         Extra = term()
%%         NewState = #state{}
%%
%% @doc Convert process state when code is changed
code_change(_OldVsn, State, _Extra) ->
  {ok, State}.

%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------

%% @doc デフォルトの相手に対する挨拶文字列を返す
%% @spec () -> string()
get_hello() ->
  Whom = 
    case application:get_env(whom) of
      {ok, Val} -> 
        Val;
      _ ->
        "world"
    end,
  get_hello(Whom).

%% @doc 引数で指定された相手に対する挨拶文字列を返す
%% @spec (string()) -> string()
get_hello(Whom) when is_list(Whom) -> 
  Greeting = 
    case application:get_env(greeting) of
      {ok, Val} -> 
        Val;
      _ ->
        "Hello"
    end,
  lists:flatten(io_lib:format("~s, ~s.~n", [Greeting, Whom])).

%% the end

メインモジュール

%% -*- coding: utf-8 -*-

%% @doc 分散helloアプリケーション
-module(dhello).

-export([ % applicationコールバック
         start/2, stop/1
        ]).
-export([ % 全体を制御
          start/0, stop/0
         ]).

-export([ % API
          get_hello/0, get_hello/1,
          hello/0, hello/1,
          sleep/1, crush/0, stop_server/0
         ]).

-behaviour(application).

-define(APP, ?MODULE).
-define(SERVER, dhello_server).

start(_Type, _StartArgs) ->
  ?SERVER:start_link().
stop(_State) ->
  ok.

start() ->
  application:start(?APP).
stop() ->
  application:stop(?APP).

%% ここから下にAPI部分

get_hello() ->
 gen_server:call({global, ?SERVER}, get_hello).

get_hello(Whom) ->
 gen_server:call({global, ?SERVER}, {get_hello, Whom}).

hello() ->
  Hello = get_hello(),
  io:fwrite("~s", [Hello]).

hello(Whom) ->
  Hello = get_hello(Whom),
  io:fwrite("~s", [Hello]).

crush() ->
  gen_server:call({global, ?SERVER}, crush).

sleep(MilliSec) ->
  gen_server:call({global, ?SERVER}, {sleep, MilliSec}).

stop_server() ->
  gen_server:cast({global, ?SERVER}, stop).

アプリケーション・リソースファイル:

%% dhello.app

{application, dhello,
  [
   {description,  "Simple Distributed Hello Application"},
   {id,           "dhell-1.0"},
   {vsn,          "1.0"}, 
   {modules,
     [dhello_server, dhello]},
   {registered,
     [dhello_server]},
   {applications, [kernel, stdlib]},
   {env, [{whom, "universe"}]},
   {mod,          {dhello, none}}
  ]
}.

RopeRope2011/09/18 23:35IMHO you've got the right asnewr!

scbsvsscbsvs2011/09/19 02:22X9zsGh <a href="http://rjfpztdrmqbd.com/">rjfpztdrmqbd</a>

zrjyprndvwtzrjyprndvwt2011/09/19 21:42tIUfxD , [url=http://pcbfpoifizfe.com/]pcbfpoifizfe[/url], [link=http://nlupgydhztus.com/]nlupgydhztus[/link], http://mtqlzaracvpu.com/

hlzjurchkhlzjurchk2011/09/24 01:14XKXXc5 <a href="http://gwgvpyofqlzg.com/">gwgvpyofqlzg</a>

fafxsrneefafxsrnee2011/10/01 02:18obObxi , [url=http://ldkcaqdjcjsv.com/]ldkcaqdjcjsv[/url], [link=http://dquztlmaovnq.com/]dquztlmaovnq[/link], http://anjwfmyvwhpt.com/

2009-01-28 (水)

inclusion -- まずは言葉から

| 10:46

included applicationsは、including application にインクルードされる。including ← included ... という階層を続けて木構造を作ることができる。この木のルートがthe primary application。つまり、application treeのような概念がある。

ツリーの構成員である各アプリケーションは間違いなくそれぞれがアプリケーションなのだが、実行時に単一のプロセスツリー(OTP監視ツリー)に統合される。つまり、includedアプリケーションは、大きな監視ツリーの部分木(つうか、DNSのゾーンのような感じ)の構成部品となる。

アプリケーションのスタート/ストップをどうするんだ? だれがどう担当するんだ? なんで大きな木が作れるんだ? と、ここらへんを理解しないとね、これから。

簡易なアプリケーション編成

| 17:49

hello_app.erlを次のように変更した。

%  hello_sup:start_link().
  hello_server:start_link().

クラッシュするとそれっきりだが、特に問題はないようだ。

実験の時は次のような構成でもいいだろう。

  1. gen_server部分 例:hello_server.erl
  2. APIとapplicationコールバック 例:hello.erl

そのとき、次のような.appファイルを書けばよい。

{application, hello,
  [
   {description,  "simple hello application"},
   {id,           "shell-1.0"},
   {vsn,          "1.0"}, 
   {modules,      % 
     [hello_server, hello]},
   {registered,
     [hello_server]},
   {applications, [kernel, stdlib]},
   {env, [{whom, "universe"}]},
   {mod,          {hello, none}}
  ]
}.

hello.erlのお決まり部分は:

start(_Type, _StartArgs) ->
  hello_server:start_link().
stop(_State) ->
  ok.

start() ->
  application:start(?APP).
stop() ->
  application:stop(?APP).

%% ここから下にAPI部分

ファイル2つならさほどの手間ではない。

IsmailIsmail2012/12/26 17:55Just what the dctoor ordered, thankity you!

bulwvotudtbulwvotudt2012/12/28 23:42aznfdf <a href="http://jwzpefkwwmin.com/">jwzpefkwwmin</a>

2009-01-23 (金)

helloちゃんと作るシリーズ 目次

| 09:34

これを実際に書いた日:2009-03-13 (金)
「はてな」は日付を自由に付けられるから、ここらへんに置こう。

  1. helloをちゃんと作る - 檜山正幸のErlang未確認情報 - はてなグループ: erlang
  2. helloをちゃんと作る (2) サーバー - 檜山正幸のErlang未確認情報 - はてなグループ: erlang
  3. helloをちゃんと作る (3) ネーミング - 檜山正幸のErlang未確認情報 - はてなグループ: erlang
  4. helloをちゃんと作る (4) supervisorのスケルトン - 檜山正幸のErlang未確認情報 - はてなグループ: erlang
  5. helloをちゃんと作る (5) supervisor - 檜山正幸のErlang未確認情報 - はてなグループ: erlang
  6. helloをちゃんと作る (6) APIモジュール - 檜山正幸のErlang未確認情報 - はてなグループ: erlang
  7. helloをちゃんと作る (7) appファイルを準備 - 檜山正幸のErlang未確認情報 - はてなグループ: erlang
  8. helloをちゃんと作る (8) applicationモジュール - 檜山正幸のErlang未確認情報 - はてなグループ: erlang
  9. helloをちゃんと作る (9) トップレベル・インターフェースモジュール - 檜山正幸のErlang未確認情報 - はてなグループ: erlang
  10. helloをちゃんと作る (10) 配備 - 檜山正幸のErlang未確認情報 - はてなグループ: erlang
  11. helloをちゃんと作る (11) いくつかの注意 - 檜山正幸のErlang未確認情報 - はてなグループ: erlang
  12. helloをちゃんと作る (12) 環境変数=構成パラメータ - 檜山正幸のErlang未確認情報 - はてなグループ: erlang
  13. helloをちゃんと作る (13) 構成パラメータによるカスタマイズ - 檜山正幸のErlang未確認情報 - はてなグループ: erlang
  14. helloの今後:もっとちゃんと作るために - 檜山正幸のErlang未確認情報 - はてなグループ: erlang

日付ごとのインデックス

  1. 12-11 :1 http://erlang.g.hatena.ne.jp/m-hiyama/20081211
  2. 12-25 :2,3 http://erlang.g.hatena.ne.jp/m-hiyama/20081225
  3. 01-14 :4 http://erlang.g.hatena.ne.jp/m-hiyama/20090114
  4. 01-15 :5,6 http://erlang.g.hatena.ne.jp/m-hiyama/20090115
  5. 01-16 :7,8 http://erlang.g.hatena.ne.jp/m-hiyama/20090116
  6. 01-17 :9,10,11,12 http://erlang.g.hatena.ne.jp/m-hiyama/20090117
  7. 01-20 :13 http://erlang.g.hatena.ne.jp/m-hiyama/20090120
  8. 01-22 :fin http://erlang.g.hatena.ne.jp/m-hiyama/20090122

JenniferJennifer2012/02/07 10:48Great article but it didn't have everything-I didn't find the kicthen sink!

yfwwlhpiryyfwwlhpiry2012/02/07 17:54X1sCE6 <a href="http://ripixfqmhddo.com/">ripixfqmhddo</a>

chkktcchkktc2012/02/11 01:39m5yUrH <a href="http://ljjtnigytebs.com/">ljjtnigytebs</a>

svnzyosvnzyo2012/02/12 05:11QhVTUG , [url=http://utkincydawzl.com/]utkincydawzl[/url], [link=http://arjsdqfhocbb.com/]arjsdqfhocbb[/link], http://zrjxdyacukwl.com/

2009-01-22 (木)

helloの今後:もっとちゃんと作るために

| 16:34

helloをちゃんと作るシリーズは、13回でいちおう終了。

でも、次の点はまだ「ちゃんと」になってません。「もっとちゃんと作る」ための課題を列挙。

分散アプリケーションとしてのhello(イメージ湧かないが)

applicationコールバックのstart仕様は、

  Module:start(StartType, StartArgs)

第2引数は、アプリケーション仕様キー(application specification key)modに書かれた{Module,StartArgs}の値を取り出して渡してくれる(あんまりうれしくない)のだが、startTypeには、

 StartType = normal | {takeover,Node} | {failover,Node}

が指定可能。{takeover,Node} | {failover,Node} が、どんなときに渡ってきて、どう対処すべきかわからない。これを調べる。

included_applications

アプリケーション仕様キーにincluded_applicationsがあり、複数のアプリケーションをグループ化(チーム化)できるが、メカニズムと使い方がわからない。

ホットフィックス、リリースハンドリング

OTPは、ホットフィックス/ホットスワップをサポートしているし、リリースハンドリングの概念もある。開発しながら稼働させ、稼働させながら開発を続けるためには、これらの機能の利用が必須。

状態のバックアップ

監視ツリーを使っていても、サーバーが死ねば状態は失われる。再起動されても直前の状態が残ってないと稼働は続けられない。おそらくは、terminateコールバック関数でなんとかするのだと思うが、系統的な方法が(今のところ)見あたらない。なんとかしたい。

2009-01-20 (火)

helloをちゃんと作る (13) 構成パラメータによるカスタマイズ

| 11:08

「helloをちゃんと作る (12)」で述べたように、環境変数=構成パラメータによりアプリケーションの挙動を変えることができる。

だが、アプリケーションリソースファイルのenv項目を書き換えるという手段は、ソースファイルやヘッダファイルを書き換えてリコンパイルするよりはチョットだけ楽という程度。次の方法を紹介する。

  1. 構成ファイル(configuration file)に構成パラメータ群を書いて渡す。
  2. erl起動オプションに許されるアプリケーションオプションを使う。

実験の準備

実験のために、構成パラメータを増やしておく。

%% hello_impl.erl  -*- coding: utf-8 -*-

%% @doc Helloアプリケーションの実装モジュール.
-module(hello_impl).
-export([hello/0, hello/1]).

%% @doc デフォルトの相手に対して挨拶をする.
%% @spec () -> ok
hello() ->
  Whom = 
    case application:get_env(whom) of
      {ok, Val} -> 
        Val;
      _ ->
        "world"
    end,
  hello(Whom).

%% @doc 引数で指定された相手に対して挨拶をする.
%% @spec (string()) -> ok
hello(Whom) when is_list(Whom) -> 
  Greeting = 
    case application:get_env(greeting) of
      {ok, Val} -> 
        Val;
      _ ->
        "Hello"
    end,
  io:fwrite("~s, ~s.~n", [Greeting, Whom]).

最初の時点でのアプリケーションリソースファイルは:

%% -*- coding: utf-8 -*-

{application, hello, % アプリケーション名はアトムである点に注意
  % 以下に記述項目のリスト
  [
   {description,  "hello application"},
   {id,           "hell-1.0"}, % idはシステムが使うわけではないので何でもよい
   {vsn,          "1.0"}, % ドットでつないだ数値を使うのが無難
   {modules,      % helloアプリケーションを構成するすべてのモジュール名を列挙
     [hello_impl, hello_server, hello_sup, hello_api, hello_app, hello]},
   {registered,   % helloアプリケーションで登録する名前を列挙
     [hello_server, hello_sup]},
   {applications, [kernel, stdlib]},
   {mod,          {hello_app, none}}
  ]
}.

envを書いてない。この設定で hello_api:hello().とすると、プログラムコード中に埋め込まれた値、"Hello", "world"が使われる。

なお、対話的な実験が面倒なら、次のコマンドを使うとよい。

OS-Shell> erl -s hello -s hello_api hello -s init stop

アプリケーションリソースファイル

アプリケーションリソースファイルに次の1行を加える。

   {env, [{whom, "universe"}]},

構成パラメータwhomの値が"universe"に設定されるので、Hello, universe. が表示される。

構成ファイル

次のような構成ファイル(configuration file)を作る。書式は http://www.erlang.org/doc/man/config.html に書いてある。拡張子は .config にする(そうしないと不吉)。

%% -*- coding: utf-8 -*-
%%
[
 {hello,
    [{greeting, "Good morning"}]
 }
].

このファイルをhello.cinfigとして、erlコマンドの -config オプションにファイル名を指定する。

erl -config hello.config <その他の引数

.config拡張子は省略可能で、省略するのが習慣らしい。

この例では、Good morning, universe. が表示される。アプリケーションリソースファイルより構成ファイルが優先されるので、次のようにアプリケーションリソースファイルをオーバライドできる。

%% -*- coding: utf-8 -*-
%%
[
 {hello,
    [{whom, "Japan"}]
 }
].

コマンドラインオプション

http://www.erlang.org/doc/man/erl.html より:

-Application Par Val

Sets the application configuration parameter Par to the value Val for the application Application, see app(4) and application(3).

これによると、-hello whom Tokyo とすれば、Hello, Tokyo. と表示しそうである。しかし、実際は:

OS-Shell>erl -config hello -hello whom Tokyo  \
  -s hello -s hello_api hello -s init stop

とするとクラッシュする。-hello whom の後には文字列が必要。OSコマンドライン上にErlangタームを表現する形式で書かなくてはいけないので、文字列なら引用符が要る。そのまま引用符を書くとOSシェルが食べてしまい、ERTSにmainに渡らない。引用符をERTSまで届ける書き方はOSシェルによりけりだが、例えば次のようだろう。

  • -hello whom \"Tokyo\"
  • -hello whom '"Tokyo"'

コマンドラインオプションはconfigファイルよりさらに優先される。

その他、細かいこと

erl -config hello -hello whom \"Tokyo\"として対話的にERTSを起動する。起動直後に application:get_env(hello, whom). としてもundefinedになる。-configや-helloオプションを指定しても、当該アプリケーションを起動しない限り、構成パラメータを読むことはできない。より正確に言うと、application:load(hello) のタイミングで構成パラメータをセットアップするようだ。このとき、included_applicationsも構成パラメータに自動的にセットされる(詳細はよくわからんが)。

application:set_env/{3,4}で実行時に動的に構成パラメータをセットできるが、変更時に通知(イベント)が飛ぶようなメカニズムはないので、不注意に使うと危険だ。また、application:loadは1度しかできないので、いったん読んでしまったアプリケーション仕様データを読み直すことはできない。つまり、構成パラメータは静的な性格が強い、実行中は定数とが考えたほうがよいだろう。

なお、init:get_argument(hello) としても-helloオプションの値(生のまま)が取れる。initが保持している値はアプリケーションコントローラとは独立なので、helloをスタートさせなくてもパラメータを取れる。この方法は、アプリケーションコントローラに登録しない野良アプリケーションで利用できる。

2009-01-17 (土)

helloをちゃんと作る (9) トップレベル・インターフェースモジュール

| 15:32

アプリケーションの起動と停止。それとテストに便利な関数(YAWSから拝借)。

%% -*- coding: utf-8 -*-

%% @doc helloアプリケーショントップレベルインターフェースモジュール.
-module(hello).

-export([ % 起動/停止
         start/0, stop/0
        ]).
-export([ % デバッグ/テストに便利
         modules/0, load/0, restart/0
        ]).

%% アプリケーション名
-define(APP, hello).

%% @doc helloアプリケーションをスタートする.
%% @spec () -> ok | {error, Reason}
%% @end
%% application:start/2についてはマニュアルを参照のこと.
start() ->
  application:start(?APP).

%% @doc helloアプリケーションをストップする.
%% @spec () -> ok | {error, Reason}
stop() ->
  application:stop(?APP).

%%%%
%% 以下のコードはyaws.erlから拝借し、
%% 多少変更
%%%%

%% @doc helloアプリケーションを構成する全てのモジュール名のリストを返す.
%% @spec () -> [atom()]
modules() ->
  application:load(?APP),
  case application:get_key(?APP, modules) of
    {ok, List} -> 
      List;
    _ -> 
      []
  end.

%% @spec ([atom()]) -> ok
%% @end
%% lists:foreach/2 の戻り値は常にok
load(List) ->
  lists:foreach(fun c:l/1, List).

%% @doc helloアプリケーションを構成する全てのオブジェクトファイルをロードする.
%% @spec () -> ok
%% @end
%% lists:foreach/2 の戻り値は常にok
load() ->
  load(modules()).

%% @doc helloアプリケーションを再スタートする.
%% 新しいオブジェクトをロードしてからスタートするので、
%% 変更があればそれが反映される.
%% @spec () -> ok | {error, Reason}
restart() ->
  stop(),
  load(),
  start().

helloをちゃんと作る (10) 配備

| 15:34

hello.appには、helloアプリケーションのメタ情報が書かれている。配備に関して特に重要なのは次の3つの項目:

  1. application, hello % アプリケーション
  2. vsn, "1.0" % バージョン番号
  3. modules, [hello_impl,hello_server,hello_sup,hello_api,hello_app,hello] % モジュール

まずは、インストールディレクトリ$HELLO_APP_DIRを決めるが、現状では、環境変数ERL_LIBSに指定されたディレクトリの1つを$USER_LIB_DIRとして、$HELLO_APP_DIR=$USER_LIB_DIR/hello-1.0/ とするのが良さそう。こうすると、なにもしなくてもアプリケーションランタイム(ERTS)に認識される。

$USER_LIB_DIRの下には、最低限ebinサブディレクトリは必要で、modulesに列挙されたモジュールのbeamファイルを置く。

なお、Makefile変数APP_NAME, VSN, MODULESを定義しておいて、開発と配備の一貫性を維持するのがよいだろう。ある程度の規模のアプリケーションになると、手作業だけでは一貫性の維持が困難になる。

helloをちゃんと作る (11) いくつかの注意

| 16:39

共通の定義はヘッダファイルにまとめる

サーバー名やスーパーバイザ名を定義するためには、-define を使うとよい。

-define(SERVER, hello_server).
-define(SUP, hello_sup).

だが、この定義を各ソースファイルに散りばめると、メンテナンスが困難。hello_names.hrlのようなヘッダーファイルに登録名の定義をまとめておくとよいだろう。

各種メッセージ、ロギング

今回のサンプルでは、io:fwrite, io:formatなどでメッセージ(出力)をイイカゲンに書いているが、実際には error_logger を使うべき。また、メッセージの種別(info, warning, error)や書式をよく考えて、一貫したスタイルにすべき。

ファイルの集約

次のファイル(モジュール)群を作った。

hello.erl アプリケーショントップレベルインターフェースモジュール
hello_api.erl 公開する関数を集めたインターフェースモジュール
hello_app.erl applicationコールバックモジュール
hello_server.erlgen_serverコールバックモジュール
hello_sup.erl トップスーパーバイザ
hello_impl.erl hello_serverの実装モジュール

App_implとApp_serverは1つにしてもよい。App_apiとAppも一緒にしてもかまわない。むしろ、一緒にしたほうが使いやすいこともある。App_api, App, App_appの3つを1つにまとめることもできる。

以下は、hello, hello_api, hello_appを1つにまとめた例。hello_api:stop/0とhello:stop/0 が名前の競合を起こすので、hello_api:stopをstop_server/0に改名している。便宜上、統合したモジュール名をhello_iifとしているが、実際はこれをhelloとすればいい。

%% -*- coding: utf-8 -*-

%% @doc helloアプリケーションの統合したインターフェースモジュール.
%% iif = Integrated InterFace
-module(hello_iif).

%% applicationコールバック
-behaviour(application).
-export([start/2, stop/1]).

%% アプリケーショントップレベルインターフェース
-export([ % 起動/停止
         start/0, stop/0
        ]).
-export([ % デバッグ/テストに便利
         modules/0, load/0, restart/0
        ]).

%% アプリケーションの外部インターフェース
-export([hello/0, hello/1, sleep/1, stop_server/0, crush/0]).

%% サーバー登録名
-define(SERVER, hello_server).
%% スーパーバイザ登録名
-define(APP, hello).


%% @spec (StartType, StartArgs) -> {ok, Pid} | {ok, Pid, State} | {error, Reason}
%% where
%%   StartType = normal | {takeover,Node} | {failover,Node}
%%   Node = node()
%%   StartArgs = term()
%%   Pid = pid()
%%   State = term()
%% @doc helloアプリケーションのトップ・スーパーバイザを起動する.
start(_Type, _StartArgs) ->
  io:fwrite("hello_app:start(~p, ~p) : Starting the hello app.~n", 
            [_Type, _StartArgs]),
  hello_sup:start_link().

%% @spec stop(State::term()) -> ok
%% @doc helloアプリケーション終了後の処理(特になし).
stop(_State) ->
  io:fwrite("hello_app:stop(~p) : The hello app stopped.~n", 
            [_State]),
  ok.



%% @doc helloアプリケーションをスタートする.
%% @spec () -> ok | {error, Reason}
%% @end
%% application:start/2についてはマニュアルを参照のこと.
start() ->
  application:start(?APP).

%% @doc helloアプリケーションをストップする.
%% @spec () -> ok | {error, Reason}
stop() ->
  application:stop(?APP).

%%%%
%% 以下のコードはyaws.erlから拝借し、
%% 多少変更
%%%%

%% @doc helloアプリケーションを構成する全てのモジュール名のリストを返す.
%% @spec () -> [atom()]
modules() ->
  application:load(?APP),
  case application:get_key(?APP, modules) of
    {ok, List} -> 
      List;
    _ -> 
      []
  end.

%% @spec ([atom()]) -> ok
%% @end
%% lists:foreach/2 の戻り値は常にok
load(List) ->
  lists:foreach(fun c:l/1, List).

%% @doc helloアプリケーションを構成する全てのオブジェクトファイルをロードする.
%% @spec () -> ok
%% @end
%% lists:foreach/2 の戻り値は常にok
load() ->
  load(modules()).

%% @doc helloアプリケーションを再スタートする.
%% 新しいオブジェクトをロードしてからスタートするので、
%% 変更があればそれが反映される.
%% @spec () -> ok | {error, Reason}
restart() ->
  stop(),
  load(),
  start().

%% edocしてない、忘れた、いいとしよう.

%% @spec () -> ok
hello() ->
  gen_server:call(?SERVER, hello).
  
%% @spec (string()) -> ok
hello(Whom) ->
  gen_server:call(?SERVER, {hello, Whom}).

%% @spec (integer()) -> ok
sleep(MilliSec) ->
  gen_server:call(?SERVER, {sleep, MilliSec}).
  
%% @spec () -> none()
crush() ->
  gen_server:call(?SERVER, crush).

%% @spec () -> ok
stop_server() ->
  % gen_server:cast/2 はokを戻す.
  gen_server:cast(?SERVER, stop).

helloをちゃんと作る (12) 環境変数=構成パラメータ

| 17:20

OS環境変数とは別に、アプリケーションごとに利用できるErlangレベルの環境変数がある。環境変数は別名・構成パラメータ(configuration parameters)と呼ばれる。説明の文言や関数変数名などでも、Env, Conf, Par などが混じって使われているので注意。以下では主に「構成パラメータ」を使う(OS環境変数と紛らわしいので)。

1つの構成パラメータは名前・値ペアで、{atom(), term()} の形。helloアプリケーションでは、デフォルトの挨拶相手を {whom, string()} という構成パラメータで指定する。一般には、構成パラメータをリストにして、いくつ指定してもよい。

構成パラメータの出所はいくつかあるが、基本は、アプリケーションコントローラ管理しているアプリケーション仕様のenv項目である。

{application, hello,
  % 以下に記述項目のリスト
  [
   %% ... 省略
   {env,          % 環境変数=構成パラメータ
     [{whom, "world"}]},
   %% ... 省略
  ]
}.

この構成パラメータ値は、application:get_env/{1, 2} で取れる。

 get_env(Par) -> {ok, Val} | undefined
 get_env(Application, Par) -> {ok, Val} | undefined

Parにパラメータ名アトムを入れると、ラップされた値が返る。

#> application:get_env(hello, whom).
{ok,"world"}
#>

特定アプリケーションに所属するモジュールプロセスから呼ぶときは第1引数は省略できる。自分がどのアプリケーションに所属するかは、application:get_application/1 で分かる。

 get_application(Pid | Module) -> {ok, Application} | undefined

application:get_env/1を使えば、hello_impl:hello/0 は次のようになる。

%% @doc デフォルトの相手に対してhelloと言う.
%% @spec () -> ok
hello() ->
  Whom = 
    case application:get_env(whom) of
      {ok, Val} -> 
        Val;
      _ ->
        "world"
    end,
  hello(Whom).

アプリケーションの動作を変えるには、ebinサブディレクトリにあるアプリケーションリソースファイルを書き換えればよい。

{application, hello,
  % 以下に記述項目のリスト
  [
   %% ... 省略
   {env,          % 環境変数=構成パラメータ
     [{whom, "universe"}]},
   %% ... 省略
  ]
}.

だが、アプリケーションリソースファイルの書き換えは便利ではないので、他の方法も用意されている。その話は次。

YasinYasin2012/02/09 04:00I was really confused, and this ansewred all my questions.

xwzliwxwzliw2012/02/09 22:07yIusIk <a href="http://nebrdzoytvby.com/">nebrdzoytvby</a>

jiqwbbdjiqwbbd2012/02/10 02:42c1SZR3 , [url=http://irzkzdxzanrf.com/]irzkzdxzanrf[/url], [link=http://abmxfrtvtshn.com/]abmxfrtvtshn[/link], http://crbtgcmkdrbt.com/

wvdslciwvdslci2012/02/10 04:05q8lSvf , [url=http://xxyhczqszsxi.com/]xxyhczqszsxi[/url], [link=http://piadszuzmedj.com/]piadszuzmedj[/link], http://fouwqfkmpjgv.com/

xjtzwxtfcnxjtzwxtfcn2012/02/10 07:00T4qB7J , [url=http://natfsecsnigp.com/]natfsecsnigp[/url], [link=http://qhfyzefntcms.com/]qhfyzefntcms[/link], http://netbopzeimlf.com/

luqvpmdluqvpmd2012/02/10 08:46btyauJ , [url=http://cllduaufqkkh.com/]cllduaufqkkh[/url], [link=http://cxlvnqjexnxl.com/]cxlvnqjexnxl[/link], http://vkzjnmmgwgyo.com/

jadeosnusjadeosnus2012/02/10 10:05T6lUoK , [url=http://hjgfbdqmxezk.com/]hjgfbdqmxezk[/url], [link=http://hdgzizuceygk.com/]hdgzizuceygk[/link], http://pnifsfvnqqjs.com/

mqtwymaslmqtwymasl2012/02/10 11:12b50w81 , [url=http://oaqslqioosyh.com/]oaqslqioosyh[/url], [link=http://nppsqmqvceee.com/]nppsqmqvceee[/link], http://jxehxrpfbdxk.com/

eflizfeflizf2012/02/11 20:55t4xBDn <a href="http://jekzuwqhksmt.com/">jekzuwqhksmt</a>

iycoarkzmliycoarkzml2012/02/13 04:22hgNGDV , [url=http://kdvqurhjtgxs.com/]kdvqurhjtgxs[/url], [link=http://rgzfwswoiktj.com/]rgzfwswoiktj[/link], http://kkwqepmwmhvx.com/