一、pry是什么
Pry
是一个非常强大的可以替代Ruby
自带irb
的控制台。
它有非常多的先进的功能。
- 源代码查看(安装
pry-doc
gem 还可以查看C语言源代码) - 文档浏览
- 在线帮助系统
- 可以在编辑器中编辑方法(edit Class#method)
- 语法高亮
- 各种命令集成(启动编辑器,运行git,执行rake)
- linux命令集成(可以使用 cd, ls 等)
- 能够查看和回放历史
二、根据实例介绍Pry
Pry
关联的几个gem
pry (0.9.12.6)
pry-debugger (0.2.2)
pry-doc (0.6.0)
pry-rails (0.3.1)
一个小例子
require 'active_record'
require 'arel'
class TestCount
PUBLIC_STATUS = 4
def initialize
ActiveRecord::Base.establish_connection(
:adapter => 'mysql2',
:host => 'localhost',
:username => 'root',
:database => 'tao800_test8'
)
Arel::Table.engine = ActiveRecord::Base
@x = 123
end
def self.my_to_sql
binding.pry
id = ::Arel::Nodes::SqlLiteral.new("id")
id.count.to_sql
end
def say_hello_to_pry
binding.pry
p "Fine, pry"
end
end
aa = TestCount.new
aa.say_hello_to_pry
TestCount.my_to_sql
-ls 命令
ls shows you which methods, constants and variables are accessible to Pry. By
default it shows you the local variables defined in the current shell, and any
public methods or instance variables defined on the current object.
-m, --methods Show public methods defined on the Object (default)
-M, --instance-methods Show methods defined in a Module or Class
-p, --ppp Show public, protected (in yellow) and private (in green) methods
-G, --grep Filter output by regular expression
-cd 命令
Move into new context (object or scope). As in UNIX shells use `cd ..` to go
back, `cd /` to return to Pry top-level and `cd -` to toggle between last two
scopes. Complex syntax (e.g `cd ../@x/y`) also supported.
cd @x
cd ..
cd /
cd -
-whereami 命令
Describe the current location. If you use `binding.pry` inside a method then
whereami will print out the source for that method.
-nesting 命令
Show nesting information.
-step 命令
Step execution forward. By default, moves a single step.
Examples:
step Move a single step forward.
step 5 Execute the next 5 steps.
-jump-to 命令
Jump to a binding further up the stack, popping all bindings below.
实战
arel (master) $ pry my_test/test_count.rb
From: (pry) @ line 9 TestCount#initialize:
8: def initialize
=> 9: binding.pry
10: ActiveRecord::Base.establish_connection(
11: :adapter => 'mysql2',
12: :host => 'localhost',
13: :username => 'root',
14: :database => 'tao800_test8'
15: )
16:
17: Arel::Table.engine = ActiveRecord::Base
18:
19: binding.pry
20: @x = 123
21: end
[1] pry(#)> step
From: (pry) @ line 10 TestCount#initialize:
8: def initialize
9: binding.pry
=> 10: ActiveRecord::Base.establish_connection(
11: :adapter => 'mysql2',
12: :host => 'localhost',
13: :username => 'root',
14: :database => 'tao800_test8'
15: )
16:
17: Arel::Table.engine = ActiveRecord::Base
18:
19: binding.pry
20: @x = 123
21: end
[1] pry(#)> step
From: /Users/wanghao/.rvm/gems/ruby-2.1.1@others/gems/activerecord-4.2.1/lib/active_record/base.rb @ line 1 :
=> 1: require 'yaml'
2: require 'set'
3: require 'active_support/benchmarkable'
4: require 'active_support/dependencies'
5: require 'active_support/descendants_tracker'
6: require 'active_support/time'
[1] pry(main)> step
From: /Users/wanghao/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb @ line 39 Kernel#require:
34: #
35: # The normal require functionality of returning false if
36: # that file has already been loaded is preserved.
37:
38: def require path
=> 39: RUBYGEMS_ACTIVATION_MONITOR.enter
40:
41: path = path.to_path if path.respond_to? :to_path
42:
43: spec = Gem.find_unresolved_default_spec(path)
44: if spec
[1] pry(main)> step
From: /Users/wanghao/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/monitor.rb @ line 184 MonitorMixin#mon_enter:
183: def mon_enter
=> 184: if @mon_owner != Thread.current
185: @mon_mutex.lock
186: @mon_owner = Thread.current
187: end
188: @mon_count += 1
189: end
[1] pry(#)> nesting
Nesting status:
--
0. # (Pry top level)
[2] pry(#)> @mon_owner
=> nil
[3] pry(#)> c
From: (pry) @ line 19 TestCount#initialize:
8: def initialize
9: binding.pry
10: ActiveRecord::Base.establish_connection(
11: :adapter => 'mysql2',
12: :host => 'localhost',
13: :username => 'root',
14: :database => 'tao800_test8'
15: )
16:
17: Arel::Table.engine = ActiveRecord::Base
18:
=> 19: binding.pry
20: @x = 123
21: end
[1] pry(#)> Arel::Table.engine
=> ActiveRecord::Base
[2] pry(#)> ls
TestCount#methods: say_hello_to_pry
locals: _ __ _dir_ _ex_ _file_ _in_ _out_ _pry_
[3] pry(#)> ls -i
[4] pry(#)> ls -m
TestCount#methods: say_hello_to_pry
[5] pry(#)> cd Arel::Table
[6] pry(Arel::Table):1> show-source
From: /Users/wanghao/Project/arel/lib/arel/table.rb @ line 2:
Class name: Arel::Table
Number of lines: 123
class Table
include Arel::Crud
include Arel::FactoryMethods
@engine = nil
class << self; attr_accessor :engine; end
attr_accessor :name, :aliases, :table_alias
# TableAlias and Table both have a #table_name which is the name of the underlying table
alias :table_name :name
def initialize(name, as: nil, type_caster: nil)
@name = name.to_s
@columns = nil
@aliases = []
@type_caster = type_caster
# Sometime AR sends an :as parameter to table, to let the table know
# that it is an Alias. We may want to override new, and return a
# TableAlias node?
if as.to_s == @name
as = nil
end
@table_alias = as
end
def alias name = "#{self.name}_2"
Nodes::TableAlias.new(self, name).tap do |node|
@aliases << node
end
end
def from
SelectManager.new(self)
end
def join relation, klass = Nodes::InnerJoin
return from unless relation
case relation
when String, Nodes::SqlLiteral
raise if relation.empty?
klass = Nodes::StringJoin
end
from.join(relation, klass)
end
def outer_join relation
join(relation, Nodes::OuterJoin)
end
def group *columns
from.group(*columns)
end
def order *expr
from.order(*expr)
end
def where condition
from.where condition
end
def project *things
from.project(*things)
end
def take amount
from.take amount
end
def skip amount
from.skip amount
end
def having expr
from.having expr
end
def [] name
::Arel::Attribute.new self, name
end
def hash
# Perf note: aliases and table alias is excluded from the hash
# aliases can have a loop back to this table breaking hashes in parent
# relations, for the vast majority of cases @name is unique to a query
@name.hash
end
def eql? other
self.class == other.class &&
self.name == other.name &&
self.aliases == other.aliases &&
self.table_alias == other.table_alias
end
alias :== :eql?
def type_cast_for_database(attribute_name, value)
type_caster.type_cast_for_database(attribute_name, value)
end
def able_to_type_cast?
!type_caster.nil?
end
protected
attr_reader :type_caster
private
def attributes_for columns
return nil unless columns
columns.map do |column|
Attributes.for(column).new self, column.name.to_sym
end
end
end
[7] pry(Arel::Table):1> show-source -l
From: /Users/wanghao/Project/arel/lib/arel/table.rb @ line 2:
Class name: Arel::Table
Number of lines: 123
2: class Table
3: include Arel::Crud
4: include Arel::FactoryMethods
5:
6: @engine = nil
7: class << self; attr_accessor :engine; end
8:
9: attr_accessor :name, :aliases, :table_alias
10:
11: # TableAlias and Table both have a #table_name which is the name of the underlying table
12: alias :table_name :name
13:
14: def initialize(name, as: nil, type_caster: nil)
15: @name = name.to_s
16: @columns = nil
17: @aliases = []
18: @type_caster = type_caster
19:
20: # Sometime AR sends an :as parameter to table, to let the table know
21: # that it is an Alias. We may want to override new, and return a
22: # TableAlias node?
23: if as.to_s == @name
24: as = nil
25: end
26: @table_alias = as
27: end
28:
29: def alias name = "#{self.name}_2"
30: Nodes::TableAlias.new(self, name).tap do |node|
31: @aliases << node
32: end
33: end
34:
35: def from
36: SelectManager.new(self)
37: end
38:
39: def join relation, klass = Nodes::InnerJoin
40: return from unless relation
41:
42: case relation
43: when String, Nodes::SqlLiteral
44: raise if relation.empty?
45: klass = Nodes::StringJoin
46: end
47:
48: from.join(relation, klass)
49: end
50:
51: def outer_join relation
52: join(relation, Nodes::OuterJoin)
53: end
54:
55: def group *columns
56: from.group(*columns)
57: end
58:
59: def order *expr
60: from.order(*expr)
61: end
62:
63: def where condition
64: from.where condition
65: end
66:
67: def project *things
68: from.project(*things)
69: end
70:
71: def take amount
72: from.take amount
73: end
74:
75: def skip amount
76: from.skip amount
77: end
78:
79: def having expr
80: from.having expr
81: end
82:
83: def [] name
84: ::Arel::Attribute.new self, name
85: end
86:
[8] pry(Arel::Table):1> whereami
Inside Arel::Table.
[9] pry(Arel::Table):1> nesting
Nesting status:
--
0. # (Pry top level)
1. Arel::Table
[10] pry(Arel::Table):1> jump-to 0
[11] pry(#)> whereami
From: (pry) @ line 19 TestCount#initialize:
8: def initialize
9: binding.pry
10: ActiveRecord::Base.establish_connection(
11: :adapter => 'mysql2',
12: :host => 'localhost',
13: :username => 'root',
14: :database => 'tao800_test8'
15: )
16:
17: Arel::Table.engine = ActiveRecord::Base
18:
=> 19: binding.pry
20: @x = 123
21: end
[12] pry(#)> next
From: (pry) @ line 20 TestCount#initialize:
8: def initialize
9: binding.pry
10: ActiveRecord::Base.establish_connection(
11: :adapter => 'mysql2',
12: :host => 'localhost',
13: :username => 'root',
14: :database => 'tao800_test8'
15: )
16:
17: Arel::Table.engine = ActiveRecord::Base
18:
19: binding.pry
=> 20: @x = 123
21: end
[12] pry(#)> next
From: /Users/wanghao/.rvm/gems/ruby-2.1.1@others/gems/pry-0.10.1/lib/pry/pry_instance.rb @ line 356 Pry#evaluate_ruby:
351: def evaluate_ruby(code)
352: inject_sticky_locals!
353: exec_hook :before_eval, code, self
354:
355: result = current_binding.eval(code, Pry.eval_path, Pry.current_line)
=> 356: set_last_result(result, code)
357: ensure
358: update_input_history(code)
359: exec_hook :after_eval, result, self
360: end
[12] pry(#)> continue
From: (pry) @ line 29 TestCount#say_hello_to_pry:
28: def say_hello_to_pry
=> 29: binding.pry
30: puts "Fine, pry"
31: id = ::Arel::Nodes::SqlLiteral.new("id")
32: binding.pry
33: id.count.to_sql
34: end
[1] pry(#)> step
From: (pry) @ line 30 TestCount#say_hello_to_pry:
28: def say_hello_to_pry
29: binding.pry
=> 30: puts "Fine, pry"
31: id = ::Arel::Nodes::SqlLiteral.new("id")
32: binding.pry
33: id.count.to_sql
34: end
[1] pry(#)> step
Fine, pry
From: (pry) @ line 31 TestCount#say_hello_to_pry:
28: def say_hello_to_pry
29: binding.pry
30: puts "Fine, pry"
=> 31: id = ::Arel::Nodes::SqlLiteral.new("id")
32: binding.pry
33: id.count.to_sql
34: end
[1] pry(#)> id
=> nil
[2] pry(#)> cd ::Arel::Nodes::SqlLiteral
[3] pry(Arel::Nodes::SqlLiteral):1> show-source
From: /Users/wanghao/Project/arel/lib/arel/nodes/sql_literal.rb @ line 3:
Class name: Arel::Nodes::SqlLiteral
Number of lines: 10
class SqlLiteral < String
include Arel::Expressions
include Arel::Predications
include Arel::AliasPredication
include Arel::OrderPredications
def encode_with(coder)
coder.scalar = self.to_s
end
end
[4] pry(Arel::Nodes::SqlLiteral):1> nesting
Nesting status:
--
0. # (Pry top level)
1. Arel::Nodes::SqlLiteral
[5] pry(Arel::Nodes::SqlLiteral):1> jump-to 0
[6] pry(#)> step
From: (pry) @ line 32 TestCount#say_hello_to_pry:
28: def say_hello_to_pry
29: binding.pry
30: puts "Fine, pry"
31: id = ::Arel::Nodes::SqlLiteral.new("id")
=> 32: binding.pry
33: id.count.to_sql
34: end
[6] pry(#)> id
=> "id"
[7] pry(#)> step
From: /Users/wanghao/.rvm/gems/ruby-2.1.1@others/gems/pry-0.10.1/lib/pry/core_extensions.rb @ line 42 Object#pry:
41: def pry(object=nil, hash={})
=> 42: if object.nil? || Hash === object
43: Pry.start(self, object || {})
44: else
45: Pry.start(object, hash)
46: end
47: end
[7] pry(#)> cd ..
[8] pry(#)> nesting
Nesting status:
--
0. # (Pry top level)
[9] pry(#)> jump-to -1
Invalid nest level. Must be between 0 and -1. Got -1.
[10] pry(#)> continue
From: (pry) @ line 32 TestCount#say_hello_to_pry:
28: def say_hello_to_pry
29: binding.pry
30: puts "Fine, pry"
31: id = ::Arel::Nodes::SqlLiteral.new("id")
=> 32: binding.pry
33: id.count.to_sql
34: end
[1] pry(#)> id
=> "id"
[2] pry(#)> id.class
=> Arel::Nodes::SqlLiteral
[3] pry(#)> id.count
From: /Users/wanghao/Project/arel/lib/arel/expressions.rb @ line 4 Arel::Expressions#count:
3: def count distinct = false
=> 4: binding.pry
5: Nodes::Count.new [self], distinct
6: end
[1] pry("id")> whereami
whereami
[1] pry("id")> whereami
whereami
[1] pry("id")> whereami -l
From: /Users/wanghao/Project/arel/lib/arel/expressions.rb @ line 4 Arel::Expressions#count:
=> 4: binding.pry
[2] pry("id")> whereami -l 20
From: /Users/wanghao/Project/arel/lib/arel/expressions.rb @ line 4 Arel::Expressions#count:
=> 4: binding.pry
[3] pry("id")> whereami 20
From: /Users/wanghao/Project/arel/lib/arel/expressions.rb @ line 4 Arel::Expressions#count:
1: module Arel
2: module Expressions
3: def count distinct = false
=> 4: binding.pry
5: Nodes::Count.new [self], distinct
6: end
7:
8: def sum
9: Nodes::Sum.new [self]
10: end
11:
12: def maximum
13: Nodes::Max.new [self]
14: end
15:
16: def minimum
17: Nodes::Min.new [self]
18: end
19:
20: def average
21: Nodes::Avg.new [self]
22: end
23:
24: def extract field
[4] pry("id")> self
=> "id"
[5] pry("id")> distinct
=> false
[6] pry("id")> cd Nodes::Count
[7] pry(Arel::Nodes::Count):1> whereami
Inside Arel::Nodes::Count.
[8] pry(Arel::Nodes::Count):1> show-source
From: /Users/wanghao/Project/arel/lib/arel/nodes/count.rb @ line 3:
Class name: Arel::Nodes::Count
Number of lines: 6
class Count < Arel::Nodes::Function
def initialize expr, distinct = false, aliaz = nil
super(expr, aliaz)
@distinct = distinct
end
end
[9] pry(Arel::Nodes::Count):1> cd Arel::Nodes::Function
[10] pry(Arel::Nodes::Function):2> show-source
From: /Users/wanghao/Project/arel/lib/arel/nodes/function.rb @ line 3:
Class name: Arel::Nodes::Function
Number of lines: 28
class Function < Arel::Nodes::Node
include Arel::Predications
include Arel::WindowPredications
attr_accessor :expressions, :alias, :distinct
def initialize expr, aliaz = nil
super()
@expressions = expr
@alias = aliaz && SqlLiteral.new(aliaz)
@distinct = false
end
def as aliaz
self.alias = SqlLiteral.new(aliaz)
self
end
def hash
[@expressions, @alias, @distinct].hash
end
def eql? other
self.class == other.class &&
self.expressions == other.expressions &&
self.alias == other.alias &&
self.distinct == other.distinct
end
end
[11] pry(Arel::Nodes::Function):2> cd Arel::Nodes::Node
[12] pry(Arel::Nodes::Node):3> show-source
From: /Users/wanghao/Project/arel/lib/arel/nodes/node.rb @ line 7:
Class name: Arel::Nodes::Node
Number of lines: 52
class Node
include Arel::FactoryMethods
include Enumerable
if $DEBUG
def _caller
@caller
end
def initialize
@caller = caller.dup
end
end
From: /Users/wanghao/Project/arel/lib/arel/nodes/node.rb @ line 7:
Class name: Arel::Nodes::Node
Number of lines: 52
class Node
include Arel::FactoryMethods
include Enumerable
if $DEBUG
def _caller
@caller
end
def initialize
@caller = caller.dup
end
end
###
# Factory method to create a Nodes::Not node that has the recipient of
# the caller as a child.
def not
Nodes::Not.new self
end
From: /Users/wanghao/Project/arel/lib/arel/nodes/node.rb @ line 7:
Class name: Arel::Nodes::Node
Number of lines: 52
class Node
include Arel::FactoryMethods
include Enumerable
if $DEBUG
def _caller
@caller
end
def initialize
@caller = caller.dup
end
end
###
# Factory method to create a Nodes::Not node that has the recipient of
# the caller as a child.
def not
Nodes::Not.new self
end
###
# Factory method to create a Nodes::Grouping node that has an Nodes::Or
# node as a child.
def or right
Nodes::Grouping.new Nodes::Or.new(self, right)
end
[13] pry(Arel::Nodes::Node):3> nesting
Nesting status:
--
0. "id" (Pry top level)
1. Arel::Nodes::Count
2. Arel::Nodes::Function
3. Arel::Nodes::Node
[14] pry(Arel::Nodes::Node):3> jump-to 2
[15] pry(Arel::Nodes::Function):2> show-source
From: /Users/wanghao/Project/arel/lib/arel/nodes/function.rb @ line 3:
Class name: Arel::Nodes::Function
Number of lines: 28
class Function < Arel::Nodes::Node
include Arel::Predications
include Arel::WindowPredications
attr_accessor :expressions, :alias, :distinct
def initialize expr, aliaz = nil
super()
@expressions = expr
@alias = aliaz && SqlLiteral.new(aliaz)
@distinct = false
end
def as aliaz
self.alias = SqlLiteral.new(aliaz)
self
end
def hash
[@expressions, @alias, @distinct].hash
end
def eql? other
self.class == other.class &&
self.expressions == other.expressions &&
self.alias == other.alias &&
self.distinct == other.distinct
end
end
[16] pry(Arel::Nodes::Function):2> nesting
Nesting status:
--
0. "id" (Pry top level)
1. Arel::Nodes::Count
2. Arel::Nodes::Function
[17] pry(Arel::Nodes::Function):2> jump to 1
NoMethodError: undefined method `to' for Arel::Nodes::Function:Class
from (pry):46:in `__binding__'
[18] pry(Arel::Nodes::Function):2> jump-to 1
[19] pry(Arel::Nodes::Count):1> whereami
Inside Arel::Nodes::Count.
[20] pry(Arel::Nodes::Count):1> show-source
From: /Users/wanghao/Project/arel/lib/arel/nodes/count.rb @ line 3:
Class name: Arel::Nodes::Count
Number of lines: 6
class Count < Arel::Nodes::Function
def initialize expr, distinct = false, aliaz = nil
super(expr, aliaz)
@distinct = distinct
end
end
[21] pry(Arel::Nodes::Count):1> continue
Error: Cannot find local context. Did you use `binding.pry`?
[22] pry(Arel::Nodes::Count):1> jump-to 0
[23] pry("id")> whereami
From: /Users/wanghao/Project/arel/lib/arel/expressions.rb @ line 4 Arel::Expressions#count:
3: def count distinct = false
=> 4: binding.pry
5: Nodes::Count.new [self], distinct
6: end
[24] pry("id")> Nodes::Count.new("name")
=> #
[25] pry("id")> Nodes::Count.new("name").to_sql
NoMethodError: undefined method `each_with_index' for "name":String
from /Users/wanghao/Project/arel/lib/arel/visitors/to_sql.rb:790:in `inject_join'
[26] pry("id")> Nodes::Count.new("name").class
=> Arel::Nodes::Count
[27] pry("id")> continue
=> #
[4] pry(#)> continue
From: /Users/wanghao/Project/arel/lib/arel/expressions.rb @ line 4 Arel::Expressions#count:
3: def count distinct = false
=> 4: binding.pry
5: Nodes::Count.new [self], distinct
6: end
[1] pry("id")> continue
heihei
self.my_to_sql
You have new mail in /var/mail/wanghao
参考
https://github.com/pry/pry