ActionRecord Not Found. Couldn't find with id= - ruby-on-rails

I'm using Caracal Gem to export a Docx file, the gem is generating the document normally. But when I try to print the contract data I get the error. It's probably some routing problem, I'm stuck in this two weeks and I'm almost giving up.
The error is Action Record Not Found Couldn't fid contrato with id=
Here is my code I highlighted the parts in the code.
Routes.rb
Rails.application.routes.draw do
get 'grayscale/index'
get 'contratos/page' HERE IS THE ROUTING
devise_for :users, path: '', path_names: {sign_in: 'login', sign_out: 'logout', sign_up: 'registrar'}
resources :contratos
root 'contratos#index'
get '/contrato_export' => 'contratos#export'
resources :contratos do
member do
# /contratos/:id/export
get 'export'
end
end
end
contratos_controller.rb
before_action :authenticate_user!
before_action :set_contrato, only: [:show, :edit, :update, :destroy, :export]
access all: [:show, :index], user: {except: [:destroy, :new, :create, :update, :edit]}, site_admin: :all
require './lib/generate_pdf'
def page
contrato = Contrato.find(params[:id])<---HERE IS THE PROBLEM!!!!!
Caracal::Document.save(Rails.root.join("public", "example.docx")) do |docx|
docx.style do
id 'Body'
name 'body'
font 'Times New Roman'
size 24
end
docx.h2 'Contrato'
docx.p do
style 'Body'
text 'Lorem ipsun dolor sit amet'
text contrato.day <--- HERE IS THE DATA THAT I WANT TO PRINT IN THE DOCX. (IN THE CONTRATO SHOW is #contrato.day, I WANT THAT DATE TO BE PRINTED.
end
end
path = File.join(Rails.root, "public")
send_file(File.join(path, "example.docx"))
end

You have a missing params
get 'contratos/:id/page', to: 'contratos#page'
#or
resources :contratos do
member do
get 'export' # /contratos/:id/export
get 'page' # add this!
end
end

That tutorial is just utter garbage. You can do the exact same thing (or at least what I think your trying to do) RESTfully without adding any additional routes just by using ActionController::MimeResponds:
class ContratosController < ApplicationController
# GET /contratos/1
# GET /contratos/1.pdf
# GET /contratos/1.docx
def show
respond_to do |format|
format.html {}
format.pdf { send_file Contratos::PdfConverter.call(#contrato) }
format.docx { send_file Contratos::XMLConverter.call(#contrato) }
end
end
end
The key here is keep your controller skinny. Controllers are notoriously hard to test.
We haven't actually declared Contratos::PdfConverter yet but this is one place where you could just use the service object pattern:
# app/services/base_service.rb
class BaseService
def self.call(*args, **kwargs, &block)
new(*args, kwargs, &block)
end
end
# app/services/contratos/docx_converter.rb
module Contratos
class DocxConverter < BaseService
# #return [Pathname]
def call(contrato)
path = Rails.root.join("public", "example.docx")
Caracal::Document.save(path) do |docx|
docx.style do
id 'Body'
name 'body'
font 'Times New Roman'
size 24
end
docx.h2 'Contrato'
docx.p do
style 'Body'
text 'Lorem ipsun dolor sit amet'
text contrato.day # ...
end
end
path
end
end
end
# app/services/contratos/pdf_converter.rb
module Contratos
class PdfConverter < BaseService
def call(contrato)
# generate PDF here
end
end
end
This lets you test the conversion separately and avoids turning your controller into a flaming garbage pile.

Related

Rails controller isn't getting specific params

I'm trying to follow wicked tutorial for creating an object partially
( https://github.com/zombocom/wicked/wiki/Building-Partial-Objects-Step-by-Step )
The problem is, I am having trouble creating the object itself. I've tried with and without strong params, or even making the call out of the controller, but can get it passed. What am I doing wrong?
class ProspectsController < ApplicationController
include Wicked::Wizard
steps :signup, :business_details, :user_details
def show
create_prospect if params[:prospect_id].nil?
byebug # => prospect_id is no appearing => Not_found
#prospect = Prospect.find(params[:prospect_id])
render_wizard
end
def update
#prospect = Prospect.find(params[:prospect_id])
params[:prospect][:status] = 'users_detailed' if step == steps.last
#prospect.update_attributes(params[:prospect])
render_wizard #prospect
end
def create_prospect
#prospect = Prospect.create
new_prospect_build_path(prospect_id: #prospect.id)
end
# def prospect_params
# params.require(:prospect).
# permit(:user_first_name, :user_last_name, :user_email, :dni, :plan, :empresa_name, :empresa_email,
# :empresa_phone, :empresa_address, :empresa_web, :empresa_category, :empresa_summary, :user_birthday,
# :user_phone, :user_address, :sex, :iban_code, :status, :prospect_id)
# end
end
Routes:
resources :prospects, only: [:show, :update] do
resources :build, controller: 'prospects'
end
you're using same controller action for two routes:
GET /prospects/:prospect_id/build/:id => prospects#show
GET /prospects/:id => prospects#show
same with update.
If you will get to that controller by GET prospect_path you will not get :prospect_id, but :id.

Rails | nested routes if nil

I have used friendly_id and globalize gem. So I can generate routes as;
/search/france/weekly/toyota-95
Here is my routes;
namespace :search do
resources :car_countries, path: '', only: [] do
resources :rental_types, path: '', only: [] do
resources :car_types, path: '', only: [:show] do
end
end
end
end
But the thing is now I would like to also get city either;
/search/nice/weekly/toyota-95
or
/search/france/nice/weekly/toyota-95
The problem is I want to have both with city name and without city name (only country). They should go to same controller which is at the end car_types.
So if I add car_cities to routes, I get error when there is no city but only country.
namespace :search do
resources :car_countries, path: '', only: [] do
resources :car_cities, path: '', only: [] do
resources :rental_types, path: '', only: [] do
resources :car_types, path: '', only: [:show] do
end
end
end
end
resources :car_countries, path: '', only: [] do
resources :rental_types, path: '', only: [] do
resources :car_types, path: '', only: [:show] do
end
end
end
end
How can I do that?
As Gerry says, take a look at route globbing http://guides.rubyonrails.org/routing.html#route-globbing-and-wildcard-segments. I'd suggest to send everything to a single controller action and either do stuff there or delegate it to a search model/service object (depends on your taste).
Example:
# in config/routes.rb
get 'search/*q' => 'searches#show'
# in app/controllers/searches_controller.rb
class SearchesController < ApplicationController
def search
# This should work for your simple use case but it will become pretty confusing if you add more filters.
search_params = params[:search].split('/')
if search_params.count == 4
country, city, rental_type, car_type = search_params
else
country, rental_type, car_type = search_params
end
# Do whatever with these variables, e.g. Car.for_country(country)...
end
end
A more stable solution would be to make use of the fact that rental types are probably a closed set (daily, weekly, ...) and use segment constraints for this part in the routes:
# config/routes.rb
scope to: 'searches#show', constraints: { rental_type: /(daily|weekly|monthly)/ } do
get '/search/:country/:rental_type/:car_type'
get '/search/:country/:city/:rental_type/:car_type'
end
This should differentiate the two URLs based on the fact that :city can never match the rental type constraint.
Yet another option would be to use a full blown constraints object (http://guides.rubyonrails.org/routing.html#advanced-constraints):
# config/routes.rb
class SearchConstraint
def initialize
# assuming that all your objects have the friendly_id on the name field
#country_names = %w[austria germany]
#rental_type_names = %w[daily weekly monthly]
#car_type_names = %w[toyota-prius vw-golf]
#city_names = %w[innsbruck munich berlin]
end
def matches?(request)
checks = []
# only check for parts if they're actually there
checks << #country_names.include?(request.parameters[:country]) if request.parameters[:country].present?
checks << #rental_type_names.include?(request.parameters[:rental_type]) if request.parameters[:rental_type].present?
checks << #car_type_names.include?(request.parameters[:car_type]) if request.parameters[:car_type].present?
checks << #city_names.include?(request.parameters[:city]) if request.parameters[:city].present?
checks.all? # or, if you want it more explicit: checks.all? { |result| result == true }
end
end
scope to: 'searches#show', constraints: SearchConstraint.new do
get '/search/:country/:rental_type/:car_type'
get '/search/:country/:city/:rental_type/:car_type'
end
Note that this last approach is probably the cleanest and least hacky approach (and it's quite easy to test) but it also comes at the cost if involving the database in every request to these particular URLs and the URLs fail hard if there's an issue with the database connection.
Hope that helps.

Redirecting to another view in rails

I'm trying to build a link shortener. The intended behavior is that on the first page (new) the user inserts his long link and presses a button, then he gets redirected to an another page called result, where a preset message will be waiting for him, along with both his short and long link.
I'm struggling with controllers, however, as no matter what I do something always comes wrong. Right now my controller looks like this:
class UrlsController < ApplicationController
def new
#short_url = Url.new
end
def create
#short_url = Url.new(url_params)
if #short_url.save
flash[:short_id] = #short_url.id
redirect_to "/urls/result"
else
render action: "new"
end
end
def show
Url.find(params[:id])
##short_url_yield =
redirect_to #short_url.url
end
def result
end
private
def url_params
params.require(:url).permit(:url)
end
end
And the routes.rb:
Rails.application.routes.draw do
resources :urls, :only => [:show, :new, :create, :result]
get 'urls/result' => 'urls#result'
root to: redirect('/urls/new')
end
When I submit the link, however, rails returns the following error:
Couldn't find Url with 'id'=result
Extracted source (around line #17):
def show
Url.find(params[:id])
##short_url_yield =
redirect_to #short_url.url
end
It seems I don't understand the logic behind it. What's going wrong? Isn't the show bit supposed to be a redirect that happens when I click the shortified link?
Rails routes have priority in the order they are defined. Since your SHOW route declaration is before get 'urls/result' => 'urls#result' the url gets matched as /urls/id=result.
Simply move your custom route above the resources block or use a collection block.
resources :urls, :only => [:show, :new, :create, :result] do
collection do
get 'result'
end
end
Using the collection and member blocks tells Rails to give priority to the routes inside over the normal CRUD actions such as show.

Publication status, Rails

I want to change the status of hotels in my site. When user create new hotel, he have status "pending". As an administrator, I can upgrade the hotel status from pending to approved or rejected. But I can not approved of in the rejected and vice versa.
I decided to do it with three buttons in admin panel in the place where showing all hotels but this code not working.
routes.rb
HotelAdvisor::Application.routes.draw do
devise_for :admins
devise_for :users
devise_scope :admin do
get '/admin', to: 'devise/sessions#new'
end
post '/rate' => 'rater#create', :as => 'rate'
root to: 'hotels#list'
resources :hotels do
resources :comments
get 'list', on: :collection
post 'comment'
end
resources :ratings, only: :update
namespace :admin do
resources :hotels, :users
end
base_controller
class Admin::BaseController < ApplicationController
before_filter :authenticate_admin!
layout 'admin'
end
hootels_controller(in admin folder)
class Admin::HotelsController < Admin::BaseController
def index
#hotels = Hotel.all
end
def new
#hotel = Hotel.new
end
def create
#hotel = Hotel.new(hotel_params)
#hotel.user_id = current_admin.id
if #hotel.save
render :index
else
render :new
end
end
def update
#hotel = Hotel.find(params[:id])
#hotel.update_attributes(params[:hotel])
end
end
index(in /admin/hotels)
- #hotels.each do |hotel|
.ui.segment
.ui.three.column.grid
.column
.ui.large.image
=image_tag hotel.avatar_url
=link_to hotel_path(hotel), class:'blue ui corner label' do
%i.fullscreen.icon
.column
.ui.message
.header
=hotel.title
.wraper=hotel.description.truncate(300)
.column
=simple_form_for Hotel.find([hotel.id]),:method => :put do |f|
=f.hidden_field :status, value: 'approved'
=f.button :submit, 'Approved', class: 'secondary button'
%br
%hr
I don't know why, but I see this error,
Missing template hotels/update, application/update with...
I think out that in updating rails do not use the controller in the folder admin. Perhaps this is causing the error
Given you didn't implement what to be done, e.g. render, redirect, etc. rails fallbacks to the default, which is to render views with the name of the action, in this case, update.
You might want to take some action, depending on the outcome of update_attributes, for instance:
if #hotel.update_attributes(params[:hotel])
redirect_to(#hotel)
else
render(:edit)
end
You might also want to take a look at Responders to DRY your actions.

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