debugging on rails. mykhaylo sorochan

88
Debugging on Rails Mykhaylo Sorochan kiev.rb #2 kiev.rb #2

Upload: sphere-consulting-inc

Post on 14-Jan-2015

1.350 views

Category:

Technology


2 download

DESCRIPTION

Kiev-rb#2

TRANSCRIPT

Page 1: Debugging on Rails. Mykhaylo Sorochan

Debugging on RailsMykhaylo Sorochan

kiev.rb #2kiev.rb #2

Page 2: Debugging on Rails. Mykhaylo Sorochan

Contents

Page 3: Debugging on Rails. Mykhaylo Sorochan

Sample app for the Ruby on Rails Tutorial (Rails 4)https://github.com/railstutorial/sample_app_rails_4

Debugged application

Page 4: Debugging on Rails. Mykhaylo Sorochan

debugger

gem 'debugger'debugger - call debuggerconfig - .rdebugrc

Page 5: Debugging on Rails. Mykhaylo Sorochan

Debug locationUserController#show

def show

@user = User.find(params[:id])

debugger

@microposts = @user.microposts.paginate(page: params[:page])

end

Page 6: Debugging on Rails. Mykhaylo Sorochan

Debugger commands(rdb:9) help

ruby-debug help v1.6.2

Type 'help <command-name>' for help on a specific command

Available commands:

backtrace delete enable help list pry restart source undisplay

break disable eval info method ps save start up

catch display exit irb next putl set step var

condition down finish jump p quit show thread where

continue edit frame kill pp reload skip trace

Page 7: Debugging on Rails. Mykhaylo Sorochan

Program info(rdb:4) help info

Generic command for showing things about the program being debugged.

--

List of info subcommands:

--

info args -- Argument variables of current stack frame

info breakpoints -- Status of user-settable breakpoints

info catch -- Exceptions that can be caught in the current stack frame

info display -- Expressions to display when program stops

info file -- Info about a particular file read in

info files -- File names and timestamps of files read in

info global_variables -- Global variables

info instance_variables -- Instance variables of the current stack frame

info line -- Line number and file name of current position in source file

info locals -- Local variables of the current stack frame

info program -- Execution status of the program

info stack -- Backtrace of the stack

info thread -- List info about thread NUM

info threads -- information of currently-known threads

info variables -- Local and instance variables of the current stack frame

Page 8: Debugging on Rails. Mykhaylo Sorochan

Listing(rdb:1) help list

l[ist] list forward

l[ist] - list backward

l[ist] = list current line

l[ist] nn-mm list given lines

NOTE - to turn on autolist, use 'set autolist' -> .rdebugrc

Page 9: Debugging on Rails. Mykhaylo Sorochan

Listing: in debug 12 @user = User.find(params[:id])

13 debugger

=> 14 @microposts = @user.microposts.paginate(page: params[:page])

15 end

16

Page 10: Debugging on Rails. Mykhaylo Sorochan

Listing: l 23,25l 23,25

[23, 25] in */sample_app_rails_4/app/controllers/users_controller.rb

23 if @user.save

24 sign_in @user

25 flash[:success] = "Welcome to the Sample App!"

Page 11: Debugging on Rails. Mykhaylo Sorochan

Breakpoints(rdb:4) help break

b[reak] file:line [if expr]

b[reak] class(.|#)method [if expr]

set breakpoint to some position, (optionally) if expr == true

(rdb:4) help delete

del[ete][ nnn...] delete some or all breakpoints

Page 12: Debugging on Rails. Mykhaylo Sorochan

Set breakpoint(rdb:4) b ApplicationHelper#full_title

Breakpoint 1 at ApplicationHelper::full_title

(rdb:4) b 18

Breakpoint 2 file */sample_app_rails_4/app/controllers/users_controller.rb, line 18

(rdb:4) info b

Num Enb What

1 y at ApplicationHelper:full_title

2 y at */sample_app_rails_4/app/controllers/users_controller.rb:18

Page 13: Debugging on Rails. Mykhaylo Sorochan

Navigate(rdb:4) help step

s[tep][+-]?[ nnn] step (into methods) once or nnn times

'+' forces to move to another line.

'-' is the opposite of '+' and disables the force_stepping setting.

(rdb:4) help next

n[ext][+-]?[ nnn] step over once or nnn times,

'+' forces to move to another line.

'-' is the opposite of '+' and disables the force_stepping setting.

(rdb:4) help up

up[count] move to higher frame

Page 14: Debugging on Rails. Mykhaylo Sorochan

Stop at breakpoint(rdb:4) c

Breakpoint 1 at ApplicationHelper:full_title

[-1, 8] in */sample_app_rails_4/app/helpers/application_helper.rb

1 module ApplicationHelper

2

3 # Returns the full title on a per-page basis.

=> 4 def full_title(page_title)

5 base_title = "Ruby on Rails Tutorial Sample App"

Page 15: Debugging on Rails. Mykhaylo Sorochan

Display(rdb:4) help display

disp[lay] <expression> add expression into display expression list

disp[lay] display expression list

(rdb:4) help undisplay

undisp[lay][ nnn]

Cancel some expressions to be displayed when program stops.

Page 16: Debugging on Rails. Mykhaylo Sorochan

Display configured 4 def full_title(page_title)

=> 5 base_title = "Ruby on Rails Tutorial Sample App"

6 if page_title.empty?

(rdb:4) disp

2: controller_name = users

3: base_title =

Page 17: Debugging on Rails. Mykhaylo Sorochan

Display changed(rdb:4) n

*/sample_app_rails_4/app/helpers/application_helper.rb:6

if page_title.empty?

2: controller_name = users

3: base_title = Ruby on Rails Tutorial Sample App

[1, 10] in */sample_app_rails_4/app/helpers/application_helper.rb

5 base_title = "Ruby on Rails Tutorial Sample App"

=> 6 if page_title.empty?

7 base_title

Page 18: Debugging on Rails. Mykhaylo Sorochan

Conditional breakpoint(rdb:8) b ApplicationHelper#full_title if page_title=="Alexander"

Breakpoint 4 at ApplicationHelper::full_title

(rdb:8) c

Breakpoint 1 at ApplicationHelper:full_title

Page 19: Debugging on Rails. Mykhaylo Sorochan

Variables(rdb:8) help var

v[ar] cl[ass] show class variables of self

v[ar] co[nst] <object> show constants of object

v[ar] g[lobal] show global variables

v[ar] i[nstance] <object> show instance variables of object. You may pass object id's hex as well.

v[ar] l[ocal] show local variables

Page 20: Debugging on Rails. Mykhaylo Sorochan

Show variables 12 @user = User.find(params[:id])

13 debugger

=> 14 @microposts = @user.microposts.paginate(page: params[:page])

15 end

16

17 def new

(rdb:12) v i

@_action_has_layout = true

@_action_name = "show"

@_config = {}

@_env = {"GATEWAY_INTERFACE"=>"CGI/1.1", "PATH_INFO"=>"/users/1", "QUERY_STRING"=>"",...

@_headers = {"Content-Type"=>"text/html"}

@_lookup_context = #<ActionView::LookupContext:0x00000008e4b710 @details_key=nil, @details={:loc...

@_params = {"action"=>"show", "controller"=>"users", "id"=>"1"}

@_prefixes = ["users", "application"]

@_request = #<ActionDispatch::Request:0x00000008e4bad0 @env={"GATEWAY_INTERFACE"=>"CGI/1....

@_response = #<ActionDispatch::Response:0x00000008e4baa8 @mon_owner=nil, @mon_count=0, @mo...

@_response_body = nil

@_routes = nil

@_status = 200

@user = #<User id: 1, name: "Alexander", email: "[email protected]", created_at: "2013-11...

Page 21: Debugging on Rails. Mykhaylo Sorochan

Remote debugger

Client# rdebug --client -h 127.0.0.1

Connected.

*/sample_app_rails_4/app/controllers/users_controller.rb:14

@microposts = @user.microposts.paginate(page: params[:page])

[9, 18] in */sample_app_rails_4/app/controllers/users_controller.rb

9 end

10

11 def show

12 @user = User.find(params[:id])

13 debugger

=> 14 @microposts = @user.microposts.paginate(page: params[:page])

ServerDebugger.wait_connection = true

Debugger.start_remote

Page 22: Debugging on Rails. Mykhaylo Sorochan

pry

REPL

Page 23: Debugging on Rails. Mykhaylo Sorochan

debugger-pry

Call pry from debugger

Page 24: Debugging on Rails. Mykhaylo Sorochan

pry-debugger

Debugger commands inside pry:step, next, continue, breakpoints

Page 25: Debugging on Rails. Mykhaylo Sorochan

jazz_hands

jazz_hands is an opinionated set of console-related gems and a bit of glue:pry, awesome_print, hirb, pry-rails, pry-doc, pry-git, pry-remote, pry-debugger, pry-stack_explorer, coolline, coderay

Page 26: Debugging on Rails. Mykhaylo Sorochan

pry – jazz_hands 1

Page 27: Debugging on Rails. Mykhaylo Sorochan

pry – jazz_hands 2

Page 28: Debugging on Rails. Mykhaylo Sorochan

pry – jazz_hands 3

Page 29: Debugging on Rails. Mykhaylo Sorochan

pry – jazz_hands 4

Page 30: Debugging on Rails. Mykhaylo Sorochan

Logging

ActiveSupport::Logger is used for logging

Rails.root/log/[environment_name].log

Page 31: Debugging on Rails. Mykhaylo Sorochan

Log levelsRails.logger.level

| name | level |

|----------+-------|

| :debug | 0 |

| :info | 1 |

| :warn | 2 |

| :error | 3 |

| :fatal | 4 |

| :unknown | 5 |

development, testing: log_level = 0 (:debug)production: log_level = 1

Messages with equal or higher level are sent to log

Page 32: Debugging on Rails. Mykhaylo Sorochan

Write to loglogger.debug User.inspect

logger.info user.id

logger.fatal "Help!"

Page 33: Debugging on Rails. Mykhaylo Sorochan

Tagging log messageslogger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))

logger.tagged("USER") { logger.info "hello" }

=> [USER] hello

Page 34: Debugging on Rails. Mykhaylo Sorochan

Tagged logging

def show

@user = User.find(params[:id])

logger.tagged("USER") { logger.info @user.email }

# tail -f log/development.log|grep \\\[USER\]

[USER] [email protected]

Page 35: Debugging on Rails. Mykhaylo Sorochan

Global variables for tracing__FILE__ - file name

__LINE__ - line number

Page 36: Debugging on Rails. Mykhaylo Sorochan

Some useful CLI commandstail -n 100

tail -f

grep, less

Page 37: Debugging on Rails. Mykhaylo Sorochan

Querying logs# cat log/development.log|grep GET|tail -n 2

Started GET "/assets/users.js?body=1" for 127.0.0.1 at 2013-11-21 23:32:01 +0200

Started GET "/assets/application.js?body=1" for 127.0.0.1 at 2013-11-21 23:32:01 +0200

# cat log/development.log|grep GET|wc -l

574

Page 38: Debugging on Rails. Mykhaylo Sorochan

config.log_formatterDefines the formatter of the Rails logger. Defaults to ActiveSupport::Logger::SimpleFormatter for all modes, production defaults to Logger::Formatter.

module ActiveSupport

class Logger < ::Logger

# Simple formatter which only displays the message.

class SimpleFormatter < ::Logger::Formatter

# This method is invoked when a log event occurs

def call(severity, timestamp, progname, msg)

"#{String === msg ? msg : msg.inspect}\n"

end

end

Page 39: Debugging on Rails. Mykhaylo Sorochan

config.log_levelDefaults to :debug for all modes, production defaults to :info.

Page 40: Debugging on Rails. Mykhaylo Sorochan

config.log_tagsSee ActionDispatch::Request methods. config.log_tags = [ :fullpath ]

[/users/1] Started GET "/users/1" for 127.0.0.1 at 2013-11-...

[/users/1] ActiveRecord::SchemaMigration Load (0.1ms) SELECT ...

[/assets/application.css?body=1] Started GET "/assets/application.css?body=1" for 127...

Page 41: Debugging on Rails. Mykhaylo Sorochan

config.loggerAccepts a logger conforming to the interface of Log4r or the default Ruby Logger class. Defaults to ActiveSupport::Logger, with auto flushing off in production mode.

Page 42: Debugging on Rails. Mykhaylo Sorochan

Rails consolerails c

rails c --sandbox

irb

pry

Page 43: Debugging on Rails. Mykhaylo Sorochan

rake routes> all_routes = Rails.application.routes.routes

> require 'action_dispatch/routing/inspector'

> inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)

Page 44: Debugging on Rails. Mykhaylo Sorochan

All routes> puts inspector.format(ActionDispatch::Routing::ConsoleFormatter.new)

Prefix Verb URI Pattern Controller#Action

following_user GET /users/:id/following(.:format) users#following

followers_user GET /users/:id/followers(.:format) users#followers

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

sessions POST /sessions(.:format) sessions#create

new_session GET /sessions/new(.:format) sessions#new

session DELETE /sessions/:id(.:format) sessions#destroy

microposts POST /microposts(.:format) microposts#create

micropost DELETE /microposts/:id(.:format) microposts#destroy

relationships POST /relationships(.:format) relationships#create

relationship DELETE /relationships/:id(.:format) relationships#destroy

root GET / static_pages#home

signup GET /signup(.:format) users#new

Page 45: Debugging on Rails. Mykhaylo Sorochan

Routes for controller> puts inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, 'microposts')

Prefix Verb URI Pattern Controller#Action

microposts POST /microposts(.:format) microposts#create

micropost DELETE /microposts/:id(.:format) microposts#destroy

Page 46: Debugging on Rails. Mykhaylo Sorochan

Filtering routes: GET> puts inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, 'users').lines.grep(/GET/).join

following_user GET /users/:id/following(.:format) users#following

followers_user GET /users/:id/followers(.:format) users#followers

users GET /users(.:format) users#index

new_user GET /users/new(.:format) users#new

edit_user GET /users/:id/edit(.:format) users#edit

user GET /users/:id(.:format) users#show

signup GET /signup(.:format) users#new

Page 47: Debugging on Rails. Mykhaylo Sorochan

Filtering routes: /relation/> puts inspector.format(ActionDispatch::Routing::ConsoleFormatter.new).lines.grep(/relation/).join

relationships POST /relationships(.:format) relationships#create

relationship DELETE /relationships/:id(.:format) relationships#destroy

Page 48: Debugging on Rails. Mykhaylo Sorochan

Matching routes> r = Rails.application.routes

> r.recognize_path "/users/47"

=> {:action=>"show", :controller=>"users", :id=>"47"}

> r.recognize_path "/users/87", :method => "PUT"

=> {:action=>"update", :controller=>"users", :id=>"87"}

> r.recognize_path "/users/47.json"

=> {:action=>"show", :controller=>"users", :id=>"47", :format=>"json"}

Page 49: Debugging on Rails. Mykhaylo Sorochan

Named routes> app.users_path

=> "/users"

> app.users_path(:json)

=> "/users.json"

> app.user_path(1)

=> "/users/1"

> app.user_path(1, :xml)

=> "/users/1.xml"

> app.user_path(1, :count => 4)

=> "/users/1?count=4"

Page 50: Debugging on Rails. Mykhaylo Sorochan

Making requests> app

=> #<ActionDispatch::Integration::Session:...>

> app.reset!

Page 51: Debugging on Rails. Mykhaylo Sorochan

Get requests> app.get '/users/1/edit'

Started GET "/users/1/edit" for 127.0.0.1 at 2013-11-26 23:24:18 +0200

Processing by UsersController#edit as HTML

Parameters: {"id"=>"1"}

Redirected to http://localhost:3000/signin

Filter chain halted as :signed_in_user rendered or redirected

Completed 302 Found in 3ms (ActiveRecord: 0.4ms)

> app.response.body

=> "<html><body>You are being <a href=\"http://localhost:3000/signin\">redirected</a>.</body></html>"

> app.get_via_redirect '/users/1/edit'

Started GET "/users/1/edit" for 127.0.0.1 at 2013-11-26 23:26:44 +0200

Redirected to http://localhost:3000/signin

...

Started GET "/signin" for 127.0.0.1 at 2013-11-26 23:26:44 +0200

Page 52: Debugging on Rails. Mykhaylo Sorochan

Session cookies> app.cookies

=> #<Rack::Test::CookieJar...

> app.cookies.to_hash

=> {"_sample_app_session"=>"RC9j...

app.cookies - received/sent cookies

Page 53: Debugging on Rails. Mykhaylo Sorochan

Post requests: signin> app.response.body.lines.grep /csrf-token/

=> ["<meta content=\"n+9uCcG2JJmgwhnNcp4s9jTwOU55RAPOdtAHWstcpKQ=\" name=\"csrf-token\" />\n"]

> app.post '/sessions', :authenticity_token => 'n+9uCcG2JJmgwhnNcp4s9jTwOU55RAPOdtAHWstcpKQ=',

'session[email]' => '[email protected]', 'session[password]' => '123456'

Started POST "/sessions" for 127.0.0.1 at 2013-11-26 23:33:01 +0200

Processing by SessionsController#create as HTML

Parameters: {"authenticity_token"=>"n+9uCcG2JJmgwhnNcp4s9jTwOU55RAPOdtAHWstcpKQ=", "session"=>{"email"=>"[email protected]", "password"=>"[FILTERED]"}}

Redirected to http://localhost:3000/users/1/edit

Completed 302 Found in 281ms (ActiveRecord: 7.2ms)

app.post_via_redirect

Page 54: Debugging on Rails. Mykhaylo Sorochan

Access to restricted resource> app.get '/users/1/edit'

Started GET "/users/1/edit" for 127.0.0.1 at 2013-11-26 23:38:47 +0200

Processing by UsersController#edit as HTML

Completed 200 OK in 41ms (Views: 35.7ms | ActiveRecord: 0.8ms)

Page 55: Debugging on Rails. Mykhaylo Sorochan

Call helper> helper

=> #<ActionView::Base:... >

# ApplicationHelper#full_title

> helper.full_title "Sign Up"

=> "Ruby on Rails Tutorial Sample App | Sign Up"

Page 56: Debugging on Rails. Mykhaylo Sorochan

Call helper with cookie> def cookies; @cookies ||= HashWithIndifferentAccess.new(app.cookies.to_hash); end

> helper.current_user

=> #<User id: 1, name: ....

Page 57: Debugging on Rails. Mykhaylo Sorochan

ActiveRecord logging> ActiveRecord::Base.logger = Logger.new(STDOUT)

> ActiveRecord::Base.clear_active_connections!

> # reload!

> User.find 1

D, [2013-12-30T21:55:17.775769 #24810] DEBUG -- : User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]]

=> #<User id: 1, name: "hello",....

Page 58: Debugging on Rails. Mykhaylo Sorochan

ActiveRecord hide logging> ActiveRecord::Base.logger = Logger.new(nil)

> ActiveRecord::Base.clear_active_connections!

> # reload!

> User.find 1

=> #<User id: 1, name: "hello",....

Page 59: Debugging on Rails. Mykhaylo Sorochan

Raw SQL requests> ActiveRecord::Base.connection.select_all("select * from users")

=> #<ActiveRecord::Result:...

> puts _

{"id"=>1, "name"=>"hello", "email"=>"[email protected]", "created_at"=>"2013-....

Page 60: Debugging on Rails. Mykhaylo Sorochan

.irbrc

Save your helper methods● routes● sql logging● etc

Page 61: Debugging on Rails. Mykhaylo Sorochan

CLI tools

Making HTTP requests with curl● -s silent● -v verbose● -c save cookie● -b use cookie● -data POST data● -data-urlencode URL-encode POST data

Page 62: Debugging on Rails. Mykhaylo Sorochan

Access restricted areacurl -s -v http://localhost:3000/users/1/edit > /dev/null

> GET /users/1/edit HTTP/1.1

< HTTP/1.1 302 Found

< Location: http://localhost:3000/signin

Page 63: Debugging on Rails. Mykhaylo Sorochan

Visit /signin, get tokencurl -s -c hello_cookies http://localhost:3000/signin > /dev/null |grep csrf-token

<meta content="/t/IoUQxKVEL+KR2/HsnxTKmnALUA99jIr/LvjlgPKs=" name="csrf-token" />

Page 64: Debugging on Rails. Mykhaylo Sorochan

Sign incurl -s -v --data "session[email][email protected];session[password]=123456" \

--data-urlencode "authenticity_token=/t/IoUQxKVEL+KR2/HsnxTKmnALUA99jIr/LvjlgPKs=" \

-b hello_cookies -c cookies \

http://localhost:3000/sessions > /dev/null

> POST /sessions HTTP/1.1

< HTTP/1.1 302 Found

< Location: http://localhost:3000/users/1

Page 65: Debugging on Rails. Mykhaylo Sorochan

Successful access to restricted areacurl -s -v http://localhost:3000/users/1/edit -b cookies > /dev/null

> GET /users/1/edit HTTP/1.1

< HTTP/1.1 200 OK

Page 66: Debugging on Rails. Mykhaylo Sorochan

Browser tools

● console● render helpers● debug info● etc

Page 67: Debugging on Rails. Mykhaylo Sorochan

rack-webconsole>> User.find 1

=> #<User id: 1, name: "Alexander", email: "[email protected]", created_at: "2013-11-17 16:19:07", updated_at: "2013-11-27 21:52:06", password_digest: "$2a$10$MEICr2zekeBhh9HYCMLmXut3ckOsiL0TkksFWVX4asFf...", remember_token: "cda4da34a5ee4238ddb78f20d4ec7e52b59fab4e", admin: nil>

>> helper

=> Error: undefined local variable or method `helper' for #<Rack::Webconsole::Sandbox:0x000000089cf600>

>> app

=> Error: undefined local variable or method `app' for #<Rack::Webconsole::Sandbox:0x000000089cf600>

>> Rails.root

=> #<Pathname:*/sample_app_rails_4>

>> self

=> #<Rack::Webconsole::Sandbox:0x000000089cf600 @locals={:u=>#<User id: 1, name: "Alexander"

Page 68: Debugging on Rails. Mykhaylo Sorochan

rack-webconsole

` - activate

Page 69: Debugging on Rails. Mykhaylo Sorochan

Rails Panel

Page 70: Debugging on Rails. Mykhaylo Sorochan

rails-footnotes: Assigns

Page 71: Debugging on Rails. Mykhaylo Sorochan

rails-footnotes: DB

Page 72: Debugging on Rails. Mykhaylo Sorochan

rails-footnotes: disable?footnotes=false

Page 73: Debugging on Rails. Mykhaylo Sorochan

xray-railsCtrl+Shift+X

Page 74: Debugging on Rails. Mykhaylo Sorochan

better_errors 1

Page 75: Debugging on Rails. Mykhaylo Sorochan

better_errors 2

Page 76: Debugging on Rails. Mykhaylo Sorochan

exception_notifier

Provides a set of notifiers for sending notifications when errors occur in a Rack/Rails application

Page 77: Debugging on Rails. Mykhaylo Sorochan

letter_opener

Preview email in the browser instead of sending it

Page 78: Debugging on Rails. Mykhaylo Sorochan

exception_notifier + letter_opener

Page 79: Debugging on Rails. Mykhaylo Sorochan

Chrome: Form Editor

Page 80: Debugging on Rails. Mykhaylo Sorochan

Chrome: JunkFill

Page 81: Debugging on Rails. Mykhaylo Sorochan

Chrome: Sight

Page 82: Debugging on Rails. Mykhaylo Sorochan

API calls, sight

Page 83: Debugging on Rails. Mykhaylo Sorochan

hurl.it: params

Page 84: Debugging on Rails. Mykhaylo Sorochan

hurl.it: response

Page 85: Debugging on Rails. Mykhaylo Sorochan

POSTMAN

Page 86: Debugging on Rails. Mykhaylo Sorochan

Chrome: REST console

Page 87: Debugging on Rails. Mykhaylo Sorochan

Chrome: Advanced Rest Client

Page 88: Debugging on Rails. Mykhaylo Sorochan

ВСЁ