Rails update PUT with same URL - ruby-on-rails

I am calling a random Post and allowing users to +1 or -1 the post before loading another post. My model generates a random record at the URL /posts/random using the following.
Post.rb // Model
def self.find(*args)
if args.first.to_s == "random"
Post.find :first, :offset => rand(Post.count)
else
super
end
This code generates a random post when the user visits posts/random. However, I also defined a thumbs_up and thumbs_down field in the database that correspond to a thumb_up and a thumb_down image that when clicked, need to +1 or -1 the value in the database, before redirecting to another /posts/random. My understanding is that to do this I need to invoke the PUT update method, however, since my URL is posts/random instead of posts/1, how can I do this? Do I need to alter my routes?

First thing is that you'd need is an instance variable representing the random post so that you could use it in the thumbs up and thumbs down links. Then you need to make sure that those links look something like this:
<%= link_to "Thumbs Up", post_path(#post, thumbs_up: 1), method: :put %>
<%= link_to "Thumbs Down", post_path(#post, thumbs_down: 1), method: :put %>
That way, when in your update action for your posts controller, you can do something like this:
#post.update_attributes(thumbs_up: #post.thumbs_up + params[:thumbs_up],thumbs_down: #post.thumbs_down + params[:thumbs_down] )
That is only a start, but I think that should get you going in the right direction.

Related

setting a dynamic link_to rails

I'm pretty new to rails and I'd like to set my links for a certain page dynamically. I have a table called "Unfinished" and it has a column called "link" (corrected from "links") I'd like to be able to call the "link" record in the view to set my link_to link path.
I am trying to do this...
<%= link_to #unfinished.link(:p => #post.id) do %> FINISH <% end %>
...but that's not working.
my controller says:
def show
#post = Post.find(params[:id])
#unfinished = Unfinished.where('progress = ?', #post.progress).last
end
and the controller logic works fine...until I try to put the #unfinished.link into link_to
Edit:
Error Message:
wrong number of arguments (1 for 0)
Model
class Unfinished < ActiveRecord::Base
end
The type of links are :
step1_path
step2_path
step3_path
I am making a multipage form that you can save partway through. Based on a value in the #post.progress column (like 1, 2, 3) the correct path to complete the post will be provided (step1_path, step2_path etc...)
try this.
<%= link_to eval(#unfinished.link.to_s) do %> FINISH <% end %>
since the link you want is actually a named route, so you will need to eval it.
but with this you wouldn't be able to be able to pass in the post id, which you will need.
If the route is the same for all records (save for what part you are on based on the progress attribute) do you even need to store it in the database? You could just make the link method return the path (that you would still need to eval).
something like
def link (post)
"step#{self.progress}_path(post.id)"
end
and then eval the link on the way back. but Not sure if that will work, just thinking out loud...
There are gems that do multi-stage forms perhaps looking into them might help?

Fire up custom action when user clicks 'Search" on RAILS 3.2 form?

Is it possible to fire up an custom action when user clicks 'Search' button on search form?
There is an mechanism in our app to save every URL the app has hit. In our search form, when clicking 'Search' button, there will bring up the search result page. The problem is that the URL for the search result form was not saved. The Back button brings back the search page (for setup search params) instead of the search result page (because its URL was not saved).
Here is the search form for model configs:
<h4>Search Form></h4>
<%= simple_form_for #config, :method => :get, :url => search_result_configs_path do |f| %>
<%=render :partial => 'search_params', :locals => {f: f} %>
<%= f.button :submit, t('Search') %>
<% end %>
The URL for the search result looks like this (with the search params set by user) after user clicks Search button:
http://localhost:3000/configs/search_results?utf8=%E2%9C%93&engine_config[start_date_s]=&engine_config[end_date_s]=&engine_config[engine_id_s]=1&engine_config[argument_name_s]=&engine_config[commissioned_s]=&commit=%E6%90%9C%E7%B4%A2
This is the URL we would like the app to remember. We figure we need custom action triggered when a user clicks 'Search' button. Is it possible?
Route
Firstly, calling a custom application is actually quite a simple process - you just need to call its route:
#config/routes.rb
resources :search do
collection do
get :custom_action
end
end
This will allow you to use the likes of form_tag to call the custom route:
#app/views/your_controller/view.html.erb
<%= form_tag search_custom_action_path, method: :get do %>
...
<% end %>
--
Form
Secondly, you're using simple_form for your search form.
This is completely fine, but the problem you have here is that when you use this, it has to have a ActiveRecord object to populate the form with. This is probably where you're getting confused, as to do this, you need ot make sure #config is available every time you load that form, which I imagine can be a lot.
We've created a search form here:
Although in Rails 4, we used a form_tag for this form, as it allowed us to create & display the form where-ever we need in the app. This allows us to pass the required params through the form & access them on the other side
--
Params
You mention you want to "save the URL" - what do you mean by this?
Surely you'd prefer to save the params?
If this is true, the way to do this is actually relatively simple - you'll get access to the params hash in your controller when you send the request through:
#app/controllers/your_controller.rb
Class YourController < ApplicationController
def custom_action
params[:your_param] #-> this is accessible here
end
end
The bottom line is if you wanted to save the query strings, you'll have to create a model called Search or similar, allowing you to pass the params through when you process the custom action in your controller, just like you would any other ActiveRecord object

How can do an ActiveRecord call on two values?

I'm writing a rails app, and i have an Article Model. This model has two attributes of issue_id and article_position
In the show view i'm attempting to make a button that goes to the "next" article in a series. the articles go in order from 1 to whatever.
How can i properly do the following call (Psuedo-code):
<%= link_to 'Next', #article_url.where(#article.article_posistion = current_article.article_position + 1, #article.issue_id = current_article.issue_id) %>
I'm pretty stuck as of right now, if i need to be using find, let me know.
thanks!
You can define a method on the Article model that would do what you want:
def next_article
Article.where(issue_id: issue_id, article_position: article_position + 1)
end
Then in your controller you could do:
#next_article = #article.next_article
And in your view:
<%= link_to 'Next', article_path(#next_article) if #next_article %>
This should work how you want, assuming you have article_path defined as a named route.

How do I create a button that increments an attribute of a model and can be pressed once per browser?

An apartment_listing has many reviews, and a review belongs to an apartment_listing.
In the file views/apartment_listings/show.html.erb, I show a list of reviews for that particular apartment_listing. These reviews are generated with the partial view apartment_listings/_review.html.erb like so:
<%= render :partial => "review", :collection => #apartment_listing.reviews %>
In _review, I want to have a button that, when pressed:
Increments that review's helpful_count attribute.
Makes it so that it cannot be pressed again while in the same browser - probably using cookies.
I feel like the former shouldn't be too hard to figure out, but it's got me beat. I'm really not sure where to start with the second goal.
EDIT: I managed to update the review's helpful_count attribute with this code in apartment_listings/_review.html.erb:
<%= form_for review, :method => :put, :remote => true do |f| %>
<%= f.hidden_field :helpful_count, value: (review.helpful_count + 1) % >
<%= f.submit 'Helpful?' %>
<% end %>
However, I'm not sure if this is the best way to do it, and I'd like to be able to disable the button after it is clicked.
Your code for updating helpful_count has the potential for problems. Imagine two users have loaded an apartment on their web page. One of them marks it helpful, and the next one does as well. Since when they initially loaded the page, helpful_count was the same, after both of them click helpful, the count will only be incremented by one: it would be updated twice to the same value.
Really, you want to create a new action, probably under the reviews resource for an apartment. That action could use ActiveRecord's increment method to update the helpful_count (technically there's still a race condition in increment!, you'd encounter it much less often) http://apidock.com/rails/ActiveRecord/Persistence/increment%21
Cookies seem like a reasonable solution for the latter problem. Simply bind to submit on the form with jQuery, and create the cookie in the handler.
What does the code look like in your reviews controller? More experienced RESTful coders might be able to speak more coherently on this, but the way I see it, incrementing the helpful_count attribute should be an action sent to the reviews controller. That way, you can create a link that performs the action asynchronously.
For example, inside _review.html.erb:
<% collection.each do |review| %>
<%= link_to "Mark as Helpful", "/apartment_listing/#{#apartment_listing.id}/reviews/#{#review.id}/incHelpful?nonce=#{SecureRandom.rand(16)}", :remote => true, :method => :put %>
# ... Do something cool with your review content ...
<% end %>
Inside your ReviewsController class:
def incHelpful
unless params[:nonce] == session[:nonce][params[:id]]
#review = Review.find(params[:id])
#review.helpful_count += 1
#review.update_attributes(:helpful_count)
session[:nonce][params[:id]] = params[:nonce]
end
render :nothing
# Optionally return some javascript or JSON back to the browser on success/error
end
Inside /config/routes.rb:
put "apartment_listing/:apart_id/reviews/:id/incHelpful" => "reviews#incHelpful"
The main idea here is that actions that edit a resource should use the PUT http method, and that change should be handled by that resource's controller. Rails' built-in AJAX functions are engaged by setting :remote => true inside the link_to helper. The second concept is that of a nonce, a random value that is only valid once. Once this value is set in the user's session, subsequent requests to incHelpful will do nothing.

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

Resources