Simple Form on Submit is not sending the input to MySQL - ruby-on-rails

So as you all can see in the screen shot below in my blog form when I click submit when creating a new blog the parameters get sent but never actually get inserted into the MySQL command. Which you can see the post in the background is empty except for the Timestamp.
Now with that being said this same form is used for my Edit Blog page. My form is a partial rendered in the new blog page and the edit blog page. So if I go edit that same blog page that was created with no input up submit of the edit's the parameters actually go through and then the blog post will show will all the information from your edits.
class PostsController < ApplicationController
before_action :find_post, only: [:edit, :update, :show, :delete]
# Index action to render all posts
def index
#posts = Post.all
end
# New action for creating post
def new
#post = Post.new
end
# Create action saves the post into database
def create
#post = Post.new
if #post.save(post_params)
flash[:notice] = "Successfully created post!"
redirect_to post_path(#post)
else
flash[:alert] = "Error creating new post!"
render :new
end
end
# Edit action retrives the post and renders the edit page
def edit
end
# Update action updates the post with the new information
def update
if #post.update_attributes(post_params)
flash[:notice] = "Successfully updated post!"
redirect_to post_path(#post)
else
flash[:alert] = "Error updating post!"
render :edit
end
end
# The show action renders the individual post after retrieving the id
def show
end
# The destroy action removes the post permanently from the database
def destroy
if #post.destroy
flash[:notice] = "Successfully deleted post!"
redirect_to posts_path
else
flash[:alert] = "Error updating post!"
end
end
private
def post_params
params.require(:post).permit(:strain, :straintype, :style, :light, :temp, :breeder, :private, :notes, :nutrients)
end
def find_post
#post = Post.find(params[:id])
end
end
I just don't understand why the edit saves correctly but no the initial submit.

I think maybe it should be .create(post_params) instead of .new
http://guides.rubyonrails.org/active_record_basics.html#create
Rails: Difference between create and new methods in ActiveRecord?

Related

Reroute to users show page

When I create an event on the users show page I want it to redirect back to the users show page if it indeed submitted correctly. Is there anyway I can do that here is my Events controller:
class EventsController < ApplicationController
def show
#event = Event.find(params[:id])
#songs = #event.songs.paginate(page: params[:page])
end
def create
#event = current_user.events.build(event_params)
if #event.save
flash[:success] = "Event Created!"
redirect_to 'user#show'
else
render 'welcome#index'
end
end
def destroy
end
private
def event_params
params.require(:event).permit(:name, :partycode)
end
end
Redirect to the user path and pass in the User ID.
redirect_to user_path(current_user.id)
Yes you can, but you'll need to use the actual path eg instead of redirect_to 'user#show' user:
redirect_to user_path(#event.user)

Rails 4 form_for nested resources issue

I have researched similar questions however I don't feel link they have addressed my particular issue:
Rails form_for results in POST instead of PUT when trying to edit
form_for with nested resources
I'm a novice with Rails (using Rails 4.2.5) an am attempting my first application. My issue is two fold: (1) When a user goes to edit a user story the fields of the form do not populate with previously inputted data (2) When the form is resubmitted, a new entry is created, opposed to editing the old data.
I have a feeling that my form_for for user_stories/edit.html.erb is the issue. When I take out the .build method from the form I get the following error message:
undefined method `to_key' for #UserStory::ActiveRecord_Associations_CollectionProxy:0x007f456a759138>
The projects/_form.html.erb for my project's view does not have the .build method and functions correctly. However the only way I can get the `user_stories/_form.html.erb form to work is if I attach the build method.
Here is my code:
user_story.rb
class UserStory < ActiveRecord::Base
belongs_to :project
belongs_to :user
include RankedModel
ranks :row_order
end
project.rb
class Project < ActiveRecord::Base
has_many :user_stories
belongs_to :user
end
routes.rb
Rails.application.routes.draw do
devise_for :users
resources :projects do
resources :user_stories
end
end
resources :user_stories do
post :update_row_order, on: :collection
end
root 'welcome#index'
end
user_stories/_form.html.erb
<%= form_for([#project, #user_story.build]) do |f| %>
<div class="form-group">
<p>As a ...</p>
<%= f.text_field :param1, placeholder: "type of user", class: "form-control" %>
</div>
<div class="form-group">
<p>I want ...</p>
<%= f.text_field :param2, placeholder: "desired functionality", class: "form-control" %>
</div>
<div class="form-group">
<p>so that...</p>
<%= f.text_field :param3, placeholder: "reason for desired functionality", class: "form-control" %>
</div>
<div class="actions">
<%= f.submit class: "btn btn-primary" %>
</div>
<% end %>
user_stories_controller.rb
class UserStoriesController < ApplicationController
before_action :set_project
before_action :set_user_story, except: [:create]
def index
#user_story = #project.user_stories.rank(:row_order).all
end
def update_row_order
#user_story.row_order_position = user_story_params[:row_order_position]
#user_story.save
render nothing:true # this is a POST action, updates sent via AJAX, no view rendered
end
def create
#user_story = #project.user_stories.create(user_story_params)
redirect_to #project
end
def new
end
def destroy
if #user_story.destroy
flash[:success] = "User story deleted"
else
flash[:error] = "User story could not be deletd"
end
redirect_to #project
end
def complete
user_story.update_attribute(completed_at, Time.now)
redirect_to #project, notice: "User story completed functionality complete"
end
def update
respond_to do |format|
if #project.user_stories.update(#project, user_story_params)
format.html { redirect_to project_path(#project), notice: 'User story was successfully updated.' }
format.json { render :show, status: :ok, location: #user_story }
else
format.html { render :edit }
format.json { render json: #user_story.errors, status: :unprocessable_entity }
end
end
end
def edit
#project = Project.find(params[:project_id])
#user_story = #project.user_stories(params[:id])
end
def show
end
private
def set_project
#project = Project.find(params[:project_id])
end
def set_user_story
#user_story = #project.user_stories(params[:id])
end
def user_story_params
params[:user_story].permit(:param1, :param2, :param3, :row_order_position)
end
end
There are just a few changes needed (tweaks, really), and I'll go through them top-to-bottom.
1) before_action :set_user_story
This will use the param[:id] to find the proper #user_story model object and automatically make it available to the proper methods. In this case it's being excepted for :create, but should also exclude other methods that don't have an :id in the route. Use this instead:
before_action :set_user_story, except: [:index, :new, :create]
This will solve (or prevent) some annoying and persistent ActiveRecord failures.
2) The index action
In this method, the name of the variable is non-standard by Rails naming conventions. The variable is currently singular, but represents a list of UserAction model object, which typically uses a plural name. Use this, instead:
def index
#user_stories = #project.user_stories.rank(:row_order).all
end
This change will cause a break in the app/views/user_stories/index.html.erb view, where any use of the #user_story variable would need to be changed to #user_stories. Keeping with naming conventions has many immediate and long-term benefits, so it's worth making the extra effort to change this to be consistent.
Note: the index action typically doesn't have a singular model object to work with, as this action is used to provide a list of the model objects.
3) The new action
The new action is used to create and initialize a new model object for editing. As the before_action :set_user_story is no longer being called for the new action, the #user_story model object has to be created here. This code will do that correctly:
def new
#user_story = UserStory.new
#user_story.project = #project
# Set other important default values for display now
end
And at this point, you should be able to successfully create a new UserStory model object, ready to be edited by the user.
4) The edit action
As the before_action :set_user_story handler is already being called for the edit action, there's no need to query for #user_story from within the body of the edit action; that line can be removed:
def edit
#project = Project.find(params[:project_id])
end
This will actually fix the original issue that was reported, as this form of find will (unfortunately for this situation) return multiple records, which means that you get a collection back, and not a single record. This is the actual cause of this error message:
undefined method `to_key' for #UserStory::ActiveRecord_Associations_CollectionProxy:0x007f456a759138>
Assigning the #user_story within the edit action overwrote the value that had previously been assigned from the before_action handler, and replaced it with an improper query result.
5) The complete action
The complete action is a custom member action, which means that it depends on the :id, just like many of the other actions. The code is almost correct, except that the user_story variable used within the body of the method is actually missing the #; this is originally retrieved by the before_action handler.
def complete
#user_story.update_attribute(completed_at, Time.now)
redirect_to #project, notice: "User story completed functionality complete"
end
It's likely that this method had not been called yet during testing, as the edit action was an upstream test that failed. This should work when you get to testing this method.
6) Teh codez
Changing those few details will finalize the UserStoriesController, which was in pretty great shape to begin with. Adding in those changes, this is the final controller code:
class UserStoriesController < ApplicationController
before_action :set_project
before_action :set_user_story, except: [:index, :new, :create]
def index
#user_stories = #project.user_stories.rank(:row_order).all
end
def update_row_order
#user_story.row_order_position = user_story_params[:row_order_position]
#user_story.save
render nothing:true # this is a POST action, updates sent via AJAX, no view rendered
end
def create
#user_story = #project.user_stories.create(user_story_params)
redirect_to #project
end
def new
#user_story = UserStory.new
#user_story.project = #project
# Set other important default values for display now
end
def destroy
if #user_story.destroy
flash[:success] = "User story deleted"
else
flash[:error] = "User story could not be deleted"
end
redirect_to #project
end
def complete
#user_story.update_attribute(completed_at, Time.now)
redirect_to #project, notice: "User story completed functionality complete"
end
def update
respond_to do |format|
if #project.user_stories.update(#project, user_story_params)
format.html { redirect_to project_path(#project), notice: 'User story was successfully updated.' }
format.json { render :show, status: :ok, location: #user_story }
else
format.html { render :edit }
format.json { render json: #user_story.errors, status: :unprocessable_entity }
end
end
end
def edit
#project = Project.find(params[:project_id])
end
def show
end
private
def set_project
#project = Project.find(params[:project_id])
end
def set_user_story
#user_story = #project.user_stories(params[:id])
end
def user_story_params
params[:user_story].permit(:param1, :param2, :param3, :row_order_position)
end
end

Rails beginner - How to make this API accept JSON?

I am new to rails and am trying to connect my rails api for an article (consists of two fields - title and description) to an iOS application. I am trying to send JSON data in a POST request and then save that data in a database. I am wondering how to make my articles_controller accept and save json data.
Here is my code so far:
class ArticlesController < ApplicationController
before_action :set_article, only: [:edit, :update, :show, :destroy]
def index
#all_articles = Article.all
end
def new
#article = Article.new
end
def edit
end
def create
#article = Article.new(article_params)
if #article.save
flash[:notice] = "Article was sucessfully created"
redirect_to article_path(#article)
else
render 'new'
end
end
def show
end
def update
if #article.update(article_params)
flash[:notice] = "Article was successfully updated."
redirect_to article_path(#article)
else
render 'edit'
end
end
def destroy
#article.destroy
flash[:notice] = "Article was successfully deleted."
redirect_to(articles_path)
end
private
def set_article
#article = Article.find(params[:id])
end
def article_params
params.require(:article).permit(:title, :description)
end
end
The above code is able to submit a form on the webpage and save it to my database. I would like to alter it to accept json data from an iOS application and save to the database.
Thank you very much for any help
Make a route for POST method:
In config/routes.rb
match 'accept_post' => 'articles#accept_post', :via => :post
In Articles Controller
def accept_post
// get parameters as usual (like params[:title] etc.)
// do the saving to db
end
Important Note: Make the url publicly accessible. But do your own authentication, so others cannot POST to it.
You can use your existing action in controller (like create) if you want, just wanna give you an idea about it.
You may need to parse the JSON in controller:
require 'json'
JSON.parse(<json object>)
JSON.parse(response.body)

Updating TopicsController to allow a moderator to update topics, but not create or delete

I'm in the process of creating a website similar to Reddit. I would like to allow a moderator to be able to update a topic, but not be able to create or delete topic. I'm aware that I need to update TopicsController but I'm not sure how. My main problem is that I'm not sure how to make the code specific enough to ensure that a moderator can only update; not delete or create a topic, as an admin can.
My current code looks like this:
class PostsController < ApplicationController
before_action :require_sign_in, except: :show
before_action :authorize_user, except: [:show, :new, :create]
def show
#post = Post.find(params[:id])
end
def new
#topic = Topic.find(params[:topic_id])
#post = Post.new
end
def create
#post.body = params[:post][:body]
#topic = Topic.find(params[:topic_id])
#post = #topic.posts.build(post_params)
#post.user= current_user
if #post.save
flash[:notice] = "Post was saved"
redirect_to [#topic, #post]
else
flash[:error] = "There was an error saving the post. Please try again."
render :new
end
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
#post.assign_attributes(post_params)
if #post.save
flash[:notice] = "Post was updated."
redirect_to [#post.topic, #post]
else
flash[:error] = "There was an error saving the post. Please try again."
render :edit
end
end
def destroy
#post = Post.find(params[:id])
if #post.destroy
flash[:notice] = "\"#{#post.title}\" was deleted successfully."
redirect_to #post.topic
else
flash[:error] = "There was an error deleting the post."
render :show
end
end
private
def post_params
params.require(:post).permit(:title, :body)
end
def authorize_user
post = Post.find(params[:id])
unless current_user == post.user || current_user.admin?
flash[:error] = "You must be an admin to do that."
redirect_to [post.topic, post]
end
end
end
I've already added a moderator role to the enum role.
I apologise if this seems really basic...but it has got me stumped!
Thanks in advance!
I could answer with some custom solution, but it's better to use a more structured and community-reviewed approach: authorization with cancan.
As tompave noticed you can use cancan gem for this.
Personally I prefer pundit.
In old days I used to define permissions directly in code everywhere: in controllers, in views and even models. But it's really bad practice. When your app grows, you are lost: you update a view, but you should make the same change in controller and sometimes in model too. It soon becomes absolutely unmanageable and you have no idea what your users can and cannot do.
Pundit, on the other hand, offers central place -- policy -- for defining what user can do. Views and controllers can then use those policies.
For example, if you need to define Post's policy you simply create app/policies/post_policy.rb file:
class PostPolicy
attr_reader :user
attr_reader :post
def initialize(user, post)
#user = user
#post = post
end
def author?
post.user == user
end
def update?
author? || user.admin? || user.moderator?
end
def create?
author? || user.admin?
end
def destroy?
author? || user.admin?
end
# etc.
end
Now whenever you need to check user's ability to perform action, you can simply invoke:
# in controller
def update
#post = Post.find(params[:id])
authorize #post
# do whatever required
end
# in view
<% if policy(post).update? %>
<%= link_to 'Edit Post', post_edit_path(post) %>
<% end %>
As you can see Pundit is very easy to comprehend and it uses the same "convention over configuration" approach as Rails. At the same time it's very flexible and allows you to test virtually anything.
You will definitely need Pundit or any similar gem to manage permission in your ambitious app.

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.

Resources