I have recently seen some code that I do not completely understand. There is an array named foo that contains instances of Proc objects. Then, an env object is used to setup an environment to work in:
env = Object.new
foo.each do |f|
env.instance_eval &f # what happens here?
end
What exactly happens when you open the object with instance_eval and pass &f as an argument? What happens to env at this point and the Proc itself?
The scope is changed for the proc, which is then evaluated in that context. Internally, all procs are stored in memory as a C struct which includes the self of the proc (the scope in which the proc was made). When you call instance_eval, the self value is manually changed in memory to the object you are calling instance_eval on. If you explore the ruby source code, you will find that it boils down to this function:
static VALUE
yield_under(VALUE under, VALUE self, VALUE values)
{
rb_thread_t *th = GET_THREAD();
rb_block_t block, *blockptr;
NODE *cref;
if ((blockptr = GC_GUARDED_PTR_REF(th->cfp->lfp[0])) != 0) {
block = *blockptr;
block.self = self; // <- This is where the scope changes!
th->cfp->lfp[0] = GC_GUARDED_PTR(&block);
}
cref = vm_cref_push(th, under, NOEX_PUBLIC, blockptr);
cref->flags |= NODE_FL_CREF_PUSHED_BY_EVAL;
if (values == Qundef) {
return vm_yield_with_cref(th, 1, &self, cref);
}
else {
return vm_yield_with_cref(th, RARRAY_LENINT(values), RARRAY_PTR(values), cref);
}
}
Note the line containing // <- This is where the scope changes!.
The Proc gets executed in the context of env. It is as if you are calling a method on env: the block has access to its instance variables and public and private methods.
env = Object.new
env.instance_variable_set :#test, "test"
class << env
private
def test
#test
end
end
env.instance_eval { #test } #=> "test"
env.instance_eval { test } #=> "test"
Related
What is the best way of chaining multiple custom methods together? I want to put the output of my method directly into the next method in an elegant way. The first one below is what I have now.
verified = search_verified(#providers)
matching = search_matching(verified)
deactivated = search_deactivated(matching)
networks = search_networks(deactivated)
designations = search_designations(networks)
disorders = search_disorders(designations)
age_groups = search_age_groups(disorders)
governing = search_governing(age_groups)
search_availabilities(governing)
maybe something more along the lines of:
search_verified(#providers)
>> search_matching
>> search_deactivated
>> search_networks
>> ....
You might want to use then to chain your methods and numbers parameter to simplify the blocks:
search_verified(#providers)
.then { search_matching(_1) }
.then { search_deactivated(_1) }
.then { search_networks(_1) }
.then { search_designations(_1) }
.then { search_disorders(_1) }
.then { search_age_groups(_1) }
.then { search_governing(_1) }
.then { search_availabilities(_1) }
You can do something along those lines. Ruby 2.6.0 introduces then, and the function composition operators << and >>.
You do have to select your methods with method(:method_name) because method_name by itself invokes the method and does not return it.
#providers.then(&
method(:search_verified) >>
method(:search_matching) >>
method(:search_deactivated) >>
method(:search_networks) >>
method(:search_designations) >>
method(:search_disorders) >>
method(:search_age_groups) >>
method(:search_governing) >>
method(:search_availabilities)
)
If you don't like the character "overhead". You could shrink the amount of characters by storing the method method in a shorter variable first:
fn = method(:method)
#providers.then(&
fn[:search_verified] >>
# ...
)
You could write that as follows.
METHODS = [:verified, :matching, :deactivated, :networks, :designations,
:disorders, :age_groups, :governing, :search_availabilities]
def doit(first_arg)
METHODS.reduce(first_arg) { |arg, meth| send(meth, arg) }
end
This would be called
doit(#providers)
For example:
def a(arg)
arg * 2
end
def b(arg)
arg + 1
end
def c(arg)
arg/5.0
end
METHODS = [:a, :b, :c]
doit(3)
#=> 1.4
One could alternatively write
def doit(first_arg)
METHODS.reduce(first_arg) { |arg, meth| method(meth).call(arg) }
end
doit(3)
#=> 1.4
One advantage of this approach is that if methods are added, removed or renamed is is only necessary to change the constant METHODS; the method doit is unaffected.
I've not actually tried this other than a quick attempt in the console, but from looking at: https://andersmurphy.com/2019/12/07/ruby-functional-programming.html it looks like something like the following should be possible:
pipe = -> *fns {fns.reverse.reduce {|f, g| -> x {f.(g.(x))}}}
add_one = -> x {x + 1}
times_two = -> x {x * 2}
add_one_and_times_two = pipe.(add_one, times_two)
add_one_and_times_two.(2)
=> 6
pipe.(add_one, times_two).(3)
=> 8
If you want to use this with methods you can possibly (this seems to work in the console) do something like:
def divide_by_three(x); x / 3.0 end
pipe.(
add_one,
times_two,
method(:divide_by_three)
).(4)
=> 3.3333333333333335
using the method function as shown in #3limin4t0r's answer.
If all the methods are in one class you can chain these methods by returning self in each method.
For the sake of clarity I take your example but the providers are just numbers.
class MyClass
def initialize
##providers = [2, 6, 4, 8, 7]
end
def search_matching
# do stuff
##providers.select!{ |n| n > 3 }
self
end
def search_deactivated
# do other stuff
##providers.select!{ |n| n < 8 }
self
end
def providers
##providers
end
end
MyClass.new.search_matching.search_deactivated.providers # [6, 4, 7]
it depends on what data do you really need and how you define the architecture of your code, i usually make a Service object.
if you only want to return only the last method output. return search_availabilities(governing).
If you need all variables, you can make it return an array with all the variables or the ones that you need. return [verified, matching, deactivated, ...].
I've been using Parametrized-pipelines in Jenkins and notice that while using parameters, the value is both useable from script scope as well as via params.variable.
PARAMETER == true
params.PARAMETER == true
In groovy, is it possible to add a variable to script scope from within a method? I would like to get similar functionality as the following...
// I don't want to have to declare value here
def function1(){
value = 1
}
def function2(){
assert value == 1
}
function1()
function2()
Is there a way to access value from within function2 without doing something like...
value = 0
def function1() {
value = 1
...
Could also do somthing like :
def f1() {
env.aaa = "hello"
}
def f2() {
assert aaa=="hello"
}
node{
f1()
f2()
}
Essentially setting it as an environment variable.
this pipeline works fine:
def f1(){
aaa = "hello"
}
def f2(){
assert aaa=="hello"
}
node{
f1()
f2()
}
the pipeline definition as actually an instance of org.jenkinsci.plugins.workflow.cps.CpsScript that extends groovy.lang.Script
so groovy script properties should work here.
You can use scope variable in script
import groovy.transform.Field
#Field List awe = [1, 2, 3]
def awesum() { awe.sum() }
assert awesum() == 6
http://docs.groovy-lang.org/2.4.9/html/gapi/groovy/transform/Field.html
Please can someone explain to me, why NOT initializing first_idx and last_idx causes the code not to run??
When I run it I get this error "undefined local variable or method last_idx". I know that the advice is to always initialize the variables, but I don't understand why. After all first_idx and last_idx will ALWAYS get a value inside the loop because the argument letter is always present in the string (in this particular problem).
I'd really appreciate some (simple) insight. Thank you!
P.S, I also know that the problem is easily solved using #index and #rindex in Ruby, but I'm not allowed to solve it using straightforward methods.
def find_for_letter(string, letter)
first_idx = nil
0.upto(string.length - 1) do |idx1|
if string[idx1] == letter
first_idx = idx1
break
end
end
last_idx = nil
(string.length - 1).downto(0) do |idx2|
if string[idx2] == letter
last_idx = idx2
break
end
end
if last_idx == first_idx
return [first_idx]
else
return [first_idx, last_idx]
end
end
def first_last_indices(word)
h = {}
word.chars.each do |char|
h[char] = find_for_letter(word, char)
end
h
end
Variables in block
From the Ruby Programming Language:
Blocks define a new variable scope: variables created within a block
exist only within that block and are undefined outside of the block.
Be cautious, however; the local variables in a method are available to
any blocks within that method. So if a block assigns a value to a
variable that is already defined outside of the block, this does not
create a new block-local variable but instead assigns a new value to
the already-existing variable.
a = 0
2.times do
a = 1
end
puts a #=> 1
b = 0
2.times do |i;b| # <- b will stay a block-local variable
b = 1
end
puts b #=> 0
2.times do |i|
c = 1
end
puts c #=> undefined local variable or method `c' for main:Object (NameError)
Refactoring your code
Iterating with chars and index
Here's a smaller method for your goal.
It keeps a hash with minmax indices for each character.
The default hash value is an empty array.
The method iterates over each character (with index).
If minmax array already contains 2 values :
it replaces the second one (max) with current index.
it adds current index to the array otherwise.
def first_last_indices(word)
minmax_hash = Hash.new { |h, k| h[k] = [] }
word.each_char.with_index do |char, index|
minmax = minmax_hash[char]
if minmax.size == 2
minmax[1] = index
else
minmax << index
end
end
minmax_hash
end
p first_last_indices('hello world')
{"h"=>[0], "e"=>[1], "l"=>[2, 9], "o"=>[4, 7], " "=>[5], "w"=>[6], "r"=>[8], "d"=>[10]}
With group_by
Here's another possibility. It uses group_by to get all the indices for each character, and minmax to get just the first and last indices :
def first_last_indices(word)
word.each_char.with_index
.group_by{ |c, _| c }.map{ |c, vs|
[c, vs.map(&:last).minmax.uniq]
}.to_h
end
p first_last_indices('hello world')
{"h"=>[0], "e"=>[1], "l"=>[2, 9], "o"=>[4, 7], " "=>[5], "w"=>[6], "r"=>[8], "d"=>[10]}
Even if you do not declare last_idx, you can still initialise it inside the loop, i.e.:
(string.length - 1).downto(0) do |idx2|
if string[idx2] == letter
last_idx = idx2 # works absolutely fine
break
end
end
However notice where you declared the variable. Its a local variable and hence its tied to the block you are in. Now when you try to access that variable outside the block, you get the error:
undefined local variable or method last_idx
To make the variable available outside the block, you have to declare it outside. That is what you are doing when you declare last_idx = nil before the block where its assigned a value.
UPDATE:
Though by using instance variables you can avoid declaration, the best practices suggests it should be used in cases where information that these variables have is relevant to all or almost all of the class. On the other hand, if the information is very much limited to this particular method use local variables.
This is just the way that local variables work.
If you use instance variables, Ruby will assume that they have been initialised inside the conditional block, but will not for local variables.
def find_for_letter(string, letter)
0.upto(string.length - 1) do |idx1|
if string[idx1] == letter
#first_idx = idx1
break
end
end
(string.length - 1).downto(0) do |idx2|
if string[idx2] == letter
#last_idx = idx2
break
end
end
if #last_idx == #first_idx
return [#first_idx]
else
return [#first_idx, #last_idx]
end
end
This works fine.
I'm trying to write a unit test against a single module function. This module collaborates with a few other modules, so I'd like to mock those modules out to isolate my system under test. Here's some simplified pseudo code:
local moduleFoo={}
local moduleBaz= require("moduleBaz")
moduleFoo.doSomething = function (arg)
if moduleBaz.bar.neatMethod(arg) then
--does something interesting
end
end
return moduleFoo
And here's the code for moduleBaz
local moduleBaz={}
moduleBaz.bar= {}
moduleBaz.bar.neatMethod=function(arg)
--does something neat
end
return moduleBaz
I'm trying to use the package.preload function to inject a mock instance of moduleBaz before my tests run, but it doesn't appear to work (i.e. the real instance of the moduleBaz is used in the test, not my mock)
Here's some psueudo test code:
package.loaded.moduleBaz= nil
local moduleBaz = {}
moduleBaz.bar = {}
moduleBaz.bar.neatMethod= function(guid) return true end
package.preload['moduleBaz'] = function ()
return moduleBaz
end
local foo= require("moduleFoo")
foo.doSomething('asdasdasda')--real moduleBaz is called, not my mock!
Any ideas what I'm doing wrong? I'm very new to Lua, and not at all comfortable with how scope is handled in the language!
You seem to be missing a return statement in your moduleBaz code
return moduleBaz
Why not use package.loaded as it gives you a simpler interface? package.loaded.moduleBaz would simply need to include whatever you'd want to return from your moduleBaz code. Something like this should work or give you an idea:
package.loaded.moduleBaz = {
bar = {
neatmethod = function(arg)
-- your mock code here
end,
}
}
Then require('moduleBaz') would simply return that object you just created.
I cannot reproduce the issue with your setup either. The files I used are below; notice that I added return moduleBaz as I described above, but this is the only change I made:
file moduleBaz.lua:
local moduleBaz={}
moduleBaz.bar= {}
moduleBaz.bar.neatMethod=function(arg)
print "baz"
return true
end
return moduleBaz
file moduleFoo.lua:
local moduleFoo={}
local moduleBaz= require("moduleBaz")
moduleFoo.doSomething = function (arg)
if moduleBaz.bar.neatMethod(arg) then
print "foo"
end
end
return moduleFoo
file testFoo.lua
package.loaded.moduleBaz= nil
local moduleBaz = {}
moduleBaz.bar = {}
moduleBaz.bar.neatMethod= function(guid) print "mock" return true end
package.preload['moduleBaz'] = function ()
return moduleBaz
end
local foo= require("moduleFoo")
foo.doSomething('asdasdasda')--real moduleBaz is called, not my mock!
When I run this, I get mock\nfoo\n printed as expected.
Please help me understand WHY the following works.
class Dog
def bark
"woof"
end
end
bark_string = Dog.new.bark
puts bark_string # "woof" - a string at this point
ref_to_bark = -> { bark_string } # the string a moment ago is now the method again
ref_to_bark.call # "woof"
Why does wrapping a reference to a method in a proc/lambda return a reference to the original method? It baffles me.
It doesn't. ref_to_bark just returns bark_string, the bark method is not called.
Lambdas (and blocks, and procs) in Ruby are closures; this means that local variables available in the same scope as the lambda is defined are accessible inside the lambda. For example:
foo = 42
l = lambda{ p foo }
l.call()
#=> 42
The above should not be any more surprising than the fact that this code works:
x = 17
[1,2,3].map do |n|
n+x # Whoa, you can use _x_ here?!
end
#=> [18,19,20]
It's slightly more surprising when you do something like this:
def make_adder( x )
->(y){ x+y }
end
add10 = make_adder(10)
z = add10.call(32) #=> 42
Again, the local variable x (the parameter passed to the method) is "closed over" by the lambda, its value preserved for reference whenever the lambda is invoked.
So in your example the lambda is just "capturing" the bark_string variable and returning its value later on. Your method is never invoked a second time.
Note that a closure captures the variable itself, not just the object referred to by the variable:
x = "hello"
y = x # reference the same _object_
l = ->{ x } # close over the _variable_ itself
x = "world" # change the variable to point to a new object
p y, #=> "hello" (same object as the original)
l[] #=> "world" (new object)
A lambda defined using -> is called lambda literal. Sometimes called stabby lambda. The following will also return the same result.
ref_to_bark = lambda { bark_string }
Or
ref_to_bark = lambda { "woof" }