When I try define(by loading script&just typing in the pry) method_missing in pry it just exit to console(cmd on windows xp).
When I try typing it on the IRB, it goes into infinite loop or when I try loading the script(irb m.rb) it shows something like this:
D:\programowanie\Ruby>irb m.rb
m.rb(main):001:0> def method_missing name, *args, &block
m.rb(main):002:1> puts 'method is missing'
m.rb(main):003:1> end
=> nil
m.rb(main):004:0>
m.rb(main):005:0* some_missing_method("lol")method is missing
m.rb(main):005:0*
method is missing
method is missing
method is missing
m.rb(main):005:0>
method is missing
method is missing
method is missing
method is missing
and it exits to the console(cmd)
Here is my code:
def method_missing name, *args, &block
puts 'method is missing'
nil
end
some_missing_method("lol")
When I return something else it doesn't go into infinite loop but shows error instead(only first few lines are changing):
Number:
method is missing
method is missing
C:/RailsInstaller/Ruby1.9.3/lib/ruby/site_ruby/1.9.1/readline.rb:45:in `raise': can't convert TypeError to String (TypeError#to_str gives Fixnum) (Typ
eError)
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/site_ruby/1.9.1/readline.rb:45:in `rescue in readline'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/site_ruby/1.9.1/readline.rb:39:in `readline'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb/input-method.rb:115:in `gets'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb.rb:139:in `block (2 levels) in eval_input'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb.rb:273:in `signal_status'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb.rb:138:in `block in eval_input'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb/ruby-lex.rb:188:in `call'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb/ruby-lex.rb:188:in `buf_input'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb/ruby-lex.rb:103:in `getc'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb/slex.rb:205:in `match_io'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb/slex.rb:75:in `match'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb/ruby-lex.rb:286:in `token'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb/ruby-lex.rb:262:in `lex'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb/ruby-lex.rb:233:in `block (2 levels) in each_top_level_statement'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb/ruby-lex.rb:229:in `loop'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb/ruby-lex.rb:229:in `block in each_top_level_statement'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb/ruby-lex.rb:228:in `catch'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb/ruby-lex.rb:228:in `each_top_level_statement'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb.rb:155:in `eval_input'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb.rb:70:in `block in start'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb.rb:69:in `catch'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/irb.rb:69:in `start'
from C:/RailsInstaller/Ruby1.9.3/bin/irb:12:in `<main>'
String:
method is missing
method is missing
C:/RailsInstaller/Ruby1.9.3/lib/ruby/site_ruby/1.9.1/readline.rb:45:in `rescue in readline': this_is_string (RuntimeError)
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/site_ruby/1.9.1/readline.rb:39:in `readline'
(..)
So this requires some understanding of Ruby's class/object/module hierarchy, as well as some understanding of how tools like IRB and Pry work. Take a look at this, which may blow your mind as it did mine - https://web.archive.org/web/20160319051340/http://madebydna.com/all/code/2011/06/24/eigenclasses-demystified.html.
As for IRB and Pry, I don't know much about their code, but I know they basically work by reading your input and eval'ing + lots of magic.
As you seem to already know, when you're in irb/pry, you're inside the Object scope, from which nearly everything else inherits, including IRB and Pry themselves. So overriding something in Object, like method_missing, might change the behavior of everything that descends from it (i.e. Everything) including IRB/Pry. Pry is probably overriding method_missing itself to do something very important, and your change is breaking that.
You might try this as an interesting experiment:
def method_missing name, *args, &block
puts "method '#{name}' is missing"
end
That might give you some idea why it's happening, but the short answer is don't do it. Encapsulate your code into its own Module so that it doesn't interfere with anything else.
And I might be confusing myself here, but this might get things working again. It should restore whatever expectations Pry has about Object#method_missing:
def method_missing name, *args, &block
puts "method '#{name}' is missing"
super
end
Edit
True, I don't think BasicObject#method_missing exists. But that's okay, because the above super will raise an error like "NoMethodError: undefined method missing_method' for #". That more or less restores the behavior that pry/irb seem to be expecting: that Object#method_missing should ultimately raise a NoMethodError. That should fix the infinite recursive loop.
However, a better solution (aside from not overwriting Object#method_missing in the first place) might be to raise a NoMethodError yourself after doing whatever else you have to do:
def method_missing(name, *args, &block)
puts "method '#{name}' is missing"
# important stuff
raise NoMethodError, name
end
My guess is that pry is rescuing from a NoMethodError in some loop. So if it's never raised, the loop continues forever. If that's right, then the above should fix it. Not that I recommend it.
Related
I have started using Celluloid gem this morning for that first time. I am following this Railscasts tutorial and trying to figure things out.
I have a class called "SomeClass" and it has only one method. Here is the code:
require 'celluloid'
class SomeClass
include Celluloid
def initialize(name)
#name = name
end
def assholify()
puts "#{#name} has become an ASSHOLE."
end
end
When I create new instances of the class and call its method (with a bang i.e. "assholify!"), I am getting the undefined method 'assholify!', error. But Celluloid is supposed to trigger the method asynchronously when it is called with a bang. So here is how I am calling the method:
names = ['John', 'Tom', 'Harry']
names.each do |name|
n = SomeClass.new name
n.assholify!
end
Here is the full backtrace of the error:
I, [2016-09-09T11:28:02.488618 #3682] INFO -- : Celluloid 0.17.3 is running in BACKPORTED mode. [ http://git.io/vJf3J ]
/home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/calls.rb:42:in `rescue in check': undefined method `assholify!' for #<SomeClass:0x10897dc> (NoMethodError)
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/calls.rb:39:in `check'
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/calls.rb:26:in `dispatch'
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/call/sync.rb:16:in `dispatch'
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/cell.rb:50:in `block in dispatch'
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/cell.rb:76:in `block in task'
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/actor.rb:339:in `block in task'
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/task.rb:44:in `block in initialize'
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/task/fibered.rb:14:in `block in create'
from (celluloid):0:in `remote procedure call'
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/call/sync.rb:45:in `value'
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/proxy/sync.rb:22:in `method_missing'
from some_class.rb:18:in `block in <main>'
from some_class.rb:16:in `each'
from some_class.rb:16:in `<main>'
Why am I getting this error? Is it the right way to call the function? Also how do I get rid of Celluloid 0.17.3 is running in BACKPORTED mode. warning?
The undefined method error occurred because actor methods are not called with a bang in the recent versions of celluloid gem. Instead you call the method like this: n.async.assholify. So here is what the code should look like:
names = ['John', 'Tom', 'Harry']
names.each do |name|
n = SomeClass.new name
n.async.assholify # Instead of "n.assholify!"
end
For "Celluloid 0.17.0 is running in BACKPORTED mode" warning, take a look at this wiki. Backported Mode is the default, for a limited time. If you use require 'celluloid/current' instead of require 'celluloid', you should not see this warning.
I have a module in app/misc/dsl/builder.rb that has this code
module Dsl
class Builder
def initialize(context, &block)
return if not block_given?
parent_context = block.binding.eval "self"
parent_context.extend Proxy
parent_context.object = context
parent_context.instance_eval &block
end
end
def self.set_context(context, &block)
Dsl::Builder.new(context, &block)
end
end
Note: this directory misc is preloaded in application.rb
config.autoload_paths += Dir[Rails.root.join('app', 'models', '{**/}'),
Rails.root.join('app', 'misc', '{**/}')
]
Then, somewhere in the text (lets say at foo.rb) I have this code:
Dsl.set_context(obj) do
#some code with obj receiving messages
end
The test stack we are using consists on Zeus+Guard+Rspec. Now, lets say I rewrite the code to something not working
Dsl.set_context(obj) do
asdqwe #this message does not exists
end
From times to times, I receive this baffling message
1) SomeOtherClass search_hash receiving keywords params should query for those keywords
Failure/Error: subject.search_hash
NoMethodError:
undefined method `set_context' for Dsl:Module
# ./app/misc/product_query.rb:116:in `base_search_hash'
# ./app/misc/product_query.rb:25:in `search_hash'
# ./spec/misc/product_query_spec.rb:78:in `block (4 levels) in <top (required)>'
# -e:1:in `<main>'
instead of the correct message that should be regarding undefined method asdqwe
Any clue about this?
Look here
it says:
Rails 3 has been updated such that classes/modules (henceforth, C/M)
are lazy loaded from the autoload paths as they are needed
so, you can do require_relative 'app/misc/dsl/builder.rb' in your rspec_helper.rb (can it be better with just require?) The problem must be that the loader doesn't know in advance where to find Dsl.set_context, but he will know once you have referenced Dsl::Builder
Hope it helps
I have a MiniTest like this:
describe Message do
describe "#is_getting_unavailable" do
let( :message ) { Message.new() }
it "should be false when user does not exist in the database" do
message.handle
assert_equal(false, message.is_getting_unavailable)
end
end
end
Running this gives me complaint from assert_equal:
Message::#is_getting_unavailable#test_0001_should be false when user does not exist in the database
ArgumentError: wrong number of arguments (2 for 0)
test/unit/message_test.rb:148:in `(root)'
org/jruby/RubyBasicObject.java:1703:in `__send__'
org/jruby/RubyKernel.java:2209:in `send'
org/jruby/RubyArray.java:1617:in `each'
org/jruby/RubyArray.java:1617:in `each'
I did not understand this, so I included the test (just before the call of assert_equal):
puts method(:assert_equal).inspect
puts method(:assert_equal).arity
puts method(:assert_equal).source_location.inspect
The output is:
#<Method: #<Class:0x1d1e394d>(Minitest::Assertions)#assert_equal>
-3
["/home/rjung/.rvm/gems/jruby-1.7.4/gems/minitest-5.0.6/lib/minitest/assertions.rb", 155]
So the method is correct, and the arity is correct. What's the issue here?
We also use rr, timecop. Any other questions, that could help me find a solution?
It took me a while, but I could narrow the problem down to this failing test:
require 'minitest/autorun'
describe 'Message' do
let( :message ) { Hash.new }
it "should not fail awkwardly" do
assert_equal false, message.nil?
end
end
The output of this test is
1) Error:
Message#test_0001_should not fail awkwardly:
ArgumentError: wrong number of arguments (2 for 0)
test/unit/message_test.rb:7:in `(root)'
org/jruby/RubyBasicObject.java:1703:in `__send__'
org/jruby/RubyKernel.java:2209:in `send'
org/jruby/RubyArray.java:1617:in `each'
org/jruby/RubyArray.java:1617:in `each'
So I filed a Bug for https://github.com/seattlerb/minitest/issues/343.
Minitest does have a method message that was overwritten, so don't use message as a variable.
What I still wonder is, why the stacktrace says the wrong number of arguments happen in message_test.rb:7, because the method that takes no arguments (message) is definitive called from somewhere else.
What I actually trying to see when no 'initialize' method is given to an class definition then the class as you said should call the "Object#initialize",which here I tried to customize and see if it has been called or not. With that approach I reached to a conclusion(although that's wrong), when I typed "ob = A .new" that yes I can overload the Object#initialize method.But all has been ended up with the below exception. Then I thought I did something wrong in my customization.So I tried to create the object creation within an exception block and when I typed "begin" and pressed "ENTER" - i got the same error.
>> class A
>> def Object.new initialize
>> p "hi"
>> rescue
>> end
>> end
=> nil
>> begin # <~~~ Here I have pressed on ENTER
"hi" #<~~~~ How was it print out?
/usr/lib/ruby/1.9.1/irb/ruby-token.rb:94:in `Token': undefined method `set_backtrace' for "hi":String (NoMethodError)
from /usr/lib/ruby/1.9.1/irb/ruby-lex.rb:348:in `block in lex_init'
from /usr/lib/ruby/1.9.1/irb/slex.rb:236:in `call'
from /usr/lib/ruby/1.9.1/irb/slex.rb:236:in `match_io'
from /usr/lib/ruby/1.9.1/irb/slex.rb:221:in `match_io'
from /usr/lib/ruby/1.9.1/irb/slex.rb:75:in `match'
from /usr/lib/ruby/1.9.1/irb/ruby-lex.rb:286:in `token'
from /usr/lib/ruby/1.9.1/irb/ruby-lex.rb:262:in `lex'
from /usr/lib/ruby/1.9.1/irb/ruby-lex.rb:233:in `block (2 levels) in each_top_level_statement'
from /usr/lib/ruby/1.9.1/irb/ruby-lex.rb:229:in `loop'
from /usr/lib/ruby/1.9.1/irb/ruby-lex.rb:229:in `block in each_top_level_statement'
from /usr/lib/ruby/1.9.1/irb/ruby-lex.rb:228:in `catch'
from /usr/lib/ruby/1.9.1/irb/ruby-lex.rb:228:in `each_top_level_statement'
from /usr/lib/ruby/1.9.1/irb.rb:155:in `eval_input'
from /usr/lib/ruby/1.9.1/irb.rb:70:in `block in start'
from /usr/lib/ruby/1.9.1/irb.rb:69:in `catch'
from /usr/lib/ruby/1.9.1/irb.rb:69:in `start'
from /usr/bin/irb:12:in `<main>'
#ubuntu:~$
Now my questions are -
How has the "hi" been printed?
What is the cause of the error as printed above?
If such initialize definition is not allowed,then why has the error not come after I ended with the class definition?
EDIT
As per #casper I tried below:
>> def Object.new
>> p "hi"
>> end
=> nil
>> begin
/usr/lib/ruby/1.9.1/irb/ruby-token.rb:96: stack level too deep (SystemStackError)
But here no "hi" printed back.
So what made the "hi" to print back in the first case?
What exactly are you trying to do? You just redefined Object.new, so there is no surprise you make everything go haywire.
You can basically get the same effect by just:
>> def Object.new
>> end
>> [press enter]
KABOOM
The reason "hi" is printed is that someone just called Object.new, probably the irb REPL loop, and it expected an object, but instead it gets gobledygook.
You can also try this:
def Object.new *args
p args
end
And you will see funny stuff. However you won't be able to quit irb or do anything useful with it after that. Again: you just broke Object.
To make some sense of it you should read this:
In Ruby, what's the relationship between 'new' and 'initialize'? How to return nil while initializing?
And then you can try this:
class Object
class << self
alias :old_new :new
end
end
Now you can do:
def Object.new *args
p args
old_new *args
end
This won't break new because you are still calling the old version of it. However you will now be printing out stuff every time someone calls new.
class TwitterProfile < ActiveRecord::Base
def send_status_update(status_update)
if publish?
client = Twitter::Client.new( :oauth_token => authentication.token,
:oauth_token_secret => authentication.secret)
client.update(status_update.to_twitter_string)
end
rescue Exception => e
logger.info "Error publishing to twitter: #{e.to_s}"
end
end
There is a StatusUpdate model and an observer that reposts them to Twitter in after_create. I sometimes get the following exception:
NameError (undefined local variable or method `e' for #<TwitterProfile:0x00000004e44ab8>):
app/models/twitter_profile.rb:23:in `rescue in send_status_update'
app/models/twitter_profile.rb:18:in `send_status_update'
app/models/status_update_observer.rb:6:in `block in after_create'
app/models/status_update_observer.rb:4:in `after_create'
app/models/workout_observer.rb:5:in `after_update'
app/controllers/frames_controller.rb:76:in `update'
app/controllers/application_controller.rb:24:in `call'
app/controllers/application_controller.rb:24:in `block (2 levels) in <class:ApplicationController>'
app/controllers/application_controller.rb:10:in `block in <class:ApplicationController>'
What am I missing here?
I have one thing I know and one that's just a wild guess.
The thing I know is that you don't need to call to_s on an overall #{} expression; that will happen automatically. But it does no harm.
My wild guess is that your test case is not really running the code you have posted. What happens if you change e to f?
I should note that rescuing Exception itself is usually a bad idea. You should rescue RuntimeError or StandardError at the highest, and preferably something more specific. You can get fairly strange errors when rescuing Exception because you interfere with threads and interpreter-level events.
You're missing the 'begin' block of the begin/rescue clause.