+1

[Step by step] Tự xây dựng Ruby Gem của bạn

Các Rubyist chắc không còn xa lạ gì với khái niệm Gem. Khi cần tìm một Gem nào đó, chúng ta thường truy cập RubyGems. Ở đây, gần như mọi Gem cần thiết cho việc phát triển đều có thể tìm thấy, thật tiện phải không. Nhưng với tư cách là một Rubyist chân chính muốn đóng góp cho cộng đồng Ruby, đã khi nào các bạn đặt ra câu hỏi, làm thế nào để tạo ra Gem ? và làm thế nào để mọi người biết đến, để đóng góp cho cộng động ? Bài viết này sẽ giúp bạn hiểu cơ bản về cách thức tạo và release Gem của mình lên RubyGems.

Gem là gì ?

Trước khi bắt đầu, ta cần tìm hiểu Gem là gì đã. Gem về cơ bản nó chính là các plugin của Ruby. Tuy nhiên trước khi có khái niệm Gem, các plugin của Ruby được tạo ra và cài đặt khá thủ công. Chúng ta phải tải plugin về và giải nén trực tiếp trong source code dự án 😦 Nhưng Gem đã làm cho cách thức cài đặt plugin dễ dàng hơn nhiều. Thay vì nhúng mã của bên thứ ba trực tiếp vào ứng dụng, thì chúng ta giờ chỉ cần lên RubyGems, tìm tên Gem cần thiết và khai báo cho Bundle thực hiện. Bundle sẽ làm nhiệm vụ quản lý Gem bằng cách tải về Gem về, đồng thời cài đặt và tự động cài đặt các phụ thuộc khác cần thiết cho ứng dụng của bạn. Điều tuyệt vời hơn là toàn bộ mã của Gem vẫn được tải về hệ thống của bạn nhưng nó được giữ tách biệt với ứng dụng của bạn, tức là bạn sẽ không thấy mã nguồn của Gem trong ứng dụng, cũng không cần quan tâm tới nó nữa, việc duy nhất ta cần quan tâm là file Gemfile nơi chưa toàn bộ tên Gem được sử dụng và cài đặt trong ứng dụng của bạn.

Xây dựng Ruby Gem

Trước tiên để bắt đầu tạo Gem, bạn cần sử dụng Gem Bundler

$ gem install bundler

Cấu trúc thư mục của Gem

Để tạo ra một Gem, ta sử dụng command dưới đây, dogeify là tên của Gem, bạn có thể sử dụng một cái tên bất kỳ cho nó.

bundle gem dogeify

Gem của chúng ta sẽ được tạo ra với các thư mục tối thiểu:

$ tree dogeify
dogeify
├── .gitignore
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── dogeify.gemspec
└── lib
    ├── dogeify
    │   └── version.rb
    └── dogeify.rb
  • dogeify.gemspec là file cung cấp cho ta thông tin về Gem như: tên, mô tả, tác giả, bản quyền và những cài đặt phụ thuộc khác cần thiết cho Gem hoạt động.
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'dogeify/version'

Gem::Specification.new do |spec|
  spec.name          = "dogeify"
  spec.version       = Dogeify::VERSION
  spec.authors       = ["Matt Huggins"]
  spec.email         = ["matt.huggins@gmail.com"]
  spec.description   = %q{Convert everyday boring English into doge speak!}
  spec.summary       = %q{English to doge translations}
  spec.homepage      = ""
  spec.license       = "MIT"

  spec.files         = `git ls-files`.split($/)
  spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
  spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
  spec.require_paths = ["lib"]

  spec.add_dependency 'engtagger'

  spec.add_development_dependency 'bundler', '~> 1.3'
  spec.add_development_dependency 'rake'
  spec.add_development_dependency 'rspec'
end
  • Tiếp theo hãy nhìn vào file lib/dogeify/version.rb. File này cung cấp số phiên bản đóng gói trong, nó sẽ được phản ảnh trên RubyGems.org
module Dogeify
  VERSION = "0.0.1"
end
  • File cần quan tâm tiếp theo là lib/dogeify.rb. Đây là file được mặc định nạp vào đầu tiên của Gem khi Bundler được chạy.
require "dogeify/version"

module Dogeify
  # Your code goes here...
end
  • Các file còn lại, ít khi thay đổi, nên tôi cũng không nhắc đến ở đây.

Viết test cho Gem

Trong TDD, việc viết unit test là cần thiết trước khi tiến hành implement. Ở đây chúng ta sẽ dùng Rspec để viết unit test. Đầu tiên cần đặt gem Rspec vào phần phụ thuộc của Gem.

# dogeify.gemspec
Gem::Specification.new do |spec|
  # code snipped ...

  spec.add_development_dependency 'rspec'
end

Tiếp đó, ta tạo folder spec như sau:

spec
├─ dogeify_spec.rb
└─ spec_helper.rb

Gem Dogeify có spec như sau:

Input: Đầu vào là một văn bản Process:

  • Tất cả sẽ được lowercased.
  • Trích xuất danh từ và tiền tố của mỗi một trong các tính từ so, such, many, much, very thành một câu gồm 2 từ.
  • Kết thúc mỗi đầu vào là wow.

Viết rspec:

# spec_helper.rb
require "dogeify"
-----------------------------------------------------------------------------------
# dogeify_spec.rb
require 'spec_helper'

describe Dogeify do
  subject { Dogeify.new }

  describe '#process' do
    let(:input) { 'My grandmom gave me a sweater for Christmas.' }
    let(:output) { subject.process(input) }

    it 'converts to lowercase' do
      expect(output.downcase).to eq output
    end

    it 'combines nouns with doge adjectives' do
      expect(output).to match /so grandmom./i
      expect(output).to match /such sweater./i
      expect(output).to match /very christmas./i
    end

    it 'always appends "wow."' do
      expect(output).to end_with 'wow.'
    end
  end
end

Bây giờ, chúng ta cần một rake task để chạy rspec. Đầu tiên, tạo một folder mới tasks chứa file rspec.rake

# tasks/rspec.rake
require 'rspec/core/rake_task'

RSpec::Core::RakeTask.new(:spec)

Tuy nhiên, Gem của ta sẽ không load được file nằm trong thư mục tasks, vì thế chúng ta cần khai báo để chúng trong file Rakefile như sau:

Dir.glob('tasks/**/*.rake').each(&method(:import))

Cuối cùng, chúng ta có thể kiểm tra rspec được rồi.

bundle exec rake spec

Implement

Ở đây, chúng ta sử dụng thêm Gem phục vụ xử lý ngôn ngữ tự nhiên là engtagger

# dogeify.gemspec
Gem::Specification.new do |spec|
  # code snipped ...

  spec.add_dependency 'engtagger'
end

Tiếp theo vào file lib/dogeify.rb

require 'dogeify/version'
require 'engtagger'

class Dogeify
  ADJECTIVES = %w(so such very much many).freeze

  def initialize
    @tagger = EngTagger.new
  end

  def process(str)
    # Convert input to lowercase.
    str = str.downcase

    # Extract nouns, prefixing each with one of the
    # above adjectives into sentences of 2 words.
    tagged_str = @tagger.add_tags(str)
    phrases = @tagger.get_nouns(tagged_str).keys
    phrases = phrases.each_with_index.map do |phrase, i|
      "#{adjective(i)} #{phrase}."
    end

    # End every input with "wow".
    phrases << 'wow.'

    # Return a string, separating each sentence
    # with a space.
    phrases.join(' ')
  end

  private

  def adjective(i)
    ADJECTIVES[i % ADJECTIVES.size]
  end
end

Release gem

Trước khi release, bạn cần kiểm tra version.rb đã phản ánh đúng number version mà bạn muốn chưa. 1.0.0 sẽ là lựa chọn tốt khi bắt đầu 😃 Tiếp theo, bạn nên commit code của mình lên Github, vì Bundler sẽ mặc định làm việc với một vài git repository, phổ biến nhất là Github. Để release lên rubygems, bạn cần tạo tài khoản trước đã. Sau khi tạo tài khoản, bạn có thể sử dụng lệnh sau để release Gem của mình.

$ bundle exec rake release

Và đây là thành quả dogeify

Tham khảo

Bài viết được dịch từ https://quickleft.com/blog/engineering-lunch-series-step-by-step-guide-to-building-your-first-ruby-gem/


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.