Accessing non-current state_machine states - ruby-on-rails-3

I've got six states on my order.rb file. I want to access each of the non-current states to populate a drop-down menu so the state can readily be changed. I've come up with this. The function doesn't work, obviously, and the states are written_like_this.
<% order.state_paths.to_states.each do |state| %>
<%= link_to(state.to_s, order.adjust(state)) %>
<% end %>
I'd like to write a catch-all method too that interprets the clicks from the above menu and transitions the record to the selected state. Something like (pseudo-code):
def adjust(state)
#order = Order.find(params[:id])
#order.state = state
end
Any thoughts would be great. Cheers!

I've figured it out.
def adjust
session[:return_to] ||= request.referer
#doc = Doc.find(params[:id])
state = params[:state]
respond_to do |format|
#doc.update_attribute(:state, state)
#doc.create_activity state.to_sym, owner: current_user
format.html { redirect_to root_url, notice: "#{#doc.title} has now been #{state}." }
format.json { render json: #doc }
end
end
And I've made the buttons like this:
<% activity.trackable.state_paths.events.each do |s| %>
<%= link_to(s.to_s.titlecase, adjust_path(s, activity.trackable)) %>
<% end %>
Boom.

Related

What are some possible reasons that you would get an undefined method 'TheModelName_path' error using a form_for in rails 3?

I can't figure out why I have this error from my knowledge everything is set up as it should to work.
this is the error i get:
NoMethodError at /short/new
undefined method `shorts_path' for #<#:0x007fbc441426e0>
my model is short not shortS
when I run rake routes there is no shorts_path so I'm not sure where this helper is coming from. I don't understand why the form_for is giving me this error when #short is defined in the def new section of my controller.
Can someone please explain this to me?
Thank you in advance
This is what my controller looks like
class ShortController < ApplicationController
def show
#short = Short.find(params[:id].to_i(36))
respond_to do |format|
#redirect directly to the url stored as long in the database
format.html { redirect_to #short.long}
end
end
def new
#short = Short.new
respond_to do |format|
format.html # new.html.erb
end
end
def create
#short = Short.new(params[:short])
respond_to do |format|
if #short.save
format.html { render action: "show" }
else
format.html { render action: "new" }
end
end
end
end
routes
Trainingproject::Application.routes.draw do
root :to => 'short#welcome'
resources :short, :only => [:new, :create, :show]
end
form partial which is rendered in the view for new
<%= form_for(#short) do |f| %>
<% if #short.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#short.errors.count,"error") %> prohibited this url from being saved:</h2>
<ul>
<% #short.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="form-field">
<%= f.label "Enter your URL" %><br />
<%= f.text_field :long %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You're getting this error because form_for(#short), where #short is a new Short record, will attempt to use the path helper called shorts_path to route to where the form needs to go. You're missing this route definition in your config/routes.rb file:
resources :shorts
Please read the Getting Started and Routing guides, which should explain the things that you're missing.

Rails 3 JQuery Pagination on a nested model

I have watched the railscasts on Pagination with AJAX. I am trying to duplicate his method using a nested model. I seem to be confused because i don't have a view for the child model or a fully integrated controller.
Assume the Product has_many Comments. How can I do the same pagination using will_paginate on comments within a product view?
UPDATE:
Here is most of my attempt (my attempt actually uses kaminari but it's basically the same as will_pagenate):
view/products/comments/_index.html.erb:
<div id="comments">
<% comments.each do |comment| %>
<%= f.fields_for :comments, comments do |comment_builder| %>
<%= render 'products/comments/field', f: comment_builder %>
<% end %>
<% end %>
<%= paginate comments %>
</div>
view/products/comments/_field.html.erb:
<fieldset>
<%= f.hidden_field :_id, :value => f.object._id %>
<%= f.hidden_field :_destroy %>
<%= f.object.text %>
<%= link_to "remove", '#', class: "remove_field" %>
</fieldset>
view/products/comments/_index.js.erb:
$("#comments").html("j render("products/comments/index")) %>");
assets/javascripts/product.js
$('.pagination a').live
click: ->
$(".pagination").html("Page is loading...")
$.getScript(this.href)
false
controllers/comments_controller.rb
class CommentsController < ApplicationController
before_filter :get_product
def index
#comments = #product.comments.desc(:created_at).page(params[:page]).per(10)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #comments }
end
end
private
def get_product
#product = Product.find(params[:product_id]) if params[:product_id]
redirect_to root_path unless defined?(#product)
end
end
views/products/show.html.erb (an excerpt)
<%= render "products/comments/index", f: f, comments: #comments%>
controllers/products_controller.rb
def show
#product = Product.find(params[:id])
#comments = #product.comments.desc(:created_at).page(params[:page]).per(1)
respond_to do |format|
format.html # show.html.erb
format.json { render json: #product }
end
end
view/products/comments/_index.js.erb never gets called. Also is there a more rails-like way of doing this?

Where do I add code that would manipulate the text of a blog post in Rails?

Here is the code that works for me in views/posts/show.html.erb file:
<% require 'rmmseg' %>
<% RMMSeg::Dictionary.load_dictionaries %>
<% text = "你好" %>
<% algor = RMMSeg::Algorithm.new(#post.content) %>
<% loop do %>
<% tok = algor.next_token %>
<% break if tok.nil? %>
<% text2 = tok.text.force_encoding('UTF-8') %>
<%= "#{text2}" %>
<% end %>
I am new to rails so I need help knowing where to put this code, or similar, in the framework so that it saves the post with the spaces in the database. Should it be in the controller? If so, how would I do it?
I was trying and failing with this:
def create
#post = Post.new(params[:post])
require 'rmmseg'
RMMSeg::Dictionary.load_dictionaries
algor = RMMSeg::Algorithm.new(#post.content)
loop do
tok = algor.next_token
break if tok.nil?
text2 = tok.text.force_encoding('UTF-8')
#post.content = "#{text2}"
end
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render json: #post, status: :created, location: #post }
else
format.html { render action: "new" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
I obviously have know idea what I am doing yet.
I called the Rails Hotline and they gave me the hint to put it in the model using a callback (before_save, after_save, etc.)
That worked for me.

Rails Routing Redirects to wrong path

I have a view called, "clients" which shows a list of calls from the Call database. That works fine. However when I added a new button with a form behind it the call will be created but it redirects to calls_path instead of clients_path.
I have no idea why it's doing this, my only theory is that I'm working with actions that touch data outside of the clients_controller and somehow Rails is defaulting to the calls_path. The same thing happens on my delete action. Can someone help me make sense of this?
calls_controller
def new
#call = Call.new :call_status => "open"
respond_with #call
end
def create
#call = Call.new(params[:call])
if #call.save
redirect_to clients_path, notice: "Call was successfully created."
else
render :new
end
end
def destroy
#call = Call.find(params[:id])
#call.destroy
respond_to do |format|
format.html { redirect_to clients_index_path }
format.json { head :no_content }
end
end
_form.html.erb
<%= form_for(#call) do |f| %>
<%= f.label :caller_name %>
<%= f.text_field :caller_name %>
<%= f.label :caller_phone %>
<%= f.text_field :caller_phone, :placeholder => 'xxx-xxx-xxxx' %>
<%= f.label :caller_email %>
<%= f.text_field :caller_email %>
<%= f.button :submit %>
<% end %>
routes.rb
devise_for :users
match 'mdt' => 'mdt#index'
get "home/index"
resources :medics
resources :clients
resources :users
resources :units
resources :mdt do
collection do
put "in_service"
put "en_route"
put "to_hospital"
put "at_hospital"
put "on_scene"
put "out_of_service"
put "at_station"
put "staging"
put "at_post"
put "man_down"
end
end
resources :calls do
member do
post 'close'
end
end
root :to => 'home#index'
devise_scope :user do
get "/login" => "devise/sessions#new"
delete "/logout" => "devise/sessions#destroy"
end
The problem I had was with routes. I needed a post method for a new calls. After creating two actions (one for create and one for destroy) along with their routes everything started working. It looks like it was trying to use the default routes and actions which would re-route to the wrong URL for my purposes.
resources :clients do
collection do
post "calls"
end
member do
delete "cancel"
end
end
def calls
#call = Call.new(params[:call])
if #call.save
redirect_to clients_path, notice: "Call was successfully created."
else
render :new
end
end
def cancel
#call = Call.find(params[:id])
#call.destroy
respond_to do |format|
format.html { redirect_to clients_path }
format.json { head :no_content }
end
end

Rails 3 - Displaying submit errors on polymorphic comment model

Fairly new to Rails 3 and have been Googling every which way to no avail to solve the following problem, with most tutorials stopping short of handling errors.
I have created a Rails 3 project with multiple content types/models, such as Articles, Blogs, etc. Each content type has comments, all stored in a single Comments table as a nested resource and with polymorphic associations. There is only one action for comments, the 'create' action, because there is no need for the show, etc as it belongs to the parent content type and should simply redisplay that page on submit.
Now I have most of this working and comments submit and post just fine, but the last remaining issue is displaying errors when the user doesn't fill out a required field. If the fields aren't filled out, it should return to the parent page and display validation errors like Rails typically does with an MVC.
The create action of my Comments controller looks like this, and this is what I first tried...
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment])
respond_to do |format|
if #comment.save
format.html { redirect_to(#commentable, :notice => 'Comment was successfully created.') }
else
format.html { redirect_to #commentable }
format.xml { render :xml => #commentable.errors, :status => :unprocessable_entity }
end
end
end
When you fill nothing out and submit the comments form, the page does redirect back to it's appropriate parent, but no flash or nothing is displayed. Now I figured out why, from what I understand, the flash won't persist on a redirect_to, only on a render. Now here's where the trouble lies.
There is only the 'create' action in the comment controller, so I needed to point the render towards 'blogs/show' (NOTE: I know this isn't polymorphic, but once I get this working I'll worry about that then). I tried this in the "else" block of the above code...
else
format.html { render 'blogs/show' }
format.xml { render :xml => #commentable.errors, :status => :unprocessable_entity }
end
Anyway, when I try to submit an invalid comment on a blog, I get an error message saying "Showing [...]/app/views/blogs/show.html.erb where line #1 raised: undefined method `title' for nil:NilClass."
Looking at the URL, I think I know why...instead of directing to /blogs/the-title-of-my-article (I'm using friendly_id), it's going to /blogs/the-title-of-my-article/comments. I figure that extra "comments" is throwing the query off and returning it nil.
So how can I get the page to render without throwing that extra 'comments' on there? Or is there a better way to go about this issue?
Not sure if it matters or helps, but the route.rb for comments / blogs looks like this...
resources :blogs, :only => [:show] do
resources :comments, :only => [:create]
end
I've been plugging away at this over the last few weeks and I think I've finally pulled it off, errors/proper direction on render, filled out fields remain filled in and all. I did consider AJAX, however I would prefer to do it with graceful degradation if at all possible.
In addition, I admit I had to go about this a very hacky-sack way, including pulling in a way to pluralize the parent model to render the appropriate content type's show action, and at this stage I need the code to simply work, not necessarily look pretty doing it.
I KNOW it can be refactored way better, and I hope to do so as I get better with Rails. Or, anyone else who thinks they can improve this is welcomed to have at it. Anyway, here is all my code, just wanted to share back and hope this helps someone in the same scenario.
comments_controller.rb
class CommentsController < ApplicationController
# this include will bring all the Text Helper methods into your Controller
include ActionView::Helpers::TextHelper
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment])
respond_to do |format|
if #comment.save
format.html { redirect_to(#commentable, :notice => 'Comment was successfully created.') }
else
# Transform class of commentable into pluralized content type
content_type = find_commentable.class.to_s.downcase.pluralize
# Choose appropriate instance variable based on #commentable, rendered page won't work without it
if content_type == 'blogs'
#blog = #commentable
elsif content_type == 'articles'
#article = #commentable
end
format.html { render "#{content_type}/show" }
format.xml { render :xml => #commentable.errors, :status => :unprocessable_entity }
end
end
end
private
# Gets the ID/type of parent model, see Comment#create in controller
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
end
end
articles_controller.rb
class ArticlesController < ApplicationController
def show
#article = Article.where(:status => 1).find_by_cached_slug(params[:id])
#comment = Comment.new
# On another content type like blogs_controller.rb, replace with appropriate instance variable
#content = #article
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #article }
end
end
end
show.html.erb for articles (change appropriate variables for blog or whatever)
<h1><%= #article.title %></h1>
<%= #article.body.html_safe %>
<%= render :partial => 'shared/comments', :locals => { :commentable => #article } %>
shared/_comments.html.erb (I'm leaving out the displaying of posted comments here for simplification, just showing the form to submit them)
<%= form_for([commentable, #comment]) do |f| %>
<h3>Post a new comment</h3>
<%= render :partial => 'shared/errors', :locals => { :content => #comment } %>
<div class="field">
<%= f.label :name, :value => params[:name] %>
<%= f.text_field :name, :class => 'textfield' %>
</div>
<div class="field">
<%= f.label :mail, :value => params[:mail] %>
<%= f.text_field :mail, :class => 'textfield' %>
</div>
<div class="field">
<%= f.text_area :body, :rows => 10, :class => 'textarea full', :value => params[:body] %>
</div>
<%= f.submit :class => 'button blue' %>
<% end %>
shared/_errors.html.erb (I refactored this as a partial to reuse for articles, blogs, comments, etc, but this is just a standard error code)
<% if content.errors.any? %>
<div class="flash error">
<p><strong><%= pluralize(content.errors.count, "error") %> prohibited this page from being saved:</strong></p>
<ul>
<% content.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
I slightly refactored #Shannon answer to make it more dynamic. In my 'find_parent' method I'm grabbing the url path and fetching the controller name. In the 'create' method I'm creating an 'instance_variable_set' which creates a dynamic variable for either Articles (#article) or Blogs (#blog) or what ever it may be.
Hopefully you'll like what I've done? Please let me know if you have any doubts or if something can be improved?
def create
#comment = #commentable.comments.new(params[:comment])
if #comment.save
redirect_to #commentable, notice: "Comment created."
else
content_type = find_parent
instance_variable_set "##{content_type.singularize}".to_sym, #commentable
#comments = #commentable.comments
render "#{content_type}/show"
end
end
def find_parent
resource = request.path.split('/')[1]
return resource.downcase
end
You're getting an error because the blogs/show view likely refers to the #blog object, which isn't present when you render it in the comments controller.
You should go back to using the redirect_to rather than render. It wasn't displaying a flash when you made an invalid comment because you weren't telling it to set a flash if the comment wasn't saved. A flash will persist till the next request.