Hi so I'm really new to rails and I am trying to figure out how to use this gem https://github.com/jonhue/acts_as_favoritor.
At the moment I am trying to get a student to favourite an internship.
My favourites controller looks like this
class FavouritesController < ApplicationController
def new
#internship = Internship.find(params[:id])
current_student.favourite(internship)
redirect_to_show
end
def show
current_students.all_favourites
end
end
My routes look like this
Rails.application.routes.draw do
resources :favourites
devise_for :students
resources :internships
devise_for :companies
# For details on the DSL ava
And my button to add an internship shown in the index is this
<td><%= link_to 'favourite', new_favourite_path %></td>
Using the gem I have put in the models acts_as_favoritor in the student model and acts_as_favoritable in the internship model. I have been banging my head against the wall for ages trying to understand how to write methods from so if someone could please help me out, Thank you heaps!
I think if I were you, I would make my routes something like:
resources :internships do
member do
post :favorite
post :unfavorite
end
end
Which will give you among other things:
favorite_internship POST /internships/:id/favorite(.:format) internships#favorite
unfavorite_internship POST /internships/:id/unfavorite(.:format) internships#unfavorite
internships GET /internships(.:format) internships#index
POST /internships(.:format) internships#create
new_internship GET /internships/new(.:format) internships#new
edit_internship GET /internships/:id/edit(.:format) internships#edit
internship GET /internships/:id(.:format) internships#show
PATCH /internships/:id(.:format) internships#update
PUT /internships/:id(.:format) internships#update
DELETE /internships/:id(.:format) internships#destroy
Then in your view, you would do something along the lines of:
<td>
<% if current_student.favorited?(internship) %>
<%= link_to 'unfavourite', unfavorite_internship_path(internship), method: :post %>
<% else %>
<%= link_to 'favourite', favorite_internship_path(internship), method: :post %>
<% end %>
</td>
This, naturally, assumes you have access to current_student and internship in your view.
Then, in your InternshipsController, you would do something like:
class InternshipsController < ApplicationController
def favorite
#internship = Internship.find(params[:id])
current_student.favorite(#internship)
# redirect somewhere
end
def unfavorite
#internship = Internship.find(params[:id])
current_students.unfavorite(#internship)
# redirect somewhere
end
end
Now, favorite and unfavorite are not very restful. So, I guess you could do:
resources :internships do
scope module: :internships do
resources :favorites, only: [:create] do
collection do
delete '/', action: :destroy
end
end
end
end
Which would give you:
internship_favorites DELETE /internships/:internship_id/favorites(.:format) internships/favorites#destroy
POST /internships/:internship_id/favorites(.:format) internships/favorites#create
internships GET /internships(.:format) internships#index
POST /internships(.:format) internships#create
new_internship GET /internships/new(.:format) internships#new
edit_internship GET /internships/:id/edit(.:format) internships#edit
internship GET /internships/:id(.:format) internships#show
PATCH /internships/:id(.:format) internships#update
PUT /internships/:id(.:format) internships#update
DELETE /internships/:id(.:format) internships#destroy
Then you would need a Internships::FavoritesController something like:
# in app/controllers/internships/favorites_controller.rb
class Internships::FavoritesController < ApplicationController
def create
#internship = Internship.find(params[:internship_id])
current_student.favorite(#internship)
# redirect somewhere
end
def destroy
#internship = Internship.find(params[:internship_id])
current_students.unfavorite(#internship)
# redirect somewhere
end
end
Then in your view, it would be more like:
<td>
<% if current_student.favorited?(internship) %>
<%= link_to 'unfavourite', internship_favorites_path(internship), method: :delete %>
<% else %>
<%= link_to 'favourite', internship_favorites_path(internship), method: :post %>
<% end %>
</td>
Before getting into details I have read through these posts to try to find the solution without success : one, two, three
That being said: I am [new and] building an ecomm site for selling secondhand clothing, shoes and decor items.
My structure has only one Product model and associated controller and table. Each 'product' has one of three different main categories, which is what I am using to differentiate and create 3 different URLs.
My routes look like this:
Rails.application.routes.draw do
root to: 'pages#home'
get 'clothing', to: 'products#clothing'
get 'clothing/:id', to: 'products#show'
get 'shoes', to: 'products#shoes'
get 'shoes/:id', to: 'products#show'
get 'home', to: 'products#home'
get 'home/:id', to: 'products#show'
get 'products/new', to: 'products#new'
post 'products', to: 'products#create'
end
My products_controller looks like this:
class ProductsController < ApplicationController
before_action :set_all_products
before_action :set_one_product, only: [:show]
def shoes
#all_shoe_products = #all_products.where(main_category_id: MainCategory.find_by_name("shoes").id)
end
def clothing
#all_clothing_products = #all_products.where(main_category: MainCategory.find_by_name("clothes").id)
end
def home
#all_home_products = #all_products.where(main_category: MainCategory.find_by_name("housewares").id)
end
def show
end
def new
#new_product = Product.new
end
private
def set_one_product
#product = Product.find(params[:id])
end
def set_all_products
#all_products = Product.all
end
end
And when writing <%= link_to clothing_path(product) %> ('product' being the placeholder in an .each loop), I get a path: root/clothing.[:id] and not root/clothing/[:id]
I know I am making a convention error, and trying to have 3 different URLs within the same controller may be where I am gong wrong.
Note: manually entering root/clothing/[:id] in the address bar does return a product correctly.
When you do this:
get 'clothing', to: 'products#clothing'
get 'clothing/:id', to: 'products#show'
in your routes.rb, it creates these routes (which you can see by doing rake routes in your console):
clothing GET /clothing(.:format) products#clothing
GET /clothing/:id(.:format) products#show
As you can see, clothing_path routes to /clothing, not /clothing/:id. So, when you do:
<%= link_to clothing_path(product) %>
rails appends the id as .id (which is what you're experiencing).
#jvillian explains the cause of the issue well here, though I'd like to propose a slight refactor as a solution.
This might be a little more work, though you'd likely be better off with seperate controllers for shoes, clothing and home, and following a RESTful design. That would allow you to use resources in your routes file.
For example, your shoes_controller.rb would be like the following:
class ShoesController < ApplicationController
before_action :set_all_products
before_action :set_one_product, only: [:show]
def index
#all_shoe_products = #all_products.where(main_category_id: MainCategory.find_by_name("shoes").id)
end
def show
end
private
def set_one_product
#product = Product.find(params[:id])
end
def set_all_products
#all_products = Product.all
end
end
And then the routes to define them would be:
resources :shoes, only: [:index, :show]
You follow this pattern for the other resources and you'll have nicely segregated code be following good Rails conventions.
This will generate the routes as you're after:
shoes GET /shoes(.:format) shoes#index
shoe GET /shoe/:id(.:format) shoes#show
That will resolve your issue and give you a nicely designed app - there's also opportunity to extrapolate some of the code shared between the new controllers, though that sounds like a follow up task :)
Hope this helps - let me know if you've any questions or feedback.
I found a solution, though seems a bit of a logic mystery to me why it's working.
In routes.....
get 'clothing', to: 'products#clothing'
get 'clothing/:id', to: 'products#show', as: 'clothing/item'
In the index page....
<%= link_to clothing_item_path(product) do %>
This yields the right URL structure: root/clothing/[:id]
While testing this I was expecting: root/clothing/item/[:id]
...though I prefer the result over my expectation
I think what you want is parameterized routes, like this:
get ':product_line', to: 'products#index'
get ':product_line/:id', to: 'products#show'
This would allow you to create any number of custom product lines without ever having to define new methods in your controller. Assuming there is a product_line attribute on your Product model, the controller would look like this:
class ProductsController < ApplicationController
def index
#product_line = params[:product_line]
#products = Product.where(product_line: #product_line)
end
def show
#product_line = params[:product_line]
#product = Product.find(params[:id])
end
end
And your views/products/index.html.erb would look like this:
<p id="notice"><%= notice %></p>
<h1><%= #product_line %></h1>
<table>
<thead>
<tr>
<th>Description</th>
<th>Price</th>
<th></th>
</tr>
</thead>
<tbody>
<% #products.each do |product| %>
<tr>
<td><%= product.description %></td>
<td><%= product.price %></td>
<td><%= link_to 'Show', "#{#product_line}/#{product.id}" %></td>
</tr>
<% end %>
</tbody>
</table>
Note that the link_to can no longer use a Rails helper method to generate the url. You'd have to do that yourself.
The beauty of this approach is that users could type in ANY product line in the URL. If you had that product line (like say 'sporting_goods'), go ahead and display it. If not, render a page thanking them for their interest and log the fact that someone requested that product line so you can guage interest as you expand your offerings.
Plus, it's RESTful! Yay!
The Rails way of solving this is by creating a nested resource:
resources :categories do
resources :products, shallow: true
end
This nests the collection routes so that you get GET /categories/:category_id/products.
While this might not be as short as your vanity routes it is much more versatile as it will let you show the products for any potential category without bloating your codebase.
You would setup the controller as so:
class ProductsController < ApplicationController
before_action :set_category, only: [:new, :index, :create]
# GET /categories/:category_id/products
def index
#products = #category.products
end
# GET /categories/:category_id/products/new
def new
#product = #category.products.new
end
# POST /categories/:category_id/products
def new
#product = #category.products.new(product_params)
# ...
end
# ...
private
def set_category
#category = MainCategory.includes(:products)
.find_by!('id = :x OR name = :x', x: params[:id])
end
end
You can link to products of any category by using the category_products_path named path helper:
link_to "#{#category.name} products", category_products_path(category: #category)
You can also use the polymorphic path helpers:
link_to "#{#category.name} products", [#category, :products]
form_for [#category, #product]
redirect_to [#category, :products]
If you want to route the unnested GET /products and nested GET /categories/:category_id/products to different controllers a neat trick is to use the module option:
resources :products
resources :categories do
resources :products, only: [:new, :index, :create], module: :categories
end
This will route the nested routes to Categories::ProductsController.
I'm trying to implement my first decorator for my view. I'm running into an issue though. When I try to render the view I get the title error undefined method "decorate" for #<Assignment::ActiveRecord_AssociationRelation. I'm not sure what I'm supposed to do? Any help would be great. Here is my code.
Assignment decorator:
class AssignmentDecorator < Draper::Decorator
delegate_all
decorates :assignment
def status
if finished
"Finished"
else
"Waiting"
end
end
end
Pages Controller:
class PagesController < ApplicationController
before_action :verify_account!, only: :dashboard
def home; end
def dashboard
#assignments = current_account.assignments.all.decorate
#invitation = Invitation.new
end
end
View:
<% #assignments.each do |assignment| %>
<tr class="assignment-rows">
<td><%= link_to assignment.name, account_assignment_path(assignment) %></td>
<td><%= assignment.assigned_workers %></td>
<td><%= assignment.status %></td>
</tr>
<% end %>
Error message:
Draper Docs:
https://github.com/drapergem/draper
If you read the docs ;) and look at the decorate_collection section:
https://github.com/drapergem/draper#collections
which states:
Note: In Rails 3, the .all method returns an array and not a query. Thus you cannot use the technique of Article.all.decorate in Rails 3. In Rails 4, .all returns a query so this techique would work fine.
So if you're using Rails 3 - you need to use decorate_collection... or you could (and probably should) upgrade to rails 4
Before
#assignments = current_account.assignments.all.decorate
After
#assignments = current_account.assignments.decorate.all
Here's the problem I've been working on for the last few days:
I have task and completed_task models:
class Task < ActiveRecord::Base
belongs_to :user
has_many :completed_tasks
end
class CompletedTask < ActiveRecord::Base
belongs_to :task
end
I have a form that says:
<% #tasks.each do |task| %>
<td><%= link_to task.description, task_path(task) %></td>
<td><%= task.user.first_name %></td>
<td><%= task.value %></td>
<td><%= task.task_type %></td>
<td><%= task.frequency %></td
<td><%= task.active %></td>
<td><%= task.due_by %></td>
<%= button_to "Task Completed", new_completed_task_path(:completed =>[:task_id =>
task.id, :task_value => task.value}) %>
<%end%>
In my completed_task_controller, I have:
def new
#completed_task = CompletedTask.new(params[:completed])
end
def create
#completed_task = CompletedTask.new(completed_task_params)
end
When I click on the button to complete a task, I want it to create a record in the completed_tasks table but the params from the parent table are not flowing from the new action to the create action. I'm guessing it has to do with the strong parameters which I have set as:
private
def set_completed_task
#completed_task = CompletedTask.find(params[:id])
end
def completed_task_params
params.require(:completed_task).permit(:task_id, :task_value)
end
Here is the error I am getting:
ActiveModel::ForbiddenAttributesError
Extracted source (around line #19):
def new
#completed_task = CompletedTask.new(params[:completed])
end
Any ideas???
When you call the new method, at that point nothing has been returned from the form (it hasn't even been dsiplayed, yet)
You should just do
def new
#completed_task = CompletedTask.new
end
When the form is returned, then the create method would typically do
def create
#completed_task = CompletedTask.new(completed_task_params)
if #completed_task.save
# stuff to do when the record is saved, maybe redirect to show page or index
else
# stuff to do if record is not saved... probably redisplay new format with errors
end
end
EDIT: to clarify, the method completed_task_params (which you correctly coded) essentially flags the form attributes as acceptable. Had you done CompletedTask.new(params[:completed_task]) strong parameters would've been unhappy as the attributes weren't flagged as permitted.
I'm trying to create a view that display all records that belongs to an association and not sure on how to go about this.
I've created an erb file in view/members/showmembers.html.erb, added 'showmembers' method in members_controller.rb but when I go to http://localhost:3000/members/showmembers it says 'Couldn't find Member with id=showmembers' and its pointing to the show method in the controller *'app/controllers/members_controller.rb:29:in `show'*.
def showmembers
#organization = Organization.find(1)
#shmembers = #organization.members.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #members }
end
end
showmembers.html.erb
<% #shmembers.each do |shmember| %>
<tr>
<td><%= shmember.id %></td>
<td><%= shmember.first_name %></td>
</tr>
<% end %>
Thanks.
the error indicates that Rails don't know which action to use to process this request : http://localhost:3000/members/showmembers.
and since this request is "GET", Rails chose the "show" action.
so the solution is simply edit your config/routes.rb, and add this route:
resources :members do
collection do
get :showmembers # add this line
end
end
Please paste this into your route file otherwise it map to show action that cause this error please define route first\
resources :members do
collection do
get :showmembers
end
end
And then in controller
hmembers = #organization.members
no need to this members = #organization.members.all
If you define association well....