accessing child namespaced controllers from a parent controller - ruby-on-rails

recently i came across problem problem in accessing the namespaced controllers i have a parent controller in education/educations_controller.rb as
class Education::EducationssController < ApplicationController
def process_educations
if highschool
controller_to_redirect = 'highschool'
if bacholors
controller_to_redirect = 'bacholors'
else
controller_to_redirect = 'masters'
end
redirect_to :controller => controller_to_redirect, :action => 'process_educations'
end
end
And three child controllers education/highschool_controller.rb , education/bacholors_controller.rb , education/masters_controller.rb with all the parametes present in educations_controller.rb passing to these controllers
class Education::HighschoolController < Education::EducationssController
def proceed_educations
do some process
end
end
class Education::BacholorsController < Education::EducationssController
def proceed_educations
do some process
end
end
class Education::MastersController < Education::EducationssController
def proceed_educations
do some process
end
end
And respective views come after that but in this process the url becomes too long so i wanted to remove all the controllers from the url and then process so irrespective of anything user is taken to same url, for this i changed routes.rb as
namespace :educations,:path => '' do
scope "/educations" do
get 'proceed_educations',to:'educations#proceed_educations'
post 'proceed_educations',to:'educations#proceed_educations'
end
resource :highschool ,:path => '' do
get 'proceed_educations',to:'highschool#proceed_educations'
post 'proceed_educations',to:'highschool#proceed_educations'
end
resource :bacholors ,:path => '' do
get 'proceed_educations',to:'bacholors#proceed_educations'
post 'proceed_educations',to:'bacholors#proceed_educations'
end
resource :masters ,:path => '' do
get 'proceed_educations',to:'masters#proceed_educations'
post 'proceed_educations',to:'masters#proceed_educations'
end
end
This generated the same url for every controller but when i try to access them as in educations_controller.rb they always redirect to same controller HighschoolController.
I am not getting where i am doing things wrong, please help me understand this process, or if there is any better way of doing this then please suggest.

If you want to avoid making the same URLs for two namespaced controllers, it will be difficult for the rails to understand which controller to send which request.
However, what you can do is, in your routes.rb, you can eliminate showing controller names and can add space there for differentiation, like this:
namespace :educations,:path => '' do
scope "/educations",:path => '' do
get 'proceed_educations',to:'educations#proceed_educations',:path => ''
post 'proceed_educations',to:'educations#proceed_educations',:path => ''
end
resource :highschool ,:path => '' do
get 'proceed_educations',to:'highschool#proceed_educations'
post 'proceed_educations',to:'highschool#proceed_educations'
end
resource :bacholors ,:path => ' ' do
get 'proceed_educations',to:'bacholors#proceed_educations',:path => ''
post 'proceed_educations',to:'bacholors#proceed_educations',:path => ''
end
resource :masters ,:path => ' ' do
get 'proceed_educations',to:'masters#proceed_educations',:path => ''
post 'proceed_educations',to:'masters#proceed_educations',:path => ''
end
end
This way, the URL will be same but with extra spaces.

Related

What is the best way to allow several methods on the controller without adding on routes?

Hi everybody I'm from the old school using rails 2.
Actually I'm using rails 4 and I'm trying to find a way to create methods on the controller without writting
On RAILS 2 used: (only needed to write the name on the controller)
#controller
def report_a
end
def report_b
end
def report_c
end
...and whatever def
#ROUTES
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
On RAILS 4
#controller
def report_a
end
def report_b
end
def report_c
end
#ROUTES
match ':controller(/:action(/:id(.:format)))', :via => [:get, :post]
The problem is when I create a view report like this: (views/reports/report_a.html.erb)
<%= form_tag :action=>"report_a" do %>
<% end %>
I get this message:
No route matches [GET] "/reports/report_a"
To resolve this issue and doing Rails instruccions works like this:
#controller
def report_a
#users= User.search(params[:name])
end
def result_report_a
#users= User.search(params[:name])
end
#view/reports/report_a.html.erb
<%= form_tag :action=>"result_report_a" do %>
<% end %>
#routes.rb
get "reports#report_a"
post "reports#result_report_a"
get "reports#report_b"
post "reports#result_report_b"
get "reports#report_c"
post "reports#result_report_c"
Also I found this better way:
#controller reports.rb
def search_report_a
report_a
render :report_a
end
def report_a
#users = User.where(:name=>params[:name])
end
def search_report_b
report_b
render :report_b
end
def report_b
#users = User.where(:address=>params[:address])
end
...
#Routes.rb
resources :users do
match 'search_report_a', :via => [:post,:get], :on => :collection
match 'search_report_b', :via => [:post,:get], :on => :collection
...
end
Is there any other way to create methods without adding all inside ROUTES.RB ?
Any suggestions or the only way is adding get and post?
Imagine a case where you have several methods.
Best approach in Rails is to use REST architecture. Your controller should be able to view, create, update and destroy some resource (of course all actions are not mandatory).
For example:
def ReportsController
def index
# Actions to show links to all possible reports
end
def show
# Show report based on params
end
end
Your #show method may show any of report (report_a, report_b, etc) just by checking param from GET request.
And you don't need to make all logics inside #show method. It would be better to place report-related logic in, maybe, some service objects.

How do I test that a before_filter will redirect for all Rails controller actions?

I have a fairly typical require_no_user as a before_filter in one of my controllers. I need to test that a logged in user is redirected by this filter if they try to access any of the controller's actions.
Is there a sensible way to do this without enumerating all of the controller's actions in my test case?
I'm trying to avoid:
context 'An authenticated user' do
setup do
activate_authlogic
#user = Factory(:user)
UserSession.create(#user)
do
should 'not be allowed to GET :new' do
get :new
assert_redirected_to(root_path)
end
should 'not be allowed to POST :create' do
# same as above
end
# Repeat for every controller action
end
Not that I'm aware of... though you could make it a bit shorter by packing all the methods and actions into a hash:
should "be redirected" do
{
:get => :new,
:post => :create,
}.each do |method, action|
send(method, action)
assert_redirected_to(root_path)
end
end
Edit: so yeah, this is probably overkill, but here's another way:
should "be redirected" do
ActionController::Routing::Routes.named_routes.routes.each do |name, route|
if route.requirements[:controller] == #controller.controller_name
send(route.conditions[:method], route.requirements[:action])
assert_redirected_to(root_path)
end
end
end
Seems though that if you define multiple :methods in custom routes that it still only "finds" the first, e.g.
map.resources :foo, :collection => {
:bar => [:get, :post]
}
The above route will only be attempted with the GET verb.
Also if there are other requirements in the URL, such as presence of a record ID, my naive example ignores that requirement. I leave that up to you to hack out :)

checking valid number of parameters in url rails

I have an url which accepts two parameter.How do i check the number of parameters passed in the routes and discard the request if it contains more than two parameters.
http://localhost:3000/users?product=car&category=honda
I want to check the number of parameters passed.
Thanks in advance
You can do that in you routes.rb with the constraint with a class to do it:
Create the class
class NbParametersConstraint
def initialize(nb)
#nb = nb
end
def matches?(request)
request.params.length <= #nb
end
end
And in your routes use it :
match "/users" => "users#index",
:constraints => NbParametersConstraint.new
After you can do that in your Controller by a filter
class UserController
before_filter :max_params, :only => :index
private
def max_params
render :status => 404 if params.size > 3
end
end

Advanced Routing with Rails3

I want to use regular expressions inside my routes. I have an Products controller, but I want a different URL structure to access the products
http://host/this/
http://host/that/
http://host/andthat/
These URLs should call a action in my controller (Products:show_category(:category))
Is something like this possible?
match "(this|that|andthat)" => "products#show_category", :category => $1
the action should look like this
def show_category
puts params[:category] # <-- "this" if http://host/this/ is called
# ...
end
I haven't actually tested it, but try out:
match ':category' => 'products#show_category', :constraints => { :category => /this|that|andthat/ }
I'm not too sure if this answers your question, but you could add a collection to routes.rb:
resources :products do
collection do
get :category1
get :category2
get :category3
end
end
If you then run rake routes, you'll see that you have urls like /products/category1 and products/category2. Category1, 2 and 3 can be defined in your controller as usual:
def category1
#custom code here
end
def category2
#custom code here
end
def category3
#custom code here
end
As I said, I'm not too sure if that's what you're looking to do, but hope that helps a bit!

Adding extra params to rails route resources

What I want to do seems simple, but might not be "proper"
let's say I have an image resource, and I manipulate the image based on the url. In the url I want to specify it's size and whether it's grayed, colored, or dimmed or some other condition.
currently I have a number of named routes that look like this.
map.gray_product_image "images/:product/:image/gray/:size.:format", :controller => 'images', :action => 'gray_product_image'
for me the trick is that if I created this useing Rails resources, I don't know how I would specify the :size, :format, or it's "color type".
I guess I would like to add a member route and specify my params like the following.
map.resources :products do |products|
products.resources :images, :member => {:gray_product_image => {':image/:size.:format' => :get}}
end
There are other times where I have wanted to added extra info to a resource route but didn't know how.
Any help would be greatly appreciated,
Thanks.
There's no good way to remove the controller/id part of a resource. The closest you're going to get through tricking ActionController with something like this:
map.resources :gray, :path_prefix => "/images/:product/:image_id/",
:controller => 'images', :requirements => {:colour => "gray"}
Which will produce routes like www.site.com/images/product/4/gray/1234.html with the following params hash:
params => {
:image_id => 4,
:id => 1234,
:colour => "gray",
:product => "product"
}
The format won't be passed explicitly but it will be available in the controller through the usually respond_to means.
Next you'll have to work some magic in controller to trick rails into doing what you want.
class ImagesController < ApplicationController
def show
#size = params[:id]
#image = Image.find(params[:image_id])
...
end
end
This actually works better as a filter so:
class ImagesController < ApplicationController
def initialize_colour
unless params[:colour].nil?
#size = params[:id]
#colour = params[:colour]
#image = Image.find(params[:image_id])
end
end
before_filter :initialize_colour, :except => [:index, :new, :create]
...
end
However to make good use of these routes, you're going to have to pass all those extra parameters to your url for calls. Like this:
gray_url(size, :image_id => #image.id, :product => product)
But helpers make that easy.
module ApplicationHelper
def easy_gray_url(image, size, product)
gray_url(size, :image_id => image.id, :product => product)
end
end
Check out the documentation for Resources. You'll find this:
The resources method accepts the
following options to customize the
resulting routes:
:requirements - Set custom routing parameter requirements; this is a hash of either regular expressions (which must match for the route to match) or extra parameters. For example:
map.resource :profile,
:path_prefix => ':name',
:requirements => { :name => /[a-zA-Z]+/, :extra => 'value' }
will only match if the first part is alphabetic, and will pass the parameter :extra to the controller.
I have realized that the way I want to represent my resources simply falls outside of the normal Rails resources, and that's ok. The problem I was really having was that each time added anther action and named route to get to what I wanted it felt wrong, I was repeating myself, both in my routes and in my actions.
I went back to simply creating my named routes, and spent a little more time in the controller so that I could keep my routes simple. Below is what I have now, and I am ok with it.
#routes.rb
map.with_options :controller => 'sketched_images', :action => 'show', :path_prefix => '/sketches', :name_prefix => 'sketched_', :color => 'grey' do |m|
m.style "styles/:style/:color/:size.:format"
m.design "designs/:design/:color/:size.:format"
m.product "products/:product/:color/:size.:format"
m.color_combo "colored_products/:color_combo/:size.:format"
end
class SketchedImagesController < ApplicationController
caches_page :show
before_filter :load_data
def show
#size = params[:size] || 100
respond_to do |wants|
wants.png
wants.jpg
end
end
private
def load_data
case
when params[:design]
#image = ClothingDesign.from_param(params[:design]).sketched_image
greyed
when params[:style]
#image = ClothingStyle.from_param(params[:style]).sketched_image
greyed
when params[:product]
#image = Product.from_param(params[:product]).sketched_images.first
greyed
when params[:color_combo]
#color_combo = ColorCombo.find_by_id(params[:color_combo])
#object = #color_combo.colorable
if #object.active? && !#object.sketched_images.blank?
#image = #object.sketched_images.first
colored
else
#image = #product.style.sketched_image
dimmed
end
end
end
def greyed
#blank = "#FFF"
#print = "#000"
#highlight = "#666"
end
def colored
#blank = "##{#color_combo.blank_color.value}"
#print = "##{#color_combo.design_color.value}"
#highlight = "##{#color_combo.highlight_color.value}" unless #color_combo.highlight_color.blank?
end
def dimmed
#blank = "#BBB"
#print = "#000"
#highlight = "#444"
end
end

Resources