Rails routing(resources/resourceとshallow)

Rails のroutingにいくつかよくわかってないメソッド?optionがあったので、この機会に調べてみた。

resources/resource

sampleとして以下のようなroutes.rbを作成

Rails.application.routes.draw do
  resources :books
  resource :person
end
rake routes

すると以下のルーティングが定義された。

                   Prefix Verb   URI Pattern                                                                              Controller#Action
                    books GET    /books(.:format)                                                                         books#index
                          POST   /books(.:format)                                                                         books#create
                 new_book GET    /books/new(.:format)                                                                     books#new
                edit_book GET    /books/:id/edit(.:format)                                                                books#edit
                     book GET    /books/:id(.:format)                                                                     books#show
                          PATCH  /books/:id(.:format)                                                                     books#update
                          PUT    /books/:id(.:format)                                                                     books#update
                          DELETE /books/:id(.:format)                                                                     books#destroy
               new_person GET    /person/new(.:format)                                                                    people#new
              edit_person GET    /person/edit(.:format)                                                                   people#edit
                   person GET    /person(.:format)                                                                        people#show
                          PATCH  /person(.:format)                                                                        people#update
                          PUT    /person(.:format)                                                                        people#update
                          DELETE /person(.:format)                                                                        people#destroy
                          POST   /person(.:format)                                                                        people#create

resources

resources :booksの場合は

                    books GET    /books(.:format)                                                                         books#index
                          POST   /books(.:format)                                                                         books#create
                 new_book GET    /books/new(.:format)                                                                     books#new
                edit_book GET    /books/:id/edit(.:format)                                                                books#edit
                     book GET    /books/:id(.:format)                                                                     books#show
                          PATCH  /books/:id(.:format)                                                                     books#update
                          PUT    /books/:id(.:format)                                                                     books#update
                          DELETE /books/:id(.:format)                                                                     books#destroy

resource

resource :personの場合は

               new_person GET    /person/new(.:format)                                                                    people#new
              edit_person GET    /person/edit(.:format)                                                                   people#edit
                   person GET    /person(.:format)                                                                        people#show
                          PATCH  /person(.:format)                                                                        people#update
                          PUT    /person(.:format)                                                                        people#update
                          DELETE /person(.:format)                                                                        people#destroy
                          POST   /person(.:format)                                                                        people#create

controller#indexがないだけの違いだった。
resourcesは複数あるリソースを対象としているのに対して、resourceは単数を想定しているために indexがないのだろう。

shallow

ルーティングはネストさせることができます。
例えばこんな感じです。

  resources :author do
    resources :books
  end
             author_books GET    /author/:author_id/books(.:format)                                                       books#index
                          POST   /author/:author_id/books(.:format)                                                       books#create
          new_author_book GET    /author/:author_id/books/new(.:format)                                                   books#new
         edit_author_book GET    /author/:author_id/books/:id/edit(.:format)                                              books#edit
              author_book GET    /author/:author_id/books/:id(.:format)                                                   books#show
                          PATCH  /author/:author_id/books/:id(.:format)                                                   books#update
                          PUT    /author/:author_id/books/:id(.:format)                                                   books#update
                          DELETE /author/:author_id/books/:id(.:format)                                                   books#destroy
             author_index GET    /author(.:format)                                                                        author#index
                          POST   /author(.:format)                                                                        author#create
               new_author GET    /author/new(.:format)                                                                    author#new
              edit_author GET    /author/:id/edit(.:format)                                                               author#edit
                   author GET    /author/:id(.:format)                                                                    author#show
                          PATCH  /author/:id(.:format)                                                                    author#update
                          PUT    /author/:id(.:format)                                                                    author#update
                          DELETE /author/:id(.:format)                                                                    author#destroy

こうするとネストがかなり深くて、複雑な部分があります。
このネストを少しでも解消するために、コレクション(idを持たない要素, index/new/create)だけを親要素の下に作り、メンバー(idを必要とする要素、 destroy/update/show)をネストに含めないという方法を取ることができます。

  resources :author do
    resources :books, shallow: true
  end
             author_books GET    /author/:author_id/books(.:format)                                                       books#index
                          POST   /author/:author_id/books(.:format)                                                       books#create
          new_author_book GET    /author/:author_id/books/new(.:format)                                                   books#new
                edit_book GET    /books/:id/edit(.:format)                                                                books#edit
                     book GET    /books/:id(.:format)                                                                     books#show
                          PATCH  /books/:id(.:format)                                                                     books#update
                          PUT    /books/:id(.:format)                                                                     books#update
                          DELETE /books/:id(.:format)                                                                     books#destroy
             author_index GET    /author(.:format)                                                                        author#index
                          POST   /author(.:format)                                                                        author#create
               new_author GET    /author/new(.:format)                                                                    author#new
              edit_author GET    /author/:id/edit(.:format)                                                               author#edit
                   author GET    /author/:id(.:format)                                                                    author#show
                          PATCH  /author/:id(.:format)                                                                    author#update
                          PUT    /author/:id(.:format)                                                                    author#update
                          DELETE /author/:id(.:format)                                                                    author#destroy

binding.pryで条件付きでデバッグする

what

Railsで binding.pryを使って、条件付きで処理を止める方法を知りたい。

Android Studioだとこれがデフォルトでできました。
例えば、何回もアクセスされるエンドポイントで、特定の条件のときにだけエラーが起きている場合、関係ないアクセスのときにも処理が止まってしまうとデバッグするのが面倒です。
(ある料理レシピサイトがあったとして、リストの3ページ目にアクセスしたときだけエラーが起きる場合。1ページめを表示したときにも処理が止まると面倒ですよね。)

how

まずは、 gemをインストール。

gem install pry-byebug

デバッグしたいファイルに以下を追加

require 'pry'

処理を止めたい箇所に以下を記述

binding.pry

さらに、処理を止めたい条件を以下のように記述

binding.pry if page == 3

これで page == 3のときにだけ処理が止まるようになりました。

おまけ

処理を止めた時に使えるコマンド

  • next
    • 次の行を実行(メソッドは実行される)
  • step
    • 次の行を実行、メソッドなら中に入る
  • continue
    • プログラムを実行し、pryを終了
  • break
    • break 数字=> 数字の行にbreak pointを貼る

aliasを貼る

以下を ~/.pryrcに貼り付ける

if defined?(PryByebug)
  Pry.commands.alias_command 'c', 'continue'
  Pry.commands.alias_command 's', 'step'
  Pry.commands.alias_command 'n', 'next'
  Pry.commands.alias_command 'f', 'finish'
end

直前のコマンドを Enterキーで繰り返す

以下を ~/.pryrcに貼り付ける

Pry::Commands.command /^$/, "repeat last command" do
  _pry_.run_command Pry.history.to_a.last
end

Javaの値渡しと参照渡し

基本型

Javaのメソッドの引数に与えられる値は基本的に値渡しです。
値渡しというのは、引数に与えられた値がコピーされるということです。
なので、メソッド内で引数に対して変更が加えられても、それは引数のコピーに対して変更を加えていることになります。

そのため、以下のような動きになることは理解できると思います。

int count = 8;
plus(count);
Log.d(count);

void plus(int value) {
    int result = value * 2;
    Log.d(result);
}
after applying plus 16
outside of method plus 8

参照型

参照型を引数に渡すと、参照値がコピーされます。

以下のコードで言えば、左辺が参照値で右辺が実際の値です。
左辺の参照値というのは例えていうと、右辺の実際の値が格納されているメモリの番地のことです。

List<String> list = new ArrayList<>();

東京都千代田区千代田1−1には皇居があります。
東京都千代田区千代田1−1が参照値で、皇居が実際の値みたいな関係です。

上記を踏まえて、以下のプログラムを考えてみたいと思います。

List<String> japanese = new ArrayList<>();
japanese.add("すぎた");
plusElementJ(japanese);
Log.d(japanese.toString());

void plusElementJ(List<String> list) {
    list.add("おはよう");
    Log.d(list.toString());
}
[すぎた, おはよう]
[すぎた, おはよう]

上記のプログラムでは、一二行目で 東京都千代田区千代田1−1に皇居があることを宣言しています。
メソッドに東京都千代田区千代田1−1の住所を渡します。
メソッド内では、受け取った住所に対して、増築を行いました。
すると、メソッドをでても、東京都千代田区千代田1−1には皇居とそれを増築した結果があるのは納得できましたね。

Stringも参照型だけど。。。

String name = "すぎた";
concatenate(name);
Log.d(name);

void concatenate(String name) {
    name = "おはよう、" + name;
    Log.d(name);
}
after applying concatenate おはよう、すぎた
outside of method concatenate すぎた

Stringは参照型ではありますが、immutableです。つまり変更不可能です。
そのため、この処理は内部的には以下のように新しいStringオブジェクトが作られているんじゃないでしょうか?

name = new String("おはよう、"  + name)

そのため、以下のようなことが起こっていると思います。

  1. コピーされている参照値 nameには新しい参照値が代入される
  2. nameが新しく参照している参照値に新しくオブジェクトが作成される

参考

もう参照渡しとは言わせない

法人税、社会保険料、所得税、役員報酬の計算スクリプトを作成してみた。

法人を作ったので、税金計算のスクリプトを作ってみました。

収入と報酬と税金と

法人の収入にかかってくる費用は以下の通り

  1. 社会保険料(健康保険、厚生年金保険、介護保険、雇用保険、労災保険)
    1. 健康保険は支払う給与の9%を給与所得者と半分ずつ負担
    2. 厚生年金保険は支払う給与の18.3%を給与所得者と半分ずつ負担
    3. 雇用保険は支払う給与の9%を法人が全額負担
    4. 労災保険は支払う給与の2.5%を法人が全額負担
    5. 介護保険は、自分の場合は該当しなかったので未調査
  2. 収入-(上記の保険料 + 役員報酬 + 給料) * 37.4%

個人の収入にかかってくる費用は以下の通り

  1. 住民税
  2. 所得税
  3. 社会保険料(健康保険、厚生年金保険)

住民税の計算式

(所得 – 控除※) * 10 / 100
※33万円

所得税の計算式

スクリーンショット 2019-05-01 16.56.10.png

課税所得によって決まってくる。

国税庁HPより

課税所得の決まり方

課税所得 = 所得 – 社会保険料 – 基礎控除(※1) – 所得控除(※2)
(※1)38万円
(※2)の決まり方

給与収入額所得控除額
360万円以下給与収入 * 30 / 100 + 18万円
660万円以下給与収入 * 20 / 100 + 54万円
1000万円以下給与収入 * 10 / 100 + 120万円
それ以上220万円

スクリプトにしてみた。

  • 入力
    • 収入額と、役員数、役員一人あたりの報酬額を指定
    • calc(収入額, 役員数, 役員一人あたり報酬額)
  • 出力
    • 指定された役員一人あたりの報酬額を10万円ずつ繰り上げて、それぞれの場合の 総資産, 法人の収入, 役員一人あたりの報酬額, 役員の税引き後収入を出力する。
    • カンマ区切りの値が以下のように出力されます。
revenue is 2000
総資産,法人の収入,個人の額面収入,個人の税引き後収入
1262.70608,406.38668000000007,360,285.4398
1261.65186,383.54080999999996,370,292.70368333333334
1258.51564,359.44294,380,299.6909
1256.8354199999999,335.97106999999994,390,306.95478333333335
1253.6992,311.8732,400,313.942
1252.64498,289.02733,410,321.2058833333333
1250.96476,265.55546000000004,420,328.46976666666666
1247.82854,241.45759000000004,430,335.4569833333333
1246.7743200000002,218.61172000000002,440,342.72086666666667
1245.0940999999998,195.13985000000002,450,349.98474999999996
1241.95788,171.04198000000002,460,356.9719666666667
1240.6638100000002,148.19611,470,364.15590000000003
1238.72104,124.72424000000001,480,371.33226666666667
1235.33227,100.62637000000001,490,378.23530000000005
1233.3895000000002,77.1545,500,385.4116666666667
def calc(revenue, manager_num, compensation)
  return if revenue < manager_num * compensation

  total_compensation = manager_num * compensation
  kenpo = total_compensation * 9 / 100
  nenkin = total_compensation * 18.3 / 100
  koyou_hoken = total_compensation * 9 / 100
  rousai = total_compensation * 2.5 / 100

  tax_for_comp = (kenpo / 2) + (nenkin / 2) + koyou_hoken + rousai
  tax_for_individual = (kenpo / 2) + (nenkin / 2)

  rev_for_comp = revenue - total_compensation - tax_for_comp
  tax_for_comp = rev_for_comp * 37.4 / 100
  rev_for_comp -= tax_for_comp

  compensation_after_social_tax = compensation - (tax_for_individual / manager_num)
  jumin_tax = (compensation_after_social_tax - 33) * 10 / 100

  kazei_shotoku = (compensation_after_social_tax -38)
  shotoku_koujo = if kazei_shotoku <= 360
                    kazei_shotoku * 0.3 + 18
                  elsif kazei_shotoku <= 660
                    kazei_shotoku * 0.2 + 54
                  elsif kazei_shotoku <= 1000
                    kazei_shotoku * 0.1 + 120
                  else
                    220
                  end
  kazei_shotoku -= shotoku_koujo

  shotoku_tax = if kazei_shotoku <= 330
                  ((kazei_shotoku - 195) * 0.1 + (195 * 0.05) - 9.75)
                elsif kazei_shotoku <= 695
                  ((kazei_shotoku - 330) * 0.2 + (300 * 0.1) + (195 * 0.05) - 42.75)
                elsif kazei_shotoku <= 900
                  ((kazei_shotoku - 695) * 0.23 + (695 - 330) * 0.2 + (300 * 0.1) + (195 * 0.05) - 63.6)
                elsif kazei_shotoku <= 1800
                  ((kazei_shotoku - 900) * 0.33 + (900 - 695) * 0.23 + (695 - 330) * 0.2 + (300 * 0.1) + (195 * 0.05) - 153.6)
                elsif kazei_shotoku <= 4000
                  ((kazei_shotoku - 1800) * 0.4 + (1800 - 900) * 0.33 + (900 - 695) * 0.23 + (695 - 330) * 0.2 + (300 * 0.1) + (195 * 0.05) - 153.6)
                else
                  ((kazei_shotoku - 4000) * 0.45 + (4000 - 1800) * 0.4 + (1800 - 900) * 0.33 + (900 - 695) * 0.23 + (695 - 330) * 0.2 + (300 * 0.1) + (195 * 0.05) - 153.6)
                end

  compensation_after_tax = compensation_after_social_tax - (jumin_tax + shotoku_tax)
  puts "#{rev_for_comp + (compensation_after_tax * manager_num)},#{rev_for_comp},#{compensation},#{compensation_after_tax}"

  calc(revenue, manager_num, compensation + 10)
end

puts "総資産,法人の収入,個人の額面収入,個人の税引き後収入"
calc(2000, 1, 360)