2 instance variables of the same name in different controllers - ruby

I finished Michael Hartl's Ruby on Rails Tutorial. Now I'm working on the suggested exercises. The application he builds is basically a Twitter clone where one can post Microposts and they appear in your feed http://ruby.railstutorial.org/chapters/user-microposts#fig-micropost_created
The main page is in home.html.erb from the StaticPagesController and features a Micropost textbox where one can post Microposts. The code for the textbox looks like so:
<%= form_for(#micropost) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
The #micropost variable is initialized in the StaticPagesController like so:
class StaticPagesController < ApplicationController
def home
if signed_in?
#micropost = current_user.microposts.build
end
end
Now inside the MicropostsController there's a create action like so:
def create
#micropost = current_user.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
#feed_items = []
render 'static_pages/home'
end
end
My question is what is the purpose of the first #micropost variable as opposed to the second?
thanks,
mike

The first #micropost becomes available to the view rendered by the first controller method; the second #micropost becomes available to the view rendered by the second controller method. And it just so happens that the two methods are rendering the same view.
The only wrinkle is that since the second controller is conditional. If the create succeeds (passes validation and saves) then there's a redirect, so there's no proper view (although there will be in a moment, after the client-side redirect). But if it fails, then the view gets an object that contains the user-entered values as well as the validation errors which the view can then show to the user.

Related

Missing Params on Post in Rails?

I'm learning Ruby on Rails at the moment and we're making a blog app to learn about crud actions and such and I'm stuck on this create method in my controller not working as it does in the course. I'm having trouble the create method in this controller:
class ArticlesController < ApplicationController
def show
#article = Article.find(params[:id])
end
def index
#articles = Article.all
end
def new
end
def create
#article = Article.new(params.require(:article).permit(:title, :description))
#article.save
redirect_to #article
end
end
I get this error when trying to create an article:
ActionController::ParameterMissing in ArticlesController#create
param is missing or the value is empty: article
It seems to be getting hung up on the first line of the create method but I'm not sure why it doesn't think there's an article... Here's my new article view as well for further reference:
<h1>Create a new Article</h1>
<%= form_with scope: #article, url: articles_path, local: true do |f| %>
<p>
<%= f.label :title %><br/>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :description %><br/>
<%= f.text_area :description %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
Check whether your parameters coming under hash with key article like this -
{"article"=>{params}}
Also initiate Article object in new action.
You can redefine the params to permit like this:
in controller new method create object
#article = Article.new
and in form HTML add one more option use method: "post"

Not showing created blog entries

I'm fairly new to Rails and learning to create a blog using this tutorial. On step 10, once I define create and show, after creating a new post in browser I don't see any entries on show with id page. All I see is heading and and blank title and post header.
Following is my controller -
class PostController < ApplicationController
def index
end
def new
end
def create
#post = Post.new(params[:posts])
#post.save
redirect_to #post
end
def show
#post = Post.find(params[:id])
end
end
Show view ---
<h1>Show a post</h1>
<p>
<strong>Title:</strong>
<%= #post.title %>
</p>
<p>
<strong>Text:</strong>
<%= #post.text %>
</p>
Route ---
RailsBlog::Application.routes.draw do
resources :post
root :to => "post#index"
end
Form ---
<%= form_for :post, url: {action: 'create'} do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit 'Submit' %>
</p>
<% end %>
May be this is just a spelling mistake, but since I've recently started learning Rails, I'm unable to resolve this.
Update: I can go to particular id using
http://localhost:3000/post/1
but am only seeing blank page with view headers
The problem is here:
#post = Post.new(params[:posts])
It should be params[:post] - singular, not plural.
Also note that the best practice with form_for is to pass an object instead of symbol:
form_for #post do |f|
Then:
You don't need to specify url
You can reuse the same form for an edit action or create action (if object creation failed due to failing validation)
This however requires to initialize new object in your new action:
def new
#post = Post.new
end
UPDATE:
Also your routes are incorrect. When defining plural resources, you need to use plural form (it's more the convention than requirement, but since you're learning stick with it). So change your routes to:
resources :posts
And rename your controller to PostsController (remember to rename file name as well). restart the server and all should work.
ANOTHER UPDATE:
You also need to rename folder app/views/post to app/view/posts.
AND YET ANOTHER UPDATE:
In rails 4, you are not allowed to mass assign any params which has not been whitelisted using strong parameters. You need to tell rails which fields you allow to be assigned first - this is a security thing. You need to make some changes to your controller:
class PostsController < ApplicationController
...
def create
#post = Post.new(post_params)
...
end
...
private
def post_params
params.require(:post).permit(:title, :text)
end
end
This is the way to tell your controller that you are expecting those attributes from your form and they can be safely assigned.
I had just similar problem on the same tutorial.
The code spelling was correct and clearly accorded to examples in tutorial and BroiSatse's answer above.
The mistake was in order of private method definition.
How it was:
...
private
def post_params
params.require(:post).permit(:title, :text)
end
def show
#post = Post.find(params[:id])
end
...
The working order:
def show
#post = Post.find(params[:id])
end
private
...
Anyway, this topic was rather helpful. Thak you for your answers!

Ruby render another controller partial

I have a controller and in the index view i am trying to render a partial from another controller. Here is the other controller:
class ModelController < ApplicationController
def view
#model = Model.where('equipment_id' => params[:equipment_id])
render :partial => 'view_model'
end
end
And in the index view I am trying this:
<div id ="mdl-inner_box" class="inner_box">
<%= render "model/view", :#model => Model.first %>
</div>
And my partial view is this:
<% #model.each do |m| %>
<div> <%= m.name %> </div>
<% end %>
I am getting this error:
ActionView::MissingTemplate at /equipment
Missing partial model/view with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :b
What is the correct way to do this?
Thanks
There are too many errors to really address this question!
First of all if you are showing code for your index action, why is it the view action?
I have a controller and in the index view...
class ModelController < ApplicationController
def view
# should be
def index
In your index view, presumably app/views/models/index.html.erb you render a partial, which points to app/views/model/_view.html.erb. That should probably be models
<%= render "model/view", :#model => Model.first %>
<!-- should be -->
<%= render "models/view", :model => #model %>
And set #model in your controller. You can put Model.first into the view itself, but I think that breaks MVC pattern a little bit.
Finally in the partial, you access the class variable #model defeating the purpose of passing it as a local. You also call .each on it, which is a method for looping through collections, not for individual records. For example you would do #models.each and never #model.each.
<% #model.each do |m| %>
<div><%= m.name %></div>
<% end %>
<!-- should be something like -->
<%= model.name %>
Long story short, seems to me you're pretty lost, so I would start with a good book about Ruby on Rails before you continue. Here's a free one: http://ruby.railstutorial.org/ruby-on-rails-tutorial-book?version=4.0

instance vs local variables in a partial

I'm currently going through Michael Hartl's tutorial Ruby on Rails Tutorial http://ruby.railstutorial.org/ruby-on-rails-tutorial-book. I'm confused about where certain partial variables come from. In his tutorial he creates Users and Microposts. A User can create a Micropost on his main page (called a Feed) and have them posted there. The layout looks like this http://ruby.railstutorial.org/chapters/user-microposts#fig:proto_feed_mockup. Now the User model looks like this (I'm not posting the entire thing):
class User < ActiveRecord::Base
has_many :microposts, dependent: :destroy
def feed
Micropost.where("user_id = ?", id)
end
end
The Micropost model looks like this:
class Micropost < ActiveRecord::Base
belongs_to :user
end
In the text the author says that the feed method inside the User model can be written equivalently like this:
def feed
microposts
end
Why are they the same?
My next questions have to do with partials. On the user's show page (show.html.erb) the _microposts.html.erb is called with this if I'm not mistaken:
<%= render #microposts %>
_microposts.html.erb looks like this:
<li>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
data: { confirm: "You sure?" },
title: micropost.content %>
<% end %>
</li>
My question here is where is the micropost variable come from? Is it the same as the #micropost variable which calls this partial?
Now on the users home page (home.html.erb) there is a call to the _feed.html.erb partial like this:
<%= render 'shared/feed' %>
_feed.html.erb looks like this:
<% if #feed_items.any? %>
<ol class="microposts">
<%= render partial: 'shared/feed_item', collection: #feed_items %>
</ol>
<%= will_paginate #feed_items %>
<% end %>
I know where #feed_items comes from. It's set in a controller. Now _feed_item.html.erb looks like this:
<li id="<%= feed_item.id %>">
<%= link_to gravatar_for(feed_item.user), feed_item.user %>
<span class="user">
<%= link_to feed_item.user.name, feed_item.user %>
</span>
<span class="content"><%= feed_item.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(feed_item.created_at) %> ago.
</span>
<% if current_user?(feed_item.user) %>
<%= link_to "delete", feed_item, method: :delete,
data: { confirm: "You sure?" },
title: feed_item.content %>
<% end %>
</li>
So a similar question is where does the variable feed_item come from and what does it contain?
thanks,
mike
Ok, let's see. This is a lot of questions in one go, but...
Why is 'feed' equivalent to 'microposts'?
This is Rails' associations at work. When you use has_many to describe an association, Rails creates a whole bunch of methods based on the association name. In this case, you say that User has_many :microposts, which, among others, creates a User#microposts method.
The instance variable used in the render call (#microposts) is presumably set in a controller action. When you call render in this fashion (with an array of ActiveRecord objects), Rails looks for a partial with a name matching the class name of those objects. In this case, they're MicroPost objects, so it looks for a partial named _micropost and renders it once for each object in the array. When rendering a partial, the object that the partial is associated with can be referred to using a local variable with the same name as the partial. Since this is the _micropost partial, the local micropost variable refers to the object it's rendering.
Once again, the local variable with the same name as the partial refers to the object the partial is rendering. #feed_items is a collection, and for each object in it, you get one rendering of the _feed_item partial, in which the feed_item local variable refers to that object.
Because a user's microposts are associated using has_many, and internally, the relationship is based on the user's id. Getting them "by hand" does essentially the same thing, but with more work.
micropost comes from convention–Rails creates it for you. I don't know what you mean by "the #micropost variable which calls this partial".
Same answer, although it's based explicitly on the template name (IIRC) rather than a singularized name. It contains a single one of whatever #feed_items contains.

Rails3 routing error with ajax

UPDATED CODE at the bottom
I am creating a story voting app via Simply Rails 2 book. I am getting this error when I click the button to vote up a story:
No route matches "/stories/4-pure-css-icons-showcase"
My routing file looks like this:
Shovell::Application.routes.draw do
get "votes/create"
root :to => "stories#index"
resources :stories do
resources :votes
end
end
votes_controller.rb:
class VotesController < ApplicationController
def create
#story = Story.find(params[:story_id])
#story.votes.create
end
end
create.rsj :
page.replace_html 'vote_score', "Score: #{#story.votes.size}"
page[:vote_score].visual_effect :highlight
show.html.erb:
<h2>
<span id="vote_score">
Score: <%= #story.votes.size %>
</span>
<%= #story.name %>
</h2>
<p>
<%= link_to #story.link, #story.link %>
</p>
<div id="vote_form">
<%= form_tag :url => story_votes_path(#story), :remote => true do %>
<%= submit_tag 'shove it' %>
<% end %>
</div>
story.rb :
class Story < ActiveRecord::Base
validates_presence_of :name, :link
has_many :votes
def to_param
"#{id}-#{name.gsub(/\W/, '-').downcase}"
end
end
I've been working through a number of other errors before this having to do with deprecated code and so forth, so I feel somewhat lost at the moment. It seems like it should just be a routing a issue, but since I've been working through AJAX errors that also have to do with the vote function I wanted to post those files just in case it was more than routing.
It says no route matches "/stories/4-pure-css-icons-showcase" but when I visit "/stories" (my root) and click on the link to take me to "/stories/4-pure-css-icons-showcase" it works fine, however after clicking on the vote button I get this error. As you could probably tell after reading the code, it is suppose to update the vote count and do a :highlight via ajax.
UPDATE:
Changed code (all changes are per Sam's advice):
routes:
Shovell::Application.routes.draw do
resources :votes
root :to => "stories#index"
resources :stories do
resources :votes
end
show.html.erb:
<div id="vote_form">
<%= form_tag :url => new_story_vote_path(#story), :remote => true do %>
<%= submit_tag 'shove it' %>
<% end %>
</div>
votes_controller.rb
class VotesController < ApplicationController
def create
#story = Story.find(params[:story_id])
#story.votes.create
respond_to do |format|
format.html
format.js
end
end
The problem is still exactly the same, but I think (read: hope) we are making progress!
The scenario: My index (/stories) page randomly displays a story from the database, when you click the link it takes you to the story's internal page (ex. /stories/2-sitepoint-forums) on this page it displays the number of votes the story has and has a button to vote for it. When you click the vote button it is suppose to use ajax to update the #story.vote.size and use a :highlight visual effect. However, the problem is that when you click the vote button the page changes to a "Routing Error" page which displays:
No route matches "/stories/2-sitepoint-forums"
Its weird to me because you can in fact be routed to that address and you are from the link on the first page...
Here is the error in the console:
Started POST "/stories/2-sitepoint-forums?url=%2Fstories%2F2-sitepoint-forums%2F
votes%2F2-sitepoint-forums&remote=true" for 127.0.0.1 at 2010-11-08 16:30:17 -08
00
ActionController::RoutingError (No route matches "/stories/2-sitepoint-forums"):
Rendered C:/Ruby192/lib/ruby/gems/1.9.1/gems/actionpack-3.0.0.rc2/lib/action_dis
patch/middleware/templates/rescues/routing_error.erb within rescues/layout (1.0m
s)
Im not sure if this is any more telling, but I thought I'd add it incase.
New:
I have not been able to solve this problem as of yet. Because I still don't feel like I completely understand the issue I have decided to move over to the Ruby on Rails 3 Tutorial Book online and see if I can't figure it out while working through it. Since I was planning to do it next anyway (I have plans to combine both apps later) it appears now is the time.
<%= form_tag :url => new_story_vote_path(#story), :remote => true do %>
<%= submit_tag 'shove it' %>
<% end %>
That should send it to the create action.
def create
#story = Story.find(params[:story_id])
#story.votes.create
respond_to do |format|
format.html
format.js
end
end
And that should take care of your ajax.
take this get "votes/create" out of your routes
and add this
map.resources :votes
I'm not familiar with using form_tag and :remote (as I normally just write jQuery for stuff like this) but a couple of things definitely pop out at me with what you're doing here that may help you resolve the issue.
First of all, I think you can rework the way you set up a vote and thus the way you set up the form for the vote. In the stories controller, for the show action, I'd set up the vote right away:
#vote = Vote.new(:story_id => #story.id)
This lets you set up your form as so:
= form_for(#vote), :remote => true do |f|
= hidden_field f.story_id
= submit_tag "Vote"
This is both a cleaner way of doing things, in my opinion, but also may fix the general issue you are dealing with, because you are now passing data with the form (the hidden field) in your POST request. Rails will behave unexpectedly if you perform AJAX POST requests that do not actually submit data.
In other words, your original form is likely running as an AJAX POST request but it would have worked better as an AJAX GET request, since it is not actually submitting data, it is simply "hitting" an URL.
I am not sure if you found the answer to your problem yet, but I wanted to post for others that may be looking for an answer similar to yours.
The code:
<%= form_tag :url => new_story_vote_path(#story), :remote => true do %>
<%= submit_tag 'shove it' %>
<% end %>
will result in /stories/:story_id/votes/new url with a :post request. It won't work because the new route is a :get method request. If you wanted to go to the new method, you'll need to tell the form to use the get http method.
<%= form_tag :url => new_story_vote_path(#story), :remote => true, :html => { :method => :get } do %>
<%= submit_tag 'shove it' %>
<% end %>
However, I think that you are wanting to route to the create method in your controller. I would do something like:
<%= form_tag :url => story_votes_path(#story), :remote => true do %>
<%= submit_tag 'shove it' %>
<% end %>
This should route correctly to the create method in your VotesController.

Resources