Learn Enough to Be Dangerous
You have to make a choice. Choose...wisely.

Get occasional notifications about things like product discounts, blog posts, and new or updated tutorials. Unsubscribe at any time.

Quick Checkout
or Pay by Credit Card
Error processing your payment
  • You didn't choose whether or not to be added to the mailing list
Confirm
$0.00

Payments and credit card details are securely managed and protected by Learn Enough's payment processor, Stripe. More information on their site:

CART
Total
$0.00

Your Cart is Empty

$30
$300
$300
$XY
$XY
1234
  • Action Cable

Learn Enough Society

Certificate of Course Completion

This page certifies that mokubo has completed Learn Enough Action Cable to Be Dangerous! 🎉

About the tutorial
Learn Enough Action Cable to Be Dangerous is an introduction to Action Cable, a Ruby library that combines ultra-responsive real-time applications with the power and convenience of Rails. Learn to build and deploy a highly responsive chat app that takes advantage of Action Cable’s elegant interface to the WebSockets protocol. Read full post
17 Mar 15:45, 2020
6 Nov 22:33, 2020
Exercise Answers
Exercise Question:
  • Does the code in Listing 2.2 still work if you swap the order of the function call and its definition?
  • mokubo's Answer:

    app/assets/javascript/application.js

    greeting_doubler('hello, world!');
    
    greeting_doubler = function(phrase) {
      alert(phrase + " " + phrase);
    };
    
    

    JavaScript Console

    Uncaught ReferenceError: greeting_doubler is not defined
    
    Exercise Question:
  • What happens if you replace 'hello, world!' in Listing 2.2 with '¡Hola, mundo!'?
  • mokubo's Answer:

    app/assets/javascript/application.js

    greeting_doubler = function(phrase) {
      alert(phrase + " " + phrase);
    };
    greeting_doubler('¡Hola, mundo!');
    
    

    Browser alert

    ¡Hola, mundo! ¡Hola, mundo!
    

    It works!!

    Exercise Question:
  • Can you guess the jQuery syntax to select elements with a particular CSS class? Google it only if necessary.
  • mokubo's Answer:

    Yes. By replacing target #messages-table with a target class like this:

    message_appender = function(content) {
      $('.messages-table').append(content);
    }
    $(document).on('turbolinks:load', function() {
      message_appender('I\'m appended to CSS class .messages-table!');
    });
    
    

    Result is:

    I'm appended to CSS class .messages-table!
    
    Exercise Question:
  • What happens if you reverse the order of the appender and document on load in Listing 2.5?
  • mokubo's Answer:

    app/assets/javascripts/application.js

    $(document).on('turbolinks:load', function() {
      message_appender('I\'m from above lines of message_appender!');
    });
    message_appender = function(content) {
      $('.messages-table').append(content);
    }
    
    

    It's still working!

    I'm from above lines of message_appender!
    
    Exercise Question:
  • What happens if you remove the parentheses in the call to message_appender in Listing 2.8?
  • mokubo's Answer:

    app/assets/javascript/messages.coffee

    
    message_appender = content ->
      $('#messages-table').append(content)
    
    $(document).on 'turbolinks:load', ->
      message_appender('hello, world! How\'s it going?')
    
    

    It returns an error in the JavaScript console

    Uncaught ReferenceError: content is not defined
    
    Exercise Question:
  • Replace on data.content with data.username in Listing 3.5. Do you get the correct value?
  • mokubo's Answer:

    app/assets/javascripts/channels/room.coffee

    App.room = App.cable.subscriptions.create "RoomChannel",
      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) ->
        # Called when there's incoming data on the websocket for this channel
        alert data.username
    
    

    Web browser make alert with username like this:

    alice
    
    Exercise Question:
  • Replace the alert in Listing 3.5 with a call to console.log. Use your browser’s web inspector to confirm that the correct value appears in the console (data.username if you completed the previous exercise, data.content otherwise). If you don’t have any experience with web inspectors or consoles, use your technical sophistication (Box 1.1) to figure it out.
  • mokubo's Answer:

    app/assets/javascripts/channels/room.coffee

    App.room = App.cable.subscriptions.create "RoomChannel",
      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) ->
        # Called when there's incoming data on the websocket for this channel
        console.log data.username
    

    Web browser JavaScript Console print username

    alice
    
    Exercise Question:
  • Describe the steps taken when Alice sends a message to Bob.
  • mokubo's Answer:
    • (client) Alice submits a new message using a remote form.
    • (server) The Messages controller receives the submission, saves the message to the database, and broadcasts the result.
    • (client) Because every user is subscribed to the Room channel, each client receives the broadcast and executes the received method, which appends the message HTML to the messages table for both Alice and Bob.
    Exercise Question:
  • What happens if you remove the preventDefault line in Listing 3.11?
  • mokubo's Answer:

    create a newline in the form after submition

    Exercise Question:
  • Remove the call to f.submit in Listing 3.9 to see if $('input').click() still works.
  • mokubo's Answer:

    removed f.submit from _message_form.html.erb

    <div class="message-input">
      <%= form_for(@message, remote: true) do |f| %>
        <%= f.text_area :content %>
      <% end %>
    </div>
    
    

    Result: Message is not going to be brodcast or saved in the database due to missing submit action in the form

    Exercise Question:
  • Because of the authorization before filter in Listing 4.3, it’s a little tricky to test that the system is really rejecting unauthorized connections. One way is to comment out the logged-in user filter and then change logged_in? to false in Listing 4.1. This forces the system to execute the reject_unauthorized_connection branch of the if statement in find_verified_user. Can you tell by looking at the Rails server log if it worked?
  • mokubo's Answer:

    connection.rb

    module ApplicationCable
      class Connection < ActionCable::Connection::Base
        include SessionsHelper
    
        identified_by :message_user
    
        def connect
          self.message_user = find_verified_user
        end
    
        private
    
        def find_verified_user
          if flase
            # current_user
          else
            reject_unauthorized_connection
          end
        end
    
      end
    end
    

    Terminal

    An unauthorized connection attempt was rejected
    
    Exercise Question:
  • Use console.log to determine the value of the scrollHeight in your current application. Hint: In most browsers, you can use console.log and jQuery directly in the web inspector console.
  • mokubo's Answer:

    JavaScript Console in the browser

    > console.log($('#messages')[0].scrollHeight)
    
    1212
    undefined
    

    a line above is not working from room.coffee or application.js while ```console.log('hello, world!'); is working from file

    Reference: https://javascriptio.com/view/350605/cannot-read-property-scrollheight-of-null

    Exercise Question:
  • Make a gratuitous change to the message partial, such as adding a random word. Does the change appear for messages created using JavaScript?
  • mokubo's Answer:

    Add " foobar" in embedded ruby code in the _message.html.erb

    
    <div class="message">
      <div class="message-user">
        <%= message.user.username %>:
      </div>
      <div class="message-content">
        <%= message.content + " foobar" %>
      </div>
    </div>
    
    

    Browser

    bob: hello foobar

    Exercise Question:
  • Using blockquote syntax for Markdown, enter a multi-line quote into the chat using Shift-Enter. Does the display look good? Improve the display by appending the Sass in Listing 5.6 to the end of main.sass.
  • mokubo's Answer:

    Blockquote appears as normal text without style

    Added style on blockquote element and it appears to be inlined block with light gray background

    Exercise Question:
  • Confirm that you can include links in your chat messages. How would you make the link Learn Enough to Be Dangerous (including italics), link to www.learnenough.com?
  • mokubo's Answer:

    It's woking!

    [Link text](Link URL or path)
    
    Exercise Question:
  • Use the Markdown image syntax to include the image at https://cdn.learnenough.com/archer_ants.jpg. Are you able to get a result that looks something like Figure 5.5?
  • mokubo's Answer:

    This line will display an image from the source on the markdown.

    ![Alt value](https://cdn.learnenough.com/archer_ants.jpg)
    

    Alt value

    Exercise Question:
  • Include the SessionsHelper (as in Listing 4.1) and then try using current_user in Listing 5.7. What error do you get?
  • mokubo's Answer:

    room_channel

    class RoomChannel < ApplicationCable::Channel
      include SessionsHelper
    
      def subscribed
        stream_from "room_channel"
        stream_from "room_channel_user_#{current_user.id}"
        current_user
      end
    
      def unsubscribed
        # Any cleanup needed when channel is unsubscribed
      end
    end
    
    

    No error message but mention stop trigering alert

    Exercise Question:
  • Write some Message model tests for the mentions method.
  • mokubo's Answer:

    message_test.rb

    require 'test_helper'
    
    class MessageTest < ActiveSupport::TestCase
    
      def setup
        @message = users(:example).messages.build(content: "Lorem ipsum @example @nonexistant")
        @user = users(:example)
      end
    
      test "should be valid" do
        assert @message.valid?, @message.errors.full_messages
      end
    
      test "should not be blank" do
        @message.content = " "
        assert !@message.valid?
      end
    
      test "should mention valid" do
        assert_includes(@message.mentions.to_s, @user.username)
        assert_not_includes(@message.mentions.to_s, "nonexistant")
      end
    
    end
    
    

    Result

    $ rails test
    Running via Spring preloader in process 3314
    Started with run options --seed 56767
    
    Run options: --seed 56767--=---=---=---=---=---=---=---=---=---=---] 0% Time: 00:00:00,  ETA: ??:??:??
    
    # Running:
    
    .  20/2: [=====                                                    ] 10% Time: 00:00:00,  ETA: 00:00:0.  20/3: [========                                                 ] 15% Time: 00:00:00,  ETA: 00:00:0..  20/5: [==============                                           ] 25% Time: 00:00:00,  ETA: 00:00:.  20/6: [=================                                        ] 30% Time: 00:00:00,  ETA: 00:00:0..  20/8: [======================                                   ] 40% Time: 00:00:00,  ETA: 00:00:..  20/10: [============================                            ] 50% Time: 00:00:00,  ETA: 00:00:.  20/11: [==============================                          ] 55% Time: 00:00:00,  ETA: 00:00:0.  20/12: [=================================                       ] 60% Time: 00:00:00,  ETA: 00:00:0..  20/14: [=======================================                 ] 70% Time: 00:00:00,  ETA: 00:00:.  20/15: [==========================================              ] 75% Time: 00:00:01,  ETA: 00:00:0.  20/16: [============================================            ] 80% Time: 00:00:01,  ETA: 00:00:0.  20/17: [===============================================         ] 85% Time: 00:00:01,  ETA: 00:00:0.  20/18: [==================================================      ] 90% Time: 00:00:01,  ETA: 00:00:0.  20/19: [=====================================================   ] 95% Time: 00:00:01,  ETA: 00:00:0.  20/20: [=======================================================] 100% Time: 00:00:01, Time: 00:00:01
    .
    Finished in 1.18467s
    20 tests, 39 assertions, 0 failures, 0 errors, 0 skips
    
    
    
    Finished in 1.202936s, 16.6260 runs/s, 32.4207 assertions/s.
    20 runs, 39 assertions, 0 failures, 0 errors, 0 skips
    $
    
    Exercise Question:
  • Why can’t you use the original unless from Listing 4.4 in Line 10 of Listing 5.10?
  • mokubo's Answer:

    It's working for both with no errors

    Join the Mailing List

    Get occasional notifications about things like product discounts, blog posts, and new or updated tutorials. Unsubscribe at any time.