rspec testing that api call increments a counter - ruby

I have a test that works right now but it's ugly and I can't help thinking there is a better way to do this. Basically I pick a record from the database and then make an api call which should affect that record. However the only way to make the test pass is to pull the record from the database a second time.
it "counts how many times a client has pulled its config" do
client = Endpoint.last
config_count = client.config_count
post '/api/config', node_key: client.node_key
same_client = Endpoint.find_by node_key: client.node_key
# expect(client.config_count).to eq(config_count + 1)
expect(same_client.config_count).to eq(config_count + 1)
end
The commented out line does not work. This fix is so ugly that it makes me think I'm doing it wrong. I also tried this:
expect {post '/api/config', node_key: client.node_key}.to change {client.config_count}.by(1)
So what is the proper way to test this?

Probably several ways to solve it. I tend to call .reload on my object if I want updated values for it and don't care what exactly is happening inside the object itself.
it "counts how many times a client has pulled its config" do
client = Endpoint.last
config_count = client.config_count
post '/api/config', node_key: client.node_key
client.reload
expect(client.config_count).to eq(config_count + 1)
end

Related

Logstash: Handling a Configuration File for a Filter

I've written a filter and use its register-function to load an external CSV-file and fill a bunch of hash-tables. The filter-function then accesses the hash-tables and adds fields to the event. While that's working nicely, the downside is that it only loads once and I'd need to restart logstash to trigger the reload after a change in the CSV-file. Maybe I should add that the filter is currently consuming events coming from three different file inputs.
Writing an input doesn't seem to solve it as the input is not tied to the filter in some way. Therefore, my plan is to somehow reload the CSV-file every few hours or at a particular time and somehow block the entire filter during that, i.e. pause incoming events. That sounds like a weird thing to do and I'm not sure whether or not logstash is actually meant to be used like this.
I'm a newbie regarding Ruby and actually I'm quite amazed that the filter is working this nice. As Google let me down on the entire issue I'm hoping that anyone on here has experience with this, can post a link to an example or can point me to another way of solving this.
For educational purposes I looked into the source of logstash and noticed that I could actually understand what's going on and things are much less complicated than I had thought.
There is a function filterworker in pipeline.rb and a class filterworker and I don't know which one is actually used, but my findings seem to be true for both.
Basically all filters seem to run in one thread in case it's not configured otherwise. This means that I can reload the file anywhere in the filter-function and the entire processing for all filters is paused (input and output might still do something, but that's handled by the queue for the events holding maximum 20 entries).
Therefore, this seems to do it for me:
public
def register
#config_files_read_timestamps = {}
read_config_files
end # def register
def filter(event)
# return nothing unless there's an actual filter event
return unless filter?(event)
read_config_files
:
# filter_matched should go in the last line of our successful code
filter_matched(event)
end # def filter
private
def read_config_files
read_marker_file
:
end
def check_for_changed_file?(filename)
mtime = File.mtime(filename)
#config_files_read_timestamps[filename] ||= Time.at(0)
if #config_files_read_timestamps[filename] < mtime
#config_files_read_timestamps[filename] = mtime
return true
end
end
def read_marker_file
if !check_for_changed_file?("markers.txt")
return
end
:
end
Obviously I don't need a separate thread for the parsing. It would become necessary if I plan to start the reload at a specific time. In that case I'd have to join the thread and then continue with event handling.
Let me know if there could be improvements...

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)

Refactor my ruby snippet so it doesn't look like C anymore: method(method(param))

I have a class which uses a connection object to send the request data created by a request_builder object.
The code looks like this:
connection.send_request(request_builder.build_request(customer))
This in turn is called by
build_report(customer, connection.send_request(request_builder.build_request(customer)))
Ugly! Any ideas on how to make it more expressive? Usually in ruby and OOP we chain objects like this: "string".make_it_bigger.flash_it.send
It's code, that how it looks. But you can make yourself a favour by not trying to cram everything together on one line:
request = request_builder.build_request(customer)
response = connection.send_request(request)
report = build_report(customer, response)
if you told us more about your code base we might be able to suggest something else, but you don't give us very much to go on. What does the request_builder object do? Does connection.send_request(...) return a response? Why does a report need a customer and a response (assuming that's what is returned by connection.send_request(...)), and so on.
build_report(customer, request_builder.build_request(customer).send_over(connection))

Generate XML with soap4r without invoking the web service?

I have set up a soap4r client for a web service, and it's working fairly well. We're using it to send data from one database to another (don't bother asking about that... I know it's not optimal), but we're not entirely sure the mapping is correct, so it's often very handy to get the XML that a particular record would generate.
Of course, that's possible - if you set $DEBUG, soap4r will supply you with a nice dump of the XML going over the wire. You can even set the "device" (file) that you would like to send it to.
However, I'd like to be able to get the XML that it's going to generate without having to actually call the web service.
Is there a way to do this? Grepping around, I've found a variety of obj2soap and similar methods, but none of them seems to be quite the one I want.
An indirect answer: you might want to look at handsoap. It's faster and tries to be more Ruby-like. It uses builder-style XML generation - but you have to generate everything yourself. It's more like a toolbox to write your client in a clean way. This way you know what was generated (and can inspect it easily).
Another option is to set $DEBUG and restore it afterwards:
$REMEMBER_DEBUG_STATE = $DEBUG
$DEBUG = true
# call soap (and have your XML generated)
$DEBUG = $REMEMBER_DEBUG_STATE
This could be extracted to a nice function like this:
def with_debug_output
remember = $DEBUG
$DEBUG = true
yield if block_given?
$DEBUG = remember
end
and then use it:
with_debug_output do
# call soap
end

SOAP::RPC::Driver formatting problems. How can I change it?

I'm dealing with a SOAP webservice call from a server that is expecting to receive method calls with the paramaters in the format of:
<urn:offeringId> 354 </urn:offeringId>
But SOAP::RPC::Driver is generating messages in the form of:
<offeringId xsi:type = "xsd:int">354</offeringId>
The server keeps erroring when it gets these messages (especially since it's expecting offeringId to be a custom type internal to itself, not an int).
Is there anyway to configure the driver to format things the way the server is expecting it. Is the server even doing SOAP? I'm having trouble finding reference to that style of formating for SOAP (I know it DOES work though, because SOAPUI works just fine with that type of message).
-Jenny
Edit: I've got at least part of it solved. the RPC::Driver (obviously) uses the RPC standard, whereas apparently the server I'm trying to talk to is doing "document". Now, when I look at RPC::Driver's API, I'm seeing a method named "add_document_method". That SOUNDS to me like it might be what I want, but I can't figure out what paramaters to give it. The examples I've seen around the net don't make much sense to me, things like:
def GetNamePair(response)
response.account.each do |x|
class << x
attr :configuration, true
end
x.configuration = Hash[*x.a.map do |y|
[y.__xmlattr[XSD::QName.new(nil, 'n')], String.new(y)]
end.flatten]
end
end
mNS = 'urn:zimbraAdmin'
drv.add_document_method('GetAllAdminAccountsRequest', mNS, [XSD::QName.new(mNS, 'GetAllAdminAccountsRequest')],
[XSD::QName.new(mNS, 'GetAllAdminAccountsResponse')] )
puts YAML.dump(GetNamePair(drv.GetAllAdminAccountsRequest([]))
All I really know is that I have a method that takes in certain parameters.... I really don't get why, if this method does what I think it does, it has to be more complicated. Isn't this just a matter of taking the exact same data and formating it differently? I'm so confused....
Okay, what I ended up doing was using SOAP:RPC:Drivers add_document_method, which requires me to give it the wsdl, namespace, etc, and then give it the attributes later as a single input hash thingy (and gives me the output in a similar format). It worked, it just wasn't as clean as add_rpc_method (which is waht add_method defaults to)
-Jenny

Resources