Link for action problem - ruby-on-rails

I have problem with my application.
I have table report, there are 2 column , user_id and comment_id
I created link on article comment view
<%= link_to "[ ! ]", report_comment_url(comment) %>
class CommentsController < ApplicationController
def report
#comment = Comment.find(params[:id])
#comment = CommentReport.new(params[:comment_report, :comment_id])
if #comment_report.save
redirect_to :back
end
redirect_to :back
end
end
but it was error
ActionController::MethodNotAllowed
Only post requests are allowed.
Do you have any suggestion how to post current_user id and comment_id to report table ?

Given what I assume you're trying to accomplish, I'd suggest you use link_to_remote.

params[:comment_report] is nil because there's no reference to that in your link_to statement. Since you mention in the comment that your view is:
<%= link_to "[ ! ]", report_comment_url(comment), :comment_id => comment.id, :user_id => comment.user_id %>
Then you need this in your controller:
#comment_report = CommentReport.new(:user_id => params[:user_id], :comment_id => params[:comment_id])
But I agree with NSD that link_to_remote would work better for what you want to accomplish (which is create a new record and return the user to the page). You would also eliminate the need for your #comment = Comment.find(params[:id]) statement.

Here's what's going on:
Using Restful routes, you've set up report as a post operation. Which seems reasonable because report is performing the create action.
Unfortunately link_to doesn't know or even care about that. Links in general only perform get requests. Forms produce post requests, but they seem unnecessary in this case.
You have four options.
Make the [ ! ] link a button on a form submitting to report.
Break RESTful guidelines and redefine report receive get requests.
Make this a link_to_remote call. N.B. This relies on javascript and will not work at all if Javascript is disabled.
Add the method options to the link_to call. N.B. This also relies on javascript and will fall back to a get request if javascript is disabled.
<%= link_to "[ ! ]", report_comment_url(comment), :method => :post %>
However none of these solutions will solve all your problems. There are a couple of bugs with your posted code that you may not have realized yet.
First:
#comment = CommentReport.new(params[:comment_report, :comment_id])
is bad syntax and will fail. There are a number of ways to fix this, the preferred method is to to roll :comment_id into the params[:comment_report] hash to fix this.
Ie pass the params as:
params = {
:id => 4, # done by report_comment_url
:comment_report => {
:attribute1 => value1,
...
:comment_id => 4
}
}
Now you can use
#comment = CommentReport.new(params[:comment_report])
for the desired effect.
Second:
report_comment_url does not pass along the additional parameters, so your controller will try to save an empty record. Adding the comment_report to the arguments of report_comment_url will fix this problem.
This will perform a remote call requesting the report action in the comments controller, with the parameter hash required to fix the other problem.
<%= link_to_remote "[ ! ]", report_comment_url(comment,
:comment_report => {:attribute1 => value1, ..., :comment_id => comment.id}),
:method => :post %>

Related

How do I set my rails app up so that I can click a letter and see only the entries starting with that letter?

My rails app basically displays a dictionary of sorts, which is stored in a database. I've set up a basic rails app with scaffold to display this on a webpage, but I can't for the life of me figure out how to set this next bit up. Basically I want a row of the 26 alphabetical letters, and when you click on a letter (say 'A'), I only want to see the dictionary entries of that letter.
My index.html.erb file:
<% for char in 'A'..'Z' %>
<%= link_to( "#{char}", "/words/lettersearch", :id => "#{char}") %>
<% end %>
my words_controller.rb file method for doing this:
def lettersearch
#word = Word.find(:all, :conditions => ["word LIKE ?", "#{params[:word]}%"])
end
It then outputs the #word to another page.
My problem is that it just outputs the entire dictionary again, no matter what letter I click on the index page. I know it's to do with the routing, as it never seems to actually run the 'lettersearch' method - it's always trying to run through the 'Show' method that's defined by default earlier in the controller.
Anyone able to give me a quick hand with how to route this thing through? I'm pretty new to rails and I don't understand the workings of link_to very well at all.
routes.rb:
Dictionary::Application.routes.draw do
resources :words do
post 'search', :on => :collection
post 'lettersearch', :on => :collection
end
#Rest of routes.rb still commented apart from
match ':controller(/:action(/:id))(.:format)'
You are passing in a param called :id but in your controller you use a param called :word
Also, update your code to use newer syntax
World.where(["word like ?", "#{params[:id]}%"])
And your view code can be cleaned as well
<% for char in 'A'..'Z' %>
<%= link_to char, "/words/lettersearch", :id => char %>
<% end %>
Your routes file only has a POST route to letter search, but a link_to is a GET request. So whats happening is the GET request is hitting /words/:id via GET which is the show action by default, and the params[:id] inside that request will be "lettersearch"
You could run the search in your index. I think that might be what you want to do. In your index controller you could put:
#orders = Order.lettersearch(params[:word])
and then in your model:
def lettersearch(word)
if lettersearch
#word = Word.find(:all, :conditions => ["word LIKE ?", "#{word}%"])
else
all
end
end

Get url for current page, but with a different format

Using rails 2. I want a link to the current page (whatever it is) that keeps all of the params the same but changes the format to 'csv'. (setting the format can be done by having format=csv in the params or by putting .csv at the end of the path). Eg
posts/1
=> posts/1.csv OR posts/1?format=csv
posts?name=jim
=> posts.csv?name=jim OR posts?name=jim&format=csv
I tried this as a hacky attempt
request.url+"&format=csv"
and that works fine if there are params in the current url (case 2 above) but breaks if there aren't (case 1). I could come up with more hacky stuff along these lines, eg testing if the request has params, but i'm thinking there must be a nicer way.
cheers, max
EDIT - btw, it's not guaranteed that the current page could have a named route associated with it, in case that's relevant: we could have got there via the generic "/:controller/:action/:id" route.
<%= link_to "This page in CSV", {:format => :csv } %>
<%= link_to "This page in PDF", {:format => :pdf } %>
<%= link_to "This page in JPEG", {:format => :jpeg } %>
EDIT
Add helper
def current_url(new_params)
url_for :params => params.merge(new_params)
end
then use this
<%= link_to "This page in CSV", current_url(:format => :csv ) %>
EDIT 2
Or improve your hack:
def current_url(new_params)
params.merge!(new_params)
string = params.map{ |k,v| "#{k}=#{v}" }.join("&")
request.uri.split("?")[0] + "?" + string
end
EDIT
IMPORTANT! #floor - your approach above has a serious problem - it directly modifies params, so if you've got anything after a call to this method which uses params (such as will_paginate links for example) then that will get the modified version which you used to build your link. I changed it to call .dup on params and then modify the duplicated object rather than modifying params directly. – #Max Williams
You can use:
link_to "text of link", your_controller_path(format:'csv',params: request.query_parameters)
#floor's answer was great, I found it very useful.
Although the method can be improved by using the to_params method rather than contructing your own, like so:
def current_url(new_params)
params.merge!(new_params)
"#{request.uri}#{params.to_params}"
end

collection.build of nested attribute through a remote_link does not create params when submitting form

I have the following model:
class Activity < ActiveRecord::Base
has_many :clientships, :dependent => :destroy, :after_add => :default_client_info
accepts_nested_attributes_for :clientships, :allow_destroy => true
end
In my controller, if I perform the following
def new
#activity = IndividualActivity.new(params[:activity])
#activity.clientships.build(:client => Client.first)
...
end
and then save the form, it creates the relevant params and submits successfully.
However, if I chose to call the following through a remote link
#activity.clientships.build(:client => Client.last)
the view is updated with the new clientship record but when I submit the form, the params[:activity] is not created for the second nested attribute. (Why not!?)
This is the view:
%h1 Create a new Activity
- form_for #activity do |f|
%div
= render "activities/client_selector", :f => f
%div
= f.submit "Save!"
Here is the remote_link's controller action
def add_client
#activity = IndividualActivity.new(session[:individual_activity])
# Refresh client
#activity.clientships.build(:client => Client.find(params[:client_id]))
respond_to do |format|
format.js
end
end
This is the add_client.html.js:
page.replace_html "selected_clients", :partial => 'activities/clients'
This is the activities/clients partial:
- form_for #activity do |f|
- f.fields_for :clientships do |client_f|
%tr
%td= client_f.hidden_field :client_id
%td= client_f.object.client.full_name
Does anyone know how I can troubleshoot this further? I seem to have come to a dead-end with my debugging... One thing to note, there is a double use of the following form_for used in new.html.haml and the activities/clients partial (is this problematic?)
- form_for #activity do |f|
I am on rails v2.3.5
Thanks
You ask about debugging, so the first step may be looking at the server log (log/development.log).
There you should see the "params" hash.
Maybe your params contain "activity"=>{"client_id"=>..} instead of "client_id"=>.. ?
Also look at the generated HTML page - use a Firebug or just use a "view source" method of your browser. Look, especially, for input names.
If everything looks OK, put a few debug calls in your action, and look at the development.log for some database activity - do the SQL queries look like they are doing what you want?
In your question there is no 'save' method. The 'build' method does NOT save the created record. Maybe this is your problem?
def add_client
logger.debug "Creating Activity"
#activity = IndividualActivity.new(session[:individual_activity])
logger.debug "Building clientship"
# Refresh client
#activity.clientships.build(:client => Client.find(params[:client_id]))
logger.debug "#activity = #{#activity.inspect}"
# Maybe you were missing this part of code?
logger.debug "Saving #activity"
#activity.save! # use a ! to easily see any problems with saving.
# Remove in production and add a proper if
logger.debug "Saved. #activity = #{#activity.inspect}"
respond_to do |format|
format.js
end
end
You should create a functional test (in case you haven't already) and ensure that if you send proper parameters, your action works as intended.
The test will narrow your search. If the test fails, you know you have a problem in the action. If the test is OK, you need to ensure the parameters are sent properly, and you probably have the problem in your view.
UPDATE:
You said you have TWO forms on the page. This may be the problem, since only one form may be sent at a time. Otherwise it would need to work in a way which can send two requests in one request.
First thing (useful in all similar problems): validate whether your page has correct HTML structure - for example http://validator.w3.org would be a good start. Try to make the code validate. I know that some people treat a "green" status as a unachievable mastery, but just it's really not so hard. With valid code you may be sure that the browser really understands what you mean.
Second: Place all your inputs in a single form. You have problems with nested attributes. For start, try to manually insert inputs with name like <input name="activity[clientship_attributes][0][name]" value="John"/>, and for existing clientships ensure that there is an input with name = activity[clientship_attributes][0][id].
This is the way nested attributes are handled.
Your view may create such fields automagically. This construction should be what you need: (it worked in one of my old project in rails 2.x, I have just replaced the names with ones you use)
<% form_for(#activity) do |f| %>
<p><%= f.text_field :activity_something %></p>
<% #activity.clientships.each do |clientship| %>
<% f.fields_for :clientships, clientship do |cform| %>
<p><%= cform.text_field :name %></p>
<p><%= cform.text_fiels :something %></p>
<% end %>
<% end %>
<% end %>
If you really want to use a partial there, don't create a new form in the partial. Use only the parts of above code.
To pass a variable to the partial, use :locals attribute in the place where you call render :partial:
<%= render :partial => 'clientship', :locals => {:form => f} %>
Then, in your partial, you may use a local variable form where you would use f outside of the partial. You may, of course, map the variables to the same name: :locals => {:f => f}

Post Params to External Site with Link_to

I have a shop application and another site thats for a special promotion. I've used Active Resource to import products from the shop in to the promo site and added a shopping cart to add the products. However, to actually order the products I need to send the items to the shop application, creating a new cart there to finish the order.
I've made a demo 'RESTful' application to practice using xml to send data back and forth, so I'm trying to use the principles of REST for the real app. However, I need to send the products to a non-RESTful controller. Just to give you an idea of the Cart controller in the shop, here are its actions:
def index…
def add…
def checkout…
def update…
def remove…
def empty…
def apply_discount…
def remove_discount…
def apply_credits…
def remove_credits…
def stock_check…
# My action to accept items from carts in other apps
def cart_import…
And in routes.rb, the only route relating to the cart is currently
map.cart 'cart/:action/:id', :controller => 'shop/cart'
I've inherited the shop application from a previous developer, so I'd probably try to make it more RESTful if I was to make it from scratch.
Anyway, I'm pretty confident that I can get the cart to respond to XML, even without being defined with map.resources. My problem is how to send a hash of the cart items and quantities from the promo app.
To group the cart items and quantities I've collected the item's product id and quantity in to a hash:
<% #items = Hash.new %>
<% #cart.items.collect {|i| #items[i.product_id] = i.quantity} %>
Which when inspected gives the following output:
<%= Rails.logger.info #items.inspect %>
{1144=>2, 1143=>1}
So I figured to send them to the shop I could pass them in a posted link_to:
<%= link_to 'Export Cart', "http://shop.example.com/cart/cart_import", :items => #items, :method => :post %>
That doesn't seem to do anything, whereas omitting the first field appends the items to the URL in a format that looks sensible, but appears as a relative link on the promo application:
<%= link_to "http://shop.example.com/cart/cart_import", :items => #items, :method => :post %>
http://promo.example.com/cart?items[1143]=1&items[1144]=2&method=post
I'm sure the clue is in that the #items object needs to be passed in with the url, but since I can't use a named route I don't really know how to get it in there so that it is posted in the correct format.
Thanks for the help,
Gareth
the way you are passing in the parameters for the link_to method is assuming that :items is one of the link_to options, not one of the url options. this is an order of precedence issue and if you wrap your url inside parens then you can use the options available for the url_for method on your url, to build the path: http://apidock.com/rails/ActionView/Helpers/UrlHelper/url_for
You can't use a link to generate a POST request. It is turning to a GET request. Better use javascript to generate a post request on click of a button or some other event.
In the end I made a helper:
def hash_to_params(items)
result = ""
i = 0
items.each do |item|
i > 0 ? result += "&" : result += ""
result += "items[#{item[0]}]=#{item[1]}"
i += 1
end
return result
end
Then for the link I called the helper:
<%= link_to "export", "http://shop.example.com/cart/cart_import?#{hash_to_params(#items)}", :method => :post %>
Pretty ugly way of doing it, but I really can't think of anything better?
Cheers,
Gareth

Generating URLs when not using an integer as an id?

So I'm building a blog engine which has /articles/then-the-article-permalink as it's URL structure. I need to have prev and next links which will jump to the next article by pub_date, my code looks like this:
In my articles#show
#article = Article.find_by_permalink(params[:id])
#prev_article = Article.find(:first, :conditions => [ "pub_date < ?", #article.pub_date])
#next_article = Article.find(:first, :conditions => [ "pub_date > ?", #article.pub_date])
And in my show.html.erb
<%= link_to "Next", article_path(#next_article) %>
<%= link_to 'Prev', article_path(#prev_article) %>
In my articles model I have this:
def to_param
self.permalink
end
The specific error message I get is:
article_url failed to generate from {:action=>"show", :controller=>"articles", :id=>nil}, expected: {:action=>"show", :controller=>"articles"}, diff: {:id=>nil}
Without the prev and next everything is working fine but I'm out of ideas as to why this isn't working. Anyone want to help?
Solved my own problem, because I only had 2 records it was always finding a nil record. I changed the code in the views to:
<%= link_to "Next", article_path(#next_article) if !#next_article.nil? %>
<%= link_to 'Prev', article_path(#prev_article) if !#prev_article.nil? %>
Stupid and overblown problem, but I thought I'd add the solution for anyone that comes across this in future.
#next_picture = Article.find(:first, :conditions => [ "pub_date > ?", #article.pub_date])
should probably be this:
#next_article = Article.find(:first, :conditions => [ "pub_date > ?", #article.pub_date])
(I changed #next_picture to #next_article)
Stick in a <% debugger %> in your template and then check what the value of the #next_article.permalink is? I suspect the permalink is blank (either empty string or nil).
Also, just in general, can I recommend the friendly_id as a more robust solution to this problem (including changing permalinks and other features).
You could use will_paginate for this problem and set the number of of articles per page to 1, and use a named_scope to order the articles by pub_date. Then your next and previous links would just work. The URLs for next and previous would show the page number rather than the date, but you could probably modify the behaviour of the params sent to the action to use the date instead of the page number.

Resources