2010-03-31

OS 스크립트에서 변수 설정 여부 확인하기

윈도우 스크립트(bat) 파일에서는 다음과 같이 변수 설정 여부를 확인한다.

if "%STARTUP_PORT%" == "" SET STARTUP_PORT=8080
if "%SHUTDOWN_PORT%" == "" SET SHUTDOWN_PORT=8005


유닉스 혹은 리눅스(sh)에서는 다음과 같이 변수 설정 여부를 확인한다.

STARTUP_PORT="${STARTUP_PORT}"
SHUTDOWN_PORT="${SHUTDOWN_PORT}"

if [ "${STARTUP_PORT}" = "" ]
then
    STARTUP_PORT="8080"
fi

if [ "${SHUTDOWN_PORT}" = "" ]
then
    SHUTDOWN_PORT="8005"
fi


2010-03-24

SMS 길이 체크하기

오늘 웹에서 SMS 전송 모듈을 작성했다.

  • 메시지 박스는 textarea로 구현한다.
  • SMS는 최대 80 바이트까지만 전송할 수 있다.
  • 사용자가 메시지 박스에 텍스트를 입력하면 실시간으로 하단에 텍스트 바이트 크기를 보여준다.

HTML 구조는 다음과 같다.

<div>
    <textarea id="text" rows="4" cols="30"></textarea>
    <br/>
    <span id="length"></span>
</div>


그리고 onkeydown(up, press) 이벤트로 바이트 크기를 체크하려는데 파이어폭스에서는 한글 입력에 대해서는 이 이벤트가 발행하지 않았다 ㅠㅠ


어쩔 수 없이 스케줄러(setInterval)로 백그라운드에서 바이트 크기를 처리하기로 했다. 약간의 딜레이를 감수하면서...

Prototype을 사용했다. 코드는 단순하지만 Prototype에 익숙하지 않으면 이해하기 쉽지 않다;;


우선 문자열 바이트 크기를 체크하는 함수를 String 클래스에 추가했다.

Object.extend(String.prototype, {bytes: function() {
    var source = this;
    var result = 0;
    for (var i = 0; i < source.length; i++) {
        result += (source.charCodeAt(i) > 128) ? 2 : 1;
    }
    return result;
}});

  • 기존 클래스에 메소드를 추가할 수 있다는 것이 자바 스크립트와 같은 언어의 장점이다.
  • Object.extend는 Prototype이 제공하는 메소드이다.
  • bytes라는 이름으로 메소드를 추가했다.

그리고 LengthChecker 클래스를 다음과 같이 작성했다.

var LengthChecker = Class.create({
    initialize: function(target, handler) {
        this.target = $(target);
        this.handler = handler;
    },
    apply: function() {
      this.target.observe("focus", this.start.bind(this));
      this.target.observe("change", this.stop.bind(this));
      this.target.observe("keydown", this.callback.bind(this));
    },
    start: function() {
        this.executer = new PeriodicalExecuter(this.callback.bind(this), 1);
    },
    stop: function() {
        this.executer.stop();
        this.callback();
    },
    callback: function() {
        this.handler($F(this.target).bytes());
    }
});

  • apply 메소드에서 observe 메소드로 target에 foucs, change, keydown 이벤트를 등록했다. focus에서 스케줄러를 시작하고, change 메소드에서 스케줄러를 정지한다. 한글이 아닌 경우에는 문제가 없기 때문에 keydown 이벤트도 등록하였다.
  • Protoype이 제공하는 PeriodicalExecuter는 setInterval을 이용한 스케줄러이다.


사용 방법은 다음과 같다.

new LengthChecker("text", function(length) {$("length").update(length);}).apply();


LengthChecker 클래스 생성자의 파라미터는 다음과 같다.

  • target: 대상 textarea 요소(혹은 id)
  • handler: 주기적으로 호출되는 함수. 이 함수의 첫번째 파라미터는 target에 입력한 텍스트의 바이트 크기이다.
LengthChecker 객체를 생성한 후 apply 메소드를 호출하면 백그라운드에서 바이트 크기 체크 작업이 이루어진다.

  • Prototype 라이브러리 PeriodicalExecuter는 최소 1초 단위로 반복된다. 딜레이를 최소화하려면 setInterval 함수를 사용하야 한다.

2010-03-17

CSS 초기화

Sitepoint에 올라온 The Right Frame of Mind: Applying the Lessons of CSS Frameworks에서 중요한 정보를 얻었다.

CSS 전문가인 Eric Meyer가 제안한 것으로 웹 브라우저마다 다른 CSS 속성 값을 일정하게 초기화하는 방법이다.

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center, dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-size: 100%;
vertical-align: baseline;
background: transparent;
}

body {
line-height: 1;
}

ol, ul {
list-style: none;
}

blockquote, q {
quotes: none;
}

/* remember to define focus styles! */

:focus {
outline: 0;
}

/* remember to highlight inserts somehow! */

ins {
text-decoration: none;
}

del {
text-decoration: line-through;
}

/* tables still need 'cellspacing="0"' in the markup */

table {
border-collapse: collapse;
border-spacing: 0;
}


2010-03-15

text-decoration

text-decoration 속성으로 텍스트 위, 아래, 가운데에 줄을 그을 수 있다.




blink로 텍스트가 깜빡이는 효과를 표현할 수 있다.



선은 color 속성으로 지정한 색상으로 표시된다. 다음과 같은 방법으로 선 색상과 텍스트 색상을 다르게 가져갈 수 있다.


<span style="color: blue; text-decoration: underline;"><span style="color: red;">CSS</span></span>

결과는 다음과 같다.


동일한 텍스트에 두 개 이상의 효과를 주려면 여러 개의 CSS 속성 값을 동시에 설정하면 된다.



2010-03-11

JIRA에서 GMAIL을 SMTP 서버로 사용하기

JIRA(버전: 4.0.2, 톰켓을 포함한 배포판)에서 GMAIL을 SMTP 서버로 사용하는 방법이다.


JIRA_HOME/conf/server.xml 파일에 다음 내용을 추가한다.

<Resource name="mail/GmailSmtpServer"
  auth="Container"
  type="javax.mail.Session"
  mail.smtp.host="smtp.gmail.com"
  mail.smtp.port="465"
  mail.smtp.auth="true"
  mail.smtp.user="myusername@gmail.com"
  password="mypassword"
  mail.smtp.starttls.enable="true"
  mail.smtp.socketFactory.class="javax.net.ssl.SSLSocketFactory"
/>


그리고 JIRA_HOME/atlassian-jira/WEB-INF/lib 디렉토리에 있는 아래 파일을

  • mail-1.4.1.jar
  • activation-1.1.1.jar

JIRA_HOME/common/lib 디렉토리에 복사한다.


마지막으로 JIRA를 재시작한 후에 Administration | Global Settings | Mail Servers 메뉴에서 SMTP 메일 서버를 추가한다.

이 때 JNDI Location에 java:comp/env/mail/GmailSmtpServer를 입력하면 된다.





2010-03-09

PERSPECTIVE ON PERFORMANCE

Jon Bentley가 쓴 Programming Pearls 6장. PERSPECTIVE ON PERFORMANCE를 정리한 글이다.

책에서 제시한 성능 문제를 해결하는 접근 방법은 다음과 같다.


1. 문제 정의(Problem Definition)

어떤 형태의 문제든 올바르게 문제를 파악하는 것보다 중요한 것은 없다. 성능 문제도 예외일 수 없다.

2. 시스템 구조(System Structure)

모듈화. 큰 문제를 작은 문제로 잘 나누기

3. 알고리즘과 데이터 구조(Algorithm and Data Structure)

잘 쪼개진 작은 문제를 해결하는 도구

4. 코드 튜닝(Code Tuning)

코드 잘짜기. 구조가 아닌 미시적인 접근

5. 시스템 소프트웨어

돈으로 해결하기 혹은 구글링

6. 하드웨어

돈...


시스템 소프트웨어나 하드웨어도 돈만으론 개선되지 않는다. 무엇을 바꾸어야 하는지를 파악해야 돈을 올바르게 쓸 수 있기 때문이다.


그런데 다른 문제와 마찬가지로 성능 문제가 발생할 대상 자체를 제거하는 것이 최상이다.

The cheapest, fastest and most reliable components of a computer system are those that aren't there.

존재하지 않는다면 장애와 보안 헛점에서도 자유롭다.

2010-02-16

프로세스 그 시작 - 스크럼

이름은 들어봤다 수준에서 아주 조금... 몇 발자국만(이나) 앞으로 나간 느낌이다.

Agile 열풍이 휘몰아 치던 시절에 조금 들어 본 스크럼을 우리가 일하는 방법 가운데에 놓았다.

3명이지만 프로세스는 필요하다고 판단했다. CMM Level 5를 준수하는 프로세스는 우리가 지구에서 가장 큰 소프트웨어 회사가 되면(구글과 MS와 애플을 합친 정도) 당연히 사용할 생각이다.(물론 관심없다의 다른 표현이다)

소프트웨어 개발 뿐만 아니라 일반적인 일하는 방식을 잡아줄 필요성에 많은 Agile 방법론 중에서 스크럼을 선택했다. 실제로 회사 설립과 관련한 일들도 백로그로 관리하고 스프린트 태스크로 설정하여 처리했다. 심지어는 마우스와 원두 커피 구매까지...


스크럼을 파악하려고 여러 글들을 읽었는데 그 중 아래 글들이 도움이 되었다.


* 책은 읽지 못했고, 다른 글들은 조금 오래되었거나.. 별로 감흥이 없었다.

첫번째 글은 철학(철학은 시류를 타지 않고 오래간다)을 다루고 두번째 글은 초보자를 위한 스크럼 안내서 같은 역할을 한다.


그리고 도구를 찾았다. 포스트잇을 사용하는 건 조금 어색하고 부끄럽다.

처음에 구글 Doc 스프레드쉬트를 사용했다. Burndown 차트 표현도 가능했고 탭으로 스프린트를 나누어 관리할 수도 있어서 나쁘진 않았다.(엑셀을 생각하면 된다) 그러나 백로그를 체계적으로 관리하기가 쉽지 않았다.

구글 Doc으로는 백로그를 User Story 혹은 Use Case로 기술하기가 쉽지 않다.


스프린트 2까지는 구글 Doc을 사용하다 스프린트 3 중간에 Rally로 전환했는데(이 역시 미리 정해놓은 태스크였다) 스프린트 3 종료와 함께 다시 구글 Doc으로 돌아갔다.

선무당이 Rally를 잡은 격이나... Rally 좋지 않다.


다음은 스프린트 1 Burndown 차트이다.

2주 혹은 4주가 아닌 업무일 기준으로 10일을 스프린트 기간으로 잡았다. 하루 실제 업무 가능한 시간을 5시간으로 정했으니 3명이 한 스프린트에서 사용할 수 있는 시간은 150 시간이다. 하지만 아직 완전한 셋업이 되지 않아 100시간 조금 넘는 규모로 스프린트 태스크를 구성했다.


지금까지 적용 과정에서 얻은 스크럼에 대한 생각은 긍정적이다.

  • 길을 잃지 않게 해준다.
  • 프로세스 자체에 소요되는 시간이 부담이 되지 않는다.(구글 Doc과 Rally를 왔다 갔다 하면서 낭비한 시간을 제외하면)

반면 좀 어렵게 다가오는 점은 백로그에 대한 관리이다. Uncle Bob 지적처럼...

  • 어떻게 백로그를 체계적(계층적)으로 관리할 수 있을까?
  • 백로그와 스프린트 태스크 간의 상관 관계는?
  • 특정 스프린트에서 완료하지 못한 태스크를 어떻게 처리해야 하는가?


(저렴하면서) 구미에 맞는 도구가 없다는 것이 아쉽다. 시간이 나면 스크럼 도구도 고민해봐야겠다.

2010-02-12

에어론 체어(Aeron Chair)... 듀오백

조엘 블로그에서 처음 알았다. 죽이는 의자라는 걸...

에어론 체어

[출처: 여기]


사업을 시작하면서 하나 갖고 싶었다. 물론 내가 갖는다는 건 모두가 갖는 다는 뜻이다. 세명 뿐이다.


Guy KawasakiThe Art of the Start에서 폼(Form)이 아닌 기능(Function)이 중요하다며 에어론 체어를 폼의 대명사로 다루었지만...어쩌면 그가 개발자는 아니었기 때문일거다(물론 내 주관적인 생각).


꽤나 자주 목이 뻐근하고... 큰 무게를 짊어진 것 같은 느낌에 정형 외과에서 물리 치료를 받은 적도 많아 더욱 탐이 났다.


그러나 구매한 건 듀오백 Alpha-100M


[출처: 듀오백 사이트]

얼마나 좋은지는 모르겠다. 에어론 체어를 써보지 않았으니...

NHN이 돈이 많다는 걸 증명한 의자에 대한 자세한 이야기는...


사람들은 다양한 생각들을 가진다.

2010-02-11

좋은 소프트웨어

사업을 시작한 후에 때때로 엄습해오는 불확실성을 억누를 때 기대는 명제가 있다.

좋은(Good) 소프트웨어는 팔린다.


조엘이 블로그에 쓴 다음(Headcount) 글이 이 명제가 참 일 가능성을 한번 더 증명한다.


그렇다면 좋은 소프트웨어는?

  • 사용자가 쓰기에 적절하다. 기능과 성능 측면을 모두 고려해서...
  • 유지보수를 포한한 개발 비용이 감당 가능하다.

결국 둘이 아니라 하나다.

적은 비용을 투자해서 개발한 쓸만한 소프트웨어


물론 구글이나 애플이나 마이크로소프트라면 이렇게 보겠지만...

뻑가는 죽이는 거... 돈 걱정은 하지 말고


불행하게도 혹은 다행하게도 난 구글을 위해 일하지 않는다.

돈을 왕창 쓰는 거야 자신 있지만... 뻑가는 걸 만들 재주는 없다. 돈으로 뻑 가는 걸 만드는 건 아니다. 삼성을 보라구...


어찌되었든 두번째 조건은 만족하면서 망망대해를 향해 항해를 시작했다.

개발자는 둘이다. 쓸만한 노트북, 모니터, 책상, 좋은 의자... 기타 등등을 사는데 소요한 비용도 이전 회사에서 받은 월급에 한참 못 미친다. (일인 기준으로)


그럼 좋은 소프트웨어를 만들 확률은 벌써 50%...

좋은 소프트웨어를 만들면 성공할 확률은 80%... (그렇다 치자^^)


그럼 성공할 확률은 이미 40%... 수학보다 산수가 좋다. 믿거나 말거나...

2010-02-01

CRACKING THE OYSTER

Jon Bentley가 쓴 Programming Pearls 1장. CRACKING THE OYSTER를 정리한 글이다.

제시된 문제는 다음과 같다.

n(최대 10,000,000 개 이하)개의 0보다 큰 정수로 이루어진 파일이 있다. 동일한 값은 존재하지 않으며, 정수 이외의 데이터는 없다.

이 파일에 있는 정수들을 어떻게 정렬할 수 있을까?

  • 메모리는 최대 1 MB까지만 사용할 수 있다.
  • 하드 디스크는 충분히 사용할 수 있다.
  • 몇 분안에 실행이 완료되어야 한다.


디스크를 이용한 Merge Sort

Merge Sort는 다음을 참고한다. 이 문제를 해결하는데 적합한 알고리즘은 아니다.


Multipass Sort

정수를 7 바이트로 표시하면 1 MB 메모리에 약 143,000 개의 정수를 올려 놓을 수 있다.

1024 * 1024 / 7 = 약 149,796

정수를 32 비트로 표시하면 1 MB 메모리에 약 250,000 개의 정수를 올려 놓을 수 있다.

1024 * 1024 / (32 / 8) = 약 262,144

이 정보를 기초로 파일을 여러번 읽어 문제를 해결할 수 있다.

첫번째 읽을 때는 0에서 249,999 사이 정수만을 메모리에 올려 놓은 후에 정렬을 하여 결과를 파일에 쓴다. 이렇게 반복적으로 마지막에는 9,750,000에서 9,999,999 사이 정수만을 대상으로 이 작업을 수행한다.

반복적으로 40 차례 파일을 일으면 모든 정수를 정렬할 수 있다.

10,000,000 / 250,000 = 40


비트맵을 이용한 처리

정수 이외의 데이터는 없고, 중복된 값이 없다는 전제 조건을 이용하면 간단하게 비트맵으로 문제를 해결할 수 있다.

즉, 크기가 10,000,000인 비트 배열을 만들어서 특정 정수가 존재하면 다음과 같이 처리한다. 예를 들어, 13,223가 존재하면 다음과 같이 한다.

bit[13223] = 1;

* 1 MB 이상 메모리가 필요하다. 1 MB가 엄격한 제한 조건이라면 Twopass Sort와 함께 사용한다.


가상 코드는 다음과 같다.

/* phase 1: initialize set to empty */
for i = [0, n)
  bit[i] = 0
/* phase 2: insert present elements into the set */
for each i in the input file
  bit[i] = 1
/* phase 3: write sorted output */
for i = [0, n)
  if bit[i] == 1
    write i on the output file



비트맵을 이용한 해결 방법은 범용적이지는 않다. 즉 특정 문제를 해결하는데 적합하다. 그런데 특정 문제를 해결하는데는 탁월한 방법이다. 

문제를 해결하는데 가장 중요한 것은 문제를 이해하고 정의하는 것이다.
Careful analysis of a small problem can sometimes yield tremendous practical benefits.


2010-01-25

A SAMPLE PROBLEM

Jon Bentley가 쓴 Programming Pearls 12장. SAMPLE PROBLEM을 정리한 글이다.

문제


여론 조사 기관에서는 N 명의 사람 중에서 M 명의 사람을 무작위로 추출하여 설문 조사를 한다.

M < N


N 명의 사람엔 0부터 시작하는 일련번호가 부여되어 있다. N을 5라고 가정하면 다음과 같다.

0 강백호
1 서태웅
2 채치수
3 정대만
4 송태섭

M이 2라면 이 중 동일한 확률로 2명을 추출해야 한다.

이는 0에서 N 사이에서 M 개의 숫자를 동일한 확률로 무작위로 추출하는 것에 다름아니다.

* 이 문제를 해결하는데 다음 2 함수를 사용할 수 있다.

bigrand : m 혹은 n 보다도 클 수 있는 정수를 무작위로 반환한다.
randint(i, j) : i에서 j 사이에 포함되어 있는 정수를 무작위로 반환한다.


솔루션 1

select = m
remaining = n
for i = [0, n)
  if (bigrand() % remaining) < select
    print i
    select--
  remaining--

* 추출된 숫자는 m 보다 많을 수도 m 보다 적을 수도 없다. 강조한 부분을 주의 깊게 보면 이를 파악할 수 있다.

일련번호가 0인 강백호가 선택될 확률은 2 / 5이지만 이 후에 있는 사람들이 선택될 확률은 앞 결과에 영향을 받는다. 예를 들어 강백호가 선택되면 일련번호가 1인 서태웅이 선택될 확률은 1 / 4이고, 강백호가 선택되지 않으면 서태웅이 선택될 확률은 2 / 4이다.

그러나 전체적으로는 서태웅이 추출될 확률도 2 / 5이다.

서태웅이 추출될 확률
= 강백호가 추출될 확률 (2 / 5) * 1 / 4 + 강백호가 추출되지 않을 확률 (3 / 5) * 2 / 4
= 2 / 20 + 6 / 20
= 2 / 5

이를 C++로 구현하면 다음과 같다.

void genknuth(int m, int n)
{
  for (int i = 0; i < n; i++)
    if ((bigrand() % (n - i) < m) {
      cout << i << "\n";
      m--:
    }
}


솔루션 2

set에 난수를 담는 방법도 있다.

* set에는 동일한 값이 들어 갈 수 없다.

initialize set S to empty
size = 0
while size < m do
  t = bigrand() % n
  if t is not in S
    insert t into S
    size++
print the elements of  S in sorted order

C++ STL을 이용하여 구현하면 다음과 같다.

void gensets(int m, int n)
{
  set<int> S;
  while (S.size() < m)
    S.insert(bigrand() % n);
  set<int>::iterator i;
  for (i < S.begin(); i != S.end(); ++i)
    cout << *i << "\n";
}


솔루션 3

n 개 크기 배열을 만든 후에 이들의 위치를 무작위로 변경(swap)하고 처음 m 개를 선택하는 방법도 있다. 화투나 포카에서 카드를 섞는 것(shuffle)과 유사하다.

for i = [0, n)
  swap(i, randint(i, n - 1))


C++를 구현하면 다음과 같다.

void genshuf(int m, int n)
{
  int i, j;
  int *x = new int[n];
  for (i = 0;; i < n; i++)
    x[i] = i;
  for (i = 0; i < m; i++) {
    j = randint(i, n - 1);
    int t = x[i]; x[i] = x[j]; x[j] = t;
  }
  sort(x, x + m)
  for (i = 0; i < m; i++)
    cout << x[i] << "\n";
}

개념 코드와는 다르게 m개 까지만 swap한다. (Ashley Shepherd와 Alex Woronow)



기타

만약 n이 100만이고 m이 n - 10이라면 조금은 다르게 접근해야 한다. 이 경우에는 추출되지 않을 10개를 계산하는 것이 적합하다.



프로그래밍 혹은 문제 해결은 다음과 같이 진행된다.

  1. Understand the Perceived Problem
  2. Specify an Abstract Problem
  3. Explore the Design Space
  4. Implement One Solution
  5. Retrospect

2010-01-13

Putty에서 캐릭터 셋 고정

Putty에서 캐릭터 셋을 UTF-8로 변경해도 다시 접속하면 값이 초기화되는 문제가 있었다.

이를 해결하는 방법이다.

http://k.daum.net/qna/view.html?qid=3yKL8



결론은 계정 정보 자체를 저장해야 한다는 것이다... 직관적이지 못하다...

2010-01-12

Color...

00FF66과 같은 HEX 문자열로 java.awt.Color 객체를 만들어야 했다.

Color 클래스 생성자는 RGB 값만을 파라미터로 받으니 귀찮은 데이터 전환을 해야 하나 고민하다가... 다음 메소드를 이용하면 된다는 걸 알았다.

Color color = Color.decode("00FF66");

그런데 예외가 발생했다. 앞에 #을 붙여 주어야 했다.

Color color = Color.decode("#00FF66");


그런데 스택트레이스 덕분에

Exception in thread "main" java.lang.NumberFormatException: For input string: "0ff66"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
    at java.lang.Integer.parseInt(Integer.java:458)
    at java.lang.Integer.valueOf(Integer.java:528)
    at java.lang.Integer.decode(Integer.java:958)
    at java.awt.Color.decode(Color.java:707)

실제로는 Integer 클래스의 decode 메소드를 사용하면 된다는 것도 알았다.


2010-01-07

이클립스에 static import 처리

이클립스에서 static import를 편리하게 사용하려면 다음 설정을 해준다.

1. Organize Import 처리

http://www.prever.co.kr/josh/?p=35

2. Code Assist 처리

http://blog.naver.com/kcufl/60094963610

2009-12-10

Java EE 6.0

Java EE 6.0이 공식적으로 완결되었다.

Introducing the Java EE 6 Platform



한마디로 Java EE 6.0을 정의하면 Ruby on Rails 따라하기가 되지 않을까...

짧아도 2년 주기로 버전 업그레이드가 되니 지금 좁혀진 간격은 (몇달 안에) 다시금 넓어지겠지...



그래도 톰켓 7.0은 기대가 된다. 빨리 나와라^^

Mark Thomas on Apache Tomcat 7

2009-12-07

아파치 더비 - 칼럼 크기 수정

ALTER TABLE USER ALTER NAME SET DATA TYPE VARCHAR(100);

2009-11-29

Ruby on rails에서 테이블없이 모델 사용하기

Ruby on rails에서 테이블에 매핑되지 않는 입력 폼을 검증할 필요가 있었다.

구글링에서 얻은 가장 만족스러운 방법은 다음이다.

Tableless models in RailsTableless models in Rails


플러그인이나 라이브러리도 필요 없고, 직접 칼럼을 선언하면 된다. 자바에서 get/set을 만드는 걸 생각하면 아무 것도 아니다.

class OnlineDemoRequest < ActiveRecord::Base
  def self.columns() @columns ||= []; end
 
  def self.column(name, sql_type = nil, default = nil, null = true)
    columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
  end
 
  column :email, :string
  column :first_name, :string
  column :last_name, :string
  column :company, :string
 
  validates_presence_of :email
  validates_format_of :email, :with => /^$|^\S+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,4}|[0-9]{1,4})(\]?)$/ix
  validates_presence_of :first_name
  validates_presence_of :last_name
  validates_presence_of :company

end


* 2.3.4에서 테스트를 했다.

Procedure와 Function

메소드는 Procedure와 Function으로 구분된다.

기계적이거나 배타적인 구분을 의미하지는 않는다.

Procedure는 상태의 변화(side effect)를 야기한다.

Function은 무언가를 반환한다.