Bypassing Controller in Rails - ruby-on-rails

Im using Rails 2.3.5 and I have a model, let's call it Post. I used named scopes to apply different kinds of sorts on Post. For example, in the Post model I have possibility to rank posts by its scores:
named_scope :order_by_acception_rate_desc,
Proc.new { |limit| { :limit => limit, :order => "acception_rate desc" } }
In the Post Controller I have:
def best
#best_posts = Post.order_by_acception_rate_desc(10)
end
In the view I just render this collection #best_posts:
<%= render :partial => "post", :collection => #best_posts
Currently my application is working like that, but actually I do not need to have the method "best" in the Controller, and I could move it to the Model Post doing like:
def self.best
self.order_by_acception_rate_desc(10)
end
and then in the view I would render the collection like:
<%= render :partial => "post", :collection => Post.best
I do not know which approach is better, but using the ranking methods in the Model, I could avoid to create routes for each one of ranking methods. What approach is better, is there any better approach than these?

with according to Rails conventions the logic should be separated,
controllers handle permissions, auth/authorization, assign instance/class variables
helpers handle html logic what to show/hide to user
views should not provide any logic, permissions check. think about it from designer's point of view
models handle data collection/manipulation over ORM
I'd like to ask you to try:
#helper
def self.best(limit)
all(:limit => limit, :order => "acception_rate desc")
end
#controller
#best_posts = Post.best
#view
<%= render :partial => "post", :collection => #best_posts %>

You should not bypass the controller and include much logic in your view.
You could keep a single route and filter the Post model depending on one of the params.
You don't tell enough here to answer more clearly but you have the big picture.

You can leave just the view file and it should work.

Related

Manage Single Rails form to other controller with create and update action

i'm new of rails. I use rails 3.0.5 .
I have an EMPLOYEE resource, but I would like to manage it with another extern controller (emp_profile_controller).
This extern controller (emp_profile_controller) manages some actions (index, new_employee, create_employee, edit_employee, update_employee ecc.. ) .
My routes for this controller are :
controller :emp_profile do
get 'emp_profile' => :index
get 'emp_edit_profile' => :edit_employee
put 'emp_edit_profile' => :update_employee
get 'new_employee' => :new_employee
post 'new_employee' => :create_employee
get 'emp_list' => :emp_list
end
How can i use one form to handle both Create and Update actions in this controller ?
I tried with :
form_for(#employee, :url => { :controller => "emp_profile"}) do |f|
but it doesn't work.
If i manage only one action at time (create OR update), url_for works, for example :
form_for(#employee, :url => { :controller => "emp_profile", :action => "update_employee" }
but how can i handle both actions with one form ?
Thanks for your availability and I apologize if I asked a stupid question.
EDIT
For now, i solved checking if object exist in the form file, if exist i set a variable with the UPDATE action path, else, i set a variable with the CREATE action path. So in the form_for statement i use url_for with the above variable.
<% if #employee.new_record?
action = "create_employee"
method = "post"
else
action = "update_employee"
method = "put"
end
%>
form_for(#employee, :url => { :controller => "emp_profile", :action => action }, :method => method
I don't think it is the best way but it works and i can use only one form file.
As your model name and controller name are different, you can add this line to your routes
resources :employees,:controller=>"emp_profile",:path=>"emp_profile"
Change the method names of create_employee,update_employee to create and update respectively.
And change your form_for as given below
<%= form_for #employee do |f| %>
....
<% end %>
First of all, if you want to update something, this object should exist.
How do plan to find it out, I don't know (cause there different ways, depends on background).
There are 2 ways of solving this issue.
You can just check if object exist in view file, and if exists, renfer form for update, else for create.
Other way is to do it in controller.
For example:
def create
#employee=Employee.find_by_name('Jack Black') #for example
if #employee!=nil
render :action=> 'update'
else
#employee=Employee.new(:employee)
#employee.save
end
as i understand you want to execute two different actions on the same controller using a form submitting, this is not possible, you can only execute one action using a form submitting,
because the form is reaching to an action controller that action is suppose to render some view at the end of it's execution code, if it was possible to use to actions on form submitting how rails will know which view to render??? (that's why it's not possible).
if you want to do some more code execution at the controller, the right way to it is to call a method with some code in it that you want to execute, that method should be in the model,
because it is a good practice to write all massive chunks of code in the model and leave the controller as light from code as possible :-)
hope this helps.

Rails 2 Render ERb template in controller?

This code in the controller
av = ActionView::Base.new(Rails::Configuration.new.view_path)
av.extend ApplicationHelper
content = av.render(:file => "show", :locals => { :user => #user })
and the show.html.erb have the link_to helper , operation code error
undefined method `url_for' for nil:NilClass
I add av.extend ActionController::UrlWriter in the controller , still error
undefined method `default_url_options' for ActionView::Base:Class
Try:
content = render_to_string(:file => "show", :locals => { :user => #user })
Usually, in Rails, when something is very hard, it is because you are not approaching the problem from an ideal angle. I can't really answer this question directly, other than to advise not to do this. Ideally, view logic should be in the view, and not the controller. With a few rare exceptions, like using a link_to helper in a flash message (which can be easily solved), these concerns should be separated. This does not seem like one of those exceptions. Instead, I would recommend one of the following (slightly more Rails-y) techniques:
Option 1:
It looks like you are trying to render the view for the show action. This can easily be accomplished by using render :action => 'show' (docs). This will not run the code for the action, just use that view.
Option 2
In the event that option 1 is not viable in your situation, you may alternatively consider some variation of the following technique. Render the default view, as normal. Move the existing content of the view into a partial, and your new content into a partial of its own. Then in your view, simply toggle the partial to render based off of an appropriate condition - the existence of #user, for this example: render :partial => #user ? 'new_content' : 'existing_content'. Depending on your application structure, it may be that this can be further simplified by just rendering the same partial from your show view and the view for the action referenced in the question.
I think keeping the various elements of an application isolated into their intended concerns not only makes this easier to develop and maintain by following the principle of least astonishment, but also usually makes the application much easier to test, as well. Sorry for not answering your question - hope this helps, anyway.
I suppose it was called outside controller so I do it this way in Rails 3:
av = ActionView::Base.new(Rails.configuration.paths["app/views"])
av.class_eval do
include ApplicationHelper
include Rails.application.routes.url_helpers
default_url_options[:host] = 'yoursite.com'
def protect_against_forgery?
false
end
end
#result = av.render(:file => "show", :locals => { :user => #user })

how to pass instance variable to another view in rails

Hey. I think I am in a mind trap here. I am using Rails 2. In the index view of my controller I set up something like
def index
#posts = Post.all
end
so that I can use #posts in my index, e.g. each-do. Id like to pass #posts to a custom made view, in where I can use the same variable again. This I want to do over a link from the index view. Something like that:
link_to "newpage", {:controller => 'posts', :action => 'newmethod', :param => #posts}
What I have created so far is a new method in my Post controller. A new view. And and a new route to that site. Any suggestions? thx for your time
You're going to have to collapse those values into something that will fit in a URL, then decode them later. For instance:
# Put this in your helper method module PostsHelper
def post_ids
#posts.collect(&:id).join(',')
end
Your adjusted link would be:
link_to "newpage", {:controller => 'posts', :action => 'newmethod', :param => post_ids }
When you fetch the next page you'll need to decode these by retrieving them again:
#posts = Posts.find(params[:param].split(/,/))
There's no way to pass an instance variable between requests because they are explicitly cleared out.
As a note, try and use the generated route methods instead of the hash-style declaration. You would probably have a route already listed in rake routes:
# Instead of { :controller => 'posts', :action => 'new', :param => post_ids }
new_post_path(:param => post_ids)
These generated methods are much more readable in practice and have the advantage of being configurable later if you want to re-interpret what they mean by adjusting your routing table.
Another note is that if the list of IDs gets very large, you may not be able to encode them into a URL as the limit is about 1500 bytes. You may instead have to serialize the conditions used to generate the list in the first place and then re-run those again later. So long as you're dealing with tens of items and not hundreds you should be okay, though.
In your controller
def newmethod
#posts = Post.all
end
You can't pass all your models in a link ! The #posts var in the index action disappears after the request
I know that store arbitrary data in session is not a best practice, but in some cases this approach is simple and easy.
In your controller:
def balabala
#...
session[:your_var] = "this is the var used in another view&action!"
# ...
end
In any other pages:
<%= session[:your_var] %>
That's it. ugly, not MVC at all. :) Only recommended for very rare cases. :)

Rails Routing with Query String

I have a problem where I need values passed in from a GET request and I don't know how to set up the routing definition.
My Category object has a type(string),a color(string) and many products. I want to create a simple web service that lets the caller get all of a Category's products by passing in the Category's type and color:
http://www.myapp.com/getProducts?catType=toy&color=red
or ?
http://www.myapp.com/categories/getProducts?catType=toy&color=red
How do I define the correct routing for this situation? Are there better ways to do this in a Restful manner... because I know that Rails is Restful, so if there is a way to do it "correctly" then that would be even better.
Thanks
Your first example:
map.getproduct '/getProduct', :controller => 'your_controller', :action => 'your_action'
In controller you will have catType and color in params hash:
params[:catType]
=> 'toy'
params[:color]
=> 'red'
Is there better way? Probably yes, but it depends on your needs. If you will always have catType and color parameters, than you can add route like this:
map.getproduct '/getProduct/:catType/:color', :controller => 'your_controller', :action => 'your_action'
You will have access to those parameters with params hash like in previous example. And your urls will look like this:
www.myapp.com/getProduct/toy/red
If your parameters may change, you can use route globbing:
map.getproduct '/getProduct/*query', :controller => 'your_controller', :action => 'your_action'
Then it will catch all request that has www.my.app.com/getProduct/... at the begining. But you will have more work in controller. You will have access to query with this:
params[:query]
and for www.myapp.com/getProduct/color/red/catType/toy it will give:
params[:query]
=> ['color', 'red', 'catType', 'toy]
So you have to parse it manualy.
One RESTful way to to do this would involve a product resource nested beneath a category resource, like so:
http://www.myapp.com/categories/toy/products?color=red
Your routes.rb would need to contain:
map.resources :categories do |category|
category.resources :products
end
Since my url above using the Category's type attribute for routing, I'm implying that each type is unique, like an id. It'll mean that whenever you're loading a category in the Categories controller (or anywhere else) you'll need to load the category with Category.find_by_type(params[:id]) instead of Category.find(params[:id]). I like routing categories this way whenever possible.
Your ProductsController controller index action would find products using lines like:
#category = Category.find_by_type(params[:category_id])
#products = #category.products.find(:all, :conditions => { :color => params[:color]} )
Remember, your Category model must contain the line:
has_many :products
It's probable a good idea to enforce that in the model with validations:
validates_presence_of :type
validates_uniqueness_of :type
To make routing work you should also overwrite the to_param method in the Category model to return type instead of id:
def to_param
self.type
end

DRYing up routes and javascript responses

I'm not sure if I'm missing a known design pattern, but I keep coming up against the following problem with RESTful routes Rails.
In my example, I have a users controller that can respond in javascript (:js) format. The default response populates a page element with a list of the paginated users:
# /app/controllers/users_controller.rb
class UsersController < ActionController
def index
#users = User.paginate(:all, :page => params[:page], :conditions => ['name ILIKE ?', params[:name])
respond_to do |format|
format.html
format.js
end
end
end
The corresponding RJS template would look like:
# /app/views/users/index.js.rjs
page.replace_html :users, :partial => 'users'
This works fine, allowing me to perform AJAX lookups on users. However, in another part of my site (say the user editing form) I would like to perform an AJAX lookup of users, but update a set of ''select'' options or perform an inline autocomplete, rather than update the #users page element, e.g.
# /app/views/users/edit.html.erb
<%= f.text_field :name %>
$('#user_name').autocomplete({url: '/users', data: 'name=value', ...})
My question is what would be the best DRY way to achieve this? I don't think I should need to create a new controller action to correspond to the different view, as this would involve repeating the finder code. The only solution I've come across so far is to build some javascript conditions into my RJS helper:
# /app/views/users/index.js.rjs
page << "if($('#users').length > 0)"
page.replace_html :users, :partial => 'users'
page << "else"
page.replace_html :user_options, :partial => 'user_options_for_select'
This feels very brittle, and unclean for Rails. Am I missing something in how I can respond with different views depending on the calling controller?
Appreciate any help!
Chris
In one of them you have a list of users and the other one a list of options.
So even though for now, your two pages are having the same feature, they're independent from each other and you might want to change things for only one of them in the future.
So I'd keep them distinct with two different javascript actions . It'll will allow you to much more easily make them evoluate on their different path.
Anyway as you can see, they're already quite different. You have two different partials and two different html tags id.
Trying to have the same code for them both here seems quite confusing to me.
So yes I'd create two actions, one for the users list and one for their options.

Resources