Madogiwa Blog

主に技術系の学習メモに使っていきます。

v3.0が出たのでfeedjiraがどう動いているか、なんとなくコードを読んでみた🦖

いろいろなファイル形式のパース処理を実装するときに便利なfeedjiraですが、最近メジャーバージョンがあがってv3.0.0になりました🎉

github.com

feedjiraを使ってxmlをパースする処理のサンプルは下記の通りです。

xml = HTTParty.get(url).body
feed = Feedjira.parse(xml)
feed.entries.first.title

Feedjira.parsexmlをパースしてオブジェクトを取得することが出来ます。

では実際に実装を見ていきます。

Feedjira.parse

Feedjira.parseは、引数にparse対象のxmlとParserのオブジェクト、ブロックを引数にとるメソッドです。 引数にparserが無かった場合、parser_for_xmlを呼び出しparse可能なparserを探して返します。これにより複数のparserから変換可能なparserを探索できるみたいですね(便利)※引数でもparser_for_xmlでもparserが見つからなかったらraiseします。

その後はxmlとブロックを引数にparser.parseを呼び出してParseします。

module Feedjira
  def parse(xml, parser: nil, &block)
    parser ||= parser_for_xml(xml)

    if parser.nil?
      raise NoParserAvailable, "No valid parser for XML."
    end

    parser.parse(xml, &block)
  end
  module_function :parse

  def parser_for_xml(xml)
    start_of_doc = xml.slice(0, 2000)
    Feedjira.parsers.detect { |klass| klass.able_to_parse?(start_of_doc) }
  end
  module_function :parser_for_xml

https://github.com/feedjira/feedjira/blob/master/lib/feedjira.rb#L57

ちなみにFeedjira.parsersにはデフォルトで下記が設定されていて、parsersに値を追加することで独自のparserもparser_for_xmlで取得する対象に含めることが出来ます。追加方法は、README(https://github.com/feedjira/feedjira#parsers)に記載されています。

module Feedjira
  module Configuration
    def default_parsers
      [
        Feedjira::Parser::RSSFeedBurner,
        Feedjira::Parser::GoogleDocsAtom,
        Feedjira::Parser::AtomYoutube,
        Feedjira::Parser::AtomFeedBurner,
        Feedjira::Parser::AtomGoogleAlerts,
        Feedjira::Parser::Atom,
        Feedjira::Parser::ITunesRSS,
        Feedjira::Parser::RSS,
        Feedjira::Parser::JSONFeed,
      ]
    end

https://github.com/feedjira/feedjira/blob/master/lib/feedjira/configuration.rb#L58

Parser#parse

実際にparserに使われているClassは、下記のような実装になっています。elementでRSSの各要素の定義を記載しています。elements :item, class: RSSEntryは、RSSEntryのオブジェクトの配列を持つことを表していて、RSSEntryも同様な形でfeedjira内で定義されています(https://github.com/feedjira/feedjira/blob/master/lib/feedjira/parser/rss_entry.rb)

parseメソッドmodule FeedUtilitiesに定義されているので、そちらも見てみます。

ちなみにelementとかの記法は、sax-machine(https://github.com/pauldix/sax-machine)というgemの機能によるものです。

module Feedjira
  module Parser
    # Parser for dealing with RSS feeds.
    # Source: https://cyber.harvard.edu/rss/rss.html
    class RSS
      include SAXMachine
      include FeedUtilities
      element :description
      element :image, class: RSSImage
      element :language
      element :lastBuildDate, as: :last_built
      element :link, as: :url
      element :rss, as: :version, value: :version
      element :title
      element :ttl
      elements :"atom:link", as: :hubs, value: :href, with: { rel: "hub" }
      elements :item, as: :entries, class: RSSEntry

      attr_accessor :feed_url

      def self.able_to_parse?(xml)
        (/\<rss|\<rdf/ =~ xml) && !(/feedburner/ =~ xml)
      end
    end
  end
end

https://github.com/feedjira/feedjira/blob/master/lib/feedjira/parser/rss.rb

しかし、FeedUtilitiesにも実際のparse処理はsuperで呼び出されていて、feedjira上にはありません。。。

module Feedjira
  module FeedUtilities
    module ClassMethods
      def parse(xml, &block)
        xml = strip_whitespace(xml)
        xml = preprocess(xml) if preprocess_xml
        super xml, &block
      end

https://github.com/feedjira/feedjira/blob/master/lib/feedjira/feed_utilities.rb#L13

実際の処理はsax-machine上にありそうです。

SAXMachine#parse

SAXMachine#parseの定義を見てみると、xmlon_errorとon_warningを引数に、handler_klassをSAXMachine.handlerから動的に生成して、 オブジェクトを生成しxmlを引数にhandler_klass#sax_parseを呼び出しています。 ※SAXMachine.handlerは、nokogiri、Oga、Ox等がReaderが指定されています。

module SAXMachine
  def parse(xml_input, on_error = nil, on_warning = nil)
    handler_klass = SAXMachine.const_get("SAX#{SAXMachine.handler.capitalize}Handler")

    handler = handler_klass.new(self, on_error, on_warning)
    handler.sax_parse(xml_input)

    self
  end

https://github.com/pauldix/sax-machine/blob/master/lib/sax-machine/sax_document.rb#L7

今回は、nokogiriを使ったhandlerのsax_parseの実装を見てみます。NokogiriのParserのオブジェクトを生成して、xmlを引数にparseを実行しているだけのようです。実際のparse処理はnokogiri側に定義されているようですね。

module SAXMachine
  class SAXNokogiriHandler < Nokogiri::XML::SAX::Document
    def sax_parse(xml_input)
      parser = Nokogiri::XML::SAX::Parser.new(self)
      parser.parse(xml_input) do |ctx|
        ctx.replace_entities = true
      end
    end

https://github.com/pauldix/sax-machine/blob/master/lib/sax-machine/handlers/sax_nokogiri_handler.rb

nokogiri側の実装は、下記に記載されていますが今回はfeedjiraの実装を知りたかっただけで、nokogiriのコードを読むと深みにハマりそうなので、やめておきます。。。 https://github.com/sparklemotion/nokogiri/blob/master/lib/nokogiri/xml/sax/parser.rb#L79

おわりに

今回は、feedjiraのコードを読んでみました。feedjiraはRSS等の主要なファイル形式をsax-machineというgemを使ってSAX形式で定義して、Nokogiri等のParseでparseするgemということがわかりました。 feedjiraの範囲では、sax-machineを使って主要形式のSAX形式の設定をSupportしているだけなので、中身は意外とシンプルでした。お手軽に主要な形式でparse処理を実装できるのは良い感じですね。

しかし独自のファイル定義メインで使うならfeedjiraで用意されているファイル形式が使えないので、変換のためのファイル定義を表すClassを新規に作らないといけないので、ruby標準のRSSとか他のを使っても良さそうな気もしました👀

docs.ruby-lang.org

ruby2.7の新機能メソッド参照演算子を使ってみよう

5/30にRuby 2.7.0-preview1がリリースされましたね🎉

www.ruby-lang.org

2.7の主要な新機能としては下記が記載されています👀

  • Pattern Matching
  • メソッド参照演算子 .:
  • 始値省略範囲式 ..100
  • 番号指定パラメータ @1
  • Enumerable#tally

今回は、中でもメソッド参照演算子が個人的に結構気になっているので、それについて書いていこうかなと思います✍

メソッド参照演算子とは

メソッド参照演算子とは.:という演算子でメソッドオブジェクトを取得できる演算子です。

サンプルコードは下記のような感じです。

irb(main):001:0> "0".:to_i
=> #<Method: String#to_i>

今まではClass#methodで取得することが出来ましたが、より簡単にかけるようになったという感じですね👀

メソッドオブジェクトとは

メソッドオブジェクトはメソッドから生成されたProcのようなものです。同じように#callで実行できます。

irb(main):001:0> "0".:to_i
=> #<Method: String#to_i>
irb(main):002:0> "0".:to_i.call
=> 0

Procとの違いとか詳細は、Rubyリファレンスマニュアルを参照してくださいm( )m

docs.ruby-lang.org

どう使えるのか?

こういうコレクションの各要素に対してオブジェクト以外のメソッドを適用したいときに便利そうですね👀

# 2.6
[0, 1, 2].map { |i| i + 1 }
# 2.7
[0, 1, 2].map(&1.:+)

番号指定パラメータでもいい感じにかけるかもしれないですが、メソッド参照演算子の方が(&:hoge)といった見慣れた形式でかけるので見やすそうですね。(下記のケースだとそうでもないですが。。。)

[0, 1, 2].map(&1.:+)
=> [1, 2, 3]
[0, 1, 2].map{ @1 + 1 }
=> [1, 2, 3]

実践的な例だとcurrent_userと合致するuserを取得するとか、メソッド参照演算子を使うと&を使ってスッキリかけますね👍

# 2.6
users.find { |user| corrent_user == user }
# 2.7
users.find(&corrent_user.id.:eql?)

hashの配列をもとにモデルのオブジェクトの配列を取得するとかも、いい感じにかけそうですね✨

# 2.6
attrs.map { |attr| Blog.new(attr) }
# 2.7
attrs.map(&Blog.:new)

おわりに

今回はruby 2.7の新機能のメソッド参照演算子を紹介してみました。2.7ではパターンマッチングや番号指定パラメータが話題になることが多いですが、irbJITの改善とか他にもいろいろ注目すべき点がありそうですね🙌今から2.7のリリースが楽しみです✨

参考

qiita.com

rails勉強BotのRailsのバージョンを6.0.0.rc1にアップグレードしました 🎉

みなさん、こんばんは。まどぎわです(・∀・)

運用しているrails勉強Botrailsのバージョンを5.2.3から6.0.0.rc1にアップグレードしました🎉

twitter.com

これでrails 6の予習がはかどりますね!!

今回は、rails 5.2.3からrails 6.0.0.rc1へのアップグレード手順をメモしておきます✍

手順

アップグレード対応を行ったPRはこちらです。 github.com

流れは下記のような形で実行しました👀 * rails guideのアップグレードガイドを読む * rails 設定ファイルの更新を把握 * bundle update でrailsのアップグレード * rails app:updateで設定ファイルを更新 * テストが通ることを確認

rails guideのアップグレードガイドを読む

rails guideにはすでに5.2から6へのアップグレードガイドが記載されているので、まずはそれをみました🙋

railsguides.jp

変更点として下記が記載されていましたが、今回はTwitterBotのAPIとして運用しているだけなので特に何も対応はしませんでした👀

  • 2.1 Force SSL
  • 2.2 署名済みまたは暗号化済みcookieのpurpose情報がcookie内部に埋め込まれるようになった
  • 2.3 Action Cable JavaScript APIの変更

業務運用しているアプリケーションの場合はcookieの互換性が失われる、 Action Cableまわりに大きな変更が入っているので注意ですね⚠

rails 設定ファイルの更新を把握

rails diffというサイトで5.2.36.0.0.rc1rails newで作成されるファイルのdiffを確認できるサイトがあるので、それを確認しました👀 http://railsdiff.org/5.2.3/6.0.0.rc1

sass-railsuglifierといったフロント関連のgemが消えたようですね👀 webpackerも入ったことですし、こういったものはgemで管理するのではなくpackage.jsonで管理するようになりそうですね。

-gem 'sass-rails', '~> 5.0'
-# Use Uglifier as compressor for JavaScript assets
-gem 'uglifier', '>= 1.3.0'
-# See https://github.com/rails/execjs#readme for more supported runtimes
-# gem 'mini_racer', platforms: :ruby

coffescriptが消えたのも印象深いですね☕

-# Use CoffeeScript for .coffee assets and views
-gem 'coffee-rails', '~> 4.2'

app/assets/javascripts/application.jsが初期で作成されなくなったようで。。。

 //= link_tree ../images
-//= link_directory ../javascripts .js
 //= link_directory ../stylesheets .css

これはもうJSはSproketsではなくWebpackerで管理するような流れっぽいですね。

rails db:prepareを使ってsetupを行うようになってみたいですね👀

   puts "\n== Preparing database =="
-  system! 'bin/rails db:setup'
+  system! 'bin/rails db:prepare'

rails db:prepare  Add db:prepare rake task. by robertomiranda · Pull Request #35768 · rails/rails · GitHub

マルチDB周りの設定が各environmentのファイルに追加されました👀(楽しみ)

+  # config.active_record.database_selector = { delay: 2.seconds }
+  # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
+  # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session

puma.rbでスレッドの数のMINとMAXを指定できるようになったみたいですね。

+max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
+min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
+threads min_threads_count, max_threads_count

test_helperに並列テストの設定が入ってました。

class ActiveSupport::TestCase
+  # Run tests in parallel with specified workers
+  parallelize(workers: :number_of_processors)

気になったのは、こんなところですかね👀

bundle update railsで関連gem含めてアップグレード

bundle updateはbundle update --conservative railsで行い最低限のgemだけ更新するようにしました。 最終的に実行したコマンドは下記です 💻

bundle update --conservative rails activesupport actionpack actionview activemodel railties

rails app:updateで設定ファイルを更新

そしてrails app:updateで設定ファイルを更新しました。 基本的に特に設定周りをいじってなかったので、config/application.rb以外は上書きして、config/application.rbについては下記変更のみを入れました🙋

-    config.load_defaults 5.2
+    config.load_defaults 6.0

テストが通ることを確認

最後に既存のテストを実行して全て通ることを確認しました🍏

DEPRECATION WARNING: Single arity template handlers are deprecated. Template handlers must
now accept two parameters, the view object and the source for the view object.
Change:
  >> JbuilderHandler.call(template)
To:
  >> JbuilderHandler.call(template, source)
 (called from <top (required)> at /Users/morita.jun/Documents/repo/ruby/rails_study_bot/config/environment.rb:5)
...................................................

Finished in 0.34418 seconds (files took 2.79 seconds to load)
51 examples, 0 failures

実行時に出ていたWARNINGについては、railsのmasterでは対応したPRがマージされているようなので一旦置いときましたm( )m github.com

おわりに

今回は運用していたサービスをrails 5.2.3から6.0.0.rc1にアップグレードしたので手順をメモしました! そろそろrails 6もstableが出るかもしれないですし、予行練習にちょうどよかったです🙌 (まぁ実際の業務で触るようなアプリケーションとは規模が全然違うので、こんなにすんなり行くはずないんですが・・・)

割と設定ファイルの差分を見るとアップグレードの全体感を把握できていいですね✨

参考資料

inside.pixiv.blog

Rubyで特定の条件を満たすまで待機(Sleep)する

はじめに

非同期でAPIにPOSTを投げて、データベースにレコードが作成されてからテストを始めたいみたいなときに、単純にsleep 1みたいなことをやってしまうとテスト実行環境のパフォーマンスによって落ちてしまう可能性があり、あまり好ましくないですよね💧

そういうときに役に立ちそうな特定の条件がtrueを返すまでsleepするメソッドを作ったのでメモしておきます✍

実際のコード

下記が実際のコードです、wait_conditionにブロックを渡すのそのblockがtrueを返すまで処理を待機します。一応引数にintevalを渡すと渡したブロックを実行する間隔を指定できて、limitを渡すと待機する最大の秒数を定義します。
※limitに指定した秒数を超えても最低1回はブロックが実行されてしまうので、注意してください⚠

def wait_condition(interval: 0.5, limit: 10, &condition)
  start_at = Time.now
  raise "must give block!" unless block_given?
  while !condition.call do
    sleep interval
    break puts("time out") if (Time.now - start_at) > limit
  end
end

def two_sec_afert_true
  sleep 2
  true
end

wait_condition { two_sec_afert_true }
puts "fire!"

ちなみに上記のコードを実行すると約2秒後に"fire!"がが出力されます🔥

例: DBにレコードが作成されるまで待つ

内部のAPIの呼び出しを行い、実際にデータが作成されるまで待つようなケースでは下記のようなコードで待つことができます⏳
※Mock作ったほうがいいとかそういうケースもありますが、、、

RSpec.describe Book, type: :model do
  def wait_condition(interval: 0.5, limit: 10, &condition)
    start_at = Time.now
    raise "must give block!" unless block_given?
    while !condition.call do
      sleep interval
      break puts("time out") if (Time.now - start_at) > limit
    end
  end

  before do
    # Bookを作成するAPIにPOSTを投げる処理を実行
    wait_condition { !Book.count.zero? } # Bookが1件でも作成されるまで待機
  end

  it 'Bookが作成されること' do
    expect(Book.take.title).to eq 'book title'
  end
end

おわりに

sleep 1とかを使ってしまうとたまに落ちるテストになってしまう可能性があるので、なるべく特定の条件で待つようにするか、Mock等を用意するようにしたいですね👀

参考

docs.ruby-lang.org

ブラウザのWindow、Documentのイベントを検証するツールを作ったよ💻

ブラウザ系のイベントをモバイルとかブラウザとか色々調査することがあったので、 いろいろ試せるbrowser-event-checkerというツールを作ってみました👀

github.com

browser-event-checker

使い方

使い方は簡単で、ブラウザでアクセスすると発火した各種イベントが画面に記録されていきます✍

f:id:madogiwa0124:20190608223511g:plain

これだけです😇

ですが、スマホとかだとconsole.logが見れないので各ブラウザでイベントが発火しているので調べるの辛いと思うので、 そのへんが画面に表示されるのは便利なのかなと💦

リロードしても画面はそのままになるはずなので、閉じるとかリロードとかで発火するイベントのログも見ることができます👀

ちなみにサポートしているイベントは下記の通りです🙇‍

  • Window
    • pageshow
    • load
    • focus
    • beforeunload
    • pagehide
    • blur
  • Document
    • visibilitychange

仕組み

どうやってイベントの発火を記録して表示いるのかというと、かなりシンプルでコードだと下記のような感じです👩‍💻

// ログを反映する画面要素
let consoleNode = document.getElementById('console');
// LocalStrageからログを取得
const initConsoleStack = window.localStorage.getItem('consoleStack');
// あれば変数にいれて無ければ、[]を判定
let consoleStack = initConsoleStack ? initConsoleStack.split(',') : [];

// ブラウザのLocalStrageにlogを保存
function pushLocalStrage(message) {
  return new Promise(function(resolve){
    consoleStack.push(consoleStack.length + ': ' + message);
    window.localStorage.setItem('consoleStack', consoleStack);
    resolve();
  })
}

// LocalStrageに保存されているログ([hoge, fuga, piyo...])を改行タグ区切りで画面に反映
function setConsoleStackToNode() {
  consoleNode.innerHTML = consoleStack.join('</br>');
}

// window.load時にログを出力して記録・表示
window.addEventListener('load', function () {
  const message = "browser-event-checker: window load!";
  console.log(message);
  pushLocalStrage(message).then(setConsoleStackToNode);
})

ローカルストレージを使っているので、リロードとか閉じるとかのイベントも取れる感じですね。

おわりに

わりとイベント系のブラウザ、端末のバリエーションチェックとか大変だと思うので、よかったら使ってみてください🙇‍
※多分モダンブラウザでしか動かないので、IEで動作するかは知りません😇

参考

developer.mozilla.org

developer.mozilla.org

JavaScript: Promiseについて分からなすぎたので手を動かしながら学んだことをMemo

JavaScriptのPromiseがあまりにも分からなかったので、手を動かしながらいろいろと動きを勉強したので、メモしておきます✍

Promiseの基本

Promiseとは、MDNを見てみると下記のような説明がされています。
非同期処理の実行の完了や失敗を検知することが出来るようなObjectみたいですね👀

Promise オブジェクトは非同期処理の最終的な完了処理(もしくは失敗)およびその結果の値を表現します。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise

MDNのサンプルコードが下記です。new Promise()でPromiseのオブジェクトを作成して、functionの引数のresolvethenで渡したcallback、rejectcatchで渡したcallbackが入ります。※thenが成功、catchが失敗した際に実行されます。

下記のサンプルでは、0.3秒後にcallbackに引数'foo'を渡して実行するPromiseのオブジェクトを作成して、 promise1.thenに引数の値を標準出力するfunctionをcallbackとして設定しているので、fooがコンソールに表示されます。

promise1自体を標準出力するとPromiseのオブジェクトが返却されます。

var promise1 = new Promise(function(resolve, reject) {
  setTimeout(function() {
    resolve('foo');
  }, 300);
});

promise1.then(function(value) {
  console.log(value);
  // expected output: "foo"
});

console.log(promise1);
// expected output: [object Promise]

いろいろ動かして試してみる。

まずは、検証用に下記のようなものを用意しました。
sleepは、timeで指定した時間後にcallbackを実行する遅延処理です。その他については普通に標準出力する処理です。

function sleep(time, callback) {
  setTimeout(() => { callback(null) }, time);
}
const exec_1 = () => { console.log('async 1 execute!!') }
const exec_2 = () => { console.log('async 2 execute!!') }
const exec_3 = () => { console.log('async 3 execute!!') }

下記のような処理を実行するとresultに書いたような結果になります。sleepで1秒まっているので、 exec_2よりもexec_3が先に実行されてしまっていますね。

exec_1()
sleep(1000, exec_2)
exec_3()

/* result
async 1 execute!!
async 3 execute!!
async 2 execute!!
*/

これをPromiseを使って、exec_2、exec_3の順番で実行されるようにしていこうと思います。

exec_2のあとにexec_3を実行するために遅延処理をPromiseにする。

まずは、sleepをPromiseのオブジェクトを返却するようにして、完了のタイミングで処理を実行出来るようにします。

function delay(time) {
  return new Promise((resolve) => {
    sleep(time, resolve)
  })
}

👇delaythenを使って処理修正したのが下記です。 delay(1000).then(exec_2)で1秒待ったあとにexec_2が実行されます。 thenの返り値もまたPromiseのオブジェクトになるので、then(exec_2).then(exec_3)によってexec_2実行後にexec_3が実行されます。

そのため結果は、resultのようになります。

exec_1()
delay(1000).then(exec_2).then(exec_3)
/* result
async 1 execute!!
// 1 sec
async 2 execute!!
async 3 execute!!
*/

thenのなかで更にPromiseを作成して各メソッドを1秒間隔で実行する

こうなってくると1秒間隔で各メソッドを実行したくなるわけですが、単純に下記のようにすると上手くいかないわけですよ。。

delay(1000).then().then(delay(1000).then(exec_3))

/* result
async 1 execute!!
// 2 sec
async 2 execute!!
async 3 execute!!
*/

なぜ上手く行かないかというと下記のような処理になってしまっていたわけですね。。。

  1. exec1が実行 -> async 1 execute!!が表示
  2. 1秒待つ
  3. exec2が実行予約(1秒後) -> delay(1000).then(exec_2)が実行
  4. exec3が実行予約(1秒後) -> delay(1000).then(exec_3)が実行
  5. 1秒待つ(exec_2、exec_3のdelayはほぼ同時に実行されるため)
  6. exec2が実行 -> async 2 execute!!が表示
  7. exec3が実行 -> async 2 execute!!が表示

上記を考慮して修正したコードが下記です。

exec_1()
delay(1000).then(
  () => {
    return new Promise(
      resolve => { exec_2(); delay(1000).then(resolve) }
    )
  }
).then(exec_3)

/* result
async 1 execute!!
// 1 sec
async 2 execute!!
// 1 sec
async 3 execute!!
*/
  1. exec1が実行 -> async 1 execute!!が表示
  2. 1sec待つ -> delay(1000)が実行
  3. exec_2と1秒後にcallbackを実行するPromiseが設定される -> delay(1000).then(..)が実行される
  4. thenが実行されてexec_2(); delay(1000).then(exec_3)が実行されるため、exec_2が実行されたあと1秒待ってexec_3が実行される。 -> exec_2(); delay(1000).then(resolve)が実行される

これでresultのように1秒間隔で実行される処理が実現出来ました🙌

おわりに

今回はJavaScriptのPromiseが分からなすぎたので、いろいろ実行しながら学んでみました。
やはりドキュメントを読むだけでなくて、手を動かすといろいろと理解が深まりますね。

それでは👋

参考

developer.mozilla.org

qiita.com

railsじゃなくても`activesupport`を使うといろいろと幸せになれるかもしれない話

みなさん、こんにちは。まどぎわです(・∀・)

みなさん、railsに入っているactivesupportというgemをご存知でしょうか?👀

blank?とかpresent?とかinquiryとかpluralizeとか、いい感じのメソッドが使えるようになるやつです🙌

railsguides.jp

このactivesupportは、個別にgemとして公開されていて、別にrailsじゃなくても使えるんですよ!

rubygems.org

使い方は普通のgemと一緒でGemfileに下記を追記して、bundle installを実行して、、、

gem 'activesupport'

使いたい部分で、requireしてあげればOKです🙆‍♂️

# 'active_support/all'だと全ての機能が有効になる
require 'active_support/all'

⚠gemはactivesupportだけど、requireするときはactive_supportにする点に注意です。

これで、blank?等の便利メソッドが有効になります🙌

require 'active_support/all'

''.blank?
=> true
'a'.blank?
=> false

gemとかツールとか、ちょっとしたAPIとか、railsを使うまでも無いけど、このへんの便利メソッドが使いたいという場合は、activesupportだけ使うと良さそうですね!💡