Debugging on Rails. Mykhaylo Sorochan
-
Upload
sphere-consulting-inc -
Category
Technology
-
view
1.350 -
download
2
description
Transcript of Debugging on Rails. Mykhaylo Sorochan
Debugging on RailsMykhaylo Sorochan
kiev.rb #2kiev.rb #2
Contents
Sample app for the Ruby on Rails Tutorial (Rails 4)https://github.com/railstutorial/sample_app_rails_4
Debugged application
debugger
gem 'debugger'debugger - call debuggerconfig - .rdebugrc
Debug locationUserController#show
def show
@user = User.find(params[:id])
debugger
@microposts = @user.microposts.paginate(page: params[:page])
end
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
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
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
Listing: in debug 12 @user = User.find(params[:id])
13 debugger
=> 14 @microposts = @user.microposts.paginate(page: params[:page])
15 end
16
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!"
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
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
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
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"
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.
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 =
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
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
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
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...
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
pry
REPL
debugger-pry
Call pry from debugger
pry-debugger
Debugger commands inside pry:step, next, continue, breakpoints
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
pry – jazz_hands 1
pry – jazz_hands 2
pry – jazz_hands 3
pry – jazz_hands 4
Logging
ActiveSupport::Logger is used for logging
Rails.root/log/[environment_name].log
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
Write to loglogger.debug User.inspect
logger.info user.id
logger.fatal "Help!"
Tagging log messageslogger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
logger.tagged("USER") { logger.info "hello" }
=> [USER] hello
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]
Global variables for tracing__FILE__ - file name
__LINE__ - line number
Some useful CLI commandstail -n 100
tail -f
grep, less
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
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
config.log_levelDefaults to :debug for all modes, production defaults to :info.
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...
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.
Rails consolerails c
rails c --sandbox
irb
pry
rake routes> all_routes = Rails.application.routes.routes
> require 'action_dispatch/routing/inspector'
> inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)
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
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
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
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
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"}
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"
Making requests> app
=> #<ActionDispatch::Integration::Session:...>
> app.reset!
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
Session cookies> app.cookies
=> #<Rack::Test::CookieJar...
> app.cookies.to_hash
=> {"_sample_app_session"=>"RC9j...
app.cookies - received/sent cookies
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
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)
Call helper> helper
=> #<ActionView::Base:... >
# ApplicationHelper#full_title
> helper.full_title "Sign Up"
=> "Ruby on Rails Tutorial Sample App | Sign Up"
Call helper with cookie> def cookies; @cookies ||= HashWithIndifferentAccess.new(app.cookies.to_hash); end
> helper.current_user
=> #<User id: 1, name: ....
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",....
ActiveRecord hide logging> ActiveRecord::Base.logger = Logger.new(nil)
> ActiveRecord::Base.clear_active_connections!
> # reload!
> User.find 1
=> #<User id: 1, name: "hello",....
Raw SQL requests> ActiveRecord::Base.connection.select_all("select * from users")
=> #<ActiveRecord::Result:...
> puts _
{"id"=>1, "name"=>"hello", "email"=>"[email protected]", "created_at"=>"2013-....
.irbrc
Save your helper methods● routes● sql logging● etc
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
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
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" />
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
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
Browser tools
● console● render helpers● debug info● etc
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"
rack-webconsole
` - activate
Rails Panel
rails-footnotes: Assigns
rails-footnotes: DB
rails-footnotes: disable?footnotes=false
xray-railsCtrl+Shift+X
better_errors 1
better_errors 2
exception_notifier
Provides a set of notifiers for sending notifications when errors occur in a Rack/Rails application
letter_opener
Preview email in the browser instead of sending it
exception_notifier + letter_opener
Chrome: Form Editor
Chrome: JunkFill
Chrome: Sight
API calls, sight
hurl.it: params
hurl.it: response
POSTMAN
Chrome: REST console
Chrome: Advanced Rest Client
ВСЁ