Rack::Builder 'to_app' method comprehension - ruby

I saw the Railscast #151. In this video, a Rack's snippet is presented. It corresponds to the Rack::Builder module. I'm missing something in the to_app method:
def to_app
app = #map ? generate_map(#run, #map) : #run
fail "missing run or map statement" unless app
#use.reverse.inject(app) { |a,e| e[a] }
end
Could someone explain the last line of the method and what it actually does? I know that inject uses an accumulator variable and an element variable. But I don't understand why we could do an array subscription with e[a].

In this case, [] is not array access. It's one way to call a lambda or proc, equivalent to e.call(a). (See the documentation.) If you look at the use method, the #use variable is an array of lambdas. This code is running through each middleware (and/or mapping) in reverse order, and calling each lambda with the app as the argument. This is how they build the app bit by bit.

Related

How to call a method on a local variable in Ruby?

Probably a stupid question but I was following along this article and came across a bit of code I couldn't quite grasp. Here it is:
class CreateArticle
attr_reader :validate_article, :persist_article
def initialize(validate_article, persist_article)
#validate_article = validate_article
#persist_article = persist_article
end
def call(params)
result = validate_article.call(params)
if result.success?
persist_article.call(params)
end
end
end
More specifically, the problematic line is this:
if result.success?
Here's my problem with it: where did the success? method come from? It's not default in Ruby, and result is a local variable, so it should be nearby. But even if it's just omitted in the code sample, where would it have to be defined for that line to work? Everywhere I tried to define it just gave me an 'undefined method' error.
For example, I tried to define it both in the CreateArticle class and in the (only alluded to) ValidateArticle class, the obvious culprits, but no dice.
Update:
The reason I ask is not so much about what success? does as it is because I'm interested in using the pattern in my code. So, for example, my version of the success? method could be just checking whether a value got updated, or an item was inserted into an array. For example, let's say it's just this:
def success? # or self.success?
return true
end
Problem is, I can find no place where I can put this that works. I even created a module just for it and included it into the class, and still it doesn't work (it just returns 'undefined method'). So I'm still at a loss as to where I would have to define such a method so that it would work the way it looks like it should.
It's a method that comes with rails. It checks.for a server response with a 200 code. If it gets a 200 code it returns true else it returns false. Read the rails API docs about it... https://apidock.com/rails/v3.2.3/ActiveResource/Response/success%3F
Actually . success? is a built in ruby method. Check here. What it actually does is checking Stat and returns a boolean.
I did some more digging around the blog and from what I found I suspect that the code is probably making use of the dry-monads gem:
You can explicitly check the type by calling failure? or success? on a monadic value.
It's not explicit in the code excerpt but it's the only thing that makes sense.

RSpec spy match_array on hash value

I have a simple test in an RSpec controller spec that checks to see if the correct message was passed to a delayed job:
it 'sends a message to NotifyFollowersJob with relevant person and split_time data' do
allow(NotifyFollowersJob).to receive(:perform_later)
post :import, params: request_params
split_time_ids = SplitTime.all.ids
person_id = SplitTime.first.effort.person_id
expect(NotifyFollowersJob).to have_received(:perform_later)
.with(person_id: person_id,
split_time_ids: split_time_ids)
end
The test usually passes, but sometimes it fails because the split_time_ids (an Array) are sometimes reversed. I do not care what order the split_time_ids are passed to NotifyFollowersJob, so the test should pass regardless of the order.
If I were testing the contents of the Array alone, I could write:
expect(split_time_ids).to match_array(SplitTime.all.ids)
But I can't figure out how to get similar functionality where the Array is a value of one of several arguments.
Any RSpec masters out there care to give me some guidance?
One idea is to make a block to check the message and pass it to your allow method.
Like this:
allow(NotifyFollowersJob).to receive(:perform_later) do |arg|
# Handle your args to avoid fails because of its order
expect(...)
end
And you should keep the expect(NotifyFollowersJob).to have_received(:perform_later) (without the with verification) just to make sure it is still being called.
It looks like your controller action runs SplitTime.all.ids query and passes them to the worker as args.
If you don't care about the order, maybe you can stub this query, to get the same result all the time?
In addition, it'll make your test faster.

Split array into comma separated list of values

I'm working on a bit of metaprogramming using send methods quite a bit. I've been successful so far because the methods I'm sending to only take one argument.
Example:
client is an API client
#command is a method on client taken as an option to a CLI utility
#verb is a method on command taken as another option in the CLI
def command_keys
case #command
when "something"
self.command_options.slice(:some, :keys)
end
end
Then I call the API client like this:
client.send(#command).send(#verb, command_keys)
This works since the methods all take a Hash as their argument. The problem I've run into is when I need to send more than 1 parameter in command_keys. What I'm wondering is the best way to handle the command_keys method returning more than 1 value. Example:
def command_keys
case #command
when "something"
return self.command_options[:some], self.command_options[:keys]
end
end
In this case, command_keys returns an Array as expected, but when I try to pass that in the send(#verb, command_options) call, it passes it as an Array (which is obviously expected). So, to make a long story short, is there some easy way to make this condition be handled easily?
I know send(#verb, argument1, argument2) would get me the result I want, but I would like to be able to not have to give my script any more implementation logic than it needs, that is to say I would like it to remain as abstracted as possible.
Use splat. You might have to rethink the code a bit, but something like:
client.send(#command).send(#verb, *all_the_args)

What is exactly happening when I set a method to its own return value?

So I had this bug where I had a method:
def returnArr
[2,3,4]
end
And I did this:
returnArr = returnArr.first
returned an error stating that nilClass doesn't have a method 'first'
Moreover, after doing that line of code, and follow it up with this:
returnArr = returnArr().first
worked completely fine, and returnArr is now different from returnArr(). What is going on here?
When you have this line
returnArr = returnArr.first
Ruby sees (and executes) this:
returnArr = nil
returnArr = returnArr.first
Before assigning value to a variable, this variable is initialized to nil. So, in this case, your local variable shadows your method. Without hints from your side, ruby can't determine that actually you wanted to call the method. When you provide parentheses, ruby understands that local variable can't have them and calls the method.
Don't ever do this again. Especially in a real app.

Accessing SOAP Service with soap4r not able to access contents of returned objects

So I need to access this service from my rails app. I'm using soap4r to read the WSDL and dynamically generate methods for accessing the service.
From what I've read, I should be able to chain methods to access the nested XML nodes, but I can't get it to work. I tried using the wsdl2ruby command and read through the generated code. From what I can tell, the soap library is not generating these accessor methods. I'm pretty new to ruby, so I don't know if I'm just missing something?
I know when I inspect the element, I can see the data I want. I just can't get to it.
For instance if I use the following code:
require "soap/wsdlDriver"
wsdl = "http://frontdoor.ctn5.org/CablecastWS/CablecastWS.asmx?WSDL"
driver = SOAP::WSDLDriverFactory.new(wsdl).create_rpc_driver
response = driver.getChannels('nill')
puts response.inspect
I get the following output:
ignored element: {http://schemas.xmlsoap.org/wsdl/soap12/}binding
ignored element: {http://schemas.xmlsoap.org/wsdl/soap12/}operation
ignored element: {http://schemas.xmlsoap.org/wsdl/soap12/}body
ignored element: {http://schemas.xmlsoap.org/wsdl/soap12/}address
#<SOAP::Mapping::Object:0x80b96394 {http://www.trms.com/CablecastWS/}GetChannelsResult=#<SOAP::Mapping::Object:0x80b96178 {http://www.trms.com/CablecastWS/}Channel=[#<SOAP::Mapping::Object:0x80b95f5c {http://www.trms.com/CablecastWS/}ChannelID="1" {http://www.trms.com/CablecastWS/}Name="CTN 5">, #<SOAP::Mapping::Object:0x80b9519c {http://www.trms.com/CablecastWS/}ChannelID="2" {http://www.trms.com/CablecastWS/}Name="PPAC 2">, #<SOAP::Mapping::Object:0x80b94620 {http://www.trms.com/CablecastWS/}ChannelID="14" {http://www.trms.com/CablecastWS/}Name="Test Channel">]>>
So the data is definitely there!
Here is the code generated by wsdl2ruby for the method being used above:
# {http://www.trms.com/CablecastWS/}GetChannels
class GetChannels
def initialize
end
end
# {http://www.trms.com/CablecastWS/}GetChannelsResponse
# getChannelsResult - ArrayOfChannel
class GetChannelsResponse
attr_accessor :getChannelsResult
def initialize(getChannelsResult = nil)
#getChannelsResult = getChannelsResult
end
end
Sorry for the long post, I figured the more info the more likely someone can point me in the right direction.
Thanks
-ray
Answer
require "soap/wsdlDriver"
wsdl = "http://frontdoor.ctn5.org/CablecastWS/CablecastWS.asmx?WSDL"
driver = SOAP::WSDLDriverFactory.new(wsdl).create_rpc_driver
response = driver.getChannels('nill')
for item in response.getChannelsResult.channel
puts item.name
puts item.channelID
end
How I got the Answer
You can figure out the methods of response via
response.methods
This will get you a long list of methods that are hard to sort through, so I like to subtract out the generic methods. Ruby lets you subtract arrays.
response.methods - Object.new.methods
Using this technique, I found the getChannelsResult method for response. I repeated the process
resonse.getChannelsResult.methods - Object.new.methods
I found the channel method for its result. Again!
response.getChannelsResult.channel.methods - Object.new.methods
This returned a bunch of methods including: sort, min, max etc. So I guessed Array. A simple confirmation was in order
response.getChannelsResult.channel.class
Sure enough it returned Array. To make life simple, I just worked with the first item of the array to get its methods
response.getChannelsResult.channel.first.methods - Object.new.methods
Whoalla, I found two more methods "name" and "channelID"

Resources