- Get rake routes to recognize constraint classes in rails
I’ve been playing recently with the new rails-3 route constraints behaviour. Quite nice, actually, I really enjoy being able to add total custom code to determine if a route match. Of course (?), my first use of that has been subdomain constraints. Here is an example :
constraints( ClientSubdomain ) do resource :organization, :only => %w( show edit update destroy ) do get 'close', :on => :member end root :to => 'static_page#get_page', :page => 'home_client' end constraints( AdminSubdomain ) do scope :module => 'admin' do resources :organizations end root :to => 'admin/organizations#index' end
So here, if I’m on the admin subdomain, I can handle multiple organizations, my clients ones. And if I am on a client subdomain, the organization resource is a singleton (client must only handle its own organization, right?).
Ok, let’s see what rake routes outputs.close_organization GET /organization/close(.:format) {:action=>"close", :controller=>"organizations"} edit_organization GET /organization/edit(.:format) {:action=>"edit", :controller=>"organizations"} organization GET /organization(.:format) {:action=>"show", :controller=>"organizations"} PUT /organization(.:format) {:action=>"update", :controller=>"organizations"} DELETE /organization(.:format) {:action=>"destroy", :controller=>"organizations"} root /(.:format) {:action=>"get_page", :controller=>"static_page"} organizations GET /organizations(.:format) {:action=>"index", :controller=>"admin/organizations"} POST /organizations(.:format) {:action=>"create", :controller=>"admin/organizations"} new_organization GET /organizations/new(.:format) {:action=>"new", :controller=>"admin/organizations"} GET /organizations/:id/edit(.:format) {:action=>"edit", :controller=>"admin/organizations"} GET /organizations/:id(.:format) {:action=>"show", :controller=>"admin/organizations"} PUT /organizations/:id(.:format) {:action=>"update", :controller=>"admin/organizations"} DELETE /organizations/:id(.:format) {:action=>"destroy", :controller=>"admin/organizations"} root /(.:format) {:action=>"index", :controller=>"admin/organizations"}Erf, not really helpful.
After playing a while with mapper, routeset and routes, I came to a solution.
Monkey patch for Constraints
First, we need to expose Constraints#constraints to the world. Create a lib/route_constraints.rb:
module ActionDispatch module Routing class Mapper class Constraints attr_reader :constraints end end end end
Be sure to require it in config/environment.rb.
Rake task
Then, put this content in lib/tasks/route_with_constraints.rake:
Gistnamespace :routes do desc 'Print out all defined routes in match order, with names, per constraint class. Target specific constraint class with CONSTRAINT=x. Target specific controller with CONTROLLER=x.' task :constrained => :environment do Rails.application.reload_routes! constraints_routes = Hash.new Rails.application.routes.routes.each do |route| group = (route.app.class == ActionDispatch::Routing::Mapper::Constraints ? route.app.constraints.to_s : 'No constraint class') constraints_routes[group] ||= [] constraints_routes[group] < < route end requested_constraint = ENV['CONSTRAINT'] constraints_routes.each do |group, all_routes| if requested_constraint.nil? or group == requested_constraint puts "\n\nConstraint class : #{group}\n\n" if ENV['CONTROLLER'] all_routes = all_routes.select{ |route| route.defaults[:controller] == ENV['CONTROLLER'] } end routes = all_routes.collect do |route| reqs = route.requirements.dup reqs[:to] = route.app unless route.app.class.name.to_s =~ /^ActionDispatch::Routing/ reqs = reqs.empty? ? "" : reqs.inspect {:name => route.name.to_s, :verb => route.verb.to_s, :path => route.path, :reqs => reqs} end routes.reject! { |r| r[:path] =~ %r{/rails/info/properties} } # Skip the route if it's internal info route name_width = routes.map{ |r| r[:name].length }.max verb_width = routes.map{ |r| r[:verb].length }.max path_width = routes.map{ |r| r[:path].length }.max routes.each do |r| puts "#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}" end end end end end
Ok, now we have a rake routes:constrained task. Let see its output :
Constraint class : No constraint class Constraint class : AdminSubdomain organizations GET /organizations(.:format) {:action=>"index", :controller=>"admin/organizations"} POST /organizations(.:format) {:action=>"create", :controller=>"admin/organizations"} new_organization GET /organizations/new(.:format) {:action=>"new", :controller=>"admin/organizations"} GET /organizations/:id/edit(.:format) {:action=>"edit", :controller=>"admin/organizations"} GET /organizations/:id(.:format) {:action=>"show", :controller=>"admin/organizations"} PUT /organizations/:id(.:format) {:action=>"update", :controller=>"admin/organizations"} DELETE /organizations/:id(.:format) {:action=>"destroy", :controller=>"admin/organizations"} root /(.:format) {:action=>"index", :controller=>"admin/organizations"} Constraint class : ClientSubdomain close_organization GET /organization/close(.:format) {:action=>"close", :controller=>"organizations"} edit_organization GET /organization/edit(.:format) {:action=>"edit", :controller=>"organizations"} organization GET /organization(.:format) {:action=>"show", :controller=>"organizations"} PUT /organization(.:format) {:action=>"update", :controller=>"organizations"} DELETE /organization(.:format) {:action=>"destroy", :controller=>"organizations"} root /(.:format) {:action=>"get_page", :controller=>"static_page"}Way better :)
- Zookshop : création de boutiques en ligne à faible coût
Travailleurs du web, combien de fois avez-vous été contactés par des clients souhaitant créer une boutique en ligne pour finalement vous rendre compte que leur budget était tout à fait insuffisant?
- Le développement top-down en uad
Cet article fait suite à la conversation que j’ai eue avec Patrick Fratczak sur la manière de faire du TDD.
Voici donc la méthode que j’emploi pour faire du user acceptance testing. L’exemple décrit ici utilise rails, cucumber et rspec, mais cela fonctionne également bien avant Typolight, cucumber et phpspec.
- Getters et setters en PHP
Venant du monde de ruby, j’ai pris l’habitude de ne pas faire de distinction dans l’utilisation d’un objet entre une méthode et un attribut. En ruby, un attribut se récupère exactement de la même manière qu’une méthode s’appelle.
Lire la suite- Typolight et les modèles : M comme dans VC
Si on me posait la question du problème majeur dans le core de Typolight, je répondrais : l’implémentation incomplète du pattern MVC. La couche modèle est difficilement utilisable telle-quelle et provoque l’abondance de requêtes SQL dans les contrôleurs. Il y a aussi le problème de la présence de HTML dans les contrôleurs, mais cela ne concernera pas ce tutoriel. Nous verrons cette fois comment rendre aux modèles ce qui appartient aux modèles.