Skip to content

Commit

Permalink
Support return: false option to return printed String instead of re…
Browse files Browse the repository at this point in the history
…turning printed object
  • Loading branch information
AndyObtiva committed Aug 24, 2020
1 parent 34f00e9 commit 9447edc
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -10,6 +10,7 @@
- Support varargs printing (example: `pd arg1, arg2, arg3`)
- Display `run_at` run number in printout
- Support `require 'pd`' as a shorter alternative to `require 'puts_debuggerer'`
- Support `return: false` option to return printed String instead of returning printed object

## 0.8.2

Expand Down
8 changes: 4 additions & 4 deletions TODO.md
Expand Up @@ -6,13 +6,13 @@ Here are tasks considered for future versions. Once done, they are moved to the

### 0.10.0

- `return: false` option to return printed String instead of returning printed object
- Support `printer` as a Logger object or Logging::Logger (from "logging" gem). Perhaps discover via ducktyping.
- Provide as logging device and/or formatter for Ruby logger API and/or logging gem
- Refactor internals to avoid global method pollution

### Version TBD

* fix issue with printing in rspec inside a Rails project without having to do extra configuration
* fix issue with erb support
* pry support: implement fallback in irb for when line number cannot be discovered (issue happens in pry, perhaps this just means support pry)
- fix issue with printing in rspec inside a Rails project without having to do extra configuration
- fix issue with erb support
- pry support: implement fallback in irb for when line number cannot be discovered (issue happens in pry, perhaps this just means support pry)
- Consider the idea of customizing print stream (e.g. stderr instead of stdout). Currently possible through setting `printer`
57 changes: 48 additions & 9 deletions lib/puts_debuggerer.rb
Expand Up @@ -9,18 +9,15 @@ module PutsDebuggerer
HEADER_DEFAULT = '*'*80
WRAPPER_DEFAULT = '*'*80
FOOTER_DEFAULT = '*'*80
RETURN_DEFAULT = true
OBJECT_PRINTER_DEFAULT = lambda do |object, print_engine_options=nil, source_line_count=nil, run_number=nil|
lambda do
if object.is_a?(Exception) && object.respond_to?(:full_message)
puts object.full_message
elsif PutsDebuggerer.print_engine.is_a?(Proc)
PutsDebuggerer.print_engine.call(object)
else
if print_engine_options.to_h.empty?
send(PutsDebuggerer.print_engine, object)
else
send(PutsDebuggerer.print_engine, object, print_engine_options) rescue send(PutsDebuggerer.print_engine, object)
end
send(PutsDebuggerer.print_engine, object)
end
end
end
Expand Down Expand Up @@ -235,6 +232,36 @@ def print_engine_default
Object.const_defined?(:AwesomePrint) ? PRINT_ENGINE_DEFAULT : :p
end

# The `return` option specifies whether to return the object being printed (default) or not (i.e. return
# the string being printed instead.)
# The purpose of returning object by default is to enable chaining pd statements safely (e.g. `pd x = ([pd(value1), value2])`)
# and avoid pd from interrupting the flow of execution or affecting the logic in any way.
# Values may be true (return object) or false (do not return object, returning printed string instead)
#
# Defaults to `true`
#
# Example:
#
# # File Name: /Users/User/example.rb
# array = [1, [2, 3]]
# pd array, return: false
#
# Prints out:
#
# [PD] /Users/User/example.rb:3
# > pd array, return: false
# => [1, [2, 3]]
# ]
#
# Returns:
#
# "[1, [2, 3]]"
attr_reader :return

def return=(return_object)
@return = return_object.nil? ? RETURN_DEFAULT : return_object
end

# Announcer (e.g. [PD]) to announce every print out with (default: "[PD]")
#
# Example:
Expand Down Expand Up @@ -425,7 +452,7 @@ def run_at?
!!@run_at
end

def determine_options(objects)
def determine_options(objects)
objects.delete_at(-1) if objects.size > 1 && objects.last.is_a?(Hash)
end

Expand All @@ -436,6 +463,14 @@ def determine_object(objects)
def determine_run_at(options)
((options && options[:run_at]) || PutsDebuggerer.run_at)
end

def determine_return(options)
if options && options.has_key?(:return)
options[:return]
else
PutsDebuggerer.return
end
end
end
end

Expand All @@ -446,6 +481,7 @@ def determine_run_at(options)
PutsDebuggerer.formatter = nil
PutsDebuggerer.app_path = nil
PutsDebuggerer.caller = nil
PutsDebuggerer.return = nil
PutsDebuggerer.run_at = nil
PutsDebuggerer.source_line_count = nil

Expand Down Expand Up @@ -482,7 +518,9 @@ def pd(*objects)
options = PutsDebuggerer.determine_options(objects)
object = PutsDebuggerer.determine_object(objects)
run_at = PutsDebuggerer.determine_run_at(options)
return_option = PutsDebuggerer.determine_return(options)

string = nil
if PutsDebuggerer::RunDeterminer.run_pd?(object, run_at)
__with_pd_options__(options) do |print_engine_options|
run_number = PutsDebuggerer::RunDeterminer.run_number(object, run_at)
Expand All @@ -491,15 +529,16 @@ def pd(*objects)
$stdout = sio = StringIO.new
PutsDebuggerer.formatter.call(formatter_pd_data)
$stdout = stdout
string = sio.string
if PutsDebuggerer.printer.is_a?(Proc)
PutsDebuggerer.printer.call(sio.string)
PutsDebuggerer.printer.call(string)
else
send(PutsDebuggerer.send(:printer), sio.string)
send(PutsDebuggerer.send(:printer), string)
end
end
end

object
return_option ? object : string
end

# Provides caller line number starting 1 level above caller of
Expand Down
Expand Up @@ -8,7 +8,7 @@
"[\n [0] 1,\n [1] [\n [0] 2,\n [1] 3\n ]\n]"
}
let(:expected_object_printout_indent2) {
"[\n [0] 1,\n [1] [\n [0] 2,\n [1] 3\n ]\n]"
"[\n [0] 1,\n [1] [\n [0] 2,\n [1] 3\n ]\n]"
}
before do
load "awesome_print/core_ext/kernel.rb"
Expand Down
72 changes: 72 additions & 0 deletions spec/lib/puts_debuggerer__with_return__spec.rb
@@ -0,0 +1,72 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe 'PutsDebuggerer' do
let(:puts_debuggerer_invoker_file) {File.expand_path(File.join(__FILE__, '..', '..', 'support', 'puts_debuggerer_invoker.rb'))}

before do
PutsDebuggerer.printer = :puts
PutsDebuggerer.print_engine = :p
end

context 'with return false (do not return object; return string instead)' do
context 'as default option' do
it 'prints and returns object by default' do
object = ['Robert', 40, 1980]
return_value = PutsDebuggererInvoker.object_with_return_option(object)
expected_string = "[PD] /Users/User/code/puts_debuggerer/spec/support/puts_debuggerer_invoker.rb:60\n > pd object, {}.tap {|h| h.merge!(return: return_option) unless return_option.nil?}\n => [\"Robert\", 40, 1980]\n"
output = $stdout.string
expect(output).to eq(expected_string)
expect(return_value).to eq(object)
end

it 'prints without returning object, yet returning string instead' do
PutsDebuggerer.return = false
object = ['Robert', 40, 1980]
return_value = PutsDebuggererInvoker.object_with_return_option(object)
expected_string = "[PD] /Users/User/code/puts_debuggerer/spec/support/puts_debuggerer_invoker.rb:60\n > pd object, {}.tap {|h| h.merge!(return: return_option) unless return_option.nil?}\n => [\"Robert\", 40, 1980]\n"
output = $stdout.string
expect(output).to eq(expected_string)
expect(return_value).to eq(expected_string)
end

it 'prints and returns object when option is specified as true' do
PutsDebuggerer.return = true
object = ['Robert', 40, 1980]
return_value = PutsDebuggererInvoker.object_with_return_option(object)
expected_string = "[PD] /Users/User/code/puts_debuggerer/spec/support/puts_debuggerer_invoker.rb:60\n > pd object, {}.tap {|h| h.merge!(return: return_option) unless return_option.nil?}\n => [\"Robert\", 40, 1980]\n"
output = $stdout.string
expect(output).to eq(expected_string)
expect(return_value).to eq(object)
end
end

context 'as piecemeal option' do # TODO
it 'prints and returns object by default' do
object = ['Robert', 40, 1980]
return_value = PutsDebuggererInvoker.object_with_return_option(object)
expected_string = "[PD] /Users/User/code/puts_debuggerer/spec/support/puts_debuggerer_invoker.rb:60\n > pd object, {}.tap {|h| h.merge!(return: return_option) unless return_option.nil?}\n => [\"Robert\", 40, 1980]\n"
output = $stdout.string
expect(output).to eq(expected_string)
expect(return_value).to eq(object)
end

it 'prints without returning object, yet returning string instead' do
object = ['Robert', 40, 1980]
return_value = PutsDebuggererInvoker.object_with_return_option(object, false)
expected_string = "[PD] /Users/User/code/puts_debuggerer/spec/support/puts_debuggerer_invoker.rb:60\n > pd object, {}.tap {|h| h.merge!(return: return_option) unless return_option.nil?}\n => [\"Robert\", 40, 1980]\n"
output = $stdout.string
expect(output).to eq(expected_string)
expect(return_value).to eq(expected_string)
end

it 'prints and returns object when option is specified as true' do
object = ['Robert', 40, 1980]
return_value = PutsDebuggererInvoker.object_with_return_option(object, true)
expected_string = "[PD] /Users/User/code/puts_debuggerer/spec/support/puts_debuggerer_invoker.rb:60\n > pd object, {}.tap {|h| h.merge!(return: return_option) unless return_option.nil?}\n => [\"Robert\", 40, 1980]\n"
output = $stdout.string
expect(output).to eq(expected_string)
expect(return_value).to eq(object)
end
end
end
end
19 changes: 12 additions & 7 deletions spec/spec_helper.rb
Expand Up @@ -21,20 +21,25 @@
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}

RSpec.configure do |config|
config.before do
$stdout = StringIO.new
end

config.after do
$stdout = StringIO.new
PutsDebuggerer.printer = :puts
PutsDebuggerer.print_engine = :p
PutsDebuggerer.app_path = nil
PutsDebuggerer.caller = nil
PutsDebuggerer.footer = nil
PutsDebuggerer.formatter = nil
PutsDebuggerer.header = nil
PutsDebuggerer.footer = nil
PutsDebuggerer.wrapper = nil
PutsDebuggerer.caller = nil
PutsDebuggerer.app_path = nil
PutsDebuggerer.printer = :puts
PutsDebuggerer.print_engine = :p
PutsDebuggerer.return = nil
PutsDebuggerer.run_at = nil
PutsDebuggerer::RunDeterminer.run_at_global_number = nil
PutsDebuggerer::RunDeterminer::OBJECT_RUN_AT.clear
PutsDebuggerer.wrapper = nil
end

end

require 'awesome_print'
Expand Down
4 changes: 4 additions & 0 deletions spec/support/puts_debuggerer_invoker.rb
Expand Up @@ -55,4 +55,8 @@ def self.vararg_array
def self.vararg_array_with_options(options)
pd 'hello', 3, true, options
end
# intentional empty line
def self.object_with_return_option(object, return_option = nil)
pd object, {}.tap {|h| h.merge!(return: return_option) unless return_option.nil?}
end
end

0 comments on commit 9447edc

Please sign in to comment.