不格好エンジニア (引っ越しました)

wordpress.comから引っ越しました。

rbenvを流し読み

注)誤ったことを書いているかもしれません。

動機

rubyで仕事してると職場でも頻繁に使うものなので、中身を理解したくなった。

version

1.1.1

ドキュメントを読んでみる

github.com

冒頭の重要そうなところをピックアップしてみます

No headaches running apps on different versions of Ruby. Just Works™ from the command line and with app servers like Pow. Override the Ruby version anytime: just set an environment variable.

異なるruby versionで動くアプリに頭痛で悩まされることがない。Powのようにコマンドラインで動かすだけ。いつでもRuby versionを上書きできる。環境変数でsetするだけだ。

Rock-solid in production. Your application's executables are its interface with ops. With rbenv and Bundler binstubs you'll never again need to cd in a cron job or Chef recipe to ensure you've selected the right runtime. The Ruby version dependency lives in one place—your app—so upgrades and rollbacks are atomic, even when you switch versions.

productionで頑丈だ。rbenvとBundler binstubsにより、cron jobの中でcdしたり、chep recipeで正しいruntimeを選択していることを保証する必要はない。Ruby versionの依存はただ一箇所、あなたのアプリに存在しているので、upgradesやrollbackはatomicはversionを切り替えるときでもatomicに行える。

One thing well. rbenv is concerned solely with switching Ruby versions. It's simple and predictable. A rich plugin ecosystem lets you tailor it to suit your needs. Compile your own Ruby versions, or use the ruby-build plugin to automate the process. Specify per-application environment variables with rbenv-vars. See more plugins on the wiki.

ecosystemがしっかりしている、ということが書いてある。

ざっくり眺める

.
├── bin
├── completions
├── libexec
├── rbenv.d
│    └── exec
│         └── gem-rehash
├── src
└── test
    └── libexec
  • bin/ メインのロジックはここに入ってるようです。
  • libexec rbenv localなどに対応するファイル群。

bin/rbenv

  command_path="$(command -v "rbenv-$command" || true)"
  if [ -z "$command_path" ]; then
    if [ "$command" == "shell" ]; then
      abort "shell integration not enabled. Run \`rbenv init' for instructions."
    else
      abort "no such command \`$command'"
    fi
  fi

rbenv-local, rbenv-init などの呼び出しはここで行っているようです。メインのロジックがここに入っているようですね。

rbenv initを実行したときに内部で起きていることを理解すれば、大体あとはわかる気がしたので、そのあたりを中心に追ってみると良さそうです。

いきなり動作を把握するのは難しそうなので、rbenv-initのテストを読みにいきます。

@test "adds shims to PATH" {
  export PATH="${BATS_TEST_DIRNAME}/../libexec:/usr/bin:/bin:/usr/local/bin"
  run rbenv-init - bash
  assert_success
  assert_line 0 'export PATH="'${RBENV_ROOT}'/shims:${PATH}"'
}

rbenv initでshimsをPATHに追加します。

@test "outputs sh-compatible syntax" {
  run rbenv-init - bash
  assert_success
  assert_line '  case "$command" in'

  run rbenv-init - zsh
  assert_success
  assert_line '  case "$command" in'
}

rbenv initでshellに互換性のある文字列を出力します。

@test "creates shims and versions directories" {
  assert [ ! -d "${RBENV_ROOT}/shims" ]
  assert [ ! -d "${RBENV_ROOT}/versions" ]
  run rbenv-init -
  assert_success
  assert [ -d "${RBENV_ROOT}/shims" ]
  assert [ -d "${RBENV_ROOT}/versions" ]
}

rbenv initでshimsとversionsディレクトリを作成します。

rbenv-init

{ echo "# Load rbenv automatically by appending"
    echo "# the following to ${profile}:"
    echo
    case "$shell" in
    fish )
      echo 'status --is-interactive; and source (rbenv init -|psub)'
      ;;
    * )
      echo 'eval "$(rbenv init -)"'
      ;;
    esac
    echo
  } >&2

表示される指示をみると、.bashrc, .bash_profile なりに eval "$(rbenv init -)" を書いておけ、とあります。 つまり、rbenv init - の結果が文字列(シェルスクリプト)として返されるので、これをevalすることによって、PATHを通したりしてるみたいですね。

参考

RubyGemコードリーディングのすすめ

rbenv + ruby-build はどうやって動いているのか - takatoshiono's blog

rbenvコードリーディング

rbenvの仕組み | Web Memorandum

次に読みたい

Ruby

wicked_pdf, request_store, bullet, wkhtmltopdf, sidekiq, active_support, webrick, draper, jbuilder, rails/active_job, rails/spring, capistrano, omniauth, committee etc..

iOS

RxSwift, AFNetworkinig, SDWebImage, Alamofire, MBProgressHUD, Masony, SwiftyJSON, MJRefresh, CocoaLumberjack, Realm, SsnapKit, Kingfisher

Rails::Railtieを流し読み

Rails::Railtieを流し読み

動機

request_storeを読んでいる途中で、結局途中でrailtiesにブチあたり、この辺ちゃんと読んだことないのが気持ち悪いな、という気持ちでコードリーディングを始めてみました。

version

2.3.0

ドキュメントを読んでみる

Ruby on Rails API

個人的に重要だと思った点をピックアップしてみます。

core of the Rails framework and provides several hooks to extend Rails and/or modify the initialization process.

rails frameworkのコアになっており、初期化プロセスを拡張するための仕組みを提供している。

Every major component of Rails (Action Mailer, Action Controller, Active Record, etc.) implements a railtie. Each of them is responsible for their own initialization.

主要なcomponentはrailtieを実装している。なるほど、Gemを読んでいく上ではこの辺の仕組みをわかってないとしんどそうです。

To add an initialization step to the Rails boot process from your railtie, just define the initialization code with the initializer macro: If specified, the block can also receive the application object, in case you need to access some application-specific configuration, like middleware:

自作のrailtieをRails の初期化プロセスに組み込むには、initialization codeを書くだけです、と。blockはapplication objectを受け取ることができて、application固有の設定にアクセスできます。

request_storeのソースコードをみてみると、こうなっております。

module RequestStore
  class Railtie < ::Rails::Railtie
    initializer "request_store.insert_middleware" do |app|
      if ActionDispatch.const_defined? :RequestId
        app.config.middleware.insert_after ActionDispatch::RequestId, RequestStore::Middleware
      else
        app.config.middleware.insert_after Rack::MethodOverride, RequestStore::Middleware
      end

      if ActiveSupport.const_defined?(:Reloader) && ActiveSupport::Reloader.respond_to?(:to_complete)
        ActiveSupport::Reloader.to_complete do
          RequestStore.clear!
        end
      elsif ActionDispatch.const_defined?(:Reloader) && ActionDispatch::Reloader.respond_to?(:to_cleanup)
        ActionDispatch::Reloader.to_cleanup do
          RequestStore.clear!
        end
      end
    end
  end
end

bulletはこんな感じです。

  if defined? Rails::Railtie
    class BulletRailtie < Rails::Railtie
      initializer 'bullet.configure_rails_initialization' do |app|
        app.middleware.use Bullet::Rack
      end
    end
  end

ざっくり眺める

Rails::Railtieはabstract class

直接インスタンス化はできないことがわかります。

rails/railtie.rb at master · rails/rails · GitHub

module Rails
  class Railtie
    # ...
    def initialize #:nodoc:
      if self.class.abstract_railtie?
        raise "#{self.class.name} is abstract, you cannot instantiate it directly."
      end
    end
  end
end  

主要なメソッドは、rake_tasks, console, runner, generatorsかな。。 ここで格納されたblockが、後に #each_registered_block(type, &block) の中で使用されるようです。

module Rails
  class Railtie
    # ...

      def rake_tasks(&blk)
        register_block_for(:rake_tasks, &blk)
      end

      def console(&blk)
        register_block_for(:load_console, &blk)
      end

      def runner(&blk)
        register_block_for(:runner, &blk)
      end

      def generators(&blk)
        register_block_for(:generators, &blk)
      end
      
      private
        # receives an instance variable identifier, set the variable value if is
        # blank and append given block to value, which will be used later in
        # `#each_registered_block(type, &block)`
        def register_block_for(type, &blk)
          var_name = "@#{type}"
          blocks = instance_variable_defined?(var_name) ? instance_variable_get(var_name) : instance_variable_set(var_name, [])
          blocks << blk if blk
          blocks
        end
        

処理の流れを追ってみる

エントリポイント

lib/rails/initializable.rb

initializerメソッドはRails::Initializableモジュールに定義されており、ここでInitializerインスタンスが initializersに格納されています。

module Rails
  module Initializable
    module ClassMethods
...
      def initializers
        @initializers ||= Collection.new
      end
    
      ..
      def initializer(name, opts = {}, &blk)
        raise ArgumentError, "A block must be passed when defining an initializer" unless blk
        opts[:after] ||= initializers.last.name unless initializers.empty? || initializers.find { |i| i.name == opts[:before] }
        initializers << Initializer.new(name, nil, opts, &blk)
      end
    end
  end
end  

lib/rails/initializable.rb

Rails::Applications#initialize!が呼ばれると、 initializersはRails::Initializable#run_initializersの中でrunされていきます。 tsortが何なのかは、Ruby's TSort explained を読めばわかりそうなのですが、いったんここでは先に進みます。

config/environment.rb

# Load the Rails application.
require_relative 'application'

# Initialize the Rails application.
Rails.application.initialize!

lib/rails/application.rb

module Rails
  class Application < Engine
...
    # Initialize the application passing the given group. By default, the
    # group is :default
    def initialize!(group = :default) #:nodoc:
      raise "Application has been already initialized." if @initialized
      run_initializers(group, self)
      @initialized = true
      self
    end
  end
end  

lib/rails/initializable.rb

module Rails
  module Initializable
...
    def run_initializers(group = :default, *args)
      return if instance_variable_defined?(:@ran)
      initializers.tsort_each do |initializer|
        initializer.run(*args) if initializer.belongs_to?(group)
      end
      @ran = true
    end
...

initializersはRails::Application#initializersとRails::Initializable#initializers があるが、モジュールのメソッドよりクラスで定義されているメソッドが優先され、Rails::Application#initializers が実行されます(実際に動かして確認しました)。

lib/rails/application.rb

module Rails
  class Application < Engine
...
   def initializers #:nodoc:
      Bootstrap.initializers_for(self) +
      railties_initializers(super) +
      Finisher.initializers_for(self)
    end
  end  
end  

Bootstrap initializersは下記の通り、applicationの準備をするものが返されます。 [:load_environment_hook, :load_active_support, :set_eager_load, :initialize_logger, :initialize_cache, :initialize_dependency_mechanism, :bootstrap_hook, :set_secrets_root]

railties initializersは Rails::Application 自身に定義されたものが返されます。

(byebug) initializers.map(&:name) [:set_load_path, :set_autoload_paths, :add_routing_paths, :add_locales, :add_view_paths, :load_environment_config, :prepend_helpers_path, :load_config_initializers, :engines_blank_point, :append_assets_path]

#railties_initializersの中身を少し掘ってみていきます。#ordered_railtiesは何をしているのでしょうか?

    def railties_initializers(current) #:nodoc:
      initializers = []
      ordered_railties.reverse.flatten.each do |r|
        if r == self
          initializers += current
        else
          initializers += r.initializers
        end
      end
      initializers
    end
    
    # Returns the ordered railties for this application considering railties_order.
    def ordered_railties #:nodoc:
      @ordered_railties ||= begin
        order = config.railties_order.map do |railtie|
          if railtie == :main_app
            self
          elsif railtie.respond_to?(:instance)
            railtie.instance
          else
            railtie
          end
        end

        all = (railties - order)
        all.push(self)   unless (all + order).include?(self)
        order.push(:all) unless order.include?(:all)

        index = order.index(:all)
        order[index] = all
        order
      end
    end
    

lib/rails/initializable.rb

ここで、定義されたブロックが実行されてますね。

class Initializer
  def run(*args)
    @context.instance_exec(*args, &block)
  end
end

次に読みたい

wicked_pdf, request_store, bullet, wkhtmltopdf, sidekiq, active_support, rbenv, webrick, draper, jbuilder, rails/active_job, rails/spring, capistrano, omniauth, committee etc..

参考

Railtieのinitializerが読み込まれる仕組み

Introduction to Railties

The Rails Initialization Process — Ruby on Rails Guides

Configure your gem the Rails way with Railtie

ブログに技術書の内容を丸写しする問題点と、オリジナルなコンテンツを書くためのアイデア - give IT a try

RubyGemコードリーディングのすすめ

#299 Rails Initialization Walkthrough (pro) - RailsCasts

vendor/bundle/ruby/2.3.0/gems/rails-4.2.5/guides/source/initialization.md · master · ypleung / cmpt276ass2 · GitLab

Ruby's TSort explained

[Ruby入門] 11. クラスを拡張する① モジュールのお話

プラットフォームの教科書 を読んだ

プラットフォームの教科書 を読んだ。

この本を読んだ自分の背景というと、普段はスタートアップ企業でグロースハックやWebアプリケーション、モバイルアプリの開発などに従事している。

プラットフォームビジネスの勝敗を分ける要因は当然、技術的な要因だけではない。どうやってWinner Takes Allの状況を突き崩していくか。"次"の打ち手を考えるヒントを得られることを期待して購入した。

要約

Winner Takes Allに見えるプラットフォーム・ビジネスにおいても、ひとり勝ちの状況はある日、突然くつがえされることがある。なぜ、それが起きるのかを近年の事例(任天堂 vs スマホゲーム、スマホシフトをテコにしたLINEの成長)をもとに解説している。

面白かった章とその理由

02 レイヤー構造化

プラットフォームのコンセプトを戦略にうまく取り入れた事例として、仮面ライダーのベルトや妖怪ウォッチのメダルが紹介されている。仮面ライダーの変身用ベルトの機能の変遷から、ビジネスモデルの進化を考察していて、非常に興味深い。

13 5つの対抗策

Winner Takes Allの進行を妨害する5つの戦略について解説している。戦略としては以下の通り。

  • 収益モデルの破壊と拡張
  • プラットフォーム包囲
  • プラットフォーム間橋渡し
  • プラットフォーム互換
  • プラットフォーム連携

ほとんどの事業者は、Winner ではなく、次のWinnerになりたい側(追う側)であるはずなので、興味深く読める方が多いはず。

理解できなかったところ

なぜ産業がレイヤー構造化するのか、疑問が生じたので調査してみた。下記のスライドがわかりやすい。

バリューチェーン戦略論からレイヤー戦略論へ

インターネットの浸透により、売り手と書い手が同一空間/同一時間に取引を行う必要がなくなったから、という事らしい。

仕事に活かせそうな知識、活かせそうな状況と活かし方

自社の状況に当てはめると、如何にリソースを集中投下するか、選択と集中について、リソースの潤沢な大企業よりもシビアに判断していく必要がある。

その視点でみると、特に使えそうな考え方は以下の3点。

  • マチュアエコノミー(UberAirbnbの事例のように、アマチュアを事業者として参画させ、ゲームのルールを変える。)

  • 信頼性と信用の確保(機能の豊富さを売りにするのではなく、信頼と信用を担保する仕組みを徹底的に磨き込む。)

  • 収益モデルの破壊と拡張(既存プレイヤーの収益源を"無料化"によって破壊する。)

まだよく理解しきれていない所も多いので、時々手に取って読み返したい。

コーディング面接対策サイトCodilityの練習問題を解いてみた(MinMaxDivision)

問題

配列AをK個に分割し、それぞれ分割したブロックの和を求める。 すべての配列の要素は5以下。

A = [2, 1, 5, 1, 2, 2, 2], K = 3, M = 5 なら

[2, 1, 5, 1, 2, 2, 2], , なら、分割したブロックの和の最大値 15; [2], [1, 5, 1, 2], [2, 2] なら、分割したブロックの和の最大値 9; [2, 1, 5], [], [1, 2, 2, 2] なら、分割したブロックの和の最大値 8; [2, 1], [5, 1], [2, 2, 2] なら、分割したブロックの和の最大値 6.

それぞれのパターンで算出した分割したブロックの和の最大値の中で、最小値を求めることがgoal。

Codility

回答

K == 1 なら各要素の総和(high)に K == 配列の長さなら、各要素の中で最大のもの(low)が答えに それ以外の場合なら、lowとhighの間に答えがあるはずなので、BinarySearchで、ちょっとずつ計算する

#A = [2,1,5,1,2,2,2]

def solution(k, m, a)
  if k == 1
    return a.inject(0) {|sum, x| sum + x }
  elsif k == a.length
    return a.max
  else
    # use binary search
    low = a.max
    high = a.inject(0) {|sum, x| sum + x }

    while (low <= high) do
      mid = (low + high) / 2
      p "mid = #{mid}"
      if check(a, k, mid)
        high = mid - 1
      else
        low = mid + 1
      end
    end
    return low
  end
end

def check(a, k, mid)
  sum = 0
  blocks = 0

  a.each {|e|
    if ( (sum + e) > mid )
      sum = e
      blocks += 1
      return false if blocks >= k
    else
      sum += e
    end
    #puts "sum = #{sum}, blocks = #{blocks}, k = #{k}, mid = #{mid} "
  }
  return true
end

p solution(3,5,A)

コーディング面接対策サイトCodilityの練習問題を解いてみた(TapeEquilibrium)

問題

テーブを前半と後半に切断して差分を比較し、最も少ない差分を返す

Test results - Codility

回答

headにテープの前半部分を、tailにテープの後半部分を格納する。 インデックスを動かすごとにdiffを再計算する。

def solution(a)
  sum = a.reduce(:+)

  head = a[0]
  tail = sum - a[0]
  min_diff = 10000

  for i in 1...a.length
    diff = (head-tail).abs
    min_diff = [min_diff, diff].min
    head += a[i]
    tail -= a[i]
  end
  min_diff
end
# debug

arr = [3,1,2,4,3]
p solution(arr)

arr1 = [-1, 1]
p solution(arr1)

その他

rubyのreduce知らんかった。。。

コーディング面接対策サイトCodilityの練習問題を解いてみた(FrogJmp)

問題

カエルが目標に到達するためには、最低何回、ジャンプする必要があるか

Codility

回答

def solution(x, y, d)
  ((y - x).to_f / d).ceil
end

その他

割る前にto_f しないと、小数が切り捨てられてしまうことに注意。 パフォーマンスを考えると、ループさせない方が良い。

1-TimeComplexity

線形代数 ベクトルと固有値 (大学入門ドリル)の読書メモ

読んだ本

Kobito.YtQg7o.png

Amazon.co.jp: 線形代数 ベクトルと固有値 (大学入門ドリル): 丸井洋子: 本

学んだことと参考リンク

ベクトルとその性質を知ろう

ベクトルどうしの和や定数倍を定義できて、いくつかの計算規則が成り立つことを知る。

行列代数これだけ

ベクトルどうしの関係を判定しよう

ベクトルの線形従属と線形独立について,幾何学的なイメージを捉える。 f:id:tjnet555:20160510011429j:plain

1.2 ベクトル空間と基底

ベクトルに内積を定義しよう

内積を定義すると、色々なものを美しく簡潔に表現できる

ベクトル3

固有値を求めて行列を対角化しよう

固有値の図形的な意味

f:id:tjnet555:20160510005721j:plain

対角化することのメリット

行列のn乗が綺麗に求められる!

linear_lecture.pdf

感想