On Jul 7, 2010, at 1:24 AM, Wincent Colaiuta wrote: > El 07/07/2010, a las 01:16, Lalish-Menagh, Trevor escribió: > >> Hi David, >> >> You make a good point. I was talking with a coworker about this >> problem, and he suggested a simpler format, that I think will coincide >> some with Wincent's thoughts. Here is my next stab >> (http://gist.github.com/466064): >> describe "routing" do >> it "recognizes and generates #index" do >> get("/days").should have_routing('days#index') >> end > > Very nice. I definitely like this better than the "map()" terminology that I > used. > >> it "generates #index" do >> get("/days").should generate('days#index') >> end > > Reads well, _but_ I think you've got it back-to-front. assert_generates, and > therefore the corresponding matcher in RSpec, asserts that a _path_ can be > generated from a set of _params_ (controller, action, additional params), but > here you're asserting the opposite. > > (One thing to note is that assert_generates really only does assert about the > _path_, not the method + path, but it's still nice to write it as "get(path)" > just for symmetry with the reverse version of the spec.) > >> it "recognizes #index" do >> ('days#index').should recognize get("/days") >> end > > This one is back-to-front too; assert_recognizes is an assertion that a path > (this time method + path) is recognized as a particular route (action, > controller, additional params) but here you're asserting the opposite. > > If we're really serious about using the same verbs as the Rails assertions, > and we want to forward and reverse versions of the spec to read in the same > way, we could use something like: > > specify { get('/days').should recognize_as('days#index') } > specify { get('/days').should generate_from('days#index') } > > If we don't mind swapping the order around > > specify { get('/days').should recognize_as('days#index') } > specify { 'days#index'.should generate('/days') } > > Note that in the "generate" spec here I drop the "get()" seeing as Rails > isn't actually looking at the method, and it is just verbiage IMO to start > nesting other method calls inside the generate() matcher. > > Weighing up the two orders, I prefer the first pair of specs, I think, > because: > > - the repetition of the pattern makes it easier to remember and apply. > > - if you have to supply additional parameters (as you do in your later > example), using the first format you can write ('days#index', :student_id => > '1') whereas with the second format you are obliged to involve another method > like with() in order to express it as "('days#index').with(:student_id => > '1')". > >> describe "nested in students" do >> it "recognizes #index" do >> ('days#index').with(:student_id => "1").should recognize >> get("/students/1/days") >> end >> end >> end > > This is another back-to-front one, I think. You're not recognizing here, but > generating. > >> Notes: >> - I am using have_routing, recognize, and generate because those are >> the verbs used in Rails for the functions we are wrapping. > > Seems like a good idea that we should definitely try to do, although with one > reservation; if we feel that the language is confusing or suboptimal then we > should feel free to change it. > > I personally have lingering doubts about the Rails language because I think > it _is_ confusing in a way. Look at the way you used recognize and generate > back-to-front in this message. And I know that every time I want to make a > comment about "assert_recognizes" and "assert_generates" I end up > double-checking the API docs just to make sure that I'm about to use them in > the right way. > >> - I like using the word "with" to represent "extras," in the Rails API >> (see >> http://edgeapi.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html), >> since I think it fits here, and we already use with in RSpec in other >> places (like stubs, etc.). > > I don't really like it; I think it adds unnecessary verbiage to the specs. > >> - I like using get('path') since it is similar to the routing calls in >> the Rails 3 route file, and I think it is easy to intuit. >> - We can use the hash notation to conform to Rails 3, with an option >> to provide a full hash as well (Rails 2 style). > > Yes, the example I posted at http://gist.github.com/464081 does that. > >> - The format still reads like English, and using "have_routing" >> instead of "routes_to" avoids the "_to" and "_from" problem that we >> have been talking about, PLUS it makes it easier to draw a >> relationship between the RSpec command and the Test::Unit command >> (assert_routing). > > Yeah, I think "have_routing" is a definite improvement over "map". > > I am less convinced that "recognize_as" is an improvement over the highly > readable "map_to". > > I am less attached to "map_from", though, and think "generate_from" might be > an improvement. > > Like I said above, I think the Rails terminology does have the potential to > be confusing, so I don't necessarily feel "wedded" to it. > >> - Using these verbs still allow "should_not" to make sense. > > Yes, I think that's important. > > One thing I wanted to add is that I discovered in my testing that the > existing "be_routable" matcher isn't very useful. This is because it relies > on "recognize_path", which I am not sure is public API or not, and > "recognize_path" sometimes recognizes the unrecognizable... For example: > > in config/routes.rb: > resource :issues, :except => :index > > visit issues#index (/issues) in your browser: > get a RoutingError (expected) > > in your spec: > recognize_path('/issues', :method => :get) # recognizes! > > So this means you can't do stuff like get('issues#index').should_not > be_routable, because the Rails recognize_path() method will tell you that it > _is_ routable. Seems that it is only useful for a subset of unroutable routes > (like completely non-existent resources, for example). > > If you look in the Gist you'll see that I work around this limitation by > adding a "be_recognized" matcher which doesn't use "recognize_path()" under > the hood. Instead it uses "assert_recognizes" and decides what to do based on > what kind of exception, if any, gets thrown by it. So you can write: > > specify { get('/issues').should_not be_recognized } > > (Funnily enough, assert_recognizes() _does_ use recognize_path() under the > hood, but it's evidently doing some additional set-up and tear-down to make > it work. It seems like wrapping assert_recognizes rather than replicating its > logic, though, is the right thing to do.)
Seems as though this format has been abandoned in this conversation: it { should route(get "/issues/new").to("issues#new") } it { should generate("/issues/new").from("issues#new") } I think that verbiage is concise and intention revealing, and keeps the path on the left and the details on the right. Supported variants would be: it { should route(:get => "/issues/new") ... # explicit hash for method/path it { should route("/issues/new")... # defaults to get it { should ...to(:controller => "issues", :action => "new") } # explicit hash for params The negative version would be: it { should_not route(get "/issues/new") } We could alias route() with recognize() to get: it { should_not recognize(get "/issues/new") } We still need to consider output. For example: it { should route("/issues/37").to("issues#show", :id => 37) } I think in this case we _might_ be able to figure out that 37 is the value of :id and output should route "/issues/:id" to "issues#show" Not sure if that'd be asking for trouble, but it seems feasible/reasonable. In terms of the more verbose format for custom messages, we could add a router() method for sugar: it "routes /issues/new to issues#new" do router.should route("/issues/new").to("issues#new") } end And we still need the bi-directional version. I'm really at a loss on this one. Thoughts? David _______________________________________________ rspec-users mailing list rspec-users@rubyforge.org http://rubyforge.org/mailman/listinfo/rspec-users