Rails4でActivityRecordを拡張し、気合を入れてくれるプラグイン acts_as_kiai を開発したのでその時のメモです。
- 仕様を決める
- 開発を始める
- コアを書く
- 気合を入れるフィールドを追加する
- 保存時に気合を入れる
- acts_as_kiaiを公開する
基本的に以下を参考にしました。
他に参考になったサイトは途中で出てきます。
早速はじめてみましょう。
- 仕様を決める ==============
ある文字列を気合を入れて保存できるプラグインを考えてみます。
文字列中の句読点などを「っ!」に置換して保存するプラグインです。どの文字列を何に置換するかをまとめてみました。
置換前 | 置換後 | 例 |
---|---|---|
。 | っ!! | 「こんにちわ。」=> 「こんにちわっ!!」 |
、 | っ! | 「たとえば、」=> 「たとえばっ!」 |
! | っっっ!!! | 「嫌です!」=> 「嫌ですっっっ!!!」 |
!? | ーーーっっっ!!!??? | 「なに?」=> 「なにーーーーっっっつつつ!!!???」 |
考えないことを決めます。今回は作り方を説明したいので以下のことは考慮しません。
- 。。。や、、、で連続する場合も何も考えずに置換する。
- 半角のカンマ、ピリオド、!、? は置換しない。
- 対象の文字列がカタカナの場合も置換後はひらがなで押し切る。
- 保存するフィールドの文字数制限を超えても知らぬ存ぜぬ。
acts_as_kiai
とします。
kiai
フィールドに気合を入れる場合、以下のように記述します。
class Tweet < ActiveRecord::Base
acts_as_kiai :kiai
end
保存すると以下のように気合が入ります。
tweet = Tweet.new({kiai :'こんにちわ、わたしの名前は山田太郎です。'})
tweet.save
puts tweet.kiai # => 'こんにちはっ!わたしの名前は山田太郎ですっ!!'
- 開発を始める ==============
プラグインの開発を始めるには、以下のコマンドを実行します。
$ rails plugin new acts_as_kiai && cd acts_as_kiai
create
create README.rdoc
create Rakefile
create acts_as_kiai.gemspec
create MIT-LICENSE
create .gitignore
create Gemfile
create lib/acts_as_kiai.rb
create lib/tasks/acts_as_kiai_tasks.rake
create lib/acts_as_kiai/version.rb
create test/test_helper.rb
create test/acts_as_kiai_test.rb
append Rakefile
vendor_app test/dummy
まずポイントとなるファイルは以下のものかと思います。
ファイル名 | 説明 |
---|---|
lib/acts_as_kiai.rb | プラグイン本体 |
test/acts_as_kiai_test.rb | プラグインのテストファイル |
test/dummy | Railsアプリケーション本体 |
テストを実行してみます。
$ rake test
Run options: --seed 27853
# Running tests:
.
Finished tests in 0.177723s, 5.6267 tests/s, 5.6267 assertions/s.
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
- コアを書く ============
String
クラスにto_kiai
というメソッドを追加して文字列を置換できるようにします。
実装を lib/acts_as_kiai/core.rb
に書きたいので、test/core_test.rb
を作成します。
# test/core_test.rb
require 'test_helper'
class CoreTest < ActiveSupport::TestCase
# 「。」を「ッ!!」に置換します
def test_convert_maru_to_kiai
assert_equal 'こんにちわッ!!', 'こんにちわ。'.to_kiai
end
end
テストを実行します。
(Gemfileにgem 'turn'
を記入してbundle install
しています)
$ rake test
Loaded Suite test/dummy/config/environments,test,test/dummy/app/controllers,test/dummy/app/helpers,test/dummy/config,test/dummy/config/initializers
Started at 2013-12-14 18:25:19 +0900 w/ seed 62186.
ActsAsKiaiTest
PASS (0:00:00.179) test_truth
CoreTest
ERROR (0:00:00.180) test_convert_maru_to_kiai
undefined method `to_kiai' for "こんにちわ。":String
@ test/core_test.rb:9:in `test_convert_maru_to_kiai'
Finished in 0.181821 seconds.
2 tests, 1 passed, 0 failures, 1 errors, 0 skips, 1 assertions
メソッドがないので失敗しました。
lib/acts_as_kiai.rb
にlib/acts_as_kiai/core
をrequire
して実装していきます。
# lib/acts_as_kiai.rb
require 'acts_as_kiai/core'
module ActsAsKiai
end
# lib/acts_as_kiai/core.rb
String.class_eval do
# 文字列に気合を入れます
def to_kiai
self.sub!(/。/, 'っ!!')
end
end
テストを実行します。
$ rake test
Started at 2013-12-14 18:38:29 +0900 w/ seed 60172.
ActsAsKiaiTest
PASS (0:00:00.182) test_truth
CoreTest
PASS (0:00:00.183) test_convert_maru_to_kiai
Finished in 0.183943 seconds.
2 tests, 2 passed, 0 failures, 0 errors, 0 skips, 2 assertions
テストが成功しました。
置換について「。」以外の仕様についてもテストと実装を繰り返します。
最終的に以下のようなコードになりました。
# test/core_test.rb
require 'test_helper'
class CoreTest < ActiveSupport::TestCase
# 「。」を「っ!!」に置換します
def test_convert_maru_to_kiai
assert_equal 'こんにちわっ!!', 'こんにちわ。'.to_kiai
end
# 「、」を「っ!」に置換します。
def test_convert_ten_to_kiai
assert_equal 'たとえばっ!', 'たとえば、'.to_kiai
end
# 「!」を「っっっ!!!」に置換します。
def test_convert_bikkuri_to_kiai
assert_equal '嫌ですっっっ!!!', '嫌です!'.to_kiai
end
# 「!?」を「ーーーっっっ!!!???」に置換します。
def test_convert_hatena_to_kiai
assert_equal 'なにーーーーっっっつつつ!!!???', 'なに!?'.to_kiai
end
end
正規表現を使って置換しています。半角全角に注意。
# lib/acts_as_kiai/core.rb
String.class_eval do
# 文字列に気合を入れます
def to_kiai
# !?以外の!を置換します
sub!(/!(?!?)/, 'っっっ!!!')
sub!('!?', 'ーーーーっっっつつつ!!!???')
sub!('、', 'っ!')
sub('。', 'っ!!')
end
end
- 気合を入れるフィールドを追加する ==============================
モデルに acts_as_kiai
を追加すると気合を入れる対象の文字列が格納されるフィールド kiai_text_field
が使えるようにします。
lib/acts_as_kiai_test.rb
にテストを追加します。
require 'test_helper'
class ActsAsKiaiTest < ActiveSupport::TestCase
test "truth" do
assert_kind_of Module, ActsAsKiai
end
test "kiai_text_fieldでTweetモデルのtextフィールドが返ること" do
assert_equal 'text', Tweet.kiai_text_field
end
test "kiai_text_fieldでArticleモデルのcontentフィールドが返ること" do
assert_equal 'content', Article.kiai_text_field
end
end
テストを実行します。
$ rake test
Loaded Suite test/dummy/config/environments,test,test/dummy/app/controllers,test/dummy/app/helpers,test/dummy/config,test/dummy/config/initializers
Started at 2013-12-14 19:40:31 +0900 w/ seed 7417.
ActsAsKiaiTest
ERROR (0:00:00.186) test_kiai_text_fieldでArticleモデルのcontentフィールドが返ること
uninitialized constant ActsAsKiaiTest::Article
@ test/acts_as_kiai_test.rb:13:in `block in <class:ActsAsKiaiTest>'
ERROR (0:00:00.188) test_kiai_text_fieldでTweetモデルのtextフィールドが返ること
uninitialized constant ActsAsKiaiTest::Tweet
@ test/acts_as_kiai_test.rb:9:in `block in <class:ActsAsKiaiTest>'
PASS (0:00:00.189) test_truth
(中略)
Finished in 0.191766 seconds.
7 tests, 5 passed, 0 failures, 2 errors, 0 skips, 5 assertions
Tweetクラス、Articleクラスがないのでテストに失敗しました。
rails g model
でモデルを作成してマイグレーションします。
コマンドは test/dummy
に移動して実行します。
$ cd test/dummy/
$ rails g model Tweet text:string
$ rails g model Article content:string
$ rake db:migrate
$ rake db:test:prepare
acts_as_kiai
ディレクトリに戻ってテストを実行します。
$ cd ../..
$ rake test
Started at 2013-12-14 19:44:53 +0900 w/ seed 5760.
ActsAsKiaiTest
ERROR (0:00:00.216) test_kiai_text_fieldでArticleモデルのcontentフィールドが返ること
undefined method `kiai_text_field' for Article(Table doesn't exist):Class
@ /Users/inoue/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/activerecord-4.0.2/lib/active_record/dynamic_matchers.rb:22:in `method_missing'
test/acts_as_kiai_test.rb:13:in `block in <class:ActsAsKiaiTest>'
ERROR (0:00:00.219) test_kiai_text_fieldでTweetモデルのtextフィールドが返ること
undefined method `kiai_text_field' for Tweet(Table doesn't exist):Class
@ /Users/inoue/.rbenv/versions/2.0.0-p353/lib/ruby/gems/2.0.0/gems/activerecord-4.0.2/lib/active_record/dynamic_matchers.rb:22:in `method_missing'
test/acts_as_kiai_test.rb:9:in `block in <class:ActsAsKiaiTest>'
PASS (0:00:00.221) test_truth
(中略)
7 tests, 5 passed, 0 failures, 2 errors, 0 skips, 5 assertions
kiai_text_field
が見つからずにテストが失敗します。
lib/acts_as_kiai.rb
にacts_as_kiai
メソッドを追加してみます。
ここがポイントになります。
# lib/acts_as_kiai.rb
require 'acts_as_kiai/core'
module ActsAsKiai
extend ActiveSupport::Concern
included do
end
module ClassMethods
def acts_as_kiai(options = {})
cattr_accessor :kiai_text_field
self.kiai_text_field = (field||:text).to_s
end
end
end
ActiveRecord::Base.send :include, ActsAsKiai
extend ActiveSupport::Concern
は Rails4 で追加された機能です。
extend
でActiveSupport::Concern
のインスタンスメソッドをself
の特異メソッドとして追加しています。
特異メソッド
とは、あるオブジェクトにだけ存在するメソッドのことです。
ActiveSupport::Concern
は、モジュールのスコープを起動元にして処理します。これによりスコープを合わせるためのコード量が減り、可読性が向上します。
included do
は、ActiveSupport::Concern
で定義されるメソッドで、最終的に Cencern
の append_futures
メソッドでこのブロックに記載したコードが評価されます。
module ClassMethods
のブロックでクラスメソッドを定義します。
def acts_as_kiai
で気合を入れる対象となるフィールドを設定します。
cattr_accessor :kiai_text_field
で kiai_text_field
へのアクセスを可能にしています。
self.kiai_text_field = (field||:text).to_s
でフィールドを設定しています。field
に指定がなければ、初期値はtext
フィールドに設定されます。
テストを実行してみます。
$ rake test
Started at 2013-12-14 20:00:11 +0900 w/ seed 55794.
ActsAsKiaiTest
PASS (0:00:00.006) test_kiai_text_fieldでArticleモデルのcontentフィールドが返ること
PASS (0:00:00.008) test_kiai_text_fieldでTweetモデルのtextフィールドが返ること
PASS (0:00:00.008) test_truth
(中略)
7 tests, 7 passed, 0 failures, 0 errors, 0 skips, 7 assertions
無事テストが通りました。
- 保存時に気合をいれる ===================
保存の直前before_save
で気合が入るようにします。
テストを追加します。
# test/acts_as_kiai_test.rb
test "Tweetを保存すると気合が入ること" do
tweet = Tweet.new
tweet.text = "こんにちわ、私の名前は山田花子です。・・・やめてください、嫌です!そんな!?"
tweet.save
assert_equal "こんにちわっ!私の名前は山田花子ですっ!!・・・やめてくださいっ!嫌ですっっっ!!!そんなーーーーっっっつつつ!!!???", tweet.text
end
test "Articleを保存すると気合が入ること" do
article = Article.new
article.content = "え、なに!?あ!そういうことか。"
article.save
assert_equal "えっ!なにーーーーっっっつつつ!!!???あっっっ!!!そういうことかっ!!", article.content
end
rake test
でテストが失敗します。
実装を続けます。include do
のブロックを追記します。
# lib/acts_as_kiai.rb
included do
before_save do
# 現在の値を取得して
string = read_attribute(self.class.kiai_text_field)
# 気合を入れて書き込む
write_attribute(self.class.kiai_text_field, string.to_kiai)
end
end
テストを実行します。
$ rake test
Started at 2013-12-15 11:28:47 +0900 w/ seed 17629.
ActsAsKiaiTest
PASS (0:00:00.046) test_Articleを保存すると気合が入ること
PASS (0:00:00.058) test_Tweetを保存すると気合が入ること
PASS (0:00:00.058) test_kiai_text_fieldでArticleモデルのcontentフィールドが返ること
PASS (0:00:00.059) test_kiai_text_fieldでTweetモデルのtextフィールドが返ること
PASS (0:00:00.059) test_truth
(中略)
8 tests, 8 passed, 0 failures, 0 errors, 0 skips, 8 assertions
テストが通りました。
以上でプラグインの実装が完了しました。次にこのプラグインを公開してみましょう。
acts_as_kiai.gemspec.rb
にプラグインの情報を記入します。
# acts_as_kiai.gemspec.rb
$:.push File.expand_path("../lib", __FILE__)
# Maintain your gem's version:
require "acts_as_kiai/version"
# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
s.name = "acts_as_kiai"
s.version = ActsAsKiai::VERSION
s.authors = ["Tomoyuki Inoue"]
s.email = ["[email protected]"]
s.homepage = "https://github.com/tomoyukiinoue/acts_as_kiai"
s.summary = "気合を入れて保存できるプラグインです"
s.description = "acts_as_kiaiを指定したモデルのtextフィールドに対して、保存時に文字列が置換され、気合が入るようになります。"
s.licenses = Dir["MIT-LICENSE"]
s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]
s.test_files = Dir["test/**/*"]
s.add_dependency "rails", "~> 4.0.2"
s.add_development_dependency "sqlite3"
end
README.md
にgithubの最初に表示される情報を記入します。
gem build acts_as_kiai.gemspec
コマンドでビルドします。
$ gem build acts_as_kiai.gemspec
Successfully built RubyGem
Name: acts_as_kiai
Version: 0.0.1
File: acts_as_kiai-0.0.1.gem
Githubにプッシュします。
$ git init
$ hub create acts_as_kiai
$ git add .
$ git commit -m "first commit"
$ git push origin master
以下のアドレスに登録されました。
https://github.com/tomoyukiinoue/acts_as_kiai
hub
コマンドはgithubと連携する便利なコマンドです。ない場合はbrew install hub
でインストールしておきましょう。
RubyGemsに登録します。
$ gem push acts_as_kiai-0.0.1.gem
(Emailとパスワードを入力)
Signed in.
Pushing gem to https://rubygems.org...
Successfully registered gem: acts_as_kiai (0.0.1)
- アカウントがない場合は作っておきます。
以下のアドレスに登録されました。
https://rubygems.org/gems/acts_as_kiai
これで、世界の皆さんが気合を入れることができるようになりました。