Ruby on Rails - Checking if Submitted Form Data matches Database data - ruby-on-rails

I have a form in which a user ('member') submits a 4-digit pin, which is then saved to a session variable called :submitted_pin. For some reason, the quick if/else statement isn't working properly, and I am assuming it is a silly mistake on my part, but if your willing to take a look I would really appreciate it!
View
<%= form_for :member_pin, :url => { :action => "verify_pin", :id => member.id } do |f| %>`
<%= f.label :pin %>
<%= f.text_field :pin %>
<%= f.submit 'Submit' %>
Controller
Before Filter
before_filter :validate_pin, :only => :show
Action (POST Route)
def verify_pin
#member = Member.find(params[:id])
session[:submitted_pin] = params[:member_pin][:pin]
redirect_to #member
end
Filter
def validate_pin
#member = Member.find(params[:id])
#member_pin = #member.pin
if session[:submitted_pin] == #member_pin
render #member
else
redirect_to '/'
end
end
And the result of all of this is a redirect to my root_url no matter what, even if the Pin entered does match the pin in the database for that user. Help! Thanks :)

Based on the results of the raise we talked about in the comments, it sounds like there's a class mismatch. The easiest way to fix (and, IMHO, the easiest to read) would be to adjust your validator code as follows:
if session[:submitted_pin].to_i == #member_pin.to_i
render #member
else
redirect_to '/'
end
If there's any chance that session[:submitted_pin] would be nil, you can use session[:submitted_pin].try(:to_i); that'll return nil if the variable is not set and prevent an error from getting thrown.

Related

How to use form_for with customized route?

The custom routing:
resources :blog, controller: 'posts'
How do I rewrite this line <%= simple_form_for(#post, blog_path) do |f| %> to get rid of the below error?
TypeError in Posts#edit
ActionView::Template::Error (no implicit conversion of Symbol into Integer)
I also tried <%= simple_form_for(blog_path(#post)) do |f| %>, which gets rid of the error, but then if I want to edit the form the inputs are emptied of their saved data.
posts_controller
def new
#post = Post.new
respond_with(#post)
end
def edit
end
def create
#post = Post.new(post_params)
if current_user.admin
#post.save
respond_with(#post)
else
flash[:success] = 'Get out of here.'
redirect_to root_url
end
end
It can take a hash options, including url, so something like this:
Edit: changed blog_path to blogs_path. The blog_path is the show action, not the create action and therefore requires an id (and isn't a post path anyway). Try it out this way.
<%= simple_form_for(#post, url: blogs_path) do |f| %>
Don't know if this applies, but a really cool feature I found the other day was .becomes - where you can change the "class" of your object so that Rails treats it in a different way:
This can be used along with record identification in Action Pack to allow, say, Client < Company to do something like render partial: #client.becomes(Company) to render that instance using the companies/company partial instead of clients/client.
So...
If you had a Blog model, and wanted each #post to be treated as such (again, I don't know if this is your setup at all), you could do the following:
<%= simple_form_for #post.becomes(Blog) do |f| %>
I'll delete if inappropriate; it's come in handy quite a lot for me.
Update
If you'd like your posts_path to be blog (IE url.com/blog/1), you'll want to look at using the path option for the routes generator:
#config/routes.rb
resources :posts, path: "blog", as: :blog # -> url.com/blogs/2

Non Mass assignable field in view

Iam kinda stuck at this problem. Trying to update a non mass-assignable attribute in the job model from an admin view. I need a button which trigger an action method in the admin controller which just changes job's isactive value to true.
In admin controller:
def approve
#job.update_attribute(:isactive = true)
if #job.save
redirect_to(:action => :show)
flash[:notice] = "Job Approved"
else
render 'show'
end
end
In jobs/1 view
<div style="text-align:center;" class="widget">
<%= form_for(#job, :url => url_for(:controller => 'admin', :action => 'approve'), :html => {:id => "contact_form", :class => "g-form"}) do |f| %>
<%= button_tag( :class => "g-btn type_default") do %>
<i class="icon-share-alt icon-white"></i>Approve this job
<% end %>
<% end %>
<% end %>
No matter what i do this form gets submitted to the job update action and show job successfully updated rather than the notice "Job approved" from the approve action in admin.
I have added the following route as well. but not sure what iam doing wrong. I need a simple button (ideally a block because of the styling req) which sets job's isactive field to true.
match "jobs/:id", :via=>:post, :controller=>"admin", :action=>"approve"
Mass assignment was designed to protect precisely against what you're trying to do here so ideally speaking, you should add it to a whitelist (attr_accessible) if you need to modify it.
However, there are a few issues with your controller action that will need to be addressed even if you get the isactive attribute whitelisted.
#job.update_attribute(:isactive = true) is not valid. update_attribute does not handle assignments, the correct syntax is update_attribute(attribute, value) so it should be:
#job.update_attribute(:isactive, true)
Also, the update_attribute method calls save on the passed object. Calling #job.save after update_attribute (provided there is no other code assigning attributes) is redundant.
A better way to write this method would be:
def approve
if #job.update_attribute(:isactive, true)
redirect_to(:action => :show)
flash[:notice] = "Job Approved"
else
render 'show'
end
end
However, if you cannot modify the model and are not worried about callbacks or validation, you can update the database column directly with update_column (or update_columns in Rails 4):
def approve
if #job.update_column(:isactive, true)
redirect_to(:action => :show)
flash[:notice] = "Job Approved"
else
render 'show'
end
end

In Rails when a resource create action fails and calls render :new, why must the URL change to the resource's index url?

I have a resource called Books. It's listed as a resource properly in my routes file.
I have a new action, which gives the new view the standard:
#book = Book.new
On the model, there are some attributes which are validated by presence, so if a save action fails, errors will be generated.
In my controller:
#book = Book.create
... # some logic
if #book.save
redirect_to(#book)
else
render :new
end
This is pretty standard; and the rationale for using render:new is so that the object is passed back to the view and errors can be reported, form entries re-filled, etc.
This works, except every time I'm sent back to the form (via render :new), my errors show up, but my URL is the INDEX URL, which is
/books
Rather than
/books/new
Which is where I started out in the first place. I have seen several others posts about this problem, but no answers. At a minimum, one would assume it would land you at /books/create, which I also have a view file for (identical to new in this case).
I can do this:
# if the book isn't saved then
flash[:error] = "Errors!"
redirect_to new_book_path
But then the #book data is lost, along with the error messages, which is the entire point of having the form and the actions, etc.
Why is render :new landing me at /books, my index action, when normally that URL calls the INDEX method, which lists all the books?
It actually is sending you to the create path. It's in the create action, the path for which is /books, using HTTP method POST. This looks the same as the index path /books, but the index path is using HTTP method GET. The rails routing code takes the method into account when determining which action to call. After validation fails, you're still in the create action, but you're rendering the new view. It's a bit confusing, but a line like render :new doesn't actually invoke the new action at all; it's still running the create action and it tells Rails to render the new view.
I just started with the Rails-Tutorial and had the same problem.
The solution is just simple: If you want the same URL after submitting a form (with errors), just combine the new and create action in one action.
Here is the part of my code, which makes this possible (hope it helps someone^^)
routes.rb (Adding the post-route for new-action):
...
resources :books
post "books/new"
...
Controller:
...
def create
#book = Book.new(book_params)
if #book.save
# save was successful
print "Book saved!"
else
# If we have errors render the form again
render 'new'
end
end
def new
if book_params
# If data submitted already by the form we call the create method
create
return
end
#book = Book.new
render 'new' # call it explicit
end
private
def book_params
if params[:book].nil? || params[:book].empty?
return false
else
return params.require(:book).permit(:title, :isbn, :price)
end
end
new.html.erb:
<%= form_for #book, :url => {:action => :new} do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :isbn %>
<%= f.text_field :isbn %>
<%= f.label :price %>
<%= f.password_field :price %>
<%= f.submit "Save book" %>
<% end %>
Just had the very same question, so maybe this might help somebody someday. You basically have to make 3 adjustments in order for this thing to work, although my solution is still not ideal.
1) In the create action:
if #book.save
redirect_to(#book)
else
flash[:book] = #book
redirect_to new_book_path
end
2) In the new action:
#book = flash[:book] ? Book.new(flash[:book]): Book.new
3) Wherever you parse the flash hash, be sure to filter out flash[:book].
--> correct URL is displayed, Form data is preserved. Still, I somehow don't like putting the user object into the flash hash, I don't think that's it's purpose. Does anyboy know a better place to put it in?
It doesn't land you at /books/new since you are creating resource by posting to /books/. When your create fails it is just rendering the new action, not redirecting you to the new action. As #MrYoshiji says above you can try redirecting it to the new action, but this is really inefficient as you would be creating another HTTP request and round trip to the server, only to change the url. At that point if it matters you could probably use javascript change it.
It can be fixed by using same url but different methods for new and create action.
In the routes file following code can be used.
resources :books do
get :common_path_string, on: :collection, action: :new
post :common_path_string, on: :collection, action: :create
end
Now you new page will render at url
books/common_path_string
In case any errors comes after validation, still the url will be same.
Also in the form instead using
books_path
use
url: common_path_string_books_path, method: :post
Choose common_path_string of your liking.
If client side validation fails you can still have all the field inputs when the new view is rendered. Along with server side errors output to the client. On re-submission it will still run create action.
In books_controller.rb
def new
#book = current_user.books.build
end
def create
# you will need to have book_params in private
#book = current_user.books.build(book_params)
if #book.save
redirect_to edit_book_path(#book), notice: "Book has been added successfully"
# render edit but you can redirect to dashboard path or root path
else
redirect_to request.referrer, flash: { error: #book.errors.full_messages.join(", ") }
end
end
In new.html.erb
<%= form_for #book, html: {class: 'some-class'} do |f| %>
...
# Book fields
# Can also customize client side validation by adding novalidate:true,
# and make your own JS validations with error outputs for each field.
# In the form or use browser default validation.
<% end %>

How can I use a method action like the 'update', 'create' or 'destroy' actions (from scaffold)?

I made these changes appropriately:
Now in 'app/controllers/users_controller.rb' I have this code:
def reset
respond_to do |format|
format.html # reset.html.erb
end
end
def send_reset
respond_to do |format|
if #user.errors.empty?
...
else
format.html { render :action => 'reset' }
end
end
end
Now in 'config/routes.rb' I have this code:
resources :users do
collection do
get 'reset'
post 'reset'
get 'send_reset'
post 'send_reset'
end
end
If I submit the form ('app/views/users/reset.html.erb') related to changes
<%= form_tag( send_reset_users_path, :method => :post ) do %>
<%= text_field_tag :email %>
<%= submit_tag("Send") %>
<% end %>
the browser URL become '.../users/send_reset' but I want '.../users/reset' (I think the problem is around the 'format.html { render :action => 'reset' }' code that does not render correctly the action) or, maybe, the route.
In few words I aim to use the 'send_reset' action like I use, for example, the 'update', 'create' or 'destroy' actions (from scaffold), in my case that is without creating the 'app/views/users/send_reset.html.erb' file but just calling the action method to handle my issue. How can I make this?
The default Rails URL scheme is
:host/:controller/:action/:id
Rename your controller action from
def send_reset
end
to
def reset
end
and rename the views and routes to match this change.
However you are already using reset for get and send_reset for Post but you want them to be the same just do different things if you ask for the page or send a form POST.
def reset
case request.method
when :post
# send them an email
# redirect_to :somewhere_else
else # :get, :put, :delete
# render the view with the recovery form
end
end

Is there a clean approach to implement acts_as_commentable in a Rails application?

The README does not show how to handle the controller and view aspects of setting up this plugin. I have been searching for a couple hours and can't find anything that shows how to use this plugin.
After even more searching, I gave up on finding a tutorial and came up with this. If anyone can point out a better / cleaner way to do this, please let me know. Otherwise, here is what I am using now in case this will benefit anyone else.
First, install the plugin with script/plugin install http://github.com/jackdempsey/acts_as_commentable.git -r 2.x
Then, generate the comment model and migration with script/generate comment and migrate the database with rake db:migrate
The tricky bit is nesting comments under other resources in a polymorphic way. Here is what I did:
# In config/routes.rb
map.resources :comments, :path_prefix => '/:commentable_type/:commentable_id'
# In app/controllers/comments_controller.rb
before_filter :load_commentable
def create
#comment = #commentable.comments.build(params[:comment])
#comment.user = current_user
respond_to do |format|
if #comment.save
format.html { redirect_to #commentable }
else
format.html { render :action => 'new' }
end
end
end
protected
def load_commentable
#commentable = params[:commentable_type].camelize.constantize.find(params[:commentable_id])
end
# In app/views/comments/_form.html.erb
<%= form_for(:comment, :url => comments_path(commentable.class.to_s.underscore, commentable.id)) do |f| %>
# In app/views/model_that_allows_comments/show.html.erb
<%= render :partial => 'comments/form', :locals => {:commentable => #model_that_allows_comments} %>
I think that shows the relevant parts clearly enough to understand what is happening. It makes it possible to add acts_as_commentable to any model. You just have to pass in the commentable object in the locals hash when rendering the comments form and the same comments controller / view code should work.
acts_as_commentable merely exposes you a Comment model and takes care of the plumbing between that model and your commentable models. It doesn't give you any controllers or views. You are responsible for deciding how you want to implement this part of your application.
It is pretty straightforward, though. For example...
# in routes.rb
map.resources :posts, :has_many => :comments
# in your comments controller...
class CommentsController < ApplicationController
before_filter :get_post
def get_post
#post = Post.find(params[:post_id])
end
def index
#comments = #post.comments.all # or sorted by date, or paginated, etc.
end
end
# In your haml view...
%h1== Comments for #{#post.title}:
%ul
- comments.each do |comment|
%h3= comment.title
%p= comment.comment
You'll see the comments for a particular post when you go to /posts/1/comments now.
I think the best way to add comments to any model is creating a method called comment in your ApplicationController.rb file like this.
def comment
# Extracts the name of the class
klass = self.class.to_s[/\A(\w+)sController\Z/,1]
# Evaluates the class and gets the current object of that class
#comentable_class = eval "#{klass}.find(params[:id])"
# Creates a comment using the data of the form
comment = Comment.new(params[:comment])
# Adds the comment to that instance of the class
#comentable_class.add_comment(comment)
flash[:notice] = "Your comment has been added!"
redirect_to :action => "show", :id => params[:id]
end
and then just create some partial _comment.rb to use it in any model you want
<%= form_tag :action => "comment", :id => Your_model_goes_here %>
<p><label for="comment_title">Title</label><br/>
<%= text_field 'comment', 'title' %></p>
<%= text_area "comment", "comment", :rows => 5, :cols => 50 %> <br />
<%= submit_tag "Comment!.." %>
</form>
I hope it's useful for someone...

Resources