where to find Native Ruby source code such as instance_exec? - ruby

i know that there is the function "method(:instance_exec).source_location" to look for source code, but only works for code in gems. But it returns null in this particular case since the method is from native ruby. i am googling but i am not able to find where this is written.
in my particular case, i want to have a better idea of how instance_exec changes the values of the scope of a proc
going on a tangent, im looking to do this for college
precondition { divisor != 0 }
postcondition { |result| result * divisor == dividend}
def divide(dividend, divisor)
dividend / divisor
end
i know instance_exec changes 'self' value in the proc scope. i want to add 'divisor' to the scope of { divisor != 0 } proc when i run it, and i think that reading the implementation of instance_exec would be very helpfull.
Since it is for college, i cant use any gem to do this, only standard ruby
PS right_click + Go To + Implementation only shows me
def instance_exec(*args)
# This is a stub implementation, used for type inference (actual method behavior may differ)
yield
end

i know that there is the function "method(:instance_exec).source_location" to look for source code, but only works for code in gems.
That's not true. Method#source_location works just fine for methods which are not in gems:
module Foo
def bar; end
end
p Foo.instance_method(:bar).source_location
#=> ['test.rb', 2]
There is no gem here. In fact, it even works if I don't use any file at all, just by using ruby -e on the command line; the result will be ['-e', 2] in that case.
On the other hand, if there is no Ruby source, then it will return nil even for gems.
The return value of Method#source_location has nothing to do with whether or not the method is from a gem, the only thing that matters is whether there even is a Ruby source to locate.
But it returns null in this particular case since the method is from native ruby.
It shouldn't return null, it should return either a two-element array or nil, but never null. There seems to be something seriously broken with your Ruby installation if that is really the case.
i am googling but i am not able to find where this is written.
It will be somewhere in the source code of the Ruby implementation you are using. You haven't specified which implementation you are using, so I cannot tell you exactly, but here are some examples:
In Rubinius, it is implemented in core/basic_object.rb:
def instance_exec(*args, &prc)
raise ::LocalJumpError, "Missing block" unless block_given?
env = prc.block
return prc.ruby_method.call(*args) if prc.ruby_method
lexical_scope = env.lexical_scope
if ::ImmediateValue === self
lexical_scope = lexical_scope.using_disabled_scope
else
sc = ::Rubinius::Type.object_singleton_class(self)
lexical_scope = lexical_scope.using_current_as(sc)
end
return env.call_under(self, lexical_scope, true, *args)
end
In TruffleRuby, it is implemented in src/main/java/org/truffleruby/core/basicobject/BasicObjectNodes.java:
#CoreMethod(names = "instance_exec", needsBlock = true, rest = true)
public abstract static class InstanceExecNode extends CoreMethodArrayArgumentsNode {
public static InstanceExecNode create() {
return InstanceExecNodeFactory.create(null);
}
#Child private CallBlockNode callBlockNode = CallBlockNode.create();
abstract Object executeInstanceExec(Object self, Object[] args, RubyProc block);
#Specialization
protected Object instanceExec(Object receiver, Object[] arguments, RubyProc block) {
final DeclarationContext declarationContext = new DeclarationContext(
Visibility.PUBLIC,
new SingletonClassOfSelfDefaultDefinee(receiver),
block.declarationContext.getRefinements());
return callBlockNode
.executeCallBlock(declarationContext, block, receiver, block.block, arguments);
}
#Specialization
protected Object instanceExec(Object receiver, Object[] arguments, NotProvided block) {
throw new RaiseException(getContext(), coreExceptions().localJumpError("no block given", this));
}
}

Related

Ruby: Retrieving values from method parameters

I'm trying to figure out how I can create a method in Ruby where I can retrieve values from the method's parameters such as strings/integers.
For example, if this were a function coded in C, it might be done similar to this:
main()
{
int value;
GetAnIntegerValue(value);
printf("The value is %d", value);
}
// The "value" integer variable is passed to it, and updated accordingly because of the use of the ampersand prior to the parameter
GetAnIntegerValue(&int value)
{
value = 5;
}
// The output would be "The value is 5"
I think the term for this is pass by value but I'm not sure. My mind is a little vague on this area and I couldn't find many decent results.
Here's my example Ruby function, the array that the parameters are being assigned to is only local to the class which is the reason for this usage:
def getRandomWordAndHint(&RandomWord, &RandomHint)
randIndex = rand(7)
RandomWord = EnglishLevel1Word[randIndex]
RandomHint = EnglishLevel1Hint[randIndex]
end
Cheers!i
Ruby is pass-by-value. Always. No exceptions. You cannot do pass-by-reference in Ruby.
What you can do, is put the object you want to change into some sort of mutable container:
class MutableCell
attr_accessor :val
def initialize(val)
self.val = val
end
end
def change_the_value(cell)
cell.val = 5
end
value = MutableCell.new(42)
change_the_value(value)
value.val
# => 5
Of course, you can just use an Array instead of writing your own MutableCell class, this is just for demonstration.
However, mutable state is a bad idea in general, and mutating arguments passed to methods is a really bad idea especially. Methods know about their own object (i.e. self) and thus can safely modify it, but for other objects, that's generally a no-go.

Ruby: Combine two similar methods into one?

I have two extremely similar methods in my Ruby object for a Rails app. I know they can be combined, I just don't know how. (Extra points if you can find a more beautiful way to handle possible nils besides return unless, not using #try.)
def is_portal_admin?(resource)
return unless resource.user && resource.user.clinic_memberships.any?
memberships = resource.user.clinic_memberships.collect { |membership| membership.portal_admin? }
memberships.include?(true)
end
def is_staff_admin?(resource)
return unless resource.user && resource.user.clinic_memberships.any?
memberships = resource.user.clinic_memberships.collect { |membership| membership.staff_admin? }
memberships.include?(true)
end
How about:
def is_admin_of_type?(type, resource)
return unless resource.user && resource.user.clinic_memberships.any? && type
memberships = resource.user.clinic_memberships.collect { |membership| membership.send("#{type}_admin?") }
memberships.include?(true)
end
If someone gives a nonexistent type, it'l throw NoMethodError. Also, it's forward compatible if you add more admin types.
Instead of your collect and include? mechanism, you can simply use any?. If clinic_memberships always return an array (which it does if it is e.g. a has_many association), you don't even need to check that.
def has_membership?(resource, &block)
return unless resource.user
resource.user.clinic_memberships.any?(&block)
end
This can then be called like
has_membership?(resource, &:portal_admin?)
which is equivalent to
has_memberhsip?(resource){|m| m.portal_admin?}
def is_admin?(resource, kind)
if resource.user && resource.user.clinic_memberships.any?
!!resource.user.clinic_memberships.detect { |membership| membership.send("#{kind}_admin?") }
end
end
If you don't enter the if branch, nil is returned, so doing your conditional like above will yield the same result as an explicit return unless...
Add a second parameter and pass :staff or :portal (or "staff" or "portal"). Using "send" will eval at run time into "staff_admin?" or "portal_admin?"
Using detect instead of collect + include? will return an object if at least one found and the !! double negates to turn it into a true/false result.
I'd personally simply do it this way since that resource.user.clinic_memberships.any? is moot in grand scheme of things:
def is_admin?(resource, kind)
!!resource.user.clinic_memberships.detect { |membership| membership.send("#{kind}_admin?") } if resource.user
end
If you're actually trying to guard against clinic_memberships being nil, then you do need the 2nd half of the conditional, but drop the ".any?", otherwise you'll get an error testing any? against nil.
def is_portal_admin?(resource)
is_admin_of_type?(resource, :portal)
end
def is_staff_admin?(resource)
is_admin_of_type?(resource, :staff)
end
def is_admin_of_type?(resource, type)
if (user = resource.user)
user.clinic_memberships.any? { |ms| ms.send("#{type}_admin?") }
end
end
It's redundant to check if there are memberships.
You can add || false after the conditinal so you make sure your method? returns a boolean.
You can make is_admin_of_type? private.

Ruby: tap writes on a read?

So if I understand correctly Object#tap uses yield to produce a temporary object to work with during the execution of a process or method. From what I think I know about yield, it does something like, yield takes (thing) and gives (thing).dup to the block attached to the method it's being used in?
But when I do this:
class Klass
attr_accessor :hash
def initialize
#hash={'key' => 'value'}
end
end
instance=Klass.new
instance.instance_variable_get('#hash')[key] # => 'value', as it should
instance.instance_variable_get('#hash').tap {|pipe| pipe['key']=newvalue}
instance.instance_variable_get('#hash')[key] # => new value... wut?
I was under the impression that yield -> new_obj. I don't know how correct this is though, I tried to look it up on ruby-doc, but Enumerator::yielder is empty, yield(proc) isn't there, and the fiber version... I don't have any fibers, in fact, doesn't Ruby actually explicitly require include 'fiber' to use them?
So what ought have been a read method on the instance variable and a write on the temp is instead a read/write on the instance variable... which is cool, because that's what I was trying to do and accidentally found when I was looking up a way to deal with hashes as instance variables (for some larger-than-I'm-used-to tables for named arrays of variables), but now I'm slightly confused, and I can't find a description of the mechanism that's making this happen.
Object#tap couldn't be simpler:
VALUE
rb_obj_tap(VALUE obj)
{
rb_yield(obj);
return obj;
}
(from the documentation). It just yields and then returns the receiver. A quick check in IRB shows that yield yields the object itself rather than a new object.
def foo
x = {}
yield x
x
end
foo { |y| y['key'] = :new_value }
# => {"key" => :new_value }
So the behavior of tap is consistent with yield, as we would hope.
tap does not duplicate the receiver. The block variable is assigned the very receiver itself. Then, tap returns the receiver. So when you do tap{|pipe| pipe['key']=newvalue}, the receiver of tap is modified. To my understanding,
x.tap{|x| foo(x)}
is equivalent to:
foo(x); x
and
y.tap{|y| y.bar}
is equivalent to:
y.bar; y

Turning an argument into a receiver

I created the following extension
class String
def is_a_number? s # check if string is either an INT or a FLOAT (12, 12.2, 12.23 would return true)
s.to_s.match(/\A[+-]?\d+?(\.\d+)?\Z/) == nil ? false : true
end
end
How can I make it work as a chained method?
is_a_number?("10") # returns true
"10".is_a_number? # returns an error (missing arguments)
Update
Thanks sawa, mikej and Ramon for their answers. As suggested, I changed the class to Object and got rid of the argument (s):
class Object
def is_a_number? # check if string is either an INT or a FLOAT (12, 12.2, 12.23 would return true)
to_s.match(/\A[+-]?\d+?(\.\d+)?\Z/) != nil
end
end
It now works perfectly fine:
23.23.is_a_number? # > true
Thanks guys...
When you write "10".is_a_number?, you already have the object "10" you want to check for, which is the receiver of is_a_number?, so your method doesn't need to take any parameters.
Because match is an instance method on String, you don't need to specify a receiver for it. It will just operate on the same object on which is_a_number? was called. Because you know you already have a String object, the to_s isn't needed either.
Just write it as:
class String
# check if string is either an INT or a FLOAT (12, 12.2, 12.23 would return true)
def is_a_number?
match(/\A[+-]?\d+?(\.\d+)?\Z/) != nil
end
end
Ramon's suggestion that you may want to put your extension on Object rather than on String is a good point if you don't know if the object you're testing is going to be a string.
Also, what you're describing isn't really what is meant by method chaining; it's just calling a method on an object. Method chaining is where the return types of methods are set up so that several methods can be called in sequence e.g in Rails, something like
User.where(:name => 'Mike').limit(3) # find the first 3 Mikes
is an example of method chaining.
It seems like you want to patch Object instead of String (since you are calling to_s):
class Object
def is_a_number?
to_s.match(/\A[+-]?\d+?(\.\d+)?\Z/).nil?
end
end
You could also look at replacing with it with validates numericality: true on your model.

Module.nesting within instance_eval/exec or module_eval/exec

I came up with this question when I was trying to answer this. The following is an expected behaviour:
module A
p Module.nesting
end
# => [A]
But the following:
A.instance_eval{p Module.nesting}
A.instance_exec{p Module.nesting}
A.module_eval{p Module.nesting}
A.module_exec{p Module.nesting}
all return []. Why do these not work as the above?
Additional Question
Mu is too short suggested an interesting point. If that is correct, then Module.nesting would be one of the methods and variables that are dependent on the literal context like Method#source_location, __FILE__. Is this understanding correct? If so, can someone provide the inventory of these methods/variables that are dependent on the literal context? I think it would be useful for reference.
Warning: This is a little long and rambling. A bit of a tour through the Ruby source code seems necessary as the documentation is a bit thin. Feel free to skip to the end if you don't care about how sausage is made.
The 1.9.2 Module.nesting is implemented in eval.c like this:
static VALUE
rb_mod_nesting(void)
{
VALUE ary = rb_ary_new();
const NODE *cref = rb_vm_cref();
while (cref && cref->nd_next) {
VALUE klass = cref->nd_clss;
if (!(cref->flags & NODE_FL_CREF_PUSHED_BY_EVAL) &&
!NIL_P(klass)) {
rb_ary_push(ary, klass);
}
cref = cref->nd_next;
}
return ary;
}
I don't know the Ruby internals that well but I read the while loop like this: extract from the cref linked list all the nodes that are associated with a class-like thing but didn't come from eval. The NODE_FL_CREF_PUSHED_BY_EVAL bit is only set in here:
/* block eval under the class/module context */
static VALUE
yield_under(VALUE under, VALUE self, VALUE values)
A bit more grepping and reading reveals that instance_eval does end up going through yield_under. I'll leave checking instance_exec, module_eval, and module_exec as exercises for the reader. In any case, it looks like instance_eval is explicitly excluded from the Module.nesting list; this is, however, more of a distraction than anything else, it just means that you won't see something the evals mentioned.
So now the question is "what are NODE and rb_vm_cref() all about?".
If you look in node.h you'll see a bunch of NODE constants for the various Ruby keywords and language structures:
NODE_BLOCK
NODE_BREAK
NODE_CLASS
NODE_MODULE
NODE_DSYM
...
so I'd guess that NODE is a node in the instruction tree. This lines up nicely with my
Module.nesting seems to be more about talking to the parser
conjecture in the comment. But we'll keep going anyway.
The rb_vm_cref function is just a wrapper for vm_get_cref which is a wrapper for vm_get_cref0. What is vm_get_cref0 all about? It is all about this:
static NODE *
vm_get_cref0(const rb_iseq_t *iseq, const VALUE *lfp, const VALUE *dfp)
{
while (1) {
if (lfp == dfp) {
return iseq->cref_stack;
}
else if (dfp[-1] != Qnil) {
return (NODE *)dfp[-1];
}
dfp = GET_PREV_DFP(dfp);
}
}
All three arguments to the function come straight out of this control frame:
rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
The iseq appears to be an instruction sequence and the lfp and dfp are frame pointers:
VALUE *lfp; // cfp[6], local frame pointer
VALUE *dfp; // cfp[7], dynamic frame pointer
The definition of cref_stack is relevant:
/* klass/module nest information stack (cref) */
NODE *cref_stack;
So it looks like you're getting some sort of call or nesting stack out of rb_vm_cref.
Now back to the specifics at hand. When you do this:
module A
p Module.nesting
end
You'll have module A in the cref linked list (which is filtered to produce the Module.nesting result array) as you haven't hit the end yet. When you say these:
A.instance_eval { puts Module.nesting }
A.instance_exec { puts Module.nesting }
A.module_eval { puts Module.nesting }
A.module_exec { puts Module.nesting }
You won't have module A in cref anymore because you've already hit the end popped module A off the stack. However, if you do this:
module A
instance_eval { puts Module.nesting.inspect }
instance_exec { puts Module.nesting.inspect }
module_eval { puts Module.nesting.inspect }
module_exec { puts Module.nesting.inspect }
end
You'll see this output:
[A]
[A]
[A]
[A]
because the module A hasn't been closed (and popped off cref) yet.
To finish off, the Module.nesting documentation says this:
Returns the list of Modules nested at the point of call.
I think this statement combined with the review of the internals indicates that Module.nesting does in fact depend on the specific literal context in which it is called.
If anyone with more experience in the Ruby internals has anything to add I can hand this over to the SO community as a community wiki.
UPDATE: All of this applies to class_eval as well as it does to module_eval and it also applies to 1.9.3 as well as it does to 1.9.2.

Resources