Sortable Table Row with Nested Resource - ruby-on-rails

In my rails app, a Timesheet has_many Entries and an Entry belongs_to a Timesheet.
class Timesheet < ActiveRecord::Base
has_many :entries, order: 'position', dependent: :destroy
end
class Entry < ActiveRecord::Base
belongs_to :timesheet
end
I'm following Railscast 147 for sortable lists (the updated version). In the development log I notice that my params hash correctly updates the sort order, but on reload it doesn't save the positions correctly. Furthermore, the request is being processed by the create action instead of my custom sort action. Here's my controller.
class EntriesController < ApplicationController
before_filter :signed_in_user
before_filter :find_timesheet
def index
#entries = #timesheet.entries.order("position")
#entry = #timesheet.entries.build
end
def create
#entry = #timesheet.entries.build(params[:entry])
#entry.position = #timesheet.entries.count + 1
if #entry.save
#flash[:notice] = "Entry created"
#redirect_to timesheet_entries_path
respond_to do |format|
format.html { redirect_to timesheet_entries_path }
format.js
end
else
flash[:alert] = "Entry could not be added"
render 'new'
end
end
def destroy
#entry = #timesheet.entries.find(params[:id])
#entry.destroy
respond_to do |format|
format.html { redirect_to timesheet_entries_path, flash[:notice] = "Entry destroyed" }
format.js
end
end
def sort
params[:entry].each_with_index do |id, index|
#timesheet.entries.update_all({position: index+1}, {id: id})
end
render nothing: true
end
private
def find_timesheet
#timesheet = Timesheet.find(params[:timesheet_id])
end
end
and my routes.rb file.
Sledsheet::Application.routes.draw do
resources :timesheets do
resources :entries, only: [:index, :create, :destroy] do
collection { post :sort }
end
end
end
The entries.js.coffee
jQuery ->
$("#entries tbody").sortable(
helper: fixHelper
update: ->
$.post($(this).data('update-url'), $(this).sortable('serialize'))
).disableSelection()
The output from the development log
Started POST "/timesheets/8/entries" for 127.0.0.1 at 2012-06-04 20:14:18 -0400
Processing by EntriesController#create as */*
Parameters: {"entry"=>["60", "59", "61"], "timesheet_id"=>"8"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = 'qDs53hgOWfRMbNN9JKau3w' LIMIT 1
Timesheet Load (0.1ms) SELECT "timesheets".* FROM "timesheets" WHERE "timesheets"."id" = ? ORDER BY date DESC LIMIT 1 [["id", "8"]]
Completed 500 Internal Server Error in 2ms
NoMethodError (undefined method `stringify_keys' for "60":String):
app/controllers/entries_controller.rb:11:in `create'
I googled the error about the undefined method, but I'm confused why the create action would be called in this case anyway? I do have a new_entry form on the page, that creates a new entry via Ajax. Perhaps this is interfering with the sort? Any help would be appreciated!

The reason why there's no 'stringify_keys' method is because you're passing an array to the create action and not the sort action.
What do you have for data-update-url in your erb.html file?
Should be sort_entries_path.

Related

{"user":["must exist"]} Error with Rails Nested Resources and Authentication

I'm relatively new to Rails and have what I think is a relatively straightforward set-up that I'd like to use. I have users, lists, and items. Each user has many lists and each list has many items. I'm having trouble creating items after logging in.
User model:
class User < ApplicationRecord
include Authentication
has_many :examples
has_many :lists
has_many :items
end
List model:
class List < ApplicationRecord
belongs_to :user
has_many :items
end
Item model:
class Item < ApplicationRecord
belongs_to :user
belongs_to :list
end
Here are the relevant routes:
Rails.application.routes.draw do
resources :lists do
resources :items
end
I can create/read/update/delete lists with the following controller:
class ListsController < ProtectedController
before_action :set_list, only: [:show, :update, :destroy]
# GET /lists
def index
#lists = current_user.lists
render json: #lists
end
# GET /lists/1
def show
render json: List.find(params[:id])
end
# POST /lists
def create
#list = current_user.lists.build(list_params)
if #list.save
render json: #list, status: :created, location: #list
else
render json: #list.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /lists/1
def update
if #list.update(list_params)
render json: #list
else
render json: #list.errors, status: :unprocessable_entity
end
end
# DELETE /lists/1
def destroy
#list.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_list
#list = current_user.lists.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def list_params
params.require(:list).permit(:name)
end
end
However, I can't create an item successfully. Here is my item controller:
class ItemsController < ProtectedController
before_action :set_item, only: [:show, :update, :destroy]
# GET /items
def index
#items = current_user.items
render json: #items
end
# GET /items/1
def show
render json: #item
end
# POST /items
def create
#list = List.find(params[:list_id])
#item = #list.items.create(item_params)
if #item.save
render json: #item, status: :created, location: #item
else
render json: #item.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /items/1
def update
if #item.update(item_params)
render json: #item
else
render json: #item.errors, status: :unprocessable_entity
end
end
# DELETE /items/1
def destroy
#item.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_item
#item = current_user.items.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def item_params
params.require(:item).permit(:name, :quantity, :price, :store, :category, :notes, :user_id, :list_id)
end
end
I sign in, create a list, and then try to post an item to that list but get the following error:
{"user":["must exist"]}
Here is the message from the server:
Started POST "/lists/4/items" for 127.0.0.1 at 2017-08-09 22:47:19 -0400
Processing by ItemsController#create as */*
Parameters: {"item"=>{"name"=>"Test Item", "quantity"=>"1", "price"=>"9.99", "store"=>"Fruit Center", "category"=>"Dairy", "notes"=>"Important Note"}, "list_id"=>"4"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."token" = $1 LIMIT $2 [["token", "27e3e2a67d86cb9d3f46d20651370b74"], ["LIMIT", 1]]
List Load (0.3ms) SELECT "lists".* FROM "lists" WHERE "lists"."id" = $1 LIMIT $2 [["id", 4], ["LIMIT", 1]]
(0.1ms) BEGIN
(0.2ms) COMMIT
(0.1ms) BEGIN
(0.1ms) ROLLBACK
[active_model_serializers] Rendered ActiveModel::Serializer::Null with ActiveModel::Errors (0.06ms)
Completed 422 Unprocessable Entity in 8ms (Views: 0.5ms | ActiveRecord: 1.1ms)
Alternatively, I've tried changing the Create action for item to be this:
def create
#item = current_user.items.create(item_params)
if #item.save
render json: #item, status: :created, location: #item
else
render json: #item.errors, status: :unprocessable_entity
end
end
Doing it this way, results in a slightly different error: {"list":["must exist"]}
From the server:
Started POST "/lists/4/items" for 127.0.0.1 at 2017-08-09 23:09:20 -0400
Processing by ItemsController#create as */*
Parameters: {"item"=>{"name"=>"Test Item", "quantity"=>"1", "price"=>"9.99", "store"=>"Fruit Center", "category"=>"Dairy", "notes"=>"Important Note"}, "list_id"=>"4"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."token" = $1 LIMIT $2 [["token", "27e3e2a67d86cb9d3f46d20651370b74"], ["LIMIT", 1]]
(0.1ms) BEGIN
(0.2ms) COMMIT
(0.1ms) BEGIN
(0.1ms) ROLLBACK
[active_model_serializers] Rendered ActiveModel::Serializer::Null with ActiveModel::Errors (0.07ms)
Completed 422 Unprocessable Entity in 32ms (Views: 2.2ms | ActiveRecord: 4.1ms)
It does appear that the list_id is getting passed through in the parameters, but it's not being picked up in the create method.
I feel like I may be missing something simple. Any ideas?
Notice in your log that list_id is not inside of the item params:
Parameters: {"item"=>{"name"=>"Test Item", "quantity"=>"1", "price"=>"9.99", "store"=>"Fruit Center", "category"=>"Dairy", "notes"=>"Important Note"}, "list_id"=>"4"}
params[:list_id] # => 4
params[:item][:list_id] # => nil
You have white listed params[:item][:list_id] in your item_params, but list_id is not nested in params[:item] because in your item form you do not have a field to pass it along (would need a form input with name='item[list_id]'). Instead params[:list_id] = 4 is being set for the request based on the route /lists/4/items. You can do
def create
#item = current_user.items.build(item_params)
#item.list_id = params[:list_id]
if #item.save
render json: #item, status: :created, location: #item
else
render json: #item.errors, status: :unprocessable_entity
end
end
Note that I have called current_user.items.build instead of current_user.items.create. Using current_user.items.create will attempt to save the record immediately which you don't want to do in this case (it seems).

Pass another data than the id in the URL

I'm currently creating a website that would allow a visitor to read a magazine online. I created a scaffold for the magazine itself, and another one for the pages, which belongs to the magazine. This way, I can create a multi upload for the pages while creating the magazine, and everything gets uploaded in the same time.
When I nest the pages resources inside the magazine's resources, as following:
resources :magazines do
resources :pages
end
I get "domain.com/magazines/<slug>/pages/id"
However, I had some issues with the id, because it can't be used to count the pages (since the id never goes back to 1), so I created a function inside the controller that would count the pages for me, and save each pages with a "page_number" value.
My question is the following: how, instead of the id, can I kindly ask my router to use the :page_number ?
I tried to create a custom route, which looked like this
resources :magazines do
get '/:page_number' => 'pages#show', as: :custom_page
end
But for an unknown reason, Rails tells me that my custom route simply doesn't exist, even though it exists when I type rake routes
Thank you in advance
Edit
Here is my magazine_controller.rb
class MagazinesController < ApplicationController
before_action :authenticate_user!, only: [:edit, :update, :destroy]
before_action :set_magazine, only: [:show, :edit, :update, :destroy]
def index
#magazines = Magazine.all
end
def show
end
def new
#magazine = Magazine.new
end
def edit
end
def create
#magazine = Magazine.new(magazine_params)
if #magazine.save
if params[:images]
(params[:image] || []).each_with_index do |image, index|
#magazine.pages.create(image: image, page_number: index + 1)
end
redirect_to #magazine, notice: 'Magazine was successfully created.'
else
render :new
end
end
end
def update
if #magazine.update(magazine_params)
if params[:images]
params[:images].each { |image|
#magazine.pages.create(image: image)
}
end
redirect_to #magazine, notice: 'Magazine was successfully updated.'
else
render :edit
end
end
def destroy
#magazine.destroy
redirect_to magazines_url, notice: 'Magazine was successfully destroyed.'
end
private
def set_magazine
#magazine = Magazine.friendly.find(params[:id])
end
def magazine_params
params.require(:magazine).permit(:titre, :apercu)
end
end
and here is page_controller.rb
class PagesController < ApplicationController
before_action :authenticate_user!, only: [:edit, :update, :destroy]
before_action :set_page, only: [:show, :edit, :update, :destroy]
def index
#pages = Page.all
end
def show
end
def new
#page = Page.new
end
def edit
end
def create
#page = Page.new(page_params)
if #page.save
redirect_to #page, notice: 'Page was successfully created.'
else
render :new
end
end
def update
if #page.update(page_params)
redirect_to #page, notice: 'Page was successfully updated.'
else
render :edit
end
end
def destroy
#page.destroy
redirect_to :back, notice: 'Page was successfully destroyed.'
end
private
def set_page
#page = Page.find(params[:id])
end
def page_params
params.require(:page).permit(:titre, :apercu)
end
end
Edit 2
Here is the output from development.log when trying to reach the page
Started GET "/magazines/magazine-54/pages/11" for 127.0.0.1 at 2016-12-12 11:04:45 +0100
Processing by PagesController#show as HTML
Parameters: {"magazine_id"=>"magazine-54", "id"=>"11"}
Completed 500 Internal Server Error in 2ms (ActiveRecord: 0.0ms)
NoMethodError (undefined method `pages' for nil:NilClass):
app/controllers/pages_controller.rb:49:in `set_page'
Rendering /var/lib/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout
Rendering /var/lib/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_source.html.erb
Rendered /var/lib/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_source.html.erb (2.4ms)
Rendering /var/lib/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb
Rendered /var/lib/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (1.2ms)
Rendering /var/lib/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb
Rendered /var/lib/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (0.7ms)
Rendered /var/lib/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout (13.8ms)
With these routes
resources :magazines do
resources :pages
end
you should be able to update PagesController and add/update following methods:
def magazine
#magazine = Magazine.find(params[:magazine_id])
end
def page
#page = #magazine.pages.find_by(page_number: params[:id])
end
to make it work you should have page_number column in Page model and has_many :pages association in Magazine model

Rails: notifications using GetStream and Acts As Follower gems

I'm using getstream and acts as follower to build a notification and news feed for my project. When following a certain user, that user is supposed to get a notification of such following. I'm able to follow and unfollow users, but getstream gives the following error:
Something went wrong creating an activity: undefined local variable or method `user' for #<Follow:0x007ffaa70c9398>
Completed 500 Internal Server Error in 37ms
NameError (undefined local variable or method `user' for #<Follow:0x007ffaa70c9398>):
app/controllers/users_controller.rb:160:in `follow_user'
Rendered /usr/local/lib/ruby/gems/2.2.0/gems/actionpack-4.2.0/lib/action_dispatch/middleware/templates/rescues/_source.erb (3.1ms)
Rendered /usr/local/lib/ruby/gems/2.2.0/gems/actionpack-4.2.0/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb (0.8ms)
Rendered /usr/local/lib/ruby/gems/2.2.0/gems/actionpack-4.2.0/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb (0.9ms)
Rendered /usr/local/lib/ruby/gems/2.2.0/gems/actionpack-4.2.0/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb (108.1ms)
This if what my follow.rb file looks like:
class Follow < ActiveRecord::Base
extend ActsAsFollower::FollowerLib
extend ActsAsFollower::FollowScopes
# NOTE: Follows belong to the "followable" interface, and also to followers
belongs_to :followable, :polymorphic => true
belongs_to :follower, :polymorphic => true
include StreamRails::Activity
as_activity
def activity_notify
[StreamRails.feed_manager.get_notification_feed(self.followable_id)]
end
def activity_object
self.followable
end
def block!
self.update_attribute(:blocked, true)
end
end
And in user.rb I have:
acts_as_followable
acts_as_follower
and nothing else referencing the Follow model. Any ideas what I'm doing wrong ?
EDIT:
Here is my follow and unfollow code in the users controller. Line 160 is if #follower.follow(#following), and likewise for unfollowing:
def follow_user
#follower = User.find_by_username(params[:username])
#following = User.where(id: params[:following_id]).first
#user = #following
if #follower == #following
respond_to do |format|
flash.now[:warning] = "Cannot follow yourself."
format.js { render file: "/app/views/layouts/notice.js.erb" }
end
end
if #follower.follow(#following)
StreamRails.feed_manager.follow_user(#follower.id, #following.id)
respond_to do |format|
format.js { render file: "/app/views/users/follow.js.erb" }
end
else
respond_to do |format|
flash.now[:info] = "Error."
format.js { render file: "/app/views/layouts/notice.js.erb" }
end
end
end
def unfollow_user
#follower = User.find_by_username(params[:username])
#following = User.find(params[:following_id])
#user = #following
if #follower.stop_following(#following)
StreamRails.feed_manager.unfollow_user(#follower.id, #following.id)
respond_to do |format|
format.js { render file: "/app/views/users/follow.js.erb" }
end
else
respond_to do |format|
flash.now[:info] = "Error."
format.js { render file: "/app/views/users/add_flag.js.erb" }
end
end
end
The stream-rails gem assumes that your model has a user attribute to lookup the actor of the activity. You can change this behavior by implementing the activity_actor method in your Follow model.
For example:
class Follow < ActiveRecord::Base
def activity_actor
self.follower
end
end
This will create activities such as "x followed y" when a follow model is created (as well as the logic that you already have implemented to create actual follow connections between feeds).

Couldn't find User with 'id'=

I know this is the most commonly asked question but i am really looking for some help here as i don't understand what i am doing wrong here .
I have a generated a devise user and intend to create a gallery for users . Following is the code i have tried
Model - Gallery.rb
class Gallery < ActiveRecord::Base
belongs_to :user
end
Controller - galleries_controller.rb
class GalleriesController < ApplicationController
before_action :set_gallery, only: [:show, :edit, :update, :destroy]
before_filter :authenticate_user!
respond_to :html
def index
#user = User.find(params[:user_id])
#galleries = Gallery.all
respond_with(#galleries)
end
def show
respond_with(#gallery)
end
def new
#user = User.find(params[:user_id])
#gallery = Gallery.new
respond_with(#gallery)
end
def edit
end
def create
#gallery = Gallery.new(gallery_params)
flash[:notice] = 'Gallery was successfully created.' if #gallery.save
respond_with(#gallery)
end
def update
flash[:notice] = 'Gallery was successfully updated.' if #gallery.update(gallery_params)
respond_with(#gallery)
end
def destroy
#gallery.destroy
respond_with(#gallery)
end
private
def set_gallery
#gallery = Gallery.find(params[:id])
end
def gallery_params
params[:gallery, :user_id]
end
end
Folowing is the error that gets shown with better errors -
Logs
Started GET "/galleries/1" for 127.0.0.1 at 2015-05-03 15:24:10 +0530
Processing by GalleriesController#show as HTML
Parameters: {"id"=>"1"}
Gallery Load (2.1ms) SELECT "galleries".* FROM "galleries" WHERE "galleries"."id" = ? LIMIT 1 [["id", 1]]
Completed 404 Not Found in 63ms
ActiveRecord::RecordNotFound - Couldn't find Gallery with 'id'=1:
activerecord (4.2.0) lib/active_record/core.rb:154:in `find'
routes.rb
Rails.application.routes.draw do
root to: 'visitors#index'
devise_for :users
resources :users do
resources :galleries
resources :photos
end
end
As i am still new to ROR , any help would be very much appreciated . Thanks in advance .
From the URL i am assuming you are not passing user_id in params.
In your route file gallery should be nested route. If you wan't user_id.
resources :users do
resources :galleries do
end
end
or you can use
#user= current_user

How to delete multiple records simultaneously using Rails 3 and multiple check_box_tag elements

I'm stuck worse than a Yugo in a ditch.
I added the changes which ultimately made this work below (marked as bold edits).
Background
Widgets have many Gadgets
The final td column in the table has check_box_tags to select individual Gadget records for deletion
Desired Behavior
User should be able to click "checkall" as well as individual checkboxes to select Gadgets one at a time
Clicking the "Delete Selected" button should delete checked records without refreshing the page
EDIT: Adding more specific details about what's not working
Observed Behavior
destroy_multiple method in gadgets_controller.rb is never being called
instead, the create method appears to be called
Started POST "/widgets/1/gadgets" ...
Processing by GadgetsController#create as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"...=", "widget"=>{"gadget_ids"=>["all", "140", "139"]}, "widget_id"=>"1"}
Widget Load (0.3ms) SELECT "widgets".* FROM "widget" WHERE "widget"."id" = $1 LIMIT 1 [["id", "1"]]
Rendered gadgets/create.html.haml within layouts/application (1.1ms)
Rendered layouts/_header.html.haml (0.2ms)
Completed 200 OK in 36ms (Views: 19.0ms | ActiveRecord: 5.0ms)
I'm also unclear about this: {"gadget_ids"=>["all", "140", "139"]}.
"all" is the css id of 1 checkbox used to "checkall". In the checkbox.js.coffee file below, I attempt to remove the "all" value from the Array using javascript. When I log the Array to console, "all" is removed successfully. Yet it continues to be sent to the gadgets controller.
EDIT rails remote form populates the gadgets array for the POST request. just had to set it up correctly.
Models
widget.rb
class Widget < ActiveRecord::Base
has_many :gadgets
validates_presence_of :name
end
gadget.rb
class Gadget < ActiveRecord::Base
belongs_to :widget
validates_presence_of :widget_id
end
Gadgets Controller
class GadgetsController < ApplicationController
include ApplicationHelper
before_filter :get_widget
before_filter :widget_gadgets, only: [ :index ]
respond_to :json
def json_widgets
[ #widget, #gadgets ]
end
def index
respond_with(json_widgets) do |format|
format.json
format.html
end
end
def new
#gadget = #widget.gadgets.new()
end
def create
#gadget=#widget.gadgets.new(params[:gadget])
if #gadget.save
flash[:success] = "Gadget was successfully created"
redirect_to widget_gadgets_path
end
end
def destroy_multiple
gadget_ids=(params[:gadget_ids])
to_delete=[]
gadget_ids.split(',').each do |gadget_id|
to_delete.push(Gadget.find(gadget_id))
end
to_delete.each do |del|
del.destroy()
end
flash.now[:success] = "Gadget Destroyed Successfully"
respond_to do |format|
format.html { redirect_to widget_gadgets_path(#widget) }
format.json { render :json => to_delete.to_json }
end
end
def destroy
#gadget = gadget.find(params[:gadget_id])
#widget.gadgets.destroy(#gadget.id)
respond_to do |format|
format.html { redirect_to(widget_gadgets_path) }
end
end
def get_widget
begin
#widget = Widget.find(params[:widget_id])
rescue ActiveRecord::RecordNotFound
render file: "public/404.html", status: 404
end
end
private
def widget_gadgets
#widget=Widget.find(params[:widget_id])
#gadgets=#widget.gadgets unless #widget.gadgets.nil?
end
end
routes.rb
I'm trying to use a collection do block. Is that the right way to implement a destroy_multiple route?
DestroyMultiple::Application.routes.draw do
resources :widgets
resources :gadgets, :only => [ :new, :create ]
resources :widgets do
resources :gadgets do
collection do
post :destroy_multiple
end
end
end
match "/:id" => 'widget#gadgets'
root to: 'widgets#index'
end
After getting this to work properly, I wanted to post the full solution.
Rather than type it all out here, please see the full application on GitHub.
While this solution works, I would be very interested to know how I can optimize the solution. Thanks again to those who helped!
https://github.com/hernamesbarbara/AJAX-Rails-Full-CRUD

Resources