Accepting bids with custom controller action - ruby-on-rails

I have a rails 3.2 project.
There are a bid object and i want the user to be able to accept this bid!
How can i do this?
My first thought is about to make a new method on bids controller and in the method i have to update the accept boolean of bid.Something like that?
def accept
#bid = Bid.find(params[:id])
#bid.subject ='accept!!!!'
flash[:notice] = "Successfully destroyed post."
respond_to do |format|
format.html { redirect_to "/mybids" }
format.json { head :no_content }
end
end

You're question is a little vague, because it's not clear what a bid is attached to, or what the other models are in your app, since you don't say.
However, let's say that this is an auction site, and auctions have bids. When a bid is accepted
All other bids on that auction are destroyed
The bid that was accepted has it's accepted boolean field set to true.
If that is close to what you're trying to do, then the code below should accomplish that.
in app/controllers/bids_controller.rb
# PUT /bids/:id/accept
def BidsController < ApplicationController
def accept
#bid = Bid.find(params[:id])
#bid.update_attribute(:accepted, true)
#bid.auction.bids.each do |rejected_bid|
rejected_bid.destroy unless rejected_bid == #bid # destroys all be the accepted bid
end
flash[:notice] = "Bid accepted."
respond_to do |format|
format.html { redirect_to "/mybids" }
format.json { head :no_content }
end
end
end
Then you need to add a route for this action to your config/routes.rb file. Something like...
resources :bids do
put :accept, :on => :member
end
Form the view, you would link to this route like this:
link_to "accept", accept_bid_path(#bid)
And if you call rake routes from your command line, you should see an entry that looks like this:
accept_bid PUT /bids/:id/accept(.format) {:action=>"accept", :controller=>"bids}

Related

Issue with before_filter

Please help me try and understand what is happening here:
I need to approve a nested snippet but when I do it says it cannot find book. I think it may be an issue with the routes because the URL in the browser doesn't match the rake routes.
If someone could hold my hand and explain this as you would to a child :)
Couldn't find Book without an ID
Below is the controller with snippets#approve and the before_filter.
class SnippetsController < ApplicationController
before_filter :authenticate_user!
before_filter :find_book
def create
#raise params.inspect
#snippet = #book.snippets.create(params[:snippet])
#snippet.user = current_user
if #snippet.save
redirect_to #book
flash[:success] = "Snippet submitted and awaiting approval."
else
flash[:base] = "Someone else has submitted a snippet, please try again later"
redirect_to #book
end
end
def approve
#raise params.inspect
#snippet = #book.snippets.find(params[:id])
#snippet.update_attribute(:approved, true)
redirect_to admins_path
end
def edit
#snippet = #book.snippets.find(params[:id])
end
def update
#snippet = #book.snippets.find(params[:id])
respond_to do |format|
if #snippet.update_attributes(params[:snippet])
format.html { redirect_to #book, notice: 'Comment was successfully updated.' }
else
format.html { render action: "edit" }
end
end
end
private
def find_book
#raise params.inspect
#book = Book.find(params[:book_id])
end
end
Now I understand that since I'm doing a post my rake routes says this.
/books/:book_id/snippets/:id(.:format)
Here is the routes for the custom route:
active_snippet POST /snippets/:id/activate(.:format)
This is my custom routes for book && snippet :approval
post "books/:id/activate" => "books#approve", :as => "active_book"
post "snippets/:id/activate" => "snippets#approve", :as => "active_snippet"
I've currently got this in my browser ../snippets/2/activate
Erm.... Not sure if I'm thinking correctly.
You're sending a POST request to snippets/:id/activate which calls snippets#approve.
There is a before_filter on the entire SnippetsController that calls find_book which executes #book = Book.find(params[:book_id]). Because your path is snippets/:id/activate, params[:book_id] is nil and hence you are getting that error.
You need to either change your snippets#approve path to include the book_id, or pass the book_id as a POST param so that your before filter has access to it.

Change Params[:id] in Ruby on Rails

I have a Ruby on Rails application where you can create 'posts'. I started of by using the scaffold generator to give generate the title which is a string and the body which is the content.
Each 'post' has a url of the id, for example /1, /2, /3, etc.
Is there a way to change it to generater a string of random characters and numbers, for example /49slc8sd, /l9scs8dl, etc?
Here is what I have for the posts_controller.rb
class PostsController < ApplicationController
# GET /posts
# GET /posts.json
def index
#posts = Post.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #posts }
end
end
# GET /posts/1
# GET /posts/1.json
def show
#post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #post }
end
end
# GET /posts/new
# GET /posts/new.json
def new
#post = Post.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #post }
end
end
# GET /posts/1/edit
def edit
#post = Post.find(params[:id])
end
# POST /posts
# POST /posts.json
def create
#post = Post.new(params[:post])
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
# PUT /posts/1
# PUT /posts/1.json
def update
#post = Post.find(params[:id])
respond_to do |format|
if #post.update_attributes(params[:post])
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#post = Post.find(params[:id])
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url }
format.json { head :no_content }
end
end
end
And here is what I have in the post.rb model
class Document < ActiveRecord::Base
attr_accessible :content, :name
end
If you want your models not to have their primary key id in a predictable sequence, you can generate the id based on uuid or guid with the help of something like http://codesnipers.com/?q=using-uuid-guid-as-primary-key-in-rails
However you can also route based on any other property which uniquely identifies the resource which is the recommended approach if in case you dont want to expose the database identifiers in your routes
person/:person_random_token, :controller => :persons, :action => :show #adding this in your route file directing to the controller where you can use params[:person_random_token] to uniquely identify your person object in Persons model
In your controller's action you can say
Person.find_by_random_token(params[:person_random_token]) #assuming random_token is your column name
to get the Person object
If you would like to obfuscate numerical ID's , you could take a look at this interesting discusion .
You should also be aware of the to_param method for ActiveRecord::Base objects.
Basically, Rails calls this method on your objects to know what to put in the URL and params[:id]. By default it is just the primary key of the record in the database. Say you override it as such:
class Post < ActiveRecord::Base
def to_param
return id*100
end
def self.find_by_new_id(n)
return self.find(n/100) # really you'd want to handle strings and integers
end
end
The first record in your database would have url /posts/100.
In your controller, to retrieve the object you just do
#post = Post.find_by_new_id(params[:id])
(Of course you could override the default find method as well, but that is probably frowned upon.) Basically the to_param method transforms your id and the new finder undoes it. Usually you just point to another database column that has been automatically populated via a hook when the record is created. This is what is described in the link posted by Qumara otBurgas.
It's not clear what you are asking here. The path to the action specified in the routes does not require the id passed to be of a certain format. You can pass non-numeric ids if you want and within your action use the id however you'd like. Maybe if you supplied more info about the routes and actions we could understand what you are asking for.
There is a number of ways how you can generate a random string in Ruby.
Now, to the second part of your question. If you want to access your posts using a route like /post/rndm5tr, you can simply change this line of code inside your controller:
#post = Post.find(params[:id])
to
#post = Post.find_by_randomness(params[:id])
Now, simply create a migration: rails g migration AddRandomnessToPost randomness:string and run rake db:migrate (or bundle exec rake db:migrate, depending on how it's set up).
Of course, you are free to name the field whatever you want, randomness is just a random name I used. I think the common convention is to call them slugs or tokens, but I might be wrong.
Now, add a method to before_create in your model to generate the random string and add it to the soon-to-be-saved Post object (using one of the examples from the above link). It would be wise to check if the string you're generating is already taken (you could write a recursive method that calls itself again if a post already has the random token).

Create rails record from two ids

The functionality I'm trying to build allows Users to Visit a Restaurant.
I have Users, Locations, and Restaurants models.
Locations have many Restaurants.
I've created a Visits model with user_id and restaurant_id attributes, and a visits_controller with create and destroy methods.
Thing is, I can't create an actual Visit record. Any thoughts on how I can accomplish this? Or am I going about it the wrong way.
Routing Error
No route matches {:controller=>"restaurants", :location_id=>nil}
Code:
Routes:
location_restaurant_visits POST /locations/:location_id/restaurants/:restaurant_id/visits(.:format) visits#create
location_restaurant_visit DELETE /locations/:location_id/restaurants/:restaurant_id/visits/:id(.:format) visits#destroy
Model:
class Visit < ActiveRecord::Base
attr_accessible :restaurant_id, :user_id
belongs_to :user
belongs_to :restaurant
end
View:
<% #restaurants.each do |restaurant| %>
<%= link_to 'Visit', location_restaurant_visits_path(current_user.id, restaurant.id), method: :create %>
<% #visit = Visit.find_by_user_id_and_restaurant_id(current_user.id, restaurant.id) %>
<%= #visit != nil ? "true" : "false" %>
<% end %>
Controller:
class VisitsController < ApplicationController
before_filter :find_restaurant
before_filter :find_user
def create
#visit = Visit.create(params[:user_id => #user.id, :restaurant_id => #restaurant.id])
respond_to do |format|
if #visit.save
format.html { redirect_to location_restaurants_path(#location), notice: 'Visit created.' }
format.json { render json: #visit, status: :created, location: #visit }
else
format.html { render action: "new" }
format.json { render json: #visit.errors, status: :unprocessable_entity }
end
end
end
def destroy
#visit = Visit.find(params[:user_id => #user.id, :restaurant_id => #restaurant.id])
#restaurant.destroy
respond_to do |format|
format.html { redirect_to location_restaurants_path(#restaurant.location_id), notice: 'Unvisited.' }
format.json { head :no_content }
end
end
private
def find_restaurant
#restaurant = Restaurant.find(params[:restaurant_id])
end
def find_user
#user = current_user
end
end
I see a lot of problems here. The first is this line of code in your VisitController's create action (and identical line in your destroy action):
#visit = Visit.create(params[:user_id => #user.id, :restaurant_id => #restaurant.id])
params is a hash, so you should be passing it a key (if anything), not a bunch of key => value bindings. What you probably meant was:
#visit = Visit.create(:user_id => #user.id, :restaurant_id => #restaurant.id)
Note that you initialize #user and #restaurant in before filter methods, so you don't need to access params here.
This line of code is still a bit strange, though, because you are creating a record and then a few lines later you are saving it (if #visit.save). This is redundant: Visit.create initiates and saves the record, so saving it afterwards is pretty much meaningless. What you probably want to do is first initiate a new Visit with Visit.new, then save that:
def create
#visit = Visit.new(:user_id => #user.id, :restaurant_id => #restaurant.id)
respond_to do |format|
if #visit.save
...
The next thing I notice is that you have not initiated a #location in your create action, but you then reference it here:
format.html { redirect_to location_restaurants_path(#location), notice: 'Visit created.' }
Since you will need the location for every restaurant route (since restaurant is a nested resource), you might as well create a method and before_filter for it, like you have with find_restaurant:
before_filter :find_location
...
def find_location
#location = Location.find(params[:location_id])
end
The next problem is that in your view your location_restaurant_path is passed the id of current_user and of restaurant. There are two problems here. First of all the first argument should be a location, not a user (matching the order in location_restaurant_path). The next problem is that for the _path methods, you have to pass the actual object, not the object's id. Finally, you have method: :create, but the method here is referring to the HTTP method, so what you want is method: :post:
link_to 'Visit', location_restaurant_visits_path(#location, restaurant.id), method: :post
You'll have to add a find_location before filter to your RestaurantController to make #location available in the view here.
There may be other problems, but these are some things to start with.
location_id is nil and the path definition doesn't say (/:location_id) forcing a non-nil value there in order to route to that path; create a new route without location_id if you can derive it from a child's attribute (i.e. a restaurant_id refers to a Restaurant which already knows its own location_id).

Save referenced resource on update_attributes (create nested resource on edit)

I have something like issue tracking system where there are issues and they have some comments.
Now on one page I want to give user an option to edit some stuff of "issue" as well as add a comment. Editing of and issue is a standard stuff like in /edit but also I want to create a comment and validate if it's not blank.
I've figured out that I can build a comment and make a form for it, but how should I check simultaneously that both issue attributes and comment attributes are valid? Because each update should be followed by a new comment, but I don't want to create a new comment if the issue attributes are no valid.
I would approach this by first adding fails_validation? methods to both your Issues and Comments models to check for problems.
Second, you will have to manually load the #issue form data from params[] and validate it BEFORE you save it (can't use update_attributes(params[:issue]).) Create a new Comment and load it via params[]. Then you can test the validation on both models and go back to the edit action if either fails.
If both pass you can save #issue and then #comment as normal.
def update
#issue = Issue.find(params[:id])
# manually transfer form data to the issue model
#issue.title = params[:issue][:title]
#issue.body = params[:issue][:body]
#...
#comment = #issue.comments.new(params[:comment])
# validate both #issue and #comment
if #issue.fails_validation? || #comment.fails_validation?
flash[:error] = "Your edits or your comment did not pass validation."
render :action => "edit",
end
# validation passed, save #issue then #comment
respond_to do |format|
if #issue.save
#comment.save
format.html { redirect_to #issue, notice: 'Issue successfully updated. Comment created' }
format.json { head :ok }
else
format.html { render action: "edit" }
format.json { render json: #issue.errors, status: :unprocessable_entity }
end
end
end
Not the most elegant solution, but it should work.
You can validate the comment model and the issue model in their respective classes.
It is not clear to me whether you are using 'accepts_nested_attributes_for' in Issue for comments. If you are, then the standard IssueController#update will not save the record if issue is invalid and consequently, it will not create the comment records as well.
Here is the standard IssueController#update:
class IssueController < ApplicationController
def update
#issue = Issue.find(params[:id])
if #issue.update_attributes(params[:issue])
redirect_to issues_path, notice: 'issue updated'
else
render action: 'edit'
end
end

Rails ActiveAdmin - change the after update redirect_to

I have a Feature page that belongs to the Car page. That is working exactly how I want to, except for one thing.
After creating, updating or destroying, I want the page to be redirected to the admin_car_path(car) instead of the defaults admin_car_feature_path(car,feature) for create and update and admin_car_features_path(car).
I unsuccessfully searched for that.
ActiveAdmin.register Car do
end
ActiveAdmin.register Feature do
belongs_to :car
end
TIA
right code for updating without skipping validation
controller do
def update
super do |success,failure|
success.html { redirect_to collection_path }
end
end
end
Here is the code for update action for your case. This code goes to the features.rb - admin file:
controller do
def update
update! do |format|
format.html { redirect_to admin_cars_path }
end
end
end
This redirects to the cars index page. So you have the idea. Same for create and destroy actions.
At the current moment accepted answer leads to ignoring validation errors.
This works for me with the latest versions of ActiveAdmin and Rails:
controller do
def update
update! do |format|
format.html { redirect_to collection_path } if resource.valid?
end
end
def create
create! do |format|
format.html { redirect_to collection_path } if resource.valid?
end
end
end
Here is a solution that also works with create_another, using parent and child for model names.
This solution assumes that you show children as part of parent (e.g. via table_for) so you do not need child's index method.
In resource override controller's smart_resource_url and index methods:
controller do
def smart_resource_url
if create_another?
new_resource_url(create_another: params[:create_another])
else
parent_path(params[:parent_id])
end
end
def index
redirect_to parent_path(params[:parent_id])
end
end
Current answer is skipping validations. Some of the other answers are working but partially correct (incorrect use of super or manually validating resource).
Most updated "proper" way to redirect with AA after create and udpate:
controller do
def create
create! do |success,failure|
success.html { redirect_to collection_path, notice: "#{resource.model_name.human} was successfully created." }
end
end
def update
update! do |success,failure|
success.html { redirect_to collection_path, notice: "#{resource.model_name.human} was successfully updated." }
end
end
end
Marcelo, I'm not sure I understand your question, but wouldn't putting this into the update, create and destroy actions in your controller do the trick?
format.html { redirect_to redirect_address }
And make redirect_address whatever you need.

Resources