Can't figure out what's going on as I try to add dirt simple blog capability to my website. Starter app I'm using is pre-configured for HAML and I'm an even bigger HAML n00b than Rails n00b, so I'm seriously struggling.
When I add this to my pages/home.html.haml:
%h1= I18n.t('brand.name')
%p
= I18n.t 'brand.name'
- #posts. each do |post|
= render 'posts/post', post: post
I get:
undefined method `each' for nil:NilClass
I can't figure it out... I thought Ruby had an "each" method built it? Why isn't it passing any class in?
Here's the _post.html.haml partial it's trying to render:
%p
%h2
= link_to post.title, post
%p
- if post.kind == 'image'
= image_tag post.content, style: "width: 100%"
- else
= simple_format post.content
%p.text-muted
%small
Posted on #{post.created_at.to_formatted_s(:long)}
And the controller:
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
def index
#posts = Post.all
end
def show
#post = Post.find(params[:id])
end
def new
#post = Post.new
end
def edit
end
def create
#post = Post.new(post_params)
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
else
format.html { render action: 'new' }
end
end
end
def update
respond_to do |format|
if #post.update(post_params)
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
else
format.html { render action: 'edit' }
end
end
end
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
#post = Post.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:title, :kind, :content)
end
end
And the model is empty, just:
class Post < ActiveRecord::Base
end
I copy/pasted most of this code from a working blog example I built in a tutorial and it works fine in that app.
I suspect it has something to do with PSQL (in this app) versus SQLite (in the example app). In rails console, trying it gives the same error:
[3] pry(main)> post = post.first
NoMethodError: undefined method `first' for nil:NilClass
[4] pry(main)> posts = post.each
NoMethodError: undefined method `each' for nil:NilClass
[5] pry(main)> posts = Post.each
NoMethodError: undefined method `each' for Post (call 'Post.connection' to establish a connection):Class
from /Users/troot/.rvm/gems/ruby-2.1.3/gems/activerecord-4.1.6/lib/active_record/dynamic_matchers.rb:26:in `method_missing'
I don't understand why the methods that I think should be working are not working here. Thanks so much for any help you can give.
In this line:
- #posts. each do |post|
there is a space between #posts and each. There should be no space in between as each is a method and you are explicitly calling it using the . operator on #posts object.
Then in console you are doing:
post = post.first
It should be:
post = Post.first
The class name should always be in capital.
And in the console if you need to run each then you need to do Post.all.each you can use each directly on class.
Try correcting this and then check. Hope this helps.
Related
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
I implemented multiple uploads with Carrierwave and I'm unable to update my post correctly. Here is my controller:
class PostsController < ApplicationController
before_action :find_posts, only: [:show, :edit, :update, :destroy, :upvote, :downvote]
before_action :authenticate_user!, except: [:index, :show, :home]
def home
end
def index
if params[:category].blank?
#posts = Post.all.order("created_at DESC")
else
#category_id = Category.find_by(name: params[:category]).id
#posts = Post.where(category_id: #category_id).order("created_at DESC")
end
end
def show
#inquiries = Inquiry.where(post_id: #post).order("created_at DESC")
#random_post = Post.where.not(id: #post).order("RANDOM()").first
#post_attachments = #post.post_attachments.all
end
def new
#post = current_user.posts.build
#post_attachment = #post.post_attachments.build
end
def create
#post = current_user.posts.build(post_params)
respond_to do |format|
if #post.save
params[:post_attachments]['image'].each do |a|
#post_attachment = #post.post_attachments.create!(:image => a)
end
format.html { redirect_to #post, notice: 'Post was successfully created.' }
else
format.html { render action: 'new' }
end
end
end
def update
if #post.update(post_params)
flash[:notice] = "Post successfully updated!"
redirect_to #post
else
flash[:notice] = "Something went wrong...give it another shot!"
render 'edit'
end
end
def edit
end
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
def upvote
#post.upvote_by current_user
redirect_to #post
end
def downvote
#post.downvote_by current_user
redirect_to #post
end
private
def find_posts
#post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :price, :description, :location, :category_name, :contact_number, :image)
end
end
Basically, the post is creating just fine, but when I attempt to update the images, the new images doesn't replace the old ones. I'm not sure how to go about changing my update to have it accept new images. NOTE: Other fields work when I update, just not my images.
What do I change in my controller to make it work? If you need to see my form or anything else, let me know, but I'm quite certain it's in the controller.
Since you said creating a post is working fine. But updating a post is not. Then after looking at your code, it seems as I expected, that your def update is missing some code that you have in your def create specifically the params[:post_attachments]['image'] part.
Just in case you do not know yet, def create is called when you click the submit button in the new-post form, while def update is called when you click the submit button in the edit-post form.
I get the following error, Couldn't find User with 'id'=
I have this in my Users_Controller,
def edit
#user = #signed_in_user
end
This is in my routes.rb,
root 'welcome#welcome'
get 'login' => 'sessions#login', :as => :login
get 'profile' => 'users#profile', :as => :profile
post 'logging/user' => 'sessions#create'
get 'logout' => 'sessions#destroy', :as => :logout
get 'about' => 'about'
resources :users
get 'register' => 'users#new', :as => :register
get 'edit' => 'users#edit', :as => :edit
This is in my application_controller.rb,
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :set_user
protected
def set_user
unless session[:user_id] == nil
#signed_in_user = User.find(session[:user_id])
end
end
end
This is in my Users_Controller
Here is my code from my User_Controller on creating the account
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /users
# GET /users.json
def index
#users = User.all
end
def profile
#user = User.find(session[:user_id]) unless session[:user_id] == ""
redirect_to login_path, notice: "You're not logged in" unless #user
end
# GET /users/1
# GET /users/1.json
def show
end
# GET /users/new
def new
#user = User.new
end
# GET /users/1/edit
def edit
#user = #signed_in_user
end
# POST /users
# POST /users.json
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# DELETE /users/1
# DELETE /users/1.json
def destroy
#user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:name, :password, :password_confirmation, :email, :age)
end
end
And this is the link that I use for my HTML,
<li role="presentation"><%= link_to 'Edit', edit_path %></li>
So, to start, a good practice when you get an 'Couldn't find' message is to check what instance variables are in your view.
So in a view, just type: <%= #user_id %> and see if anything shows up on your page, thus indicating if any user is even present! The other problem is that your instance variable might be <%= user.id %> but I am not sure as I can't see your code and how the user is stored in the database.
Second if you run rake routes, you generally find that the edit path will have a URI pattern like: "/edit(.:format)", meaning the route need "edit_path(#user.id)" rather than just "edit_path".
Let me know if this leads you anywhere or you have further questions and I hope I can answer them!
===========
Additional info:
Well without more code to look at, I would provide a few more suggestions...The goal is to have the <%= #user.id %> (or user_id) show up on the page somehow, thus telling you it is available.
The set_user method is an instance method, not a class method. To make it a class method, try def self.set_user. This invokes the method of the instance on the controller, thus making it a class method.
Make sure you have a session object to use. In the routes, it looks like post logging/user might be creating the session, but I am not sure.
Keep the edit_path(#user.id) or however the id is stored for the user as the route rather than just edit_path. I am pretty sure if you run 'rake routes', it will tell you that an additional variable needs to be passed for the link to work
Use the gem byebug Here is the link: https://github.com/deivid-rodriguez/byebug. You get this error while the edit page or where ever you are getting the error write in the action byebug. As you have mentioned in the console it shows a arrow pointing at a specific line in the application, the last line should appear as this (byebug), here write the variable in which you are getting the user id. If we take an example of your application controller in the set_user method:
def set_user
byebug
unless session[:user_id] == nil
#signed_in_user = User.find(session[:user_id])
end
end
In the console after (byebug) write session[:user_id] so this will give you the value of the session[:user_id]. So if this is null then you have a problem here or just follow the same procedure to check anywhere else.
Also there is one more thing you can do to learn is just create a new project or use the existing one and generate a scaffold which will give you options of show, edit, index. It will generate all the views, controller code, migration and everything. You can do that like this:
rails generate scaffold User email:string password:string
You can add more fields if you want. And then in your application just visit http://localhost:[port_no]/users which will by default take you to index page where you can add new users, edit existing ones. This will teach you about everything. It would be like a reference code for you. Read more at: http://guides.rubyonrails.org/command_line.html#rails-generate
And being more specific to users only there is a gem named Devise which will give you all the required things like sign_in, sign_up, session_management for users. Hope these things help you with your issue.
Edit:
Here is a very good tutorial link which will help you: https://www.railstutorial.org/book/updating_and_deleting_users#sec-updating_users
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.
I'm having an issue where my article_controller.rb's create method is redirecting to the index when the article.save fails due to invalid input by the user. The articles creation url is /articles/new but when the submit fails, I'm redirected to /articles. The form is still available in /articles exactly as it was on /articles/new. The desired behavior would be to return to the /articles/new with whatever the user may have entered repopulated in the form. Is there a way to do this? Here are some of the code snippets to illustrate what's going on.
Here is the article new method:
def new
#article = Article.new
respond_to do |format|
format.html
end
end
Here is the article create method:
def create
#article = current_user.articles.new(params[:article])
respond_to do |format|
if #article.save
format.html { redirect_to(#article, :notice => 'Article was successfully created.') }
else
format.html { render 'new' }
end
end
end
Here is the form:
<%= form_for(#article) do |f| %>
.....
<% end %>
I'm eventually hoping to get this working with a :remote => :true call in the form_for, but just want to get it working first the way it is. Any suggestions?
Try
format.html { render :action => "new" }
And if you are using Rails 3+, try writing your controller something like this DRY.
class ArticlesController < ApplicationController
respond_to :html
def new
#article = Article.new
respond_with #article
end
def create
#article = Article.new(params[:article])
#article.save
respond_with(#article)
end
end