DeepintoPharo:Chapter 08: Difference between revisions

From 흡혈양파의 번역工房
Jump to navigation Jump to search
(이미지)
(오타수정)
 
Line 59: Line 59:




===Gofet란 무엇인가?===
===Gofer란 무엇인가?===


Gofer는 Monticello를 위한 스크립팅 툴이다. 이는 Lukas Renggli에 의해 개발되었고 Metacello에 의해 사용된다 (Monticello의 최상위에 빌드된 맵과 프로젝트 관리 시스템). Gofer는 패키지의 로딩, 저장, 병합, 업데이트, 인출(fetch)을 위해 스크립트를 쉽게 생성하도록 지원한다. 뿐만 아니라 Gofer는 시스템이 확실히 깨끗한 상태로 유지되도록 한다. Gofer는 하나의 연산으로 여러 저장소에 위치한 패키지를 로딩하고, 최신 버전이나 최근 개발된 버전을 로딩하도록 해준다. Gofer는 Pharo의 기초에 해당하는데, Pharo 1.0 Metacello는 복잡한 프로젝트를 로딩하기 위해 Gofer를 기본 기반 구조로 사용하기 때문이다.  
Gofer는 Monticello를 위한 스크립팅 툴이다. 이는 Lukas Renggli에 의해 개발되었고 Metacello에 의해 사용된다 (Monticello의 최상위에 빌드된 맵과 프로젝트 관리 시스템). Gofer는 패키지의 로딩, 저장, 병합, 업데이트, 인출(fetch)을 위해 스크립트를 쉽게 생성하도록 지원한다. 뿐만 아니라 Gofer는 시스템이 확실히 깨끗한 상태로 유지되도록 한다. Gofer는 하나의 연산으로 여러 저장소에 위치한 패키지를 로딩하고, 최신 버전이나 최근 개발된 버전을 로딩하도록 해준다. Gofer는 Pharo의 기초에 해당하는데, Pharo 1.0 Metacello는 복잡한 프로젝트를 로딩하기 위해 Gofer를 기본 기반 구조로 사용하기 때문이다.  

Latest revision as of 06:06, 20 May 2014

제 8 장 Gofer
패키지 로딩 스크립팅하기

Gofer: 패키지 로딩 스크립팅하기

Pharo는 구문을 기반으로 한 병합, 트리 diff-merge, git-like 분산 버저닝 시스템과 같은 소스 코드를 관리하는 강력한 툴을 제시한다. 특히 Monticello 장에서 보인 바와 같이 Pharo는 Monticello라는 패키지 시스템을 사용한다. 이번 장에서는 Monticello의 주요 측면들을 상기시킨 후에 Gofer를 이용해 패키지를 스크립팅하는 방법을 보일 것이다. Gofer는 Monticello를 위한 단순한 API다. Gofer는 L. Renggli에 의해 개발되었고, 후에 E. Lorenzano와 C. Bruni에 의해 확장되었다. 이는 Metacello 장에서 소개한 패키지 맵을 관리하는 데에 사용되는 언어인 Metacello 에 의해 사용된다.


서문: 패키지 관리 시스템

패키지. 패키지는 클래스 및 메서드 정의의 리스트다. Pharo에서는 패키지가 네임스페이스와 연관되지 않는다. 패키지는 다른 패키지에 정의된 클래스를 확장할 수 있는데, 가령 Network 패키지에 String이 정의되어 있지 않더라도 Network 패키지는 String 클래스에 메서드를 추가할 수 있다는 말이다. 클래스 확장은 레이어의 정의를 지원하고, 패키지의 자연적 정의를 허용한다.


패키지를 정의하기 위해서는 Monticello 브라우저를 이용해 패키지 하나를 선언하고 클래스 확장을 정의하면 되는데, '*' 으로 시작해 패키지명이 따라오는 (본문에서는 '*network') 범주로 된 메서드를 정의하는 것으로 충분하다.

Object subclass: #ButtonsBar
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''


DeepintoPharo Image 8-1-1.jpg
DeepintoPharo Image 8-1-2.jpg
그림 8.1: 브라우저는 String 클래스가 network-url 패키지로부터 asUrl 과 asUrlRelativeTo: 메서드를 얻음을 보여준다.
category: 'Zork'


Monticello Browser에서 패키지를 선택하고 Changes 를 클릭하면 공개 전에 패키지의 변경 리스트를 얻을 수 있다.


패키지 버저닝 시스템. 버전 관리 시스템은 버전 저장을 도와주고 시스템 진화의 히스토리를 보관한다. 뿐만 아니라 소스 코드 저장소에 대한 동시 접근까지 관리한다. 저장된 모든 변경 내용의 흔적을 유지하고, 다른 기술자들과 협력하도록 해준다. 프로젝트가 성장할수록 버전 관리 시스템의 사용은 더 중요해진다.


Monticello는 Pharo의 버전 관리와 패키지 시스템을 정의한다. Pharo에서 클래스와 메서드는 액션이 실행될 때 (슈퍼클래스 변경, 인스턴스 변수 변경, 메서드의 추가, 변경, 삭제 등) Monticello에 의해 버저닝되는 기본 엔티티다. 소스는 HTTP 서버로서, Monticello가 관리하는 프로젝트를 (특히 패키지) 저장할 수 있도록 해준다. 이는 기부자들의 관리와 상태, 선명도(visibility) 정보, RSS 피드와 함께 wiki를 제공한다. 모든 사람에게 공개되는 소스는 http://www.smalltalkhub.com/에서 이용하면 된다.

그림 8.2: change 브라우저를 통해 String>>asUrl 메서드가 변경되었음을 볼 수 있다.


Monticello의 분산 아키텍처. Monticello는 git처럼 분산 버전 제어 관리 시스템이지만 Smalltalk를 집중으로 한다. Monticello는 클래스, 메서드와 같은 소스 코드 엔티티를 조작한다. 그리고 로컬 및 분산형 코드 서버를 관리하는 것이 가능하다. Gofer는 그러한 서버들을 스크립팅하여 서버의 공개(publish), 다운로드, 동기화를 허용한다.


Monticello는 패키지에 대한 로컬 캐시를 사용한다. 패키지가 필요할 때마다 이러한 로컬 캐시에서 먼저 검색된다. 이와 유사한 방식으로, 패키지를 저장할 때는 로컬 캐시에도 저장된다. 물리적 관점에서 보면 Monticello 패키지는 패키지의 완전한 소스 코드와 메타 데이터를 포함하는 압축 파일이다. 좀 더 명확히 설명하기 위해 앞으로는 Pharo 이미지에 로딩된 패키지와 캐시에 저장되었으나 로딩되지 않은 패키지를 구별하겠다. 현재 로딩된 패키지를 패키지의 작업 사본(working copy)이라고 부른다. 그리고, 이미지(가상 머신이 실행하는 바이트코드와 객체), 로딩된 패키지(메모리에 로딩된 서버로부터 로딩된 패키지), 더러운(dirty) 패키지(저장되지 않은 수정내용이 있는 로딩된 패키지)로 정의하겠다. 더러운 패키지는 로딩된 패키지를 의미한다.


예를 들어, 그림 8.3에서 패키지 a.1 은 smalltalk.com 서버로부터 로딩되었으나 수정되지 않았다. 패키지 b.1 는 yoursource.com 서버로부터 로딩되었으나 이미지에 국부적(locally)으로 수정되었다. 더러운 패키지에 해당하는 b.1 이 yoursource.com 서버 상에 저장되면 캐시와 원격 서버에 저장된 b.2 로 버저닝된다.

그림 8.3: (왼쪽) 로딩 및 캐시 저장된 깨끗한 패키지와 더러운 패키지의 일반적인 셋업 - (오른쪽) 공개된 패키지.


Gofer란 무엇인가?

Gofer는 Monticello를 위한 스크립팅 툴이다. 이는 Lukas Renggli에 의해 개발되었고 Metacello에 의해 사용된다 (Monticello의 최상위에 빌드된 맵과 프로젝트 관리 시스템). Gofer는 패키지의 로딩, 저장, 병합, 업데이트, 인출(fetch)을 위해 스크립트를 쉽게 생성하도록 지원한다. 뿐만 아니라 Gofer는 시스템이 확실히 깨끗한 상태로 유지되도록 한다. Gofer는 하나의 연산으로 여러 저장소에 위치한 패키지를 로딩하고, 최신 버전이나 최근 개발된 버전을 로딩하도록 해준다. Gofer는 Pharo의 기초에 해당하는데, Pharo 1.0 Metacello는 복잡한 프로젝트를 로딩하기 위해 Gofer를 기본 기반 구조로 사용하기 때문이다.

아래 표현식을 실행하여 Gofer에게 스스로를 업데이트하도록 요청할 수 있다.

Gofer gofer update


Gofer 사용하기

Gofer의 사용은 간단한데, 위치, 로딩할 패키지, 실행해야 할 동작을 명시하기만 하면 된다. 위치는 종종 파일 시스템을 표현하는데, HTTP, FTP 또는 단순히 하드 디스크가 되기도 한다. 위치는 Monticello 저장소로 접근하는 데 사용되는 것과 동일하다. 아래 표현식에서 사용되는 것처럼 'http://smalltalkhub.com/mc/MyAccount/MyPackage/main' 을 예로 들 수 있겠다.

MCHttpRepository
    location: 'http://smalltalkhub.com/mc/MyAccount/MyPackage/main'
    user: ''
    password: ''


전형적인 Gofer 스크립트를 소개하겠다. 이는 JennikLaval 계정의 http://www.smalltalkhub.com 에서 이용 가능한 PBE2GoferExample 저장소로부터 PBE2GoferExample 패키지를 로딩하고 싶다는 의미다.

Gofer new
    url: 'http://smalltalkhub.com/mc/PharoBooks/GoferExample/main';
    package: 'PBE2GoferExample';
    load.


저장소(HTTP 또는 FTP)가 식별(identification)을 필요로 한다면 url:username:password: 메시지를 이용할 수 있다. 이것은 하나의 메시지이므로 사이에 cascade를 넣지 않도록 주의하라. directory: 메시지는 로컬 파일로의 접근을 지원한다.

Gofer new
    url: 'http://smalltalkhub.com/mc/PharoBooks/GoferExample/main'
    username: 'pharoUser'
    password: 'pharoPwd';
    package: 'PBE2GoferExample';
    load.


"we work on the project PBE2GoferExample and provide credentials"
Gofer new
    url: 'http://smalltalkhub.com/mc/PharoBooks/GoferExample/main/PBE2GoferExample'
    username: 'pharoUser'
    password: 'pharoPwd';
    package: 'PBE2GoferExample'; 	"define the package to be loaded"
    disablePackageCache; 		"disable package lookup in local cache"
    disableRepositoryErrors; 		"stop the error raising"
    load.				"load the package"


동일한 public 서버가 사용되는 것이 보통이기 때문에 Gofer의 API는 스크립트를 단축하기 위해 많은 단축키를 제공한다. 스크립트를 작성해 다른 사람들이 우리 코드를 로딩할 수 있도록 제공하길 원할 때가 있는데, 이런 경우 비밀번호를 명시해야 하는 것은 바람직하지 못하다. smalltalkHub의 예로, GoferExample 프로젝트에 대해 'http://smalltalkhub.com/mc/PharoBooks/GoferExample/main' 와 같은 긴 urls를 들어보자. smalltalkHubUser:project: 메시지를 이용해 최소한의 정보만 명시하겠다. 이번 장에서는 http://ss3.gemtalksystems.com/ss 에 대한 단축키로 squeaksource3: 를 이용하겠다.

"Specifying a user but no password"
Gofer new
    smalltalkhubUser: 'PharoBooks' project: 'GoferExample';
    package: 'PBE2GoferExample';
    load


게다가 명시된 URL에 패키지를 Gofer가 성공적으로 로딩하지 못하면 보통 자신의 이미지의 루트에 위치한 로컬 캐시를 살펴본다. disablePackageCache를 이용해 Gofer가 캐시를 사용하지 못하도록 강요하거나, enablePackageCache 메시지를 이용해 캐시를 사용하도록 강요할 수 있다.


비슷한 방식으로 Gofer는 저장소들 중 하나로 접근할 수 없을 때에 오류를 리턴한다. 우리는 이에 disableRepositoryErrors 메시지를 이용해 그러한 오류를 무시하도록 지시했다. 반대로 활성화하는 메시지는 enableRepositoryErrors를 이용할 수 있겠다.


패키지 식별

URL과 옵션이 명시되고 나면 로딩하고자 하는 패키지를 정의해야 한다. version: 메시지를 사용하면 확실히 로딩해야 할 버전을 정의하는 반면, package: 메시지는 모든 저장소에서 이용 가능한 최신 버전을 로딩하는 데 사용해야 한다.


아래 예제는 패키지의 버전 2를 로딩한다.

Gofer new
    smalltalkhubUser: 'PharoBooks' project: 'GoferExample';
    version: 'PBE2GoferExample-janniklaval.1';
    load


블록을 전달하도록 package: aString constraint: aBlock 메시지를 이용해 패키지를 식별하기 위한 몇 가지 제약을 명시하는 것도 가능하다.


예를 들어, 아래 코드는 janniklaval 이라는 개발자가 저장한 패키지의 최신 버전을 로딩할 것이다.

Gofer new
    smalltalkhubUser: 'PharoBooks' project: 'GoferExample';
    package: 'PBE2GoferExample'
    constraint: [ :version | version author = 'janniklaval' ];
    load


Gofer 액션

여러 개의 패키지 로딩하기

우리는 여러 개의 서버로부터 여러 개의 패키지를 로딩할 수 있다. 구체적인 예를 들기 위해 OSProcess의 Metacello 설정을 이용해 먼저 로딩해야겠다.

Gofer new
    "we will load a version of the configuration of OSProcess "
    url: 'http://www.squeaksource.com/MetacelloRepository';
    package: 'ConfigurationOfOSProcess';
    load.

"Now to load OSProcess you need to ask for its configuration."
((Smalltalk at: #ConfigurationOfOSProcess) project version: #stable) load.


아래 코드 조각은 여러 서버로부터 다수의 패키지를 로딩한다. 로딩 순서는 언제나 그렇듯 스크립트가 Collections-Arithmetic을 먼저 로딩한 후 Sound, 마지막으로 Scratch 비주얼 프로그래밍 언어의 포트인 Phratch를 로딩한다.


Phratch 애플리케이션을 완전히 로딩하기 때문에 시간이 어느 정도 소요될 수 있다는 점을 명심하라.

Gofer new
    url: 'http://smalltalkhub.com/mc/PharoExtras/CollectionArithmetic/main';
    package: 'Collections-Arithmetic';
    url: 'http://smalltalkhub.com/mc/PharoExtras/Sound/main';
    package: 'Sound';
    package: 'Settings-Sound';
    package: 'SoundScores';
    package: 'SoundMorphicUserInterface';
    url: 'http://smalltalkhub.com/mc/JLaval/Phratch/main';
    package: 'Phratch';
    load


이 예제를 살펴보면 Collections-Arithmetic이 smalltalkhub 서버의 CollectionArithmetic 저장소에서 검색되고, Phratch가 smalltalkhub 서버의 Phratch 프로젝트에서 검색된다는 인상을 받을 수도 있다. 하지만 이는 사실이 아니며, Gofer는 이 순서를 고려하지 않는다. 버전 번호가 없을 시 Gofer는 두 개의 서버를 살펴보고 발견되는 가장 최근의 패키지 버전을 로딩한다.


따라서 스크립트를 아래와 같이 다시 작성할 수 있다.

Gofer new
    url: 'http://smalltalkhub.com/mc/PharoExtras/CollectionArithmetic/main';
    url: 'http://smalltalkhub.com/mc/PharoExtras/Sound/main';
    url: 'http://smalltalkhub.com/mc/JLaval/Phratch/main';
    package: 'Collections-Arithmetic';
    package: 'Sound';
    package: 'Settings-Sound';
    package: 'SoundScores';
    package: 'SoundMorphicUserInterface';
    package: 'Phratch';
    load


특정 서버로부터 패키지를 로딩해야 함을 명시하고 싶다면 다수의 스크립트를 작성해야 한다.

Gofer new
    url: 'http://smalltalkhub.com/mc/PharoExtras/CollectionArithmetic/main';
    package: 'Collections-Arithmetic';
    load.
Gofer new
    url: 'http://smalltalkhub.com/mc/PharoExtras/Sound/main';
    package: 'Sound';
    package: 'Settings-Sound';
    package: 'SoundScores';
    package: 'SoundMorphicUserInterface';
    load.
Gofer new
    url: 'http://smalltalkhub.com/mc/JLaval/Phratch/main';
    package: 'Phratch';
    load


그러한 스크립트는 패키지의 최신 버전을 로딩하므로, 새 패키지 버전이 공개될 경우 부적절하더라도 로딩되기 때문에 사실상 취약하다는 점을 주목하라. 대개는 우리가 의존하는 외부 구성요소의 버전을 제어하고 현재 개발에 최신 버전을 이용하는 것이 훌륭한 실습이 되겠다. 이제 그러한 문제는 설정을 표현하고 로딩하는 툴, Metacello로 해결할 수 있겠다.


기타 프로토콜

Gofer는 로컬 디렉터리로부터 로딩을 비롯해 FTP 또한 지원한다. 몇 가지만 변경하여 앞과 동일한 메시지를 사용하겠다.


FTP에서는 'ftp'를 헤딩으로 이용해 URL을 명시해야 한다.

Gofer new
    url: 'ftp://wtf-is-ftp.com/code';
    ...


로컬 디렉터리를 작업하기 위해서는 directory: 메시지 다음에 디렉터리의 절대 경로를 사용해야 한다. 사용할 디렉터리는 /home/pharoer/hacking/MCPackages에서 찾을 수 있음을 명시한다.

Gofer new
    directory: '/home/pharoer/hacking/MCPackages';


마지막으로 별표 기호를 (keen star가 오역 같아 kleene star로 번역하였습니다.) 이용해 저장소와 그 모든 하위 폴더에서 패키지를 검색할 수 있다.

Gofer new
    directory: '/home/pharoer/hacking/MCPackages/*';
    ...


Gofer 인스턴스가 매개변수화되면 여러 액션을 실행하도록 메시지를 전송할 수 있는데, 가능한 액션 목록을 아래 제공하며, 그 중 일부는 추후 설명하겠다.

load 명시된 패키지를 로딩한다.
update 로딩된 패키지 버전을 업데이트한다.
merge 떨어져 있는 버전과 현재 로딩된 버전을 병합한다.
localChanges bases 버전과 현재 수정된 버전 간 변경 내용의 목록을 표시한다.
remoteChanges 현재 수정된 버전과 서버에 공개된 버전 간 변경 내용을 표시한다.
cleanup Cleanup 패키지: 시스템의 오래된 정보가 제거된다.
commit / commit: 떨어진 서버에 패키지를 저장한다 - 메시지 로그 이용.
revert 이전에 로딩된 패키지를 재로딩한다.
recompile 패키지를 재컴파일한다.
unload 이미지로부터 패키지를 언로딩한다.
fetch 원격 서버로부터 원격 패키지 버전을 로컬 캐시로 다운로드한다.
push 로컬 캐시로부터 원격 서버로 버전을 업로드한다.


원격 서버 작업하기

Monticello는 분산형 버저닝 제어 시스템이기 때문에 때때로 원격 서버에 공개된 버전들과 MC 로컬 캐시에 국부적으로 공개된 버전들을 동기화하는 것이 유용하다. 여기서는 그러한 업무를 지원하는 주요 연산을 소개하겠다.


병합, 업데이트, 복구 연산. merge 메시지는 원격 버전과 작업 사본(현재 로딩된) 간 병합을 실행한다. 작업 사본에 일어나는 변경은 원격 버전의 코드와 병합된다. 병합 후 작업 사본이 더러워지고, 재공개(republish)되어야 하는 것이 보통이다. 새 버전은 현재 변경 내용과 원격 버전의 변경 내용을 포함할 것이다. 충돌이 발생할 경우 사용자는 경고를 수신하고, 그렇지 않은 경우 연산은 조용히 발생할 것이다.

Gofer new
    smalltalkhubUser: 'PharoBooks' project: 'GoferExample';
    package: 'PBE2GoferExample';
    merge


update 메시지는 이미지에 원격 버전을 로딩한다. 작업 사본의 수정내용은 손실된다.


revert 메시지는 로컬 버전을 리셋하는데, 현재 버전을 다시 로딩하는 것을 예로 들 수 있다. 이후 작업 사본의 변경 내용은 손실된다.


commit과 commit: 연산. 패키지를 병합하거나 변경했다면 이를 저장하길 원할 것이다. 이러한 작업을 위해 commit과 commit: 메시지를 이용할 수 있다. 후자는 주석을 필요로 하는데, 훌륭한 실습에 해당한다.

Gofer new
    "We save the package in the repository"
    smalltalkhubUser: 'PharoBooks' project: 'GoferExample';
    package: 'PBE2GoferExample';
    "We comments the changes and save"
    commit: 'I try to use the message commit: '


localChanges 와 remoteChanges 연산. 버전을 로딩하거나 저장하기 전에 국부적으로 혹은 서버 상에 일어난 변경 내용을 검증하는 것이 유용하게 작용한다. localChanges 메시지는 마지막으로 로딩된 버전과 작업 사본 간 변경 내용을 표시한다. remoteChanges는 작업 사본과 서버에 마지막으로 공개된 버전의 차이를 보여준다. 두 가지 모두 변경 내용의 목록을 리턴한다.

Gofer new
    smalltalkhubUser: 'PharoBooks' project: 'GoferExample';
    package: 'PBE2GoferExample';
    "We check that we will publish only our changes by comparing local changes versus
        the packages published on the server"
    localChanges


browseLocalChanges와 browserRemoteChanges 메시지를 이용하면 일반적인 코드 브라우저를 이용해 변경 내용을 살펴보는 것이 가능하다.

Gofer new
    smalltalkhubUser: 'PharoBooks' project: 'GoferExample';
    "we add the latest version of PBE2GoferExample"
    package: 'PBE2GoferExample';
    "we browse the latest version published on the server"
    browseRemoteChanges


unload 연산. unload 메시지는 이미지로부터 패키지를 언로딩한다. Monticello 브라우저를 이용하면 패키지를 삭제할 수는 있지만 그러한 연산이 패키지와 연관된 클래스의 코드까지 제거하지는 않으며 사실상 패키지를 파괴할 뿐이라는 사실을 주목해야 한다. 패키지를 언로딩하면 패키지와 그것이 포함하는 클래스를 파괴한다.


아래 코드는 패키지와 그 클래스를 현재 이미지에서 언로딩한다.

Gofer new
    package: 'PBE2GoferExample';
    unload


이런 방식으로는 Gofer를 언로딩할 수 없음을 주목하라. Gofer gofer unload는 효과가 없다.


fetch 와 push 연산. Monticello는 분산형 버저닝 시스템이기 때문에 원격 서버에 강제로 공개하지 않고 자신이 원하는 버전을 모두 국부적으로 저장하는 편이 나으며, 특히 오프라인으로 작업 시 더 그러하다. 이제 모든 로컬 및 원격으로 공개된 패키지를 동기화하는 작업은 지루하다. fetch와 push 메시지를 이용하면 될 일이다.


fetch 메시지는 자신의 로컬 서버에 누락된 패키지를 원격 서버로부터 복사한다. 패키지는 Pharo에 로딩되지 않는다. 인출(fetch)된 이후 원격 서버가 붕괴(break down)하더라도 패키지를 로딩할 수 있다.

Gofer new
    smalltalkhubUser: 'PharoBooks' project: 'GoferExample';
    package: 'PBE2GoferExample';
    fetch


이제 자신의 패키지를 국부적으로 로딩하고 싶다면 검색(lookup)은 로컬 캐시를 고려하고 본 장의 처음에 제시된 바와 같이 오류를 비활성화 시키도록 셋업해야 함을 명심한다 (disableRepositoryErrors와 enablePackageCache 메시지 참고).


push 메시지는 inverse 연산을 실행한다. 국부적으로 이용 가능한 패키지를 원격 서버로 공개한다. 당신이 국부적으로 공개한 모든 패키지는 이후 서버로 삽입된다(pushed).

Gofer new
    smalltalkhubUser: 'PharoBooks' project: 'GoferExample';
    package: 'PBE2GoferExample';
    push


습관상 우리는 사용한 프로젝트나 프로젝트의 모든 버전에 대한 사본을 항상 로컬 캐시에 보관한다. 이 방법을 이용하면 어떤 네트워크 실패가 발생하든 그로부터 자율적일 수 있고, 패키지는 정기적 백업으로 저장된다.


두 개의 메시지를 이용하면 로컬 및 원격 저장소를 동기화하는 sync 스크립트를 작성하기가 쉽다.

Gofer new
    smalltalkhubUser: 'PharoBooks' project: 'GoferExample';
    package: 'PBE2GoferExample';
    push.
Gofer new
    smalltalkhubUser: 'PharoBooks' project: 'GoferExample';
    package: 'PBE2GoferExample';
    fetch


물론 앞에서 언급했듯이 다수의 패키지를 삽입하고 인출할 수 있다.

Gofer new
    smalltalkhubUser: 'PharoBooks' project: 'GoferExample';
    package: 'PBE2GoferExample';
    package: 'PBE2GoferExampleSecondPackage';
    push.
Gofer new
    smalltalkhubUser: 'PharoBooks' project: 'GoferExample';
    package: 'PBE2GoferExample';
    package: 'PBE2GoferExampleSecondPackage';
    fetch


응답 자동화하기

때로는 패키지 설치 시 비밀번호와 같은 정보를 요청한다. 빌드 서버를 체계적으로 이용할 경우 패키지가 그러한 정보의 요청을 중단할지도 모르지만 이러한 질문에 대해 스크립트 내에서 어떠한 응답을 제공하는지 아는 것이 중요하다. 이러한 작업을 지원하는 메시지로 valueSupplyingAnswers: 를 들 수 있다.

[ Gofer new
    squeaksource: 'Seaside30';
    package: 'LoadOrderTests';
    load ]
    valueSupplyingAnswers: {
        {'Load Seaside'. True}.
        {'SqueakSource User Name'. 'pharoUser'}.
        {'SqueakSource Password'. 'pharoPwd'}.
        {'Run tests'. false}.
    }


해당 메시지는 앞에 소개한 예제들이 보여주듯이 블록으로 전송되면 질문과 응답의 목록을 제공한다.


설정 로딩

Gofer는 Metacello 설정 로딩을 지원하기도 한다. 이는 configurationOf:, loadVersion:, loadDevelopment, loadStable과 같은 메시지 집합을 제공하여 설정을 처리하도록 한다.


NativeBoost의 개발 버전을 로딩하는 예제를 들어보자. 여기서 당신이 해야 할 일은 NativeBoost 프로젝트를 명시하고, ConfigurationofNativeBoost를 로딩하여 개발 버전을 실행하기만 하면 된다.

Gofer new
    smalltalkhubUser: 'Pharo' project: 'NativeBoost';
    configuration;
    loadDevelopment


저장소 이름이 설정의 이름과 일치하지 않으면 configurationOf: 를 이용해 configuration 클래스명을 제공해야 한다.


몇 가지 유용한 스크립트

Gofer는 allResolved 메시지를 통해 주어진 저장소 내 모든 패키지를 얻을 수 있는 훌륭한 기능을 제공한다.

스크립트 8.1: 저장소 내 패키지 개수 얻기.

(Gofer new
    smalltalkhubUser: 'Pharo' project: 'NativeBoost';
    allResolved) size


아래 스크립트는 패키지 버전을 패키지별로 그룹화하여 dictionary를 리턴한다. 가장 많이 사용되는 패키지를 이해한다면 도움이 될 것이다.


스크립트 8.2: 버전을 패키지명에 따라 그룹화하기.

((Gofer new
    smalltalkhubUser: 'Pharo' project: 'NativeBoost';
    allResolved)
    groupedBy: [ :each | each packageName])


스크립트 8.3: SS3 상의 Kozen 프로젝트에 관한 패키지 리스트 얻기.

((Gofer new
    squeaksource3: 'Kozen';
    allResolved)
    groupedBy: [ :each | each packageName]) keys


패키지 인출하기

주어진 저장소의 모든 패키지를 인출하는 스크립트를 소개하겠다. 이는 모든 파일을 잡고 버전을 국부적으로 얻는 데에 유용하다.


스크립트 8.4: 저장소의 모든 패키지를 인출하기 (Pharo로부터)

| go |
go := Gofer new squeaksource3: 'Pharo20'.
go allResolved
    do: [ :each |
    self crLog: each packageName.
    go package: each packageName;
        fetch]


스크립트 8.5: Pharo 2.0 저장소로부터 모든 재팩토링 패키지를 인출하기.

| go |
go := Gofer new.
go squeaksource3: 'Pharo20'.
(go allResolved select: [ :each | 'Refactoring*' match: each packageName])
    do: [ :pack |
        self crLog: pack packageName.
        go package: pack packageName; fetch]


로컬 파일 공개하기

아래의 스크립트는 자신의 로컬 캐시로부터 주어진 저장소로 파일을 공개한다.


스크립트 8.6: Pharo 1.4를 이용해 패키지 파일을 새 저장소로 공개하는 방법.

| go |
go := Gofer new.
go repository: (MCHttpRepository
    location: 'http://ss3.gemtalksystems.com/ss/Pharo14'
    user: 'pharoUser'
    password: 'pharoPwd').

((FileSystem workingDirectory / 'package-cache')
    allEntries
        select: [ :each | '*.mcz' match: each])
            do: [ :f | go version: ('.' join: (f findTokens: $.) allButLast); push]


다음 스크립트는 새 파일시스템 라이브러리를 이용하며, 버전이 아니라 패키지명을 어떻게 얻는지도 보여주고자 한다. 스크립트는 mcz 파일을 공개하는 데에도 집중한다. 특정 패키지를 선택적으로 공개하도록 확장할 수도 있다.


스크립트 8.7: Pharo 20을 이용해 패키지 파일을 새 저장소에 공개하는 방법. (Pharo의 버전을 확인하기 바랍니다.)

| go |
go := Gofer new.
go repository: (MCHttpRepository
    location: 'http://ss3.gemtalksystems.com/ss/rb-pharo'
    user: 'pharoUser'
    password: 'pharoPwd').

(((FileSystem disk workingDirectory / 'package-cache')
    allFiles select: [:each | '*.mcz' match: each basename])
        groupedBy: [:each | (each base copyUpToLast: $-) ])
            keys do: [:name | go package: name; push]


스크립트 8.8: SmalltalkHub 상의 Moose Team 로 Fame을 공개하는 방법.

|go repo|
repo := MCSmalltalkhubRepository
    owner: 'Moose'
    project: 'Fame'
    user: 'pharoUser'
    password: 'pharoPwd'.

go := Gofer new.
go repository: repo.
(((FileSystem disk workingDirectory / 'package-cache')
    allFiles select: [:each | '*Fame*.mcz' match: each basename])
        groupedBy: [:each | (each base copyUpToLast: $-) ]) keys
            do: [:name | go package: name; push]


모든 것을 한 페이지에

많은 생각이 필요 없는 간단한 스크립트를 선호하므로 Monticello 저장소들 간 이동하는 전체 버전을 소개하겠다.


스크립트 8.9: Monticello의 어떤 저장소에서 다른 저장소로 동기화하기 위한 스크립트

| source goferSource destination goferDestination files destinationFiles |

source := MCHttpRepository location: 'http://www.squeaksource.com'.
destination := MCSmalltalkhubRepository
    owner: 'TheOwner'
    project: 'YourPackage'
    user: 'YourName'
    password: ''.

goferSource := Gofer new repository: source.
goferDestination := Gofer new repository: destination.

files := source allVersionNames.
"select the relevant mcz packages"
goferSource allResolved
    select: [ :resolved | files anySatisfy: [ :each | resolved name = each ] ]
    thenDo: [ :each | goferSource package: each packageName ].
"download all mcz on your computer"
goferSource fetch.

"check what files are already at destination"
destinationFiles := destination allVersionNames.
"select only the mcz that are not yet at destination"
files
    reject: [ :file | destinationFiles includes: file ]
    thenDo: [ :file | goferDestination version: file ].
"send everything to SmalltalkHub"
goferDestination push.

"check if we have exactly the same files at source and destination"
self assert: destination allVersionNames sorted equals: files sorted.


요약

Gofer는 패키지의 관리를 스크립팅하기 위한 강력하고도 안정적인 구현을 제공한다. 프로젝트 크기가 증가할수록 Metacello의 사용을 고려해야 할 것이다 (제 9장 참고).

이번 장에서는 Gofer로 패키지를 스크립팅하는 방법을 소개하였다.

  • load 메서드는 url: 와 package: 메서드와 함께 주어진 소스로부터 패키지를 로딩하도록 해준다.
  • url: 메서드는 FTP와 로컬 디렉터리 접근을 지원한다.
  • API는 몇 가지 유용한 단축키를 제공한다. smalltalkhubUser:project: 는 http://www.smalltalkhub.com , squeaksource3: 는 http://ss3.gemtalksystems.com/ss/ , gemsource: 는 http://seaside.gemstone.com/ss/ 의 단축키에 해당한다.
  • load를 호출하기 전에 package: 메서드를 여러 번 호출하여 여러 개의 패키지를 로딩할 수 있다.
  • Gofer 인스턴스가 매개변수화되고 나면 update, merge, push 등의 많은 관련된 연산을 실행할 수 있다.


Notes