루비 Gem 빌드하기 – 2015 업데이트

특히나 다양한 자바스크립트 라이브러리를 레일스 프로젝트의 assets pipeline으로 사용하고자 할 경우, 레일스 프로젝트에서 쉽게 추가할 수 있도록 루비 젬으로 빌드하는 과정을 소개한다.

레일스 프레임워크는 버전 3.1부터 assets pipeline이라는 개념을 도입하여 Sprockets 라이브러리를 이용한 자원관리를 효과적으로 하고 있다. 특히나 다양한 자바스크립트 라이브러리를 assets pipeline으로 사용하고자 할 경우, 매번 app/assets/javascripts/ 또는 app/assets/stylesheets/ 디렉토리에 해당 라이브러리의 파일들을 복사해 넣어 사용해야 하는데, 여간 번거로운 일이 아닐 수 없다. 이 글에서는 이러한 라이브러리들을 레일스 프로젝트에서 쉽게 추가할 수 있도록 루비 젬으로 빌드하는 과정을 소개한다. 만약, 젬 빌드에 대한 기초 지식을 얻고자 하다면 루비젬스 가이드 문서를 참고하면 된다. Lokesh Dhakar의 Lightbox JS라는 자바스트립트 툴을 이용하면 현재의 웹페이지에 이미지들을 오버레이하여 보여 주는 작업을 쉽게 할 수 있다. 또한 Bootstrap 버전도 찾아 볼 수 있다. (Lightbox JS는 현재 v2.0이 릴리스되어 있음) 이 글에서는 바로 이 Bootstrap 버전을 루비 젬으로 빌드할 것이다.

작업 환경

  • Ruby 2.2.2p95
  • Bundler version 1.9.5

젬 파일의 생성

레일스 프로젝트를 개발할 때 필요한 젬을 Gemfile에 등록하고 bundle install 하여 사용한다. 여기서 사용하는 bundle 명령은 새로운 젬을 생성할 때도 사용한다. 아래와 같이 lightbox-bootstrap-rails 라는 젬을 생성하자.

$ bundle gem lightbox-bootstrap-rails
Creating gem 'lightbox-bootstrap-rails'...
      create  lightbox-bootstrap-rails/Gemfile
      create  lightbox-bootstrap-rails/.gitignore
      create  lightbox-bootstrap-rails/lib/lightbox/bootstrap/rails.rb
      create  lightbox-bootstrap-rails/lib/lightbox/bootstrap/rails/version.rb
      create  lightbox-bootstrap-rails/lightbox-bootstrap-rails.gemspec
      create  lightbox-bootstrap-rails/Rakefile
      create  lightbox-bootstrap-rails/README.md
      create  lightbox-bootstrap-rails/bin/console
      create  lightbox-bootstrap-rails/bin/setup
      create  lightbox-bootstrap-rails/.travis.yml
Initializing git repo in /Users/user_account/.../lightbox-bootstrap-rails

디렉토리 구조를 보면 아래와 같다.

$ tree .
.
├── Gemfile
├── README.md
├── Rakefile
├── bin
│   ├── console
│   └── setup
├── lib
│   └── lightbox
│       └── bootstrap
│           ├── rails
│           │   └── version.rb
│           └── rails.rb
└── lightbox-bootstrap-rails.gemspec

5 directories, 8 files

Gemspec 파일 작성

여기서 우선적으로 주목할 것은 gemspec 파일이다. 이 파일엔 해당 젬에 대한 정보를 작성해 둔다. 또한 이 파일은 젬을 Rubygems.org 로 배포하여 공유할 때 반드시 필요한 파일이기도 한다.

# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'lightbox/bootstrap/rails/version'

Gem::Specification.new do |spec|
  spec.name          = "lightbox-bootstrap-rails"
  spec.version       = Lightbox::Bootstrap::Rails::VERSION
  spec.authors       = ["author name"]
  spec.email         = ["author email address"]

  spec.summary       = %q{TODO: Write a short summary, because Rubygems requires one.}
  spec.description   = %q{TODO: Write a longer description or delete this line.}
  spec.homepage      = "TODO: Put your gem's website or public repo URL here."

  # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
  # delete this section to allow pushing this gem to any host.
  if spec.respond_to?(:metadata)
    spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
  else
    raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
  end

  spec.files         = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
  spec.bindir        = "exe"
  spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
  spec.require_paths = ["lib"]

  spec.add_development_dependency "bundler", "~> 1.9"
  spec.add_development_dependency "rake", "~> 10.0"
end

우선, 위의 소스에서 9, 10, 12, 13, 14번 코드라인을 각자에 상황에 맞게 적절하게 수정한다. 다음은 19번 코드라인을 아래와 같이 수정한다.

spec.metadata['allowed_push_host'] = "https://rubygems.org"

그리고, 24번 코드라인을 주목하자.

spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }

files 속성에는 젬에 포함할 파일 목록을 지정해 준다. 디폴트 상에서의 지정된 내용의 결과를 아래와 같이 접근하여 irb 에서 확인해 볼 수 있다.

$ bin/console
irb(main):001:0> `git ls-files -z`
=> ".gitignore\u0000.travis.yml\u0000Gemfile\u0000README.md\u0000Rakefile\u0000bin/console\u0000bin/setup\u0000lib/lightbox/bootstrap/rails.rb\u0000lib/lightbox/bootstrap/rails/version.rb\u0000lightbox-bootstrap-rails.gemspec\u0000"
irb(main):002:0> `git ls-files -z`.split("\x0")
=> [".gitignore", ".travis.yml", "Gemfile", "README.md", "Rakefile", "bin/console", "bin/setup", "lib/lightbox/bootstrap/rails.rb", "lib/lightbox/bootstrap/rails/version.rb", "lightbox-bootstrap-rails.gemspec"]
irb(main):003:0> `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
=> [".gitignore", ".travis.yml", "Gemfile", "README.md", "Rakefile", "bin/console", "bin/setup", "lib/lightbox/bootstrap/rails.rb", "lib/lightbox/bootstrap/rails/version.rb", "lightbox-bootstrap-rails.gemspec"]

요약하자면, 파일 경로상에 test, spec, features 단어가 포함된 파일을 제외한 모든 파일을 포함하라는 것이다. 그러나 우리가 젬에 포함할 것은 lib 디렉토리와 README.md 파일 그리고 라이센스 문서다. 디폴트 상태에서는 라이센트 문서가 누락되어 있으므로 http://opensource.org/licenses/MIT 를 참고하여 MIT-LICENSE 파일을 생성한 후 아래와 같은 내용을 포함시킨다.

The MIT License (MIT)

Copyright (c)  

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

위에서 수정할 내용은 Copyright 부분이다. 부분을 각자에 맞도록 수정한다. 이렇게 해서 spec.files에 지정할 내용은 아래와 같이 변경하도록 하자.

spec.files = Dir["{lib,vendor}/**/*"] + ["MIT-LICENSE", "README.md"]

Vendor 디렉토리 생성

위에서 눈여겨 볼 내용은 현재의 디렉토리상에서는 볼 수 없는 vendor라는 디렉토리가 포함되어 있다는 것이다. 이제 vendor 디렉토리를 생성하고 lightbox.js 에서 사용하는 assets 자원들(javascript/stylesheet)을 복사하여 아래와 같은 디렉토리 구조를 가지도록 한다.

vendor
└── assets
    ├── javascripts
    │   └── lightbox-bootstrap
    │       ├── ekko-lightbox.js
    │       ├── ekko-lightbox.min.js
    │       └── index.js                  # <===
    └── stylesheets
        ├── lightbox-bootstrap
        │   ├── ekko-lightbox.css
        │   └── ekko-lightbox.min.css
        └── lightbox-bootstrap.css.scss   # <===

5 directories, 6 files

필요한 소스파일은 https://github.com/ashleydw/lightbox/tree/master/dist 에 있다. 여기서 두가지 사항을 언급할 필요가 있다. 첫째, index.js 파일이다. 자바스크립트 파일의 인덱스 파일 기능을 사용하기 위한 것이다. 즉, 레일스 프로젝트의 application.js 파일에서 //= require lightbox-bootstrap 명령으로 lightbox 자바스크립트 파일을 포함하기 위한 것이다. index.js 파일의 내용을 아래와 같이 작성한다.

//= require ./ekko-lightbox.min

둘째, lightbox-bootstrap.css.scss 파일이다. 이것은 레일스 프로젝트의 application.css 또는 application.scss 파일에서 각각 아래와 같이 lightbox 스타일시트를 포함하기 위한 것이다. in app/assets/stylesheets/application.css,

/*...
*= require lightbox-bootstrap
*= require ...
*/

또는 in app/assets/stylesheets/application.scss,

...
@import "lightbox-bootstrap";
...

lightbox-bootstrap.css.scss 파일의 내용을 아래와 같이 작성한다.

@import "lightbox-bootstrap/ekko-lightbox.min"

레일스 엔진 선언

이제 마지막으로 코딩을 추가할 부분은 아래와 같다.

$ tree lib
lib
└── lightbox
    └── bootstrap
        ├── rails
        │   └── version.rb   # <=====
        └── rails.rb         # <=====

3 directories, 2 files

version.rb 파일의 내용은 아래와 같다.

module Lightbox
  module Bootstrap
    module Rails
      VERSION = "0.1.0"
    end
  end
end

해당 젬의 버전을 표시하는 상수를 정의한다. 디폴트 값은 0.1.0이지만, 원하는 값으로 변경할 수 있다. 이 젬의 버전은 Lightbox for Bootstrap 3 라이브러리의 버전을 차용하여 minor 버전을 추가하는 방식으로 사용하기로 한다. 즉, Lightbox for Bootstrap 3의 현재 버전이 3.3.0 이므로 이 젬의 버전은 3.3.0.0 으로 정한다. 따라서 이 젬을 배포후 업데이트가 필요한 경우 3.3.0.1 과 같이 마지막 minor 버전번호를 증가시키는 식으로 작성하면 된다. 이제 rails.rb 파일을 열고 아래와 같이 변경한다.

require "lightbox/bootstrap/rails/version"

module Lightbox
  module Bootstrap
    module Rails
      class Engine < ::Rails::Engine    # <===
      end                               # <===
    end
  end
end

화살표시한 부분을 추가했다. 이것은 이 젬을 레일스 엔진으로 둔갑하게 해 주어 레일스 프로젝트에서 이 젬을 사용할 때 assets pipeline으로 사용할 수 있게 해 준다.

젬 배포과정

이제 대략적인 작업이 완료되었다. 현재까지 작업한 내용을 테스트해 볼 수 있도록 레일스 애플리케이션을 생성하여 젬 소스에 추가해 놓으면 젬 사용자들이 소스레벨에서 이 젬을 쉽게 테스트해 볼 수 있을 것이다. 현재 이 젬은 Rubygems.org에 배포된 상태이며, github 저장소를 방문하면 이 테스트 애플리케이션의 소스를 볼 수 있을 것이다.

젬 빌드하기

gemspec에 따라 gem 파일을 생성한다. 이 때 pkg 디렉토리에 젬 파일에 생성된다.

$ rake build
lightbox-bootstrap-rails 3.3.0.0 built to pkg/lightbox-bootstrap-rails-3.3.0.0.gem.

젬 설치하기

gemspec에 따라 gem 파일을 생성하고 로컬 시스템에 설치한다.

$ rake install
lightbox-bootstrap-rails 3.3.0.0 built to pkg/lightbox-bootstrap-rails-3.3.0.0.gem.
lightbox-bootstrap-rails (3.3.0.0) installed.

젬 배포하기

이제 남은 것은 Rubygems.org에 배포하는 것이다. 우선 Rubygems.org 에 등록한 후, 로컬 터미널에서 접근할 수 있도록 명령을 실행하여 인증을 받아야 한다.

$ curl -u [user-account] https://rubygems.org/api/v1/api_key.yaml >
~/.gem/credentials; chmod 0600 ~/.gem/credentials

Enter host password for user '[user-account]':

아래의 명령을 실행하면 버전 tag를 생성하고 젬을 빌드한 후 rubygems.org로 배포하게 된다.

$ rake release
lightbox-bootstrap-rails 3.3.0.0 built to pkg/lightbox-bootstrap-rails-3.3.0.0.gem.
Tag v3.3.0.0 has been created.
...

이제 마지막으로 테스트용 애플리케이션의 Gemfile을 열고 gem ‘lightbox-bootstrap-rails’, path: ‘../’ 코드라인에서 path 옵션을 삭제한 후 bundle update 명령을 실행하여 서버를 재실행하자. 브라우저에서 확인하여 제대로 동작하는지 확인하자.

참고 : 2년전에 관련 글을 작성한 것(Gem Creation with Bundler)도 읽어 보기 바랍니다.

글쓴이: 최효성

외과전문의,웹프로그래밍,컴퓨터 일러스트레이션 / Surgeon, Medical Illustration, Web Programmer

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중