Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Rails4 で気合を入れるプラグイン acts_as_kiai を作ってみた

Rails4 で気合を入れるプラグイン acts_as_kiai を作ってみた

Rails4でActivityRecordを拡張し、気合を入れてくれるプラグイン acts_as_kiai を開発したのでその時のメモです。

基本的な流れ

  1. 仕様を決める
  2. 開発を始める
  3. コアを書く
  4. 気合を入れるフィールドを追加する
  5. 保存時に気合を入れる
  6. acts_as_kiaiを公開する

参考

基本的に以下を参考にしました。

他に参考になったサイトは途中で出てきます。

早速はじめてみましょう。

  1. 仕様を決める ==============

ある文字列を気合を入れて保存できるプラグインを考えてみます。

1.1 どんな仕様か

文字列中の句読点などを「っ!」に置換して保存するプラグインです。どの文字列を何に置換するかをまとめてみました。

置換前 置換後
っ!! 「こんにちわ。」=> 「こんにちわっ!!」
っ! 「たとえば、」=> 「たとえばっ!」
っっっ!!! 「嫌です!」=> 「嫌ですっっっ!!!」
!? ーーーっっっ!!!??? 「なに?」=> 「なにーーーーっっっつつつ!!!???」

考えないことを決める

考えないことを決めます。今回は作り方を説明したいので以下のことは考慮しません。

  • 。。。や、、、で連続する場合も何も考えずに置換する。
  • 半角のカンマ、ピリオド、!、? は置換しない。
  • 対象の文字列がカタカナの場合も置換後はひらがなで押し切る。
  • 保存するフィールドの文字数制限を超えても知らぬ存ぜぬ。

1.1 名前を決める

acts_as_kiai とします。

1.2 使い方を決める

kiaiフィールドに気合を入れる場合、以下のように記述します。

class Tweet < ActiveRecord::Base
	acts_as_kiai :kiai
end

保存すると以下のように気合が入ります。

tweet = Tweet.new({kiai :'こんにちわ、わたしの名前は山田太郎です。'})
tweet.save

puts tweet.kiai # => 'こんにちはっ!わたしの名前は山田太郎ですっ!!'
  1. 開発を始める ==============

プラグインの開発を始めるには、以下のコマンドを実行します。

$ 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
  1. コアを書く ============

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.rblib/acts_as_kiai/corerequireして実装していきます。

# 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
  1. 気合を入れるフィールドを追加する ==============================

モデルに 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.rbacts_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 で追加された機能です。

extendActiveSupport::Concernのインスタンスメソッドをselfの特異メソッドとして追加しています。

特異メソッドとは、あるオブジェクトにだけ存在するメソッドのことです。

ActiveSupport::Concernは、モジュールのスコープを起動元にして処理します。これによりスコープを合わせるためのコード量が減り、可読性が向上します。

included do は、ActiveSupport::Concernで定義されるメソッドで、最終的に Cencernappend_futuresメソッドでこのブロックに記載したコードが評価されます。

module ClassMethodsのブロックでクラスメソッドを定義します。

def acts_as_kiaiで気合を入れる対象となるフィールドを設定します。

cattr_accessor :kiai_text_fieldkiai_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

無事テストが通りました。

  1. 保存時に気合をいれる ===================

保存の直前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

テストが通りました。

以上でプラグインの実装が完了しました。次にこのプラグインを公開してみましょう。

6. acts_as_kiaiを公開する

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       = ["tomoyuki.inoue@gmail.com"]
  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

これで、世界の皆さんが気合を入れることができるようになりました。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment