Truncate #inspect output in irb (ruby) - ruby

I want to truncate #inspect output in irb (a large output must be cropped to MAX_LEN).
Currently, I override :inspect, :to_s methods for all specific objects.
Is there are other solution?
change $stdout ?
other?

For a clean solution, gem install hirb. hirb pages irb's returned values if they get too long.
If you want to monkeypatch irb:
module IRB
class Irb
def output_value
#context.last_value.to_s.slice(0, MAX_LEN)
end
end
end
I don't recommend this because it's a hack and breaks any time gems like ap and hirb are required.
Instead of monkeypatching irb, I'd recommend trying ripl, an irb alternative that is meant to extended.
The above as a ripl plugin would be:
require 'ripl'
module Ripl::SlicedInspect
def format_result(result)
result_prompt + result.inspect.slice(MAX_LEN)
end
end
Ripl::Shell.send :include, Ripl::SlicedInspect
With this plugin, you could require it as needed or add to your ~/.riplrc if you want to always use it.

Your solution is good.
It involves no dark magic, which might make the code less understandable and error-prone.

If you're just in IRB - you could define a monkeypatch in irb itself and or load a file that monkeypatches inspect via 'load'. This way you keep it out of your core codebase but you still get the functionality you need w/o having to override inspect in every class you wish to inspect....

If it's because you have a nested hash or something that's hard to decipher, try awesome_print. You can make it the default output formatter in irb by placing the following in your .irbrc:
require 'ap'
module IRB
class Irb
def output_value
ap #context.last_value
end
end
end
This makes objects with lots of data easy to decipher in IRB.
Even if you don't use awesome_print, you can truncate output using this same technique so you don't have to override to_s in your code.

For rails 3.1.1+, place the code below in helpers/irb_helper.rb
module IRB
class Irb
MAX_LEN = 10000
def output_value
if (#context.inspect_last_value.length > MAX_LEN)
printf #context.return_format, "#{#context.inspect_last_value[0..MAX_LEN]} <- Truncated"
else
printf #context.return_format, #context.inspect_last_value
end
end
end
end
If you'd like to customize your output more, check irb's source at https://github.com/Ruby/Ruby/blob/trunk/lib/irb.rb

I sometimes modify the objects themselves (via a module called BoringInspect which I include into the relevant classes) so that exception messages are also manageable.

Related

Get list of classes and methods that call a specific global method

I have a method called $muffinize and I would like to find where it can be found in my code. In other words, given the following code:
class A
def foo
$muffinize(1)
end
def bar
...
end
end
class B
def shoop
$muffinize(2)
end
def woop
...
end
end
class C
def nope
...
end
end
I would like to the result to be (written to a file):
A:foo
B:shoop
I was thinking of accomplishing this with a Regex, but I was wondering if there would be some way of accomplishing this with Ruby meta-programming (which I might be accidentally using as a buzz-word)?
Kernel.caller() will help you show the line number and method that is calling it at runtime. If you put something like puts caller(1,1) in your muffinize function it will output those locations, but only if they are called at runtime.
If you want to do offline source analysis, you need to parse the AST (abstract syntax tree) with something like https://github.com/whitequark/parser.
Here is a quick example with ripper (built into new rubies) - this isn't strictly an AST but it's not extracting classes either
#!/usr/local/env ruby
require 'ripper'
#require 'pry'
contents = File.open('example.rb').read
code = Ripper.lex(contents)
code.each do |line|
if(line[1] == :on_ident and line[2] == "muffinize")
puts "muffinize found at line #{line.first.first}"
end
end
Ignoring the fact that your code isn't even syntactically valid, this is simply not possible.
Here's a simple example:
class A
def foo
bar
muffinize(1)
end
end
A#foo will call Object#muffinize if and only if bar terminates. Which means that figuring out whether or not A#foo calls Object#muffinize requires to solve the Halting Problem.
By getting a list of classes and methods via ri, I was then able to analyze each method to retreive their source code using the method_source gem and then searching for muffinize. This does not rule out the possibility of muffinize from appearing in a comment or a string, but I consider the likelihood of this happening to be small enough to ignore.

How to use Ruby Module?

The following code works fine:
class Float
def round
self.format.to_f
end
def format
"%.2f" % self
end
end
However, it seems bad practice to monkeypatch a class like Float because other people are doing the same thing and it causes problems.
Then I installed simplecov and the problem started: simplecov monkeypatches the same methods.
So I created a module and mixed it in to Float.
module MyModule
def round
self.format.to_f
end
def format
"%.2f" % self
end
end
Which I guess works as well. But the problem is that simplecov seems to be overwriting the mixed-in method above.
So, what is the proper way to extend built-in classes so that they do not conflict with other people's code?
Ruby 1.9.3
Why not use just argument on the round call?
13.6657.round(2) # => 13.67
But if you are sure you need module (to possibly adjust the format for all Floats out there, I'd propose you just define format method as such:
module MyModule
def format
("%.2f" % self).to_f
end
end
And mix this in to Float.
And later in code you call the format method instead of round:
13.6657.format # => 13.67
This way it does not hurt the core functionality (as your initial code dropped the argument from the round definition).
Even better - if you want (can) pinpoint the monkey-patching, simply extend specific instance:
a = 13.6657
a.extend MyModule
a.format # => 13.67
This way it wont mess with other Floats, but you can still adjust the format without finding all calls to a.round(2) in your code.

How do you check if a library/ruby-gem has been loaded?

In ruby code, how would I check what external libraries are loaded? For example,
require 'some-library'
if is-loaded?('some-library')
puts "this will run"
end
or
# require 'some-library' Don't load it in here
if is-loaded?('some-library')
puts "this will not run"
end
Is there a way to do this?
Note on why I need this:
I'm working on boom, and on windows, it will try to include 'Win32/Console/ANSI', to enable ANSI color codes like \e[36m. What I'm trying to do is if the system is windows and 'Win32/Console/ANSI' is not loaded, it would append the color codes, so the color codes are not outputted. Here is the file.
Most libraries will typically define a top-level constant. The usual thing to do is to check whether that constant is defined.
> defined?(CSV)
#=> nil
> require "csv"
#=> true
> defined?(CSV)
#=> "constant"
> puts "loaded!" if defined?(CSV)
loaded!
#=> nil
require will throw a LoadError if it can't find the library you are trying to load. So you can check it like this
begin
require 'some-library'
puts 'This will run.'
rescue LoadError
puts 'This will not run'
# error handling code here
end
If you want to safely try requiring a gem/library that may or may not be available, use something like this:
begin
require 'securerandom'
rescue LoadError
# We just won't get securerandom
end
This works even if the gem in question has already been required. In that scenario the require statement will do nothing and the rescue block will never execute.
If you are just interested in whether or not a gem/library has already been loaded, check to see if one of its constants is present. I do something like this to dynamically load additional functionality if ActiveSupport is loaded:
if defined?(ActiveSupport)
require "active_support/cache/redis_store"
end
You can also use the opposite to load a compatibility layer if the gem/library is NOT present. For example, I use some Hash methods that don't exist in Ruby's core Hash implementation, but are added by ActiveSupport. So, I define those methods when my gem runs in an environment where ActiveSupport doesn't exist.
require 'core_ext/hash' unless defined?(ActiveSupport)
Require Library Unless Already Loaded
For simplicity, here's how you load a library unless it's already loaded:
require 'RMagick' unless defined?(Magick)
try this :
def loaded?(name)
r = Regexp.new("#{name}.rb$")
$LOADED_FEATURES.select{|t| t.match(r) }.any?
end
Be sure of the name of your module (search here $LOADED_FEATURES).

How to get the name of the calling method?

is there a way in Ruby to find the calling method name inside of a method?
For example:
class Test
def self.foo
Fooz.bar
end
end
class Fooz
def self.bar
# get Test.foo or foo
end
end
puts caller[0]
or perhaps...
puts caller[0][/`.*'/][1..-2]
In Ruby 2.0.0, you can use:
caller_locations(1,1)[0].label
It's much faster than the Ruby 1.8+ solution:
caller[0][/`([^']*)'/, 1]
Will get included in backports when I get the time (or a pull request!).
Use caller_locations(1,1)[0].label (for ruby >= 2.0)
Edit: My answer was saying to use __method__ but I was wrong, it returns the current method name.
I use
caller[0][/`([^']*)'/, 1]
How about
caller[0].split("`").pop.gsub("'", "")
Much cleaner imo.
Instead you can write it as library function and make a call wherever needed. The code goes as follows :
module CallChain
def self.caller_method(depth=1)
parse_caller(caller(depth+1).first).last
end
private
# Copied from ActionMailer
def self.parse_caller(at)
if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at
file = Regexp.last_match[1]
line = Regexp.last_match[2].to_i
method = Regexp.last_match[3]
[file, line, method]
end
end
end
To trigger the above module method you need to call like this:
caller = CallChain.caller_method
code reference from
In order to see the caller and callee information in any language, whether it be ruby or java or python, you would always want to look at the stack trace. In some languages, such as Rust and C++, there are options built into the compiler to turn on some sort of profiling mechanism you can view during run time. I do belive one exists for Ruby called ruby-prof.
And as mentioned above, you could look into the execution stack for ruby. This execution stack is an array containing backtrace location objects.
Essentially all you need to know about this command is as follows:
caller(start=1, length=nil) → array or nil

Why don't modules always honor 'require' in ruby?

(sorry I should have been clearer with the code the first time I posted this. Hope this makes sense)
File "size_specification.rb"
class SizeSpecification
def fits?
end
end
File "some_module.rb"
require 'size_specification'
module SomeModule
def self.sizes
YAML.load_file(File.dirname(__FILE__) + '/size_specification_data.yml')
end
end
File "size_specification_data.yml
---
- !ruby/object:SizeSpecification
height: 250
width: 300
Then when I call
SomeModule.sizes.first.fits?
I get an exception because "sizes" are Object's not SizeSpecification's so they don't have a "fits" function.
Are your settings and ruby installation ok? I created those 3 files and wrote what follows in "test.rb"
require 'yaml'
require "some_module"
SomeModule.sizes.first.fits?
Then I ran it.
$ ruby --version
ruby 1.8.6 (2008-06-20 patchlevel 230) [i486-linux]
$ ruby -w test.rb
$
No errors!
On second reading I'm a little confused, you seem to want to mix the class into module, which is porbably not so advisable. Also is the YAML supposed to load an array of the SizeSpecifications?
It appears to be that you're not mixing the Module into your class. If I run the test in irb then the require throws a LoadError. So I assume you've put two files together, if not dump it.
Normally you'd write the functionality in the module, then mix that into the class. so you may modify your code like this:
class SizeSpecification
include SomeModule
def fits?
end
end
Which will allow you to then say:
SizeSpecification::SomeModule.sizes
I think you should also be able to say:
SizeSpecification.sizes
However that requires you to take the self off the prefix of the sizes method definition.
Does that help?
The question code got me a little confused.
In general with Ruby, if that happens it's a good sign that I am trying to do things the wrong way.
It might be better to ask a question related to your actual intended outcome, rather than the specifics of a particular 'attack' on your problem. They we can say 'nonono, don't do that, do THIS' or 'ahhhhh, now I understand what you wanna do'

Resources