- 投稿日:2019-02-11T22:27:19+09:00
rbenv で Rails 環境をローカルに作る
今どき Docker じゃなくて、Mac に Rails 環境を作る方法。
グローバルで使う Ruby の設定
$ rbenv global 2.6.1 $ rbenv rehash $ ruby -vRails に依存する gem はすべて bundler 経由でインストールして、ローカルに保存する。グローバルの Ruby 環境は極力汚さない。
Ruby 2.6.1 のデフォルトの gem はこんなかんじ。
$ rbenv exec gem listbigdecimal (default: 1.4.1) bundler (default: 1.17.2) cmath (default: 1.0.0) csv (default: 3.0.4) date (default: 2.0.0) dbm (default: 1.0.0) did_you_mean (1.3.0) e2mmap (default: 0.1.0) etc (default: 1.0.1) fcntl (default: 1.0.0) fiddle (default: 1.0.0) fileutils (default: 1.1.0) forwardable (default: 1.2.0) io-console (default: 0.4.7) ipaddr (default: 1.2.2) irb (default: 1.0.0) json (default: 2.1.0) logger (default: 1.3.0) matrix (default: 0.1.0) minitest (5.11.3) mutex_m (default: 0.1.0) net-telnet (0.2.0) openssl (default: 2.1.2) ostruct (default: 0.1.0) power_assert (1.1.3) prime (default: 0.1.0) psych (default: 3.1.0) rake (12.3.2) rdoc (default: 6.1.0) rexml (default: 3.1.9) rss (default: 0.2.7) scanf (default: 1.0.0) sdbm (default: 1.0.0) shell (default: 0.7) stringio (default: 0.0.2) strscan (default: 1.0.0) sync (default: 0.5.0) test-unit (3.2.9) thwait (default: 0.1.0) tracer (default: 0.1.0) webrick (default: 1.4.2) xmlrpc (0.3.0) zlib (default: 1.0.0)パッケージマネージャの bundler はグローバルにインストールする
$ rbenv exec gem install bundler $ rbenv rehashRails をローカルインストール
$ mkdir create-rails-project $ cd create-rails-projectGemfile を作る
$ cat << EOS > Gemfile source "http://rubygems.org" gem "rails", "5.2.2" EOSRails を
vender/bundle
にインストールする$ bundle install --path vendor/bundleこれで『ガワ』を作ったことになるので、このディレクトリ内で Rails プロジェクトを作る。
$ bundle exec rails new my-app --skip-bundle
--skip-bundle
を必ず忘れないように。これを忘れると、グローバルの Ruby 環境に依存 gem がインストールされてしまう。また、
bundle exec rails new my-app -BCMT --skip-coffee -d sqlite3
とすると、skip-bundle
,skip-action-cable
,skip-action-mailer
,skip-test
と CoffeeScriptのインストールがキャンセルされるので便利。-dは使用するデータベース種別を指定する。こういう構成になった。
. ├── Gemfile ├── Gemfile.lock ├── my-app └── vendor
my-app
が実際に開発する Rails 環境になる。$ mv my-app ../ $ cd .. $ cd my-appgem をローカルにインストールする。
$ bundle install --path vendor/bundle
vendor/bundle
を git の管理外にする。$ echo '/vendor/bundle' >> .gitignorebundle lockコマンドの実行
$ bundle lock --add-platform x64-mingw32 x86-mingw32このコマンドを実行しておくと、bundle install時の
The dependency tzinfo-data (>= 0) will...
のワーニングが表示されなくなる。起動してみる。
$ bundle exec rails s成功。
- 投稿日:2019-02-11T22:25:06+09:00
macOS Mojave で rails s が起動しない
エラーログ
Puma caught this error: Error loading the 'sqlite3' Active Record adapter. Missing a gem it depends on? can't activate sqlite3 (~> 1.3.6), already activated sqlite3-1.4.0. Make sure all dependencies are added to Gemfile. (LoadError)
Gemfile の sqlite 箇所を以下のようにしたら治った
gem 'sqlite3', '~> 1.3.6'
- 投稿日:2019-02-11T22:07:32+09:00
さくらのレンタルサーバでRubyを使う
さくらのレンタルサーバでRubyを使いたい。
$ ruby -v ruby 1.8.7 (2012-10-12 patchlevel 371) [amd64-freebsd9]えぇ…
rbenv使って自前で入れます。
$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv $ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile $ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile $ source ~/.bash_profile $ mkdir -p ~/.rbenv/plugins $ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-buildこれでrbenvとruby-buildの準備ができました。
$ rbenv install 2.6.1 ruby-build: TMPDIR=/tmp cannot hold executables (partition possibly mounted with `noexec`)(バージョン番号は適宜読み替えてください)
/tmp
に実行権限がないと怒られました。レンタルサーバーだししょうがないね。$ mkdir tmp $ TMPDIR=~/tmp rbenv install 2.6.1 Downloading ruby-2.6.1.tar.bz2... -> https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.1.tar.bz2 Installing ruby-2.6.1... BUILD FAILED (FreeBSD 9.1-RELEASE-p24 using ruby-build 20190130) Inspect or clean up the working tree at /home/example/tmp/ruby-build.00000000000000.00000 Results logged to /home/example/tmp/ruby-build.00000000000000.00000.log Last 10 log lines: xmlrpc 0.3.0 installing rdoc: /home/example/.rbenv/versions/2.6.1/share/ri/2.6.0/system installing html-docs: /home/example/.rbenv/versions/2.6.1/share/doc/ruby installing capi-docs: /home/example/.rbenv/versions/2.6.1/share/doc/ruby The Ruby openssl extension was not compiled. ERROR: Ruby install aborted due to missing extensions Configure options used: --prefix=/home/example/.rbenv/versions/2.6.1 LDFLAGS=-L/home/example/.rbenv/versions/2.6.1/lib CPPFLAGS=-I/home/example/.rbenv/versions/2.6.1/includeつらい。
OpenSSLを自前ビルドします。(バージョン番号は適宜読み替えてください)
$ mkdir -p openssl/src $ cd openssl/src $ wget https://www.openssl.org/source/openssl-1.1.1a.tar.gz $ tar -zxvf openssl-1.1.1a.tar.gz $ cd openssl-1.1.1a $ ./config --prefix=~/openssl/openssl $ make $ make test $ make installいざ。
$ TMPDIR=~/tmp RUBY_CONFIGURE_OPTS=--with-openssl-dir=~/openssl/openssl rbenv install 2.6.1 Downloading ruby-2.6.1.tar.bz2... -> https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.1.tar.bz2 Installing ruby-2.6.1... Installed ruby-2.6.1 to /home/example/.rbenv/versions/2.6.1長く苦しい戦いだった…
$ rbenv shell 2.6.1 $ ruby -v ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-freebsd9.1]
ちなみにRuby2.4までならOpenSSL自前で用意しなくてもビルドが通るんだけど、
gem install
が落ちる。$ rbenv shell 2.4.5 $ ruby -v ruby 2.4.5p335 (2018-10-18 revision 65137) [x86_64-freebsd9.1] $ gem install bundle ERROR: Could not find a valid gem 'bundle' (>= 0), here is why: Unable to download data from https://rubygems.org/ - SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A: tlsv1 alert protocol version (https://api.rubygems.org/specs.4.8.gz)なぜなのか
$ rbenv shell 2.4.5 $ ruby -ropenssl -e "p OpenSSL::OPENSSL_VERSION" "OpenSSL 0.9.8zf 19 Mar 2015"つらい
参考文献
- Home · rbenv/ruby-build Wiki · GitHub:ruby-buildのオプションとかはこのへんに書いてある
- openssl/INSTALL at master · openssl/openssl · GitHub:OpenSSLのconfigure optionsとかはtarballの
INSTALL
に書いてある- Bundler: How to troubleshoot RubyGems and Bundler TLS/SSL Issues
- 投稿日:2019-02-11T20:35:02+09:00
Daru::Viewで都道府県のマップを描いてみた
都道府県別のデータをRESAS-APIから取得してDaru::Viewでグラフを描いてみました。本家のサンプルに国ごとのマップを描く方法は載っていますが、県別にマップに色つけする方法がなかったので記録を残します。
前提となる環境
- Jupyter Notebook / Lab
- IRuby
Daru::Viewとは?
Ruby用のデータフレーム Daru のライブラリ。バックエンドにGoogle Chartsや、Highchartsを利用する。Google Chartsの基本的な機能はほぼカバーされている。
https://github.com/SciRuby/daru-viewrequire 'daru/view' require 'resas_kit'RESAS-APIから情報の取得するのはResasKitというGemを利用した。(Qiitaに作者による解説記事があります)
client = ResasKit::Client.new(api_key: KEY) # キー取得には登録が必要都道府県のリストを取得。あとでGoogle Chartに投げるために「県」「都」「府」を消しておきます。
prefs = client.get('prefectures').body.result.map do |item| name = item.prefName name.chop! unless name == "北海道" [name, item.prefCode] end #[["北海道", 1], # ["青森", 2], # ["岩手", 3], # ... # ["沖縄", 47]]都道府県別の総人口を取得する
今回は練習のため、県ごとの総人口を取得してみます。
データの取得
県別のデータを一括して取得する方法がわからなかったので、ここではループをまわして取得しています。
population_data = prefs.map do |name, n| hoge = client.get 'population/composition/perYear', pref_code: n value = hoge.body.result.data[0].data[12].value # 12 は 2020年のデータ population_data << [name, value] endデータフレームの作成
df = Daru::DataFrame.rows(population_data) df.vectors = Daru::Index.new ["都道府県","総人口"] df.head 3 # 上から3行を表示して確認今回は使いませんが、nilの除去をする場合は下記のようにしたりします。
df.replace_values nil, 0 # nilを0に変換する都道府県のチャートを描く
Daru::View::Plot.new(df, adapter: :googlecharts, region: 'JP', resolution: 'provinces', type: :geo, height: 500, width: 800 ).show_in_irubyおまけで、円グラフや棒グラフも描いておきましょう。
円グラフ
df2 = df.sort(["総人口"], ascending: false) # ↑ ソートをかけた Daru::View::Plot.new(df2, type: :pie, height: 500, width: 800 ).show_in_iruby棒グラフ
Daru::View::Plot.new(df, adapter: :googlecharts, type: :bar, vAxis: {textStyle: {fontSize: 8}}, height: 800, width: 800 ).show_in_iruby上記のコードを少し書き換えるだけでも、さまざまなグラフが作成できそうです。
Pythonのすばらしい可視化ライブラリには遠く及びませんが、使用頻度の高い古典的なグラフについてはかなり便利に使えるのではないでしょうか。
余談ですが、上記の簡単なコードを書くのには実はそれなりに時間がかかっています。初めて見たAPIで何の情報がどこに格納されているかを探すのに、かなり時間がかかるようです。
この記事は以上です。
- 投稿日:2019-02-11T20:31:56+09:00
Rails で rubocop-rspec を使うときは、 rubocop-inflector も一緒に使うと便利
TL; DR
RSpec と Rails を一緒につかって開発するときは、
rubocop-inflector
を gem install した上で、以下のような.rubocop.yml
にすると、 Railsで利用している語形変化(ActiveSupport::Inflector
の設定)がそのまま rubocopにも適用されます。
.rubocop.yml
require: - rubocop-rspec # If you are using rubocop-rspec, this should come first. - rubocop-inflector - ./config/initializers/inflections # Your custom rule file
config/initializers/inflections.rb
ActiveSupport::Inflector.inflections(:en) do |inflect| inflect.acronym 'RuboCop' inflect.acronym 'PvP' endRuboCop とは?
Rubocop は、Rubyのコードを静的解析してくれて、フォーマットを整えてくれる君です。
とても便利なツールなので、常日頃お世話になっている方が多いのではないでしょうか?https://github.com/rubocop-hq/rubocop
RuboCop is a Ruby static code analyzer and code formatter. Out of the box it will enforce many of the guidelines outlined in the community Ruby Style Guide.
そして、
rubocop-rspec
とは、RSpecに対しても、フォーマットを整えてくれる君です。https://github.com/rubocop-hq/rubocop-rspec
チーム開発でRailsを利用する場合は、フォーマットに対して不毛な時間を費やさないためにも、これらは是非導入しておきたいところです。
rubocop-rspec
を単体で使った場合の問題点RuboCop は非常に良いツールです。そして
Rubocop::RSpec
もそのとおりです。
しかし、Railsを使って開発していると、かゆいところに手が届きづらいということがあります。例:
RSpec/FilePath
についてこのcopは、RSpecのファイルパスと、RSpec内でのテスト対象が一致しているかをチェックしてくれます。
https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FilePath
例えば、以下のようなテストファイルに対して、ファイル名が不適切だと指摘してくれます。
# my_class_spec.rb describe MommyClass do # ファイル名も mommy_class_spec.rb にしろとおこられる endこれは内部的には、対象を snake_case にした結果がファイル名と一致しているかを確認しています。
しかし、単純に snake_case にするのではなく、例えば、RuboCop
をrubocop
に変換するように、いくつかの言葉は一つの言葉として snake_case にしなければなりません。rubocop-rspec
を単体で利用する場合は、このような言葉は 設定値のCustomTransform
に追加することで対応することが出来ます。例えば、
PvP
という言葉を使っている場合は、以下のような設定値を書くことでこれを回避できます。RSpec/FilePath: CustomTransform: PvP: pvp SyncPvP: sync_pvp AsyncPvP: async_pvp PvPOverPvP: pvp_over_pvp PvPController: pvp_controllerしかし、
CustomTransform
は完全一致であるため、利用しているパターンの数だけ書く必要があります。
そして、Railsを使っている方ならお気づきかもしれませんが、このような設定値ってどこかで見た記憶がありますよね?
rubocop-rspec
も一緒に利用する場合そうです。
config/initializers/inflections.rb
に記述しているActiveSupport::Inflector
の設定が、今回の問題を解消するための鍵になります。そして、この設定を簡単にrubocopに反映してくれるのが、以下のrubocop-inflector
になります。https://github.com/aeroastro/rubocop-inflector
これを導入することで、以下のような簡潔な設定を書くだけで、特殊な言葉への対応が完了します。そして、これらは Rails での inflector と同じ設定であるため、実際のコードと、Rubocopの設定が同期的にメンテナンスされるというメリットもあります。
require: - rubocop-rspec # If you are using rubocop-rspec, this should come first. - rubocop-inflector - ./config/initializers/inflections # Your custom rule fileActiveSupport::Inflector.inflections(:en) do |inflect| inflect.acronym 'PvP' endこの
rubocop-inflector
はリリースされたばかりですが、非常に薄いgemであり、これを導入することで、Rubocopの設定ファイルのメンテナンスが非常に楽になるので、rubocop-rspec
を Rails で利用されている方は、是非利用してみることをおすすめします。
- 投稿日:2019-02-11T18:29:42+09:00
anyenv 経由の rbenv をアップデートする
- 投稿日:2019-02-11T16:47:13+09:00
Rubocop Airbnbの導入
はじめに
Rubocop Airbnbを導入したので、手順をメモします。
Rubocopとは
Rubocopは書かれたコードがRubyのコーディング規約に沿った書かれ方をしているかを自動的に確認してくれるgemです。
Rubocopではなく、Rubocop Airbnbを導入する理由
Rubocopはデフォルト設定だと自分で設定を変更する必要があるとのことなので、airbnbの開発で使用されているrubocopの設定をインストールできるRubocop Airbnbを導入することにしました。
Rubocop Airbnbの導入手順
基本的にRubocop Airbnbの手順に従いました。
Gemfileにgemを追加
gemを追加したら、bundle installします。
Gemfilegroup :development do gem 'rubocop-airbnb' endRubocop-airbnbを適用させるアプリファイルに、rubocop.ymlとrubocop-airbnb.ymlを作成し、それぞれ内容を記載
rubocop.ymlの内容は必要に応じて適宜変更してください。
rubocop.ymlinherit_from: - .rubocop_airbnb.yml # Rails用に最適化 Rails: Enabled: true # 文字数の上限を80文字から変更 LineLength: Max: 130 #rubocopで検証したくないフォルダを指定 AllCops: Exclude: - 以下省略rubocop-airbnb.ymlrequire: - rubocop-airbnb実行
設定が完了したら、
bundle exec rubocop --require rubocop-airbnb
で実行します。以上で終了です。
- 投稿日:2019-02-11T16:30:00+09:00
各言語で定義できる関数のパラメータの最大個数
ちょっと気になって、関数で定義できるパラメータの最大個数について実験してみました。
255を閾値として、それ以上のパラメータを持つ関数を定義・実行できるのか、各言語のREPLを使って調べました。 (JavaScript はブラウザのコンソールです。)
JavaScript は256以上できたので "(unlimited)" としています、実際はもっと少ないかも。
C, C++, C# はある方からいただいたコメントを元にしています。
C, C++, NodeJS, PHP, Ruby について @Nabetani 様 よりいただいた情報を反映しています。
Language Version the maximum number note C 127以上 コンパイラによって変わる。 オーバフローが発生するまで制限なく扱える場合もある。 SOLOLEARN, @Nabetani C++ 256以上 コンパイラによって変わる。 オーバフローが発生するまで制限なく扱える場合もある。 SOLOLEARN, @Nabetani C# 16383 オーバフローするまで成功すると思われる。 Stackoverflow Python 3.6.5 255 256個以上のパラメータを持つ関数は定義不可。 PHP 7.2.9 262145 オーバフローするまで成功すると思われる。 @Nabetani Ruby 2.5.1 130767 オーバフローするまで成功すると思われる。 @Nabetani Kotlin 1.3.0 255 256個以上のパラメータを持つ関数は定義可能だが実行時に java.lang.NullPointerException
が発生する。Java 11.0.2 255 256個以上のパラメータを持つ関数は定義不可。 JavaScript Chrome 72.0.3626.96 (unlimited) オーバフローするまで成功すると思われる。 NodeJS 11.9.0 65535 ルール上は65535。 現実にはオーバフローするまで。 @Nabetani 特に Kotlin は 1.3 で 255個のパラメータを持つ関数を定義できるようになりました。 Java も、 ルールとして 255までパラメータ持てることになっています。 たぶん Scala も?
Python
シンタックスエラーが関数定義の時に255までしか受け付けませんとメッセージを返します。
>>> def f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92, a93, a94, a95, a96, a97, a98, a99, a100, a101, a102, a103, a104, a105, a106, a107, a108, a109, a110, a111, a112, a113, a114, a115, a116, a117, a118, a119, a120, a121, a122, a123, a124, a125, a126, a127, a128, a129, a130, a131, a132, a133, a134, a135, a136, a137, a138, a139, a140, a141, a142, a143, a144, a145, a146, a147, a148, a149, a150, a151, a152, a153, a154, a155, a156, a157, a158, a159, a160, a161, a162, a163, a164, a165, a166, a167, a168, a169, a170, a171, a172, a173, a174, a175, a176, a177, a178, a179, a180, a181, a182, a183, a184, a185, a186, a187, a188, a189, a190, a191, a192, a193, a194, a195, a196, a197, a198, a199, a200, a201, a202, a203, a204, a205, a206, a207, a208, a209, a210, a211, a212, a213, a214, a215, a216, a217, a218, a219, a220, a221, a222, a223, a224, a225, a226, a227, a228, a229, a230, a231, a232, a233, a234, a235, a236, a237, a238, a239, a240, a241, a242, a243, a244, a245, a246, a247, a248, a249, a250, a251, a252, a253, a254, a255, a256, a257): ... print(1) ... File "<stdin>", line 1 SyntaxError: more than 255 argumentsPHP
256以上のパラメータでも定義・実行できます。
php > function f($a0, $a1, $a2, $a3, $a4, $a5, $a6, $a7, $a8, $a9, $a10, $a11, $a12, $a13, $a14, $a15, $a16, $a17, $a18, $a19, $a20, $a21, $a22, $a23, $a24, $a25, $a26, $a27, $a28, $a29, $a30, $a31, $a32, $a33, $a34, $a35, $a36, $a37, $a38, $a39, $a40, $a41, $a42, $a43, $a44, $a45, $a46, $a47, $a48, $a49, $a50, $a51, $a52, $a53, $a54, $a55, $a56, $a57, $a58, $a59, $a60, $a61, $a62, $a63, $a64, $a65, $a66, $a67, $a68, $a69, $a70, $a71, $a72, $a73, $a74, $a75, $a76, $a77, $a78, $a79, $a80, $a81, $a82, $a83, $a84, $a85, $a86, $a87, $a88, $a89, $a90, $a91, $a92, $a93, $a94, $a95, $a96, $a97, $a98, $a99, $a100, $a101, $a102, $a103, $a104, $a105, $a106, $a107, $a108, $a109, $a110, $a111, $a112, $a113, $a114, $a115, $a116, $a117, $a118, $a119, $a120, $a121, $a122, $a123, $a124, $a125, $a126, $a127, $a128, $a129, $a130, $a131, $a132, $a133, $a134, $a135, $a136, $a137, $a138, $a139, $a140, $a141, $a142, $a143, $a144, $a145, $a146, $a147, $a148, $a149, $a150, $a151, $a152, $a153, $a154, $a155, $a156, $a157, $a158, $a159, $a160, $a161, $a162, $a163, $a164, $a165, $a166, $a167, $a168, $a169, $a170, $a171, $a172, $a173, $a174, $a175, $a176, $a177, $a178, $a179, $a180, $a181, $a182, $a183, $a184, $a185, $a186, $a187, $a188, $a189, $a190, $a191, $a192, $a193, $a194, $a195, $a196, $a197, $a198, $a199, $a200, $a201, $a202, $a203, $a204, $a205, $a206, $a207, $a208, $a209, $a210, $a211, $a212, $a213, $a214, $a215, $a216, $a217, $a218, $a219, $a220, $a221, $a222, $a223, $a224, $a225, $a226, $a227, $a228, $a229, $a230, $a231, $a232, $a233, $a234, $a235, $a236, $a237, $a238, $a239, $a240, $a241, $a242, $a243, $a244, $a245, $a246, $a247, $a248, $a249, $a250, $a251, $a252, $a253, $a254, $a255, $a256, $a257) { echo 1; } php > php > function f($a0 = 1, $a1 = 1, $a2 = 1, $a3 = 1, $a4 = 1, $a5 = 1, $a6 = 1, $a7 = 1, $a8 = 1, $a9 = 1, $a10 = 1, $a11 = 1, $a12 = 1, $a13 = 1, $a14 = 1, $a15 = 1, $a16 = 1, $a17 = 1, $a18 = 1, $a19 = 1, $a20 = 1, $a21 = 1, $a22 = 1, $a23 = 1, $a24 = 1, $a25 = 1, $a26 = 1, $a27 = 1, $a28 = 1, $a29 = 1, $a30 = 1, $a31 = 1, $a32 = 1, $a33 = 1, $a34 = 1, $a35 = 1, $a36 = 1, $a37 = 1, $a38 = 1, $a39 = 1, $a40 = 1, $a41 = 1, $a42 = 1, $a43 = 1, $a44 = 1, $a45 = 1, $a46 = 1, $a47 = 1, $a48 = 1, $a49 = 1, $a50 = 1, $a51 = 1, $a52 = 1, $a53 = 1, $a54 = 1, $a55 = 1, $a56 = 1, $a57 = 1, $a58 = 1, $a59 = 1, $a60 = 1, $a61 = 1, $a62 = 1, $a63 = 1, $a64 = 1, $a65 = 1, $a66 = 1, $a67 = 1, $a68 = 1, $a69 = 1, $a70 = 1, $a71 = 1, $a72 = 1, $a73 = 1, $a74 = 1, $a75 = 1, $a76 = 1, $a77 = 1, $a78 = 1, $a79 = 1, $a80 = 1, $a81 = 1, $a82 = 1, $a83 = 1, $a84 = 1, $a85 = 1, $a86 = 1, $a87 = 1, $a88 = 1, $a89 = 1, $a90 = 1, $a91 = 1, $a92 = 1, $a93 = 1, $a94 = 1, $a95 = 1, $a96 = 1, $a97 = 1, $a98 = 1, $a99 = 1, $a100 = 1, $a101 = 1, $a102 = 1, $a103 = 1, $a104 = 1, $a105 = 1, $a106 = 1, $a107 = 1, $a108 = 1, $a109 = 1, $a110 = 1, $a111 = 1, $a112 = 1, $a113 = 1, $a114 = 1, $a115 = 1, $a116 = 1, $a117 = 1, $a118 = 1, $a119 = 1, $a120 = 1, $a121 = 1, $a122 = 1, $a123 = 1, $a124 = 1, $a125 = 1, $a126 = 1, $a127 = 1, $a128 = 1, $a129 = 1, $a130 = 1, $a131 = 1, $a132 = 1, $a133 = 1, $a134 = 1, $a135 = 1, $a136 = 1, $a137 = 1, $a138 = 1, $a139 = 1, $a140 = 1, $a141 = 1, $a142 = 1, $a143 = 1, $a144 = 1, $a145 = 1, $a146 = 1, $a147 = 1, $a148 = 1, $a149 = 1, $a150 = 1, $a151 = 1, $a152 = 1, $a153 = 1, $a154 = 1, $a155 = 1, $a156 = 1, $a157 = 1, $a158 = 1, $a159 = 1, $a160 = 1, $a161 = 1, $a162 = 1, $a163 = 1, $a164 = 1, $a165 = 1, $a166 = 1, $a167 = 1, $a168 = 1, $a169 = 1, $a170 = 1, $a171 = 1, $a172 = 1, $a173 = 1, $a174 = 1, $a175 = 1, $a176 = 1, $a177 = 1, $a178 = 1, $a179 = 1, $a180 = 1, $a181 = 1, $a182 = 1, $a183 = 1, $a184 = 1, $a185 = 1, $a186 = 1, $a187 = 1, $a188 = 1, $a189 = 1, $a190 = 1, $a191 = 1, $a192 = 1, $a193 = 1, $a194 = 1, $a195 = 1, $a196 = 1, $a197 = 1, $a198 = 1, $a199 = 1, $a200 = 1, $a201 = 1, $a202 = 1, $a203 = 1, $a204 = 1, $a205 = 1, $a206 = 1, $a207 = 1, $a208 = 1, $a209 = 1, $a210 = 1, $a211 = 1, $a212 = 1, $a213 = 1, $a214 = 1, $a215 = 1, $a216 = 1, $a217 = 1, $a218 = 1, $a219 = 1, $a220 = 1, $a221 = 1, $a222 = 1, $a223 = 1, $a224 = 1, $a225 = 1, $a226 = 1, $a227 = 1, $a228 = 1, $a229 = 1, $a230 = 1, $a231 = 1, $a232 = 1, $a233 = 1, $a234 = 1, $a235 = 1, $a236 = 1, $a237 = 1, $a238 = 1, $a239 = 1, $a240 = 1, $a241 = 1, $a242 = 1, $a243 = 1, $a244 = 1, $a245 = 1, $a246 = 1, $a247 = 1, $a248 = 1, $a249 = 1, $a250 = 1, $a251 = 1, $a252 = 1, $a253 = 1, $a254 = 1, $a255 = 1, $a256 = 1) { echo 1; } php > f() php > ; 1Ruby
256以上のパラメータでも実行できます。
2.5.1 :004 > def f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92, a93, a94, a95, a96, a97, a98, a99, a100, a101, a102, a103, a104, a105, a106, a107, a108, a109, a110, a111, a112, a113, a114, a115, a116, a117, a118, a119, a120, a121, a122, a123, a124, a125, a126, a127, a128, a129, a130, a131, a132, a133, a134, a135, a136, a137, a138, a139, a140, a141, a142, a143, a144, a145, a146, a147, a148, a149, a150, a151, a152, a153, a154, a155, a156, a157, a158, a159, a160, a161, a162, a163, a164, a165, a166, a167, a168, a169, a170, a171, a172, a173, a174, a175, a176, a177, a178, a179, a180, a181, a182, a183, a184, a185, a186, a187, a188, a189, a190, a191, a192, a193, a194, a195, a196, a197, a198, a199, a200, a201, a202, a203, a204, a205, a206, a207, a208, a209, a210, a211, a212, a213, a214, a215, a216, a217, a218, a219, a220, a221, a222, a223, a224, a225, a226, a227, a228, a229, a230, a231, a232, a233, a234, a235, a236, a237, a238, a239, a240, a241, a242, a243, a244, a245, a246, a247, a248, a249, a250, a251, a252, a253, a254, a255, a256, a257) 2.5.1 :005?> puts 1 2.5.1 :006?> end => :f 2.5.1 :001 > def f(a0 = 1, a1 = 1, a2 = 1, a3 = 1, a4 = 1, a5 = 1, a6 = 1, a7 = 1, a8 = 1, a9 = 1, a10 = 1, a11 = 1, a12 = 1, a13 = 1, a14 = 1, a15 = 1, a16 = 1, a17 = 1, a18 = 1, a19 = 1, a20 = 1, a21 = 1, a22 = 1, a23 = 1, a24 = 1, a25 = 1, a26 = 1, a27 = 1, a28 = 1, a29 = 1, a30 = 1, a31 = 1, a32 = 1, a33 = 1, a34 = 1, a35 = 1, a36 = 1, a37 = 1, a38 = 1, a39 = 1, a40 = 1, a41 = 1, a42 = 1, a43 = 1, a44 = 1, a45 = 1, a46 = 1, a47 = 1, a48 = 1, a49 = 1, a50 = 1, a51 = 1, a52 = 1, a53 = 1, a54 = 1, a55 = 1, a56 = 1, a57 = 1, a58 = 1, a59 = 1, a60 = 1, a61 = 1, a62 = 1, a63 = 1, a64 = 1, a65 = 1, a66 = 1, a67 = 1, a68 = 1, a69 = 1, a70 = 1, a71 = 1, a72 = 1, a73 = 1, a74 = 1, a75 = 1, a76 = 1, a77 = 1, a78 = 1, a79 = 1, a80 = 1, a81 = 1, a82 = 1, a83 = 1, a84 = 1, a85 = 1, a86 = 1, a87 = 1, a88 = 1, a89 = 1, a90 = 1, a91 = 1, a92 = 1, a93 = 1, a94 = 1, a95 = 1, a96 = 1, a97 = 1, a98 = 1, a99 = 1, a100 = 1, a101 = 1, a102 = 1, a103 = 1, a104 = 1, a105 = 1, a106 = 1, a107 = 1, a108 = 1, a109 = 1, a110 = 1, a111 = 1, a112 = 1, a113 = 1, a114 = 1, a115 = 1, a116 = 1, a117 = 1, a118 = 1, a119 = 1, a120 = 1, a121 = 1, a122 = 1, a123 = 1, a124 = 1, a125 = 1, a126 = 1, a127 = 1, a128 = 1, a129 = 1, a130 = 1, a131 = 1, a132 = 1, a133 = 1, a134 = 1, a135 = 1, a136 = 1, a137 = 1, a138 = 1, a139 = 1, a140 = 1, a141 = 1, a142 = 1, a143 = 1, a144 = 1, a145 = 1, a146 = 1, a147 = 1, a148 = 1, a149 = 1, a150 = 1, a151 = 1, a152 = 1, a153 = 1, a154 = 1, a155 = 1, a156 = 1, a157 = 1, a158 = 1, a159 = 1, a160 = 1, a161 = 1, a162 = 1, a163 = 1, a164 = 1, a165 = 1, a166 = 1, a167 = 1, a168 = 1, a169 = 1, a170 = 1, a171 = 1, a172 = 1, a173 = 1, a174 = 1, a175 = 1, a176 = 1, a177 = 1, a178 = 1, a179 = 1, a180 = 1, a181 = 1, a182 = 1, a183 = 1, a184 = 1, a185 = 1, a186 = 1, a187 = 1, a188 = 1, a189 = 1, a190 = 1, a191 = 1, a192 = 1, a193 = 1, a194 = 1, a195 = 1, a196 = 1, a197 = 1, a198 = 1, a199 = 1, a200 = 1, a201 = 1, a202 = 1, a203 = 1, a204 = 1, a205 = 1, a206 = 1, a207 = 1, a208 = 1, a209 = 1, a210 = 1, a211 = 1, a212 = 1, a213 = 1, a214 = 1, a215 = 1, a216 = 1, a217 = 1, a218 = 1, a219 = 1, a220 = 1, a221 = 1, a222 = 1, a223 = 1, a224 = 1, a225 = 1, a226 = 1, a227 = 1, a228 = 1, a229 = 1, a230 = 1, a231 = 1, a232 = 1, a233 = 1, a234 = 1, a235 = 1, a236 = 1, a237 = 1, a238 = 1, a239 = 1, a240 = 1, a241 = 1, a242 = 1, a243 = 1, a244 = 1, a245 = 1, a246 = 1, a247 = 1, a248 = 1, a249 = 1, a250 = 1, a251 = 1, a252 = 1, a253 = 1, a254 = 1, a255 = 1, a256 = 1) 2.5.1 :002?> puts 1 2.5.1 :003?> end => :f 2.5.1 :004 > f() 1 => nilKotlin
定義はできているように見えます。しかし実行時にエラーが出ました。
>>> fun f(a0: Int, a1: Int, a2: Int, a3: Int, a4: Int, a5: Int, a6: Int, a7: Int, a8: Int, a9: Int, a10: Int, a11: Int, a12: Int, a13: Int, a14: Int, a15: Int, a16: Int, a17: Int, a18: Int, a19: Int, a20: Int, a21: Int, a22: Int, a23: Int, a24: Int, a25: Int, a26: Int, a27: Int, a28: Int, a29: Int, a30: Int, a31: Int, a32: Int, a33: Int, a34: Int, a35: Int, a36: Int, a37: Int, a38: Int, a39: Int, a40: Int, a41: Int, a42: Int, a43: Int, a44: Int, a45: Int, a46: Int, a47: Int, a48: Int, a49: Int, a50: Int, a51: Int, a52: Int, a53: Int, a54: Int, a55: Int, a56: Int, a57: Int, a58: Int, a59: Int, a60: Int, a61: Int, a62: Int, a63: Int, a64: Int, a65: Int, a66: Int, a67: Int, a68: Int, a69: Int, a70: Int, a71: Int, a72: Int, a73: Int, a74: Int, a75: Int, a76: Int, a77: Int, a78: Int, a79: Int, a80: Int, a81: Int, a82: Int, a83: Int, a84: Int, a85: Int, a86: Int, a87: Int, a88: Int, a89: Int, a90: Int, a91: Int, a92: Int, a93: Int, a94: Int, a95: Int, a96: Int, a97: Int, a98: Int, a99: Int, a100: Int, a101: Int, a102: Int, a103: Int, a104: Int, a105: Int, a106: Int, a107: Int, a108: Int, a109: Int, a110: Int, a111: Int, a112: Int, a113: Int, a114: Int, a115: Int, a116: Int, a117: Int, a118: Int, a119: Int, a120: Int, a121: Int, a122: Int, a123: Int, a124: Int, a125: Int, a126: Int, a127: Int, a128: Int, a129: Int, a130: Int, a131: Int, a132: Int, a133: Int, a134: Int, a135: Int, a136: Int, a137: Int, a138: Int, a139: Int, a140: Int, a141: Int, a142: Int, a143: Int, a144: Int, a145: Int, a146: Int, a147: Int, a148: Int, a149: Int, a150: Int, a151: Int, a152: Int, a153: Int, a154: Int, a155: Int, a156: Int, a157: Int, a158: Int, a159: Int, a160: Int, a161: Int, a162: Int, a163: Int, a164: Int, a165: Int, a166: Int, a167: Int, a168: Int, a169: Int, a170: Int, a171: Int, a172: Int, a173: Int, a174: Int, a175: Int, a176: Int, a177: Int, a178: Int, a179: Int, a180: Int, a181: Int, a182: Int, a183: Int, a184: Int, a185: Int, a186: Int, a187: Int, a188: Int, a189: Int, a190: Int, a191: Int, a192: Int, a193: Int, a194: Int, a195: Int, a196: Int, a197: Int, a198: Int, a199: Int, a200: Int, a201: Int, a202: Int, a203: Int, a204: Int, a205: Int, a206: Int, a207: Int, a208: Int, a209: Int, a210: Int, a211: Int, a212: Int, a213: Int, a214: Int, a215: Int, a216: Int, a217: Int, a218: Int, a219: Int, a220: Int, a221: Int, a222: Int, a223: Int, a224: Int, a225: Int, a226: Int, a227: Int, a228: Int, a229: Int, a230: Int, a231: Int, a232: Int, a233: Int, a234: Int, a235: Int, a236: Int, a237: Int, a238: Int, a239: Int, a240: Int, a241: Int, a242: Int, a243: Int, a244: Int, a245: Int, a246: Int, a247: Int, a248: Int, a249: Int, a250: Int, a251: Int, a252: Int, a253: Int, a254: Int, a255: Int, a256: Int, a257: Int) { println(1) } >>> fun f(a0: Int = 1, a1: Int = 1, a2: Int = 1, a3: Int = 1, a4: Int = 1, a5: Int = 1, a6: Int = 1, a7: Int = 1, a8: Int = 1, a9: Int = 1, a10: Int = 1, a11: Int = 1, a12: Int = 1, a13: Int = 1, a14: Int = 1, a15: Int = 1, a16: Int = 1, a17: Int = 1, a18: Int = 1, a19: Int = 1, a20: Int = 1, a21: Int = 1, a22: Int = 1, a23: Int = 1, a24: Int = 1, a25: Int = 1, a26: Int = 1, a27: Int = 1, a28: Int = 1, a29: Int = 1, a30: Int = 1, a31: Int = 1, a32: Int = 1, a33: Int = 1, a34: Int = 1, a35: Int = 1, a36: Int = 1, a37: Int = 1, a38: Int = 1, a39: Int = 1, a40: Int = 1, a41: Int = 1, a42: Int = 1, a43: Int = 1, a44: Int = 1, a45: Int = 1, a46: Int = 1, a47: Int = 1, a48: Int = 1, a49: Int = 1, a50: Int = 1, a51: Int = 1, a52: Int = 1, a53: Int = 1, a54: Int = 1, a55: Int = 1, a56: Int = 1, a57: Int = 1, a58: Int = 1, a59: Int = 1, a60: Int = 1, a61: Int = 1, a62: Int = 1, a63: Int = 1, a64: Int = 1, a65: Int = 1, a66: Int = 1, a67: Int = 1, a68: Int = 1, a69: Int = 1, a70: Int = 1, a71: Int = 1, a72: Int = 1, a73: Int = 1, a74: Int = 1, a75: Int = 1, a76: Int = 1, a77: Int = 1, a78: Int = 1, a79: Int = 1, a80: Int = 1, a81: Int = 1, a82: Int = 1, a83: Int = 1, a84: Int = 1, a85: Int = 1, a86: Int = 1, a87: Int = 1, a88: Int = 1, a89: Int = 1, a90: Int = 1, a91: Int = 1, a92: Int = 1, a93: Int = 1, a94: Int = 1, a95: Int = 1, a96: Int = 1, a97: Int = 1, a98: Int = 1, a99: Int = 1, a100: Int = 1, a101: Int = 1, a102: Int = 1, a103: Int = 1, a104: Int = 1, a105: Int = 1, a106: Int = 1, a107: Int = 1, a108: Int = 1, a109: Int = 1, a110: Int = 1, a111: Int = 1, a112: Int = 1, a113: Int = 1, a114: Int = 1, a115: Int = 1, a116: Int = 1, a117: Int = 1, a118: Int = 1, a119: Int = 1, a120: Int = 1, a121: Int = 1, a122: Int = 1, a123: Int = 1, a124: Int = 1, a125: Int = 1, a126: Int = 1, a127: Int = 1, a128: Int = 1, a129: Int = 1, a130: Int = 1, a131: Int = 1, a132: Int = 1, a133: Int = 1, a134: Int = 1, a135: Int = 1, a136: Int = 1, a137: Int = 1, a138: Int = 1, a139: Int = 1, a140: Int = 1, a141: Int = 1, a142: Int = 1, a143: Int = 1, a144: Int = 1, a145: Int = 1, a146: Int = 1, a147: Int = 1, a148: Int = 1, a149: Int = 1, a150: Int = 1, a151: Int = 1, a152: Int = 1, a153: Int = 1, a154: Int = 1, a155: Int = 1, a156: Int = 1, a157: Int = 1, a158: Int = 1, a159: Int = 1, a160: Int = 1, a161: Int = 1, a162: Int = 1, a163: Int = 1, a164: Int = 1, a165: Int = 1, a166: Int = 1, a167: Int = 1, a168: Int = 1, a169: Int = 1, a170: Int = 1, a171: Int = 1, a172: Int = 1, a173: Int = 1, a174: Int = 1, a175: Int = 1, a176: Int = 1, a177: Int = 1, a178: Int = 1, a179: Int = 1, a180: Int = 1, a181: Int = 1, a182: Int = 1, a183: Int = 1, a184: Int = 1, a185: Int = 1, a186: Int = 1, a187: Int = 1, a188: Int = 1, a189: Int = 1, a190: Int = 1, a191: Int = 1, a192: Int = 1, a193: Int = 1, a194: Int = 1, a195: Int = 1, a196: Int = 1, a197: Int = 1, a198: Int = 1, a199: Int = 1, a200: Int = 1, a201: Int = 1, a202: Int = 1, a203: Int = 1, a204: Int = 1, a205: Int = 1, a206: Int = 1, a207: Int = 1, a208: Int = 1, a209: Int = 1, a210: Int = 1, a211: Int = 1, a212: Int = 1, a213: Int = 1, a214: Int = 1, a215: Int = 1, a216: Int = 1, a217: Int = 1, a218: Int = 1, a219: Int = 1, a220: Int = 1, a221: Int = 1, a222: Int = 1, a223: Int = 1, a224: Int = 1, a225: Int = 1, a226: Int = 1, a227: Int = 1, a228: Int = 1, a229: Int = 1, a230: Int = 1, a231: Int = 1, a232: Int = 1, a233: Int = 1, a234: Int = 1, a235: Int = 1, a236: Int = 1, a237: Int = 1, a238: Int = 1, a239: Int = 1, a240: Int = 1, a241: Int = 1, a242: Int = 1, a243: Int = 1, a244: Int = 1, a245: Int = 1, a246: Int = 1, a247: Int = 1, a248: Int = 1, a249: Int = 1, a250: Int = 1, a251: Int = 1, a252: Int = 1, a253: Int = 1, a254: Int = 1, a255: Int = 1) { println(1) } >>> f() java.lang.NullPointerException at Line_1.f$default(Line_1.kts:1) >>> f(1) java.lang.NullPointerException at Line_1.f$default(Line_1.kts:1) >>> fun f(a0: Int = 1, a1: Int = 1, a2: Int = 1, a3: Int = 1, a4: Int = 1, a5: Int = 1, a6: Int = 1, a7: Int = 1, a8: Int = 1, a9: Int = 1, a10: Int = 1, a11: Int = 1, a12: Int = 1, a13: Int = 1, a14: Int = 1, a15: Int = 1, a16: Int = 1, a17: Int = 1, a18: Int = 1, a19: Int = 1, a20: Int = 1, a21: Int = 1, a22: Int = 1, a23: Int = 1, a24: Int = 1, a25: Int = 1, a26: Int = 1, a27: Int = 1, a28: Int = 1, a29: Int = 1, a30: Int = 1, a31: Int = 1, a32: Int = 1, a33: Int = 1, a34: Int = 1, a35: Int = 1, a36: Int = 1, a37: Int = 1, a38: Int = 1, a39: Int = 1, a40: Int = 1, a41: Int = 1, a42: Int = 1, a43: Int = 1, a44: Int = 1, a45: Int = 1, a46: Int = 1, a47: Int = 1, a48: Int = 1, a49: Int = 1, a50: Int = 1, a51: Int = 1, a52: Int = 1, a53: Int = 1, a54: Int = 1, a55: Int = 1, a56: Int = 1, a57: Int = 1, a58: Int = 1, a59: Int = 1, a60: Int = 1, a61: Int = 1, a62: Int = 1, a63: Int = 1, a64: Int = 1, a65: Int = 1, a66: Int = 1, a67: Int = 1, a68: Int = 1, a69: Int = 1, a70: Int = 1, a71: Int = 1, a72: Int = 1, a73: Int = 1, a74: Int = 1, a75: Int = 1, a76: Int = 1, a77: Int = 1, a78: Int = 1, a79: Int = 1, a80: Int = 1, a81: Int = 1, a82: Int = 1, a83: Int = 1, a84: Int = 1, a85: Int = 1, a86: Int = 1, a87: Int = 1, a88: Int = 1, a89: Int = 1, a90: Int = 1, a91: Int = 1, a92: Int = 1, a93: Int = 1, a94: Int = 1, a95: Int = 1, a96: Int = 1, a97: Int = 1, a98: Int = 1, a99: Int = 1, a100: Int = 1, a101: Int = 1, a102: Int = 1, a103: Int = 1, a104: Int = 1, a105: Int = 1, a106: Int = 1, a107: Int = 1, a108: Int = 1, a109: Int = 1, a110: Int = 1, a111: Int = 1, a112: Int = 1, a113: Int = 1, a114: Int = 1, a115: Int = 1, a116: Int = 1, a117: Int = 1, a118: Int = 1, a119: Int = 1, a120: Int = 1, a121: Int = 1, a122: Int = 1, a123: Int = 1, a124: Int = 1, a125: Int = 1, a126: Int = 1, a127: Int = 1, a128: Int = 1, a129: Int = 1, a130: Int = 1, a131: Int = 1, a132: Int = 1, a133: Int = 1, a134: Int = 1, a135: Int = 1, a136: Int = 1, a137: Int = 1, a138: Int = 1, a139: Int = 1, a140: Int = 1, a141: Int = 1, a142: Int = 1, a143: Int = 1, a144: Int = 1, a145: Int = 1, a146: Int = 1, a147: Int = 1, a148: Int = 1, a149: Int = 1, a150: Int = 1, a151: Int = 1, a152: Int = 1, a153: Int = 1, a154: Int = 1, a155: Int = 1, a156: Int = 1, a157: Int = 1, a158: Int = 1, a159: Int = 1, a160: Int = 1, a161: Int = 1, a162: Int = 1, a163: Int = 1, a164: Int = 1, a165: Int = 1, a166: Int = 1, a167: Int = 1, a168: Int = 1, a169: Int = 1, a170: Int = 1, a171: Int = 1, a172: Int = 1, a173: Int = 1, a174: Int = 1, a175: Int = 1, a176: Int = 1, a177: Int = 1, a178: Int = 1, a179: Int = 1, a180: Int = 1, a181: Int = 1, a182: Int = 1, a183: Int = 1, a184: Int = 1, a185: Int = 1, a186: Int = 1, a187: Int = 1, a188: Int = 1, a189: Int = 1, a190: Int = 1, a191: Int = 1, a192: Int = 1, a193: Int = 1, a194: Int = 1, a195: Int = 1, a196: Int = 1, a197: Int = 1, a198: Int = 1, a199: Int = 1, a200: Int = 1, a201: Int = 1, a202: Int = 1, a203: Int = 1, a204: Int = 1, a205: Int = 1, a206: Int = 1, a207: Int = 1, a208: Int = 1, a209: Int = 1, a210: Int = 1, a211: Int = 1, a212: Int = 1, a213: Int = 1, a214: Int = 1, a215: Int = 1, a216: Int = 1, a217: Int = 1, a218: Int = 1, a219: Int = 1, a220: Int = 1, a221: Int = 1, a222: Int = 1, a223: Int = 1, a224: Int = 1, a225: Int = 1, a226: Int = 1, a227: Int = 1, a228: Int = 1, a229: Int = 1, a230: Int = 1, a231: Int = 1, a232: Int = 1, a233: Int = 1, a234: Int = 1, a235: Int = 1, a236: Int = 1, a237: Int = 1, a238: Int = 1, a239: Int = 1, a240: Int = 1, a241: Int = 1, a242: Int = 1, a243: Int = 1, a244: Int = 1, a245: Int = 1, a246: Int = 1, a247: Int = 1, a248: Int = 1, a249: Int = 1, a250: Int = 1, a251: Int = 1, a252: Int = 1, a253: Int = 1, a254: Int = 1) { println(1) } >>> f() 1 >>>Java
256個以上のパラメータがあると、定義することもできません。
jshell> void f(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12, int a13, int a14, int a15, int a16, int a17, int a18, int a19, int a20, int a21, int a22, int a23, int a24, int a25, int a26, int a27, int a28, int a29, int a30, int a31, int a32, int a33, int a34, int a35, int a36, int a37, int a38, int a39, int a40, int a41, int a42, int a43, int a44, int a45, int a46, int a47, int a48, int a49, int a50, int a51, int a52, int a53, int a54, int a55, int a56, int a57, int a58, int a59, int a60, int a61, int a62, int a63, int a64, int a65, int a66, int a67, int a68, int a69, int a70, int a71, int a72, int a73, int a74, int a75, int a76, int a77, int a78, int a79, int a80, int a81, int a82, int a83, int a84, int a85, int a86, int a87, int a88, int a89, int a90, int a91, int a92, int a93, int a94, int a95, int a96, int a97, int a98, int a99, int a100, int a101, int a102, int a103, int a104, int a105, int a106, int a107, int a108, int a109, int a110, int a111, int a112, int a113, int a114, int a115, int a116, int a117, int a118, int a119, int a120, int a121, int a122, int a123, int a124, int a125, int a126, int a127, int a128, int a129, int a130, int a131, int a132, int a133, int a134, int a135, int a136, int a137, int a138, int a139, int a140, int a141, int a142, int a143, int a144, int a145, int a146, int a147, int a148, int a149, int a150, int a151, int a152, int a153, int a154, int a155, int a156, int a157, int a158, int a159, int a160, int a161, int a162, int a163, int a164, int a165, int a166, int a167, int a168, int a169, int a170, int a171, int a172, int a173, int a174, int a175, int a176, int a177, int a178, int a179, int a180, int a181, int a182, int a183, int a184, int a185, int a186, int a187, int a188, int a189, int a190, int a191, int a192, int a193, int a194, int a195, int a196, int a197, int a198, int a199, int a200, int a201, int a202, int a203, int a204, int a205, int a206, int a207, int a208, int a209, int a210, int a211, int a212, int a213, int a214, int a215, int a216, int a217, int a218, int a219, int a220, int a221, int a222, int a223, int a224, int a225, int a226, int a227, int a228, int a229, int a230, int a231, int a232, int a233, int a234, int a235, int a236, int a237, int a238, int a239, int a240, int a241, int a242, int a243, int a244, int a245, int a246, int a247, int a248, int a249, int a250, int a251, int a252, int a253, int a254, int a255) { System.out.println(1); } | Error: | too many parameters | void f(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12, int a13, int a14, int a15, int a16, int a17, int a18, int a19, int a20, int a21, int a22, int a23, int a24, int a25, int a26, int a27, int a28, int a29, int a30, int a31, int a32, int a33, int a34, int a35, int a36, int a37, int a38, int a39, int a40, int a41, int a42, int a43, int a44, int a45, int a46, int a47, int a48, int a49, int a50, int a51, int a52, int a53, int a54, int a55, int a56, int a57, int a58, int a59, int a60, int a61, int a62, int a63, int a64, int a65, int a66, int a67, int a68, int a69, int a70, int a71, int a72, int a73, int a74, int a75, int a76, int a77, int a78, int a79, int a80, int a81, int a82, int a83, int a84, int a85, int a86, int a87, int a88, int a89, int a90, int a91, int a92, int a93, int a94, int a95, int a96, int a97, int a98, int a99, int a100, int a101, int a102, int a103, int a104, int a105, int a106, int a107, int a108, int a109, int a110, int a111, int a112, int a113, int a114, int a115, int a116, int a117, int a118, int a119, int a120, int a121, int a122, int a123, int a124, int a125, int a126, int a127, int a128, int a129, int a130, int a131, int a132, int a133, int a134, int a135, int a136, int a137, int a138, int a139, int a140, int a141, int a142, int a143, int a144, int a145, int a146, int a147, int a148, int a149, int a150, int a151, int a152, int a153, int a154, int a155, int a156, int a157, int a158, int a159, int a160, int a161, int a162, int a163, int a164, int a165, int a166, int a167, int a168, int a169, int a170, int a171, int a172, int a173, int a174, int a175, int a176, int a177, int a178, int a179, int a180, int a181, int a182, int a183, int a184, int a185, int a186, int a187, int a188, int a189, int a190, int a191, int a192, int a193, int a194, int a195, int a196, int a197, int a198, int a199, int a200, int a201, int a202, int a203, int a204, int a205, int a206, int a207, int a208, int a209, int a210, int a211, int a212, int a213, int a214, int a215, int a216, int a217, int a218, int a219, int a220, int a221, int a222, int a223, int a224, int a225, int a226, int a227, int a228, int a229, int a230, int a231, int a232, int a233, int a234, int a235, int a236, int a237, int a238, int a239, int a240, int a241, int a242, int a243, int a244, int a245, int a246, int a247, int a248, int a249, int a250, int a251, int a252, int a253, int a254, int a255) { System.out.println(1); } | ^----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------^ | modified method f(int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int), however, it cannot be referenced until this error is corrected: | too many parameters | void f(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12, int a13, int a14, int a15, int a16, int a17, int a18, int a19, int a20, int a21, int a22, int a23, int a24, int a25, int a26, int a27, int a28, int a29, int a30, int a31, int a32, int a33, int a34, int a35, int a36, int a37, int a38, int a39, int a40, int a41, int a42, int a43, int a44, int a45, int a46, int a47, int a48, int a49, int a50, int a51, int a52, int a53, int a54, int a55, int a56, int a57, int a58, int a59, int a60, int a61, int a62, int a63, int a64, int a65, int a66, int a67, int a68, int a69, int a70, int a71, int a72, int a73, int a74, int a75, int a76, int a77, int a78, int a79, int a80, int a81, int a82, int a83, int a84, int a85, int a86, int a87, int a88, int a89, int a90, int a91, int a92, int a93, int a94, int a95, int a96, int a97, int a98, int a99, int a100, int a101, int a102, int a103, int a104, int a105, int a106, int a107, int a108, int a109, int a110, int a111, int a112, int a113, int a114, int a115, int a116, int a117, int a118, int a119, int a120, int a121, int a122, int a123, int a124, int a125, int a126, int a127, int a128, int a129, int a130, int a131, int a132, int a133, int a134, int a135, int a136, int a137, int a138, int a139, int a140, int a141, int a142, int a143, int a144, int a145, int a146, int a147, int a148, int a149, int a150, int a151, int a152, int a153, int a154, int a155, int a156, int a157, int a158, int a159, int a160, int a161, int a162, int a163, int a164, int a165, int a166, int a167, int a168, int a169, int a170, int a171, int a172, int a173, int a174, int a175, int a176, int a177, int a178, int a179, int a180, int a181, int a182, int a183, int a184, int a185, int a186, int a187, int a188, int a189, int a190, int a191, int a192, int a193, int a194, int a195, int a196, int a197, int a198, int a199, int a200, int a201, int a202, int a203, int a204, int a205, int a206, int a207, int a208, int a209, int a210, int a211, int a212, int a213, int a214, int a215, int a216, int a217, int a218, int a219, int a220, int a221, int a222, int a223, int a224, int a225, int a226, int a227, int a228, int a229, int a230, int a231, int a232, int a233, int a234, int a235, int a236, int a237, int a238, int a239, int a240, int a241, int a242, int a243, int a244, int a245, int a246, int a247, int a248, int a249, int a250, int a251, int a252, int a253, int a254, int a255) { System.out.println(1); } | ^----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------^ jshell> void f(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12, int a13, int a14, int a15, int a16, int a17, int a18, int a19, int a20, int a21, int a22, int a23, int a24, int a25, int a26, int a27, int a28, int a29, int a30, int a31, int a32, int a33, int a34, int a35, int a36, int a37, int a38, int a39, int a40, int a41, int a42, int a43, int a44, int a45, int a46, int a47, int a48, int a49, int a50, int a51, int a52, int a53, int a54, int a55, int a56, int a57, int a58, int a59, int a60, int a61, int a62, int a63, int a64, int a65, int a66, int a67, int a68, int a69, int a70, int a71, int a72, int a73, int a74, int a75, int a76, int a77, int a78, int a79, int a80, int a81, int a82, int a83, int a84, int a85, int a86, int a87, int a88, int a89, int a90, int a91, int a92, int a93, int a94, int a95, int a96, int a97, int a98, int a99, int a100, int a101, int a102, int a103, int a104, int a105, int a106, int a107, int a108, int a109, int a110, int a111, int a112, int a113, int a114, int a115, int a116, int a117, int a118, int a119, int a120, int a121, int a122, int a123, int a124, int a125, int a126, int a127, int a128, int a129, int a130, int a131, int a132, int a133, int a134, int a135, int a136, int a137, int a138, int a139, int a140, int a141, int a142, int a143, int a144, int a145, int a146, int a147, int a148, int a149, int a150, int a151, int a152, int a153, int a154, int a155, int a156, int a157, int a158, int a159, int a160, int a161, int a162, int a163, int a164, int a165, int a166, int a167, int a168, int a169, int a170, int a171, int a172, int a173, int a174, int a175, int a176, int a177, int a178, int a179, int a180, int a181, int a182, int a183, int a184, int a185, int a186, int a187, int a188, int a189, int a190, int a191, int a192, int a193, int a194, int a195, int a196, int a197, int a198, int a199, int a200, int a201, int a202, int a203, int a204, int a205, int a206, int a207, int a208, int a209, int a210, int a211, int a212, int a213, int a214, int a215, int a216, int a217, int a218, int a219, int a220, int a221, int a222, int a223, int a224, int a225, int a226, int a227, int a228, int a229, int a230, int a231, int a232, int a233, int a234, int a235, int a236, int a237, int a238, int a239, int a240, int a241, int a242, int a243, int a244, int a245, int a246, int a247, int a248, int a249, int a250, int a251, int a252, int a253, int a254) { System.out.println(1); } | created method f(int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int) jshell> f(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); 1 jshell>JavaScript
パラメータが256個以上でも関数定義できます・
function f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92, a93, a94, a95, a96, a97, a98, a99, a100, a101, a102, a103, a104, a105, a106, a107, a108, a109, a110, a111, a112, a113, a114, a115, a116, a117, a118, a119, a120, a121, a122, a123, a124, a125, a126, a127, a128, a129, a130, a131, a132, a133, a134, a135, a136, a137, a138, a139, a140, a141, a142, a143, a144, a145, a146, a147, a148, a149, a150, a151, a152, a153, a154, a155, a156, a157, a158, a159, a160, a161, a162, a163, a164, a165, a166, a167, a168, a169, a170, a171, a172, a173, a174, a175, a176, a177, a178, a179, a180, a181, a182, a183, a184, a185, a186, a187, a188, a189, a190, a191, a192, a193, a194, a195, a196, a197, a198, a199, a200, a201, a202, a203, a204, a205, a206, a207, a208, a209, a210, a211, a212, a213, a214, a215, a216, a217, a218, a219, a220, a221, a222, a223, a224, a225, a226, a227, a228, a229, a230, a231, a232, a233, a234, a235, a236, a237, a238, a239, a240, a241, a242, a243, a244, a245, a246, a247, a248, a249, a250, a251, a252, a253, a254, a255) { console.log(1); } f(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); 1
- 投稿日:2019-02-11T15:40:01+09:00
Railsチュートリアル2章 学習ログ
続きです
Railsチュートリアル1章 学習ログ
https://qiita.com/KoDoKu77/items/12c1e7197ba69044b1aaScaffoldでなんやかんや
micropostsモデルを作った後でビューを確認していたら、変なフィールドができてしまいました。
schema.rbcreate_table "microposts", force: :cascade do |t| t.text "content" t.string "user_id" t.string "integer" t.datetime "created_at", null: false t.datetime "updated_at", null: false endinteger型を指定したつもりが、intergerというカラムを作ってしまっています...
どうやら、scaffoldで間違った引数を与えてしまったようです。rails g scaffold Micropost content:text user_id: integer空白一つ開けちゃってますね...
マイグレーションファイルを作って、間違った部分を修正します。$ rails g migration change_microposts_column
xxxxx_change_microposts_column.rbclass ChangeMicropostsColumn < ActiveRecord::Migration[5.1] def change change_column(:microposts, :user_id, integer) remove_column(:microposts, :integer) end enduser_idカラムの型をinteger型へ変更し、
integerというカラムを削除します。$ rails db:migrate
マイグレーションを実行して、テーブルを変更します。
schema.rbcreate_table "microposts", force: :cascade do |t| t.text "content" t.integer "user_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false end上手くいきました。
その他、各ビューを修正。Herokuへデプロイ
- 投稿日:2019-02-11T14:44:38+09:00
[Devise] パスワードを入力せずにユーザー情報を編集する
はじめに
deviseを使ってユーザーの登録情報を編集する際にデフォルトのままだと
パスワードの入力を求められます。しかし、一々ユーザーにパスワードの入力を
求めるのはユーザーフレンドリーではないので、修正していきます。環境
ruby 2.5.3
rails 5.2.2
devise 4.6.0Deviseを使う準備
アプリの作成
rails new devisedeviseのインストール
GEMFILEgem 'devise'上記を追加したら
bundle install
します。
次に、deviseのファイルを生成します。rails generate devise:installすると、次のメッセージが表示されますのでとりあえず
下記のメッセージ1~4の通りにセットアップしてみましょう。Running via Spring preloader in process 3919 create config/initializers/devise.rb create config/locales/devise.en.yml =============================================================================== Some setup you must do manually if you haven't yet: 1. Ensure you have defined default url options in your environments files. Here is an example of default_url_options appropriate for a development environment in config/environments/development.rb: config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } In production, :host should be set to the actual host of your application. 2. Ensure you have defined root_url to *something* in your config/routes.rb. For example: root to: "home#index" 3. Ensure you have flash messages in app/views/layouts/application.html.erb. For example: <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> 4. You can copy Devise views (for customization) to your app by running: rails g devise:views ===============================================================================セットアップ
(1)
config/environments/development.rb# 追記する config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }(2)
localhost:3000/
にアクセスした時のページの設定を行います。
今のままだと何もページが作成されていないので、ここではPagesControllerを作ります。ターミナルrails generate controller Pages indexroutes.rb# get 'pages/index' root 'pages#index'これでrootパスの設定は完了です。
(3)
フラッシュメッセージを表示するようにviewを修正します。application.html.erb<body> <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> <%= yield %> </body>とりあえずこれでいいでしょう。
(4)
deviseのviewをカスタマイズするには以下のコマンドを実行して
viewファイルを作成する必要があります。rails generate devise:viewsRunning via Spring preloader in process 4256 invoke Devise::Generators::SharedViewsGenerator create app/views/devise/shared create app/views/devise/shared/_error_messages.html.erb create app/views/devise/shared/_links.html.erb invoke form_for create app/views/devise/confirmations create app/views/devise/confirmations/new.html.erb create app/views/devise/passwords create app/views/devise/passwords/edit.html.erb create app/views/devise/passwords/new.html.erb create app/views/devise/registrations create app/views/devise/registrations/edit.html.erb create app/views/devise/registrations/new.html.erb create app/views/devise/sessions create app/views/devise/sessions/new.html.erb create app/views/devise/unlocks create app/views/devise/unlocks/new.html.erb invoke erb create app/views/devise/mailer create app/views/devise/mailer/confirmation_instructions.html.erb create app/views/devise/mailer/email_changed.html.erb create app/views/devise/mailer/password_change.html.erb create app/views/devise/mailer/reset_password_instructions.html.erb create app/views/devise/mailer/unlock_instructions.html.erbこれでセットアップは完了です。
モデルの作成
登録情報を編集するユーザーを作成していきます。
まず、次のコマンドを実行します。rails generate devise UserRunning via Spring preloader in process 4296 invoke active_record create db/migrate/20190211043147_devise_create_users.rb create app/models/user.rb invoke test_unit create test/models/user_test.rb create test/fixtures/users.yml insert app/models/user.rb route devise_for :usersモデルとマイグレーションファイルは次のようになってます。
user.rbclass User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable endmigration# frozen_string_literal: true class DeviseCreateUsers < ActiveRecord::Migration[5.2] def change create_table :users do |t| ## Database authenticatable t.string :email, null: false, default: "" t.string :encrypted_password, null: false, default: "" ## Recoverable t.string :reset_password_token t.datetime :reset_password_sent_at ## Rememberable t.datetime :remember_created_at ## Trackable # t.integer :sign_in_count, default: 0, null: false # t.datetime :current_sign_in_at # t.datetime :last_sign_in_at # t.string :current_sign_in_ip # t.string :last_sign_in_ip ## Confirmable # t.string :confirmation_token # t.datetime :confirmed_at # t.datetime :confirmation_sent_at # t.string :unconfirmed_email # Only if using reconfirmable ## Lockable # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts # t.string :unlock_token # Only if unlock strategy is :email or :both # t.datetime :locked_at t.timestamps null: false end add_index :users, :email, unique: true add_index :users, :reset_password_token, unique: true # add_index :users, :confirmation_token, unique: true # add_index :users, :unlock_token, unique: true end endここでは、ユーザーのメールアドレスをパスワードの入力なしで、
変更することを目標とするので何も変更しないで、migrate
します。rails db:migrateユーザー情報を登録
まず、ユーザーを登録します。
登録画面にアクセスするためにルーティングを確認しましょう。routes.rbdevise_for :user上記がdeviseのルーティングを提供しています。
rails routesPrefix Verb URI Pattern Controller#Action new_user_session GET /users/sign_in(.:format) devise/sessions#new user_session POST /users/sign_in(.:format) devise/sessions#create destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy new_user_password GET /users/password/new(.:format) devise/passwords#new edit_user_password GET /users/password/edit(.:format) devise/passwords#edit user_password PATCH /users/password(.:format) devise/passwords#update PUT /users/password(.:format) devise/passwords#update POST /users/password(.:format) devise/passwords#create cancel_user_registration GET /users/cancel(.:format) devise/registrations#cancel new_user_registration GET /users/sign_up(.:format) devise/registrations#new edit_user_registration GET /users/edit(.:format) devise/registrations#edit user_registration PATCH /users(.:format) devise/registrations#update PUT /users(.:format) devise/registrations#update DELETE /users(.:format) devise/registrations#destroy POST /users(.:format) devise/registrations#create root GET / pages#index rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#createパスワードを入力せずにユーザー情報を編集する
まず、編集画面にアクセスして、パスワードを入力せずに更新すると
次のようなエラーメッセージが表示されるかと思います。Current password can't be blankこのpasswordのvalidationをスキップするためにdeviseをカスタマイズしていきます。
Controllerを生成する
deviseのcontrollerをカスタマイズするには、次のコマンドを実行して
自身のcontrollerを作成する必要があります。rails generate devise:controllers usersRunning via Spring preloader in process 4601 create app/controllers/users/confirmations_controller.rb create app/controllers/users/passwords_controller.rb create app/controllers/users/registrations_controller.rb create app/controllers/users/sessions_controller.rb create app/controllers/users/unlocks_controller.rb create app/controllers/users/omniauth_callbacks_controller.rb =============================================================================== Some setup you must do manually if you haven't yet: Ensure you have overridden routes for generated controllers in your routes.rb. For example: Rails.application.routes.draw do devise_for :users, controllers: { sessions: 'users/sessions' } end ===============================================================================routes.rbを修正する
先ほど生成したcontrollerを利用するため次のように修正してください。
routes.rbdevise_for :users, controllers: { registrations: 'users/registrations' }update_resourceメソッドをオーバーライドする
registrations_controller.rbclass RegistrationsController < Devise::RegistrationsController protected # 追記する def update_resource(resource, params) resource.update_without_password(params) end endcurrent_passwordフォームを削除する
views/devise/registrations/edit.html.erb<div class="field"> <%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br /> <%= f.password_field :current_password, autocomplete: "current-password" %> </div>こちらのフォームを削除しましょう。
編集してみる
http://localhost:3000/users/edit
にアクセスして
メールアドレスを変更してみてください。
すると次のフラッシュメッセージが表示されるはずです。Your account has been updated successfully.これで、パスワードを入力しなくてもユーザーの登録情報を
編集することが可能になりました!最後に
deviseはとても便利なのですが、カスタマイズするには色々修正が必要です。
いつかdevise大辞典作りたいな〜参考
- 投稿日:2019-02-11T14:25:19+09:00
RailsのActiveRecord::FinderMethodsのSQLクエリ発行の有無について調べる
環境
Ruby 2.5
Rails 5.2.1目的
Railsでアプリケーションを書く時、このメソッドはクエリを発行するかどうか、度々調べていて、ちゃんと覚えきれていないので、ちょっと調べようと思い、まとめてみることにしました。
ActiveRecord::FinderMethodsに絞ったのは読みやすそうだなと思ったからです。思ったより長くなり完全には調べきれるには気力を消耗しすぎたので、間違っている点があればご指摘頂けると幸いです。参考
ActiveRecord::FinderMethods
Ruby on Rails APIまとめ表
記事本文はかなり長くなったので結果だけ見たい方はこちら。
(例外発生時のクエリ有無は含んでいません。)
メソッド名 一般的用途でのクエリ発行 例外的な(一般的な用途でない)クエリ発行しない条件 take 下記の場合ではクエリ発行しない。
1. loadedなインスタンスに対する、take/take!呼び出し。
2. 一度引数なしのtake/take!を呼び出したインスタンスへの、二度目のtake/take!呼び出し。exist? 基本的にはクエリ発行。 下記の場合はクエリを発行しない。(いずれもfalseが返る)
1. 引数にfalseを渡した場合
2. limit(0)をチェーンしていた場合。find 基本的にはクエリ発行。 下記の場合はクエリ発行しない。
1. loadedなのインスタンスに対して、blockつきで呼び出した場合(Enumerableのfindが呼ばれる)
2. 引数の先頭に空配列を渡した場合。(空配列が返る)find_by/find_by! 基本的にはクエリ発行。 例外的に下記の場合はクエリ発行しない。
1. loadedなインスタンスに対して、blank?がtrueになる引数を渡した場合
(ex: users.find_by(nil))first/first! 下記の場合ではクエリ発行しない。
1. loadedなインスタンスに対する、first/first!呼び出し。
2. 一度引数なしのfirst/first!を呼び出したインスタンスへの、二度目のfirst/first!呼び出し。下記の場合はクエリを発行しない。
(引数なしの場合はnilが、ありの場合は空配列が返る)
1. limit(0)をチェーンしていた場合。second/second!
third/third!
fourth!/fourth
fifth/fifth!
forty_two/forty_two!下記の場合ではクエリ発行しない。
1. loadedなインスタンスに対する、メソッド呼び出し。
2. 一度メソッドを呼び出したインスタンスへの、二度目の同一メソッド呼び出し。下記の場合はクエリを発行しない。
1. 内部で呼び出されている数値以下の引数のlimitをチェーンしていた場合。
(ex: forty_twoであれば、41以下の引数をもつlimitをチェーンしていた場合)last/last! 下記の場合ではクエリ発行しない。
1. loadedなインスタンスに対する、メソッド呼び出し。second_to_last/second_to_last!
third_to_last/third_to_last!下記の場合ではクエリ発行しない。
1. loadedなインスタンスに対する、メソッド呼び出し。ActiveRecord::FinderMethodsにはどんなメソッドがあるのか?
まず
ActiveRecord::FinderMethods
にどんなメソッドがあるか調べていきます。rails console
で確認すると全部で25個のメソッドがあることがわかります。(読みやすいように並びを変更しています。)pry(main)> ActiveRecord::FinderMethods.instance_methods(false) => [:take, :take!, :exists?, :find, :find_by, :find_by!, :first, :first!, :second, :second!, :third, :third!, :fourth!, :fourth, :fifth, :fifth!, :forty_two, :forty_two!, :last, :last!, :second_to_last, :second_to_last!, :third_to_last, :third_to_last!, :raise_record_not_found_exception!] pry(main)> ActiveRecord::FinderMethods.instance_methods(false).count => 25
:raise_record_not_found_exception!
は例外をあげるメソッドなので、実質24個です。ActiveRecord::Relationの
loaded?
メソッド先のメソッドを一つずつ見ていこうと思うのですが、その前にActiveRecord::Relationの
loaded?
メソッドについて少し触れておきます。なぜこのメソッドに触れるのかというと、クエリ発行の有無に関わる場合が多いものだからです。
loaded?
メソッドはクエリ発行をし、オブジェクトを取得したかどうかを確認するメソッドです。
(参考:ActiveRecord::Relationとは一体なんなのか)
loaded?
メソッドの実態は、@loaded
インスタンスへのアクセサーのaliasで、@loaded
のBooleanを返すものとなっています。module ActiveRecord class Relation attr_reader :table, :klass, :loaded, :predicate_builder alias :loaded? :loaded end endそれでは
@loaded
にどのようにtrue/false
が設定されるかを見ていきます。まずinitialize
時には、false
がセットされます。def initialize(klass, table: klass.arel_table, predicate_builder: klass.predicate_builder, values: {}) ~中略~ @loaded = false ~中略~ endその後、
load
が呼ばれるとloaded
でない場合、内部的にはexec_queries
を呼び出しますが、この中で@loaded
にtrue
が代入されます。def load(&block) exec_queries(&block) unless loaded? self end private def exec_queries(&block) skip_query_cache_if_necessary do ~中略~ @loaded = true @records end endそのためloadedなものに対して、
load
をかけてもクエリは発行されません。一方、
reload
などが呼ばれると内部的には、reset
とload
が走りますが、reset
では@loaded
にnilが代入されるので、次のload
でクエリが走り、再度@loaded
にtrue
が入ります。def reload reset load end def reset ~中略~ @to_sql = @arel = @loaded = @should_eager_load = nil ~中略~ endこのようにクエリ実行すべきかどうかの確認を行なっているのが
loaded?
メソッドになります。それではこれから一つ一つメソッドを下記で見ていきます。
take/take!
take
及びtake!
メソッドは下記のようになっています。take!
はtake
がnil
の場合に例外をあげる以外、違いはありません。def take(limit = nil) limit ? find_take_with_limit(limit) : find_take end def take! take || raise_record_not_found_exception! end
take
は引数がある場合は、find_take_with_limit
を、ない場合はfind_take
を呼んでいます。これらはどのようになっているかというと下記になります。def find_take if loaded? records.first else @take ||= limit(1).records.first end end def find_take_with_limit(limit) if loaded? records.take(limit) else limit(limit).to_a end end早速
loaded?
が出てきましたね。loadedなインスタンスに対して、
・find_take
の場合は、recordsの最初のインスタンス
(records.first
はArray#first
なのでクエリ発行しない。)
・find_take_with_limit
の場合は、recordsの先頭から引数個分の配列
(同じくArray#take
)
を取り出すことがわかります。一方load済みでない場合は、
find_take
とfind_take_with_limit
で異なります。
find_take
の場合は、@take
でキャッシュしており、キャッシュがあればそれを、ない場合はlimit(1)
のクエリ発行することがわかります。
一方、find_take_with_limit
の場合はいずれの場合も毎回limit
を発行します。実際にテストデータで実験してみるとよくわかります。
pry(main)> users = User.where(created_at: 2.months.ago..1.months.ago); pry(main)> users.take; [DEBUG] SELECT `users`.* FROM `users` WHERE `users`.`deleted_at` IS NULL AND `users`.`created_at` BETWEEN '2018-12-10 15:04:02' AND '2019-01-10 15:04:02' LIMIT 1` pry(main)> users.take; pry(main)> users.take(2); [DEBUG] SELECT `users`.* FROM `users` WHERE `users`.`deleted_at` IS NULL AND `users`.`created_at` BETWEEN '2018-12-10 15:04:02' AND '2019-01-10 15:04:02' LIMIT 2` pry(main)> users.take(2); [DEBUG] SELECT `users`.* FROM `users` WHERE `users`.`deleted_at` IS NULL AND `users`.`created_at` BETWEEN '2018-12-10 15:04:02' AND '2019-01-10 15:04:02' LIMIT 2結果的にはクエリ発行されない条件は下記になります。
1. loadedなインスタンスに対する、take/take!呼び出し。 2. 一度引数なしのtake/take!を呼び出したインスタンスへの、二度目の引数なしのtake/take!呼び出し。exist?
exists?
メソッドは下記のようになっています。def exists?(conditions = :none) if Base === conditions raise ArgumentError, <<-MSG.squish You are passing an instance of ActiveRecord::Base to `exists?`. Please pass the id of the object by calling `.id`. MSG end return false if !conditions || limit_value == 0 if eager_loading? relation = apply_join_dependency(eager_loading: false) return relation.exists?(conditions) end relation = construct_relation_for_exists(conditions) skip_query_cache_if_necessary { connection.select_value(relation.arel, "#{name} Exists") } ? true : false rescue ::RangeError false end結構長いですが、自分なりに翻訳すると下記であろうと思います。
def exists?(conditions = :none) if Base === conditions # ActiveRecordのインスタンスが渡されたらエラーを吐く。 end # 条件にfalseもしくは、チェーンでlimit(0)を渡していた場合、falseが返る。 return false if !conditions || limit_value == 0 if eager_loading? # eager_loadしていた場合、eager_loadしたものに対して、exists?する。 # (eager_load先のものを条件にすることが可能。) end # relationを構築する。 relation = construct_relation_for_exists(conditions) # 必要であれば、クエリキャッシュをスキップする。(skip_query_cache_if_necessaryの内容をみる限り、block内は実行される。) skip_query_cache_if_necessary { connection.select_value(relation.arel, "#{name} Exists") } ? true : false rescue ::RangeError false endそのため、
exists?
において、例外が出る場合を除いて、クエリが発行されない条件は下記の二つになります。いずれも例外的で普段使わないと思うので、基本的にクエリ発行があると考える方が自然ですね。1. 引数にfalseを渡した場合 2. limit(0)をチェーンしていた場合。実際に実験してみると確かにSQLは発行されていません。
pry(main)> User.exists?; [DEBUG] SELECT 1 AS one FROM `users` WHERE `users`.`deleted_at` IS NULL LIMIT 1` pry(main)> User.exists?(false); pry(main)> User.limit(0).exists?; pry(main)> User.all.limit(1).exists?; [DEBUG] SELECT 1 AS one FROM `users` WHERE `users`.`deleted_at` IS NULL LIMIT 1find
find
メソッドは下記のようになっています。def find(*args) return super if block_given? find_with_ids(*args) endfindはこの部分は結構シンプルです。findはblockが渡された場合、Enumerableのfindとして動作します。その他の場合は、
find_with_ids
が呼ばれます。find_with_ids
は下記のようになっています。def find_with_ids(*ids) raise UnknownPrimaryKey.new(@klass) if primary_key.nil? expects_array = ids.first.kind_of?(Array) return [] if expects_array && ids.first.empty? ids = ids.flatten.compact.uniq model_name = @klass.name case ids.size when 0 error_message = "Couldn't find #{model_name} without an ID" raise RecordNotFound.new(error_message, model_name, primary_key) when 1 result = find_one(ids.first) expects_array ? [ result ] : result else find_some(ids) end rescue ::RangeError error_message = "Couldn't find #{model_name} with an out of range ID" raise RecordNotFound.new(error_message, model_name, primary_key, ids) endこれもざっくり自分なりに翻訳すると下記になります。
def find_with_ids(*ids) # primary_keyがnilの場合、例外をあげる。 # 渡されたidsのうち、先頭が配列かつ、空の場合に空配列を返す。 expects_array = ids.first.kind_of?(Array) return [] if expects_array && ids.first.empty? # 配列を整理 ids = ids.flatten.compact.uniq model_name = @klass.name case ids.size when 0 # 整理した配列の中身が空だった場合、(ex: User.find(nil, nil)を渡した場合)例外をあげる。 when 1 # find_oneを呼び、引数が配列だった場合はインスタンス一つの配列で(ex: User.find([2]))、 # 単数だった場合はインスタンスを返す。 result = find_one(ids.first) expects_array ? [ result ] : result else # find_someを呼ぶ。 find_some(ids) end rescue ::RangeError # RangeErrorをrescue end
find_one
では、中でprimary_keyを条件としたwhere
と引数なしのtake
が呼ばれており、条件ありのwhereをチェーンしているので、クエリが発行されます。
find_some
では、中でも同じく条件ありのwhere
とto_a
が呼ばれており、クエリが発行されます。
find_with_ids
ではメソッドの先頭部分で、渡されたidsのうち、先頭が配列かつ、空の場合に空配列を返す
ようになっています。そのためこの場合はクエリ発行前に空配列が返るので、クエリは発行されません。実際に実験してみると確認できます。pry(main)> User.find([1],2,3,4); [DEBUG] User Load (3.6ms) SELECT `users`.* FROM `users` WHERE `users`.`deleted_at` IS NULL AND `users`.`id` IN (1, 2, 3, 4)` pry(main)> User.find([],2,3,4); pry(main)> User.find([],2,3,4) => []結論として、
find
において、例外が出る場合を除いて、クエリが発行されない条件は下記になります。1. loadedなインスタンスに対して、blockつきで呼び出した場合(Enumerableのfind) 2. 引数の先頭に空配列を渡した場合。(空配列が返る)find_by/find_by!
find_by
及びfind_by!
メソッドは下記のようになっています。find_by
とfind_by!
の違いは例外時を除くとtake
かtake!
かだけですね。def find_by(arg, *args) where(arg, *args).take rescue ::RangeError nil end def find_by!(arg, *args) where(arg, *args).take! rescue ::RangeError raise RecordNotFound.new("Couldn't find #{@klass.name} with an out of range value", @klass.name, @klass.primary_key) endこの場合は
where
に対してtake
をチェーンしているので、基本的にはクエリが発行されます。しかし、where
とtake
の性質からある場合についてはクエリが発行されません。
take
については既に見たので、where
について見てみましょう。where
は下記のようになっています。def where(opts = :chain, *rest) if :chain == opts WhereChain.new(spawn) elsif opts.blank? self else spawn.where!(opts, *rest) end end
where
は結構複雑なので深入りはしませんが、一点着目して欲しいポイントがあります。それはopts.blank?
の場合、self
を返すということです。ここではself
を返すだけなのでもちろんクエリ発行はしません。そして、take
はロード済みである場合はクエリ発行しないのでした。つまり、load済みのものに対して、
find_by
の引数にblank?
なものを渡せばクエリ発行はされません。実際に実験したのが下記です。pry(main)> users = User.where(created_at: 2.months.ago..1.months.ago).load; pry(main)> users.find_by(nil); pry(main)> users.find_by([]);使い道は全くないですが、面白いです。
find_by/find_by!
の場合、クエリが発行されない条件は下記です。1. loadedなインスタンスに対して、blank?がtrueになる引数を渡した場合(users.find_by(nil)など)first/first!
さて番号系メソッドの
first/first!
です。他の番号系メソッドと違い、first/first!
だけ引数に数値を取れます。内容的には下記のようになります。
first!
はfirst
がnil
の場合に例外をあげるだけですね。def first(limit = nil) if limit find_nth_with_limit(0, limit) else find_nth 0 end end def first! first || raise_record_not_found_exception! end
first
は引数がある場合とない場合で呼び出すメソッドが異なっています。それらのメソッドは下記になります。def find_nth(index) @offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first end def find_nth_with_limit(index, limit) if loaded? records[index, limit] || [] else relation = ordered_relation if limit_value limit = [limit_value - index, limit].min end if limit > 0 relation = relation.offset(offset_index + index) unless index.zero? relation.limit(limit).to_a else [] end end end
find_nth
はfind_nth_with_limit
を使用しており、なおかつ結果をインスタンス変数に格納していますね。find_nth_with_limit
を自分なりに翻訳すると下記になります。def find_nth_with_limit(index, limit) if loaded? # loadedの場合、recordsから特定のindexから特定数取り出す。nilの場合は空配列。 else relation = ordered_relation if limit_value # limitメソッドを間に挟んでいた場合、limitメソッドの引数からindexを引いたものとfirstの引数の小さい方をlimitとして取る。 end if limit > 0 # offset_indexとindex数を足して、offsetする relation = relation.offset(offset_index + index) unless index.zero? relation.limit(limit).to_a else # limitが0以下の場合は空配列を返す end end end上記からfirstのクエリ発行しない条件は下記になります。
1. loadedなインスタンスに対する、first/first!呼び出し。 2. 一度引数なしのfirst/first!を呼び出したインスタンスへの、二度目の引数なしのfirst/first!呼び出し。 3. limit(0)をチェーンしていた場合。second/second!/third/third!/fourth!/fourth/fifth/fifth!/forty_two/forty_two!
さて、first以外の番号系のメソッドですね。
forty_two
だけ少し謎いですね…
(今回初めて知った)こちらは基本的にはfirstと同じですが、引数は取れません。これらは内部的なメソッド呼び出しの引数が異なる以外は同じなので、サンプルにforty_two/forty_two!をみてみましょう。
def forty_two find_nth 41 end def forty_two! forty_two || raise_record_not_found_exception! endやっていることは
find_nth
に特定番号の引数を渡しているだけですね。ほとんとfirst
と同じです。そのため、クエリ発行しない条件は下記になります。1. loadedなインスタンスに対する、メソッド呼び出し。 2. 一度メソッドを呼び出したインスタンスへの、二度目の同一メソッド呼び出し。 3. 内部で呼び出されている数値以下の引数のlimitをチェーンしていた場合。 (ex: forty_twoであれば、41以下の引数をもつlimitをチェーンしていた場合)last/last!
残りも少なくなってきました。お次は
last/last!
です。メソッド内容は下記になります。last!
はnil
の場合に例外をあげるだけですね。def last(limit = nil) return find_last(limit) if loaded? || has_limit_or_offset? result = ordered_relation.limit(limit) result = result.reverse_order! limit ? result.reverse : result.first end def last! last || raise_record_not_found_exception! end
last
はloadedもしくはlimitかoffsetがある場合と、そうでない場合で挙動が異なります。まず、そうでない場合をみていきましょう。そうでない場合は内容的にはそれほど難しくなさそうですね。
last
に引数がある場合、引数をlimit数としてセットし、reverse_order
で内容を取得します。そして、引数なしの場合は、一つ目を、ありの場合は、reverse_orderで取得してきた内容をわざわざreverse
メソッドで入れ替えています。おかげで、私たちはlast(2)
と呼び出すと、最後から二つを順番通り(逆順ではなく)得ることができるということですね。そのためこの場合はクエリ発行されそうです。続いて、loadedもしくは
limit
かoffset
がある場合を見ていきます。この場合は、find_last
メソッドを呼び出しています。def find_last(limit) limit ? records.last(limit) : records.last endたったこれだけです。超シンプルです。さて、loadedの場合は
Array#last
が呼ばれているのでクエリ発行はやはりされません。それではlimit
やoffset
の場合はどうでしょうか?。この場合は、要するにクエリ発行の際に
DESC
で取ったりせずに単純にoffset
やlimit
で取ってきて、それを後ろから特定数取るということをしているだけなのです。実際のクエリを見るとわかりやすいです。[70] pry(main)> User.last; [DEBUG] SELECT `users`.* FROM `users` WHERE `users`.`deleted_at` IS NULL ORDER BY `users`.`id` DESC LIMIT 1` [71] pry(main)> User.offset(2).limit(3).last; [DEBUG] SELECT `users`.* FROM `users` WHERE `users`.`deleted_at` IS NULL LIMIT 3 OFFSET 2
offset
とlimit
を使っている場合は、DESC
が入っていませんよね。そのため、limit
やoffset
がある場合はクエリが発行されます。これらを踏まえるとクエリを発行しない条件は下記になります。1. loadedなインスタンスに対する、メソッド呼び出し。
first
と違ってlast
の場合は、インスタンス変数を保持していないので、二度目の呼び出しでもクエリは発行されますし、limit(0)
をつけてもクエリ発行されます。second_to_last/second_to_last!/third_to_last/third_to_last!
ようやく最後のメソッドです。最後まで見てくださって頂き誠にありがとうございます。私もこの時点で既に記事を書き始めて調査含め、6時間が経過しておりそろそろ終わりたいです。笑
メソッド内容を見てみましょう。
second_to_last/second_to_last!
とthird_to_last/third_to_last!
は内部的には引数が異なるだけなので、second_to_last/second_to_last!
を見ていきます。def second_to_last find_nth_from_last 2 end def second_to_last! second_to_last || raise_record_not_found_exception! endおなじみの
second_to_last!
の場合は、second_to_last
がnil
の場合に例外をあげるだけですね。それでは実態のメソッドのfind_nth_from_last
を見ていきます。def find_nth_from_last(index) if loaded? records[-index] else relation = ordered_relation if equal?(relation) || has_limit_or_offset? relation.records[-index] else relation.last(index)[-index] end end endloadedの場合はおなじみですね。普通に
records
から特定インデックスの場所を取り出すだけですね。そのためクエリ発行はされません。問題は、loadedでない場合です、
if
の部分が少しややこしいですが、これは要するに、現状保持しているrelation
の状態がordered_relation
と同じかどうか判定しています。ordered_ralation
では、order
の指定がなく、primary_key
が存在する場合は、primary_key
のasc
でorder
するように指定しています。それ以外の場合はインスタンスをそのまま返しています。def ordered_relation if order_values.empty? && primary_key order(arel_attribute(primary_key).asc) else self end endすなわち先の
if
の戻ると、何かしらのorder
をしているもしくは、limit
やoffset
をしている場合は、そのorder
やlimit/offset
でクエリ発行をし、そこから特定インデックスの場所を取るということをしており、逆に指定がない場合は、primary_key
のasc
でレコードを引いてきて、それに対して、特定インデックス分の引数を与えたlast
メソッドを呼び出して、そこからさらに特定インデックスで取り出すということをしています。relation = ordered_relation if equal?(relation) || has_limit_or_offset? relation.records[-index] else relation.last(index)[-index] end実際にクエリを見ると、
order
なしの場合は、特定インデックス分のlimit
が入っており、order
がありlimit
がない場合は一旦全て引いてきていることがわかります。pry(main)> User.second_to_last; [DEBUG] SELECT `users`.* FROM `users` WHERE `users`.`deleted_at` IS NULL ORDER BY `users`.`id` DESC LIMIT 2` pry(main)> User.order(id: :desc).second_to_last; [DEBUG] SELECT `users`.* FROM `users` WHERE `users`.`deleted_at` IS NULL ORDER BY `users`.`id` DESC全レコードを取ってきているため、レコード数が多いテーブルに対して、気軽に引くと痛い目にあいます。
last
はきちんとlimit
をつけてくれるのですが、second_to_last
等を使う場合は要注意です。結果として、クエリを発行しない条件は下記になります。
1. loadedなインスタンスに対する、メソッド呼び出し。
last
と同じですね。最後に
以上で終わりです。
想像以上にActiveRecordは奥深く、各メソッドの内部で呼び出されているメソッドは全然調べきれませんでした…
もし異なる点や実験してみたら違う結果が出たり他にクエリ発行しない条件があれば、ご指摘頂けると幸いです。
最後までお読み頂きありがとうございました。
- 投稿日:2019-02-11T13:44:51+09:00
Ruby にも関数言語ではやりの |> と <| がほしいよ!
class Object define_method :'|>' do |*args| send *args end define_method :'<|' do |arg1, *args| arg1.send self, *args end end # 本当は 'abc' |> upcase と書きたい p 'abc'.method('|>').call :upcase # => "ABC" p :downcase.method('<|').call 'XYZ' # => "xyz"Ruby は演算子の新規作成が無理なんで、こんなまどろっこしい方法で呼び出している。確か Jekyll の Liquid ってシステムでパイプを使ったリダイレクト?を実現していた記憶があるが、まり使い勝手が良いとはいえなかった。それに Liquid のやり方を Object で実装すると、SOLID の O と L であるオープン・クローズドの原則とリスコフの置換原則を破るので、あんまり行儀の良い方法ではないとは思うしね。
えー、そんなことはともかくそれで、次期 Ruby には搭載されてもいいと思うのですが、コア開発の皆様、2.6 で関数合成を導入したのだから、ここはひとつ、2.7 では |>, <| があると、クリスマスが楽しみになるんで、よろしくおねがいいたします。
かしこ
- 投稿日:2019-02-11T12:21:26+09:00
Windows10からRuby環境をアンインストールするメモ
環境
- Windows 10 Home (ver.1803)
- RubyInstaller2を使ってRubyをインストール済み
Rubyのアンインストール
Windowsの
設定
→アプリ
→アプリと機能
から「ruby」で検索。
バージョンは異なるかもしれませんが以下のようなアプリが見つかります。クリックしてアンインストールを選びます。
インストール先のディレクトリを削除
デフォルトではCドライブの直下に以下のような
Ruby ***
というディレクトリがあるので、これを削除します。
しかし、Rubyアンインストール直後だと「他のプロセスによって開かれています」みたいなメッセージが出て削除できないことがあるので、一度ログオフするか再起動するかしてから削除します。これでRuby環境のアンインストールが完了です。
- 投稿日:2019-02-11T11:52:08+09:00
【Ruby】変数を文字列に入れ込む
変数に文字列を入れ込む
Ruby初心者のメモ書きです。
qiita.rbname = "田中" name2 = "斎藤" puts "こんにちは、#{name}さん!" puts 'こんにちは、#{name2}さん!' #結果 こんにちは、田中さん! こんにちは、#{name}さん!シングルクオートで囲むと、変数は展開されずにそのまま出力される!!
- 投稿日:2019-02-11T09:49:03+09:00
PostScriptをテキスト編集して圧縮画像を埋め込み
高機能な画像編集ソフトの使い方ではなく、自力でPostScriptを編集してJPEGやPNGを埋め込む方法のメモ。
※数年前に書いたまま放置していたが、平成が終わる前に公開することにした。コードが拙い…。
したい理由
- 手元のPostScriptの図に画像を追加して重ねたい
- 図を描いたライブラリ1は外部画像の埋め込みに非対応(元々そういう用途でないので)
- 汎用ソフトで編集すると大幅に書き換えられ、ライブラリ付属の編集ツールが一切使えなくなる
- というより自分自身がPostScriptを理解できなくなる
- 非圧縮のビットマップ画像ならライブラリを模倣して埋め込めるが、そのために圧縮画像を展開するのは気が進まない
- ファイルサイズが無駄に増えてしまう
- 元の形に再圧縮するのはたぶんどのソフトでも無理
要するに、素材本来の情報をなるべく維持して画像を追加したい。
例:上の図は平成26年豪雪のときの衛星雲画像2(JPEG)と海面気圧3・海岸線・経緯線(PostScript)を重ねたもの。海岸線がきれいに一致しているので、正しく重ねられていそう。
考え方
PostScriptはデータをエンコード・デコードするフィルターを複数持っている。これを利用して、「ASCII化したデータ」(PSファイルに貼り付け)→「圧縮画像本来のバイナリデータ」(元画像から抽出したもの)→「展開した画像データ」(ビットマップに近いもの)とデコードさせる。
- 画像
- DCTDecode : JPEGデータの展開
- FlateDecode : PNGなど、deflateで圧縮されたデータの展開
- RLEDecode : 連長圧縮データの展開
- ASCII化データの読み込み
- Ascii85Decode : 4バイトのデータ(=256進数4桁)を
"!-u"
の範囲の文字5つ(=85進数5桁)で表す。約1.25倍に増加- AsciiHexDecode : 16進数の文字列で表す。約2倍に増加
データを展開し終わったら、それを適切な画像として解釈するための情報が必要になる。これを事前に画像ファイルから抜き出すことになる。
- 色空間 : グレースケール・RGB・インデックスカラーなど
- 色深度 : 普通は8ビット、インデックスカラーでは4, 2, 1も
- 画像の幅と高さ
PostScriptはラスタ画像を描画する際、設定された座標上の1x1の領域に描こうとする。そのため、まともな大きさ・縦横比の画像を表示するには事前に座標変換を指定しておく必要がある。
サンプルコード
EPSに変換するコード。
gsave
~grestore
をコピペして座標変換をうまく設定すれば、PostScriptに画像を重ねることができる。raster2ps.rbclass Raster2PS # 画像ファイルを指定してインスタンスを作成する # (必要なメソッドを実装したサブクラスを用いること) def initialize(filename) raise 'Not implemented' if self.instance_of?(Raster2PS) @filename = filename read_image end # 画像を埋め込んだPostScriptをファイルへ書き出す def write_image(filename) File.open(filename, 'w') do |f| f.puts <<-EOS %!PS-Adobe-3.0 EPSF-3.0 %%Title: #{@filename} %%Creator: raster2ps.rb %%CreationDate: #{Time.now} %%BoundingBox: 0 0 #{@width} #{@height} %%DocumentData: Clean7Bit %%LanguageLevel: 3 %%EndComments %%BeginProlog %%EndProlog %%Page: 1 1 gsave 0 0 translate 0 rotate #{@width} #{@height} scale #{colorspace} setcolorspace /Data currentfile /ASCII85Decode filter #{image_filter} def << /ImageType 1 /Width #{@width} /Height #{@height} /ImageMatrix [#{@width} 0 0 #{-@height} 0 #{@height}] /DataSource Data /BitsPerComponent #{@depth} /Decode [#{decode.join(" ")}] /Interpolate true >> image #{self.class.ascii85_encode(@data)} grestore %%EOF EOS end end private # @filenameを読み込み、@width, @height, @depth, @data, (適宜その他)へ保存する def read_image; end # PostScriptに埋め込む文字列を組み立てて返す def colorspace; end def image_filter; end def decode; end # バイナリ文字列をAdobe Ascii85形式の7bit文字列へエンコードする def self.ascii85_encode(str) m = (-str.length) % 4 tuples = (str + "\x00" * m).unpack('N*') tuples.collect! do |tuple| 5.times.inject([]) do |ary,i| tuple, mod = tuple.divmod(85) ary.unshift(mod + 0x21) end end tuples.flatten! tuples.pop(m) str = tuples.pack('C*') str.gsub!(/.{1,5}/) { |s| s == '!!!!!' ? 'z' : s } str.scan(/.{1,80}/).unshift('<~').push('~>').join("\n") end endjpeg2ps.rbrequire './raster2ps' class JPEG2PS < Raster2PS def read_image @data = File.read(@filename, mode: 'rb') raise 'Invalid JPEG' if @data[0,4] != "\xff\xd8\xff\xe0" n = @data.length i = 2 while i < n mark, size = @data[i,4].unpack('a2n') if mark == "\xff\xc0" # SOF0 @height, @width, @type = @data[i+4,size-2].unpack('xnnc') break end i += 2 + size end @depth = 8 self end def colorspace case @type when 1 then '/DeviceGray' when 3 then '/DeviceRGB' when 4 then '/DeviceCMYK' else raise "unknown JPEG type: #{@type}" end end def image_filter '/DCTDecode' end def decode [0, 1] * @type end endpng2ps.rbrequire './raster2ps' class PNG2PS < Raster2PS def read_image @data = '' File.open(@filename, 'rb') do |f| raise 'Invalid PNG' if f.read(8) != "\x89PNG\r\n\x1a\n" until f.eof? size, name = f.read(8).unpack('Na4') bin = f.read(size) crc = f.read(4) case name when 'IHDR' @width, @height, @depth, @type, = bin.unpack('N2C5') when 'PLTE' @plte = bin when 'IDAT' @data << bin end end end self end def colorspace case @type when 0 then '/DeviceGray' when 2 then '/DeviceRGB' when 3 then <<-EOS /Palette currentfile #{@plte.length} string readhexstring #{@plte.pack('H*').gsub(/.{1,6}/, '#\0 ').scan(/.{1,64}/).join("\n")} pop def [/Indexed /DeviceRGB #{@plte.length / 3 - 1} Palette] EOS else raise "unknown PNG type: #{@type}" end end def image_filter <<-EOS << /Predictor 10 /Columns #{@width} /Colors #{decode.length / 2} /BitsPerComponent #{@depth} >> /FlateDecode EOS end def decode case @type when 0 then [0, 1] when 2 then [0, 1] * 3 when 3 then [0, 2 ** @depth - 1] else raise "unknown PNG type: #{@type}" end end end補足
サンプルコードを読めば分かるかもしれないが、細かい点はこちらにメモ。
画像の読み込み
幅と高さ・色深度・色数などの情報および貼り付けるデータ本体を画像ファイルから取り出す。
JPEG
- ファイルは
"\xff\xd8\xff\xe0"
で始まる- 各種ヘッダは、マーカー(2バイト)・サイズ(2バイト)・データ(n-2バイト)という構造
- SOF0(マーカー
"\xff\xc0"
)の中に、画像サイズや色の種類の情報がある- PSに貼り付けるデータはファイル全体でよい。本体を抽出する必要なし
PNG
- ファイルは
"\x89PNG\r\n\x1a\n"
で始まる- 各チャンクは、サイズ(4バイト)・名前(4バイト)・データ(nバイト)・CRC(4バイト)という構造
- IHDRの中に、画像サイズや色深度や色の種類の情報がある
- PLTEはカラーパレットなので、あれば抜き出しておく
- IDATは画像本体
- PSに貼り付けるデータはIDATの中身。大きなデータは分割されているので、全て抜き出して連結する必要がある
画像設定
色空間
- 基本的な場合は色の次元で決まり、
/DeviceGray
,/DeviceRGB
のように名前だけ指定すればいい- インデックスカラーの場合は複雑で、
[/Indexed 色空間名 インデックス最大値(=色数-1) パレットデータ]
と配列で与えるフィルター
- JPEGは
/DCTDecode
だけで全て処理してくれる- PNGは汎用のZlibを利用しているので、画像のデコードであることを辞書で教える必要がある
/Decode
- 画素の数値がとる範囲を指定する
- 通常は0~1。次元の数だけ必要なので、RGBなら
[0 1 0 1 0 1]
と6要素の配列になる- インデックスの場合は色数ではなく、色深度によって決まる。例えば4ビットなら0~15
参考
地球流体電脳ライブラリ(DCL) v5.3.4.2 ※v6から出力形式が変更された ↩
高知大学気象情報頁より ↩
MERRA再解析データより ↩
- 投稿日:2019-02-11T03:10:44+09:00
pigpioのrubyバインディングを作った。
置き場
https://github.com/nak1114/ruby-extension-pigpio
ドキュメント
https://nak1114.github.io/ruby-extension-pigpio/Pigpio/IF.html
はい。FileとThread以外の関数をバインドしたところで力尽きました。
今後は暇を見てはrubyらしい使い方が出来るようにラッピングクラス(Pigpio)を作っていきます。(Pigpioって打ちにくいですよね。)
後、Rspecも書かないといけないです。ダミーのpigpioライブラリを作ってラズパイが無くてもRspec出来るように環境を整えたので、あとは120個近い関数のテストを書くだけ。きっかけ
rubyの拡張ライブラリの書き方を勉強する必要があったので、どうせならばと実用性のあるブツで勉強しようとつくりました。
おかげさまで、ほぼほぼ習得できたと思います。なので私の習得方法を書いてみたいと思います。習得方法
拡張ライブラリ作成に必要な事は全て extension_ja に書いてありますが、何も知らない人がここを見ても理解できないと思います。
なので、理解できないなりに流し読みした後、適当にググったサイトを2つ、3つ写経して2時間くらいすると悟りがひらけます。悟りの中身は「ruby本体のCソースを読むべし」です。
悟りがひらけるころにはextension_jaを読んで中身を理解することができるので、これを読みながら自分なりに小さなライブラリを作るのを4時間くらい。
だいたい5,6時間もあれば拡張ライブラリをマスターできると思います。習得できなかったこと
GCのトリガ
結局
ruby.h
に記載されたどの関数がGCトリガになるのか、最後まで不明でした。深くソースを追うのはちょっと躊躇われるので、ここは雰囲気で作ってます。多分、RB_GC_GUARD
は多めに入っていると思います。Moduleの特異メソッドの第一引数にVALUE selfが必要な理由
クラスメソッドはincludeでクラスのクラスメソッドになるからselfが必要と納得。
特異メソッドはどこにもくっつかないので、selfが参照されることは無い・・・はず。
これは多分全てをオブジェクトとして扱えて言語仕様が美しいという美的感覚かな?。さいごに
当初の目的の勉強は達成したので、pigpioのバインディングの方はあまり進捗がなくなります。
なので、使いたいという人は自由に改変してください。PRくれたらコミットします。
- 投稿日:2019-02-11T03:06:18+09:00
config.i18n.fallbacksのメッセージについて
rails 5.2.2をインストールしたら標準出力の最後に以下のメッセージが表示された。
HEADS UP! i18n 1.1 changed fallbacks to exclude default locale. But that may break your application. Please check your Rails app for 'config.i18n.fallbacks = true'. If you're using I18n (>= 1.1.0) and Rails (< 5.2.2), this should be 'config.i18n.fallbacks = [I18n.default_locale]'. If not, fallbacks will be broken in your app by I18n 1.1.x. For more info see: https://github.com/svenfuchs/i18n/releases/tag/v1.1.0最近i18nがバージョンアップしたから表示されるようになったのかなと思いきや、現在のバージョンは1.5.3だった。今までずっとメッセージが表示されていたのかな・・・ずっと無視してた・・・。まあ、日本オンリーの私には関係なさそうだし。。。
訳してみる。
HEADS UP! i18n 1.1 changed fallbacks to exclude default locale. But that may break your application.「注意してね。i18n 1.1でデフォルトロケールを除外するようにフォールバックを変更したよ。」
「だけどその変更が君のアプリをエラーで落とすかもしれないよ。」うーん。意味がわからない。「除外する」とか、よくわからない。「But」も逆接で訳すと何かおかしい感じ。
とにかく、危ない問題ということだけはわかった。Please check your Rails app for 'config.i18n.fallbacks = true'. If you're using I18n (>= 1.1.0) and Rails (< 5.2.2), this should be 'config.i18n.fallbacks = [I18n.default_locale]'. If not, fallbacks will be broken in your app by I18n 1.1.x.「君のRailsアプリの'config.i18n.fallbacks = true'という設定をチェックしてね。」
「もしI18n (>= 1.1.0) かつ Rails (< 5.2.2) を使っているなら、'config.i18n.fallbacks = [I18n.default_locale]'という設定に変更してね。」
「もし使っていないのなら、君のアプリのフォールバックはI18n 1.1.x(1.1.0以上という意味かな)によってエラーになるよ。」うーむ。このように訳すと、今回のようにI18n 1.5.3 (>= 1.1.0) かつ Rails 5.2.2 (>= 5.2.2)を使った場合は設定変更するという手段は取れず、エラーを回避できないってことなのかな。それとも5.2.2以上は問題ないのかな。
今まで日本語サイトしか作ってこなかったし、今後も作る予定もないから、個人的にはどうでもいい問題ではある。。。
For more info see: https://github.com/svenfuchs/i18n/releases/tag/v1.1.0「詳細はこのURLを見てね」
実際に見て見ると、タイトルは以下となっていた。v1.1.0 BREAKING CHANGE: Fallbacksv1.1.0 のリリースノートなのかな。「フォールバックについて、互換性のない変更がある」ということかな。書いてあることはgemインストール時に表示されたものとほぼ同じっぽい。
結局、意味がわからなかったので、RailsGuide 5.2.2 を見てみる。すると、'config.i18n.fallbacks'の書き方として3つあるようだ。
# You can set the option to true for using default locale as fallback, like so: config.i18n.fallbacks = true # Or you can set an array of locales as fallback, like so: config.i18n.fallbacks = [:tr, :en] # Or you can set different fallbacks for locales individually. For example, if you want to use :tr for :az and :de, :en for :da as fallbacks, you can do it, like so: config.i18n.fallbacks = { az: :tr, da: [:de, :en] } # or config.i18n.fallbacks.map = { az: :tr, da: [:de, :en] }なるほど。I18n (>= 1.1.0)からは1つ目の書き方ができないってことかな。だから2つ目の書き方をしろと。あーそういうことか。以下のメッセージの意味がわかった。。。(再掲)
HEADS UP! i18n 1.1 changed fallbacks to exclude default locale.「注意してね。i18n 1.1で、デフォルトロケールを設定するという設定は除外されるようにフォールバックを変更したよ。」
でも、結論として、Rails 5.2.2からは問題なく使えるんだよね? Rails 5.2.2で作成した場合でも、デフォルトで以下のように設定されているから。
72 # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 73 # the I18n.default_locale when a translation cannot be found). 74 config.i18n.fallbacks = trueということで、あとで実機確認をしなくては。これまでは全て机上です。
- 投稿日:2019-02-11T01:58:34+09:00
【Python】「マネーフォワード社内PRに見られるRubyの書き方について (3) 」をPythonに置き換えて考えてみた
マネーフォワード社内PRに見られるRubyの書き方について (3) | Money Forward Engineers' Blogの記事はRubyだけどPythonにも適用できそうだから、考えてみた。
また、Rubyは一切やったことがないため、解釈が間違っているかもしれません。もし間違っていたら、教えていただけると嬉しいです。自分が気になったところだけ書いたので、書いていない部分は記事を見てください。
動作環境
Python 3.7.1文字列の生成や検証
必要もないのに正規表現を使ってやろうとしていたり、特定のメソッドに固執してそれを乱用しているということに集約される
...
正規表現の乱用の例とメソッド別の不的確な用例を挙げていきます。正規表現の乱用 (単一の文字列を表している)
「単一の文字列」とのマッチは
re.split()
(正規表現での分割)ではなく、str.split()
(文字列での分割)を使う>>> s = '1, 2, 3, 4' # No: >>> re.split(r',', s) ['1', ' 2', ' 3', ' 4'] # Yes: >>> s.split(',') ['1', ' 2', ' 3', ' 4']また、Rubyの場合
String#split
に正規表現を渡すことができるが、Pythonのstr.split()
に正規表現は渡せないため、re.split()
を使う
str.find()
やre.search()
を使わなくてよいところは、極力使わない正規表現でのマッチの場合、
re.match()
だと先頭にしかマッチしないため、re.search()
を使うこと文字列内に文字が含まれているかは
in
演算子を使う
- 文字列内に、ある文字(リテラル)が含まれているかのチェックは、
in
演算子を使う- 文字列内に、あるパターンが含まれているかのチェックは、
re.search()
を使う文字列内に、ある文字が含まれているかの真偽値が欲しい場合、
str.find()
(マッチした文字の位置を返すメソッド)ではなく、obj in str
(含まれているかどうかを返すin
演算子)を使う。ドキュメントにも書いてあった
>>> s = 'Hello world!' # No: >>> s.find('H') != -1 True # Yes: >>> 'H' in s Trueパターンの場合、
re.search()
しかないため何も考えずにre.search()
を使う# Yes: >>> import re >>> re.search(r'[hH]ello', s) <re.Match object; span=(0, 5), match='hello'>完全一致しているかどうかは
==
を使う
==
を使えば一発で判断できる
わざわざ、^str$
のように正規表現を使わなくても良い# No: >>> re.search(r'^hoge$', 'hoge') <re.Match object; span=(0, 4), match='hoge'> # Yes: >>> 'hoge' == 'hoge' True先頭や末尾での一致はstrの関数を使う
先頭や末尾での一致は正規表現ではなく、
str.startswith()
やstr.endswith()
を使う# No: >>> re.match(r'^sample', s) is not None True >>> re.search(r'.py$', s) is not None True # Yes: >>> s.startswith('sample') True >>> s.endswith('.py') True
str.split()
とre.split()
の乱用区切り文字が、文字列(リテラル)の場合、
str.split()
を使い、パターンの場合、re.split()
を使う文字や行への分解
str.split()
が多く使われれる処理
- (文字への分割)
- 行への分割
Pythonでは、
list()
に文字列を渡すことで簡単に文字への分割ができる>>> s = 'abc\ndef\n' # Yes: >>> list(s) ['a', 'b', 'c', '\n', 'd', 'e', 'f', '\n']行への分割をするには、
str.splitlines()
を使う>>> s = 'abc\ndef\n' # No: >>> s.split('\n') ['abc', 'def', ''] # Yes: >>> s.splitlines() ['abc', 'def'] # また、改行を残したい場合、`keepends`引数に`True`を渡す >>> s.splitlines(keepends=True) ['abc\n', 'def\n']無駄な分割をしないようにする
もし、分割した要素の「最初」と「最後」のみ取得したい場合、「最初」と「最後」の要素さえ分割できればいいため、すべてを分割する必要はない
# No: >>> s = 'foo##bar##baz##qux' >>> s.split('##') ['foo', 'bar', 'baz', 'qux'] # 最初の要素 >>> s.split('##')[0] 'foo' # 最後の要素 >>> s.split('##')[-1] 'qux'
'bar'
(2つ目の要素)と'baz'
(3つ目の要素)は一切使わないため、要素としてはいらない無駄がないように処理をするには、2つの方法がある。
str.split()
(とstr.rsplit()
)の第2引数で分割を行う回数を指定するstr.partition()
(とstr.rpartition()
)を使い、最低限の分割しか行わないようにする1つ目の方法の「
str.split()
(とstr.rsplit()
)の第2引数で分割を行う回数を指定する」でやってみる# Yes: # 最初の要素 # 第2引数で、分割回数を1回に指定している >>> s.split('##', 1) ['foo', 'bar##baz##qux'] >>> s.split('##', 1)[0] 'foo' # 最後の要素 >>> s.rsplit('##', 1) ['foo##bar##baz', 'qux'] >>> s.rsplit('##', 1)[-1] 'qux'次に2つ目の方法の「
str.partition()
(とstr.rpartition()
)を使い、最低限の分割しか行わないようにする」でやってみる# Yes: # first >>> s.partition('##') ('foo', '##', 'bar##baz##qux') >>> s.partition('##')[0] 'foo' # last >>> s.rpartition('##') ('foo##bar##baz', '##', 'qux') >>> s.rpartition('##')[-1] 'qux'
partition(sep)
は、先頭に近い位置のsep
で分割し、3つの要素のタプルを返す。1つ目はsep
より前の文字列、2つ目はsep
そのもの、3つ目はsep
より後の文字列となっている。
また、rpartition(sep)
は、末尾に近い位置のsep
で分割し、3つの要素のタプルを返す。1つ目はsep
より後ろの文字列、2つ目はsep
そのもの、3つ目はsep
より前の文字列となっている。単純なパターンで書ける方を採用する
re
モジュールにはsplit()
とfindall()
というメソッドがある
re.split()
は、指定したパターンにマッチした文字列を区切り文字とし、分割した結果のリストを返す。re.findall()
は、指定したパターンにマッチする文字列のリストを返す。もし、どちらかが複雑なパターンになってしまった場合には、簡単に理解できる方で書くようにする
>>> import re >>> l = ['2019年2月1日', '2019-2-1', '2019/2/1'] # 複雑... >>> [re.split(r'[年月日/-]', x) for x in l] [['2019', '2', '1', ''], ['2019', '2', '1'], ['2019', '2', '1']] # 簡単! >>> [re.findall(r'\d+', s) for s in l] [['2019', '2', '1'], ['2019', '2', '1'], ['2019', '2', '1']]このように、分割する文字列(
[年月日/-]
)よりも、取得したい文字列(\d+
)のほうが簡単に理解できるため、この場合、re.findall()
を使うほうが良い。
re.replace()
の乱用1文字のパターンをバラバラに繰り返し使っている
1文字のパターンの置換を繰り返しているコードがあったとする
>>> s = "foo:bar,baz-gux" >>> s.replace(';', ':').replace(',', '.').replace('-', '|') 'foo:bar.baz|gux'大変...
Rubyでは、マッチ文字と置き換え文字の対応をした文字列を渡せる
String#tr
というメソッドがあるらしい(Ruby全く知らないからグーグル先生にきいた...)p "foo".tr("f", "X") # => "Xoo" p "foo".tr('a-z', 'A-Z') # => "FOO" p "FOO".tr('A-Z', 'a-z') # => "foo"Pythonには、Rubyの
String#tr
のようなメソッドなさそう...
それ通りに実装してみたimport re def str_tr(s: str, old: str, new: str): for o, n in zip(old, new): s = re.sub(o, n, s) return s str_tr("foo;bar,baz-gux", ";,-", ":.|") # 'foo:bar.baz|gux'一応できたけど、もっと良い書き方無いのかな...
まとめ
正規表現を使うのではなく、strのメソッドを使うことで簡単に書けることもある
参考文献
- 投稿日:2019-02-11T01:30:47+09:00
rails generateする時sqlite3の指定で躓いた話
環境
- cloud9
- Rails 5.2.2
controllerをgenerateしたかった
controlleを作ろうとrails generate。
$ rails generate controller search booksいやー、railsはこれがあるから良いですねー。
昔やってたStrutsなんて用意するファイルを全部手で作ってたから効率がぜんぜん違うわー。
さてさて、ズラズラっと出てきたから出来てー…/usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/gems/2.4.0/gems/bundler-1.16.5/lib/bundler/rubygems_integration.rb:408:in `block (2 levels) in replace_gem': Error loading the 'sqlite3' Active Record adapter. Missing a gem it depends on? can't activate sqlite3 (~> 1.3.6), already activated sqlite3-1.4.0. Make sure all dependencies are added to Gemfile. (LoadError) : (以下スタックトレース) :ないじゃん。
おもくそエラーじゃん。答えはエラーに書いてある
ちゃんとしたフレームワークならちゃんとしたエラー文が出力されるので、まずはエラー文を眺めてみましょう。
すると、最後にこんな記載があるわけですな。Make sure all dependencies are added to Gemfile.英語が得意とは言えない人(=ぼく)でも「dependencies」を「Gemfile」でなんかしなきゃいけないことはなんとなーくわかりますね。
じゃあその直前に何が書いてあるかというと、Missing a gem it depends on? can't activate sqlite3 (~> 1.3.6), already activated sqlite3-1.4.0.ざっくり和訳すると、「Genfileのsqlite3周りちゃんと見ろ」。
わざわざバージョン指定まで入ってるので、Gemfileにコピペしときましょう。
現在のsqlite3をコメントアウトして、その下にコピってペーします。gemfile# Use sqlite3 as the database for Active Record # gem 'sqlite3' gem 'sqlite3', '~> 1.3.6'保存したのを確認して、rails generateを再実行。
$ rails generate controller search books Running via Spring preloader in process 12947 create app/controllers/search_controller.rb route get 'search/books' invoke erb create app/views/search create app/views/search/books.html.erb invoke test_unit create test/controllers/search_controller_test.rb invoke helper create app/helpers/search_helper.rb invoke test_unit invoke assets invoke coffee create app/assets/javascripts/search.coffee invoke scss今度はちゃんと動きました。めでたしめでたし。
参考資料
- 投稿日:2019-02-11T01:22:46+09:00
正規表現の先読み/後読みは前方確認/後方確認と変換すると分かりやすい
Ruby の正規表現の先読みと後読み、全然理解できません。
挙動は理解できます。単語の意味が分からないのです。全くピンと来ない。
仕方ないので、できる限り他の日本語に変換して噛み砕いてみようというのがこの記事の趣旨です。多分この手の記事は既出だと思うので、すでに仕様を理解されている方、自分なりに結論が出ている方には必要ないかもしれません。単に単語が分かりにくいだけなので。
TL;DR
- 先読み … ある位置から先の文字列があるパターンにマッチするか
- 後読み … ある位置より前の文字列があるパターンにマッチするか
言い換えると、先読み → 前方確認、後読み → 後方確認していると考えると少しは理解しやすいかもしれません(少なくとも自分は理解しやすいです)。が、しかしこの解釈の仕方は自分にとって重要です。
何が問題点か
そもそも読み方は、「先読み(さきよみ)」「後読み(あとよみ)」だと思うんですが、これを初見で聞いて何の話なのかピンと来ませんでした。
- 「先読み(さきよみ)」 … 先を読む?先に読む?
- 「後読み(あとよみ)」 … 後ろを読む?前を読む?後で読む?
先(さき)と後(あと)という読み方だけを見ると、「評価するタイミングのことを言っているのかな?」と思ってしまいます。ですが、ここで言っているのは「ある位置から先か後ろか」ということで、時間の話ではないようです。
時間の話か、位置の話か、これがはっきりしない(ように見える)ことが問題のように感じます。
Ruby の公式ドキュメントを確認する
ある位置から続く文字列が ある部分式にマッチするならばその位置にマッチする という正規表現を書くことができます。「ある位置から続く文字列(先読み、lookahead)/ある位置の手前までの文字列(後読み、lookbehind)」 と「マッチする(肯定、positive)/マッチしない(否定、negative)」 の組み合わせで4つのパターンがあります。
https://docs.ruby-lang.org/ja/latest/doc/spec=2fregexp.html
パターン 名称(日本語) 名称(英語) (?=pat) 肯定先読み positive lookahead (?!pat) 否定先読み negative lookahead (?<=pat) 肯定後読み positive lookbehind (?<!pat) 否定後読み negative lookbehind ドキュメントを読むと、英語の表現も含めて位置関係を示していることが分かると思います。
「ある位置から続く文字列」なのか
「ある位置の手前までの文字列」なのか/(?<=<code>).+?(?=<\/code>)/
↑の例の場合、<code></code>に挟まれている文字列にマッチします(結構パフォーマンス悪そう…)。<code>から続くかつ</code>よりも手前の文字列を見ているっちゅーことですね。時間とかタイミングとか全く関係ないじゃん(いや評価するタイミングとか内部的には色々あるかもしれんけれども)。
先読み/後読みは前方確認/後方確認と変換する
まとめると、先(さき)とか後(あと)とか言っていますが、文字列の位置関係の話なので、なにか空間的な何かを感じる単語の方がしっくり来るのではないかと思います(諸説あり)。
もし、「なんだかな〜しっくりこねえなあ〜」と阿藤快状態になっている方いましたらご参考にしていただければ幸いです。
ちなみに「こんどこそわかる(肯|否)定(先|後)読み」という記事はとても参考になったので、ぜひ合わせて読んでみていただければと思います。