Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

action cable setup #805

Merged
merged 41 commits into from
Aug 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9b0487e
WIP action cable setup
ViditChitkara Jul 4, 2019
3b7b2bf
basic action cable setup complete
ViditChitkara Jul 6, 2019
10d42f4
minor change
ViditChitkara Jul 6, 2019
c7af515
minor changes
ViditChitkara Jul 10, 2019
826745f
few changes
ViditChitkara Jul 14, 2019
6e8e33b
initial working functionality complete
ViditChitkara Jul 18, 2019
53d8aad
Merge branch 'development' of https://github.com/publiclab/mapknitter…
alaxalves Jul 30, 2019
193f553
Refactoring code
alaxalves Jul 30, 2019
454cf2f
Merge branch 'development' of https://github.com/publiclab/mapknitter…
alaxalves Aug 4, 2019
852d5f9
Adding Foreman gem
alaxalves Aug 4, 2019
8fd235d
Scheduling Puma and Passenger servers
alaxalves Aug 4, 2019
a158e77
WIP action cable setup
ViditChitkara Jul 4, 2019
35aa34f
basic action cable setup complete
ViditChitkara Jul 6, 2019
2610522
minor change
ViditChitkara Jul 6, 2019
4040bc3
minor changes
ViditChitkara Jul 10, 2019
e231418
few changes
ViditChitkara Jul 14, 2019
55baec4
initial working functionality complete
ViditChitkara Jul 18, 2019
4904217
Refactoring code
alaxalves Jul 30, 2019
53084a1
Adding Foreman gem
alaxalves Aug 4, 2019
add9f16
Scheduling Puma and Passenger servers
alaxalves Aug 4, 2019
87deccd
few minor fix
ViditChitkara Aug 4, 2019
2432805
added a few tests
ViditChitkara Aug 6, 2019
2f9f6eb
Merge branch 'action-cable-integration' of https://github.com/publicl…
alaxalves Aug 6, 2019
a389a97
Merge branch 'development' of https://github.com/publiclab/mapknitter…
alaxalves Aug 6, 2019
54b5ef7
Refactoring connection module
alaxalves Aug 6, 2019
fdcd38a
Merge branch 'development' of https://github.com/publiclab/mapknitter…
alaxalves Aug 6, 2019
9191cf6
Using strong params in requests
alaxalves Aug 6, 2019
d0bdd43
Merge branch 'development' of https://github.com/publiclab/mapknitter…
alaxalves Aug 6, 2019
855089a
Merge branch 'development' of https://github.com/publiclab/mapknitter…
alaxalves Aug 6, 2019
d07cd13
Merge branch 'development' of https://github.com/publiclab/mapknitter…
alaxalves Aug 14, 2019
f271a84
added documentation
ViditChitkara Aug 14, 2019
fe95fc8
added more docs
ViditChitkara Aug 14, 2019
53bd5ab
added tests
ViditChitkara Aug 14, 2019
a522eaa
Merge branch 'development' of https://github.com/publiclab/mapknitter…
alaxalves Aug 16, 2019
3b2274a
Merge branch 'action-cable-integration' of https://github.com/publicl…
alaxalves Aug 16, 2019
ac8e40c
Using puma as dependency and correct image controller
alaxalves Aug 16, 2019
f002ad0
added a few tests
ViditChitkara Aug 18, 2019
1464157
a few changes
ViditChitkara Aug 18, 2019
b033cc5
remove unnecessary render
ViditChitkara Aug 19, 2019
e568fbe
few test fixes
ViditChitkara Aug 20, 2019
7779a96
Merge branch 'development' of https://github.com/publiclab/mapknitter…
alaxalves Aug 20, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ group :dependencies do
gem 'bootsnap', '~> 1.4.4'
gem 'turbolinks', '~> 5'
gem 'mini_magick', '~> 4.8'
gem 'puma', '~> 4.1.0'

# if you use amazon s3 for warpable image storage
gem 'aws-sdk', '~> 1.5.7'
Expand All @@ -37,12 +38,15 @@ group :dependencies do
# compiling markdown to html
gem 'rdiscount', '2.2.0.1'

# Process manager for applications with multiple components
gem 'foreman', '~> 0.85.0'

# asset pipelining
gem 'bootstrap-sass'
gem 'sassc-rails'
gem 'jquery-rails'
gem 'sprockets', '3.7.2'
gem "sprockets-rails"
gem 'sprockets-rails'
gem 'sass', require: 'sass'
gem 'autoprefixer-rails', '~> 9.5.1.1'
gem 'uglifier', '~> 4.1.20'
Expand All @@ -65,11 +69,11 @@ end

group :development, :test do
gem 'capybara'
gem 'puma'
gem 'selenium-webdriver'
gem 'byebug', '~> 11.0.1', platforms: [:mri, :mingw, :x64_mingw]
gem 'faker', '~> 2.1.2'
gem 'pry-rails', '~> 0.3.9'
gem 'action-cable-testing'
end

group :development do
Expand Down
15 changes: 10 additions & 5 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ GEM
RubyInline (3.12.4)
ZenTest (~> 4.3)
ZenTest (4.11.2)
action-cable-testing (0.6.0)
actioncable (>= 5.0)
actioncable (5.2.3)
actionpack (= 5.2.3)
nio4r (~> 2.0)
Expand Down Expand Up @@ -89,6 +91,8 @@ GEM
faker (2.1.2)
i18n (>= 0.8)
ffi (1.11.1)
foreman (0.85.0)
thor (~> 0.19.1)
friendly_id (5.2.5)
activerecord (>= 4.0.0)
geokit (1.13.1)
Expand Down Expand Up @@ -210,7 +214,7 @@ GEM
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.1.0)
rails-html-sanitizer (1.2.0)
loofah (~> 2.2, >= 2.2.2)
rails-perftest (0.0.7)
railties (5.2.3)
Expand Down Expand Up @@ -261,9 +265,8 @@ GEM
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sassc (2.0.1)
sassc (2.1.0)
ffi (~> 1.9)
rake
sassc-rails (2.1.2)
railties (>= 4.0.0)
sassc (>= 2.0)
Expand Down Expand Up @@ -296,7 +299,7 @@ GEM
sqlite3 (1.4.1)
terrapin (0.6.0)
climate_control (>= 0.0.3, < 1.0)
thor (0.20.3)
thor (0.19.4)
thread_safe (0.3.6)
tilt (2.0.9)
turbolinks (5.2.0)
Expand Down Expand Up @@ -330,6 +333,7 @@ PLATFORMS

DEPENDENCIES
RubyInline (~> 3.12.4)
action-cable-testing
autoprefixer-rails (~> 9.5.1.1)
aws-sdk (~> 1.5.7)
bootsnap (~> 1.4.4)
Expand All @@ -338,6 +342,7 @@ DEPENDENCIES
capybara
codecov
faker (~> 2.1.2)
foreman (~> 0.85.0)
friendly_id
geokit-rails (= 1.1.4)
httparty
Expand All @@ -357,7 +362,7 @@ DEPENDENCIES
passenger
popper_js (~> 1.11, >= 1.11.1)
pry-rails (~> 0.3.9)
puma
puma (~> 4.1.0)
rack_session_access
rails (~> 5.2.3)
rails-controller-testing
Expand Down
2 changes: 2 additions & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
passenger: passenger start
puma: puma -C config/puma.rb
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ViditChitkara Is this the proper puma command or are there any other flags needed?

30 changes: 30 additions & 0 deletions SYNCHRONOUS_EDITING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
The new synchronous editing feature
===================================

With the introduction of ActionCable to our system, it has been possible
to do perform real-time tasks quite easily. We have used rail's default
action cable to make a _concurrent_editing_channel.rb_ in the _app/channels_ folder,
to handle all the incoming requests and consists of all the business
logic as well. At the frontend we have, _app/javascripts/channels/concurrent_editing.js_ which
handles the logic at the browser or the frontend.

## Flow of the feature:

1. When the map is updated, the _speak_ method of _concurrent_editing.js_ is called which requests
the _sync_ method of _concurrent_editing_channel.rb_ to broadcast the updated data to
the connected users.

2. The broadcasted data is finally caught by the _received_ function of _app/javascripts/channels/concurrent_editing.js_

3. Finally the _received_ function calls the _synchronizeData_ function to update
all the fresh data on the map.


## Testing:

1. The _action-cable-testing_ gem is used for the feature's testing. It has some really
cool testing functionality which was required for our use case.

2. Currently we have separate tests written for connection related features and channel
specific features. The relevant files are test/channels/concurrent_editing_channel_test.rb and
test/channels/connection_test.rb
67 changes: 34 additions & 33 deletions app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,41 +12,42 @@
//


//= require leaflet/dist/leaflet-src.js
// = require leaflet/dist/leaflet-src.js

//= require jquery
//= require jquery-ujs
//= require jquery/dist/jquery.js
//= require jquery-ujs/src/rails.js
//= require jquery-ui/jquery-ui.min.js
// = require jquery
// = require jquery-ujs
// = require jquery/dist/jquery.js
// = require jquery-ujs/src/rails.js
// = require jquery-ui/jquery-ui.min.js

//= require blueimp-tmpl/js/tmpl.js
//= require blueimp-file-upload/js/vendor/jquery.ui.widget
//= require blueimp-file-upload/js/jquery.fileupload
//= require blueimp-file-upload/js/jquery.fileupload-process
//= require blueimp-file-upload/js/jquery.fileupload-ui
// = require blueimp-tmpl/js/tmpl.js
// = require blueimp-file-upload/js/vendor/jquery.ui.widget
// = require blueimp-file-upload/js/jquery.fileupload
// = require blueimp-file-upload/js/jquery.fileupload-process
// = require blueimp-file-upload/js/jquery.fileupload-ui

//= require bootstrap/dist/js/bootstrap.js
// = require bootstrap/dist/js/bootstrap.js

//= require leaflet-fullHash.js
//= require leaflet-providers/leaflet-providers.js
//= require leaflet-environmental-layers/dist/LeafletEnvironmentalLayers.js
//= require leaflet-environmental-layers/src/windRoseLayer.js
//= require leaflet-easybutton/src/easy-button.js
//= require sparklines/source/sparkline.js
//= require glfx-js/dist/glfx.js
//= require ion-rangeslider/js/ion.rangeSlider.js
//= require exif-js/exif.js
//= require webgl-distort/dist/webgl-distort.js
//= require mapknitter/core/Class.js
//= require leaflet-spin/example/spin/dist/spin.min.js
//= require leaflet-spin/example/leaflet.spin.min.js
//= require image-sequencer/dist/image-sequencer.js
//= require leaflet-toolbar/dist/leaflet.toolbar.js
//= require leaflet-draw/dist/leaflet.draw-src.js
//= require leaflet-distortableimage/dist/leaflet.distortableimage.js
//= require leaflet-illustrate/dist/Leaflet.Illustrate.js
//= require leaflet-distortableimage/src/edit/tools/EditAction.js
//= require mapknitter/Map.js
// = require leaflet-fullHash.js
// = require leaflet-providers/leaflet-providers.js
// = require leaflet-environmental-layers/dist/LeafletEnvironmentalLayers.js
// = require leaflet-environmental-layers/src/windRoseLayer.js
// = require leaflet-easybutton/src/easy-button.js
// = require sparklines/source/sparkline.js
// = require glfx-js/dist/glfx.js
// = require ion-rangeslider/js/ion.rangeSlider.js
// = require exif-js/exif.js
// = require webgl-distort/dist/webgl-distort.js
// = require mapknitter/core/Class.js
// = require leaflet-spin/example/spin/dist/spin.min.js
// = require leaflet-spin/example/leaflet.spin.min.js
// = require image-sequencer/dist/image-sequencer.js
// = require leaflet-toolbar/dist/leaflet.toolbar.js
// = require leaflet-draw/dist/leaflet.draw-src.js
// = require leaflet-illustrate/dist/Leaflet.Illustrate.js
// = require leaflet-distortableimage/dist/leaflet.distortableimage.js
// = require leaflet-distortableimage/src/edit/tools/EditAction.js
// = require mapknitter/Map.js
// = require cable.js

//= require_tree .
// = require_tree .
11 changes: 11 additions & 0 deletions app/assets/javascripts/cable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//
//= require action_cable
//= require_self
//= require_tree ./channels

(function() {
this.App || (this.App = {});

App.cable = ActionCable.createConsumer();

}).call(this);
26 changes: 26 additions & 0 deletions app/assets/javascripts/channels/concurrent_editing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* Handles all the frontend interactions with action cable and the server. */

App.concurrent_editing = App.cable.subscriptions.create("ConcurrentEditingChannel", {
connected: function() {
// Called when the subscription is ready for use on the server
},

disconnected: function() {
// Called when the subscription has been terminated by the server
},

received: function(data) {
// Called when there's incoming data on the websocket for this channel
window.mapKnitter.synchronizeData(data.changes);
},

speak: function(changes) {
/* Called when an image is updated from Map.js ('saveImage' function).
* This function calls concurrent_editing_channel.rb's 'sync' method
* which is responsible for broadcasting the updated warpables
* to all the user's connected to the concurrent_editing channel. */
return this.perform("sync", {
changes: changes
});
}
});
29 changes: 29 additions & 0 deletions app/assets/javascripts/mapknitter/Map.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,32 @@ MapKnitter.Map = MapKnitter.Class.extend({
if (this.editing._mode !== "lock") { e.stopPropagation(); }
},

/* Called by the concurrent_editing.js channel's 'received' function (app/assets/javascripts/channels/concurrent_editing.js).
* It recieves a list of updated warpables,i.e. list of images with updated corner points. The aim of writing this function
* is to reposition the updated images onto the map on every connected browser (via the ActionCable). */

synchronizeData: function(warpables) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updation corresponding to each change will happen with the help of this function. This function gets all the necessary information related to the warpables on the map (which are broadcasted my the server on the concurrent_editing_channel)

var layers = [];
map.eachLayer(function(l) {layers.push(l)});
alaxalves marked this conversation as resolved.
Show resolved Hide resolved
layers = layers.filter(image => (image._url!=undefined || image._url!=null));
warpables.forEach(function(warpable) {
corners = [];
warpable.nodes.forEach(function(node) {
corners.push(L.latLng(node.lat, node.lon));
});

x = corners[2];
y = corners [3];
corners [2] = y;
corners [3] = x;

console.log(corners);

layer = layers.filter(l => l._url==warpable.srcmedium)[0];
layer.setCorners(corners);
});
},

saveImageIfChanged: function () {
var img = this,
edit = img.editing;
Expand Down Expand Up @@ -424,6 +450,9 @@ MapKnitter.Map = MapKnitter.Class.extend({
beforeSend: function (e) {
$('.mk-save').removeClass('fa-check-circle fa-times-circle fa-green fa-red').addClass('fa-spinner fa-spin')
},
success: function(data) {
App.concurrent_editing.speak(data);
},
complete: function (e) {
$('.mk-save').removeClass('fa-spinner fa-spin').addClass('fa-check-circle fa-green')
},
Expand Down
4 changes: 4 additions & 0 deletions app/channels/application_cable/channel.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module ApplicationCable
class Channel < ActionCable::Channel::Base
end
end
17 changes: 17 additions & 0 deletions app/channels/application_cable/connection.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user

def connect
self.current_user = find_verified_user
end

private

def find_verified_user
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This handles the authentication part.

User.find(cookies.signed["user_id"])
rescue ActiveRecord::RecordNotFound
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

reject_unauthorized_connection
end
end
end
17 changes: 17 additions & 0 deletions app/channels/concurrent_editing_channel.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class ConcurrentEditingChannel < ApplicationCable::Channel
# This class handles the server side logic of the actioncable communication.

def subscribed
# Called first to connect user to the channel.
stream_from "concurrent_editing_channel"
end

def unsubscribed
# Any cleanup needed when channel is unsubscribed
end

def sync(changes)
# Responsible for broadcasting the updated warpables or simply images to the user's connected on this channel.
ActionCable.server.broadcast 'concurrent_editing_channel', changes
end
end
4 changes: 3 additions & 1 deletion app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ def current_user
user_id = session[:user_id]
if user_id
begin
@user = User.find(user_id)
u = User.find(user_id)
cookies.signed["user_id"] = u.id
@user = u
rescue StandardError
@user = nil
end
Expand Down
6 changes: 5 additions & 1 deletion app/controllers/images_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,11 @@ def update
@warpable.locked = params[:locked]
@warpable.cm_per_pixel = @warpable.get_cm_per_pixel
@warpable.save
render html: 'success'

respond_to do |format|
format.html { render html: 'success' }
format.json { render json: @warpable.map.fetch_map_data }
end
else
render plain: 'You must be logged in to update the image, unless the map is anonymous.'
end
Expand Down
6 changes: 6 additions & 0 deletions app/models/map.rb
Original file line number Diff line number Diff line change
Expand Up @@ -273,4 +273,10 @@ def add_tag(tagname, user)
tagname = tagname.downcase
tags.create(name: tagname, user_id: user.id, map_id: id) unless has_tag(tagname)
end

def fetch_map_data
# fetches a list of updated warpables along with their corners in a json format.
data = warpables
data.to_json
end
end
1 change: 1 addition & 0 deletions app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<link rel="shortcut icon" href="/images/mapknitter-255.png">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
<%= stylesheet_link_tag 'application' %>
<%= action_cable_meta_tag %>
<%= javascript_include_tag 'application' %>
</head>
<body>
Expand Down
3 changes: 3 additions & 0 deletions config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,7 @@
# Use an evented file watcher to asynchronously detect changes in source code,
# routes, locales, etc. This feature depends on the listen gem.
config.file_watcher = ActiveSupport::EventedFileUpdateChecker

config.action_cable.url = "ws://localhost:3000/cable"
config.action_cable.allowed_request_origins = [/http:\/\/*/, /https:\/\/*/]
end
Loading