This section describes the basic usage of pry, which is a powerful REPL of Ruby, and REPL-driven development of Ruby.
REPL-driven development is a development style centered around REPL. The general development flow that is not REPL driven is
However, it takes a lot of time to finish up to stage 2, and it is easy to make mistakes during that time. On the other hand, putting the REPL at the center of development makes it easier to execute code and understand the internal state, so you can get coding feedback very quickly.
To install pry, run the following command.
$ gem install pry pry-doc
On Mac or Linux, if you do not have root privileges, execute the following command.
$ sudo gem intall pry pry-doc
pry is the main body. With pry-doc, you can see the documentation and source code for Ruby built-in classes and methods.
After installation, start pry.
$ pry
[1] pry(main)>
If you touch it a little, you can see that syntax highlighting and input completion by Tab are effective, unlike irb.
For the time being, I will write an appropriate code.
[1] pry(main)> def fizzbuzz(num)
[1] pry(main)* 1.upto(num) do |n|
[1] pry(main)* if n % 3 == 0
[1] pry(main)* print "Fizz\n"
[1] pry(main)* elsif n % 5 == 0
[1] pry(main)* print "Buzz\n"
[1] pry(main)* elsif n % 15 == 0
[1] pry(main)* print "FizzBuzz\n"
[1] pry(main)* else
[1] pry(main)* print n.to_s + "\n"
[1] pry(main)* end
[1] pry(main)* end
[1] pry(main)* end
=> :fizzbuzz
[2] pry(main)> fizzbuzz(15)
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
Fizz
=> 1
[3] pry(main)>
This code converts multiples of 3 to Fizz
, multiples of 5 to Buzz
, and multiples of 15 to FizzBuzz
, and outputs an integer from 1 to 15. But something is wrong. 15 is now Fizz
instead of FizzBuzz
. Since 15 is also a multiple of 3, the cause is that the first condition n% 3 == 0
was matched before the third condition n% 15 == 0
. ..
You have to rewrite the fizzbuzz
method. However, typing 13 lines again is a hassle. You can use the edit
command to edit the specified method in the editor.
[3] pry(main)>edit fizzbuzz
In the editor you started, modify the contents of the method as follows, save and close the editor, and you will be returned to the pry session.
def fizzbuzz(num)
1.upto(num) do |n|
if n % 15 == 0
print "FizzBuzz\n"
elsif n % 3 == 0
print "Fizz\n"
elsif n % 5 == 0
print "Buzz\n"
else
print n.to_s + "\n"
end
end
end
Try running the modified fizzbuzz
method in the REPL.
[4] pry(main)> fizzbuzz(15)
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
=> 1
This time it went well.
By the way, I think that the editor launched by the edit
command is nano on Mac and Linux and notepad on Windows by default, but you can change it with the pry configuration file. The pry configuration file is ~/.pryrc
and the editor property is Pry.editor
. So, for example, if you want to change the editor to vim:
$ echo "Pry.editor = 'vim'" >> ~/.pryrc
For the 'vim'
part, specify any editor in the directory in your PATH, whether it is'emacs'
or 'code'
.
Now that the program is complete, save this method to a file. To save to a file, run save-file {method name} --to {filename}
. For example, if you want to save the fizzbuzz
method to fizzbuzz.rb
in the current directory:
[5] pry(main)>save-file fizzbuzz --to './fizzbuzz.rb'
If you want to read an existing file when you start a new pry, use the -r
option. For example, if you want to load fizzbuzz/rb
and start pry, do the following:
$ pry -r "./fizzbuzz.rb"
Next, let's write a slightly more complicated program. I will write a bubble sort. It is an algorithm that compares adjacent elements and changes the order if they are not in ascending order.
[6] pry(main)> def bubble_sort(nums)
[6] pry(main)* right_end = nums.length - 1
[6] pry(main)* while right_end > 0
[6] pry(main)* (0..right_end).each do |i|
[6] pry(main)* if nums[i] > nums[i + 1]
[6] pry(main)* nums[i], nums[i + 1] = nums[i + 1], nums[i]
[6] pry(main)* end
[6] pry(main)* end
[6] pry(main)* right_end -= 1
[6] pry(main)* end
[6] pry(main)* return nums
[6] pry(main)* end
=> :bubble_sort
[7] pry(main)> bubble_sort([3, 2, 1])
ArgumentError: comparison of Integer with nil failed
from (pry):5:in `>'
[8] pry(main)>
I got an error. It is said that Integer and nil cannot be compared. The exception is thrown on line 5, so if i
is any value,num [i]
ornum [i + 1]
will be nil
, and let's compare them. Is the cause. Find out the cause of the error and fix it. Edit bubble_sort
in the editor.
[9] pry(main)>edit bubble_sort
Insert the following code to see the value of i
where the error occurs.
def bubble_sort(nums)
right_end = nums.length - 1
while right_end > 0
(0..right_end).each do |i|
binding.pry if nums[i].nil? || nums[i + 1].nil? # <=Inserted code
if nums[i] > nums[i + 1]
nums[i], nums[i + 1] = nums[i + 1], nums[i]
end
end
right_end -= 1
end
return nums
end
When binding.pry
is evaluated by the Ruby processing system, pry will be started in that context. This is what it means.
[10] pry(main)> bubble_sort([3, 2, 1])
From: /XXXXXXXX/pry-redefined(0x3fdb8dc62bec#bubble_sort):5 Object#bubble_sort:
1: def bubble_sort(nums)
2: right_end = nums.length - 1
3: while right_end > 0
4: (0..right_end).each do |i|
=> 5: binding.pry if nums[i].nil? || nums[i + 1].nil?
6: if nums[i] > nums[i + 1]
7: nums[i], nums[i + 1] = nums[i + 1], nums[i]
8: end
9: end
10: right_end -= 1
11: end
12: return nums
13: end
[1] pry(main)>
Time has stopped when the 5th line of bubble_sort
is executed. In this state, variables in this scope can be evaluated by REPL as follows.
[1] pry(main)> i
=> 2
[2] pry(main)> nums[i].nil?
=> false
[3] pry(main)> nums[i + 1].nil?
=> true
[4] pry(main)> nums.length
=> 3
[5] pry(main)>
By examining the variable just before the error, I found that when i
is 2, i + 1
points outside the boundaries of the array, so nums [i + 1]
is nil
. It means that it has become. In other words, because it refers to the i
th and i + 1
th of nums
," i + 1
is from 1 to right_end
, not " i
is from 0 to right_end
". That is, i
must move in the range from 0 to right_end --1
. Therefore, fix it that way.
To rerun the code, run exit
.
[5] pry(main)> exit
When you're back in the original context, fix the code with edit bubble_sort
.
def bubble_sort(nums)
right_end = nums.length - 1
while right_end > 0
(0..(right_end - 1)).each do |i| # <= right_end to right_end -To 1
# binding.Remove pry
if nums[i] > nums[i + 1]
nums[i], nums[i + 1] = nums[i + 1], nums[i]
end
end
right_end -= 1
end
return nums
end
Save your edits and return to your pry session to see your corrections.
[10] pry(main)> bubble_sort([3, 2, 1])
=> [1, 2, 3]
[11] pry(main)> bubble_sort([3, 2])
=> [2, 3]
[12] pry(main)> bubble_sort([3])
=> [3]
[13] pry(main)> bubble_sort([])
=> []
[14] pry(main)>
Now that you have confirmed the fix, save the source code.
[15] pry(main)>save-file bubble_sort --to './bubble_sort.rb'
In the above example, we just referenced the value of the variable i
, but of course you can reassign the value to a local variable and then re-execute the code, just as you would normally do with a REPL.
[1] pry(main)> def add_tax(price)
[1] pry(main)* tax = 1.08
[1] pry(main)* binding.pry
[1] pry(main)* return (price * tax).to_i
[1] pry(main)* end
=> :add_tax
[2] pry(main)> add_tax(1000)
From: (pry):3 Object#add_tax:
1: def add_tax(price)
2: tax = 1.08
=> 3: binding.pry
4: return (price * tax).to_i
5: end
[1] pry(main)> tax = 1.10
=> 1.1
[2] pry(main)> exit
=> 1100
[3] pry(main)>
As you can see, using pry makes it very quick to validate and modify your code.
A context is information about which object or method you are currently inside. As you move the context, you can reference or modify the instance variables of that object or the local variables of the method.
As an example, create a class Queue that represents the data structure of the FIFO and two instances of it.
[16] pry(main)* def initialize(initial_list)
[16] pry(main)* @queue = initial_list
[16] pry(main)* end
[16] pry(main)*
[16] pry(main)* attr_reader :queue
[16] pry(main)*
[16] pry(main)* def enqueue(value)
[16] pry(main)* @queue.push(value)
[16] pry(main)* end
[16] pry(main)*
[16] pry(main)* def dequeue()
[16] pry(main)* @queue.shift
[16] pry(main)* end
[16] pry(main)* end
=> :dequeue
[17] pry(main)> q1 = Queue.new([])
=> #<Thread::Queue:0x00007fa61bf0b4f0 @queue=[]>
[18] pry(main)> q2 = Queue.new([])
=> #<Thread::Queue:0x00007fa617f08240 @queue=[]>
[19] pry(main)> q1.enqueue(1)
=> [1]
[20] pry(main)> q2.enqueue(2)
=> [2]
[21] pry(main)> q2.enqueue(3)
=> [2, 3]
[22] pry(main)>
To move the context, run cd {destination object}
. For example, to move to the q1
created earlier, do the following:
[22] pry(main)> cd q1
[23] pry(#<Thread::Queue>):1>
You can use ls
to list the objects that can be referenced in the current context.
[23] pry(#<Thread::Queue>):1> ls
Thread::Queue#methods: << clear close closed? deq dequeue empty? enq enqueue initial_list length marshal_dump num_waiting pop push shift size
self.methods: __pry__
instance variables: @queue
locals: _ __ _dir_ _ex_ _file_ _in_ _out_ pry_instance
[24] pry(#<Thread::Queue>):1> @queue
=> [1]
[25] pry(#<Thread::Queue>):1>
To exit the current context and return to the original context, run exit
.
[25] pry(#<Thread::Queue>):1> exit
=> #<Thread::Queue:0x00007fa72c80ca48 @queue=[1]>
Similarly, if you look in the context of q2
, you can see that the value of the instance variable @ queue
is different.
[26] pry(main)> cd q2
[27] pry(#<Thread::Queue>):1> ls
Thread::Queue#methods: << clear close closed? deq dequeue empty? enq enqueue initial_list length marshal_dump num_waiting pop push shift size
self.methods: __pry__
instance variables: @queue
locals: _ __ _dir_ _ex_ _file_ _in_ _out_ pry_instance
[28] pry(#<Thread::Queue>):1> @queue
=> [2, 3]
[29] pry(#<Thread::Queue>):1> exit
=> #<Thread::Queue:0x00007fa72429c4d0 @queue=[2, 3]>
[30] pry(#<Thread::Queue>):1>
You can also use binding.pry
to see the local variables of the method being executed, as described in the previous section.
Attach a document to the created class. The document can be viewed from pry if it is written in the specified format, or it can be automatically converted to HTML etc. with other command line tools.
First, save the Queue
class you created earlier to a file. To save to queue.rb
in the current directory:
[30] pry(main)> save-file Queue --to './queue.rb'
queue.rb successfully saved
[31] pry(main)>
You can edit the saved source code with edit {filename}
. This time, I will write the document according to YARD, which is a major documentation style of Ruby.
[31] pry(main)> edit './queue.rb'
queue.rb
#FIFO data structure
class Queue
def initialize(initial_list)
@queue = initial_list
end
# @return [Array]Current queue
attr_reader :queue
#Store the value in the queue
# @param value [*object]Objects to queue.
# @return [Array]Queue after storing values
def enqueue(value)
@queue.push(value)
end
#Retrieving a value from the queue
# @return [object | nil]The oldest element. Nil if there are no elements.
def dequeue()
@queue.shift
end
end
After saving and closing the editor, you will be returned to the pry session and the edited code will be loaded automatically.
To view the documentation, run show-source {class/method name} -d
. show-source
also has an alias of$
.
[32] pry(main)> $ Queue -d
From: queue.rb:1
Class name: Thread::Queue
Number of lines: 23
FIFO data structure
#Source code. Omitted because it is long.
[33] pry(main)>
[33] pry(main)> $ Queue#queue -d
From: queue.rb:7:
Owner: Thread::Queue
Visibility: public
Signature: queue()
Number of lines: 3
return [Array]Current queue
attr_reader :queue
[34] pry(main)>
[34] pry(main)> $ Queue#enqueue -d
From: queue.rb:10:
Owner: Thread::Queue
Visibility: public
Signature: enqueue(value)
Number of lines: 7
Store the value in the queue
param value [*object]Objects to queue.
return [Array]Queue after storing values
def enqueue(value)
@queue.push(value)
end
[35] pry(main)>
[35] pry(main)> $ Queue#dequeue -d
From: queue.rb:17:
Owner: Thread::Queue
Visibility: public
Signature: dequeue()
Number of lines: 6
Retrieving a value from the queue
return [object | nil]The oldest element. Nil if there are no elements.
def dequeue()
@queue.shift
end
[36] pry(main)>
There is pry-byebug
to step after stopping at binding.pry
, and pry-rails
to use pry with Rails.
Recommended Posts