2008년 8월 31일 일요일

RDoc 을 blog 작성 도구로

JtestR 관련 두번째 글을 작성하고 blogger 에 올리려고 했을 때 잠시 좌절했었다. 소스 코드와 함께 예쁘게 포맷되어서 올려진 다른 글들을 보고는 기본 에디터에서 소스라고 지정해줄 수 있을 것이라고 생각했던 것이다. 소제목들과 소스만 영역 설정해서 표시해주면 될 것이라고 생각했었는데 너무 많은 것을 기대했었다는 것을 올리려는 순간에야 알았다.

참고로한 소스 코드와 함께 올려졌으면서도 예쁘게 포맷된 블로그 포스트의 경우 이미 HTML 출력을 지원하는 에디터의 손을 거친 것이었다. 어떻게 해야하나? 우선은 별 생각 없이 HTML 이나 Blog 저작 툴이라고 되어 있는 것들을 깔아보고 하나를 고를 계획이었다.

그렇지만, HTML 편집을 직접하는 것은 원하지 않았다. 포맷팅에 신경을 쓰지 않고 글을 쓰고 싶었다. Docbook 이나 LaTeX 같은 것을 쓸까? 한 페이지 블로그 엔트리 작성에 그것들을 사용하는 것은 아무래도 오버라는 생각이 들었다.

그렇게 HTML 저작 툴들이 깔려가고 있을 때 떠오른 것이 RDoc 이다.

RDoc ?

RDoc 은 Javadoc 이나 Doxygen 과 비슷한 부류의 프로그램이다. Ruby 소스의 주석으로부터 문서를 만들어내는 도구이지만 소스 파일이 아닌 경우도 포맷팅을 해준다.

RDoc 에서 지원해주는 Markup 들이 딱 내가 필요로하는 정도였다.

  • 빈 줄로 단락 구분
  • 들여쓰기로 verbatim 처리 (소스 표시에 좋다.)
  • 다양한 목록 처리 (일반, numbered, labeled)
  • http: ftp: 등의 링크 표시의 간편함
  • =로 단락 제목 (Heading)

예를 들어서 위 내용의 변환 전 소스는 아래와 같다.

 == RDoc ?

 RDoc[http://rdoc.rubyforge.org/] 은 Javadoc 이나 Doxygen 과 비슷한 부류의 프로그램이다.
 Ruby 소스의 주석으로부터 문서를 만들어내는 도구이지만 소스 파일이 아닌 경우도 포맷팅을 해준다.

 RDoc 에서 지원해주는 Markup[http://rdoc.rubyforge.org/classes/RDoc/Markup.html] 들이 딱 내가 필요로하는 정도였다.
 * 빈 줄로 단락 구분
 * 들여쓰기로 verbatim 처리 (소스 표시에 좋다.)

원래의 JtestR 글을 조금 만져주었더니 RDoc 의 소스로 바로 사용이 가능했다. (링크, 헤딩(Heading), 소스 들여쓰기) 문제는 RDoc 이 Ruby 문서화를 위한 도구이기 때문에 기본 출력이 blog 포스팅에 사용하기에는 불편하다는 점이다. 우선은 인코딩과 출력 양식을 조절해 줄 필요가 있다.

  rdoc -1 -c utf-8 rdoc_as_html_writing.rdoc -n without_template.html
-1
한개의 html 파일로 출력
-c utf-8
인코딩은 utf-8
-n without_template.html
without_template.html 파일로 출력

원하는 단계에 거의 다 왔다. 하지만 여전히 RDoc 의 기본 출력은 군더더기 들이 많아서 불편하다.

예를들면 without_template.html 의 내용은 아래와 같다.

  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  <html>
  <head>
    <title>RDoc Documentation</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  </head>
  <body>
  <h2>File: rdoc_as_html_writing.rdoc</h2>
  <table>
    <tr><td>Path:</td><td>rdoc_as_html_writing.rdoc</td></tr>
    <tr><td>Modified:</td><td>Mon Sep 01 11:11:45 +0900 2008</td></tr>
  </table>

  <h1>RDoc 을 blog 작성 도구로</h1>
  <p>
  ...
  <h2>Classes</h2>
  </body>
  </html>

여기서 파일 이름이나 날짜가 나올 필요도 없고, 관련 Classes 가 나올 필요도 없고 오로지 <body> 테그 안의 </table> 이후의 내용만 있으면 된다. 어떻게? 템플릿을 조금 손 보면 된다.

RDoc 의 기본 팀플릿 중 one_page_html.rb 를 가져다가 손을 보았다.

  cp /usr/lib/ruby/1.8/rdoc/generators/template/html/one_page_html.rb blog_template.rb

수정 후의 blog_template.rb

   module RDoc
   module Page
   ######################################################################
   #
   # The following is used for the -1 option
   #

   CONTENTS_XML = %{
   IF:description
   %description%
   ENDIF:description
   }

   ########################################################################

   ONE_PAGE = %{
   START:files
   } + CONTENTS_XML + %{
   END:files
   }

   end
   end

많은 내용이 있었지만 우리의 목적에는 이것으로 충분하다. 소스 코드가 아닌 파일의 경우에는 그냥 description 으로 처리가 된다.

템플릿을 사용하는 것은 —template 옵션을 사용한다.

최종에 가까워 지는 Rakefile 내용이다. (최종 룰은 아래에서 완성된다.)

  rule '.html' => '.rdoc' do |t|
    sh "rdoc -1 -c utf-8 --template ./blog_template.rb #{t.source} -n #{t.name}"
  end

이제 해당 html 생성은 rake some_file.html 하면 된다.

포스팅

이제 첫째줄을 제목으로 해서 나머지 내용을 올리면 포스팅이 완료된다.

(생성된 html을 blogger.com 에 올릴 때 주의할 점은 "설정"=>"서식" 에서 "줄바꿈 전환" 을 "아니오"로 해 두어야 한다는 것이다. 그렇지 않으면 원하는 결과가 아닌 수시로 줄바꿈이 있는 결과를 보게 된다. 미리보기에는 반영이 안되기 때문에 편집기에 복사해 붙이고 미리보기를 할 경우에는 당황스러울 수 있다.)

(소스 코드 부분의 배경색을 바꿔주기 위해서 "레이아웃"=>"HTML 편집"을 하고 CSS 부분에 <pre> 태그의 스타일을 추가했다.

  pre {
    background: #dddddd;
  }

)

헤딩 조절

기본적으로 blogger 는 포스트 제목에 <h3> 헤딩을 사용한다.(blogger 템플릿에 따라서 다른 지는 모르겠다.) 하지만 RDoc 은 <h1> 부터 출력을 만들어낸다. 그대로 사용하면 전체 적인 조화가 흐트러지는 문제가 생긴다.

해결방법은

  1. 그냥 조화가 흐트러진 채로 쓴다. - (지금까지는 이렇게 했다.)
  2. 작성시부터 <h3> 이후의 헤딩을 사용한다. (=== 부터 사용)
  3. 전처리를 통해서 헤딩을 바꿔주고 그 파일로부터 html을 만들어 준다.
  4. RDoc 처리를 바꾼다. (단순히 RDoc 템플릿을 수정하는 것으로는 해결이 안 되는 것 같다.)

정도를 생각해 볼 수 있었다. 첫번째는 해결방법이 아니고, 두번째는 적절한 방법이 아니다. 세번째 방법은 간단히 할 수 있다.

Rakefile .html 룰 처리에 변환 코드를 넣었다.

   rule '.html' => '.rdoc' do |t|
     h3_heading = IO.read("#{t.source}").map do |l|
       l[/^=/] = '===' if l[/^=/]
       l
     end

     tmp_file = "__#{t.source}"
     #
     tmp_file = tmp_file.gsub!(/[\.\/\\]/,'_')

     File.open(tmp_file, "w") { |f| f.write(h3_heading) }

     sh "rdoc -1 -c utf-8 --template ./blog_template.rb #{tmp_file} -n #{t.name}"

     rm tmp_file
   end

이제 되었다. 이 글을 변환하고 올려 보아야겠다. ^^

2008년 8월 27일 수요일

JtestR 써보기 - 실행

(이 글의 많은 내용은 JtestR 홈페이지의 Getting StartedConfiguration 에 기초하고 있다.)

디렉토리, 기본 설정

JtestR 은 기본적으로 test 디렉토리에 jtestr_config.rb 와 Ruby 테스트들이 있을 것을 기대한다. 물론 이 디렉토리가 test 가 아닐 경우 실행시 지정해 줄 수 있다. 지정해 주는 방식은 JtestR을 실행하는 방식에 따라 조금씩 다르다. (Getting Started 를 참조) JtestR의 기본 classpath 구성은 아래와 같이 이루어 진다.

   def find_existing_common_paths
     Dir["{build,target}/{classes,test_classes}"] + Dir['{lib,build_lib}/**/*.jar']
   end

이것과 현재의 구성이 다를 경우에는 jtestr_config.rb 에서 classpath 를 설정해 주어야 한다.

classpath 설정 예

   classpath ['classes','test_classes']
   classpath Dir['lib/*.jar']
   classpath Dir['test_lib/*.jar']

JUnit 테스트들의 경우는 jtestr_config.rb 에서 지정을 해 주어야 실행이 된다. TestNG 의 경우도 거의 같은 방식으로 지정해줄 수 있다고 한다.(TestNG를 써보지는 않았다.)

jtestr_config.rb 예

   junit 'unit' => ['com.some.company.any.prj.util.SomeAllTest',
                    'com.some.company.any.prj.app.SomeAllTest']

실행

background 서버

JtestR은 시작하는데 시간이 많이 걸린다. 이것은 JRuby 가 시작하는데 시간이 많이 걸리기 때문이다. 이 문제를 해결하기 위해 JtestR은 background 서버를 실행시키는 방법을 제공한다. background 서버가 실행 중일 경우 JtestR은 자동적으로 이것을 사용하도록 되어있다.

Ant, Maven 과 연동

Ant 예

   <target name="test" description="Runs all tests">
     <taskdef name="jtestr" classname="org.jtestr.ant.JtestRAntRunner" classpath="test_lib/jtestr-0.3.1.jar"/>
     <jtestr tests="test"/>
   </target>

Background 서버

   <target name="test-server" description="Starts test server">
     <taskdef name="jtestr-server" classname="org.jtestr.ant.JtestRAntServer" classpath="test_lib/jtestr-0.3.1.jar"/>

     <jtestr-server/>
   </target>
JUnit

JUnit의 TestCase 로 실행시키는 방법을 제시한 것은 다른 IDE 와 결합해서 사용하기 쉽게 해주려는 의도인 것 같다. org.jtestr.ant.JtestRSuite 을 JUnit 테스트로 사용하면 된다.

원문의 예

   <junit haltonfailure="false" fork="yes">
     <jvmarg value="-Djtestr.junit.tests=test_tests/one_of_each"/>

     <formatter type="xml" usefile="false"/>

     <test name="org.jtestr.ant.JtestRSuite"/>
   </junit>

(www.evalcode.com/2008/08/jruby-jtestr-in-eclipse 에 Eclipse 에서 이 방법을 사용해서 JtestR 을 실행시키는 방법이 소개되어 있다.)

command line

classpath 가 바르게 설정되어 있다고 가정한다면 그냥

   java org.jtestr.JtestRRunner

하면 된다.

테스트 디렉토리가 test 가 아닐 경우는

   java org.jtestr.JtestRRunner 22332 my_prj/my_tests

와 같이 하면 된다. (그 외 기타 옵션들에 대한 자세한 안내는 Getting Started 참조)

   java org.jtestr.JtestRRunner [port] [tests] [logging] [configFile] [outputLevel] [output] [groups]

(실수 - 처음 JtestR을 실행시킬 때 엉뚱한 문제로 한참 고생을 했었다. 기존의 JUnit 테스트들을 실행할 때 MethodNotFound 였던가? exception 을 내고는 중단 되는 것이다. 한참을 헤멘 뒤에야 CLASSPATH 에 junit.jar 가 있기 때문이라는 것을 알았다. 컴파일 시의 JUnit 과 실행시의 JUnit 버젼이 달랐다는 …)

(Getting Started 에는 없지만 background 서버를 command line 에서 실행시키는 방법이 있다. (JtestR 0.3.1 기준)

   java org.jtestr.BackgroundServer

)

Eclipse 에서 JtestR

필자의 Eclipse 환경은 대략 이렇다.

  • OS - ubuntu8.04 x86_64
  • JDK - sun-jdk-1.5.0
  • Eclipse - 3.4

(java 6 가 깔려 있기는 하지만 Eclipse 실행이나 개발시에는 쓰지 않는다.)

myeclipse.sh

   LANG=ko_KR \
     JAVA_HOME=/usr/lib/jvm/java-1.5.0-sun \
     PATH=/usr/lib/jvm/java-1.5.0-sun/bin:$PATH \
     ~/usr/eclipse3.4/eclipse

필자 외에는 전부 MS 윈도우가 주 개발 환경이고 기존 코드의 한글 주석이나 스트링이 완성형이었기 때문에 LANG=ko_KR 로 설정하고 실행한다. (eclipse 안에서 설정할 수도 있지만, 처음 eclipse 를 쓰기 시작할 당시에는 이 방법이 가장 간단했다.)

Eclipse 에서 JtestR 을 실행시키는 방법은 위의 방법들이 거의 다 적용 가능하다. ant 도 지원하고 JUnit 테스트도 지원하고, JtestRRunner 를 Run 으로 실행시킬 수도 있다.

처음 시도한 방법은 JUnit 테스트로 실행시키는 방법이다. 그러나, 이 방법은 생각만큼 유용하지 않았다. Fail 이나 Error 시에 해당 파일로 이동이 잘 되지 않았다. JUnit 테스트들은 Failure Trace 가 제대로 나오지도 않았다.

ant 실행이나, 일반 Java Run configuration 을 이용하는 방법은 출력이 거의 유사하다. 이 경우 console 윈도우에서 ruby 파일로의 이동은 여전히 안 되지만, JUnit 테스트 Failure 의 경우는 이동이 가능하다. 현재 상태로는 일반 Java Run configuration 을 이용하는 것이 최선인 것 같다.

(Eclipse 플러그인 중에 coverage 분석 툴인 Emma 를 기반으로한 eclEmma 가 있다. JtestR을 실행하면서 coverage 분석을 하려면 background 서버를 사용하지 않도록 해야 한다.)

Emacs 에서 JtestR

Java 개발에 있어서는 팀의 준 표준 툴이기도 하고, 꽤 괜찮은 툴인 eclipse 를 주로 사용하기는 하지만, 원래 필자는 emacs 신도이다. ruby 파일 수정은 여전히 emacs 에서 주로 이루어지기도 하고, Eclipse 에서 ruby 파일로 바로 링크도 되지 않기 때문에 emacs 에서 JtestR 을 실행시켜 보기로 하였다.

처음 떠오른 것이 C 개발을 emacs 에서 해 본 사람이라면 누구나 기억할 emacs 의 compile 명령이다. M-x compile. (필자의 경우 예전에 너무도 자주 사용하던 명령이라 키 바인딩이 되어 있었다.) Compile command: 하고 물어온다. 관련 설정을 바꾸지 않았다면 처음 실행시에는 "make -k" 가 디폴트로 나올 것이다. "java org.jtestr.JtestRRunner" 라고 입력해도 되지만, 이건 좀 아니다. ant 를 쓴다면 "ant test" 해도 되겠지만, ant 실행시의 출력은 compile 이 적절한 파일을 찾는데 방해가 된다. (물론 여전히 그냥 실행시킨다고 적절한 파일을 바로 찾는 것은 아니긴 하지만 말이다.)

Rake 를 이용하자.

Makefile 을 만들고 make -k 를 그대로 사용하는 방법도 있겠지만, 루비스트 들에게는 Rake 가 있다.

필자가 만든 Rakefile 의 일부이다.

   task :test do |t|
     sh "java -cp test_lib/jtestr-0.3.1.jar org.jtestr.JtestRRunner" rescue nil
   end

(Java 빌드를 위한 ruby 툴로 rake 기반의 툴들이 있다. raven 과 buildr 이 그것들인데 불행하게도 이들을 사용하려할 당시 raven 은 잘 동작하지 않았고 buildr 홈페이지의 문서들은 온전한 상태가 아니었다. 지금은 buildr 의 문서들이 온전히 돌아와 있다.)

이제 Compile command: 에 rake test 를 입력하면 테스트들을 실행한다.

rspec Failure 로 바로 이동

실험을 위해 test/unit/fail_spec.rb 을 만들어 보았다.

   describe "fail test" do
     it "should fail" do
       'a'.should be == 'b'
     end
   end

그러나 compile 에서 fail_spec.rb 로 바로 이동하는 것은 여전히 잘 되지 않았다. 엄밀히 말해서 이동 시켜줄 생각이 전혀 없어 보였다. 이것은 JtestR 이 테스트 결과를 통합해 보여주면서 indent 를 하기 때문인 것 같다.

간단한 해결 방법은 jtestr_config.rb 에 rspec_formatter "p" 라는 내용을 추가 하는 것이다. (Configuration 참조) 이것은 rspec 자체의 출력기를 사용하는 것으로 indent 되지 아니한 결과를 출력하게 되고 이것은 compile 에서 잘 잡아 준다. 대략 다음과 같은 출력이 나온다.

   ...
   'fail test should fail' FAILED
   expected == "b", got "a"
   /home/hjlee/workspace/a_project/test/unit/fail_spec.rb:5:
   ...
그러나 아직은

그러나 compile 과 JtestR을 현재 상태 그대로 사용하는 것은 문제가 있다. (compile 이라는 명령 이름에서 보이듯이 테스트 를 위한 것이 아니라, compile 을 위한 도구이니 딱 입맛에 맞기를 바라는 것이 사실 무리이다.)

rspec 의 경우 위와 같은 꼼수를 사용해서 어찌할 수 있었지만 JUnit 의 경우나 Test::Unit 의 경우에는 여전히 해당 파일로 바로 가기가 어려웠다. 아마도 JtestR이 지원하는 다른 종류의 ruby 테스트 툴들도 비슷할까?

이것이 마음에 드는 수준으로 동작하게 하려면 JtestR과 포함된 테스트 툴들의 출력을 모두 고치거나, 새로운 emacs-lisp 페키지를 만들거나 compile 이 JtestR의 결과를 이해할 수 있도록 커스터마이징, 아니면 수정을 해야 한다.

차선책 - tags

이런 일들이 이루어지거나 누군가 해 놓은 것을 발견하거나 스스로 해결하기 전에는 차선책을 사용할 수 있다. 필자가 생각한 차선책은 TAGS 파일을 사용하는 방법이다. TAGS 파일을 만들고 visit-tags-table 로 이 파일을 읽어둔다. compile 결과 화면에서 이동하고 싶은 class 나 method 이름 위에 커서를 위치하고 C-x 4 . (‘find-tag-other-window’) 로 돌아다니는 것이다.

tags:emacs task

(blog.lathi.net/articles/2007/11/07/navigating-your-projects-in-emacs 여기서 본 것 같은데 열리지가 않는다.)

필자가 어떤 글에서 긁어서 조금 수정한 rake 소스 이다. (원문과의 차이 - *.java 는 없었다. TAGS 과 SRC_FILES 간의 의존 설정이 안 되어 있었다. xctags 가 ctags 로 바뀌었다.) 시스템에 설치되어 있는 ctags 명령은 오리지널 ctags 가 아닌 exuberant-ctags 이어야 한다. (ruby 를 이해하고 emacs 용의 TAGS 도 만들 수 있다.)

   module Tags
     SRC_FILES = FileList['**/*.rb'].exclude("pkg") + FileList['**/*.java']
   end

   namespace "tags" do
     task :emacs => "TAGS"
     file "TAGS" => Tags::SRC_FILES do
       puts "Making Emacs TAGS file"
       sh "ctags -e #{Tags::SRC_FILES}", :verbose => false
     end
   end

   task :tags => ["tags:emacs"]

소스 변경이 있으면 rake tags 로 TAGS 파일을 함께 수정해 준다. 뭐 test 테스크가 tags 테스크에 의존하도록 해 놓아도 될 것 같긴 하지만, 그렇게 해보지는 않았다. (기본 설정 상태에서 TAGS 파일의 변경이 있으면 emacs 가 find-tag 수행 전에 다시 읽을 것인지 물어본다. tags-revert-without-query - 이 customizing variable 로 물어 볼지 그냥 항상 다시 읽을지 설정할 수 있다.)

마치며

간단한 글들을 자주 올리려고 했는데, 세번째 글이 꽤 길어져 버렸다. JtestR은 시작한지 얼마 되지 않아 다른 툴들(시도해 본 것은 Eclipse 와 emacs 뿐이지만)과 함께 사용하기에 아쉬운 부분들이 있기는 하지만, 쓸만한 상태에 도달한 것 같다. 아무튼 누군가 JtestR을 써보려 하는 사람이 있다면 이 글이 도움이 되었으면 좋겠다.

2008년 8월 19일 화요일

JtestR - Ruby 도구들을 이용해서 Java 코드 테스트를 쉽게 해주는 도구.

http://jtestr.codehaus.org/
JtestR is a tool that will make it easier to test Java code with state of the art Ruby tools.
(원문의 state of the art 는 번역하지 않았다. 번역한다면 "첨단의 Ruby 도구들을 ..." 정도가 될 것인데, "첨단"이라는 단어는 왠지 어색한 느낌이고, 그렇다고 자연스러운 적절한 단어가 떠오르지도 않았다. 또, 괜한 Language war의 불씨가 될지도 모른다는 생각도 들었고...)
소개 그대로 JtestR은 주 개발 언어가 Java인 프로젝트에서 Ruby 언어로 된 테스트 도구들을 사용할 수 있도록 해주겠다는 것이다. JUnit 등의 Java 자체 테스트 도구들 보다 Test::Unit 이나 RSpec 같은 Ruby 테스트 도구들이 편하다고 생각하는 나 같은 사람에게 딱 좋은 도구다.