Refactor a method to find records by status - ruby-on-rails

I currently use .find_by_status(params[:status]) on my #tasks to find tasks that aren't closed or a sticky. (either 4,5).
def self.find_by_status(status)
status = status.to_i
if status == 0 then
status = 1
else
status = status
end
if status == 1 || !status then
Task.where(["STATUS NOT IN (4,5)"])
else
Task.where(:status => status)
end
end
I also copied this for my tickets model too, to find only open tickets on the home page.
It's also accompanied by this
<% status_active = 1 %>
<% Task.new.statuses.each do |status| %>
<li class="<%= if (params[:status].to_i || status_active) == status[0] then "active" end %>">
<%= link_to status[1], :controller => params[:controller], :action => params[:action], :params => { :status => status[0] } %>
</li>
I'm new to rails and I'm really struggling on refactoring this. I would probably prefer making those links into a drop down select filter, but that also I struggle with.
Any help is appreciated!

This snippet:
def self.find_by_status(status)
if status.to_i.zero?
Task.where(["STATUS NOT IN (4,5)"])
else
Task.where(:status => status.to_i)
end
end
Is identical to your above code (There is no way that status can be false after you have cast it to_i).
You can clean the view code down to:
<% Task.new.statuses.each do |status| %>
<li class="<%= 'active' if (params[:status].to_i || Task::STATUS_ACTIVE) == status[0] %>">
<%= link_to status[1], :controller => params[:controller], :action => params[:action], :params => { :status => status[0] } %>
</li>
<% end %>
I suggest adding STATUS_ACTIVE as a constant in your model instead of coding it into the view.
Also, the fact that you are specifying controllers and actions via param is strange but without knowing more about your use-case I cannot troubleshoot that.

Related

Determine if Rails Resource Controller has an Action Enabled

I have a Rails 3 app and am writing an "administrative" partial I'd like to include in views across many different models. The partial will include resourceful links (e.g. new, index, show), but my design fails if the resource has a route (e.g. index) disabled in routes.rb.
A sample view (views/my_resources/show.html.erb):
<% provide(:title, 'My Resource') %>
<div style="float:right;">
<%= render '/shared/resource_menu' %>
</div>
<h1>My Resource</h1>
<%= #my_resource.name %>
Excerpt from /shared/_resource_menu.html.erb:
<div class="submenu">
<b><%= File.basename(params[:controller]).pluralize.titleize %></b><br />
<% if params[:action] != 'index' %>
<%= link_to 'Index', url_for(:controller => params[:controller], :action => 'index', :only_path => true) %><br />
<% end %>
<% if params[:action] != 'new' %>
<%= link_to 'New', url_for(:controller => params[:controller], :action => 'new', :only_path => true) %><br />
<% end %>
</div>
Currently, I've hacked it so that each conditional looks like:
<% if params[:action] != 'index' && !#omit_index_in_resource_menu %>
and I set the #omit_index_in_resource_menu by hand in the relevant controllers, but that's clunkier than I would prefer.
Can I instead do something more elegant like:
<% if params[:action] != 'index' && controller.actions.include?('index') %>
Maybe this would help you:
UsersController.action_methods (it's a class method)
or
UsersController.new.available_action? :index (it's an instance method)
More info: http://api.rubyonrails.org/classes/AbstractController/Base.html#method-i-action_methods

AJAX Form Issues with Ruby on Rails

I'm sure this question has been asked before in a different context but I'm still so stuck with figuring out AJAX Rails and, I guess, Rails in general (kinda makes me wonder if I should just go back to PHP...). Well anyways I have this form that I want to AJAXify.
This is the "list" view which is part of the "subject" controller
<h1>Listing Subjects</h1>
<ul id="subject_list">
<% #subjects.each do |c| %>
<li><%= link_to c.name, :action => 'show', :id => c.id %></li>
<% end %>
</ul>
<p id="add_link"><%= link_to_function("Add a Subject",
"Element.remove('add_link'); Element.show('add_subject')")%></p>
<div id="add_subject" style="display:none;">
<%= form_tag(:action => 'create') do%>
Name: <%= text_field "subject", "name" %>
<%= submit_tag 'Add' %>
<% end %>
</div>
Code for my "subject" controller
class SubjectController < ApplicationController
def list
#subjects = User.find(:all)
end
def show
#subject = User.find(params[:id])
end
def create
#subject = User.new(params[:subject])
if #subject.save
render :partial => 'subject', :object => #subject
end
end
end
My "subject" partial
<li id="subject_<%= subject.id %>">
<%= link_to subject.name, :action => 'show', :id => subject.id %>
</li>
And the User is just a simple model I made that contains two columns "name" and "email".
How this code currently works is that when you click "Add", the textfield input is revealed. When you type something in the input and submit it, the "_show" partial is rendered in the create link. I was following a Rails 2.0 tutorial but I have 3.0 and I've read through some tutorials and they all mention ":remote => true" and jquery_ujs.js but I have no idea how to apply them to a "form_tag" rather than "form_for" Rails helper.
Basically I want to asynchronously add the element to the bottom of the list without a page load. I've really tried to understand absolutely all of the tutorials I could find but I just can't figure it out.
I believe that you'd better use some Unobtrusive JavaScript to tell to your app
and browser what exactly you want to render and how.
You want too much from simple render :partial => 'subject', :object => #subject line of code.
Here's my snippet that may be helpful to you.
# in the view (:remote => true for form_tag is not problem at all)
<%= form_tag({:controller => :products, :action => :empty_cart }, {:id => 'empty_cart', :remote => true}) do %>
<%= submit_tag 'Clear' %>
<% end %>
# in the controller (note that format.js section in the respond_to block)
def empty_cart
...
respond_to do |format|
format.html { redirect_to :root, :notice => 'Your cart is empty now' } # in the case of disabled JS support
format.js { render :js => "$('#empty_cart').fadeOut()" } # or you can place js code in the empty_cart.js.erb file and specify format.js here without the block
end
end
Check this article if I'm not clear enough.

Rails way to make a horizontal menu

I want to ask you about best rails way to create a menu.
I should to make a horizontal menu. Every page belongs to the specific item of this menu and this item should has id="current" when user opens on this page.
<ul>
<li id="current">Home</li>
<li>About us</li>
</ul>
As I understand I should to create a special helper which will create this html markup and use this helper with special parameter on every view of every page. Maybe there is a better rails way to do this?
PS. Update
My solution:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :open_most_important
protected
def open_most_important
#menu = {
:cases => { :name => 'Cases', :link => '/cases'},
:groups => { :name => 'Groups', :link => '/groups' },
:projects => { :name => 'Projects', :link => '/projects' },
:settings => { :name => 'Settings', :link => '/settings' },
:about => { :name => 'About us', :link => '/about' }}
#current_menu_item = :cases
end
The fragment of the layout application.html.erb:
<div id="menu">
<ul>
<% #menu.each do |item, value| -%>
<% if #current_menu_item == item -%>
<li id="current"><%= value[:name] %></li>
<% else -%>
<li><%= value[:name] %></li>
<% end -%>
<% end -%>
</ul>
</div>
After I need to set #current_menu_item for every controller in the before_filter
If I understand correctly you want this menu to appear on every page. If that is the case you can just add it to your layout. If you don't understand layouts read up on them at Ruby on Rails Guides http://guides.rubyonrails.org/getting_started.html#customizing-the-layout
This is how I tend to do it:
<%= menu_item("Home", root_path) {
controller.controller_name == "pages" &&
controller.action_name = "home" } %>
<%= menu_item("About us", page_path("about") {
controller.controller_name == "pages" &&
controller.action_name == "about" } %>
The helper:
def menu_item(name, url, options = {})
if yield
options["id"] = "current"
end
link_to name, url, options
end
The "id" will be "current" if the block passed to menu_item returns true.
The way I handle it is to put this in a navigation partial:
<%= nav_link "All Users", users_path, active_tabs.include?('all_users') %>
<%= nav_link "Add User", new_user_path, active_tabs.include?('add_user') %>
And then this helper at the top of each view:
<%= nav 'nav_partials/users_tabs', 'all_users' %> # one view
<%= nav 'nav_partials/users_tabs', 'add_user' %> # another view
And the helpers:
def nav_link(title, path, active, options = {})
if active
options[:class] ? options[:class] += " active" : options[:class] = "active"
end
link_to(title, path, options)
end
def nav(partial, *active_tabs)
partial = "#{params[:controller]}/nav_partials/#{partial}" unless partial.chars.first == '/'
render :partial => partial, :locals => { :active_tabs => active_tabs }
end
To me, it is elegant, even though there are some things that could be a bit better (like the file path thing in the nav helper, as well as referencing params in the helper). But it works very nicely, and it's very flexible.

Does this rails code belong in Application Controller?

I have some code (that is working), but I just want to make sure I am putting it in the right place per rails conventions/best practices. The purpose of the code, is to generate a set of subheading navigation links if '#facility' is defined (which could be done in one of several controllers). Right now, I have the following bits of code spread out as identified below.
My issue: this doesn't feel right, particularly the bit of logic at the beginning of the html template. I will also want to define a few other similar helpers to define these subhead links based on different models. Should this logic (of setting #subhead_links) be in the application controller? As a method in each model (so I would set #subhead_links = #facility.subhead_links)?
I've looked some for answers, but this type of philosophical question isn't as readily google-able as an error code.
in application helper
# build subhead
def subhead_links(facility)
links = [
{ :label => "Configure Facility", :path => facility_path(facility) },
{ :label => "Waitlists", :path => waitlist_tiers_path(:id => facility.id) },
{ :label => "Applications", :path => sponsors_path(:id => facility.id) }
return links
end
in a partial included in application.html.erb template
<% if #facility %>
<% #subhead_links = subhead_links(#facility) %>
<% end %>
<% if #subhead_links %>
<nav class="subnav">
<ul>
<% #subhead_links.each do |link| %>
<li><%= link_to link[:label], link[:path] %></li>
<% end %>
</ul>
</nav>
<% end %>
in various other controllers...
#facility = Facility.find(params[:id])
I never rely on instance variables in partials and try to put as much logic as possible in helpers.
Here this would result in the following:
application_layout:
<%= render_subhead_links(#facility) %>
application_helper:
def render_subhead_links(facility)
if facility
links = [
{ :label => "Configure Facility", :path => facility_path(facility) },
{ :label => "Waitlists", :path => waitlist_tiers_path(:id => facility.id) },
{ :label => "Applications", :path => sponsors_path(:id => facility.id) }
render :partial_name, :links => links
end
end
partial:
<nav class="subnav">
<ul>
<% links.each do |link| %>
<li><%= link_to link[:label], link[:path] %></li>
<% end %>
</ul>
</nav>

Why do I get 'no route matches', for a route that exists?

This is the error:
No route matches {:action=>"send_to_client", :controller=>"stages"}
It corresponds to this line:
<%= link_to "<span class='icon send-to-client-icon' title='Send to Client' id='send-to-client'> </span>".html_safe, send_to_client_stage_path(#stage), :id => stage.id, :confirm => "This will send #{stage.name.capitalize} to #{stage.client.email}. Are you sure you are ready?" %>
In this _show_table.html.erb
<%
if #upload != nil
stage = #upload.stage
end
%>
<h1 class="panel-header">Images</h1>
<% if stage == nil %>
<div class="images_menu">
<%= link_to "<span class='icon send-to-client-icon' title='Send to Client' id='send-to-client'> </span>".html_safe, send_to_client_stage_path(#stage), :id => stage.id, :confirm => "This will send #{stage.name.capitalize} to #{stage.client.email}. Are you sure you are ready?" %>
<span class="icon compare-icon" data-url="<%= compare_stage_path(stage)%>" title="Compare Images" id="compare-images"> </span>
</div>
<% end %>
Here is my routes.rb:
resources :stages do
member do
get :step
get :compare
get :send_to_client
end
end
The issue is that this partial _show_table.html.erb is in the view folder of my uploads model...not the stages model.
When I execute that link_to in the stages model, it works fine. Once I take it out into the uploads model it throws that error.
Why would that be the case ?
Edit1: Here is the send_to_client action of the stages controller:
def send_to_client
stage = Stage.find(params[:id])
ClientMailer.send_stage(stage).deliver
if ClientMailer.send_stage(stage).deliver
flash[:notice] = "Successfully sent to client."
redirect_to("/")
else
flash[:notice] = "There were problems, please try re-sending."
redirect_to("/")
end
end
Rails will raise an ActionController::RoutingError if you use send_to_client_stage_path(nil).
You're mixing up #stage and stage. If you don't define #stage in the controller action, it will be nil and the error raises. In this case, just use #upload.stage.
Like:
<% if #upload.stage %>
<%= link_to "...", send_to_client_stage_path(#upload.stage), :confirm => "..." %>
<% end %>
If you want to use #stage, just define it in the action with #stage = #upload.stage and use it instead of #upload.stage:
<% if #stage %>
<%= link_to "...", send_to_client_stage_path(#stage), :confirm => "..." %>
<% end %>
It should probably be
send_to_client_stage_path(stage)
instead of
send_to_client_stage_path(#stage)
And it should be "unless", not "if" here, right?
<% unless stage.nil? %>
Also, dont forget you can use "unless", it's nicer sometimes
if #upload != nil
stage = #upload.stage
end
->
stage = #upload.stage unless #upload.nil?

Resources