-
Notifications
You must be signed in to change notification settings - Fork 22k
Description
In API development it's often useful to map API versions to module namespaces. If your version naming convention contains dots (i.e. v2.1.0) you typically translate dots to underscores for the module names (i.e. V2_1_0). In Rails 3.0.x and 3.1.x, you could work around a quirk in the Rails inflector and get Rails to honor underscores in module names by specifying two underscores in the string passed to the :module
argument of scope
in your config/routes.rb
file. For example:
scope :module => "V2__1__0", :constraints => { :format => /(json|xml)/ } do
match '/foos.(:format)' => 'foos#index', :via => :get
end
In Rails 3.0/3.1, this would correctly route to app/controllers/v2_1_0/foos_controller.rb
, which contains:
class V2_1_0::FoosController < ApplicationController
def index
render :text => 'v2.1.0'
end
end
Client:
curl http://localhost:3000/foos.json
v2.1.0
Server:
Started GET "/foos.json" for 127.0.0.1 at 2012-04-14 18:21:36 -0400
Processing by V2_1_0::FoosController#index as JSON
Rendered text template (0.0ms)
Completed 200 OK in 24ms (Views: 24.1ms | ActiveRecord: 0.0ms)
As of Rails 3.2.x, this no longer works. When requesting /foos.json with the same exact setup as above now results in the following RoutingError:
ActionController::RoutingError (uninitialized constant V210):
activesupport (3.2.3) lib/active_support/inflector/methods.rb:229:in `block in constantize'
activesupport (3.2.3) lib/active_support/inflector/methods.rb:228:in `each'
activesupport (3.2.3) lib/active_support/inflector/methods.rb:228:in `constantize'
actionpack (3.2.3) lib/action_dispatch/routing/route_set.rb:69:in `controller_reference'
actionpack (3.2.3) lib/action_dispatch/routing/route_set.rb:54:in `controller'
actionpack (3.2.3) lib/action_dispatch/routing/route_set.rb:32:in `call'
journey (1.0.3) lib/journey/router.rb:68:in `block in call'
journey (1.0.3) lib/journey/router.rb:56:in `each'
journey (1.0.3) lib/journey/router.rb:56:in `call'
actionpack (3.2.3) lib/action_dispatch/routing/route_set.rb:600:in `call'
actionpack (3.2.3) lib/action_dispatch/middleware/best_standards_support.rb:17:in `call'
rack (1.4.1) lib/rack/etag.rb:23:in `call'
rack (1.4.1) lib/rack/conditionalget.rb:25:in `call'
actionpack (3.2.3) lib/action_dispatch/middleware/head.rb:14:in `call'
actionpack (3.2.3) lib/action_dispatch/middleware/params_parser.rb:21:in `call'
actionpack (3.2.3) lib/action_dispatch/middleware/flash.rb:242:in `call'
rack (1.4.1) lib/rack/session/abstract/id.rb:205:in `context'
rack (1.4.1) lib/rack/session/abstract/id.rb:200:in `call'
actionpack (3.2.3) lib/action_dispatch/middleware/cookies.rb:338:in `call'
activerecord (3.2.3) lib/active_record/query_cache.rb:64:in `call'
activerecord (3.2.3) lib/active_record/connection_adapters/abstract/connection_pool.rb:467:in `call'
actionpack (3.2.3) lib/action_dispatch/middleware/callbacks.rb:28:in `block in call'
activesupport (3.2.3) lib/active_support/callbacks.rb:405:in `_run__4355974194936707360__call__1035143701529953630__callbacks'
activesupport (3.2.3) lib/active_support/callbacks.rb:405:in `__run_callback'
activesupport (3.2.3) lib/active_support/callbacks.rb:385:in `_run_call_callbacks'
activesupport (3.2.3) lib/active_support/callbacks.rb:81:in `run_callbacks'
actionpack (3.2.3) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
actionpack (3.2.3) lib/action_dispatch/middleware/reloader.rb:65:in `call'
actionpack (3.2.3) lib/action_dispatch/middleware/remote_ip.rb:31:in `call'
actionpack (3.2.3) lib/action_dispatch/middleware/debug_exceptions.rb:16:in `call'
actionpack (3.2.3) lib/action_dispatch/middleware/show_exceptions.rb:56:in `call'
railties (3.2.3) lib/rails/rack/logger.rb:26:in `call_app'
railties (3.2.3) lib/rails/rack/logger.rb:16:in `call'
actionpack (3.2.3) lib/action_dispatch/middleware/request_id.rb:22:in `call'
rack (1.4.1) lib/rack/methodoverride.rb:21:in `call'
rack (1.4.1) lib/rack/runtime.rb:17:in `call'
activesupport (3.2.3) lib/active_support/cache/strategy/local_cache.rb:72:in `call'
rack (1.4.1) lib/rack/lock.rb:15:in `call'
actionpack (3.2.3) lib/action_dispatch/middleware/static.rb:62:in `call'
railties (3.2.3) lib/rails/engine.rb:479:in `call'
railties (3.2.3) lib/rails/application.rb:220:in `call'
rack (1.4.1) lib/rack/content_length.rb:14:in `call'
railties (3.2.3) lib/rails/rack/log_tailer.rb:14:in `call'
rack (1.4.1) lib/rack/handler/webrick.rb:59:in `service'
/Users/bploetz/.rvm/rubies/ruby-1.9.2-p0/lib/ruby/1.9.1/webrick/httpserver.rb:111:in `service'
/Users/bploetz/.rvm/rubies/ruby-1.9.2-p0/lib/ruby/1.9.1/webrick/httpserver.rb:70:in `run'
/Users/bploetz/.rvm/rubies/ruby-1.9.2-p0/lib/ruby/1.9.1/webrick/server.rb:183:in `block in start_thread'
Note that the underscores are being dropped altogether at some point in the processing, as it's looking for the constant V210
instead of the correct V2_1_0
.
I think this is due to a change made to ActiveSupport::Inflector.camelize. Observe:
Rails 3.0/3.1:
rails console
Loading development environment (Rails 3.0.7)
ruby-1.9.2-p0 :001 > test = "V2__1__0"
=> "V2__1__0"
ruby-1.9.2-p0 :002 > test.camelize
=> "V2_1_0"
Rails 3.2:
rails console
Loading development environment (Rails 3.2.3)
ruby-1.9.2-p0 :001 > test = "V2__1__0"
=> "V2__1__0"
ruby-1.9.2-p0 :002 > test.camelize
=> "V210"
Ideally you wouldn't need the double underscore hack at all, but at least it worked in Rails 3.0/3.1. If there's another undocumented way to get Rails to honor underscores in module names in Rails 3.2 let me know. If Rails doesn't support underscores in module names anymore, it should be documented somewhere, as underscores are obviously perfectly valid characters for module names in Ruby.
Thanks.