ObjectSpace#allocation_class_path not working as expected - ruby-on-rails

Using Ruby Version 2.1.2
class A
def foo
ObjectSpace::trace_object_allocations do
obj = Object.new
p "#{ObjectSpace::allocation_class_path(obj)}"
end
end
end
A.new.foo #=> "Class"
The above method is expected to return back the allocation class path of the object.
But I am getting an empty string back instead of "Class".
Is it an implementation bug in the method or the documentation is not updated.
Here is link
Thanks

I think you have just found a possible bug in core.
I've already reported the issue to devs, bug #9938.
I do suspect lookup_allocation_info from object_tracing.c but further investigation required.
UPDATE:
Koichi Sasada (aka ko1) already identified the cause and promised to be fixed in forthcomming Ruby 2.2.
In the meantime you may use a workaround to manually cache object names in a module with
ObjectSpace.each_object(Module){|o| o.name}
placed before objects tracing code.

I think you try like this,
require 'objspace'
class A
def foo
ObjectSpace::trace_object_allocations do
obj = Object.new
p "#{ObjectSpace::allocation_class_path(obj)}"
end
end
end
A.new.foo
#"Class"
=> "Class"

Related

Ruby: undefined method for not initialized

Note: There are numerous answers explaining that you can get this error when you subclass ActiveRecord::Base and add an #initialize without super. No answer explains what is actually happening.
I am working in someone else's code and I have an HTTParty service in a Rails app with the following class hierarchy. Note the subclass #initialize with a differing signature to the parent class.
module A
class Base
include HTTParty
...
end
end
module A
class User < Base
def initialize(user)
#user = user
end
end
end
module A
class PublicUser < User
def initialize(opts = {})
#limit = opts[:limit]
# no call to super
end
end
end
Locally there are no problems with this, but in SemaphoreCI the following results:
A::PublicUser.new(limit: 1).some_method
undefined method `some_method' for #<A::PublicUser not initialized>
I can't find any documentation about the "not initialized" message. What causes this sort of failure?
OK, I got it. I also tagged your question with ruby-on-rails, since plain good ruby would rare give such a weird behaviour.
You have experienced two different issues, more or less unrelated.
#<A::PublicUser not initialized> is a result of (sic!) calling inspect on A::PublicUser. So, ruby tries to format an error message and—voilà—the class is printed out that way.
Rails messes with you, as well as with constant lookup. A::Base name conflicts with ActiveRecord::Base, and guess what is resolved when class User < Base is met. To replicate this behaviour you might open a console and do: class Q < ActiveRecord::Base; end; Q.allocate, resulting in #<Q not initialized>. (Do you already love Rails as I do?)
To fix this, either explicitly specify class User < A::Base or rename Base to MyBase. Sorry for suggesting that.

Rails 3.2.21 and Ruby 2.2.0 breaking Time.zone.parse

With Rails 3.2.21 and Ruby 2.2.0p0 the time zone parser is broken. With Ruby 2.1.2 this was working just fine.
[1] pry(main)> Time.zone.parse("2015-01-12")
NoMethodError: undefined method `year' for nil:NilClass
from /Users/user/.rvm/gems/ruby-2.2.0/gems/activesupport-3.2.21/lib/active_support/values/time_zone.rb:275:in `parse'
Now I know that you can replace it with Time.parse("2015-01-12").localtime but this breaks functionality in my apps. Are there any known fixes for this?
TLDR: rails bug that has been fixed, first fixed version on the 3.2 branch is 3.2.22
Ruby 2.2 changes how default arguments are resolved when there is a name ambiguity:
def now
...
end
def foo(now = now)
end
In older versions of ruby calling foo with no arguments results in the argument now Being set to whatever the now() method calls. In ruby 2.2 it would instead be set to nil (and you get a warning about a circular reference)
You can resolve the ambiguity by doing either
def foo(now = now())
end
Or
def foo(something = now)
end
(And obviously changing uses of that argument)
Apparently the way it used to work was a bug all along. Rails had a few places where this bad behaviour was relied on, including in AS::Timezone.parse. The fix was backported to the 3-2-stable branch and eventually released as part of 3.2.22.
The commit to rails master fixing the issue has a link to the ruby bug filed about this
So, this was your original situation:
class Dog
def do_stuff(x, y=2)
puts x + y
end
end
d = Dog.new
d.do_stuff(1)
--output:--
3
But, the code for do_stuff() has changed, and now you are faced with something similar to this:
class Dog
def do_stuff(x, y=nil)
puts x + y
end
end
d = Dog.new
d.do_stuff(1)
--output:--
1.rb:4:in `+': nil can't be coerced into Fixnum (TypeError)
from 1.rb:4:in `do_stuff'
from 1.rb:10:in `<main>'
Blasted !##$!##$!##$!##$!#### developers!!!
In ruby, you can use alias_method() to create an additional name for a method:
class Dog #Reopen the previously defined Dog class.
alias_method :orig_do_stuff, :do_stuff #Create additional name for do_stuff()
#Now redefine do_stuff():
def do_stuff(x, y=2) #Use a better default value for y.
orig_do_stuff(x, y) #Call the original method.
end
end
d.do_stuff(1)
--output:--
3
According to the rails docs, Time.zone() returns a TimeZone object, so that's the class that defines parse(), which is the method you want to alias. So, the code would look like this:
class Timezone #Re-open the Timezone class.
alias_method :orig_parse, :parse #Create an additional name for parse().
def parse(str, now=now()) #Now, redefine parse().
orig_parse(str, now) #Call the original parse() method.
end
end
Then, you can call parse() like you always have:
Time.zone.parse("2015-01-12")
I guess you should put the code inside app/helpers/application_helper.rb. See if that works.
I think the above would be considered an Adapter pattern. Although, because the plug already fits--it just does't produce the results you want--it might be considered a Decorator pattern. So, now you can put on your resume that you use custom Adapter and/or Decorator patterns in your rails code. :)
Extending 7stud's answer, since parse method is defined in ActiveSupport::TimeZone class, you can simply open it and make this slight change accordingly.
You may place this under config/initializers/timezone.rb or refer this question for alternative places to pace this.
class ActiveSupport::TimeZone
alias_method :orig_parse, :parse #Create an additional name for parse().
def parse(str, now=now()) #Now, redefine parse().
orig_parse(str, now) #Call the original parse() method.
end
end

How to instantiate class from name string in Rails?

How we can instantiate class from it's name string in Ruby-on-Rails?
For example we have it's name in database in format like "ClassName" or "my_super_class_name".
How we can create object from it?
Solution:
Was looking for it myself, but not found, so here it is.
Ruby-on-Rails API Method
name = "ClassName"
instance = name.constantize.new
It can be even not formatted, we can user string method .classify
name = "my_super_class"
instance = name.classify.constantize.new
Of course maybe this is not very 'Rails way', but it solves it's purpose.
klass = Object.const_get "ClassName"
about class methods
class KlassExample
def self.klass_method
puts "Hello World from Class method"
end
end
klass = Object.const_get "KlassExample"
klass.klass_method
irb(main):061:0> klass.klass_method
Hello World from Class method
Others may also be looking for an alternative that does not throw an error if it fails to find the class. safe_constantize is just that.
class MyClass
end
"my_class".classify.safe_constantize.new # #<MyClass:0x007fec3a96b8a0>
"omg_evil".classify.safe_constantize.new # nil
You can simply convert a string and initialize a class out of it by:
klass_name = "Module::ClassName"
klass_name.constantize
To initialize a new object:
klass_name.constantize.new
I hope this turns out to be helpful.
Thanks!
I'm surprised nobody is considering security and hacking in their responses. Instantiation of an arbitrary string that likely came even indirectly from user input is asking for trouble and hacking. We all should/must be whitelisting unless we're sure the string is fully controlled and monitored
def class_for(name)
{
"foo" => Foo,
"bar" => Bar,
}[name] || raise UnknownClass
end
class_for(name_wherever_this_came_from).create!(params_somehow)
How you would know the appropriate params arbitrarily without having a whitelist would be challenging but you get the idea.

Constant Lookup with instance_eval in Ruby 1.9

The Short and Sweet
Running this code in Ruby 1.9:
FOO = "global constant"
class Something
FOO = "success!"
def self.handle &block
self.new.instance_eval &block
end
end
class Other
FOO = "wrong constant"
def self.handle
Something.handle{FOO}
end
end
puts Something.handle{FOO}
puts Other.handle
I get "success!" and "wrong constant". How can I get both calls to print "success!"? This is not a contrived exercise for fun - I wouldn't waste people's time for that. I have a real-world problem, and I've whittled it down to the simplest possible example that demonstrates the issue. Read on for the "why".
A More Thorough Explanation
Calling Something.handle{FOO} works correctly. Ruby uses the definition of FOO given in the Something class. However, If I try to call it the same way from another class, it gives me the definition of FOO for that class.
I thought the idea of tighter constant looksups in Ruby 1.9 was to avoid problems like this. instance_eval is supposed to use the scope of its receiver (self.new, in this case), not the scope of the calling block. It works for things like instance variables, but not for constants. This is not a problem of precedence - remove the "global" and "wrong" constants, and ruby still won't be able to find the remaining correct constant.
The real-world problem I'm having is that I have a module with several classes. I have a method that accepts a block, and runs the block in the context of the module. Inside that block, I want to be able to refer to those classes by their short names (like I can anywhere in the module itself), rather than having to prepend the module name.
This is painful:
ThirdPartyApis::MyAnswerSite.connection question.id, answer.id do |question_id, answer_id|
question = ThirdPartyApis::MyAnswerSite::Question.find question_id
answer = ThirdPartyApis::MyAnswerSite::Answer.find answer_id
ThirdPartyApis::MyAnswerSite::Solution.new question, answer
end
This is pleasant:
ThirdPartyApis::MyAnswerSite.connection question.id, answer.id do |question_id, answer_id|
question = Question.find question_id
answer = Answer.find answer_id
Solution.new question, answer
end
Summary
So that's the long-winded explanation. Please don't offer workarounds that don't address my question. I appreciate the desire to explore other avenues, but my question is simple: can ONLY the Something class by modified, in a way that prints "success!" twice at the end? That's the test case, and any solution that fits this is my accepted answer, and its author is my personal hero for the week. Please!
I got it to work using the sourcify gem (gem install sourcify):
require 'sourcify'
FOO = "global constant"
class Something
FOO = "success!"
def self.handle &block
(self.new.instance_eval(block.to_source)).call
end
end
class Other
FOO = "wrong constant"
def self.handle
Something.handle{FOO}
end
end
puts Something.handle{FOO}
=> success!
puts Other.handle
=> success!
edit: Oops, you could see where I was working on using bindings too, removed that code.

Rails Reserved Class Names

I tried creating a model called "class" (as in a graduating class of students), and encountered all kinds of problems. What are some other words or class names to avoid in Rails?
Some links I've found:
http://juicebar.wordpress.com/2007/05/30/reserved-words-in-rails/
http://railsforum.com/viewtopic.php?id=22242
This page has a very long list of words not to use:
https://reservedwords.herokuapp.com/words
Because 'class' comes up very commonly as a name with metaprogamming, I think the accepted ruby alternative is 'klass'. This is obviously a different context from your graduating class situation, but maybe still helpful.
You've got most of them there. Obviously, you also need to avoid the Ruby keywords:
alias and BEGIN begin break case class def defined
do else elsif END end ensure false for if
in module next nil not or redo rescue retry
return self super then true undef unless until when
while yield
(from http://www.zenspider.com/Languages/Ruby/QuickRef.html#4).
Also, don't name a model Transaction (but the generator warns about that!).
Class is a built-in ruby class. It is what classes is an instance of.
class Foo
end
puts Foo.class
# => Class
Overriding that blows up the entire object structure in Ruby.

Resources