Raccoon Tech Blog [株式会社ラクーン 技術戦略部ブログ]

株式会社ラクーン 技術戦略部より、tipsやノウハウなど技術的な話題を発信いたします。

Ruby

Ruby on Rails5.0へ移行

こんにちは。開発ユニットbcの堀口です。
URIHO開発を担当しています。

今回は弊社のグループ会社である株式会社トラスト&グロースのサービス「URIHO」で使用しているRuby on Railsについてです。
2017年4月27日にRuby on Rails 5.1が正式リリースされましたね。(Rails 5.1のリリースノートはこちら)
「URIHO」ではRails4.2系を使っていますが、このリリースを機にRails5.1に移行しよう!ということになりました。
それでは、さっそくRails5.1に移行するぞ!...といきたいところですが、まずはリリースノートを見てましょう。
既存のアプリケーションをアップグレードするのであれば、その前に質のよいテストカバレッジを用意するのはよい考えです。アプリケーションがRails 5.0までアップグレードされていない場合は先にそれを完了し、アプリケーションが正常に動作することを十分確認してからRails 5.0にアップデートしてください。アップグレードの注意点などについてはRuby on Railsアップグレードガイド を参照してください。
「Ruby on Rails 5.1リリースノート」 https://railsguides.jp/5_1_release_notes.html
どうやらRails5.1にアップデートする前にRails5.0へアップデートしておく必要があるようです。
というわけで本記事ではRails4.2系からRails5.0へのアップデート手順について紹介します!

続きを読む

RubyKaigi2017レポート

itsukushima

こんにちは。開発チームの大原です。

2017年9月18日~20日に広島国際会議場で開催されたRubyKaigi2017に参加してきました。

弊社には技術サポート制度があり、カンファレンス参加費用を出してもらったり、業務として参加させてもらいました。感謝!

(ついでに宮島観光もしてきました。鳥居やまわりの景色が壮大でした!)

各発表を話題ごとに整理して簡単に紹介します。

続きを読む

Rails + JBuilder + AngularJSでJSONベースの画面処理


こんにちは、開発ユニットbcです。

去る8月2日、弊社の子会社である株式会社トラスト&グロースが、新サービス「URIHO」(https://uriho.jp)を開始しました。
(「URIHO」は年商5億円以下の企業様を対象とした、売掛債権の保証サービスです。ご興味ある方はサイトをご覧ください。)

当ユニットがこの新システムの開発を担当したのですが、その中からJSONをベースにした画面処理をどう実装したかについて簡単にご紹介します。

構成としてはサーバサイドにRailsとJBuilder、フロントにAngularJSを採用しています。
JBuilderにはJSON編集のための強力な機能が備わっており、また、AngularJSとの相性が非常に良いと感じました。

処理の流れ

sqd

処理のおおまかな流れとしては上記のような感じです。
では、画面イメージとソースコードで説明していきます。 
続きを読む

[Rails]Model/テーブル設計で必ず覚えておきたいSTI

開発チームの下田です。

Ruby on Railsについて、基本的なこと(modelやcontrollerが何か知っている程度)を理解されている方を対象としています。

BtoBクラウド受注・発注システムCORECはRailsで開発しています。 開発初期のコードを見直してみると失敗したなと思うことがあります。 中でも特に開発効率に影響しているなと思うのが、RailsのActiveRecordのSTI(Single Table Inheritance、単一テーブル継承)を活用しなかったことです。

STIを簡単に言うと、モデルを永続化するときに、どのクラスなのかというメタ情報を含めてデータベースに保存します。 これだけではわかりづらいと思うので、失敗した例と改善例からご紹介したいと思います。

悪い設計

仮に下記の仕様のコードを書くとします。

要件1:
 CORECではWEBアプリから発注書を送信できます。送信方法は次の3つから選べます。

  • WEBアプリ内で送信する
  • メールで送信する
  • FAXで送信する

この時、次のようなコードにしていました。
(項目名、メソッド名は意味が通じるように和名にしています)

# 注文書model 
class Order < ActiveRecord::Base
  include FAX送信モジュール
  validates :送信方法, :presence => true
 
  # WEBで送信するなら送信先IDが必須 
  validates :送信先ユーザ_id, :presence => true, if: ->{ 送信方法 == 'WEB' }
 
  # メールならメールアドレスが必須 
  validates :メールアドレス, :presence => true, if: ->{ 送信方法 == 'メール' }
 
  # FAXならFAX番号が必須 
  validates :FAX番号, :presence => true, if: ->{ 送信方法 == 'FAX' }
 
  has_many :FAX送信結果
 
  def 送信する
    self.送信済 = true
    validate!
    case 送信方法
    when 'メール'
      注文Mailer.メール注文書送信.deliver
    when 'FAX'
      FAX送信する
    end
    save
  end
 
  private
 
  def FAX送信する
    FAX送信結果.create(送信結果: FAX送信モジュール.送信)
  end
end


何が問題か

当初は送信するときだから、送信手段ごとに処理をcase-whenで振り分けることに違和感はありませんでした。しかし、開発を進めていると、同じコードがあちこちに現れて読みづらいコードになり始めました。

要件2:

  • FAXは送信に失敗することがあるので、結果判定をする
  • WEBとメールは必ず成功したとみなす
class Order < ActiveRecord::Base
  # ~~ 略 ~~ 
 
  def 送信成功?
    # メソッドごとに送信方法による分岐がある状態 
    case 送信方法
    when 'FAX'
      FAX送信結果.送信成功?
    default
      true
    end
  end
end


あるべき姿

FAXもメールもすべて注文書の情報なのでOrderクラスにしたことは正しいのですが、送信方法によってふるまいが違います。役割が同じでふるまいが違うなら、サブクラスを作りポリモーフィズムを持たせるべきです。

sti


class Order < ActiveRecord::Base
  def 送信する
    self.送信済 = true
    save
  end
 
  def 送信成功?
    false
  end
end
 
# WEBで送信する注文書クラス
class Order::Web < Order
  validates :送信先ユーザ_id, :presence => true
 
  def 送信する
    validate!
    super
  end
end
 
# メールで送信する注文書クラス
class Order::Mail < Order
  validates :メールアドレス, :presence => true
 
  def 送信する
    validate!
    注文Mailer.メール注文書送信.deliver
    super
  end
end
 
# FAXで送信する注文書クラス 
class Order::Fax < Order
  validates :FAX番号, :presence => true
 
  has_many :FAX送信結果
 
  def 送信する
    validate!
    FAX送信する
    super
  end
 
  def 送信成功?
    FAX送信結果.送信成功?
  end
 
  private
 
  def FAX送信する
    FAX送信結果.create(送信結果: FAX送信モジュール.送信)
  end
end

メソッドの中から冗長なcase文が消えます。また、relationやvalidationのふるまいも送信方法ごとに整理され、見通しが良くなりました。

このようなクラス設計にしたい場合はSTIを使用します。STIを使用するにはtype:stringカラムを追加します。

  create_table "orders", force: true do |t|
    t.string   "type" # 追加するとSTIになる 
    t.integer  "送信先ユーザ_id"
    t.string   "メールアドレス"
    t.string   "FAX番号"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

typeカラムがある場合とない場合を比較してみましょう。

# typeカラムがない場合 
order = Order::Mail.new(メールアドレス: 'hoge@example.com')
order.save
# id: 1 
# メールアドレス: "hoge@example.com" 
 
# OrderからロードするとOrderになる 
Order.find(order.id)
=> #<Order id: 1, メールアドレス: "hoge@example.com"> 
 
# Order::MailからロードすればOK 
Order::Mail.find(order.id)
=> #<Order::Mail id: 1, メールアドレス: "hoge@example.com"> 
# typeカラムがあり、STIになっている場合 
order = Order::Mail.new(メールアドレス: 'hoge@example.com')
order.save
# type: 'Order::Mail' # クラス名がtypeに自動的にsaveされる 
# id: 1 
# メールアドレス: "hoge@example.com" 
# created_at: 2015-01-01 12-34-56 
# updated_at: 2015-01-01 12-34-56 
 
# Orderからロードしても、きちんとOrder::Mailになる 
Order.find(order.id)
=> #<Order::Mail id: 1, メールアドレス: "hoge@example.com"> 
 
# サブクラスからロードすると、typeを検索条件に自動で加えてくれる 
Order::Mail.all.to_sql
=> "select * from orders where type = 'Order::Mail'"

データベースに保存し永続化した後でもふるまいを維持し続けてくれるので、クラス設計を適切に行えば複雑な仕様が追加されても、良い状態のコードを保てると思います。

運用上の注意

開発には非常に便利なSTIですが、運用を考えて開発しなければならないことが1点あります。typeカラムに存在しないclass名が設定されていると、ActiveRecord::SubclassNotFound例外が発生します。 ActiveRecord::SubclassNotFound例外が発生するのは、2パターンがあります。

  • リリース時に並行稼動している時に、新しいAPで保存した新しいサブクラスを古いAPでロードした
  • リリースしたがロールバックし、新しいAPで保存した新しいサブクラスを古いAPでロードした<

どちらの場合も、何も変更しないサブクラスを作り、先行してリリースしておくと、予期せぬ例外が起きません。フェイルソフトになります。

# 仮置きする。 
class Order::NewClass < Order
end

もしくはデフォルトスコープに現バージョンで存在しているclassのみを指定すると、ロードされないので例外が発生しません。サブクラスを追加した時に追加し忘れたり、where句が思わぬところで効いたりしないか確認が必要です。

class Order < ActiveRecord::Base
  # 条件に加えてしまう。
  default_scope ->{ where(type: [Order, Order::Corec, Order::Mail, Order::Fax]) }
  # ~~ 略 ~~ 
end

おわりに

STI以外にも、ああすればよかったと思うことは多くあります。その中でもSTIについて書いたのは、知っているか知らないかでModelの設計に差が出てしまうため、気づいた時には手軽にはリファクタリングできなくなっているからです。その反面、ある程度の規模のアプリケーションでなければ使い道が無くRuby on Rails チュートリアルに載っていなかったりと軽視されがちです。 新しくmodelを設計するときには頭の片隅に置いておくといいと思います。

RubyKaigi2015レポート

メリークリスマス!

技術開発部のおおはらです。

本日12月25日はクリスマスでもありますが、Ruby2.3のリリース日でもあります!

毎年クリスマスにRubyはバージョンアップリリースを続けており、 開発や利用が盛んであることはRubyエンジニアにとって喜ばしい限りです。

ちなみに私が開発を担当しているコレックはRuby on Railsを使用しています。

さて、先日開催されたRubyKaigi2015に参加してきましたので、そのレポートをお送りしたいと思います。

RubyKaigi2015

1日目

Ruby3 challenges

  • Yukihiro "Matz" Matsumotoさん(rubyの作者)の基調講演
  • Rubyコミュニティのスローガン「MINASWAN」の紹介
  • Ruby2.3.0の新機能の紹介
    • did_you_mean gemがデフォルトでバンドルされる
      • タイポした時に正しいと思われる変数名やメソッド名などを教えてくれる
    • grep_vメソッド
      • パターンにマッチしないものを集める(grep -v)
    • digメソッド
      • 深い階層の要素(例:array[0][1][2])をエラー無く取得
    • indented here document
      • sql = <<~SQLとするとインデントの浅いところに合わせて先頭を削除
    • safe navigation operator(ぼっち演算子) &.
      • &.が膝を抱えて座って、石を寂しそうにながめている人に見えることからぼっち演算子と命名。面白くて覚えやすい!
      • user && user.accountuser&.accountのように簡潔に書ける。 railsのtry!と同じ。
    • Ruby3でStringがimmutableになることへの移行パス(マジックコメント)
  • Ruby3の進捗
    • Ruby2.0と比較して3倍の速度向上を目標
    • そのためにconcurrency(並行性)やJITコンパイラの導入を検討中
  • http://togetter.com/li/911263

Compiling Ruby scripts

  • 笹田耕一さん。Rubyコミッタ。Heroku社でのMatzチームの一員。
  • 性能改善のためにRubyスクリプトの実行前コンパイルを検討中
  • 作成したISeq(InstructionSequence)シリアライザ、デシリアライザの効果
    • 高速起動
    • メモリ削減
    • コンパイル済みコードを別プロセスで共有できる
  • Ruby2.3のsample/iseq_load.rbで試せる
  • 2015_RubyKaigi2015.pdf

Saving people from typos

  • Yuki Nishijimaさん。Pivotal Labs社。kaminari gemのメンテナ
  • Ruby2.3から同梱されるようになったdid_you_mean gemの仕組みの解説
  • spell checkerとmonkey patchで構成
  • spell checker
    • dictionary, control mechanism, optimizationで構成
  • dictionary
    • Symbol.all_symbolsなどで辞書を作成、エラータイプで辞書を分類
  • control mechanism
    • ミスタイプをLevenshtein距離、ミススペルをJaro-Winkler距離+Prefix bonuxで評価
  • monkey patch
    • NameError#to_sをオーバーライドして出力
    • 例外が発生した場所を特定するためにRuby2.3以前はC拡張を使っていたが、Ruby2.3から追加されたNameError#receiverが使えるようになった
  • http://togetter.com/li/911325

Time flies like an arrow; Fruit flies like a banana: Parsers for Great Good

Fast Metaprogramming with Truffle

  • Kevin Menardさん。Oracle社のエンジニア。
  • JRubyとTruffleを使うとメタプログラミングによる実行効率が落ちない
    • 例:method_missing の実行効率が落ちない
  • http://togetter.com/li/911356

High Performance Template Engine: Guide to optimize your Ruby code

  • Kohei Suzukiさん、Takashi Kokubunさん。両名ともクックパッド社。
  • Suzukiさんはfamlテンプレートエンジンを作成
  • benchmark-ips gemでベンチマークし、stackprof, rblineprof gemでプロファイリング、コード改善
  • slimテンプレートエンジンと同じバックエンドのtempleを使用
  • クックパッド社でfamlを本番投入している
  • KokubunさんはHamlitテンプレートエンジンを作成
  • stringを先につなげて高速化
  • いらない機能・オプションを削って高速化
  • Famlより高速だが互換性を一部捨てた
  • Q.同じ会社の同じ部署にいるなら、統合して互換性を失わないモードをオプションで提供しないのはなぜか
    • A. Hamlitは自分の勉強のために作りたかったから。作ったついでにベンチマークで勝ちたかった。
  • https://speakerdeck.com/k0kubun/high-performance-template-engine

TRICK 2015: The second Transcendental Ruby Imbroglio Contest for RubyKaigi


2日目

Keynote

  • KOSAKI Motohiroさん。Rubyコミッタ、Linuxカーネルコミッタ。
  • Linuxデバッグツールの紹介
    • Ftrace
      • Linuxのイベントをトレース
    • Perf tools(パフォーマンスツール群)
      • perf top 実行中のプロセスの関数ごとのtop
      • perf trace 全プロセスが呼んだシステムコールを記録
    • System Tap
      • デバッグスクリプト言語
      • 言語仕様があまり良くない(loopがないなど)
      • CとRubyでシステムコールが取れる
  • http://togetter.com/li/911612

The history of testing framework in Ruby

  • Kouhei Sutouさん。クリアコード社の代表。
  • /¥Atest.+unit¥z/iにマッチするtest unitの違いがわかるようになる話
  • Ruby1.3
    • testsupp
  • Ruby1.4
    • RubyUnit
  • Ruby1.6
    • Lapidary
    • Test::Unit(RubyUnit + Lapidary)
  • Ruby1.8
    • Test::Unit
      • rubyが標準でバンドル。require 'test/unit'することからtest/unitとも表現される
      • メンテナが交代したが、test/unitが複雑でメンテできなくなった
  • Ruby1.9
    • Rubyはtest/unitをバンドルしなくなった
      • test-unit gemとしてリリース(メンテナがSutouさん)
    • minitest
      • 作者はtest/unitのメンテナ)
      • Ruby開発者がminitestのラッパーとして新しくtest/unitを作成してrubyにバンドル
  • Ruby2.1
    • minitest5で互換性のない変更が入った
    • ruby自身のテストにはminitest4を使用
    • ユーザには最新のminitestを提供
  • Ruby2.2
    • minitest5をバンドル
    • 再びtest-unit gemをバンドル(for user)
    • ruby自身のテストにはフォークしたminitest4とtest/unitを使う
  • http://slide.rabbit-shocker.org/authors/kou/rubykaigi-2015/

Turbo Rails with Rust

The worst Ruby codes I've seen in my life

Ruby for one day game programming camp for beginners

  • Ippei Obayashiさん。京大マイコンクラブのメンバー。東北大助教授。
  • 京大マイコンクラブの新入生歓迎イベントとして1日ゲームプログラミングを開催
  • プログラミング初心者である参加者の8割ほどが、動くゲームを完成
  • 成功の原因を考察
    • 参加者と同数のクラブのメンバーがマンツーマンで教えた
    • 1日という締め切りがあること
      • できることの見積がしやすい
      • 型を教えない、グローバル変数で頑張る
      • インストールを簡単にする工夫(ゲームのスターターキット)
  • http://togetter.com/li/911699

Ruby meets Go

Building CLI Apps for Everyone

making robot with mruby

LT

Update Early, Update Often

Automating View Internationalization in Ruby on Rails

  • Rails2から3に変わるときにデフォルトでhメソッド(html_escape)が実行されるのと同じように、I18nのtメソッドやlメソッドがデフォルトで実行されても良いのでは?という提案

A new testing framework Rgot

Building an Unbreakable MRI-based Embedded Computer Appliance

Do you trust that certificate?

How I debugged debugger

Padrino Travel Guide

What I learned by implementing a Ruby VM in Erlang

Rubygemsで作るお手軽データ分析基盤 〜あるいは 私はどうやって他人の褌で相撲を取ったか〜

  • gem選びの基準はシンプル、読みやすい、プルリクの送りやすさ
  • プルリクを送る時に、自分たち固有の機能か、汎用な機能かを切り分けないとマージされにくい

Rationalを最適化してみた

The Mythical Creatures of Summer of Code

  • Rails Girls Summer of Codeの紹介

3日目

Writing web application in Ruby

Refinements - the Worst Feature You Ever Loved

  • Paolo Perrottaさん。Metaprogramming Rubyの著者。
  • Ruby2.1から正式に導入されたRefinementsの解説
  • モンキーパッチは便利だが影響範囲が大きい。その範囲を制限するのがRefinements。
  • あるmodule内で、拡張したいクラスをrefineの引数として定義し、usingキーワードを記述したレキシカルスコープでのみ有効となる
module A
  refine String do
    def shout
      upcase + "!"
    end
  end
end

Class C
  using A
    "hello".shout # => "HELLO!"
  end
end

C.class_eval do
  "hello".shout # => NoMethodError
end

Discussion on Thread between version 1.8.6 and 2.2.3

Plugin-based software design with Ruby and RubyGems

  • Sadayuki Furuhashiさん。トレジャーデータ社の設立者。Fluentdの作者。
  • Fluentd
    • ログの集約ツール
    • RubyGems.orgのgemを使ったプラグインベースのアーキテクチャ
  • Embulk
    • CSVなどのデータ変換・投入ツール
    • RubyGems.orgのgemとJRubyを使ったプラグインベースのアーキテクチャ
  • 課題
    • Ruby VMのバージョン衝突
  • http://www.slideshare.net/frsyuki/pluginbased-software-design-with-ruby-and-rubygems

Request and Response

  • Aaron Pattersonさん。Ruby,Rails,Rackのコミッタ。講演は日本語(すごい)
  • Railsではリクエストをパースするために数十のrack middlewareを呼び出している
  • 開発環境高速化のためにhttp2を使えるライブラリを書いてみた
  • Rack3ではAPIが多分変わる
  • http://togetter.com/li/912368

Actor, Thread and me

  • Masatoshi SEKIさん。Rubyコミッタ。
  • アクターモデルのおさらい
  • 要素としては、アクター、メッセージ、エーテル(メッセージングシステム)
  • アクターは他のアクターにメッセージを送る。自分の都合の良い時に自分宛てのメッセージを受け取る
  • アクターが返事待ちになってシステムが止まることがある
  • https://speakerdeck.com/m_seki/actor-thread-and-me-rubykaigi2015

Keynote


初めてRubyKaigiに参加したのですが、今年はRuby3リリースに向けてどのように高速化していくか、という話題が多かったです。

Rubyそのものの実装の話が多かったので、C言語未経験の自分にとっては少し難解な部分もありました。

Rubyのカンファレンスではありますが、Ruby以外の言語や設計、テスト手法などに触れることができ、幅広く勉強できたと思います。

来年は9月8日~10日に京都国際会館で開催されるようなので今から楽しみです!

記事検索