I set up a Rails toy to play with Twitter API, but already having challenges right from the start. My files look like this:
routes.rb
Rails.application.routes.draw do
devise_for :users
root to: 'accounts#index'
resources :accounts do
resources :posts
end
end
account.rb
class Account < ActiveRecord::Base
belongs_to :user
has_many :posts
accepts_nested_attributes_for :posts
validates :name, :description, presence: :true
end
post.rb
class Post < ActiveRecord::Base
belongs_to :account
validates :tweet, presence: true
end
accounts_controller.rb
class AccountsController < ApplicationController
before_action :authenticate_user!
def index
#accounts = Account.all
#user = current_user
end
def new
#account = Account.new
end
def create
#account = Account.new(account_params)
#account.user = current_user
if #account.save
flash[:notice] = "Account succesfully created."
redirect_to #account
else
flash.now[:alert] = "Oops, something went wrong!"
render 'new'
end
end
def show
#account = Account.find(params[:id])
end
def update
end
def destroy
end
private
def account_params
params.require(:account).permit(:name, :description, posts_attributes: [:tweet])
end
end
post_controller.rb
class PostsController < ApplicationController
before_action :set_account
def index
#posts = Post.all
end
def new
#post = Post.new
end
def create
#post = #account.posts.build(post_params)
if #post.save
flash[:notice] = "Tweet created successfully."
redirect_to [#account, #post]
else
flash.now[:alert] = "Something went wrong."
render 'new'
end
end
def edit
#post = Post.find(params[:id])
end
def update
if #post.update
flash[:notice] = "Tweet updated."
redirect_to [#account, #post]
else
flash.now[:alert] = "Something is not right!"
render 'edit'
end
end
def destroy
end
def show
#post = Post.find(params[:id])
end
private
def post_params
params.require(:post).permit(:tweet)
end
def set_account
#account = Account.find(params[:account_id])
end
end
The tricky part is here:
What I am trying to do here is on the accounts page, when a user clicks on the account name, he should be redirects to the new action of the Posts controller that will allow him to create a new post for the account in question. Somehow, I am not sure how to pass the :account_id parameter.
views/accounts/index.html.erb
<table>
<tr>
<th>Account Name</th>
<th>Description</th>
<th>Tweets</th>
</tr>
<% #user.accounts.each do |account| %>
<tr>
<td><%= link_to account.name, new_account_post_path(#account) %></td>
<td><%= account.description %></td>
<td><%= account.posts.count %></td>
</tr>
<% end %>
</table>
<%= link_to "Sign out", destroy_user_session_path, :method => :delete %>
<%= link_to "Create new account", new_account_path %>
Error in browser:
You need to change
<%= link_to account.name, new_account_post_path(#account) %>
to
<%= link_to account.name, new_account_post_path(account) %>
Use this code:
<% #user.accounts.each do |account| %>
<tr>
<td><%= link_to account.name, new_account_post_path(account) %></td>
<td><%= account.description %></td>
<td><%= account.posts.count %></td>
</tr>
<% end %>
Related
Right now i am finishing my small learning project but I am sturggling with one thing. The funcionalities of the project are: register, login, logout for the Author; creating, editing, removing of the Events; attend, un-attend event.
Last thing what I want to implement is, to be able to see who is attending certain event. The functionality of attending / un-attending is already done (made a table Eventattendences with references to events and author).
The only thing which is missing is showing WHO is attending different event (that means to show Authors_key from the eventattendances table where events_id is equal in my show.html.erb). When i am trying to do that i always get an empty object from the SHOW method of eventattendacnes_controller.
Adding here all the code that should be related to this (if anything is missing just let me know and I will add it here). THANK YOU!
Versions: Rails(4.0.0), Ruby(2.3.3p222)
eventattendances_controller
class EventattendancesController < ApplicationController
before_action only: [:show, :edit, :update, :destroy]
before_filter :require_login
def new
if Eventattendance.exists?(events_id: params[:event_id].to_i, authors_id: current_user.id)
redirect_to event_path(params[:event_id])
else
#eventattendance = Eventattendance.new(events_id: params[:event_id].to_i, authors_id: current_user.id)
#eventattendance.save
redirect_to event_path(params[:event_id])
end
end
def destroy
if Eventattendance.exists?(events_id: params[:event_id].to_i, authors_id: current_user.id)
Eventattendance.where(events_id: params[:event_id].to_i, authors_id: current_user.id).first.destroy
redirect_to event_path(params[:event_id])
else
redirect_to event_path(params[:event_id])
end
end
def show
#eventattendances = Eventattendance.find(events_id= params[:event_id].to_i)
end
private
def author_params
params.require(:author).permit(:username, :email, :password,:password_confirmation)
end
def event_params
params.require(:event).permit(:title, :body)
end
end
eventattendances.rb (Model)
class Eventattendance < ActiveRecord::Base
belongs_to :author
belongs_to :event
end
routes.rb
Events::Application.routes.draw do
root to: 'events#index'
resources :events
resources :authors
resources :author_sessions, only: [ :new, :create, :destroy ]
resources :eventattendances, only: [:destroy, :new, :show]
get 'login' => 'author_sessions#new'
get 'logout' => 'author_sessions#destroy'
end
views/events/show.html.erb (here i want to set the show function from eventattendances to show the authors_id of the authors that are attending the event)
`<h1><%= #event.title %></h1>
<p><%= #event.body %></p>
<%= #at_list.blank? %>
<%= #at_list_att.blank? %>
<%=sql = "Select * from eventattendances"
at_list = ActiveRecord::Base.connection.execute(sql) %>
<% #event.eventattendances.map do |eventattendance| %>
<tr>
<td><%= eventattendance.author_id %></td>
</tr>
<% end %>
<%= link_to "<< Back to Event List", events_path %>
<p> </p>
<br>
<% if logged_in? %>
<%= link_to "edit", edit_event_path(#event) %>
<%= link_to "delete", event_path(#event), method: :delete, data: { confirm: "Are you sure?" } %>
<% end %>
<%= link_to "Cancel atendance", eventattendance_path(event_id: #event.id), method: :delete, class: "btn btn-primary" %>
<%= link_to "Attend", new_eventattendance_path(event_id: #event.id), class: "btn btn-success"%>
`
events_controller
class EventsController < ApplicationController
include EventsHelper
before_filter :require_login, except: [:index]
def index
#events = Event.all
end
def show
#event = Event.find(params[:id])
end
def new
#event = Event.new
end
def create
#event = Event.new(event_params)
#event.save
redirect_to event_path(#event)
end
def destroy
#event = Event.find(params[:id])
#event.destroy
redirect_to events_path
end
def edit
#event = Event.find(params[:id])
end
def update
#event = Event.find(params[:id])
#event.update(event_params)
redirect_to event_path(#event)
end
private
def event_params
params.require(:event).permit(:title, :body)
end
end
author.rb
`class Author < ActiveRecord::Base
authenticates_with_sorcery!
validates_confirmation_of :password, message: "should match confirmation", if: :password
has_and_belongs_to_many :events
end`
event.rb
class Event < ApplicationRecord
has_and_belongs_to_many :authors
end
In first place, if you are rendering the views/events/show you could do
<% #event.eventattendances.map do |eventattendance| %>
<tr>
<td><%= eventattendance.author_id %></td>
</tr>
<% end %>
to get the author_id.
But thinking better, maybe you are having a problem of relations and you need a
has and belongs to many association
The solution was to put it into the show method of events_controller.rb (instead of eventattendances_controller)
#eventattendances = Eventattendance.find_by(events_id: params[:id])
+
to change the arguments of the find:
from (events_id= params[:event_id]) to (events_id: params[:id])
(maybe it is going to help someone in the future)
I am receiving this error and I don't understand as I have defined the method in the comments controller, haven't I?
I am getting slightly confused to why it is not working.
Comments controller:
class CommentsController < ApplicationController
def create
#story = Story.find(params[:story_id])
#comment = #story.comments.create(params[:comment].permit(:name, :body))
redirect_to root_path
end
end
Stories Controller:
class StoriesController < ApplicationController
before_action only: [:destroy, :show, :edit, :update]
def index
#stories = Story.order('created_at DESC')
end
def new
#story = current_user.stories.build
end
def create
#story = current_user.stories.build(story_params)
if #story.save
flash[:success] = "Your beautiful story has been added!"
redirect_to root_path
else
render 'new'
end
end
def edit
#story = Story.find(params[:id])
end
def update
#story = Story.find(params[:id])
if #story.update_attributes(params.require(:story).permit(:name, :description))
flash[:success] = "More knowledge, more wisdom"
redirect_to root_path
else
render 'edit'
end
end
def destroy
#story = Story.find(params[:id])
if #story.destroy
flash[:success] = "I think you should have more confidence in your storytelling"
redirect_to root_path
else
flash[:error] = "Can't delete this story, sorry"
end
end
def show
#stories = Story.all
end
private
def story_params
params.require(:story).permit(:name, :description)
end
end
Index.html.erb:
<p id="notice"><%= notice %></p>
<h1>This is a list of posts</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>User</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #stories.each do |story| %>
<tr>
<td><%= story.name %></td>
<td><%= story.description %></td>
<td><%= story.user.username %></td>
<td><%= link_to 'Show', story %></td>
<% if user_signed_in? %>
<td><%= link_to 'Edit', edit_story_path(story)%></td>
<td><%= link_to 'Destroy', story_path(story),method: :delete,data: { confirm: 'Are you sure?' } %></td>
<% end %>
</tr>
</tbody>
</table>
<h2><%= #story.comments.count %>Comments</h2>
<%= render #story.comments %>
<h3>Add a comment</h3>
<%= render 'comments/form' %>
<%= link_to 'New Story', new_story_path %>
Story Controller:
class CommentsController < ApplicationController
before_action :set_comment, only: [:show, :edit, :update, :destroy]
# GET /comments
# GET /comments.json
def index
#comments = Comment.all
end
# GET /comments/1
# GET /comments/1.json
def show
#comments = #story.comments.all
#comment = #stroy.comments.build
end
# GET /comments/new
def new
#comment = Comment.new
end
# GET /comments/1/edit
def edit
end
# POST /comments
# POST /comments.json
def create
#story = Story.find(params[:story_id])
#story.comments.create(comment_params)
end
# PATCH/PUT /comments/1
# PATCH/PUT /comments/1.json
def update
respond_to do |format|
if #comment.update(comment_params)
format.html { redirect_to #comment, notice: 'Comment was successfully updated.' }
format.json { render :show, status: :ok, location: #comment }
else
format.html { render :edit }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
# DELETE /comments/1
# DELETE /comments/1.json
def destroy
#comment.destroy
respond_to do |format|
format.html { redirect_to comments_url, notice: 'Comment was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_comment
#comment = Comment.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def comment_params
params.require(:comment).permit(:user_name, :body, :story_id)
end
end
Make sure you have run the right migrations
rails g scaffold comment user_name:string body:text story:references
now
rake db:migrate
In your story model write
has_many :comments
And in your comment model make sure you have
belongs_to :story
or in your story controller in the show method have this
#comments = #story.comments.all
#comment = #stroy.comments.build
And now in your story form show views, something like this
<h3>Comments</h3>
<% #comments.each do |comment| %>
<div>
<p><%= comment.body %></p>
</div>
<% end %>
<%= render 'comments/form' %>
In your comment/_form.html.erb add
<%= f.hidden_field :story_id %>
If you want to display on your index edit you like so
<tbody>
<% #stories.each do |story| %>
<tr>
<td><%= story.name %></td>
<td><%= story.description %></td>
<td><%= story.user.username %></td>
<td><%= link_to 'Show', story %></td>
<% if user_signed_in? %>
<td><%= link_to 'Edit', edit_story_path(story)%></td>
<td><%= link_to 'Destroy', story_path(story),method: :delete,data: { confirm: 'Are you sure?' } %></td>
<% story.comments.each do |c| %>
<%= c.body %>
<% end %>
<% end %>
</tr>
</tbody>
You have to start by understanding how associations works:
One Story will have many comments.
You have to define the association in the model:
class Story < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :story
end
Then the methods in the controller will be available:
class CommentsController < ApplicationController
def create
#story = Story.find(params[:story_id])
#story.comments.create(comment_params)
end
end
And in your app/stories/show.html.erb view:
<% #story.comments.each do |comment| %>
<%= comment.body #or the comment content method %>
<% end %>
I currently have a DB in Ruby on Rails, however, I have been having trouble with the documentation on how to do much other than list all of the items in DB. I am still new to the this language as a whole, and wish I didn't need to ask for so much help, but here it goes. My pertinent code is as follows:
migrate/(DB name)
class CreateArticles < ActiveRecord::Migration
def change
create_table :articles do |t|
t.string :title
t.text :text
t.timestamps null: false
end
end
end
articles_controller.rb
class ArticlesController < ApplicationController
def index
#articles = Article.all
Article.search(params[:id])
end
def show
#article = Article.find(params[:search])
end
def new
#article = Article.new
end
def edit
#article = Article.find(params[:id])
end
def create
#article = Article.new(params.require(:article).permit(:title, :text))
if #article.save
redirect_to #article
else
render 'new'
end
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
article.rb
class Article < ActiveRecord::Base
validates :title, presence: true,
length: { minimum: 5 }
def self.search(search)
if search
#article = Article.where('name LIKE ?', "%#{search}%")
else
#article = Article.all
end
end
end
index.html.rb
<h1>Listing articles</h1>
<%= link_to 'New article', new_article_path %>
<table>
<tr>
<th>Title</th>
<th>Text</th>
<th colspan="3"></th>
</tr>
<% #articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
<td><%= link_to 'Show', article_path(article) %></td>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
<td><%= link_to 'Destroy', article_path(article),
method: :delete,
data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
<%= form_tag articles_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
</table>
Thanks for any help in advance!
Essentially your issue is that you're trying to set controller instance variables in a class method in your model.
class ArticlesController < ApplicationController
def index
#articles = Article.search(params[:search])
end
end
article.rb
class Article < ActiveRecord::Base
validates :title, presence: true,
length: { minimum: 5 }
def self.search(search)
if search.present?
Article.where('title LIKE ?', "%#{search}%")
else
Article.all
end
end
end
So now the class method does the search, returns the collection to the controller which assigns them to an instance variable for use in the view.
You have got your search form, made sure that is a GET one. Good. When you hit search you'll notice in your development log that there's a hit to articles#index and the browser will show the same as before. To make the search matter edit the index method in the articles controller.
def index
#articles = Article.all.search(params[:search])
end
In Article.search you have a name where you should have a title.
PS: You've got the show method a bit wrong.
notifications/index has <%= render partial: "notifications/notification", collection: #notifications %>, which contains:
<%= link_to "", notifications_habit_path(notification.id), method: :delete, class: "glyphicon glyphicon-remove" %>
<%= link_to Comment.find_by(notification.comment_id).user.name, user_path(Comment.find_by(notification.comment_id).user.id) %>
commented on <%= link_to "your habit", habit_path(notification) %>
which shows:
This is problematic because it should say 3x ".com commented on your habit" and 2x ".com commented on your value".
We need to create two separate partials notifications/_habits & notifications/_values.
My confusion is how to make the code know when to direct to the habit partial or the value partial based on whether it's a habit or value.
notifications_controller
def index
#habits = current_user.habits
#valuations = current_user.valuations #aka values
#notifications = current_user.notifications
#notifications.each do |notification|
notification.update_attribute(:read, true)
end
The notifications are based on if a user comments on one of your habits or values:
comment.rb
class Comment < ActiveRecord::Base
after_save :create_notification
has_many :notifications
belongs_to :commentable, polymorphic: true
belongs_to :user
validates :user, presence: true
private
def create_notification
Notification.create(
user_id: self.user_id,
comment_id: self.id,
read: false
)
end
end
I followed this tutorial but it is based on using just one model: http://evanamccullough.com/2014/11/ruby-on-rails-simple-notifications-system-tutorial/
UPDATE FOR VALADAN
class CommentsController < ApplicationController
before_action :load_commentable
before_action :set_comment, only: [:show, :edit, :update, :destroy, :like]
before_action :logged_in_user, only: [:create, :destroy]
def index
#comments = #commentable.comments
end
def new
#comment = #commentable.comments.new
end
def create
#comment = #commentable.comments.new(comment_params)
if #comment.save
redirect_to #commentable, notice: "comment created."
else
render :new
end
end
def edit
#comment = current_user.comments.find(params[:id])
end
def update
#comment = current_user.comments.find(params[:id])
if #comment.update_attributes(comment_params)
redirect_to #commentable, notice: "Comment was updated."
else
render :edit
end
end
def destroy
#comment = current_user.comments.find(params[:id])
#comment.destroy
redirect_to #commentable, notice: "comment destroyed."
end
def like
#comment = Comment.find(params[:id])
#comment_like = current_user.comment_likes.build(comment: #comment)
if #comment_like.save
#comment.increment!(:likes)
flash[:success] = 'Thanks for liking!'
else
flash[:error] = 'Two many likes'
end
redirect_to(:back)
end
private
def set_comment
#comment = Comment.find(params[:id])
end
def load_commentable
resource, id = request.path.split('/')[1, 2]
#commentable = resource.singularize.classify.constantize.find(id)
end
def comment_params
params[:comment][:user_id] = current_user.id
params.require(:comment).permit(:content, :commentable, :user_id, :like)
end
end
Your notification is associated with comment, and comment can have commentable of type Habit or Value (you havent show those two model, so lets call them Habit and Value models).
So you can check if notification is for Habit or Value by checking commentable type like this:
Comment.find_by(notification.comment_id).commentable.class == Habit
or check if its value notification:
Comment.find_by(notification.comment_id).commentable.class == Value
Similar way is checking polymorphic type on the comment, like:
Comment.find_by(notification.comment_id).commentable_type == 'Habit'
So on the end, you dont actualy need two partials just IF and two different link_to, one for value and one for habit.
<%= link_to "", notifications_habit_path(notification.id), method: :delete, class: "glyphicon glyphicon-remove" %>
<%= link_to Comment.find_by(notification.comment_id).user.name, user_path(Comment.find_by(notification.comment_id).user.id) %> commented on
<% if Comment.find_by(notification.comment_id).commentable.class == Habit %>
<%= link_to "your habit", habit_path(notification) %>
<% else %>
<%= link_to "your value", value_path(notification) %>
<% end %>
I needed
<% if notification.habit_id %>
<%= link_to "your habit", habit_path(notification) %>
<% elsif notification.valuation_id %>
<%= link_to "your value", valuation_path(notification) %>
<% elsif notification.quantified_id %>
<%= link_to "your stat", quantified_path(notification) %>
<% elsif notification.goal_id %>
<%= link_to "your goal", goal_path(notification) %>
<% end %>
and in the comment model:
def create_notification
Notification.create(
habit_id: self.habit_id,
valuation_id: self.valuation_id,
quantified_id: self.quantified_id,
goal_id: self.goal_id,
user_id: self.user_id,
comment_id: self.id,
read: false
)
end
I'm creating a website where users can post their items & services for sale (a classified ads site) and have setup the 'listing', 'category', and 'user' models. The listings and categories are associated with each other in a "has_many"-->"belongs_to" relationship, with categories owning listings.
However, even though the Listings are successfully associating with a Category upon creation of a new listing (I believe...?), the "show" page for Categories is not displaying it's associated listings; (displaying the "No listings to display" message"). What could be the problem?
-Here's my 'show' page for Categories:
<h1><%= #category.name %></h1>
<%= render partial: 'listings/list', locals: {
listings: #category.listings } %>
-and here's the controller for Categories:
class CategoriesController < ApplicationController
def show
#category = Category.find(params[:id])
end
end
-Here's the controller for Listings:
class ListingsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, except: [:create, :index, :new]
def index
#listings = Listing.all
end
def show
end
def new
#listing = Listing.new
end
def edit
end
def create
#listing = current_user.listings.build(listing_params)
if #listing.save
redirect_to #listing
flash[:success] = "Listing was successfully created."
else
render 'new'
end
end
def update
if #listing.update(listing_params)
flash[:success] = "Listing was successfully updated."
redirect_to #listing
else
render 'edit'
end
end
def destroy
#listing.destroy
flash[:success] = "Listing deleted."
redirect_to request.referrer || root_url
end
private
def listing_params
params.require(:listing).permit(:name, :description, :price, :image,
:category_id)
def correct_user
#listing = current_user.listings.find_by(id: params[:id])
redirect_to root_url if #listing.nil?
end
end
-This is listing partial referenced in the show page:
<% if #listings.nil? %>
No listings to display! Go <%= link_to 'create one', new_listing_path %>.
<% else %>
<table class="table table-striped">
<tbody>
<% #listings.each do |listing| %>
<tr>
<td><%= link_to listing.name, listing %></td>
<td class="text-right">
<% if listing.price %>
<%= number_to_currency(listing.price) %>
<% end %>
</td>
<td class="text-right">
<% #listings.each do |listing| %>
<% if listing.category %>
<%= link_to listing.category.name, listing.category %>
<% end %>
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
<% end %>
-The model file for listing:
class Listing < ActiveRecord::Base
belongs_to :user
belongs_to :category
default_scope -> { order('created_at DESC') }
validates :name, presence: true
validates :description, presence: true
validates :price, presence: true
validates :user_id, presence: true
mount_uploader :image, ImageUploader
end
-The model file for category:
class Category < ActiveRecord::Base
has_many :listings
end
<%= render partial: 'listings/list', locals: {
listings: #category.listings } %>
Makes listings avalable via listings var, not #listings, as in your template.
Just remove # sign.