RoR NoMethodError in Posts#new - ruby-on-rails

So I've been building an app using rails and the google datastore. I keep encountering an error in my new.html.erb file where I get the NoMethodError.
I've scoured looking for a solution to my problem and it's most likely a simple typo somewhere in my code. I've been staring at the code for hours so maybe some fresh new eyes can help me figure it out
This is my posts_controller.rb
class PostsController < ApplicationController
PER_PAGE = 10
def index
#post, #cursor = Post.query limit: PER_PAGE, cursor: params[:cursor]
end
def new
#post = Post.new
end
def create
#post = Post.new post_params
if #post.save
flash[:success] = "Posted"
redirect_to posts_path(#post)
else
render :new
end
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def show
#post = Post.find(params[:id])
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :body)
end
end
This is my new.html.erb
<h1>Create Post</h1>
<%= form_for #post do |f| %>
<% if #post.errors.any? %>
<% #post.errors.full_messages.each do |msg| %>
<div class="alert alert danger"><%= msg %></div>
<% end %>
<% end %>
<div class="form-group">
<%= f.label:title %><br>
<%= f.text_field(:title, {:class => 'form-control'}) %>
</div>
<div class="form-group">
<%= f.label:body %><br>
<%= f.text_area(:body, {:class => 'form-control'}) %>
</div>
<p>
<%= f.submit({:class => 'btn btn-primary'}) %>
</p>
<% end %>
This is my model post.rb
class Post
attr_accessor :title, :body
# Return a Google::Cloud::Datastore::Dataset for the configured dataset.
# The dataset is used to create, read, update, and delete entity objects.
def self.dataset
#dataset ||= Google::Cloud::Datastore.new(
project: Rails.application.config.
database_configuration[Rails.env]["dataset_id"]
)
end
# Query Book entities from Cloud Datastore.
#
# returns an array of Book query results and a cursor
# that can be used to query for additional results.
def self.query options = {}
query = Google::Cloud::Datastore::Query.new
query.kind "Post"
query.limit options[:limit] if options[:limit]
query.cursor options[:cursor] if options[:cursor]
results = dataset.run query
posts = results.map {|entity| Book.from_entity entity }
if options[:limit] && results.size == options[:limit]
next_cursor = results.cursor
end
return posts, next_cursor
end
# [START from_entity]
def self.from_entity entity
post = Post.new
post.id = entity.key.id
entity.properties.to_hash.each do |name, value|
post.send "#{name}=", value if post.respond_to? "#{name}="
end
post
end
# [END from_entity]
# [START find]
# Lookup Book by ID. Returns Book or nil.
def self.find id
query = Google::Cloud::Datastore::Key.new "Post", id.to_i
entities = dataset.lookup query
from_entity entities.first if entities.any?
end
# [END find]
# [START validations]
# Add Active Model validation support to Book class.
include ActiveModel::Validations
validates :title, presence: true
# [END validations]
# Save the book to Datastore.
# #return true if valid and saved successfully, otherwise false.
def save
if valid?
entity = to_entity
Post.dataset.save entity
self.id = entity.key.id
true
else
false
end
end
end
routes.rb
Rails.application.routes.draw do
get 'auth/:provider/callback', to: 'sessions#create'
get 'auth/failure', to: redirect('/')
get 'signout', to: 'sessions#destroy', as: 'signout'
resources :sessions, only: [:create, :destroy]
resource :main, only: [:show]
resources :posts
root to: 'posts#index', as: "home"
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
EDIT: This is the error log that I'm getting
ActionView::Template::Error (undefined method `to_key' for #<Post:0x2ae2c68>
Did you mean? to_query):
1: <h1>Create Post</h1>
2: <%= form_for #post do |f| %>
3: <% if #post.errors.any? %>
4: <% #post.errors.full_messages.each do |msg| %>
5: <div class="alert alert danger"><%= msg %></div>
app/views/posts/new.html.erb:2:in `_app_views_posts_new_html_erb__241418705_22466964'

Your Post Model is a plain ruby object but you treat it like an ActiveModel/ActiveRecord object.
Try adding
include ActiveModel::Model inside the Post model like so:
class Post
include ActiveModel::Model
...
end

You have defined #posts in new method but you are using #post in the new.html.erb. Which is the reason for the error. Keep the same name either #posts or #post

Related

"form_with cannot find the Post model"

I am trying to create the form on my "new" page. I am using form_with. I have already checked my routes, controllers and views, but I have not yet identified the problem.
The error that is returning is:
NameError in Pages # new
undefined local variable or method `post 'for # <# <Class: 0x00007f5640e1f440>: 0x00007f5640e1c060>
Did you mean? #post
Extracted source (around line # 5):
<% = form_with (model: post, location: true) do | form | %>
<div class = "form-group">
<% = form.label: title%>
Below is my form using form_with:
<h1> CREATE NEW ARTICLE </h1>
<% = form_with (model: post, location: true) do | form | %>
<div class = "form-group">
<% = form.label: title%>
<% = form.text_field: title, class: 'form-control'%>
</div>
<div class = "form-group">
<% = form.label: author%>
<% = form.text_field: author, class: 'form-control'%>
</div>
<div class = "form-group">
<% = form.label: body%>
<% = form.text_area: body, class: 'form-control', rows: 10%>
</div>
<% = form.submit class: 'btn btn-primary', data: {disable_with: 'Creating ..'}%>
<% end%>
this is my controller:
class PagesController <ApplicationController
def articles
#posts = Post.all
end
def new
#post = Post.new
end
def show
#post = Post.find (params [: id])
end
def create
#post = # Post.new (post_params)
# post.save
redirect_to article_path (#post)
end
private
def post_params
params.require (: post) .permit (: title,: author,: body)
end
end
Now here are my routes:
Rails.application.routes.draw do
root 'pages # index'
get 'articles', to: 'pages # articles'
get 'articles / new', to: 'pages # new'
get 'articles /: id', to: 'pages # show', as: 'article'
post 'articles', to: 'pages # create'
end
Replace
<% = form_with(model: post, location: true) do | form | %>
.....
<% end %>
with
<% = form_with(model: post, local: true) do | form | %>
....
<% end %>
and new.html.erb should contains
<%= render 'form', post: #post` %>
And update your controller with posts_controller.rb
class PostsController < ApplicationController
before_action :find_post, only: [:show]
def index
#posts = Post.all
end
def new
#post = Post.new
end
def show
end
def create
#post = Post.new(post_params)
#post.save
redirect_to #post
end
private
def find_post
#post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :author, :body)
end
end
Routes should be as
get 'posts', to: 'posts#index'
get 'posts/new', to: 'posts#new'
get 'posts/:id', to: 'posts#show'
post 'posts', to: 'posts#create'
or simply as
resources :posts, only: [:index, :new, :show, :create]
There's quite a lot of whitespace issues going on in your code snippets, but I think that might be a side effect of however you've copied the code rather than any deliberate cause.
The error you're seeing is because the form_with method doesn't know what post is. In your controller, you set up #post in your new method, which means that new.html.erb will be able to see the instance variable #post, but it won't know what post (without the #) is.
So if the form is in new.html.erb, you'd need to use #post in place of post here.
That said, common Rails behaviour is to have the form in its own partial _form.html.erb. That might be the case here (you don't specify the file names of your views). In that case, you can tell the partial to use local variables rather than the instance variable that the main template file uses.
To do that, you need to map an instance variable to a local one in the render call to your form, for example:
# in new.html.erb
<%= render 'form', post: #post` %>
This is saying 'take the object that I can see as #post, and make it available as the post local variable within the form template'.

In Rails, show for nested resources not working

I am using nested resources. When I click on a goal that's been created, I keep getting this error:
param is missing or the value is empty: goal
And it directs me to the "params.require..." line:
private
def goal_params
params.require(:goal).permit(:text)
end
I'm not sure what's causing this. I can create and show the list. But when I click on a goal I get this error. I'm new to rails and I'm at my wit's end.
My view:
<h1>Listing goals</h1>
<table>
<tr>
<th>Text</th>
<th></th>
</tr>
<% #user.goals.each do |goal| %>
<tr>
<td><%= link_to goal.text, user_goals_path(#user, #goal)%></td>
</tr>
<% end %>
</table>
<h2>Add a comment:</h2>
<%= form_for([#user, #user.goals.build]) do |form| %>
<p>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
My controller:
class GoalsController < ApplicationController
def index
#user = User.find(params[:user_id])
#goal = #user.goals.find(goal_params)
end
def show
#user = User.find(params[:user_id])
#goal = #user.goals.find(:id)
#also tried goal_params and :goal_id instead of :id
end
def new
#user = User.find(params[:user_id])
#goal = #user.goals.new
end
def create
#user = User.find(params[:user_id])
#goal = #user.goals.build(goal_params)
#goal.user = current_user
if #goal.save
redirect_to new_user_goal_path, notice: "Success!~"
else
redirect_to new_user_goal_path, alert: "Failure!"
end
#to root_path
end
private
def goal_params
params.require(:goal).permit(:text)
end
end
My routes:
Rails.application.routes.draw do
devise_for :users
resources :user do
resources :goals
end
devise_scope :user do
authenticated :user do
root 'home#index', as: :authenticated_root
end
unauthenticated do
root 'devise/sessions#new', as: :unauthenticated_root
end
end
end
My show.html.erb:
<p>
<strong>Text:</strong>
<%= #goal.text %>
</p>
First thing, in the show
#goal = #user.goals.find(:id)
Should be
#goal = #user.goals.find(params[:id])
You said you tried with #user.goals.find(goal_params) in show action and I see it in your index action also. This will call the goal_params method, which require params[:goal] while your index or show request does not send to server, only when you submit the form, you will have that params. This is the cause of your error.
Second thing, your index should use
#goals = #user.goals
INSTEAD OF
#goal = #user.goals.find(goal_params)
Also, the strong parameters is used for create and update actions only to avoid mass assignment to our database. It's not used to find a record.

Error Found NoMethodError in Posts#show

So I'm working on a social web app using cloud datastore and ruby on rails. I've completed the crud operations for the posting aspect of the app and I'm trying to crud operations for my comment aspect. I'm running into an error when I try to render my comments form in the individual post page. I get an error and I think it has something to do with either one of my controllers or my erb files. I've been looking at for a long time now so hopefully someone can shed some light on it. I tried this solution I found, but it didn't work. link
For reference, I'm basically following the Google Datastore Bookshelf Tutorial App. BookShelf Tutorial App
Here is my posts controller
class PostsController < ApplicationController
PER_PAGE = 10
def index
#post, #cursor = Post.query limit: PER_PAGE, cursor: params[:cursor]
end
def new
#post = Post.new
end
def create
#post = Post.new post_params
if #post.save
flash[:success] = "Posted"
redirect_to posts_path(#post)
else
render :new
end
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update post_params
flash[:success] = "Updated Book"
redirect_to posts_path(#post)
else
render :edit
end
end
def show
#post = Post.find params[:id]
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :body)
end
end
Here is my comments controller
class CommentsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(comment_params)
redirect_to post_path(#post)
end
def destroy
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to post_path(#post)
end
private
def comment_params
params.require(:comment).permit(:body)
end
end
Comment model
require "google/cloud/datastore"
class Comment
include ActiveModel::Model
attr_accessor :id, :body
# Return a Google::Cloud::Datastore::Dataset for the configured dataset.
# The dataset is used to create, read, update, and delete entity objects.
def self.dataset
#dataset ||= Google::Cloud::Datastore.new(
project: Rails.application.config.
database_configuration[Rails.env]["dataset_id"]
)
end
def to_entity
post.parent = dataset.key "Post"
entity = Google::Cloud::Datastore::Entity.new
entity.key = post
entity["body"] = body
entity
end
# [START from_entity]
def self.from_entity entity
comment = Comment.new
comment.id = entity.key.id
entity.properties.to_hash.each do |name, value|
comment.send "#{name}=", value if comment.respond_to? "#{name}="
end
comment
end
# [END from_entity]
# Save the book to Datastore.
# #return true if valid and saved successfully, otherwise false.
def save
if valid?
entity = to_entity
Book.dataset.save entity
self.id = entity.key.id
true
else
false
end
end
def persisted?
id.present?
end
Post Model
require "google/cloud/datastore"
class Post
include ActiveModel::Model
attr_accessor :id, :title, :body
# Return a Google::Cloud::Datastore::Dataset for the configured dataset.
# The dataset is used to create, read, update, and delete entity objects.
def self.dataset
#dataset ||= Google::Cloud::Datastore.new(
project: Rails.application.config.
database_configuration[Rails.env]["dataset_id"]
)
end
# Query Book entities from Cloud Datastore.
#
# returns an array of Book query results and a cursor
# that can be used to query for additional results.
def self.query options = {}
query = Google::Cloud::Datastore::Query.new
query.kind "Post"
query.limit options[:limit] if options[:limit]
query.cursor options[:cursor] if options[:cursor]
results = dataset.run query
posts = results.map {|entity| Post.from_entity entity }
if options[:limit] && results.size == options[:limit]
next_cursor = results.cursor
end
return posts, next_cursor
end
def to_entity
entity = Google::Cloud::Datastore::Entity.new
entity.key = Google::Cloud::Datastore::Key.new "Post", id
entity["title"] = title
entity["body"] = body
entity
end
# [START from_entity]
def self.from_entity entity
post = Post.new
post.id = entity.key.id
entity.properties.to_hash.each do |name, value|
post.send "#{name}=", value if post.respond_to? "#{name}="
end
post
end
# [END from_entity]
# [START find]
# Lookup Post by ID. Returns Post or nil.
def self.find id
query = Google::Cloud::Datastore::Key.new "Post", id.to_i
entities = dataset.lookup query
from_entity entities.first if entities.any?
end
# [END find]
# [START validations]
# Add Active Model validation support to Book class.
include ActiveModel::Validations
validates :title, presence: true
# [END validations]
# [START update]
# Set attribute values from provided Hash and save to Datastore.
def update attributes
attributes.each do |name, value|
send "#{name}=", value if respond_to? "#{name}="
end
save
end
# [END update]
# [START destroy]
def destroy
Post.dataset.delete Google::Cloud::Datastore::Key.new "Post", id
end
# [END destroy]
# Save the book to Datastore.
# #return true if valid and saved successfully, otherwise false.
def save
if valid?
entity = to_entity
Post.dataset.save entity
self.id = entity.key.id
true
else
false
end
end
def persisted?
id.present?
end
end
My show.html.erb file
<h1><%= #post.title %></h1>
<p><%= #post.body %></p>
<hr>
<%= link_to 'Edit', edit_post_path(#post), :class => 'btn btn-default' %>
<%= link_to 'Delete', post_path(#post),
method: :delete,
data: {confirm: 'Are You Sure?'},
:class => 'btn btn-danger' %>
<%= render 'comments/form'%>
This is the comments html erb file
<h3>Comments</h3>
<%= #post.comment.each do |comment| %>
<div class="well">
</p><%= link_to '[X]', [comment.post, comment],
method: :delete,
data: {confirm: 'Are You Sure?'} %></p>
</div>
<% end %>
This is the error log
ActionView::Template::Error (undefined method `comment' for #<Post:0xbb05498>):
1: <h3>Add Comment</h3>
2: <%= form_for([#post, #post.comment.build]) do |f| %>
3: <div class="form-group">
4: <%= f.label:body %><br>
5: <%= f.text_area(:body, {:class => 'form-control'}) %>
app/views/comments/_form.html.erb:2:in `_app_views_comments__form_html_erb__676677109_98008740'
app/views/posts/show.html.erb:12:in `_app_views_posts_show_html_erb___922952118_98050956'

NameError in PostsController#create

So I'm working on a web app using rails and the google cloud datastore. I'm relatively new at coding web apps. Most of the errors I'm running into come from trying to integrate the datastore with the rails MVC model. For further context most of my code is based of the Google Datastore Bookshelf tutorial found on the datastore information page. https://cloud.google.com/ruby/getting-started/using-cloud-datastore
So recently I ran into a NameError in PostsController#create when I was trying to save a post to the datastore database. I can't seem to find the cause of this issue so hopefully someone can be my new set of eyes or provide some insight.
Also if you know any places where I can find helpful documentation using datastore and rails. That would be appreciated
So this is my post_controller.rb file
class PostsController < ApplicationController
PER_PAGE = 10
def index
#post, #cursor = Post.query limit: PER_PAGE, cursor: params[:cursor]
end
def new
#post = Post.new
end
def create
#post = Post.new post_params
if #post.save
flash[:success] = "Posted"
redirect_to posts_path(#post)
else
render :new
end
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def show
#post = Post.find(params[:id])
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :body)
end
end
This is my model for the post post_rb
require "google/cloud/datastore"
class Post
include ActiveModel::Model
attr_accessor :title, :body
# Return a Google::Cloud::Datastore::Dataset for the configured dataset.
# The dataset is used to create, read, update, and delete entity objects.
def self.dataset
#dataset ||= Google::Cloud::Datastore.new(
project: Rails.application.config.
database_configuration[Rails.env]["dataset_id"]
)
end
# Query Book entities from Cloud Datastore.
#
# returns an array of Book query results and a cursor
# that can be used to query for additional results.
def self.query options = {}
query = Google::Cloud::Datastore::Query.new
query.kind "Post"
query.limit options[:limit] if options[:limit]
query.cursor options[:cursor] if options[:cursor]
results = dataset.run query
posts = results.map {|entity| Post.from_entity entity }
if options[:limit] && results.size == options[:limit]
next_cursor = results.cursor
end
return posts, next_cursor
end
# [START from_entity]
def self.from_entity entity
post = Post.new
post.id = entity.key.id
entity.properties.to_hash.each do |name, value|
post.send "#{name}=", value if post.respond_to? "#{name}="
end
post
end
# [END from_entity]
# [START find]
# Lookup Book by ID. Returns Book or nil.
def self.find id
query = Google::Cloud::Datastore::Key.new "Post", id.to_i
entities = dataset.lookup query
from_entity entities.first if entities.any?
end
# [END find]
# [START validations]
# Add Active Model validation support to Book class.
include ActiveModel::Validations
validates :title, presence: true
# [END validations]
# Save the book to Datastore.
# #return true if valid and saved successfully, otherwise false.
def save
if valid?
entity = to_entity
Post.dataset.save entity
self.id = entity.key.id
true
else
false
end
end
end
This is my new.html.erb file that i use to call the save method in datastore
<h1>Create Post</h1>
<%= form_for #post do |f| %>
<% if #post.errors.any? %>
<% #post.errors.full_messages.each do |msg| %>
<div class="alert alert danger"><%= msg %></div>
<% end %>
<% end %>
<div class="form-group">
<%= f.label:title %><br>
<%= f.text_field(:title, {:class => 'form-control'}) %>
</div>
<div class="form-group">
<%= f.label:body %><br>
<%= f.text_area(:body, {:class => 'form-control'}) %>
</div>
<p>
<%= f.submit({:class => 'btn btn-primary'}) %>
</p>
<% end %>
This is my routes.rb file
Rails.application.routes.draw do
get 'auth/:provider/callback', to: 'sessions#create'
get 'auth/failure', to: redirect('/')
get 'signout', to: 'sessions#destroy', as: 'signout'
resources :sessions, only: [:create, :destroy]
resource :main, only: [:show]
resources :posts
root to: 'posts#index', as: "home"
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
EDIT: Forgot to include the error message log
NameError (undefined local variable or method `to_entity' for #<Post:0x65b0d70>
Did you mean? to_key):
app/models/post.rb:72:in `save'
app/controllers/posts_controller.rb:16:in `create'
NOTE: The create.html.erb is blank so I choose not to include it
Okay, it looks like you are missing a to_entity method. Try adding something similar to this for your post model. I slightly modified the code from the Book documentation example here. You will want to read that entire file to see what their Book model looks like with all of the code.
def to_entity
entity = Google::Cloud::Datastore::Entity.new
entity.key = Google::Cloud::Datastore::Key.new "Post", id
entity["title"] = title
entity["body"] = body
entity
end

NoMethodError for new controller in Rails

Cities#new controller shows error involving cities_path, but I don't have it in any file nor in CitiesController. I checked all files, tried to restart the server but still nothing.
undefined method `cities_path' for #<#<Class:0x007f9e4c1cb348>:0x00000003836140>
Did you mean? city_path
CitiesController
class CitiesController < ApplicationController
def index
#cities = City.all
end
def show
find_city
end
def new
#city = City.new
end
def edit
find_city
end
def update
find_city
if #city.save
redirect_to city_path(#city)
else
render "edit"
end
end
def create
#city = City.new(city_params)
if #city.save
redirect_to index_path
else
render "new"
end
end
private
def find_city
#city = City.find(params[:id])
end
def city_params
params.require(:city).permit(:name, :icon_url)
end
end
Routes
get "/cities/new" => "cities#new", as: "new_city"
post "/index" => "cities#create"
get "/cities/:id" => "cities#show", as: "city"
get "/cities/:id/edit" => "cities#edit", as: "edit_city"
patch "/city/:id" => "cities#update"
Form (error is raised on first line)
<%= form_for #city do |f| %>
<% if #city.errors.any? %>
<div class="errors">
<ul>
<% city.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label "Name:" %>
<%= f.text_field :name, class: "form-control" %>
<%= f.label "Icon:" %>
<%= f.text_field :icon_url, class: "form-control" %>
<%= f.submit "PoĊĦalji" %>
<% end %>
When you use form_for #city, and #city is a new record, form_for will try to find a cities_path to POST the new attributes back to.
You should be using resources :cities in your routes file to automatically define the routes and their names. If you want to define a limited set of routes, you can use :only or :except:
resources :cities, only: %i(new create show edit update)
If you don't use resources, you either need to explicitly specify a path for your form_for call, or you need to provide a route named cities_path manually:
post "/index" => "cities#create", as: :cities
Note that index routes don't typically actually contain the word index, you should really just be posting to /cities, not /index.
post "/cities" => "cities#create", as: :cities

Resources