Creating custom view similar to index.html.erb - ruby-on-rails

I'm creating a custom view that is a slight modification of the index.html.erb. I'd like to be able to create a link on my web app that directs a user to this custom view called list.html.erb.
Here's what I've done:
1) Copied the default scaffold index view and renamed it to list.html.erb
2) Modified GalleriesController by copying the index method and renaming to list:
def list
#galleries = Gallery.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #galleries }
end
end
3) Modified routes.rb file like so:
match "galleries/list" => "galleries#list"
I keep getting the following error:
Couldn't find Gallery with ID=list
Rails.root: /Users/scervera/Sites/MDN
Application Trace | Framework Trace | Full Trace
app/controllers/galleries_controller.rb:28:in `show'
In my search on stackoverflow I was unable to find any similar questions.

I'm guessing you put the match outside of, and after, the gallery resources routing.
This means the list is being interpreted as the :id of the default RESTful mapping.
Options include:
Just using index unless you truly need them both (which seems weird).
Adding a list RESTful action as described here (see below).
Changing the order of your routing and/or using a constraint to avoid route overlap. IMO this is the most-fragile and least-preferable.
To add the list action (option 2):
resources :galleries do
  get 'list', :on => :collection
end

You should put your galleries/list route before all other gallery routes.
Order matters. In your case, route "galleries/:id" gets matched first and causes this error.
You can get exhaustive information about Rails routing here: Rails Routing from the Outside In.

Related

Rails create file and render

In rails, is there a way (in a controller) to:
create a file
render a view or template to that file
redirect_to or render another view
I've tried all kinds of constructions, but keep getting the same error: Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action.
Otherwise; is it possible to render a template or view to a file without displaying that template/view?
thnx!
code:
def get_report
# part 1: create and render file for use with phantomjs
File.new('./vendor/assets/javascripts/graph_disk1.json','w') {|f| f.write(render "reports/disk", :layout => false)}
system `phantomjs ./vendor/assets/javascripts/highcharts-convert.js -infile ./vendor/assets/javascripts/graph_disk1.json -outfile ./app/assets/images/chart01.png -options ./vendor/assets/javascripts/resources.json`
# part 2: create odf-report and use image created bij phantomjs/highcharts-convert
report = ODFReport::Report.new("#{Rails.root}/app/report_templates/PSC2_KalScanV0.odt") do |r|
r.add_image :graphd1, "#{Rails.root}/app/assets/images/chart01.png"
send_data report.generate, type: 'application/vnd.oasis.opendocument.text',
disposition: 'attachment',
filename: 'report.odt'
end
end
the 2 parts work each, but not when called liked this (in 1 action/controller).
The solution is always easy once you've found it:
Instead of: f.write(render "reports/disk", :layout => false),
Use: f.write(render_to_string "reports/disk", :layout => false)
and voila, no more error
it seems you tried to create custom routes with render different file other than rails way, let me give you sample case, for example you have client controller but then you want to create custom method and routes other than 7 standard rails way
rails generate controller clients
inside routes.rb
resources :clients do
collection {
get :check_data # this to get data
post :import_data # this to post data
}
}
# prease remove all other routes for client controller that usually generated with get
inside app/controllers/client_controller.rb create two method for route above
def check_data
...
# the default view file is /views/clients/check_data.html.erb
# but you may also type like this below to render other file
# please note the first thing you must mention controllers name then the file name
render "clients/noname.html.erb"
end
def import_data
...
#
# here after client saved, it goes to other path instead of default
if #client.save
redirect_to courses_path
end
end

Add RESTful Action

The source of my information is section 2.9 here:
[http://guides.rubyonrails.org/routing.html#connecting-urls-to-code][1]
What I'm trying to do is add a custom action "search" and corresponding view.
So, as it says to do in the documentation, I've added this code in my config/routes.rb file:
resources :dimensions do
collection do
get "search"
end
end
I've also defined in the dimensions_controller file:
def search
#dimensions = Dimension.all
respond_to do |format|
format.html # search.html.erb
format.json { render json: #dimensions }
end
end
I then stopped and restarted the rails server, but when I navigate to /dimensions/home, I'm still getting this error message:
Couldn't find Dimension with id=search
Also showing that my parameter is:
{"id"=>"search"}
So am I just missing another bit of code that gives the instruction to interpret /dimension/search as a collection action as opposed to the show action?
I've already confirmed that search_dimensions_path exists, so I know that the resource block in the routes.rb file is actually adding paths. It's just interpreting them as a separate search action that's giving me trouble.
Thanks for your time.
This code should work fine. Can you show us your routes.rb file?
On a side note, you probably don't want to have a separate action for searching, using the index action is the preferred way.
Found the issue:
I had to make the resource declaration in my config/routes.db file for dimensions after creating the collection action, like so:
resources :dimensions do
collection do
get "search"
end
end
resources :dimensions
Then everything worked as expected.

Rails beginner config/routes.rb issue

I am having a little trouble understanding what config/routes actually routes to. For example lets say I started a brand new project and generated a Users controller and edited my config/routes.rb to look like this:
config/routes.rb
SampleApp::Application.routes.draw do
match '/signup', to: 'users#new'
end
I start the server and as expected, it says I don't have a "new" action in my Users controller. I create one:
users_controller.rb
class UsersController < ApplicationController
def new
end
I refresh the page and as expected it tells me I need a users/new template. So my question is: do my view templates always have to be the same as the controller and action names in "(controller name)/(action name)" format (in this case users/new.html.erb)? Is it not possible to name my template something random (e.g. users/rubyonrailsmeetup.html.erb instead of users/new.html.erb) if have a controller action linked to one of the site's functions?
Also, does adding "resources :users" to config/routes.rb by default match the view template file names with the behavior I mentioned above so that views must be named after their "controller/action" names?
Sorry for the bother, I'm just trying to figure out what's part of Rails' magic and what isn't.
Rails attempts to render the template with the same name as the action by default, if no other render or redirect is invoked in the controller action. Basically, there's an implicit render :action at the end of every controller action.
But you can override this easily enough by adding an explicit render, e.g.,
render :rubyonrailsmeetup
Edit for clarity: this call to render goes in the controller code, not in config/routes
do my view templates always have to be the same as the controller and action names in "(controller name)/(action name)" format
These are defaults, you can render any view from the action by giving render :view_file_rel_path at the end of the action
does adding "resources :users" to config/routes.rb by default match the view template file names
Anything added in routes.rb is directly related upto controllers only, i.e. it matches the request and maps it to the controller/action. Logic of view comes only inside the action code

undefined method `medium_url'

I have a 'media' model, and the controller through which I am doing any changes to that table is called 'MultimediaController'.
When creating a new media and trying to redirect, I get the following error: undefined method 'medium_url' for #<MultimediaController:0x007f86f49ca400>
I dont have any tables, models, controllers or anything else called "medium". I'm assuming rails is doing this automatically based on my table named 'media'. Is there any way I can overwrite this?
edit: my redirect looks like this: respond_with(#media, {:controller => 'multimedia', :action => 'index', :id => session[:user_id], :collection => #media.collection_id})
"Media" is plural for "medium" so it sounds like you've got some renaming to do or customizations in inflections.rb.
Medium_url would mean rails is looking for a single "Medium" record (or whatever database you are dealing with .. media etc). You'd have to pass in an id in order to redirect to the Medium object when using medium_url.
If you want to redirect to a list of all Mediums (Your index action) you should be using mediums_url.
Rails has a fantastic guide on restful routing.
http://guides.rubyonrails.org/routing.html
def create
# Code to create the record goes here blah blah.
# Redirect to a list of all mediums
redirect_to mediums_url
# Or redirect to the medium object we just created
# redirect_to medium_url(#object)
end

Is it a bad idea to reload routes dynamically in Rails?

I have an application I'm writing where I'm allowing the administrators to add aliases for pages, categories, etc, and I would like to use a different controller/action depending on the alias (without redirecting, and I've found that render doesn't actually call the method. I just renders the template). I have tried a catch all route, but I'm not crazy about causing and catching a DoubleRender exception that gets thrown everytime.
The solution for this I've come up with is dynamically generated routes when the server is started, and using callbacks from the Alias model to reload routes when an alias is created/updated/destroyed.
Here is the code from my routes.rb:
Alias.find(:all).each do |alias_to_add|
map.connect alias_to_add.name,
:controller => alias_to_add.page_type.controller,
:action => alias_to_add.page_type.action,
:navigation_node_id => alias_to_add.navigation_node.id
end
I am using callbacks in my Alias model as follows:
after_save :rebuild_routes
after_destroy :rebuild_routes
def rebuild_routes
ActionController::Routing::Routes.reload!
end
Is this against Rails best practices? Is there a better solution?
Ben,
I find the method you're already using to be the best. Using Rails 3, you'd have to change the code a bit, to:
MyNewApplication::Application.reload_routes!
That's all.
Quick Solution
Have a catch-all route at the bottom of routes.rb. Implement any alias lookup logic you want in the action that route routes you to.
In my implementation, I have a table which maps defined URLs to a controller, action, and parameter hash. I just pluck them out of the database, then call the appropriate action and then try to render the default template for the action. If the action already rendered something, that throws a DoubleRenderError, which I catch and ignore.
You can extend this technique to be as complicated as you want, although as it gets more complicated it makes more sense to implement it by tweaking either your routes or the Rails default routing logic rather than by essentially reimplementing all the routing logic yourself.
If you don't find an alias, you can throw the 404 or 500 error as you deem appropriate.
Stuff to keep in mind:
Caching: Not knowing your URLs a priori can make page caching an absolute bear. Remember, it caches based on the URI supplied, NOT on the url_for (:action_you_actually_executed). This means that if you alias
/foo_action/bar_method
to
/some-wonderful-alias
you'll get some-wonderful-alias.html living in your cache directory. And when you try to sweep foo's bar, you won't sweep that file unless you specify it explicitly.
Fault Tolerance: Check to make sure someone doesn't accidentally alias over an existing route. You can do this trivially by forcing all aliases into a "directory" which is known to not otherwise be routable (in which case, the alias being textually unique is enough to make sure they never collide), but that isn't a maximally desirable solution for a few of the applications I can think of of this.
First, as other have suggested, create a catch-all route at the bottom of routes.rb:
map.connect ':name', :controller => 'aliases', :action => 'show'
Then, in AliasesController, you can use render_component to render the aliased action:
class AliasesController < ApplicationController
def show
if alias = Alias.find_by_name(params[:name])
render_component(:controller => alias.page_type.controller,
:action => alias.page_type.action,
:navigation_node_id => alias.navigation_node.id)
else
render :file => "#{RAILS_ROOT}/public/404.html", :status => :not_found
end
end
end
I'm not sure I fully understand the question, but you could use method_missing in your controllers and then lookup the alias, maybe like this:
class MyController
def method_missing(sym, *args)
aliased = Alias.find_by_action_name(sym)
# sanity check here in case no alias
self.send( aliased.real_action_name )
# sanity check here in case the real action calls a different render explicitly
render :action => aliased.real_action_name
end
def normal_action
#thing = Things.find(params[:id])
end
end
If you wanted to optimize that, you could put a define_method in the method_missing, so it would only be 'missing' on the first invocation, and would be a normal method from then on.

Resources