Rails 4 - Routing & Namespaces? - ruby-on-rails

Ok, so - What did I do wrong? I feel like I'm missing something very simple...
REQUESTS belongs to USER
USER has many REQUESTS
I'm logged in as current_user (id=3) and want to list my requests:
<%= link_to("My Requests", user_requests_path(current_user)) %>
That link goes to /users/3/requests, but it shows ALL requests, not just those belonging to me.... ???
routes.rb
resources :users do
resources :requests
end
rake routes:
user_requests GET /users/:user_id/requests(.:format) requests#index

This isn't related to your routes, then, it's a scoping problem on your ActiveRecord query. You probably have something like the following in RequestsController:
def index
#requests = Request.all
end
But what you need to have is something more like the following:
def index
#requests = current_user.requests
end
If your Request resource can be accessed independently of users (i.e. there's a use-case for Request.all or /requests) you should actually do a separate namespaced controller (e.g. Users::RequestsController) to handle user-specific requests. Your routes will then need to specify the namespace for the user-specific requests as well.

Related

Nested resource from own controllers method - or create completely new controller?

Issue: When a user (when signed in) creates an order, they are sent to the OrderControllers show page which can only be accessed if signed in by both the buyer and seller. From here they can edit/update their order, etc.
We also have guest_user, someone who isn't signed in, and for them I need a order confirmation in the browser (I'm using Devise gem)
I have created a method:
def order_confirmation
In the OrdersController.
Although, how can I nest this within orders so the page knows which order to show.
Is this possible to nest methods under its' own controllers, or should i just create a small controller only for order confirmations?
For example: example.com/orders/1/order-confirmation
Maybe there are better ways to go about this other than just nesting and creating a controller?
I Tried:
resources :orders do
collection do
get 'order_confirmation'
end
end
With:
def order_confirmation
#order = Order.all.find(params[:id])
end
But it won't work how i want i t seems.
The rake routes gives me:
order_confirmation_orders GET /orders/order_confirmation(.:format)
How can i get?:
order_order_confirmation_orders GET /orders/id/order_confirmation(.:format)
I was able to figure this out from the help of this SO post:
Rails: Custom nested controller actions
By using:
resources :orders do
get 'order_confirmation', :on => :member
end
This creates:
order_confirmation_order GET /orders/:id/order_confirmation(.:format) orders#order_confirmation

Rails routing: Scope using a database field

I am creating an multitenant app based on ideas from Ryan Bigg's book "Multitenancy with Rails". In this book, the tenants has their own subdomain. This approach is not applicable in my case, so I'm trying to scope by a slug of the account's name instead.
So instead of URLs like http://account-name.myapp.com, i want http://myapp.mydomain.com/account-name/. The subdomain is reserved for the app itself, because I want to be able to have more than one app on my domain.
Here's a piece of my routes.rb:
scope module: 'accounts' do
resources :customers do
resources :notes
end
end
To achieve my goal, i try to follow the routing guide on rubyonrails.com (the last code snippet in chapter 4.5), and change the above code to:
scope ':slug', module: 'accounts' do
resources :customers do
resources :notes
end
end
slug is an attribute in the accounts table in the database, so if an account is called "My Business", the slug will typically be "my-business".
This change seems to correct my routes:
customers GET /:slug/customers(.:format)
.. but it also seems to break my site, as the slug is not fetched from the database. I can't seem to wrap my mind around how this scope':slug', module: 'accounts' works. Is Rails supposed to automatically recognize :slug as an attribute of the Accoounts table? If not, can anyone please help me find a way to use the account's slug in my URLs?
I have googled around for a couple of days now, and read numerous answers here on Stackoverflow. Nothing helped, so any pointers is greatly appreciated. :-)
EDIT:
The relevant controllers are set up like this:
controllers/accounts/base_controller.rb
controllers/accounts/customers_controller.rb
controllers/accounts/products_controlelr.rb
controllers/accounts_controller.rb
controllers/application_controller.rb
The accounts_controller.rb only has actions for new and create at this point.
The accounts/base_controller.rb look like this:
module Accounts
class BaseController < ApplicationController
before_action :authorize_user!
def current_account
#current_account ||= Account.find_by_slug(params[:slug])
end
...
end
end
I addded this to my Account model:
def to_param
slug
end
Before i tried to implement scope ':slug' in my routes, everyting worked when logged in users where directed to myapp.mydomain.com/dashboard and navigated to i.e. myapp.mydomain.com/customers. Now it works with myapp.mydomain.com/account-name/dashboard, but as soon as I try to navigate to a view that use helpers like new_customer_path, i get the error:
No route matches {:action=>"show", :controller=>"accounts/customers", :id=>nil, :slug=>#
I hope this makes my issue clearer. :-)
I am not sure whether your routes is set up correctly or not because you didn't post your controller source code, but basically how it works if very simple. If you are using the current routes you set up what you should do is create an account_customers_controller.rb file in controllers\account\folder, and it should look like this:
class Accounts::CustomersController < ActionController::Base
def show
#account = Account.find_by_slug(params[:slug])
...
end
end

Rails 4. Different routing for few models (relatd and not)

I am now working on website SEO optimization and what I am required to do is proper routing for links to be very seo friendly. I have read lots of information about routing, but it messed up in my head and I stuck.
So I have Store model which belongs to StoreType model, to City model and District model + District belongs_to :city.
I need to have routes like this:
/stores/store_type_name/ - store_type 'show' action(list of stores by type)
/stores/city_name/store_type_name/ - store_type 'show' action(list of stores by city&type)
/stores/city_name/district_name/store_type_name/ - store_type 'show' action(list of stores by city&district&type)
/stores/city_name/store_type_name/store_name - store 'show' action
The only solution I came up with for now is:
Routes.rb
namespace :stores do
get ':transliterated', to: 'store_types#show'
get ':transliterated/:name_en', to: 'store_types#city'
get ':transliterated/:name_en/:id', to: 'store_types#district'
end
With controller like this:
def district
#store_type = StorerType.find_by_transliterated(params[:transliterated])
#city = City.find_by_name_en(params[:name_en])
#district = District.find_by_id(params[:id])
if #store_type && #city && #district
stores = #store_type.stores.where(city_id:#city.id)
#stores = stores.where(district_id:#district.id)
else
redirect_to root_path
end
end
That works well but 1) I can not now add route for last example(store show page) as route is looking for :transliterated params in that namespace and redirects if record is not found. 2) I understand that this solution is bad and can be done much better, I just do not know how. Give me an advice please.
PS. Actually there is routing implemented on the site already so I am looking for the solution for those 4 urls listed above only, without touching anything else there.
Resourceful
Firstly, let me define the basis of all your routing for you...
Rails' routing structure is known as being resourceful - meaning based around resources / objects. As with Ruby being an object-orientated language, Rails is an object-orientated framework; the routes are no exception to this:
This means anything you do with your routes has to be resource-based, as follows:
#config/routes.rb
namespace :stores do
resources :store_types, only: [:show], path: "" do #-> domain.com/stores/:id -> store_types#show
get :name_en, action: :city #-> domain.com/stores/:store_type_id/:name_en
get :name_en/:id, action: :district #-> domain.com/stores/:store_type_id/:name_en/:id
end
end
This will give you the ability to send the traffic directly to your store_types controller without having all sorts of crazy routes all over the place
--
friendly_id
Something else to consider is a gem called friendly_id
friendly_id basically allows you to define / call routes with slugs, rather than ids. The difference is that the routes remain the same - it's the data, and the handling of that data, which changes
Typically in Rails, you'll create routes like this: domain.com/controller/:id
When you send people to links, they'll hit domain.com/controller/1 for example. Friendly_ID basically facilities the ability to send people to domain.com/controller/your_name, handling it in exactly the same way as you would with an ID:
#app/models/your_model.rb
Class YourModel < ActiveRecord::Base
friendly_id :name, use: [:slugged, :finders]
end
This will allow you to call:
#app/controllers/your_controller.rb
Class YourController < ApplicationController
def show
#model = Model.find params[:id]
end
end
You can use some static strings in the urls to help to identify the actions, for examples:
namespace :stores do
get 'type/:transliterated', to: 'store_types#show'
get 'type/:transliterated/city/:name_en', to: 'store_types#city'
get ':transliterated/:name_en/:id', to: 'store_types#district'
end

Customize/modify Rails 3 resources routing

I'm currently writing a rails app that has your regular resource like objects. However I would like to make my resources syncable. My web application uses web offline storage to cache results from the server (user's cannot modify data on server making syncing easier). When I fetch from the server, it returns a hash response like:
{
:new => [...]
:updated => [...]
:deleted => [...]
}
This is all well and good until I want to have a regular fetch method that doesn't do any sort of syncing and simply return an array of models
Now my question is I want to create a method in my routes.rb file that sets up routes so that I have a route to synced_index and index. Ideally, I'd be able to do something like this:
synced_resources :plans
And then all of the regular resource routes would be created plus a few extra ones like synced_index. Any ideas on how to best do this?
Note: I do know that you can modify resources with do...end syntax but I'd like to abstract that out into a function since I'd have to do it to a lot of models.
You can easily add more verbs to a restful route:
resources :plans do
get 'synced_index', on: :collection
end
Check the guides for more information on this.
If you have several routes that are similar to this, then sure, you can add a 'synced_resources' helper:
def synced_resources(*res)
res.each do |r|
resources(r) do
get 'synced_index', on: :collection
end
end
end
Wrap above method in a module to be included in ActionDispatch::Routing::Mapper.

RESTFUL routing with the same page

I am looking to find the best way to setup the routes for my app.
The application allows users to register, post jobs and apply for jobs. The issue I am having is that two routes that should really show different things are linking through to the same pages.
My routes are as follows:
resources :users do
resources :apps
end
resources :jobs do
resources :apps
end
As a result of this I end up with two paths:
users/id/apps
jobs/id/apps
What I would like to do is use the first path to show job applications that a user has completed. I would then like to use the second path to show the owner of the job the applications they have received for that job.
The issue I am having is that both paths end up at apps#index
Any advice people can offer on how to best route this would be much appreciated! :)
Both those paths are potentially fine for what you want to do. When a request comes into
users/:user_id/apps
then you'll be able to do something like this in your controller to populate the list of apps:
#apps = User.find_by_id(params[:user_id]).apps
And in the case of the other path, you'll do the same but with params[:jobs_id], e.g.
#apps = Job.find_by_id(params[:job_id]).apps
In your controller you would have some conditional code that builds #apps depending on which path the request came in via (by looking to see if :user_id or :job_id is in params)... something like this:
if params[:user_id]
#apps = User.find_by_id(params[:user_id]).apps
elsif params[:job_id]
#apps = Job.find_by_id(params[:job_id]).apps
end
or maybe refactored to...
if params[:user_id]
#user = User.find(params[:user_id])
#apps = #user.apps
elsif params[:job_id]
#job = Job.find(params[:job_id])
#apps = #job.apps
end
If you use the "resources" keyword, rails will just map to the default routes.
If you would like specific routes to map to something else, you should consider using "match."
For example:
match "users/id/apps" => "users/id/apps"
match "jobs/id/owners" => "jobs/id/owners"
This page shows a more detailed usage of it: http://guides.rubyonrails.org/routing.html
You also have the options to change the route to something else or the code itself.
Hope that helps.

Resources