Problems with implementing a realtime update with Rails5 and Actioncable - ruby

I'm trying to implement a simple rails fullstack todo app with action cable.
Unfortunately the docs on action cable is still confusing to me.
I tried to refer to DHH's example and https://blog.heroku.com/real_time_rails_implementing_websockets_in_rails_5_with_action_cable and adjusting to my app https://github.com/tenzan/rails-fullstack-todo
It's not showing realtime updates at http://localhost:3000/tasks when I used one browser for task creation and another one to see if it changes automatically.
My environment:
Mac OS X Sierra
Rails 5.0.0.1
Ruby 2.4.0
Redis enabled in Gemfile and running at port 6379
RethinkDB
I implemented:
app/assets/javascripts/channels/index.coffee
//= require cable
//= require_self
//= require_tree .
this.App = {};
App.cable = ActionCable.createConsumer();
app/assets/javascripts/channels/tasks.js
App.tasks = App.cable.subscriptions.create('TasksChannel', {
received: function(data) {
$("#tasks").removeClass('hidden')
return $('#tasks').append(this.renderTask(data));
}
});
app/channels/tasks_channel.rb
class TasksChannel < ApplicationCable::Channel
def subscribed
stream_from "tasks"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
app/controllers/tasks_controller.rb
def create
#task = Task.new(task_params)
respond_to do |format|
if #task.save
ActionCable.server.broadcast 'tasks', task: #task.title
format.html { redirect_to tasks_url, notice: 'Task was successfully created.' }
format.json { render :show, status: :created, location: #task }
else
format.html { render :new }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
app/views/layouts/application.html.erb: Added <%= action_cable_meta_tag %>
config/cable.yml
local: &local
url: redis://localhost:6379
development: *local
test: *local
config/environments/development.rb
Rails.application.configure do
config.action_cable.url = "ws://localhost:3000/cable"
config/routes.rb
Rails.application.routes.draw do
mount ActionCable.server => '/cable'
app/views/tasks/index.html.erb
I suspect something wrong with
app/assets/javascripts/channels/tasks.coffee
app/views/tasks/index.html.erb
But I really don't have an idea how to fix/adjust them.

Related

What to use instead of after_commit in controller in Rails 5?

Today I tried to use a wonderful callback :after_commit which triggers when the object is written to database, however, I've got the error message from Rails:
ActionController::RoutingError (undefined method `after_commit' for ImagesController:Class
Did you mean? after_action):
Well, that was embarassing! And it seems like this callback was deprecated!
Looking through search, I tried to use :after_create_commit, which gave me the same error.
The third step was to try :after_action. Here goes the question:
How to make it work the same way as :after_commit?
I've already tried apidock.com - it's really minimal! Also I've tried api.rubyonrails.org - it is saying about blocks, but I'm not a ruby ninja to understand it. So I really apprecite if you could spill some light on it!
ImagesController:
class ImagesController < ApplicationController
after_create_commit :take_image_to_album
def take_image_to_album
if check_album
add_inner_to_album(#image)
end
end
def create
#image = Image.create(image_params.merge(:user_id => current_user.id)
respond_to do |format|
unless #image.save
format.html { render :show, notice: "Error!" }
format.json { render json: #image.errors, status: :unprocessable_entity }
else
format.html
format.json { render :show, status: :ok, location: #image }
end
end
end
...
def add_inner_to_album(image)
contents = #album.content
contents << [image.id, image[:imageup], false]
#album.update(:content => contents)
end
end
The after_commit method is only for models. In the controllers family, you have after_action, that will be executed after the action of the controller is finished.
For example, the after_action in a controller works like this:
class UsersController < ApplicationController
after_action :log_activity, only: :show
# GET v3/users/:id
def show
render status: :ok,
json: { id: current_user.id, name: current_user.name }
end
def log_activity
current_user.update(last_activity_at: Time.current)
end
end
The log_activity method is executed after responding the request.
In the after_action :log_activity, only: :show, with only you can specify after which actions log_activity will run. If you do not specify any, it will run after every action defined in the controller.

ActionCable NoMethod Error

I've added simple ActionCable to my toy rails app and discovered that I'm getting
Error: ProductsControllerTest#test_should_update_product:
NoMethodError: undefined method server' for ApplicationCable:Module
app/controllers/products_controller.rb:53:inblock in update'
app/controllers/products_controller.rb:44:in `update'
test/controllers/products_controller_test.rb:44:in `block in <class:ProductsControllerTest>'
when I start my rails unit tests. It's the only time when error occurs.
My code:
products_controller_test:
test 'should update product' do
patch product_url(#product), params: { product: #update }
assert_redirected_to product_url(#product)
end
config/application.rb
require 'action_cable'
/app/channels/application_cable/channel.rb
module ApplicationCable
class Channel < ActionCable::Channel::Base
end
end
/app/channels/application_cable/connection.rb
module ApplicationCable
class Connection < ActionCable::Connection::Base
end
end
/app/channels/products_channel.rb
class ProductsChannel < ApplicationCable::Channel
def subscribed
stream_from 'products'
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
end
/app/assets/javascripts/channels/products.coffee
App.products = App.cable.subscriptions.create "ProductsChannel",
connected: ->
# Called when the subscription is ready for use on the server
disconnected: ->
# Called when the subscription has been terminated by the server
received: (data) ->
$(".store #main").html(data.html)
/app/controllers/products_controller.rb
# PATCH/PUT /products/1
# PATCH/PUT /products/1.json
def update
respond_to do |format|
if #product.update(product_params)
format.html do
redirect_to #product, notice: 'Product was successfully
updated.'
end
format.json { render :show, status: :ok, location: #product }
#products = Product.all
ApplicationCable.server.broadcast 'products',
html: render_to_string('store/index',
layout: false)
else
format.html { render :edit }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
Thank you for any help!
in products_controller.rb. You should use 'ActionCable.server.broadcast' instead of ApplicationCable.server.broadcast
Have fun!

rails parameters not found

I am developing application using stripe for payment. I have Plan and Subscription model in my app. Plan has many subscriptions and subscription belongs t plan. On plans index page i have plans listings and user can click on any of the plans
<td><%=link_to plan.name, new_subscription_path(:plan_id => plan.id) %></td>
and in my subscription controller i have this
def new
#plan = Plan.find(params[:plan_id])
#subscription = #plan.subscriptions.new
end
def create
#plan = Plan.find(params[:plan_id])
#subscription = #plan.subscriptions.new(subscription_params)
respond_to do |format|
if #subscription.save
format.html { redirect_to #subscription, notice: 'Subscription was successfully created.' }
format.json { render :show, status: :created, location: #subscription }
else
format.html { render :new }
format.json { render json: #subscription.errors, status: :unprocessable_entity }
end
end
end
I am trying to build subscription on particular plan but i am getting this error
Couldn't find Plan with 'id'=
You should update your config/routes.rb to look like:
resources :plans do
resources :subscriptions
end
And later in your template, you should use new_plan_subscription_path like:
<td><%=link_to plan.name, new_plan_subscription_path(:plan_id => plan.id) %></td>
This should help!
Good luck!

How to connect Devise User to ProfilesController Ruby on Rails 4

Before I begin, let me say that I have two different devise user models.
I began removing the unnecessary methods from the scaffold_controller, because devise handles certain actions.
I'm trying to route to the current_user profile page, but I get an error every time I try to add the link in my navbar <%= 'My Profile', current_user %> doesn't work in this case. I plan to setup the associations as soon as I finish hooking up these controllers.
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
# GET /profiles
# GET /profiles.json
def index
#profiles = Profile.all
end
# GET /profiles/1
# GET /profiles/1.json
def show
#user = User.find(params[:id])
respond_to do |format|
format.html
format.json {render :json => #user }
end
end
# GET /profiles/1/edit
def edit
end
# PATCH/PUT /profiles/1
# PATCH/PUT /profiles/1.json
def update
respond_to do |format|
if #profile.update(profile_params)
format.html { redirect_to #profile, notice: 'Profile was successfully updated.' }
format.json { render :show, status: :ok, location: #profile }
else
format.html { render :edit }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# DELETE /profiles/1
# DELETE /profiles/1.json
def destroy
#profile.destroy
respond_to do |format|
format.html { redirect_to profiles_url, notice: 'Profile was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_profile
#profile = Profile.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def profile_params
params[:profile]
end
end
profiles GET /profiles(.:format) profiles#index
POST /profiles(.:format) profiles#create
new_profile GET /profiles/new(.:format) profiles#new
edit_profile GET /profiles/:id/edit(.:format) profiles#edit
profile GET /profiles/:id(.:format) profiles#show
PATCH /profiles/:id(.:format) profiles#update
PUT /profiles/:id(.:format) profiles#update
DELETE /profiles/:id(.:format) profiles#destroy
Assuming (such as bad thing to do), that you have already created the profile while creating the user ( since there is no create action in the controller ) and that a profile belongs_to a user, then this should work:
<%= link_to "My Profile", current_user.profile %>
Generally, I would get my associations set up properly before I even look at the controllers.

best_in_place gem with rails4 not displaying errors

I am trying to use the best_in_place gem with a Rails 4 app. I can
get it to update valid edits to a field fine, but if I enter an
invalid value, I don't see error messages. I have added the .purr
styling rules, but still no joy.
I use the following in the controller:
def update
#transact = Transact.find(params[:id])
respond_to do |format|
if #transact.update_attributes(transact_params)
flash[:notice] = 'Transaction was successfully updated.'
format.html { redirect_to(#transact) }
format.xml { head :ok }
format.json { respond_with_bip(#transact) }
else
#errors = #transact.errors
format.html { render :action => "edit" }
format.xml { render :xml => #transact.errors, :status => :unprocessable_entity }
# format.json {
# render :json => #errors.full_messages,
# :status => :unprocessable_entity
# }
format.json { respond_with_bip(#transact) }
end
end
end
And I've also tried the commented-out code in the above, with similar results.
Here is what the server responds on an invalid value:
Processing by TransactsController#update as JSON
Parameters: {"transact"=>{"shares"=>"6741433.0x"}, "authenticity_token"=>"e+1ZEhVYuEMDURf81Kcxg0Ld28BfY60rRFRSZUq8RsY=", "id"=>"144314"}
Transact Load (0.5ms) SELECT "transacts".* FROM "transacts" WHERE "transacts"."id" = $1 LIMIT 1 [["id", "144314"]]
(0.1ms) BEGIN
(0.3ms) ROLLBACK
Completed 422 Unprocessable Entity in 60ms (Views: 0.2ms | ActiveRecord: 0.9ms)
Is there any thing obvious I am doing wrong?
I had the same problem. My solution was to include ALL these three lines in application.js:
//= require best_in_place
//= require best_in_place.purr
//= require jquery.purr
If jquery.purr or best_in_place.purr was left out, no error message was displayed to the user.
For the record, see my comment above: I was not including best_in_place.purr.js in my javascript assets in addition to best_in_place.js.
Hope it helps someone else overlooking this.
I considered this answered.

Resources