pry-railsでRuby(Rails)を探索する話
pry-rails/binding.pry 使ってますかー
私。pry-railsが好きです。 めっちゃ使いやすい。
javascriptのdebugger
も好きなんですが、それ以上に個人的にRails開発でのpryが好きなのでそれについて書こうと思っています。
はい
というわけでMakeIT AdventCalendar 8日目pry-railsでRuby(Rails app)を探索する話で投稿したいとおもいます。
前回ポエム書いちゃったので2回目は技術的な何かに触れないとねw
明日は @ymzk-jp がGitのHEADとは何者なのか
を投稿してくれるはずです。
./bin/rails c とか binding.pry の話
個人的な話ですが、pry-rails。最初はすごい使いにくくてエラーに遭遇してから原因調査をして、 問題箇所を修正するみたいな開発をすることが多かったなと感じています。
ただ、pry(以下デバッガ)をある程度使えるといい感じにアプリ開発が進むなと感じました。 そのため、よく使うメソッドをまとめたいなと思います
Rails始めた人とかRails使ってるけどデバッガ使わない人に届いたら嬉しいですね。
今回モデルケースにするのは↓のようなものです。
1] pry(main)> [2] pry(main)> show-models (1.5ms) SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483 ApplicationRecord Table doesn't exist Comment id: integer user_id: integer post_id: integer body: string created_at: datetime updated_at: datetime belongs_to :post belongs_to :user Post id: integer title: string body: text published: boolean # statusって名前にすれば良かったと後悔してます user_id: integer created_at: datetime updated_at: datetime belongs_to :user User id: integer nickname: string email: string password_digest: string created_at: datetime updated_at: datetime has_many :comments has_many :posts
みたいなものを使ってた時。を想定して話していきたいなと思います。
rubyは全てがオブジェクト
突然ですがいわゆるclass,moduleなども一つのオブジェクトとなります。 オブジェクトであれば - 当然メソッド持っています - ほとんどの場合クラス本体もしくは、なにかのインスタンスであるでしょう。 - またそのクラスorインスタンスはほとんどの場合親クラスが存在しています といったことを念頭に起いた状態で次のコードを見てください
[1] pry(main)> self => main [2] pry(main)> ls ActiveSupport::ToJsonWithActiveSupportEncoder#methods: to_json Rails::ConsoleMethods#methods: app controller helper new_session reload! self.methods: inspect to_s locals: _ __ _dir_ _ex_ _file_ _in_ _out_ _pry_ [3] pry(main)> cd app [4] pry(#<ActionDispatch::Integration::Session>):1> self => #<ActionDispatch::Integration::Session:0x00007fd184305860 @_mock_session=nil, @_routes=nil, @accept="text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5", @app= #<SampleApp::Application:0x00007fd180ab8c28 @_all_autoload_paths=
デバッガ起動直後のself
は main (オブジェクト?)を参照しています。(わからんw)
rubyの実行
ここ(デバッガ)ではirb同様Rubyを実行することができます
Ex) fizzbuzz
[42] pry(main)> 100.times{|n| puts n % 15 == 0 ? 'fizzbuzz' : n % 5 == 0 ? 'fizz' : n % 3 == 0 ? 'buzz' : n }
Railsでの使い方
Rubyの使い方はあまり触れずに今回はRailsの使い方を。
先ほど cd
をしましたが。 bash使ってれば察すると思いますが、 lsもありそうですよね。
[47] pry(main)> self => main [48] pry(main)> ls ActiveSupport::ToJsonWithActiveSupportEncoder#methods: to_json Rails::ConsoleMethods#methods: app controller helper new_session reload! self.methods: inspect to_s instance variables: @app_integration_instance @controller locals: _ __ _dir_ _ex_ _file_ _in_ _out_ _pry_ [49] pry(main)>
cd, ls
ここでいうlsは’self’のオブジェクトがもっている(利用可能な)メソッドを羅列してくれます。
例えば今回のモデルである'Post'クラスや’Post’のインスタンスに潜ってls
をしてみようと思います
[70] pry(main):1> cd Post [85] pry(Post):2> self => Post(id: integer, title: string, body: text, published: boolean, user_id: integer, created_at: datetime, updated_at: datetime)
こうするとPostクラスに移動した状態です 基本的に、今いる参照先(currentなオブジェクト?)を確認する際は'self'を使う、もしくは'プロンプト'の表示項目 "[82] pry(Post)"を確認する などをすると思います
[82] pry(Post):2> ls Object.methods: yaml_tag ActiveModel::Naming#methods: model_name ActiveSupport::Benchmarkable#methods: benchmark ActiveSupport::DescendantsTracker#methods: descendants direct_descendants ActiveRecord::ConnectionHandling#methods: clear_active_connections! clear_reloadable_connections! connection_config connection_specification_name= mysql2_connection clear_all_connections! connected? connection_pool establish_connection remove_connection clear_cache! connection connection_specification_name flush_idle_connections! retrieve_connection ActiveRecord::QueryCache::ClassMethods#methods: cache uncached ActiveRecord::Querying#methods: any? distinct find_each #--------省略----------------- Post.methods: __callbacks _reflections _validators attribute_type_decorations defined_enums draft published publisheds Post#methods: autosave_associated_records_for_user belongs_to_counter_cache_after_update ...etc
例えば、モデルクラスであるPost、ここで'ls'をするとRailsのモデルが持ってるメソッドが全て表示されます。
破線の後半にある Post.methods
の部分はPostクラスにて定義したものが表示されます
さらに、自身の定義したメソッドを見る場合はいくつか方法があります
- object#methods.grep(
) #-> tab押すと予測がでる - object#find-method
#-> Recursively search for a method within a Class/Module or the current namespace. - ls --grep
#-> Show the list of vars and methods in the current scope., # Exclude -q, -v and --grep because they,
だそうです。 使ってる感じはこちら
おすすめは
メソッド名がある程度わかってるのであれば、ls --grep
を使う。
そうでないのであれば methods.grep(:hog)
-> hogを含んでるメソッドをタブで予測表示される
という使い分けです。
appオブジェクト
デバッガーではappオブジェクトなるものにアクセスすることができます
appオブジェクトを経由することでURLヘルパー、pathヘルパー、またリクエストを送ることが可能です
URLhelperといえば $./bin/rake routes
を使う人やhttp://localhost:3000/rails/info/routes
を使う人は多いのではないでしょうか?
これらの確認はこのデバッガでも可能です
[138] pry(main)> show-routes Prefix Verb URI Pattern Controller#Action users GET /users(.:format) users#index POST /users(.:format) users#create new_user GET /users/new(.:format) users#new edit_user GET /users/:id/edit(.:format) users#edit user GET /users/:id(.:format) users#show PATCH /users/:id(.:format) users#update PUT /users/:id(.:format) users#update DELETE /users/:id(.:format) users#destroy posts GET /posts(.:format) posts#index POST /posts(.:format) posts#create new_post GET /posts/new(.:format) posts#new edit_post GET /posts/:id/edit(.:format)
またこのRoutingコマンドに関しても先の'ls'で確認したようにGrepを使えます
[144] pry(main)> show-routes --grep post posts GET /posts(.:format) posts#index POST /posts(.:format) posts#create new_post GET /posts/new(.:format) posts#new edit_post GET /posts/:id/edit(.:format)
そのほかにも。RouteをControllerごとに見るのであれば
[39] pry(main)> find-route Post Routes for PostsController -- index GET /posts(.:format) [posts] create POST /posts(.:format) new GET /posts/new(.:format) [new_post] edit GET /posts/:id/edit(.:format) [edit_post] show GET /posts/:id(.:format) [post] update PATCH /posts/:id(.:format) update PUT /posts/:id(.:format) destroy DELETE /posts/:id(.:format)
というわけで、実際に調べた posts_path, new_post_pathなどの確認や、リクエストをしてみたいと思います。
[161] pry(main)> app.posts_path => "/posts" [162] pry(main)> app.new_post_path => "/posts/new" [163] pry(main)> app.post_path(1) => "/posts/1" # このように生成されたRouteの確認ができますね。 # 実際にpost_pathにreqしてみると [177] pry(main)> app.get app.posts_path Started GET "/posts" for 127.0.0.1 at 2018-12-08 20:02:01 +0900 (0.9ms) SELECT `schema_migrations`.`version` FROM `schema_migrations` ORDER BY `schema_migrations`.`version` ASC Processing by PostsController#index as HTML Rendering posts/index.html.erb within layouts/application Post Load (1.3ms) SELECT `posts`.* FROM `posts` Rendered posts/index.html.erb within layouts/application (206.8ms) Completed 200 OK in 533ms (Views: 503.4ms | ActiveRecord: 2.7ms) => 200 [200] pry(main)> res = app.response # res.bodyとするとSSRされたHTMLが見れます [227] pry(main)> app.request.params => {"controller"=>"posts", "action"=>"index"} [228] pry(main)> app.request.headers => header情報 [229] pry(main)> app.controller.class => PostsController
といったようなリクエストを送信することもできます。
helperオブジェクト
railsで開発してるとメソッドの切り出しなどにHelperを使うことがあると思います。 そういったときのHelperもデバッガーにて確認することが可能です。
module ApplicationHelper def hello_world puts 'hello world' end end
例えばこんな感じのHelperがあると
[4] pry(main)> helper.hello_world hello world => nil
このように呼ぶことが可能です。 他にも今まで同様
[21] pry(main)> cd ApplicationHelper [22] pry(ApplicationHelper):1> self => ApplicationHelper [23] pry(ApplicationHelper):1> ls ApplicationHelper#methods: hello_world locals: _ __ _dir_ _ex_ _file_ _in_ _out_ _pry_ [24] pry(ApplicationHelper):1> show-method hello_world From: /Users/fumihumi/project/sample_app/app/helpers/application_helper.rb @ line 2: Owner: ApplicationHelper Visibility: public Number of lines: 3 def hello_world puts 'hello world' end
という形で参照することができます ここでHelperメソッドを呼び出したい場合は
[38] pry(ApplicationHelper):1> show-source self From: /Users/fumihumi/project/sample_app/app/helpers/application_helper.rb @ line 1: Module name: ApplicationHelper Number of lines: 9 module ApplicationHelper def hello_world puts 'hello world' end def self.public_world puts 'class method hello' end end [39] pry(ApplicationHelper):1> public_world class method hello => nil [40] pry(ApplicationHelper):1> hello_world NameError: undefined local variable or method `hello_world' for ApplicationHelper:Module from (pry):14:in `__binding__'
一時的にクラスメソッドにする。、もしくは mainを参照している状態でhelper.hello_worldとします。
show-method(show-source)
これはメソッドの定義を見ることができます
たとえばshow-routes
をみてみると
[64] pry(main)> show-source show-routes From: /Users/fumihumi/project/sample_app/vendor/bundle/gems/pry-rails-0.3.8/lib/pry-rails/commands/show_routes.rb Number of lines: 76 class PryRails::ShowRoutes < Pry::ClassCommand match 'show-routes' group 'Rails' description 'Show all routes in match order.' banner <<-BANNER Usage: show-routes [-G] show-routes displays the current Rails app's routes. BANNER def options(opt) opt.on :G, "grep", "Filter output by regular expression", :argument => true, :as => Array end ...etc
のようになっており、DescとOptionの存在を知ることができますね。 他にも自分が定義しているメソッドなども参照ができます。
edit
これは引数に渡したメソッドがVimで(?)起動がされます。 多分'.pryrc'で設定できるはず GIFにしましたがまぁこんな感じでみれるということです。
reload
editorなどでアプリのモデル等を編集したらreload!をすることでアプリを再読み込みすることができます。 ただ、時々意図しない感じな挙動になることがある?気がするのでおかしくなったら'exit'した方がいいかと思います。
watch
4] pry(main)> hoge = 'hoge' => "hoge" [15] pry(main)> fuga = 'fuga' => "fuga" [16] pry(main)> watch hoge Watching hoge watch: hoge => "hoge" [17] pry(main)> watch fuga Watching fuga watch: fuga => "fuga" [18] pry(main)> hoge = 'ww' watch: hoge => "ww" => "ww" [20] pry(main)> foo ='foo' => "foo" [21] pry(main)> foo << 'ee' => "fooee" [23] pry(main)> fuga << 'ee' watch: fuga => "fugaee" => "fugaee"
watchを使うとWatchで指定した変数が変更があったときにwatch: <before> => <after>
で教えてくれます
hist
名前の通りHistoryをみせてくれます。 これもGrepができます
[48] pry(main)> hist --grep find 20: show-method find-route 21: find-route Post 23: find-route Post
便利。w
ActiveRecord
user = User.last user.posts.published.to_sql => "SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` = 10 AND `posts`.`published` = 1" # to_sqlメソッドでこれから吐かれるSQLをみれる [100] pry(main)> (Date.today..Date.tomorrow).to_s(:db) => "BETWEEN '2018-12-08' AND '2018-12-09'" # 引用:http://thr3a.hatenablog.com/entry/20181206/1544099172 [106] pry(main)> user.posts.published.new => #<Post:0x00007fd184778208 id: nil, title: nil, body: nil, published: "published", user_id: 10, created_at: nil, updated_at: nil> # user.posts.new -> user_id == user.id # user.posts.publisdhed.new -> published: publishedな状態でnewできる
ActiveRecord周りの便利な使い方はSQLを考えながら書くといい感じに描けそうですね わたしはまだまだダメなのでもっと頑張りたいところの一つです。
model, DB構造
[92] pry(main)> show-model User User id: integer nickname: string email: string password_digest: string created_at: datetime updated_at: datetime has_many :comments has_many :posts
のようにするとモデルのColumnなどを把握することができます
shell-mode
[130] pry(main)> shell-mode pry main:/Users/fumihumi/project/sample_app $
と、なんかプロンプトが変わりますが操作が変わる感じはあまりしてません。 どうやって使うのか知ってたら教えていただきたいです...
つらつらと書いてきましたがこんな感じでデバッガー一つとっても複雑なことがたくさんできます。 こういった使い方もあくまでも一つの使い方だと思いますし、もっと便利な方法があるのかもしれません。 自分のデバッガーの使い方を見つけておくとよりよいプログラミングになるのかなとか思ったりしています。 もっと自由自在に使いこなせるようになりたいですね。
以上でAdventCalendar8日目を終わりにしたいと思います。最後まで読んでいただきありがとうございます。
メモ書きのような文章になってしまい申し訳ありませんw