달력

4

« 2024/4 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
2008. 4. 11. 23:58

Ruby Programming Ruby2008. 4. 11. 23:58

API Manual 보기

ri GC <-- 클래스
ri garbage_collect <-- method

509 export RI="--format ansi --width 70"
510 ri -c

t1.rb 로딩하기

msmac ~/ruby/chapt-1] irb
>> load "t1.rb"
=> true
>> hello("XXXXX")
Hello XXXXX
=> nil
>> exit

:
Posted by codetemplate
2008. 4. 3. 10:52

Bootcamp 파티션이 안 만들어질 때 Mac2008. 4. 3. 10:52

이럴때 disk 조각 모음을 해야 한다고 한다.
근데

cmd+s하고 들어가서 /sobin/fsck -fy

해도 된다고 한다.
[출처] 부트캠프하려고 파티션 나누다 도중에 에러나시는 분들 이렇게 해보세요! (맥북을 쓰는 사람들) |작성자 연습장

:
Posted by codetemplate
2008. 4. 2. 13:29

rails 팁 Ruby2008. 4. 2. 13:29

1. 모델에 속성 추가할 때

필요한 경우(relationship 추가) model 변경
script/migration mig_name
db/migrate/mig_name 수정
rake db:migrate

:
Posted by codetemplate
2008. 3. 30. 13:38

backlog system을 rails로 개발하기 Ruby2008. 3. 30. 13:38

2008/03/29

프로젝트 생성

msmac ~/ruby> rails backlog

svn에 프로젝트 임포트하기

msmac ~/ruby> svn import backlog http://source.daumcorp.com/private/msbaek/trunk/backlog -m '1 프로젝트 생 성 후'

MakeResourceFul 플러그인 설치

msmac ~/ruby/codetalks] script/plugin install http://svn.hamptoncatlin.com/make_resourceful/trunk
vendor/plugins/make_resourceful 디렉토리에 생성됨

msmac ~/ruby/backlog] svn add vendor/plugins/make_resourceful

msmac ~/ruby/backlog] svn status

msmac ~/ruby/backlog] svn commit -m 'MakeResourceFul 플로그인 설치 후, scaffold 생성 전'
Committed revision 6.

haml 플러그인 설치

msmac ~/ruby/codetalks] sudo gem install haml
msmac ~/ruby/codetalks] haml --rails .

scaffold 생성하기

msmac ~/ruby/backlog] script/generate resourceful_scaffold task type:integer state:integer title:string description:text

이때 주요하게 생성된 파일은 아래와 같다.

app/models/task.rb
app/controllers/tasks_controller.rb
config/routes.rb
db/migrate/001_create_tasks.rb

msmac ~/ruby/backlog] svn commit -m '스캐폴드 생성 후'
Committed revision 7.

:
Posted by codetemplate
2008. 3. 29. 22:52

쾌속 개발 주자의 선두 - 레일즈 활용하기 Ruby2008. 3. 29. 22:52

http://jus1170.tistory.com/4383

svn co svn://rubyforge.org/var/svn/springnote/codetalks@65

1. 프로젝트 생성
msmac ~/ruby] rails codetalks -d sqlite3

1.1 MakeResourceFul 플러그인 설치
msmac ~/ruby/codetalks] script/plugin install http://svn.hamptoncatlin.com/make_resourceful/trunk

1.2 scaffold 생성하기
msmac ~/ruby/codetalks] script/generate resourceful_scaffold code \
> title:string \
> source:text \
> description:tex

1.3 haml 설치
msmac ~/ruby/codetalks] sudo gem install haml

1.4 haml 플러그인 설치
msmac ~/ruby/codetalks] haml --rails .
Haml plugin added to .

http://localhost:3000/codes

blueprintcss 플러그인 설치
script/plugin install http://svn.ariejan.net/plugins/blueprint

이게 잘 안된다.

Ultraviolet을 이용한 코드 문법 강조
http://snippets. aktagon.com/snippets/61-Installing-Ultraviolet-and-Onigurama
$ wget http://www.geocities.jp/kosako3/oniguruma/archive/onig-5.8.0.tar.gz
$ tar zxvf onig-5.8.0.tar.gz
$ cd onig-5.8.0/
$ ./configure
$ make
$ sudo make install
$ sudo gem install -r ultraviolet --include-dependencies

엑스퀘어드 XHTML 편집기의 적용
msmac ~/ruby/codetalks] script/plugin install svn://rubyforge.org/var/svn/springnote/plugins/xquared

msmac ~/ruby/codetalks] vi app/views/layouts/application.html.haml
%html
%head
%title
= title
= stylesheet_link_tag 'application', 'cobalt', :media => 'screen, projection'
= xquared_include_tag

msmac ~/ruby/codetalks] vi app/views/codes/_form.html.haml
%p
%label{:for => "code_title"} Title:
= f.text_field :title
%p
%label{:for => "code_source"} Source:
= f.text_area :source, :cols => 75, :style => 'display:block'
%p
%label{:for => "code_description"} Description:
= xquared_text_area_tag "code[description]", @code['description']

쓰레드 형식으로 보기
msmac ~/ruby/codetalks] script/plugin install svn://rubyforge.org/var/svn/betternestedset/tags/stable/betternestedset

msmac ~/ruby/codetalks] cat app/models/code.rb class Code < ActiveRecord::Base
acts_as_nested_set
end

msmac ~/ruby/codetalks] script/generate migration AddThreadSupport

msmac ~/ruby/codetalks] vi db/migrate/002_add_thread_support.rb

class AddThreadSupport < ActiveRecord::Migration
def self.up
add_column :codes, :parent_id, :integer
add_column :codes, :lft, :integer, :default => 1
add_column :codes, :rgt, :integer, :default => 2
end

def self.down
remove_column :codes, :parent_id
remove_column :codes, :lft
remove_column :codes, :rgt
end
end

msmac ~/ruby/codetalks] rake db:migrate

msmac ~/ruby/codetalks] vi app/views/codes/show.html.haml

= code_highlight current_object.source
%p.description
= current_object.description

%p
= link_to 'Edit', edit_object_path
|
= link_to 'Destroy', object_path, :confirm => 'Really destroy code?', :method => :delete
|
= link_to 'Back', objects_path
|
= link_to 'Reply', new_object_path + "?parent_id=#{current_object.id}"

%hr/
%h2 Replies
= render :partial => 'code', :collection => current_object.all_children

msmac ~/ruby/codetalks] cat app/controllers/codes_controller.rb
class CodesController < ApplicationController
make_resourceful do
actions :all

before :new do
current_object.title = @parent.title if @parent
end

before :new, :create do
@parent = Code.find(params[:parent_id]) unless params[:parent_id].blank?
end

after :create do
current_object.move_to_child_of(@parent) if @parent
end
end
end

코드를 주제별로 분류한다.

:
Posted by codetemplate
2008. 3. 29. 10:38

MySQL Federated Table DB2008. 3. 29. 10:38

오라클의 db link와 유사한 기능을 제공

federeated table은 transaction이 지원되지 않는다.

grant all privileges on *.* to dbUserId@'ip' identified by 'pwd' with grant option;
flush privileges;

CREATE TABLE IF NOT EXISTS tableName (
table description
) ENGINE=FEDERATED DEFAULT CHARSET=utf8 CONNECTION='mysql://userId:pwd@hostname:3306/dbname/tableName'

슬레이브 쪽에, 레플리케이션을 하고자하는 테이블/디비를 설정하거나, 반대로 레플리케이션을 하지 않을 테이블/디비를 my.cnf에 명시해주시면 됩니다.

replicate-ignore-db=indexmessage

replicate-ignore-table=gaiabbs.indexmessage

http://dev.mysql.com/doc/refman/5.0/en/replication-options.html

:
Posted by codetemplate
2008. 3. 20. 01:12

dbcp connection이 자꾸 끊어 질 때 DB2008. 3. 20. 01:12

http://kwon37xi.egloos.com/2472081

testOnBorrow를 유력한 후보로 생각했는데, 이 녀석은 default가 true이다. 그래서 제외

그리고 maxWait는 풀에 가용한 컨넥션이 없을 때 exception을 던지기 전에 풀에 반환되는 컨넥션을 기다리는 최대 millisecond이다. 디폴트는 무한대이다.

정답으로 보이는 설정은 위의 url에 설명된 바와 같다.

timeBetweenEvictionRunsMillis(-1)

The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive, no idle object evictor thread will be run.

numTestsPerEvictionRun(3)

The number of objects to examine during each run of the idle object evictor thread (if any).

minEvictableIdleTimeMillis(1000 * 60 * 30)

The minimum amount of time an object may sit idle in the pool before it is eligable for eviction by the idle object evictor (if any).

:
Posted by codetemplate
2008. 3. 20. 01:11

MySQL Commands DB2008. 3. 20. 01:11

my.conf 사용하기

bin/safe_mysqld --defaults-extra-file=/opt/mysql-5.0.51a-osx10.5-x86/my.cnf --user=root

사용자 추가하기

grant all privileges on *.* to userId@'ip.%' identified by 'password' with grant option; flush privileges;

리플리케이션 상태보기

Read_Master_Log_Pos와 Exec_Master_Log_Pos의 차이가 없어야 함

dump/import

  • /usr/local/mysql/bin/mysqldump --user=userId database --default-character-set=utf8 > xx.sql
  • /usr/local/mysql/bin/mysql --user=userId database [optional_table_name] < xx.sql

uptime 보기

./mysqladmin status -ppassword

커맨드 누적 횟수 보기

mysql> show status like 'Com_%';

프로세스 개수 보기

./mysqladmin -uroot processlist | wc -l
:
Posted by codetemplate
2008. 3. 17. 22:13

Spring, DDD and AspectJ - An Example 프레임워크2008. 3. 17. 22:13

아래와 같은 코드를 예로 들어보자.

public class Customer {

private Long accountId;

public Long getAccountId() {
return accountId; }
}

public void setAccountId(Long accountId) {
this.accountId = accountId;
}
...
}

public class CustomerServiceImpl {
public Double getNetWorth(Customer customer) {

Long accountId = customer.getAccountId();

Account account = accountRepository.getAccountById(accountId);

return account.getValue();
}

}

위의 예는 전형적인 Anemic Domain Model이다(행위는 서비스 레이어에만 있고, 도메인 객체는 단순히 데이터 컨테이너 역할만 한다).

이를 해결하고자 할 때 부딪히는 가장 큰 문제는 Cutomer객체가 어떻게 Account객체를 얻느냐는 것이다. 이 문제에 대한 해결책은 아래와 같은 3가지가 있을 수 있다.

  1. CustomerServiceImpl이 Account를 얻어서 Customer객체에 전달한다. 이 경우 CustomerServiceImpl은 여전히 2개의 비즈니스 로직에 참여하게 되서 위 코드와 별반 다를바가 없게 된다.
  2. Cutomer객체가 service locator를 통해 능동적으로(actively) AccountRepository를 얻는다. 이 방법은 Customer 객체가 ArticleRepository와 service locator에 종속성(dependency)을 갖게 해서 transparent한 해결책이라 할 수 없다.
  3. DI를 통해 ArticleRepository를 Customer 객체에 주입한다.

위 방법 중에 3안이 가장 이상적인 방안으로 보인다.

3안은 아래와 같이 구현될 수 있다.

@Configurable("smartCustomer")
public class SmartCustomer extends Customer {
private AccountRepository accountRepository;

public AccountRepository getAccountRepository() {
return accountRepository;
}

public void setAccountRepository(AccountRepository accountRepository) {
this.accountRepository = accountRepository;
}

public Double getNetWorth() {
Account account = accountRepository.getAccountById(getAccountId());
return account.getValue();
}
}

@Configurable은 해당 클래스가 새로운 인스턴스가 생성될 때 Spring에 의해 DI되어야 한다는 표식이다. @Configurable이 붙은 클래스를 스프링에 등록해 주면 스프링은 그 객체가 생상될 때 그 객체가 필요로 하는 bean들을 주입해준다. 이를 위한 설정 파일은 아래와 같다.

<bean id="smartCustomer" abstract="true" class="org.springdallasug.aspectj.domain.SmartCustomer" scope="prototype" >
<property name="accountRepository" ref="accountRepository" />
</bean>

그리고 LTW로 DI가 발생하도록 하기 위해 JVM 실행 옵션에 아래 라인을 추가한다.

-javaagent:PATH_TO_JAR/aspectjweaver-1.5.0.jar

Pros and Cons
나도 같은 생각이지만 이 글의 필자도 도메인 객체에 DI를 적용하는 것이 맞는지 의문이라고 한다. 예제와 같은 스타일로 구현하게 되면 도메인 객체는 자신의 역할 수행(net worth 계산)을 위한 완벽한 로직을 갖게 되는 장점을 갖는다. 하지만 도메인 객체가 리파지토리를 사용함으로써 이상한 설계가 된다.
보다 풍부한(richer) 도메인 모델이 되지만 복잡해질 수도 있는 보다 많은 종속성이 도메인 모델에 생긴다.

구현 후기
- service 메소드가 인자로 Customer를 받으면 동작하지 않는다. SmartCustomer를 받아야만 제대로 동작한다.

참고자료

:
Posted by codetemplate

Domain 객체에 Repository를 inject해야 하나 ?

먼저 DAO와 Repository의 차이를 알아보자.

presentation <--> service <--> domain <--> data access

이런 관계에서 DAO는 data access layer에 속한다.
DAO는 CRUD 연산을 encapsulate하는데, ORM이 없는 상황에서는 RDB와 OO Technique 간의 impedence를 처리한다.

Eric Evans는 "객체는 더 이상 <자신의 의미나 상호작용에서의 역할 지원>과 관련된 코드가 없을 때 까지 정제되어야 한다"라고 한다.

@Configurable("organization")
class Organization {
// implementation needs to be injected
private EmployeeDAO employeeDao;
// ..
// ..
public List<Employee> getOutstationEmployees() {
List<Employee> emps = employeeDao.getEmployeesByAddress(corporateOffice);
List<Employee> allEmps = employeeDao.getAllEmployees();
return CollectionUtils.minus(allEmps, emps);
}
// ..
}

위의 코드에서 employeeDao를 통해 데이터를 준비하는 것은 Oraganization의 역할이 아니다. 이런 상세한 로직은 data access layer에 가까운 다른 추상화에 속해야 한다.

가장 이상적인 추상화 수준은 EmployeeRepository이다.

Repository의 역할은
- data accessors(Dao)와 상호작용을 추상화하는 분리된 레이어
- 도메인 모델에 "business interface"를 제공한다.

이런 경우 Employee aggregate(Employee 클래스는 Employee aggregate를 구성하는 Adress, Office 등과 같은 클래스와 연관을 갖을 수 있다)에 대해 하나의 Repository를 사용한다.

Aggregate의 리파지토리는 도메인 언어로 도메인 모델에게 data access 서비스를 제공하기 하기 위해 필요한 모든 Dao들과 상호작용하는 책임을 갖는다. 도메인 모델은 리파지토리 덕에 데이터 레이어로부터의 결과를 수집하는 것과 같은 상세 구현과 분리/유지될 수 있게된다.

public interface EmployeeRepository {
List<Employee> getOutstationEmployees(Address address);
// .. other business contracts
}

public class EmployeeRepositoryImpl implements EmployeeRepository {

private EmployeeDAO employeeDao;

public List<Employee> getOutstationEmployees(Address address) {
List<Employee> emps = employeeDao.getEmployeesByAddress(corporateOffice);
List<Employee> allEmps = employeeDao.getAllEmployees();
return CollectionUtils.minus(allEmps, emps);
}
}

리파지토리와 Dao의 주요 차이점은 아래와 같다.

  • Dao는 리파지토리 하위의 추상화 단계로써 데이터베이스로부터의 데이터를 읽어오기 위한 배관 코드(plumbing code)를 가질 수 있다. 데이터베이스 테이블 마다 하나의 Dao를 사용하고, 하나의 도메인 타입이나 aggregate에 대해 하나의 리파지토리를 사용한다.
  • 리파지토리가 제공하는 인터페이스/계약(contract)은 순수 "도메인 중심적"이고 같은 도메인 언어를 사용한다.
리파지토리는 도메인 구조물이다.

리파지토리는 도메인의 ubiquitous 언어를 사용한다. 그래서 리파지토리가 제공하는 인터페이스는 도메인 모델에 속한다. 하지만 리파지토리의 구현은 테이블 종속적인 메소드나 Dao를 사용하기 위한 배관코드를 갖는다. 그래서 순수 도메인 모델은 리파지토리 인터페이스에만 종속적이어야 한다. 마틴 파울러는 이를 위해 "Separated Interface" 패턴(http://www.martinfowler.com/eaaCatalog/separatedInterface.html - 리파지토리 인터페이스는 도메인 패키지 정의하고 구현은 다른 패키지. 클라이언트는 상세 구현을 완전히 몰라도 된다. Separated Interface는 Gateway를 위한 훌룡한 plug point가 된다.)을 추천했다.

도메인 모델에 리파지토리를 Inject하는 것은 아래와 같이 단순하다.

@Configurable("organization")
class Organization {
private String name;
private Address corporateOffice;
// .. other members

// implementation needs to be injected
private EmployeeRepository employeeRepo;

// ..
// ..

public List<Employee> getOutstationEmployees() {
return employeeRepo.getOutstationEmployees(corporateOffice);
}
// ..
}

하이버네이트 같은 ORM을 사용하게 되면 transparent persistence로 인해 Dao가 불필요해진다.

이러한 방식으로 구현된 어플리케이션을 수행하기 위해서는 아래와 같이 jvm에 옵션을 추가해야 한다.

-javaagent:/Users/msbaek/.m2/repository/org/springframework/spring-agent/2.5.2/spring-agent-2.5.2.jar

eclipse에서 아래 화면과 같이 설정하면 테스트 수행시마다 설정을 할 필요가 없어진다.

Javaagent-Options

질의응답
Q1. 리파지토리 대신 Dao에서 Aggregate를 처리하면 되지않나 ?
A1. Dao는 높은 재사용성을 제공하는 fine grained 메소드를 제공. 리파지토리는 도메인 구조물로써 도메인 언어를 사용하고 Aggregation에 대한 coarse grained 메소드를 제공. Dao는 "outstation(출장소)"라는 도메인 언어의 해석을 포함면 안된다.

참고자료

  • http://debasishg.blogspot.com/2007/02/domain-driven-design-inject.html
:
Posted by codetemplate