So, I'm having trouble getting the Thumbs Up gem workin'.
I've followed this tutorial but I seem to be getting the below errror:
Error msg:
NoMethodError in Songs#index
Showing /Users/apane/Downloads/leap_stage/leap_stage/app/views/songs/index.html.erb where line #10 raised:
undefined method `vote_for_song_songs_path' for #<#<Class:0x007fd0b60203d0>:0x007fd0b26ecdc0>
<li><%= link_to song.title, song %><br></li>
Submitted <%= time_ago_in_words(song.created_at) + " ago" %>
<span class="comments"> | <%= pluralize(song.comments.size, 'comment') %></span><br />
<%=link_to image_tag('thumbs_up', :border => 0), vote_for_song_songs_path(#song), :remote => true %>
<%=link_to image_tag('thumbs_down', :border => 0), vote_against_song_songs_path(#song), :remote => true %>
index.html.erb
<div id="layout1">
<h3>Songs</h3>
<ol>
<% #songs.each do |song| %>
<li><%= link_to song.title, song %><br></li>
Submitted <%= time_ago_in_words(song.created_at) + " ago" %>
<span class="comments"> | <%= pluralize(song.comments.size, 'comment') %></span><br />
<%=link_to image_tag('thumbs_up', :border => 0), vote_for_song_songs_path(#song), :remote => true %>
<%=link_to image_tag('thumbs_down', :border => 0), vote_against_song_songs_path(#song), :remote => true %>
<%#= link_to 'Show', song, class: "button small secondary" %>
<%= link_to('Edit', edit_song_path(song), class: "button small secondary") if can? :update, #song %>
<%= link_to('Destroy', song, method: :delete, data: {confirm: 'Are you sure?'}, class: "button small secondary") if can? :destroy, #song %>
<% end %>
</ol>
</div>
<br />
</div>
song_controller.rb
class SongsController < ApplicationController
before_filter :authenticate_user!, only: [:create ,:edit, :update, :destroy, :vote_for_song]
before_action :set_song, only: [:show, :edit, :update, :destroy, :vote_for_song]
def vote_for_song
#song = Song.find(params[:id])
current_user.vote_for(#song)
respond_to do |format|
format.js
end
end
def vote_against_song
#song = Song.find(params[:id])
current_user.vote_against(#song)
respond_to do |format|
format.js
end
end
# GET /Songs
# GET /Songs.json
def index
#songs = Song.all
end
# GET /Songs/1
# GET /Songs/1.json
def show
#comment = Comment.new(song: #song)
end
# GET /Songs/new
def new
#song = Song.new
end
# GET /Songs/1/edit
def edit
end
# POST /Songs
# POST /Songs.json
def create
#song = Song.new(song_params)
respond_to do |format|
if #song.save
format.html { redirect_to #song, notice: 'Song was successfully created.' }
format.json { render action: 'show', status: :created, location: #song }
else
format.html { render action: 'new' }
format.json { render json: #song.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /Songs/1
# PATCH/PUT /Songs/1.json
def update
respond_to do |format|
if #song.update(song_params)
format.html { redirect_to #song, notice: 'Song was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #song.errors, status: :unprocessable_entity }
end
end
end
# Song /Songs/1
# Song /Songs/1.json
def destroy
#song.destroy
respond_to do |format|
format.html { redirect_to songs_url }
format.json { head :no_content }
end
end
private
def set_song
#song = Song.find(params[:id])
end
def song_params
params.require(:song).permit(:title, :artist, :bio, :track, :user_id)
end
end
song.rb
class Song < ActiveRecord::Base
acts_as_voteable
belongs_to :user
has_many :comments, :dependent => :destroy
has_attached_file :track,
:url => "/assets/songs/:id/:style/:basename.:extension",
:path => ":rails_root/public/assets/songs/:id/:style/:basename.:extension"
validates_attachment :track, :presence => true
validates :title, length: { minimum: 10 }
validates :bio, length: { maximum: 300 }
end
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :songs
has_many :comments
acts_as_voter
end
vote.rb
class Vote < ActiveRecord::Base
scope :for_voter, lambda { |*args| where(["voter_id = ? AND voter_type = ?", args.first.id, args.first.class.base_class.name]) }
scope :for_voteable, lambda { |*args| where(["voteable_id = ? AND voteable_type = ?", args.first.id, args.first.class.base_class.name]) }
scope :recent, lambda { |*args| where(["created_at > ?", (args.first || 2.weeks.ago)]) }
scope :descending, lambda { order("created_at DESC") }
belongs_to :voteable, :polymorphic => true
belongs_to :voter, :polymorphic => true
attr_accessible :vote, :voter, :voteable if ActiveRecord::VERSION::MAJOR < 4
# Comment out the line below to allow multiple votes per user.
validates_uniqueness_of :voteable_id, :scope => [:voteable_type, :voter_type, :voter_id]
end
routes.rb
Leap2::Application.routes.draw do
resources :comments
devise_for :users, controllers: {registrations: 'registrations'}
resources :songs
get '/contact', to: 'songs#contact'
get '/faq', to: 'songs#faq'
root to: 'songs#index'
end
Since you need an :id param, it should be get or put, and to avoid writing "song_songs" you can do
resources :songs do
member do
get :vote_for, :vote_against
end
end
That gets vote_for_song_path(#song) and vote_against_song_path(#song). Technically it would be more correct to have put :vote_for, since it's not an idempotent request, but then you'd need to remember to put method: :put at the end of your url.
Edit: to get the number of votes to show up, put an element in the page like
<span class="votes"><%= pluralize(song.votes.count, 'Vote') %></span>
right after the similar comments span. Also, since you have the vote_for_song_path called remotely, you'll want to update the page with js, which means you need to be able to find the spot on the page that has the number of votes for a song. Do do that, replace the <li> with
<%= content_tag_for :li, song do %>
[then put everything in your view that has to do with one song in the block]
<% end %>
That will generate html like <li id="song_21" class="song">..., which you can refer to in an update_votes.js.erb template,
$("#song_<%= #song.id %> .votes").html("<%= pluralize(song.votes.count, "Vote") %>")
That will send javascript to your view to update the proper element; you just have to instruct the controller to send it. I believe
format.js { render 'update_votes' }
in both the vote_for and vote_against actions will do it.
Related
I'm trying to implement add to favorites and I'm getting this error:
ActiveRecord::RecordNotFound in FavoriteItemsController#index
Couldn't find Item with 'id'=
this is where the error pops up, at the following link:
<%= link_to "Favorite Items", favorites_path, method: :get, :class => 'navbar-link' %>
model associations
class Favorite < ApplicationRecord
belongs_to :viewer
belongs_to :favorited, polymorphic: true
end
class Viewer < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :favorites
has_many :favorite_items, through: :favorites, source: :favorited, source_type: 'Item'
end
routes:
get '/favorites', to: 'favorite_items#index', as: 'favorites'
resources :favorite_items, only: [:create, :destroy]
and the add or remove favorites links on my show page:
<%- unless current_shopper.favorite_items.exists?(id: #item.id) -%>
<%= link_to 'Add to favorites', favorite_items_path(item_id: #item), method: :post %>
<%- else -%>
<%= link_to 'Remove from favorites', favorite_item_path(#item), method: :delete %>
<%- end -%>
the controller
class FavoriteItemsController < ApplicationController
before_action :set_item
def index
#favorites = current_viewer.favorites
end
def create
if Favorite.create(favorited: #item, viewer: current_viewer)
redirect_to #item, notice: 'Item has been favorited'
else
redirect_to #item, alert: 'Something went wrong...*sad panda*'
end
end
def destroy
Favorite.where(favorited_id: #item.id, viewer_id: current_viewer.id).first.destroy
redirect_to #item, notice: 'Item is no longer in favorites'
end
private
def set_item
#item = Item.find(params[:item_id] || params[:id])
end
and the index to loop thru the favored items, although not sure if this is correct since the error stops before the loop:
<% #favorites.each do |item| %>
<tr>
<td><%= item.title %></td>
<td><%= item.price %></td>
<td><%= link_to 'Remove from favorites', favorite_items_path(#item.id), method: :delete %></td>
< /tr>
<% end %>
Update 1
This is what I get when I click on add to favorites
Item Exists (4.3ms) SELECT 1 AS one FROM "items" INNER JOIN "favorites"
ON "items"."id" = "favorites"."favorited_id" WHERE "favorites"."viewer_id"
= $1 AND "favorites"."favorited_type" = $2 AND "items"."id" = $3 LIMIT $4
[["viewer_id", 2], ["favorited_type", "Item"], ["id", 4], ["LIMIT", 1]]
items_controller.rb
def create
#item = Item.new(item_params)
respond_to do |format|
if #item.save
format.html { redirect_to #item, notice: 'Item was successfully created.' }
format.json { render :show, status: :created, location: #item }
else
format.html { render :new }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
def show
#comments = Comment.where(item_id: #item).order("created_at DESC")
#items = Item.find(params[:id])
end
class FavoriteItemsController < ApplicationController
before_action :set_item, only: [:create, :destroy]
..
..
end
Index html
<%= link_to 'Remove from favorites',
favorite_items_path(#item.id), method: :delete %>
replace with
<%= link_to 'Remove from favorites', favorite_item_path(item.id),
method: :delete %>
Try replacing you FavoriteItemsController with
before_action :set_item, :only => [:create, :destroy]
I have problem with the comments.
If I comment a status whatever it is, the comment will be displayed on the last status.
Hope you could help, I can edit with more code if needed, let me know what you need.
_form.html.erb
<%= simple_form_for([status, status.comments.new]) do |f|%>
<%= f.input :content %>
<%= f.button :submit, "Comment", class:"btn btn-primary"%>
<% end %>
index.html.erb
<% #statuses.each do |status| %>
<%= image_tag status.user.avatar.thumb if status.user.avatar?%>
<%= status.user.full_name%>
<%= simple_format(status.content) %>
<%= link_to time_ago_in_words(status.created_at) + " ago", status %>
<% if status.user == current_user %>
<span class="admin">
<%= link_to "Edit", edit_status_path(status) %> |
<%= link_to "Delete", status, method: :delete, data: {confirm: "Are you sure?"} %>
</span>
<% end %>
<% status.comments.each do |comment| %>
<%= comment.content %>
<% end %>
<%= render partial: "comments/form", locals: {status: status} %>
<% end %>
routes.rb
Rails.application.routes.draw do
get 'profiles/show'
devise_for :users
devise_scope :user do
get 'register', to: 'devise/registrations#new', as: :register
get 'login', to: 'devise/sessions#new', as: :login
get 'logout', to: 'devise/sessions#destroy', as: :logout
end
resources :statuses do
resources :comments
end
resources :comments
get 'feed', to: "statuses#index", as: :feed
root "statuses#index"
get '/:id', to: "profiles#show"
end
statuses_controller.rb
class StatusesController < ApplicationController
before_filter :authenticate_user!, only: [:new, :create, :edit, :update]
before_action :set_status, only: [:show, :edit, :update, :destroy]
def index
#users = User.all
#statuses = Status.all
#comments = Comment.all
end
def show
#status = Status.find(params[:id])
#comments = #status.comments.all
end
def new
#status = Status.new
#comment = #status.comments.build
end
def create
#status = Status.new(status_params)
#status.user = current_user
respond_to do |format|
if #status.save
format.html { redirect_to #status, notice: 'Status was successfully created.' }
format.json { render :show, status: :created, location: #status }
else
format.html { render :new }
format.json { render json: #status.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #status.update(status_params)
format.html { redirect_to #status, notice: 'Status was successfully updated.' }
format.json { render :show, status: :ok, location: #status }
else
format.html { render :edit }
format.json { render json: #status.errors, status: :unprocessable_entity }
end
end
end
def destroy
#status.destroy
respond_to do |format|
format.html { redirect_to statuses_url, notice: 'Status was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_status
#status = Status.find(params[:id])
end
def status_params
params.require(:status).permit( :content)
end
end
comments_controller.rb
class CommentsController < ApplicationController
def create
#status = Status.find_by(params[:status_id])
#comment = #status.comments.create(params_comment)
#comment.save
redirect_to statuses_path
end
def index
#statuses = Status.all
#comments = Comment.all
#comment = Comment.find_by(params[:id])
end
def new
#status = Status.find(params[:status_id])
#comment = Comment.new
end
private
def params_comment
params.require(:comment).permit(:content)
end
end
my models
class Comment < ActiveRecord::Base
belongs_to :status
belongs_to :user
end
class Status < ActiveRecord::Base
belongs_to :user
has_many :comments
default_scope -> { order(created_at: :DESC)}
validates :content, presence: true,
length: {minimum: 2}
validates :user_id, presence: true
end
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
mount_uploader :avatar, AvatarUploader
validates_presence_of :avatar
validates_integrity_of :avatar
validates_processing_of :avatar
has_many :statuses
has_many :users
validates :first_name, presence: true
validates :last_name, presence: true
validates :profile_name, presence: true,
uniqueness: true,
format: {
with: /\A[a-zA-Z0-9_-]+\z/,
message: "Must be formatted correctly." }
def full_name
first_name + " " + last_name
end
def gravatar_url
stripped_email = email.strip
end
end
Thank you
This is when I commen as "Nelly Johan" here on my own status...
And this my comment not where I expected:
I am creating a gallery where you can upload multiple images via form.
The form is nested inside another form and when that is submitted it should create the gallery with the attached images inside it. When I check the output I get this which means its not submitting the correct images to the gallery object at all:
Parameters: {
"utf8"=>"✓",
"authenticity_token"=>"BJhkDx7rCRMIlXQ6T9FskXZe7+fdYxj0qm+VnCaC51w=",
"book"=>{
"jacket_cover" =>#<ActionDispatch::Http::UploadedFile:0x000001025d3128
#tempfile=#<File:/var/folders/mf/srx7jt8s2rdg0mn5hr98cvz80000gn/T/RackMultipart20140918-47766-1g9kyas>, #original_filename="559a7a477253d58f891f8e852162dfac.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"book[jacket_cover]\"; filename=\"559a7a477253d58f891f8e852162dfac.jpg\"\r\nContent-Type: image/jpeg\r\n">,
"title"=>"zd szw wrwr",
"synopsis"=>"<p>cfdcrgcgrere</p>\r\n",
"body"=>"<p>rccgregrrgerereg</p>\r\n",
"age"=>"19",
"publisher"=>"Dove books",
"author_attributes"=>{
"name"=>"zsdxfrrwg",
"biography"=>"<p>exffwfwefewewf</p>\r\n"},
"gallery_attributes"=>{
"images_attributes"=>{
"0"=>{
"file"=>[#<ActionDispatch::Http::UploadedFile:0x000001025d20e8 #tempfile=#<File:/var/folders/mf/srx7jt8s2rdg0mn5hr98cvz80000gn/T/RackMultipart20140918-47766-tk1rdb>, #original_filename="23ebb202a3655c6d0947251cce8625b6.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"book[gallery_attributes][images_attributes][0][file][]\"; filename=\"23ebb202a3655c6d0947251cce8625b6.jpg\"\r\nContent-Type: image/jpeg\r\n">, #<ActionDispatch::Http::UploadedFile:0x000001025d1ff8 #tempfile=#<File:/var/folders/mf/srx7jt8s2rdg0mn5hr98cvz80000gn/T/RackMultipart20140918-47766-j3ji7c>, #original_filename="559a7a477253d58f891f8e852162dfac.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"book[gallery_attributes][images_attributes][0][file][]\"; filename=\"559a7a477253d58f891f8e852162dfac.jpg\"\r\nContent-Type: image/jpeg\r\n">]
}
}
}
}, "commit"=>"Create Book"
}
The books_controller handles the submission of the gallery with the images in it:
class BooksController < ApplicationController
before_action :set_book, only: [:show, :edit, :update, :destroy]
before_filter :authenticate_user!, only: [:new, :edit, :update, :destroy]
# GET /books
# GET /books.json
def index
#books = Book.order('created_at DESC').all
end
# GET /books/1
# GET /books/1.json
def show
# #book = Book.find(params[:id])
#book = Book.friendly.find(params[:id])
#gallery = #book.gallery
end
# GET /books/new
def new
#book = Book.new
#book.build_author
#gallery = #book.build_gallery
#gallery.images.build
end
# GET /books/1/edit
def edit
end
# POST /books
# POST /books.json
def create
#raise params.inspect
#book = Book.new(book_params)
#binding.pry
respond_to do |format|
if #book.save
format.html { redirect_to #book, notice: 'Book was successfully created.' }
format.json { render action: 'show', status: :created, location: #book }
else
format.html { render action: 'new' }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /books/1
# PATCH/PUT /books/1.json
def update
respond_to do |format|
if #book.update(book_params)
format.html { redirect_to #book, notice: 'Book was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
end
# DELETE /books/1
# DELETE /books/1.json
def destroy
#book.destroy
respond_to do |format|
format.html { redirect_to books_url }
format.json { head :no_content }
end
end
# Get authors
# def reviews
# #book = Book.friendly.find(params[:id])
# #reviews = #movie.reviews
# respond_to do |format|
# format.html { render 'reviews/index' } # index.html.erb
# format.json { render json: #movies }
# end
# end
private
# Use callbacks to share common setup or constraints between actions.
def set_book
# #book = Book.find(params[:id])
#book = Book.friendly.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def book_params
params.require(:book).permit(:title, :synopsis, :body, :age, :publisher, :jacket_cover, author_attributes: [:name,:biography], gallery_attributes: [:name, :book_id ] )
end
end
book.rb
class Book < ActiveRecord::Base
has_attached_file :jacket_cover, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :jacket_cover, :content_type => /\Aimage\/.*\Z/
validates :jacket_cover, :title, :slug, :synopsis, :body, :age, :publisher, presence: true
validates_uniqueness_of :title
extend FriendlyId
friendly_id :title, use: [:slugged, :finders]
belongs_to :author
has_one :gallery
has_many :stories
accepts_nested_attributes_for :author
accepts_nested_attributes_for :gallery
scope :available, ->{ where(available: true) }
scope :unavailable, ->{ where(available: [nil, false]) }
end
image.rb
class Image < ActiveRecord::Base
belongs_to :gallery
has_attached_file :file, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :file, :content_type => /\Aimage\/.*\Z/
end
gallery.rb
class Gallery < ActiveRecord::Base
belongs_to :books
has_many :images
accepts_nested_attributes_for :books
accepts_nested_attributes_for :images, :allow_destroy => true
end
galleries_controller.rb
class GalleriesController < ApplicationController
before_action :set_gallery, only: [:show, :edit, :update, :destroy]
def index
#galleries = Gallery.all
end
def show
#gallery = Gallery.find(params[:id])
#images = #gallery.images
end
def new
#gallery = Gallery.new
##gallery.images.build
# #images = #gallery.build_images
# #gallery.images.build
end
def edit
#gallery.images.build
end
def create
#gallery = Gallery.new(gallery_params)
#image = Image.create()
##gallery.images.build
respond_to do |format|
if #gallery.save
format.html { redirect_to #gallery, notice: 'Gallery was successfully created.' }
format.json { render action: 'show', status: :created, location: #gallery }
else
format.html { render action: 'new' }
format.json { render json: #gallery.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #gallery.update(gallery_params)
format.html { redirect_to #gallery, notice: 'Gallery was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #gallery.errors, status: :unprocessable_entity }
end
end
end
def destroy
#gallery.destroy
respond_to do |format|
format.html { redirect_to galleries_url }
format.json { head :no_content }
end
end
private
def set_gallery
#gallery = Gallery.find(params[:id])
end
def gallery_params
params.require(:gallery).permit(:name, :book_id, :image)
#params.require(:gallery).permit(:name, :book_id, :images[], images_attributes: [:id, :file []])
#params.require(:gallery).permit(:name, :book_id, :images[images_attributes: [:file]])
# params.require(:gallery).permit(:name, :book_id, images_attributes: [:id, :image[:file]])
end
end
Added form in here that submits the gallery of images:
<%= simple_form_for(#book, :html => { :multipart => true } ) do |f| %>
<%= f.error_notification %>
<div class="inputs">
<div id="image" class="field">
<h3>Add the book image here</h3>
<div class="single-file">
<div class="file_upload">
<%= f.file_field :jacket_cover %>
</div>
</div>
</div>
<div id="title" class="field">
<h3>Add the title</h3>
<%= f.input :title %>
</div>
<div id="synopsis" class="field">
<h3>Add the book synopsis</h3>
<%= f.input :synopsis, :as => :ckeditor, :label => false, :input_html => { :ckeditor => { :toolbar => 'Full', :height => 400 } } %>
</div>
<div id="body" class="field">
<h3>Add the book body summary here</h3>
<%= f.input :body, :as => :ckeditor, :label => false, :input_html => { :ckeditor => { :toolbar => 'Full', :height => 400 } } %>
</div>
<div id="age" class="field">
<h3>Add the book age group</h3>
<%= f.input :age, collection: [['3-7', '3-7'],['7-11', '7-11'],['11-14', '11-14']], prompt: "Select age range" %>
</div>
<div id="publisher" class="field">
<h3>Who published the book</h3>
<%= f.input :publisher %>
</div>
<div id="school" class="field">
<h3>Schools reading this book (add the name and full address of the school)</h3>
<%= f.simple_fields_for :schools, :wrapper => 'inline' do |builder| %>
<%= render 'school_fields', :f => builder %>
<%= link_to_add_association 'add school', f, :schools, :render_options => {:wrapper => 'inline' }, :class => 'fa fa-plus' %>
<% end %>
</div>
</div>
<%#= f.select( :author_id, Author.all.map {|u| [u.name,u.id]}, {:include_blank => false, prompt: "No Author"} ) %>
<div id="author-inputs">
<h3>Add author</h3>
<%#= link_to 'New Author', new_author_path, :remote => true, :id => "new_author_link" %>
<%= f.simple_fields_for :author, :wrapper => 'inline' do |builder| %>
<%= render 'author_fields', :f => builder %>
<% end %>
</div>
<%= f.simple_fields_for :gallery do |builder| %>
<%= render 'galleries/form', :f => builder %>
<% end %>
<div class="actions">
<%= f.button :submit %>
</div>
<% end %>
When I check in the db console I cannot see any images inside the gallery when I search Gallery.all
I have created a gist file with all the necessary info in it.
Can anyone shed any light into this for me at all?
The main problem is with your parent form and model relationship.
In your book model you should have belongs_to :gallery instead of has_one :gallery. Also check your database. books Table should have gallery_id. As a database concept belongs_to should have parent table id. So model should be :
class Book < ActiveRecord::Base
has_attached_file :jacket_cover, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :jacket_cover, :content_type => /\Aimage\/.*\Z/
validates :jacket_cover, :title, :slug, :synopsis, :body, :age, :publisher, presence: true
validates_uniqueness_of :title
extend FriendlyId
friendly_id :title, use: [:slugged, :finders]
belongs_to :author
belongs_to :gallery
has_many :stories
accepts_nested_attributes_for :author
accepts_nested_attributes_for :gallery
scope :available, ->{ where(available: true) }
scope :unavailable, ->{ where(available: [nil, false]) }
end
and your gallery model should look like :
class Gallery < ActiveRecord::Base
has_many :books
has_many :images
accepts_nested_attributes_for :books
accepts_nested_attributes_for :images, :allow_destroy => true
end
Your parent model in form should be gallery model instead of book. And also make required changes in your gallery controller.
Here is the error I receive:
Here is a Gist of the files (some of you may find this easier to read):
https://gist.github.com/drichards2013/7902811
Here is index.html.erb:
<%= render 'pages/home' if !user_signed_in? %>
<div id="things" class="transitions-enabled">
<% #things.each do |thing| %>
<div class='panel panel default'>
<div class="box">
<%= link_to image_tag(thing.image.url(:medium)), thing %>
<div class='panel-body'>
<% if thing.link.blank? %>
<strong><%= thing.title %></strong>
<% else %>
<strong><%= link_to thing.title, "http://#{thing.link}"%></strong>
<% end %>
<p><%= thing.description %></p>
By <%= link_to thing.user.username, user_path(thing.user) %>
<% if thing.user == current_user %>
<%= link_to edit_thing_path(thing) do %>
<span class='glyphicon glyphicon-edit'></span>
<% end %>
<%= link_to thing_path(thing), method: :delete, data: { confirm: 'Are you sure?' } do %>
<span class='glyphicon glyphicon-trash'></span>
<% end %>
</div>
<% end %>
</div>
</div>
<% end %>
</div>
<%= will_paginate #posts, renderer: BootstrapPagination::Rails, class: 'pull-left' %>
Here is thing.rb:
class Thing < ActiveRecord::Base
belongs_to :user
default_scope -> { order('created_at DESC') }
has_attached_file :image, :styles => { :large => '500x500>', :medium => '300x300>', :thumb => '100x100>' }
validates :image, presence: true
validates :title, presence: true, length: { minimum: 5, maximum: 50 }
# Returns microposts from the users being followed by the given user.
def self.from_users_followed_by(user)
followed_user_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
where("user_id IN (#{followed_user_ids}) OR user_id = :user_id",
user_id: user.id)
end
end
class ThingsController < ApplicationController
before_action :set_thing, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
# GET /things
# GET /things.json
def index
#things = Thing.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 50)
end
# GET /things/1
# GET /things/1.json
def show
end
# GET /things/new
def new
#thing = current_user.things.build
end
# GET /things/1/edit
def edit
end
# POST /things
# POST /things.json
def create
#thing = current_user.things.build(thing_params)
respond_to do |format|
if #thing.save
format.html { redirect_to #thing, notice: 'Thing was successfully created.' }
format.json { render action: 'show', status: :created, location: #thing }
else
format.html { render action: 'new' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /things/1
# PATCH/PUT /things/1.json
def update
respond_to do |format|
if #thing.update(thing_params)
format.html { redirect_to #thing, notice: 'Thing was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# DELETE /things/1
# DELETE /things/1.json
def destroy
#thing.destroy
respond_to do |format|
format.html { redirect_to things_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_thing
#thing = Thing.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def thing_params
params.require(:thing).permit(:title, :description, :image, :link)
end
end
Here is things_controller.rb:
class ThingsController < ApplicationController
before_action :set_thing, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
# GET /things
# GET /things.json
def index
#things = Thing.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 50)
end
# GET /things/1
# GET /things/1.json
def show
end
# GET /things/new
def new
#thing = current_user.things.build
end
# GET /things/1/edit
def edit
end
# POST /things
# POST /things.json
def create
#thing = current_user.things.build(thing_params)
respond_to do |format|
if #thing.save
format.html { redirect_to #thing, notice: 'Thing was successfully created.' }
format.json { render action: 'show', status: :created, location: #thing }
else
format.html { render action: 'new' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /things/1
# PATCH/PUT /things/1.json
def update
respond_to do |format|
if #thing.update(thing_params)
format.html { redirect_to #thing, notice: 'Thing was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# DELETE /things/1
# DELETE /things/1.json
def destroy
#thing.destroy
respond_to do |format|
format.html { redirect_to things_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_thing
#thing = Thing.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def thing_params
params.require(:thing).permit(:title, :description, :image, :link)
end
end
Here is user.rb:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, #:recoverable,
:rememberable, :trackable, :validatable
has_many :things
validates :name, presence: true, length: { minimum: 2, maximum: 20}
validates :username, presence: true, length: { minimum: 2, maximum: 20}
validates :username, uniqueness: true
validates :email, presence: true
validates :email, uniqueness: true
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" , :nav => "25x25"}
extend FriendlyId
friendly_id :username
def show
end
#follow features
has_many :followed_users, through: :relationships, source: :followed
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :followed_users, through: :relationships, source: :followed
def following?(other_user)
relationships.find_by(followed_id: other_user.id)
end
def follow!(other_user)
relationships.create!(followed_id: other_user.id)
end
def unfollow!(other_user)
relationships.find_by(followed_id: other_user.id).destroy!
end
def feed
Thing.from_users_followed_by(self)
end
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
end
Here is users_controller.rb:
class UsersController < ApplicationController
def show
#user = User.find_by_username(params[:id])
end
def user_params
params.require(:user).permit(:avatar)
end
def following
#title = "Following"
#user = User.find_by_username(params[:id])
#users = #user.followed_users.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find_by_username(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
end
What I have tried
I went to the console, did thing = Thing.last, then tried to call thing.user, and that returned nil. So it appears the user_id isn't saving. How can I make that happen?
This is where the "try" method is your friend.
You're trying to get the "username" value from your user, which is missing.
If you were to write your line like:
thing.user.try(:username)
Then it would not crash, and it would fetch the username if user existed.
Your user is not saving because your one-to-many relationship is not set up. You might want to add a user_id to your things table.
Fixed by adding :user_id to the thing_params method in my things controller.
def thing_params
params.require(:thing).permit(:title, :description, :image, :link, :user_id)
end
I've been trying to get paperclip working with amazon s3 all day and I'm gettin' pretty close.
Albeit, how do I get around this error? I've included the bucket in the song model so I'm not sure what it's asking for. Once this is solved it should work.
Error:
ArgumentError in SongsController#create
missing required :bucket option
respond_to do |format|
if #song.save
format.html { redirect_to #song, notice: 'Song was successfully created.' }
format.json { render action: 'show', status: :created, location: #song }
else
song.rb
class Song < ActiveRecord::Base
acts_as_voteable
belongs_to :user
has_many :comments, :dependent => :destroy
has_many :genre_songs
has_many :genres, through: :genre_songs
has_attached_file :track,
:storage => :s3,
:path => '/:class/:attachment/:id_partition/:style/:filename',
:url => ":s3_domain_url",
:bucket => ENV['bucketname']
validates_attachment :track, :presence => true
validates_presence_of :url
validates :title, length: { minimum: 10 }
validates :url, length: { maximum: 300 }
def self.tagged_with(name)
Genre.find_by_name!(name).songs
end
def tag_list
genres.map(&:name).join(", ")
end
def tag_list=(names)
self.genres = names.split(",").map do |n|
Genre.where(name: n.strip).first_or_create!
end
end
end
paperclip.rb
# config/initializers/paperclip.rb
Paperclip::Attachment.default_options[:url] = ':s3_domain_url'
Paperclip::Attachment.default_options[:path] = '/:class/:attachment/:id_partition/:style/:filename'
production.rb and development.rb
config.paperclip_defaults = {
:storage => :s3,
:s3_credentials => {
:bucket => ENV['my bucketname'],
:access_key_id => ENV['my access key'],
:secret_access_key => ENV['my secret access key']
}
}
show.html.erb
<p id="notice"><%= notice %>
<p>
<%= #song.title %> | ( <%= #song.url %> )
<br />
<span class="subtext"><span class="votes_<%= #song.id %>"><%= pluralize(#song.votes.count, 'like') %>,</span>
posted <%= time_ago_in_words(#song.created_at) + " ago" %>
<small><span class="comments"></small> | <%= pluralize(#song.comments.size, 'comment') %></span></small><br /></span></span>
</p>
<p>
<%= audio_tag (#song.track.url), controls: "controls", alt: "Please use chrome, ie, or safari", preload: :auto %>
</p>
<p>Genres: <%= raw #song.genres.map(&:name).map { |t| link_to t, genre_path(t) }.join(', ') %></p>
<%#= link_to 'Show', song, class: "button small secondary" %>
<%= link_to('Edit', edit_song_path(#song), class: "button small secondary") if can? :update, #song %>
<%#= link_to 'Back', songs_path, class: "button small secondary" %>
<br /><br />
<%= render :partial => 'comments/form' %>
<div class="replies">
<% unless #song.comments.empty? %>
<h5><%= pluralize(#song.comments.size, 'comment') %></h5>
<br />
<% end %>
<% if #song.comments.empty? %>
<p>There are no comments...</p>
<% else %>
<div id="comments">
<% for comment in #song.comments %>
<div class="comment">
<strong><%= link_to_unless comment.site_url.blank?, h(comment.author_name), h(comment.site_url) %></strong>
<em>on <%= comment.created_at.strftime('%b %d, %Y at %H:%M') %></em>
<%=simple_format comment.content %><hr>
<p>
<%= link_to("Edit", edit_comment_path(comment)) if can? :update, #comment %>
<% end %>
<%= link_to("Destroy", comment, :method => :delete, :confirm => "Are you sure?") if can? :destroy, #comment %>
</p>
</div></div>
<% end %>
</div></div>
song_controller.rb
class SongsController < ApplicationController
before_filter :authenticate_user!, only: [:create ,:edit, :update, :destroy, :vote_for_song]
before_action :set_song, only: [:show, :edit, :update, :destroy, :vote_for_song]
def vote_for
#song = Song.find(params[:id])
current_user.vote_for(#song)
#song.plusminus = #song.votes_for
#song.save
respond_to do |format|
format.js { render 'update_votes' }
end
end
def vote_against
#song = Song.find(params[:id])
current_user.vote_against(#song)
respond_to do |format|
format.js { render 'update_votes' }
end
end
def new_songs
#songs = Song.order "id DESC"
end
# GET /Songs
# GET /Songs.json
def index
if params[:genre]
#songs = Song.tagged_with(params[:genre]).paginate(:page => params[:page], :per_page => 15)
else
#songs = Song.order('plusminus').paginate(:page => params[:page], :per_page => 15)
end
end
# GET /Songs/1
# GET /Songs/1.json
def show
#comment = Comment.new(song: #song)
end
# GET /Songs/new
def new
#song = Song.new
end
# GET /Songs/1/edit
def edit
end
# POST /Songs
# POST /Songs.json
def create
#song = Song.new(song_params)
respond_to do |format|
if #song.save
format.html { redirect_to #song, notice: 'Song was successfully created.' }
format.json { render action: 'show', status: :created, location: #song }
else
format.html { render action: 'new' }
format.json { render json: #song.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /Songs/1
# PATCH/PUT /Songs/1.json
def update
respond_to do |format|
if #song.update(song_params)
format.html { redirect_to #song, notice: 'Song was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #song.errors, status: :unprocessable_entity }
end
end
end
# Song /Songs/1
# Song /Songs/1.json
def destroy
#song.destroy
respond_to do |format|
format.html { redirect_to songs_url }
format.json { head :no_content }
end
end
private
def set_song
#song = Song.find(params[:id])
end
def song_params
params.require(:song).permit(:title, :artist, :url, :track, :user_id, :tag_list)
end
end
In dev and prod env you call:
ENV['my bucketname']
In model:
ENV['bucketname']
Also a good name for env variable would be:
ENV['MY_BUCKETNAME']
In your case:
ENV['AWS_BUCKET']
config.paperclip_defaults = {
:storage => :s3,
:bucket => ENV['BUCKETNAME'],
:s3_credentials => { :access_key_id => ENV['my access key'], :secret_access_key => ENV['my secret access key'] } }