I saw the following code posted on railscasts:
def each(&block)
block.call("<!-- #{#message}: #{#stop - #start} -->\n") if #headers["Content-Type"].include? "text/html"
#response.each(&block)
end
I was wondering where the &block was coming from? What's happening here?
&block is the way to define a method in Ruby that accepts a method and to explicitly assign the block to the variable referenced by the &.
Specifically, what that code does, it to accept a block, call the block passing a string as argument, then passing the block to the inner response.
At first glance, I would say that the specific middleware you saw is basically injecting the string
"<!-- #{#message}: #{#stop - #start} -->\n"
into the HTTP response body if the request content type is "text/html", which means an HTTP page.
If the request is for an HTTP page, than the response is served and that string is injected. Otherwise, if the request is for another kind of file (for instance a binary file, a text file, a javascript, etc), then the response is served without any modification.
The following example will help you to understand a little bit more the scope of &block
def foo(&block)
puts "foo"
bar(&block)
end
def bar(&block)
puts "bar"
block.call
end
foo do
puts "block"
end
# => foo
# => bar
# => block
Also note that the variable holding the block can be named anything, not necessary block
def foo(&my_block)
puts "foo"
bar(&my_block)
end
def bar(&another_block)
puts "bar"
another_block.call
end
foo do
puts "block"
end
# => foo
# => bar
# => block
On the given example each is defined to make the middleware behaves as a valid rack response body too (I really don't recommend that).
Rack expect the response body to be an instance of a class that defines each as a public method (an Array or even an ActionController::Response instance).
Any middleware after that one may use that each to operate over the response body. At the end of the middlewares chain rack itself will use it to process and dispatch the final response.
Related
A double colon(::) allows constants, instance methods, and class methods defined within a class or module, to be accessed from anywhere outside the class or module.
Looking at this example:
class Sample
VAR_SAMPLE="what is the difference?"
def self.show_var
return VAR_SAMPLE
end
def method2
return VAR_SAMPLE
end
end
puts Sample::show_var # => what is the difference?
puts Sample.show_var # => what is the difference?
puts Sample::new::method2 # => what is the difference?
puts Sample.new.method2 # => what is the difference?
What is the difference in accessing class method using dot(.) and a double colon (::) operator then? Any thoughts are appreciated.
The double colon :: namespace operator can also be used as a message sending operator. In other words,
foo.bar
can also be written as
foo::bar
Except when not.
In particular, . is always a message send. :: is usually a namespace lookup, except when it cannot possibly be. That means, for example, you cannot call a message that starts with an uppercase character, unless you also pass an argument list.
foo = Class.new do
def BAR; :method end
BAR = :constant
end
foo.BAR #=> :method
foo::BAR #=> :constant
foo::BAR() #=> :method
The fact that :: can also be used for message sends is a historical curiosity, and is banned in most style guides except for "class factories", i.e. methods that return classes. Imagine a web framework that is set up like this:
module Controller
def self.R(path)
Class.new(AbstractController) do
# a bunch of methods for dealing with routing to `path`
end
end
end
class IndexController < Controller::R '/index.html'
def get
render 'Welcome'
end
end
In this case, in some style guides, it would be acceptable to write Controller::R because even though R is a method, it returns a class, so it kind-of acts like one.
But this is a special case for certain DSLs and is only allowed in certain style guides. Most style guides disallow :: for message sends, because it is redundant with ., because it already has a another different meaning (namespace resolution), and because it doesn't behave like . in all cases.
What is the difference in accessing class method using dot(.) and a double colon (::) operator then?
On the one hand, you can say, there is no difference because when used as the message sending operator, they both do the exact same thing.
On the other hand, there is a difference in syntax, namely that foo::BAR isn't a message send, it is a namespace lookup which is completely different. from foo.BAR, which is a message send.
You can call ruby methods using following ways
using Dot (.), Double Colon (::), send method & method method
class Sample
VAR_SAMPLE="what is the difference?"
def self.show_var
return VAR_SAMPLE
end
def method2
return VAR_SAMPLE
end
end
puts Sample::show_var
puts Sample.show_var
puts Sample.send(:show_var)
puts Sample.method(:show_var).call
puts Sample::new::method2
puts Sample.new.method2
puts Sample.send(:new).send(:method2)
puts Sample.method(:new).call.method(:method2).call
# All the above will return `what is the difference?` only
Now consider method which is a private
class Sample
VAR_SAMPLE="what is the difference?"
private
def self.show_var
return VAR_SAMPLE
end
end
puts Sample::show_var # Will throw error private method `show_var' called for
puts Sample.show_var # Will throw error private method `show_var' called for
puts Sample.send(:show_var) # what is the difference?
puts Sample.method(:show_var).call # what is the difference?
Note:- Other than this you can call ruby methods using other metaprogramming methods as well.
I want to test a following method, which calls a module method with a block.
def test_target
MyModule.send do |payload|
payload.my_text = "payload text"
end
end
MyModule's structure is like following.
module MyModule
class Payload
attr_accessor :my_text
def send
# do things with my_text
end
end
class << self
def send
payload = Payload.new
yield payload
payload.send
end
end
How can I test whether MyModule receives send method with a block, which assigns "payload text" to payload.my_text?
Currently I'm only testing expect(MyModule).to receive(:send).once. I looked through and tried Rspec yield matchers but cannot get things done. (Maybe I've ben searching for wrong keywords..)
The easiest way is to insert a double as the yield argument, which you can make an assertion on.
payload = Payload.new
allow(Payload).to receive(:new).and_return(payload)
test_target
expect(payload.my_text).to eq 'payload text'
Alternatively you could also use expect_any_instance_of, but I'd always prefer to use a specific double instead.
I would mock MyModule to yield another mock, that would allow speccing that my_text= is called on the yielded object.
let(:payload) { instance_double('Payload') }
before do
allow(MyModule).to receive(:send).and_yield(payload)
allow(payload).to receive(:my_text=).and_return(nil)
end
# expectations
expect(MyModule).to have_received(:send).once
expect(payload).to have_received(:my_text=).with('payload text').once
I understand that method_missing is something of a last resort when Ruby is processing messages. My understanding is that it goes up the Object hierarchy looking for a declared method matching the symbol, then back down looking for the lowest declared method_missing. This is much slower than a standard method call.
Is it possible to intercept sent messages before this point? I tried overriding send, and this works when the call to send is explicit, but not when it is implicit.
Not that I know of.
The most performant bet is usually to use method_missing to dynamically add the method being to a called to the class so that the overhead is only ever incurred once. From then on it calls the method like any other method.
Such as:
class Foo
def method_missing(name, str)
# log something out when we call method_missing so we know it only happens once
puts "Defining method named: #{name}"
# Define the new instance method
self.class.class_eval <<-CODE
def #{name}(arg1)
puts 'you passed in: ' + arg1.to_s
end
CODE
# Run the instance method we just created to return the value on this first run
send name, str
end
end
# See if it works
f = Foo.new
f.echo_string 'wtf'
f.echo_string 'hello'
f.echo_string 'yay!'
Which spits out this when run:
Defining method named: echo_string
you passed in: wtf
you passed in: hello
you passed in: yay!
#rubiii has previously shown (Savon soap body problem) that you can customize Savon requests with
class SomeXML
def self.to_s
"<some>xml</some>"
end
end
client.request :some_action do
soap.body = SomeXML
end
But why would you use a class method like this? It would seem more likely that you would ask an instance of a class to turn itself into a hash for the request body. i.e.
#instance = SomeClass.new
client.request :some_action do
soap.body = #instance.to_soap
end
However, when I try doing this, #instance variable isn't in 'scope' within the request block. So I get a can't call method to_soap on nil. But if instead I use a class method then I can get it to work. i.e.
class SomeClass
##soap_hash = nil
def self.soap_hash=(hash)
##soap_hash = hash
end
def self.soap_hash
##soap_hash
end
end
SomeClass.soap_hash = #instance.to_soap
client.request :some_action do
soap.body = SomeClass.soap_hash
end
I don't get it?
The class-method example was just that, an example. Feel free to use any object that responds to to_s.
The block is processed via instance_eval with delegation, which is why you can only use local variables and methods inside the block. If you need to use instance variables, change your block to accept arguments. Savon will notice that you specified arguments and yield those values instead of evaluating the block.
For information about which arguments to specifiy and everything else, please RTFM ;)
Forgive me, guys. I am at best a novice when it comes to Ruby. I'm just curious to know the explanation for what seems like pretty odd behavior to me.
I'm using the Savon library to interact with a SOAP service in my Ruby app. What I noticed is that the following code (in a class I've written to handle this interaction) seems to pass empty values where I expect the values of member fields to go:
create_session_response = client.request "createSession" do
soap.body = {
:user => #user, # This ends up being empty in the SOAP request,
:pass => #pass # as does this.
}
end
This is despite the fact that both #user and #pass have been initialized as non-empty strings.
When I change the code to use locals instead, it works the way I expect:
user = #user
pass = #pass
create_session_response = client.request "createSession" do
soap.body = {
:user => user, # Now this has the value I expect in the SOAP request,
:pass => pass # and this does too.
}
end
I'm guessing this strange (to me) behavior must have something to do with the fact that I'm inside a block; but really, I have no clue. Could someone enlighten me on this one?
First off, #user is not a "private variable" in Ruby; it is an instance variable. Instance variables are available within the the scope of the current object (what self refers to). I have edited the title of your question to more accurately reflect your question.
A block is like a function, a set of code to be executed at a later date. Often that block will be executed in the scope where the block was defined, but it is also possible to evaluate the block in another context:
class Foo
def initialize( bar )
# Save the value as an instance variable
#bar = bar
end
def unchanged1
yield if block_given? # call the block with its original scope
end
def unchanged2( &block )
block.call # another way to do it
end
def changeself( &block )
# run the block in the scope of self
self.instance_eval &block
end
end
#bar = 17
f = Foo.new( 42 )
f.unchanged1{ p #bar } #=> 17
f.unchanged2{ p #bar } #=> 17
f.changeself{ p #bar } #=> 42
So either you are defining the block outside the scope where #user is set, or else the implementation of client.request causes the block to be evaluated in another scope later on. You could find out by writing:
client.request("createSession"){ p [self.class,self] }
to gain some insight into what sort of object is the current self in your block.
The reason they "disappear" in your case—instead of throwing an error—is that Ruby permissively allows you to ask for the value of any instance variable, even if the value has never been set for the current object. If the variable has never been set, you'll just get back nil (and a warning, if you have them enabled):
$ ruby -e "p #foo"
nil
$ ruby -we "p #foo"
-e:1: warning: instance variable #foo not initialized
nil
As you found, blocks are also closures. This means that when they run they have access to local variables defined in the same scope as the block is defined. This is why your second set of code worked as desired. Closures are one excellent way to latch onto a value for use later on, for example in a callback.
Continuing the code example above, you can see that the local variable is available regardless of the scope in which the block is evaluated, and takes precedence over same-named methods in that scope (unless you provide an explicit receiver):
class Foo
def x
123
end
end
x = 99
f.changeself{ p x } #=> 99
f.unchanged1{ p x } #=> 99
f.changeself{ p self.x } #=> 123
f.unchanged1{ p self.x } #=> Error: undefined method `x' for main:Object
From the documentation:
Savon::Client.new accepts a block inside which you can access local variables and even public methods from your own class, but instance variables won’t work. If you want to know why that is, I’d recommend reading about instance_eval with delegation.
Possibly not as well documented when this question was asked.
In the first case, self evaluates to client.request('createSession'), which doesn't have these instance variables.
In the second, the variables are brought into the block as part of the closure.
Another way to fix the issue would be to carry a reference to your object into the block rather than enumerating each needed attribute more than once:
o = self
create_session_response = client.request "createSession" do
soap.body = {
:user => o.user,
:pass => o.pass
}
end
But now you need attribute accessors.