Constant Lookup with instance_eval in Ruby 1.9 - ruby-on-rails

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.

Related

ObjectSpace#allocation_class_path not working as expected

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"

What do functions with the same name do?

I'm learning ruby and can't for the life of it figure out what this does:
def topic_list
topics.map(&:name).join(",")
end
def topic_list=(names)
if names.present?
topics = names.split(",")
self.topics = topics.map do |n|
unless topic = Topic.find_by(slug: n)
topic = Topic.create!(name: n)
end
topic
end
end
end
Why would two functions have the same name? Does the first function call the second one?
topic_list is a getter and topic_list= is a setter method. No they are not the same methods.
This question Trying to learn / understand Ruby setter and getter methods will be helpful as a basic food for this concept. So Read it.
The line self.topics = topics.map... in the method topic_list=, and topics.map(&:name).join(",") line in the method topic_list smells there is one getter called topics and setter topics=. I am sure(If you tell this code works altough) about this by looking at your code.
To clarify Arup Rakshit's answer a little more:
Ruby allows having some "weird" characters in its methods, that you would normally not see in other languages. The most notably ones are ?, ! and =.
So, you can define the methods:
def answered?()
not #answer.nil?
end
def answer!()
#answer = Answer.new
end
def answer()
#answer
end
def answer=(answer)
#answer = answer
end
For someone coming from, say, PHP, this looks strange. But really they are all different methods! I have added brackets everywhere, to clarify my point, but in Ruby, you can leave them off, so when a method does not take arguments,people generally leave them off.
def answer()
end
def answer
end
Are the same.
The last two, the answer() and answer=(), are often refered to as getters and setters. The first is often used to test something binary, like has_question? or valid?. The second is referred to as a bang method, answer!, more often used in methods that modify itself, like some_array.merge!(other_array), which modifies some_array.

Ruby on Rails: Passing argument to singleton

I have a Rails app that repeatedly talks to another Web server through a wrapper, and I'd like to stick the wrapper in a Singleton class so it's not recreated for every request. Easy enough, I thought:
class AppWrapper < Wrapper
include Singleton
end
...
wrapper = AppWrapper.instance "url"
Only it doesn't work:
wrong number of arguments (0 for 1)
/usr/lib/ruby/1.8/singleton.rb:94:in `initialize'
/usr/lib/ruby/1.8/singleton.rb:94:in `new'
/usr/lib/ruby/1.8/singleton.rb:94:in `instance'
Wrapper.initialize needs an argument, and apparently it's not getting passed through, since line 94 in question says
#__instance__ = new # look Ma, no argument
How do I work around this? Redefining initialize in AppWrapper doesn't seem to help, and
mucking around with Wrapper to separate "set URL" from "initialize" seems suboptimal.
Passing argument to singleton
class Parameterized_Singleton
def initialize(a)
#pdf = a
puts #pdf
end
def self.instance(p)
begin
##instance =Parameterized_Singleton.new(p)
private_class_method :new
rescue NoMethodError
# return ##instance # or you can return previous object
puts "Object Already Created"
exit
end
return ##instance
end
def scanwith(b)
puts "scan"
end
def show_frequence_distribution
puts "fd"
end
def show_object_number(a)
puts "no"
end
end
Parameterized_Singleton.instance(20).show_object_number(10)
Parameterized_Singleton.instance(10).show_object_number(20)
Are you sure you need a singleton and not a factory . Refer this
I asked this question while I was still getting my head around Ruby, and it seems so naive now. The easy solution is to just store the Wrapper object in a member variable and use ||= to initialize it only if it hasn't been set yet:
class WrapperUserClass
def initialize
#wrapper = nil # Strictly speaking unnecessary, but it's a bit clearer this way
end
def wrapper
#wrapper ||= Wrapper.new(foobar)
end
def do_something
wrapper.booyakasha
end
end
Since you mention something about editing Wrapper as a solution, can't you just use Wrapper directly and do this?
class Wrapper; include Singleton; end
If not, you could use something like this, which will just make sure AppWrapper.new isn't called more than once:
class AppWrapper
def self.new(*args)
class << app_wrapper = Wrapper.new(*args)
include Singleton
end
app_wrapper
end
end
If you need the singleton "Klass.instance" method, you'll have to take either take out the parameter in Wrapper#initialize, or just redefine Singleton#instance to take arguments optionally and passes them to the call to new on line 94.

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.

Rails: I can't call a function in a module in /lib - what am I doing wrong?

I have a module saved in /lib as test_functions.rb that looks like this
module TestFunctions
def abc
puts 123
end
end
Going into ruby script/runner, I can see that the module is loading automatically (good ol' convention over configuration and all that...)
>> TestFunctions.instance_methods
=> ["abc"]
so the method is known, let's try calling it
>> TestFunctions.abc
NoMethodError: undefined method `abc' for TestFunctions:Module from (irb):3
Nope. How about this?
>> TestFunctions::abc
NoMethodError: undefined method `abc' for TestFunctions:Module from (irb):4
Test
Nope again.
defined?(TestFunctions::abc) #=> nil, but
TestFunctions.method_defined? :abc #=> true
Like I said at the top, I know I'm being dumb, can anyone de-dumb me?
If you want Module-level functions, define them in any of these ways:
module Foo
def self.method_one
end
def Foo.method_two
end
class << self
def method_three
end
end
end
All of these ways will make the methods available as Foo.method_one or Foo::method_one etc
As other people have mentioned, instance methods in Modules are the methods which are available in places where you've included the Module
I'm going to try to summarise the various answers myself, since each had something valuable to say, but none really got to what I now realise is probably the best response:
I was asking the wrong question because I was doing it wrong.
For reasons I can no longer explain, I wanted a set of completely stand-alone functions in a library, which represented methods I was trying to DRY out of my classes. That can be achieved, using things like
module Foo
def self.method_one
end
def Foo.method_two
end
class << self
def method_three
end
end
def method_four
end
module_function :method_four
end
I could also include my module, either within a class, in which case the methods become part of the class or outside, in which case they are defined on whatever class I'm running inside (Object? Kernel? Irb, if I'm interactive? Probably not a great idea, then)
The thing is, there was no good reason not to have a class in the first place - I'd somehow got on to a train of thought that took me down an seldom-used and frankly slightly weird branch line. Probably a flashback to the days before OO became mainstream (I'm old enough that up to today I've spent a lot more years writing procedural code).
So the functions have moved into a class, where they seem pretty happy, and the class methods thus exposed are being cheerfully used wherever necessary.
You can also use module_function like so:
module TestFunctions
def abc
puts 123
end
module_function :abc
end
TestFunctions.abc # => 123
Now you can include TestFunctions in class and call "abc" from within TestFunctions module.
I messed with this for a while and learned several things. Hopefully this will help someone else out. I am running Rails 3.2.8.
My module (utilities.rb) looks like this and is in the /lib directory of my rails app:
module Utilities
def compute_hello(input_string)
return "Hello #{input_string}"
end
end
My test (my_test.rb) looks like this and is in the /test/unit directory of my rails app:
require "test_helper"
require "utilities"
class MyTest < ActiveSupport::TestCase
include Utilities
def test_compute_hello
x = compute_hello(input_string="Miles")
print x
assert x=="Hello Miles", "Incorrect Response"
end
end
Here are a few things to note: My test extends ActiveSupport::TestCase. This is important because ActiveSupport adds /lib to the $LOAD_PATH. (seehttp://stackoverflow.com/questions/1073076/rails-lib-modules-and)
Secondly, I needed to both "require" my module file, and also "include" the module. Lastly, it is important to note that the stuff that gets included from the module essentially gets placed in the test class. So... be careful that the module that you include doesn't start with "test_". Otherwise, Rails will attempt to run your module method as a test.
You can't call a method in a Module directly. You need to include it in a class. Try this:
>> class MyTest
>> include TestFunctions
>> end
=> MyTest
>> MyTest.new.abc
123
=> nil
You need to include the module
include Testfunctions
Then 'abc' will return something.
You need to prefix your function with the module name because modules are not classes:
Your /lib/test_functions.rb:
module TestFunctions
def TestFunctions.abc
puts 123
end
end
Your code using the module method:
require 'test_functions'
TestFunctions.abc
Today you can do it using module_function notation.
module TestFunctions
def abc
puts 123
end
end
Now TestFunctions.abc prints "123"
A little more about module_function: https://apidock.com/ruby/Module/module_function
Try this code:
service = Class.new { extend TestFunctions }
service.method_four

Resources