Unable to reach binding.pry - ruby

Trying to use binding.pry works until the emoticons_hash.each
Trying to use it inside of .each or after it will not trigger it. It also doesn't work within other methods of this program, even with a puts "hello" after it.
require 'pry'
require 'yaml'
def load_library(filepath)
emoticons_hash = YAML.load_file (filepath)
puts emoticons_hash.inspect
language_hash = {}
emoticons_hash.each do |emoticon|
language_hash[emoticon] = emoticons_hash[emoticon].value
binding.pry
end
binding.pry
puts "hello"
end
def english
binding.pry
puts "hello"
end

There's something in your emoticons_hash variable? If yes, it responds to .each method?
I think your binding.pry are not reaching this way because one of these two problems.

Related

Ruby configuration techniques

I am very new to ruby and I just spent time studying patterns from the existing ruby projects in github. Now, I landed on the twitter's ruby project and noticed these lines in their configuration:
client = Twitter::REST::Client.new do |config|
config.consumer_key = "YOUR_CONSUMER_KEY"
config.consumer_secret = "YOUR_CONSUMER_SECRET"
config.access_token = "YOUR_ACCESS_TOKEN"
config.access_token_secret = "YOUR_ACCESS_SECRET"
end
In the declaration of this method call, I also noticed this:
module Twitter
class Client
include Twitter::Utils
attr_accessor :access_token, :access_token_secret, :consumer_key, :consumer_secret, :proxy
def initialize(options = {})
options.each do |key, value|
instance_variable_set("##{key}", value)
end
yield(self) if block_given?
end
...
Now, as I do my practice, I copied the same logic but observe the content of "initialize" method.
module Main
class Sample
attr_accessor :hello, :foo
def initialize(options={})
yield(self) if block_given?
end
def test
#hello
end
end
end
And call it (same on how twitter code above does)
sample = Main::Sample.new do |config|
config.hello = "world"
config.foo = "bar"
end
puts "#{sample.hello} #{sample.foo}" # outputs => world bar
puts sample.test # outputs => world
Now, my question is that even though I don't have these lines in my code (see the code block from twitter above) inside my "initialize" method,
options.each do |key, value|
instance_variable_set("##{key}", value)
end
the code
puts "#{sample.hello} #{sample.foo}" and puts sample.test still works fine. Why is this so? How was the instance variable really set here?
It's because you're manually calling them with thing like config.hello= and config.foo=.
What won't work without that chunk of code is this:
Main::Sample.new(hello: 'world')
You'll need that part to pick up the options and apply them.
That Twitter version is pretty slack. Normally you'd want to test that there's a property with that name instead of just randomly assigning instance variables. Typically this is done with a white-list of some sort:
ATTRIBUTES = %i[ hello world ]
attr_accessor *ATTRIBUTES
def initialize(options = nil)
options and options.each do |attr, value|
if (ATTRIBUTES.include?(attr))
send("#{attr}=", value)
else
raise "Unknown attribute #{attr.inspect}"
end
end
yield self if (block_given?)
end
That will raise exceptions if you call with invalid options.

binding.pry being skipped in Ruby

binding.pry is not catching for me in some situations.
For instance, when I run this code using ruby programtorun.rb in the terminal, it doesn't open up a Pry session.
require 'pry'
class Foo
def bar
boo = true
binding.pry
end
end
f = Foo.new
f.bar
I tried reinstalling Pry but the problem persisted.
The problem is that binding.pry stops on the next line to be executed in your program. Your next line is non-existent. binding.pry is literally the last thing you call before your script ends.
Changing
class Foo
def bar
boo = true
binding.pry
end
end
to
class Foo
def bar
binding.pry
boo = true
end
end
caused it to stop for me at boo=true.

Output the source of a ruby method

Say I make a class with a method in it.
class A
def test
puts 'test'
end
end
I want to know what goes on inside of test. I want to literally output:
def test
puts 'test'
end
Is there any way to output the source of a method in a string?
You can use Pry to view methods
# myfile.rb
require 'pry'
class A
def test
return 'test'
end
end
puts Pry::Method(A.new.method(:test)).source #(1)
# or as suggested in the comments
puts Pry::Method.from_str("A#test").source #(2)
# uses less cpu cycles than #(1) because it does not call initialize - see comments
puts Pry::Method(A.allocate.method(:test)).source #(3)
# does not use memory to allocate class as #(1) and #(3) do
puts Pry::Method(A.instance_method(:test)).source #(4)
Then run ruby myfile.rb and you will see:
def test
return 'test'
end

Ruby method interception

I want to intercept method calls on a ruby-class and being able to do something before and after the actual execution of the method. I tried the following code, but get the error:
MethodInterception.rb:16:in before_filter': (eval):2:inalias_method': undefined method
say_hello' for classHomeWork'
(NameError)
from (eval):2:in `before_filter'
Can anybody help me to do it right?
class MethodInterception
def self.before_filter(method)
puts "before filter called"
method = method.to_s
eval_string = "
alias_method :old_#{method}, :#{method}
def #{method}(*args)
puts 'going to call former method'
old_#{method}(*args)
puts 'former method called'
end
"
puts "going to call #{eval_string}"
eval(eval_string)
puts "return"
end
end
class HomeWork < MethodInterception
before_filter(:say_hello)
def say_hello
puts "say hello"
end
end
I just came up with this:
module MethodInterception
def method_added(meth)
return unless (#intercepted_methods ||= []).include?(meth) && !#recursing
#recursing = true # protect against infinite recursion
old_meth = instance_method(meth)
define_method(meth) do |*args, &block|
puts 'before'
old_meth.bind(self).call(*args, &block)
puts 'after'
end
#recursing = nil
end
def before_filter(meth)
(#intercepted_methods ||= []) << meth
end
end
Use it like so:
class HomeWork
extend MethodInterception
before_filter(:say_hello)
def say_hello
puts "say hello"
end
end
Works:
HomeWork.new.say_hello
# before
# say hello
# after
The basic problem in your code was that you renamed the method in your before_filter method, but then in your client code, you called before_filter before the method was actually defined, thus resulting in an attempt to rename a method which doesn't exist.
The solution is simple: Don't Do That™!
Well, okay, maybe not so simple. You could simply force your clients to always call before_filter after they have defined their methods. However, that is bad API design.
So, you have to somehow arrange for your code to defer the wrapping of the method until it actually exists. And that's what I did: instead of redefining the method inside the before_filter method, I only record the fact that it is to be redefined later. Then, I do the actual redefining in the method_added hook.
There is a tiny problem in this, because if you add a method inside of method_added, then of course it will immediately get called again and add the method again, which will lead to it being called again, and so on. So, I need to guard against recursion.
Note that this solution actually also enforces an ordering on the client: while the OP's version only works if you call before_filter after defining the method, my version only works if you call it before. However, it is trivially easy to extend so that it doen't suffer from that problem.
Note also that I made some additional changes that are unrelated to the problem, but that I think are more Rubyish:
use a mixin instead of a class: inheritance is a very valuable resource in Ruby, because you can only inherit from one class. Mixins, however, are cheap: you can mix in as many as you want. Besides: can you really say that Homework IS-A MethodInterception?
use Module#define_method instead of eval: eval is evil. 'Nuff said. (There was absolutely no reason whatsoever to use eval in the first place, in the OP's code.)
use the method wrapping technique instead of alias_method: the alias_method chain technique pollutes the namespace with useless old_foo and old_bar methods. I like my namespaces clean.
I just fixed some of the limitations I mentioned above, and added a few more features, but am too lazy to rewrite my explanations, so I repost the modified version here:
module MethodInterception
def before_filter(*meths)
return #wrap_next_method = true if meths.empty?
meths.delete_if {|meth| wrap(meth) if method_defined?(meth) }
#intercepted_methods += meths
end
private
def wrap(meth)
old_meth = instance_method(meth)
define_method(meth) do |*args, &block|
puts 'before'
old_meth.bind(self).(*args, &block)
puts 'after'
end
end
def method_added(meth)
return super unless #intercepted_methods.include?(meth) || #wrap_next_method
return super if #recursing == meth
#recursing = meth # protect against infinite recursion
wrap(meth)
#recursing = nil
#wrap_next_method = false
super
end
def self.extended(klass)
klass.instance_variable_set(:#intercepted_methods, [])
klass.instance_variable_set(:#recursing, false)
klass.instance_variable_set(:#wrap_next_method, false)
end
end
class HomeWork
extend MethodInterception
def say_hello
puts 'say hello'
end
before_filter(:say_hello, :say_goodbye)
def say_goodbye
puts 'say goodbye'
end
before_filter
def say_ahh
puts 'ahh'
end
end
(h = HomeWork.new).say_hello
h.say_goodbye
h.say_ahh
Less code was changed from original. I modified only 2 line.
class MethodInterception
def self.before_filter(method)
puts "before filter called"
method = method.to_s
eval_string = "
alias_method :old_#{method}, :#{method}
def #{method}(*args)
puts 'going to call former method'
old_#{method}(*args)
puts 'former method called'
end
"
puts "going to call #{eval_string}"
class_eval(eval_string) # <= modified
puts "return"
end
end
class HomeWork < MethodInterception
def say_hello
puts "say hello"
end
before_filter(:say_hello) # <= change the called order
end
This works well.
HomeWork.new.say_hello
#=> going to call former method
#=> say hello
#=> former method called
Jörg W Mittag's solution is pretty nice. If you want something more robust (read well tested) the best resource would be the rails callbacks module.

How do I include Ruby source inline in another file?

I have a number of Ruby files, each of which declares a Class, but each of which could conceivably be run from the command line.
I'd like to put the following functionality at the bottom of each file with the least duplication possible:
if __FILE__ == $0
# instantiate the class and pass ARGV to instance.run
end
My first instinct was to do this:
# /lib/scriptize.rb:
Kernel.class_eval do
def scriptize(&block)
block.call(ARGV) if __FILE__ == $0
end
end
# /lib/some_other_file.rb:
include 'scriptize'
class Foo
# ...
end
scriptize { |args| Foo.new.run(args) }
But that doesn't work because __FILE__ is evaluated in scriptize.rb, so it's never Foo.
I imagine the solution is to literally inline the contents of scriptize.rb, but I don't know the syntax. I could use eval, but that's still quite a bit of duplication -- it can't really be reduced to a method I add to Kernel.
Try evaling it.
eval(IO.read(rubyfile), binding)
That's what Rails' initializer does when loading files in config/environments, because it needs to evaluate them within the Rails::Initializer.run block.
binding is a ruby method that'll return the current context, when passed to eval, causes it to evaluate the code within the calling environment.
Try this:
# my_class.rb
class MyClass
def run
puts 'hi'
end
end
eval(IO.read('whereami.rb'), binding)
# whereami.rb
puts __FILE__
$ ruby my_class.rb
my_class.rb
Use caller to determine how close you are to the top of the call stack:
---------------------------------------------------------- Kernel#caller
caller(start=1) => array
------------------------------------------------------------------------
Returns the current execution stack---an array containing strings
in the form ``_file:line_'' or ``_file:line: in `method'_''. The
optional _start_ parameter determines the number of initial stack
entries to omit from the result.
def a(skip)
caller(skip)
end
def b(skip)
a(skip)
end
def c(skip)
b(skip)
end
c(0) #=> ["prog:2:in `a'", "prog:5:in `b'", "prog:8:in `c'", "prog:10"]
c(1) #=> ["prog:5:in `b'", "prog:8:in `c'", "prog:11"]
c(2) #=> ["prog:8:in `c'", "prog:12"]
c(3) #=> ["prog:13"]
This gives this definition for scriptize
# scriptize.rb
def scriptize
yield ARGV if caller.size == 1
end
Now, as an example, we can use two libraries/executables that require each other
# libexA.rb
require 'scriptize'
require 'libexB'
puts "in A, caller = #{caller.inspect}"
if __FILE__ == $0
puts "A is the main script file"
end
scriptize { |args| puts "A was called with #{args.inspect}" }
# libexB.rb
require 'scriptize'
require 'libexA'
puts "in B, caller = #{caller.inspect}"
if __FILE__ == $0
puts "B is the main script file"
end
scriptize { |args| puts "B was called with #{args.inspect}" }
So when we run from the command line:
% ruby libexA.rb 1 2 3 4
in A, caller = ["./libexB.rb:2:in `require'", "./libexB.rb:2", "libexA.rb:2:in `require'", "libexA.rb:2"]
in B, caller = ["libexA.rb:2:in `require'", "libexA.rb:2"]
in A, caller = []
A is the main script file
A was called with ["1", "2", "3", "4"]
% ruby libexB.rb 4 3 2 1
in B, caller = ["./libexA.rb:2:in `require'", "./libexA.rb:2", "libexB.rb:2:in `require'", "libexB.rb:2"]
in A, caller = ["libexB.rb:2:in `require'", "libexB.rb:2"]
in B, caller = []
B is the main script file
B was called with ["4", "3", "2", "1"]
So this shows the equivalence of using scriptize and if $0 == __FILE__
However, consider that:
if $0 == __FILE__ ... end is a standard ruby idiom, easily recognized by others reading your code
require 'scriptize'; scriptize { |args| ... } is more typing for the same effect.
In order for this to really be worth it, you'd need to have more commonality in the body of scriptize - initializing some files, parsing arguments, etc. Once it gets complex enough, you might be better off with factoring out the changes in a different way - maybe passing scriptize your class, so it can instantiate them and do the main script body, or have a main script that dynamically requires one of your classes depending on what the name is.
Or, you could simply pass __FILE__ to scriptize
# /lib/scriptize.rb:
module Kernel
def scriptize(calling_file, &block)
block.call(ARGV) if calling_file == $0
end
end
# /lib/some_other_file.rb:
...
scriptize(__FILE__) { |args| Foo.new.run(args) }
I also took the time to do away with the class_eval thing. (and you might also do away with the whole module thing, since Kernel is your scope by default.
Another way to do it is how Test::Unit does it. A test case file only has a class definition in it (and a require 'test/unit').
The 'test/unit' library sets up an at_exit handler that automatically runs any test cases and suites. If your most common case is going to be running these class files, and occasionally using them as libraries, you could do something similar, and set a global to disable autorun when it was included as a library.
For example:
# tc_mytest.rb
require 'test/unit'
class TC_MyTest < Test::Unit::TestCase
def test_succeed
assert(true, 'Assertion was true.')
end
def test_fail
assert(false, 'Assertion was false.')
end
end
No boilerplater required to run:
% ruby tc_mytest.rb
Loaded suite tc_mytest
Started
F.
Finished in 0.007241 seconds.
1) Failure:
test_fail(TC_MyTest) [tc_mytest.rb:8]:
Assertion was false.
<false> is not true.
2 tests, 2 assertions, 1 failures, 0 errors
We can use eval(IO.read('filename.rb'), binding)
Example:-
setup.rb
def setup
#driver = Selenium::WebDriver.for :chrome
#base_url = "http://stage.checkinforgood.com/"
#driver.manage.timeouts.implicit_wait = 30
#verification_errors = []
end
def teardown
#driver.quit
assert_equal [], #verification_errors
end
c4g.rb
require "selenium-webdriver"
require "test/unit"
class C4g < Test::Unit::TestCase
eval(IO.read('setup.rb'), binding)
def test_login
#driver.get "http://stage.checkinforgood.com/"
#driver.find_element(:link, "Sign In").click
#driver.find_element(:id, "user_email").clear
#driver.find_element(:id, "user_email").send_keys "vtr#weboniselab.com"
#driver.find_element(:id, "user_password").clear
#driver.find_element(:id, "user_password").send_keys "test123"
#driver.find_element(:id, "user_submit").click
end
def element_present?(how, what)
#driver.find_element(how, what)
true
rescue Selenium::WebDriver::Error::NoSuchElementError
false
end
def verify(&blk)
yield
rescue Test::Unit::AssertionFailedError => ex
#verification_errors << ex
end
end
Now we can run,
$ruby c4g.rb
load 'somefile'

Resources