Not possible to override base find() service call?
nzifnab opened this issue · comments
I'm running into an issue where I want to override a GET
request's response xml with a little customization, but I don't seem to be able to.
From script/console
, here's what I'm trying to do:
require 'dupe'
class Quote < ActiveResource::Base
self.prefix = '/my_namespace/'
end
Dupe.define :quote
Get %r{^/my_namespace/quotes/(\d+)\.xml$} do |quote_id|
raise "hi!"
end
dupe_quote = Dupe.create(:quote)
And finally:
>> Quote.find(dupe_quote.id)
=> #<Quote:0x211680c @attributes={"id"=>1}, @prefix_options={}>
I would have expected it to raise my error. My personal usage for this is somewhat more complicated, but I've made this simple example to demonstrate the problem.
Looking at the log...
>> puts Dupe.network.log.pretty_print
Logged Requests:
Request: GET /my_namespace/quotes/1.xml
Response:
1
And inspecting the defined mocks:
>> Dupe.network.mocks
=> {:get=>[#<Dupe::Network::GetMock:0x213679c @response=#<Proc:0x02137430@(eval):5>, @url_pattern=/^\/my_namespace\/quotes\.xml$/>, #<Dupe::Network::GetMock:0x21366fc @response=#<Proc:0x02137160@(eval):10>, @url_pattern=/^\/my_namespace\/quotes\/(\d+)\.xml$/>, #<Dupe::Network::GetMock:0x2131508 @response=#<Proc:0x02131594@(irb):7>, @url_pattern=/^\/my_namespace\/quotes\/(\d+)\.xml$/>], :post=>[#<Dupe::Network::PostMock:0x21366d4 @response=#<Proc:0x02136f1c@(eval):15>, @url_pattern=/^\/my_namespace\/quotes\.xml$/>], :put=>[#<Dupe::Network::PutMock:0x21366ac @response=#<Proc:0x02136b98@(eval):20>, @url_pattern=/^\/my_namespace\/quotes\/(\d+)\.xml$/>], :delete=>[#<Dupe::Network::DeleteMock:0x2136684 @response=#<Proc:0x021368b4@(eval):25>, @url_pattern=/^\/my_namespace\/quotes\/(\d+)\.xml$/>]}
Of specific note is the url patterns:
>> Dupe.network.mocks[:get].map{|mock| mock.url_pattern}
=> [/^\/my_namespace\/quotes\.xml$/, /^\/my_namespace\/quotes\/(\d+)\.xml$/, /^\/my_namespace\/quotes\/(\d+)\.xml$/]
Notice how there are two identical url patterns there (originally I was doing this with a slightly different url pattern - didn't have the ^$
symbols and was using the character class [0-9]
instead of \d
, but tried duplicating the pattern to see if it would overwrite -- it didn't, as you can see). It seems as though the dupe definition is creating it's normal default GET
mock, but no matter what I do I can't override it with my own. Is there a way to force certain calls to not create their auto-mocks? Or is there some other way to do this?
How does it decide which url pattern/response to follow? It should just be the last one defined shouldn't it? I figure that the Dupe.define :quote
would make a url pattern, but since I define mine after that one it should catch on my definition and not fall through to the automatically-generated one.
Edit: Added the call to Dupe.network.log.pretty_print
hi nzifnab,
thanks for using dupe. i've started working on this issue in the "override-get-requests" branch. on the one hand, it's a simple fix: internally, when Dupe is looking through intercept mocks to figure out which one to use for the response, it just starts plowing through the array of mocks, and stops when it gets to the first mock with a matching regular expression.
new Get requests get added onto the end of this array - hence, since the built in :get mocks that were created when you did a "Dupe.define :quote" were at the beginning of the array of :get requests, it stopped there before ever finding your new custom mock.
Note that Post, Put, and Delete requests don't have this problem; new mocks of those types get added to the beginning of their respective mock arrays (instead of the end). it was simply an oversite that i hadn't done the same for Get requests.
however, though the solution seems simple, there's something i don't like about it: When I'm cuking with Dupe, and I create a custom mock file under features/dupe/custom_mocks, i like to read it top to bottom, and much like a routes file, i can currently expect Get mocks higher up to have higher precedence than Get mocks listed lower in the file.
So... even though i have a quick fix already in place in the "override-get-requests" branch, I'm now thinking of a solution that would allow us to override the default Get, Post, Put, and Delete mocks created when we Dupe.define a resource, but still let the custom mock files to read top-to-bottom in order of precedence.
I do see that as a small problem, but I tend to try to make non-ambiguous regexes to begin with so my own definitions don't really require a 'precedence' (But I can see how this would be desired from a design point-of-view). I thought the problem might be something similar to what you described, glad to see the short-term solution is simple :)
Great work on Dupe btw, we really needed a factory_girl type of gem for ActiveResource, and it's great to see that someone took the initiative. Fakeweb with it's static pages was certainly not cutting it. Works great with both rspec and cuke :)
cool - for now, feel free to go ahead and use the "override-get-requests" branch on your project (if you want to install the gem on your machine using that branch, simply "git clone" the project, "git checkout override-get-requests", and "rake install"). hopefully i'll have enough time this week to move forward with a solution that preserves the top-to-bottom precedence order while still allowing us to override the default mocks. (my wife is due to have our first child any day now, so if i drop off the radar suddenly for a couple weeks, you'll know why).
glad dupe is useful for you! we started an SOA project about 6 months ago, and instantly realized that cuking ActiveResource was going to be a total PITA. so i bit the bullet and started Dupe. it was my first gem, and i've learned a lot writing it. it's also already gone through one total rewrite (over the xmas holidays), but now i think i have it on a firm enough architectural footing that hopefully it will scale to meet all of our future needs without any more significant rewrites :-) (fingers crossed)