Rails 3 routes 'match' syntax - ruby-on-rails

I want to route
[PUT] http:// foo /data/base_states/?id=8
to my controller, preferably with this style of definition:
`match '/data/base_state:id' => 'base_state#create_or_update', via: [:put]`
How?
It seems there are many different ways to define routes, with documentation somewhat lacking. Would be interested in any other suggested syntax (that works) as well.
No idea why the suggested routes aren't working, this is my routes.rb and controller ...
config/routes.rb
EntwineBe::Application.routes.draw do
namespace :data, defaults: {format: :json} do
resources :event_categories
resources :state_categories
resources :base_events
resources :base_states
match '/block/:bock' => 'block#show', via: [:get] # curiously, this WORKS AS EXPECTED with GET http://foo/data/block/foo
match '/base_state/:id' => 'base_states#create_or_update', via: [:put]
end
end
app/controllers/data/base_states_controller.rb
class Data::BaseStatesController < ApplicationController
before_action :set_base_state, only: [:show, :edit, :update, :destroy]
def index
#base_states = BaseState.all
end
def show
end
def new
#base_state = BaseState.new
end
def edit
end
def create # typical REST is that this is PUT
#base_state = BaseState.new(base_state_params)
respond_to do |format|
if #base_state.save
format.html { redirect_to #base_state, notice: 'Base state was successfully created.' }
format.json { render action: 'show', status: :created, location: data_base_state_url(#base_state) }
else
format.html { render action: 'new' }
format.json { render json: #base_state.errors, status: :unprocessable_entity }
end
end
end
def create_or_update
logger.info "WTF"
end
def destroy
#base_state.destroy
respond_to do |format|
format.html { redirect_to base_states_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_base_state
#base_state = BaseState.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def base_state_params
params.require(:base_state).permit(:foo, :bar)
end
end
Relevant output of 'rake routes'
data GET /data/block/:block(.:format) data/code_block#show {:format=>:json}
PUT /data/base_state/:id(.:format) data/base_states#create_or_update {:format=>:json}
PUT /data/base_state:id(.:format) base_state#create_or_update {:format=>:json}
example call
$.ajax({'type':'PUT', 'url':'/data/base_state?id=8'})
$.ajax({'type':'PUT', 'url':'/data/base_state/?id=8'})
$.ajax({'type':'PUT', 'url':'/data/base_state?id=8.json'}) // not that expect it to need / I want to have .json
$.ajax({'type':'PUT', 'url':'/data/base_state/?id=8.json'}) // not that expect it to need / I want to have .json
--> 404!

Add defaults: {format: :json} to your route.
match '/data/base_state:id' => 'base_state#create_or_update', via: [:put], defaults: {format: :json}

put '/data/base_state:id' => 'base_state#create_or_update', defaults: { format: :json }

put "data/base_state:id(.:format)" => 'base_state#create_or_update'

Hope I'm not too late with this!
If your route is /data/base_state/:id, then the url you use to view the matching resource with id 8 say is /data/base_state/8.json.
As spas correctly points out, if you set the correct datatype in the ajax call, dataType: 'json', then you do not need to add the .json at the end of the URL

Related

Rails Resources new and edit routes redirecting to root page instead of their respective routes

Basically, I'm trying to update the layout of my portfolio/new form but whenever I type in 'localhost:3000/portfolios/new' I get redirected to my home page. Same with 'localhost:3000/portfolios/(:id)/edit'
My routes are below:
Rails.application.routes.draw do
#Devise Routes
devise_for :users, path: '', path_names: { sign_in: 'login', sign_out: 'logout', sign_up: 'register' }
#homepages routes
get 'about-me', to: 'pages#about'
get 'contact', to: 'pages#contact'
# blog
resources :blogs do
member do
get :toggle_status
end
end
#portfolio
resources :portfolios, except: [:show]
get 'portfolio/:id', to: 'portfolios#show', as: 'portfolio_show'
get 'react-items', to: 'portfolios#react'
# setting root path --> ex: localhost:3000/
root to: 'pages#home'
end
Here is my controller:
class PortfoliosController < ApplicationController
before_action :set_portfolio_item, only: [:edit, :update, :show, :destroy]
layout "portfolio"
access all: [:show, :index, :react], user: {except: [:destroy, :new, :create, :update, :edit]}, site_admin: :all
def index
#portfolio_items = Portfolio.all
end
# custom scope
def react
#react_portfolio_items = Portfolio.react
end
def show
end
def new
# new portfolio item is initialized.
#portfolio_item = Portfolio.new
3.times { #portfolio_item.technologies.build }
end
def create
#portfolio_item = Portfolio.new(portfolio_params)
respond_to do |format|
if #portfolio_item.save
format.html { redirect_to portfolios_path, notice: 'Portfolio Item was successfully created.' }
else
format.html { render :new }
end
end
end
def edit
end
def update
respond_to do |format|
if #portfolio_item.update(portfolio_params)
format.html { redirect_to portfolios_path, notice: 'Portfolio Item was successfully updated.' }
else
format.html { render :edit}
end
end
end
def destroy
# destroy the record
#portfolio_item.destroy
# redirect
respond_to do |format|
format.html { redirect_to portfolios_url, notice: 'Record was removed.' }
end
end
private
def portfolio_params
params.require(:portfolio).permit(:title,
:subtitle,
:body,
technologies_attributes: [:name]
)
end
def set_portfolio_item
#portfolio_item = Portfolio.find(params[:id])
end
end
So overall I'm not sure why it's doing that. When I do rails routes I can see the correct paths but when I input them in the browser, they do not work.
Any help would be greatly appreciated.
I think you had reverted access to create/update/edit/destroy.
Remove except condition from access_all for user create/update/edit/destroy
when user trying to opening a page which doesn't has permission to view, it will be redirect_to its root_path by default.

Dry repeated methods and routes in Rails

I'm new to ruby/rails and I have this situation with 3 resources which uses comments provided by the acts_as_votable gem. The thing is, I have added the necessary methods and routes in all of the resources, resulting in a complete non-dry aberration.
My routes are:
Rails.application.routes.draw do
root to: 'home#index'
get 'home/index', to: 'home#index'
resources :users
get 'thinga/tagged/:tag', to: 'thinga#tags', as: 'tagged_thingas'
get 'thingb/tagged/:tag', to: 'thingb#tags', as: 'tagged_thingbs'
resources :thinga do
member do
get :like
get :dislike
get :unvote
end
end
resources :thingb do
member do
get :like
get :dislike
get :unvote
end
end
resources :thingc, only: [:create, :update, :destroy] do
member do
get :like
get :dislike
get :unvote
end
end
devise_for :users, path: 'auth',
:controllers => { :omniauth_callbacks => 'omniauth_callbacks' }
end
In the controllers the repeated code is
def like
#thinga = Thinga.find(params[:id])
#thinga.liked_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js { render layout: false }
end
end
def dislike
#thinga = Thinga.find(params[:id])
#thinga.disliked_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js { render layout: false }
end
end
def unvote
#thinga = Thinga.find(params[:id])
#thinga.unvote_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js { render layout: false }
end
end
Can someone help me to learn how to dry off this cases please?. Thank you.
Try rails routes concerns
http://edgeguides.rubyonrails.org/routing.html#routing-concerns
And I think you'll be able to do this
concern :votable do
member do
get :like
get :dislike
get :unvote
end
end
resources :thinga, :thingb, :concerns => :votable
resources :thingc, :concerns => :votable, only: [:create, :update, :destroy]
For the controllers - Make a VotablesController and have the ThingasController or ThingbsController subclass it. You can call a before action to set the #votable instance variable and have the subclasses set the variable. The shared methods will be in one place.
class VotablesController < ApplicationController
before_action :set_votable
def like
#votable.liked_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js { render layout: false }
end
end
def dislike
#votable.disliked_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js { render layout: false }
end
end
def unvote
#votable.unvote_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js { render layout: false }
end
end
end
in the subclasses (this is an example for thinga)
class ThingasController < VotablesController
def set_votable
#votable = Thinga.find(params[:id])
end
end
Ruby provides a lot of scope for using code to write code (metaprogramming), so you should be able to write a method that will do the job of adding the repeated routes for you. Perhaps the following code will run:
Rails.application.routes.draw do
def create_votes_resources(name, options = {})
resources name, options do
member do
get :like
get :dislike
get :unvote
end
end
end
create_votes_resources :thing_a
create_votes_resources :thing_b
create_votes_resources :thing_c, only: [:create, :update, :destroy]
end

Rails way to define parameter for filter

There is the following routes:
namespace :api do
namespace :v1 do
resources :places, only: [:index]
end
end
The code of the controller:
class API::V1::PlacesController < API::V1::ApplicationController
def index
#places = (!params[:id]) ? Place.all : Place.find_all_by_type_id(params[:id])
respond_to do |format|
format.json { render json: #places }
format.html
end
end
end
'Place' has 'type_id' field, and I want to filter places by its filter_id. As you can see, now I send the parameter through URL as "places?id=1". But may be I must send parameter as "places/1"? I need also to set up paths; now they doesn't work with "?id=1" form. Please, tell me, how should I do? Thanks.
Rails convention would be to have the list of the places in the "index" action mapped to the relative path /places (GET method).
And then /places/1 (GET) would be mapped to "show", which is intended for presenting a member of the collection. For "show", the route would assign the ID segment of the path ("1") to params[:id].
The guides have a table of default route mappings. The :type_id attribute in the model vs. the :id attribute in the route probably confused you.
A simple solution would be to use /places?type_id=1 instead. In your controller, you can have something like:
def index
collection = Place.all
collection = collection.where(:type_id => params[:type_id].to_s) unless params[:type_id].to_s.blank?
respond_to do |format|
# ...
end
end
Setting :type_id as a query parameter instead of integrating into the relative path seems especially reasonable to me since you are building an API and might add support for more filters in the future.
My recommendation is to rewrite it like this:
# Your routes
namespace :api do
namespace :v1 do
resources :places, only: [:index]
get "/places/by_type/:type_id" => "places#by_type", as: :places_by_type
end
end
# Your controller
class API::V1::PlacesController < API::V1::ApplicationController
def index
respond_to do |format|
format.json { render json: #places }
format.html
end
end
def by_type
#places = Place.where(type_id: params[:type_id])
respond_to do |format|
format.js { render json: #places }
format.html do
render action: "index"
end
end
end
end
I could be slightly wrong about the routes, but I'm pretty sure it should work.

Rails 3.1/Mongoid - How to find model by name rather than id in routes

Edit: i am using Mongoid/MongoDB for my database, meaning I don't get the normal Active Record tools I think.
I have a simple Rails 3.1 app with a model Page. I would like to match '/:customURL' to the Page#show action for the Page with the relevant :customURL. How should I change the controller and routes? Keep in mind that there are a few routes from '/SOMETHING' that I want to keep. For instance '/pages' should still go to my Page#index action not try to find a page with customURL of 'pages'.
current controller:
def show
#page = Page.find(params[:id])
#title = #page.title
respond_to do |format|
format.html # show.html.erb
format.json { render json: #page }
end
end
routes:
resources :pages do
resources :feeds
end
get "pages/index"
get "pages/show"
get "pages/new"
root :to => "pages#index"
Thanks a million.
Assuming that your Page has a customURL attribute from its database table. In your controller:
def show
#page = Page.first(:conditions => {:customURL => params[:customURL]})
#title = #page.title
respond_to do |format|
format.html # show.html.erb
format.json { render json: #page }
end
end
In your routes
resources :pages, :except => :show do
resources :feeds
end
# Anything you want to match before the custom URLs needs to go above the next route definition
get "/:customURL" => "pages#show"
root :to => "pages#index"

Confused about Ruby on Rails REST Routing

I am really confused about Ruby on Rails REST routing. Even though I have specified that after the success it should go to the confirm action it goes to the show action and pass the ID=confirm.
def create
#article = Article.new(params[:article])
respond_to do |format|
if #article.save
format.html { redirect_to :action => "confirm" }
else
format.html { render :action => "new" }
end
end
end
The error I get is the following:
ActiveRecord::RecordNotFound in ArticlesController#show
Couldn't find Article with ID=confirm
Rails.root: /Projects/highoncoding
Application Trace | Framework Trace | Full Trace
app/controllers/articles_controller.rb:31:in `show'
UPDATE 1:
Here is my Route.rb file:
resources :articles
get "articles/confirm"
# config/routes.rb
MyApp::Application.routes.draw do
resources :articles do
member do
get 'confirm'
end
end
end
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def create
#article = Article.new(params[:article])
respond_to do |format|
if #article.save
# use a named route here
format.html { redirect_to confirm_article_url(#article) }
else
format.html { render :action => "new" }
end
end
end
end
you'll need to add the route so it looks like
match 'articles/confirm/', :controller => 'article', :action => 'confirm'
resources :articles
you need to have the :id in there or it will think that confirm is an id which is why you are seeing the error ID=confirm. make sure also that this is the first route. (at least before the resources for the articles controller.
You should probably add the confirm route directly in your routes file.
match 'articles/confirm' => 'articles#confirm'
resources only work for create/update/destroy/etc.

Resources