why does Object.superclass.respond_to? :object_method ? - ruby

I am confused.
When I define a method in Object
I can call it in Objects superclass BasicObject!
Like so:
class Object
def object_method
"object_method called"
end
end
Object.superclass.respond_to? :object_method
# => true
Object.superclass.object_method
# => "object_method called"
I have expected that only derived classes inherit the new method!
P.S.: I come to this question from an exercise on rubymonk
.. implement a method superclasses inside Object ..
where the recursion stop criterion is "affected".

When you call Object.superclass, you get an object that describes BasicObject class.
This object is an instance of Class class that inherits from Object. Therefore, it has all the methods that Object has, including the one you added.
However, instances of the BasicObject class do not have this method:
irb(main):122:0> Object.object_method
=> "object_method called"
irb(main):123:0> Object.new.object_method
=> "object_method called"
irb(main):124:0> Object.superclass.object_method # Same as BasicObject.object_method
=> "object_method called"
irb(main):125:0> Object.superclass.new.object_method # Same as BasicObject.new.object_method
NoMethodError: undefined method `object_method' for #<BasicObject:0x441743be>
from (irb):125:in `evaluate'
from org/jruby/RubyKernel.java:1065:in `eval'
from org/jruby/RubyKernel.java:1390:in `loop'
from org/jruby/RubyKernel.java:1173:in `catch'
from org/jruby/RubyKernel.java:1173:in `catch'
from ~/.rvm/rubies/jruby-1.7.0/bin/irb:13:in `(root)'
Here is more stuff to meditate on:
irb(main):129:0> Object
=> Object
irb(main):130:0> Object.class
=> Class
irb(main):131:0> Object.new.class
=> Object
irb(main):132:0> Object.superclass
=> BasicObject
irb(main):133:0> Object.superclass.class
=> Class
irb(main):134:0> Object.superclass.new.class
NoMethodError: undefined method `class' for #<BasicObject:0x1c944d4a>
from (irb):134:in `evaluate'
from org/jruby/RubyKernel.java:1065:in `eval'
from org/jruby/RubyKernel.java:1390:in `loop'
from org/jruby/RubyKernel.java:1173:in `catch'
from org/jruby/RubyKernel.java:1173:in `catch'
from ~/.rvm/rubies/jruby-1.7.0/bin/irb:13:in `(root)'
Have fun!

As you can see, it is a derived class.
#ruby 1.8.7
Object.superclass
# => nil
nil.kind_of? Object
# => true
#ruby 2.0.0
Object.superclass
# => BasicObject
BasicObject.kind_of? Object
# => true

Related

Ruby - dry-rb - how to update an existing object's attributes?

Using dry-rb structs and types, I'm trying amend an object that has already been created, but can't seem to figure it out.
[3] pry(main)> class User < Dry::Struct
attribute :name, Types::String.optional
attribute :age, Types::Coercible::Integer
end
=> User
[4] pry(main)> user = User.new(name: nil, age: '21')
=> #<User name=nil age=21>
[5] pry(main)> user.name = "ted"
NoMethodError: undefined method `name=' for #<User name=nil age=21>
Did you mean? name
from (pry):10:in `__pry__'
[6] pry(main)> user(name: "Ted")
NoMethodError: undefined method `user' for main:Object
Did you mean? super
from (pry):11:in `__pry__'
[7] pry(main)> user[:name => "Ted"]
Dry::Struct::MissingAttributeError: Missing attribute: {:name=>"Ted"}
from /Users/me/.rvm/gems/ruby-2.6.5/gems/dry-struct-1.3.0/lib/dry/struct.rb:137:in `block in []'
[8] pry(main)> user('name' => 'Ted')
NoMethodError: undefined method `user' for main:Object
Did you mean? super
from (pry):13:in `__pry__'
Is this just not possible, or am I missing something super obvious? Any help is much appreciated

Parent class's instance variable in Ruby

So, for whatever reason there is no peek method in the ruby core Queue class. I am trying to create a child class that implements the peek method. However, I don't understand why I am getting an error. Is it not possible to use instance variables in this way? Looking at the source code for Queue, there are instance variables in the constructor of the parent class. Is there a way to reference these in the subclass?
class PeekQueue < Queue
def peek
#mutex.synchronize{
while true
if #que.empty?
raise ThreadError, "queue empty" if non_block
#waiting.push Thread.current
#mutex.sleep
else
return #que[0]
end
end
}
end
end
a = PeekQueue.new
a.push(1)
a.peek
NoMethodError: undefined method 'synchronize' for nil:NilClass
Edit: The Queue class is created at compile time, which is why I couldn't find the source on the ruby source code on github. This is what the parent class looks like:
https://gist.github.com/anonymous/574e20fea3a28663bfe2
I do not see that error:
irb(main):025:0> qq = PeekQueue.new
=> #<PeekQueue:0x000006002bf498 #que=[], #num_waiting=0, #mutex=#<Mutex:0x000006002bf420>, #cond=#<ConditionVariable:0x000006002bf3f8 #waiters={}, #waiters_mutex=#<Mutex:0x000006002bf3a8>>>
irb(main):026:0> qq.peek
NameError: undefined local variable or method `non_block' for #<PeekQueue:0x000006002bf498>
from (irb):15:in `block in peek'
from (irb):12:in `synchronize'
from (irb):12:in `peek'
from (irb):26
from /usr/bin/irb:12:in `<main>'
irb(main):027:0> qq.push 1
=> #<ConditionVariable:0x000006002bf3f8 #waiters={}, #waiters_mutex=#<Mutex:0x000006002bf3a8>>
irb(main):028:0> qq.peek
=> 1
Method #non_block seems to be an issue. But access to #mutex works with your code.

Unexpected Method Call

I'm using mongomapper to store pages in a db, and I index them first. In the index method, I loop through each word, and check to see if it is already in the words hashmap. If not, I add an empty array to the hash, then push its location to the array.
def index_words
#words = self.body.split(" ")
#words.each_with_index do |word,i|
if self.words[word.stem].nil?
self.words[word.stem] = []
end
puts "Called from #{caller[0]}"
self.words[word.stem].push(i)
end
end
When I run this, I get an undefined method error, saying that self.words[word.stem] is nil. Furthermore, this method is actually being called from the loop, when it's only called once in the constructor:
def initialize(*args)
super
index_words
end
The error message is:
p = Page.new({author: 'Michael',url: 'michaelfine.me',title: 'Michael Fine',body: 'Body Text'})
called fromPage.rb:19:in `each'
NoMethodError: undefined method `push' for nil:NilClass
from Page.rb:24:in `block in index_words'
from Page.rb:19:in `each'
from Page.rb:19:in `each_with_index'
from Page.rb:19:in `index_words'
from Page.rb:14:in `initialize'
from (irb):103:in `new'
from (irb):103
from /Users/Michael/.rvm/rubies/ruby-1.9.3-p286/bin/irb:16:in `<main>'

Having trouble with Nokogiri and Rails

Been at this for a while. If i tell Category to just create, everything works fine. If I tell it to find_or_create I get errors.
These work:
puts topic.at_xpath("#topicid")
puts topic.at_xpath("#topicname")
and
Category.create!(:topic_id => topic.at_xpath("#topicid"), :name => topic.at_xpath("#topicname"))
But these don't:
Category.find_by_name(topic.at_xpath("#topicname"))
or
Category.find_or_create_by_topic_id_and_name(topic.at_xpath("#topicid"), topic.at_xpath("#topicname"))
Where am I messing up?
class FeedEntry < ActiveRecord::Base
require 'nokogiri'
require 'open-uri'
has_many :category_feeds
has_many :categories, :through => :category_feeds
accepts_nested_attributes_for :categories, :allow_destroy => true, :reject_if => proc { |obj| obj.blank? }
def self.nokogiri_get_feed
url = "http://some_feed.com/atom_feed"
doc = Nokogiri::HTML(open(url))
doc.remove_namespaces!
doc.search('feed entry').each do |item|
unless exists? :guid => item.css('id').text
create!(:name => item.css('title').text, :summary => item.css('title').text, :url => item.at_css("link")[:href], :published_at => item.css('updated').text, :guid => item.css('id').text)
item.xpath('content').each do |i|
i.css('topic').each do |topic|
id = topic.at_xpath("#topicid")
name = topic.at_xpath("#topicname")
update_attributes!(:categories=>[Category.find_or_create_by_topic_id_and_name(id, name)])
end
end
end
end
end
end
errors are:
ruby-1.9.2-p180 :001 > FeedEntry.nokogiri_get_feed
TypeError: Cannot visit Nokogiri::XML::Attr
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/visitor.rb:21:in `rescue in visit'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/visitor.rb:15:in `visit'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/to_sql.rb:260:in `visit_Arel_Nodes_Equality'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/visitor.rb:15:in `visit'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/to_sql.rb:120:in `visit_Arel_Nodes_Grouping'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/visitor.rb:15:in `visit'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/to_sql.rb:91:in `block in visit_Arel_Nodes_SelectCore'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/to_sql.rb:91:in `map'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/to_sql.rb:91:in `visit_Arel_Nodes_SelectCore'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/to_sql.rb:77:in `block in visit_Arel_Nodes_SelectStatement'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/to_sql.rb:77:in `map'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/to_sql.rb:77:in `visit_Arel_Nodes_SelectStatement'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/sqlite.rb:7:in `visit_Arel_Nodes_SelectStatement'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/visitor.rb:15:in `visit'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/visitor.rb:5:in `accept'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/to_sql.rb:19:in `block in accept' ... 11 levels...
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/nokogiri-1.4.4/lib/nokogiri/xml/node_set.rb:238:in `each'
from /Users/pca/projects/cdapp/cdrails/app/models/feed_entry.rb:35:in `block (2 levels) in nokogiri_get_feed'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/nokogiri-1.4.4/lib/nokogiri/xml/node_set.rb:239:in `block in each'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/nokogiri-1.4.4/lib/nokogiri/xml/node_set.rb:238:in `upto'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/nokogiri-1.4.4/lib/nokogiri/xml/node_set.rb:238:in `each'
from /Users/pca/projects/cdapp/cdrails/app/models/feed_entry.rb:33:in `block in nokogiri_get_feed'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/nokogiri-1.4.4/lib/nokogiri/xml/node_set.rb:239:in `block in each'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/nokogiri-1.4.4/lib/nokogiri/xml/node_set.rb:238:in `upto'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/nokogiri-1.4.4/lib/nokogiri/xml/node_set.rb:238:in `each'
from /Users/pca/projects/cdapp/cdrails/app/models/feed_entry.rb:30:in `nokogiri_get_feed'
from (irb):1
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/railties-3.0.3/lib/rails/commands/console.rb:44:in `start'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/railties-3.0.3/lib/rails/commands/console.rb:8:in `start'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/railties-3.0.3/lib/rails/commands.rb:23:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
Summary
Instead of these lines:
id = topic.at_xpath("#topicid")
name = topic.at_xpath("#topicname")
Use these instead:
id = topic['topicid']
name = topic['topicname']
Explanation
Let's look at a simple test case:
require 'nokogiri'
xml = Nokogiri::XML("<root foo='bar' />")
foo = xml.root.at_xpath('#foo')
puts foo
#=> bar
p foo
#=> #<Nokogiri::XML::Attr:0x15c1d64 name="foo" value="bar">
p foo.text
#=> "bar"
p xml.root['foo']
#=> "bar"
As you can see from the above, selecting an attribute via XPath actually gives you an Attr node, which is not the same as the string value of that attribute. (Using puts causes the to_s method of the Attr to show you only the value, but that doesn't mean that it's actually a string.)
As shown above, you need to use the text method (or value or content) on the Attr nodes to get the string value back that you really wanted:
id = topic.at_xpath("#topicid").text
name = topic.at_xpath("#topicname").text
Alternatively (and more simply) use the Element#[] method to fetch the value of an attribute off of an element directly:
id = topic['topicid']
name = topic['topicname']

Array.find method problem

I find this line in the ZenTest source code:
result = #test_mappings.find { |file_re, ignored| filename =~ file_re }
The #test_mappings and result here are both Array object, but I didn't found 'find' method on Array class in ruby doc. I also tried it on irb:
irb(main):014:0> Array.respond_to? :find
=> false
irb(main):015:0> [1,2,3].find
LocalJumpError: no block given
from (irb):15:in `find'
from (irb):15:in `each'
from (irb):15:in `find'
from (irb):15
irb(main):016:0> [1,2,3].find{|x| x>1}
=> 2
Could any one explain it to me? How could find method also return an Array object? thanks in advance.
Array includes the Enumerable module, which adds the find method.
In your example you tested Array.respond_to. This will only return true for class methods of Array. find is an instance method, so respond_to? must be invoked on an instance of the class.
>> a = Array.new
=> []
>> a.respond_to? :find
=> true
Another sometimes useful trick is calling the 'methods' function which lists all the methods available to the instance of the object and using the grep method to filter out for something specific. It also gives you a good picture of what standard methods are provided by base classes without referring to docs.
a = Array.new
=> []
>> a.methods.grep /find/
=> ["find", "find_all"]

Resources