Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ActionCable, Rails API, and React - Modern Single Page Apps

ActionCable, Rails API, and React - Modern Single Page Apps

Presented at RailsPacific 2016(http://www.railspacific.com/).

vipulnsward

May 21, 2016
Tweet

More Decks by vipulnsward

Other Decks in Technology

Transcript

  1. “* The weather in Taiwan is HOT with chances of

    rain in the afternoon. “ - Rexy Second time in Taiwan
  2. What is an API Application? https://developer.github.com/v3/emojis/ # GET /emojis
 


    {
 "+1": "https://github.global.ssl.fastly.net/images/icons/emoji/+1.png?v5",
 "-1": "https://github.global.ssl.fastly.net/images/icons/emoji/-1.png?v5",
 "101": "https://github.global.ssl.fastly.net/images/icons/emoji/100.png?v5",
 "1234": "https://github.global.ssl.fastly.net/images/icons/emoji/1234.png?v5",
 "8ball": "https://github.global.ssl.fastly.net/images/icons/emoji/8ball.png?v5",
 "a": "https://github.global.ssl.fastly.net/images/icons/emoji/a.png?v5",
 "ab": "https://github.global.ssl.fastly.net/images/icons/emoji/ab.png?v5"
 }

  3. Why Rails? Rails Development Environment Nifty Testing Inbuilt security Parameter

    parsing Code Reloads anyone? Header responses Other Rails Goodness: AR, AM, AJ, ACa
  4. What this does Slimmed down middleware* stack No views &

    cruft from AV ApplicationController < ActionController::API

  5. Default Stack - Rack::Sendfile
 - ActionDispatch::Static
 - ActionDispatch::Executor
 - ActiveSupport::Cache::Strategy::LocalCache::Middleware


    - Rack::Runtime
 - ActionDispatch::RequestId
 - Rails::Rack::Logger
 - ActionDispatch::ShowExceptions
 - ActionDispatch::DebugExceptions
 - ActionDispatch::RemoteIp
 - ActionDispatch::Reloader
 - ActionDispatch::Callbacks
 - ActiveRecord::Migration::CheckPending
 - Rack::Head
 - Rack::ConditionalGet
 - Rack::ETag
  6. ActionController::API - ActionController::UrlFor
 - ActionController::Redirecting
 - AbstractController::Rendering and ActionController::ApiRendering
 -

    ActionController::Renderers::All
 - ActionController::ConditionalGet
 - ActionController::BasicImplicitRender
 - ActionController::StrongParameters
 - ActionController::ForceSSL
 - ActionController::DataStreaming
 - AbstractController::Callbacks
 - ActionController::Rescue
 - ActionController::Instrumentation
 - ActionController::ParamsWrapper
  7. Connections # app/channels/application_cable/connection.rb
 module ApplicationCable
 class Connection < ActionCable::Connection::Base
 identified_by

    :current_user
 
 def connect
 self.current_user = find_verified_user
 end
 
 protected
 def find_verified_user
 if current_user = User.find_by(id: cookies.signed[:user_id])
 current_user
 else
 reject_unauthorized_connection
 end
 end
 end
 end
  8. Channels # app/channels/application_cable/channel.rb
 module ApplicationCable
 class Channel < ActionCable::Channel::Base
 end


    end # app/channels/update_channel.rb
 class UpdateChannel < ApplicationCable::Channel
 end # app/channels/chat_channel.rb
 class ChatChannel < ApplicationCable::Channel
 end
 

  9. Channels # app/channels/chat_channel.rb
 class ChatChannel < ApplicationCable::Channel
 # Called when

    the consumer has successfully
 # become a subscriber of this channel.
 def subscribed
 end
 
 def my_custom_action
 end
 
 def user_posted_something
 end
 end

  10. Consumer Connection // app/assets/javascripts/cable.js
 //= require action_cable
 //= require_self
 //=

    require_tree ./channels
 
 (function() {
 this.App || (this.App = {});
 
 App.cable = ActionCable.createConsumer();
 }).call(this);
  11. Streams # app/channels/chat_channel.rb
 class ChatChannel < ApplicationCable::Channel
 def subscribed stop_all_streams


    stream_from "chat_#{params[:room]}"
 end
 end # app/channels/comments_channel.rb class CommentsChannel < ApplicationCable::Channel
 def subscribed
 post = Post.find(params[:id])
 stream_for post
 end
 end

  12. Client Side Subscription App.chatChannel = App.cable.subscriptions.create({ channel: "ChatChannel", room: "Best

    Room" }, {
 //Entry point when some data will be received received: function (data) {
 return this.appendLine(data);
 }, 
 appendLine: function (data) {
 var html;
 html = this.createLine(data);
 return $("[data-chat-room='Best Room']").append(html);
 }, 
 createLine: function (data) {
 return "data[ "sent_by" ] + “: "+ data[ "body" ];
 }
 });
  13. A Simple Component var HelloMessage = React.createClass({
 render: function() {


    return <div>Hello {this.props.name}</div>;
 }
 });
 
 ReactDOM.render(<HelloMessage name="John" />, mountNode);

  14. A Simple Component var Timer = React.createClass({
 getInitialState: function() {


    return {secondsElapsed: 0};
 },
 tick: function() {
 this.setState({secondsElapsed: this.state.secondsElapsed + 1});
 },
 componentDidMount: function() {
 this.interval = setInterval(this.tick, 1000);
 },
 componentWillUnmount: function() {
 clearInterval(this.interval);
 },
 render: function() {
 return (
 <div>Seconds Elapsed: {this.state.secondsElapsed}</div>
 );
 }
 });
 
 ReactDOM.render(<Timer />, mountNode);

  15. console.log('Start')
 var App = React.createClass({
 componentWillMount: function(){
 console.log('componentWillMount');
 },
 


    componentDidMount: function(){
 console.log('componentDidMount');
 },
 
 getInitialState: function(){
 return { status: true}
 },
 
 getDefaultProps: function(){
 return {name: 'John'};
 },
 
 …

  16. …
 
 render: function() {
 console.log('render');
 return <h1 onClick={this.toggleState}>
 {this.state.status.toString()}


    </h1>
 },
 
 componentWillUnmount: function(){
 console.log('componentWillUnmount')
 },
 
 toggleState: function() {
 this.setState({status: !this.state.status})
 }
 });
  17. … componentDidMount() {
 this.setupSubscription();
 }
 … setupSubscription() {
 
 App.comments

    = App.cable.subscriptions.create("CommentsChannel", {
 message_id: this.state.message.id,
 
 connected: function () {
 setTimeout(() => this.perform('follow', { message_id: this.message_id }), 1000);
 },
 
 received: function (data) {
 this.updateCommentList(data.comment);
 },
 
 updateCommentList: this.updateCommentList.bind(this)
 
 });
 
 }