Skip to content

Commit

Permalink
[ver] 5.0.0 (#40)
Browse files Browse the repository at this point in the history
* changes

* [internal] refactoring

* [test] refactoring

* [internal] refactoring

* [internal] refactoring

* [test]

* [internal] refactoring

* [internal] refactoring

* [test]

* [internal] refactoring

* [internal] refactoring

* [internal] refactoring

* [internal] refactoring

* [test]

* [internal] refactoring

* [test]

* Rubocop: NewCops

* [internal] remove unneeded line

* [internal] refactoring

* [internal] refactoring

* [internal] refactoring

* [test] refactoring

* [internal] refactoring

* [test] closer WS to reality

* [internal] refactoring

* [test]

* [internal] refactoring

* [internal] refactoring

* [test]

* [internal] refactoring

* [feature] Only Redis can be a storage

* [internal] WsConnectionStorage - improve #get and #set

* [test]

* [test]

* WsConnectionStorage#find

* [internal] WsConnectionFinder

* [internal] WsConnectionStorage#find scans hashes

* [internal] find => scan

* [test]

* [internal] refactoring in progress

* [test] fix

* [internal] WsConnectionStorage#hlen

* [test] refactoring

* [internal] refactoring

* [internal] WsConnectionStorage#del can delete hash's keys

* [internal] no WsConnectedResourcesManager

* [internal] refactoring

* [internal] refactoring

* [internal] changes for native Redis data types

* [internal] WsConnectionFinder.call(:all)

* [internal] Sender accepts a hash with token

* [internal] Sender accepts {'class' => SomeClass}

* [internal] Broadcaster is in progress

* [test]

* [test] refactoring

* [internal] refactoring

* [test]

* [test]

* [test]

* [test]

* [test]

* [internal] Fix Hub#connected_uuids

* [internal] change PREFIX for Hub

* [test] refactoring

* [test] refactoring

* [internal] change Redis keys

* [fix]

* [fix] 2 errors have left

* [internal] refactoring

* All tests pass

* [internal] refactoring

* [internal] refactoring

* [internal] Refactoring

* [internal] refactoring

* [internal] refactoring

* [internal] refactoring

* A different Redis structure (#38)

* [test]

* [test]

* [internal] refactoring

* [test] refactoring

* [internal] support for Redis sets is in progress

* [test] refactoring

* [internal] add new methods to WsConnectionStorage

* a different Redis structure

* [internal] refactoring

* [feature] a different API for Hubs

* [feature] Hub.set

* [internal] WsConnectionFinder can find Hub's connections

* [test] refactoring

* [internal] refactoring

* [internal] refactoring

* [internal] refactoring

* [internal] refactoring

* [test]

* [test] silence warnings

* [test] refactoring

* [test]

* [internal] CleanerJob

* [test]

* [internal] WsConnectionCleaner

* [internal] check connection in the main thread

* [internal] .call() => .()

* [internal] refactoring

* [test] add links to user & admin panels

* [internal] refactoring

* [feature][test] don't establish ws connection if not signed in

* [feature] emit with payload: or data:

* [internal] send idempotency key always

* [test] remove unneeded code

* [test] refactoring

* [internal] don't use SenderJob if a 'simple' recipient

* [test][dummy] update loco config

* [test][dummy][frontend] refactor a config

* [test][dummy][frontend] models in config

* [test][dummy][frontend] controllers in config

* [test][dummy][frontnend] App controller

* [test][dummy][frontend] refactoring

* [test][dummy][frontend] no Object.assign

* [test]

* [internal] ping pong

* [internal] no CleanerJob

* [internal] send to a given UUID once

* [test]

* [internal] no source-maps-fixer

* [test]

* [ver][test][doc]

* [doc] README

* [test] update front-end deps

* [doc] README
  • Loading branch information
himn1 authored Dec 23, 2020
1 parent 1086aac commit f711936
Show file tree
Hide file tree
Showing 80 changed files with 5,566 additions and 2,327 deletions.
13 changes: 11 additions & 2 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require: rubocop-rails

AllCops:
NewCops: enable
TargetRubyVersion: 2.6
Exclude:
- node_modules/**/*
Expand All @@ -19,7 +20,7 @@ Layout/InitialIndentation:

Layout/LineLength:
Enabled: true
Max: 97
Max: 103
Exclude:
- test/dummy/config/environments/production.rb

Expand All @@ -33,11 +34,13 @@ Metrics/BlockLength:
Exclude:
- loco-rails.gemspec
- test/dummy/config/routes.rb
- test/loco/*

Metrics/ClassLength:
Enabled: true
Exclude:
- test/integration/**/*
- test/loco/**/*

Metrics/MethodLength:
Enabled: true
Expand All @@ -56,6 +59,9 @@ Rails/ApplicationJob:
Exclude:
- app/jobs/loco/*

Style/ArrayCoercion:
Enabled: false

Style/Documentation:
Enabled: false

Expand All @@ -65,7 +71,10 @@ Style/FrozenStringLiteralComment:
- lib/generators/loco/file_injector/templates/application_controller.rb
- lib/generators/loco/file_injector/templates/connection.rb

Style/LambdaCall:
Enabled: false

Style/MixinUsage:
Enabled: true
Exclude:
- lib/generators/loco/file_injector/templates/application_controller.rb
- lib/generators/loco/file_injector/templates/application_controller.rb
15 changes: 11 additions & 4 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
loco-rails (4.1.1)
loco-rails (5.0.0)
loco-rails-core (~> 0.2.0)
rails (>= 5.0)

Expand Down Expand Up @@ -80,6 +80,7 @@ GEM
concurrent-ruby (1.1.7)
crass (1.0.6)
database_cleaner (1.8.5)
diff-lcs (1.4.4)
erubi (1.9.0)
ffi (1.13.1)
globalid (0.4.2)
Expand Down Expand Up @@ -152,6 +153,13 @@ GEM
redis (4.2.2)
regexp_parser (1.7.1)
rexml (3.2.4)
rspec-expectations (3.9.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-mocks (3.9.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-support (3.9.3)
rubocop (0.91.0)
parallel (~> 1.10)
parser (>= 2.7.1.1)
Expand All @@ -172,8 +180,6 @@ GEM
selenium-webdriver (3.142.7)
childprocess (>= 0.5, < 4.0)
rubyzip (>= 1.2.2)
source_maps_fixer (0.1.4)
rails (>= 5.0, < 7.0)
sprockets (4.0.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
Expand Down Expand Up @@ -207,10 +213,11 @@ DEPENDENCIES
mysql2 (~> 0.5.3)
puma (~> 4.3.6)
redis (~> 4.2.2)
rspec-expectations (~> 3.9.2)
rspec-mocks (~> 3.9.1)
rubocop
rubocop-rails
selenium-webdriver (~> 3.142)
source_maps_fixer
will_paginate (~> 3.3.0)

BUNDLED WITH
Expand Down
21 changes: 14 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class User
class RoomsController < UserController
def join
@hub.add_member current_user
emit @room, :member_joined, data: {
emit @room, :member_joined, payload: {
room_id: @room.id,
member: {
id: current_user.id,
Expand Down Expand Up @@ -143,7 +143,7 @@ This is just the tip of the iceberg. Look at [Loco-JS](https://github.com/locofr
* [Loco-Rails-Core](https://github.com/locoframework/loco-rails-core) - Rails plugin that has been extracted from Loco-Rails so it could be used as a stand-alone lib. It provides a logical structure for JavaScript code that corresponds with Rails` controllers and their actions that handle a given request. Loco-Rails-Core requires [Loco-JS-Core](https://github.com/locoframework/loco-js-core) to work.
* modern Ruby (tested on >= 2.3.0)
* Rails 5
* [Redis](http://redis.io) and [redis](https://github.com/redis/redis-rb) gem - Loco-Rails stores information about WebSocket connections in Redis. It is not required if you don't want to use ActionCable, or you use Rails in the development environment. In the last case - Loco-Rails uses an in-process data store or Redis (if available).
* [Redis](http://redis.io) and [redis](https://github.com/redis/redis-rb) gem - Loco-Rails stores information about WebSocket connections in Redis. It is not required if you don't want to use ActionCable.

# 📥 Installation

Expand Down Expand Up @@ -195,9 +195,8 @@ end
Where:

* notifications_size - max number of notifications returned from the server at once
* app_name - used as key's prefix to store info about current WebSocket connections in Redis or memory

In a production environment - it's better not to store all the data Loco-Rails needs to work in memory. A better option is Redis, which is shared between app servers.
* app_name - used as key's prefix to store info about current WebSocket connections in Redis

If Loco-Rails discovers Redis instance under `Redis.current`, it will use it. Except that, you can specify Redis instance directly using `redis_instance: Redis.new(your_config)`.

2️⃣ Browse all generated files and customize them according to the comments.
Expand Down Expand Up @@ -225,7 +224,7 @@ include Loco::Emitter
receivers = [article.user, Admin, 'a54e1ef01cb9']
data = { foo: 'bar' }

emit(article, :confirmed, to: receivers, data: data)
emit(article, :confirmed, to: receivers, payload: data)
```

Arguments:
Expand Down Expand Up @@ -288,7 +287,7 @@ Details:
Important instance methods of `Loco::Hub`:

* `name`
* `members` - returns the hub's members. Members are stored in an informative, shortened form inside Redis / in-process storage. Be aware that this method performs calls to DB to fetch all members.
* `members` - returns the hub's members. Members are stored in an informative, shortened form inside Redis. Be aware that this method performs calls to DB to fetch all members.
* `raw_members` - returns hub's members in the shortened form as they are stored: `"{class}:{id}"`
* `add_member(member)`
* `del_member(member)`
Expand Down Expand Up @@ -343,6 +342,14 @@ Capybara powers integration tests. Capybara is cool, but sometimes random tests

## Major releases 🎙

### 5.0 _(2020-12-23)_

* `connection.rb` template has been modified

* **Breaking changes**:
* Redis is required in dev env too when you use ActionCable
* internal data structures in Redis have changed. Running `FLUSHDB` is recommended

### 4.1 _(2020-07-27)_

* Loco-JS-Core has been updated to v0.2
Expand Down
58 changes: 17 additions & 41 deletions app/channels/loco/notification_center_channel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,61 +5,37 @@ class NotificationCenterChannel < ApplicationCable::Channel
def subscribed
return unless loco_permissions.is_a?(Array)

stream_for_resources
return if loco_permissions.compact.size > 1
@uuid, *signed_in_resources = PermissionsPresenter.signed_in(loco_permissions)
return unless @uuid.is_a?(String)

SenderJob.perform_later @uuid, loco: { start_ajax_polling: true }
stream_from("loco:notification_center:#{@uuid}")
broadcast_to(@uuid, loco: { uuid: @uuid })
signed_in_resources.each { |resource| WsConnectionManager.new(resource).add(@uuid) }
end

def unsubscribed
loco_permissions.each do |resource|
next if resource.nil? || resource.is_a?(String)

UuidJob.perform_later serialize_resource(resource), @uuid, 'del'
PermissionsPresenter.signed_in(loco_permissions, except: :uuid).each do |resource|
WsConnectionManager.new(resource).del(@uuid)
end
end

def receive(data)
update_connections if data['loco'] && data['loco']['connection_check']
NotificationCenter.new.received_message permissions, data
end

protected

def stream_for_resources
loco_permissions.compact.each do |resource|
if resource.is_a? String
@uuid = resource
stream_for_resource resource
SenderJob.perform_later @uuid, loco: { uuid: @uuid }
else
UuidJob.perform_later serialize_resource(resource), @uuid, 'add'
stream_for_resource resource
end
def receive(payload)
if payload.dig('loco', 'pong')
update_connections
broadcast_to(@uuid, loco: { ping: true })
end
end
return if payload.keys == ['loco']

def stream_for_resource(resource)
identifier = WsConnectionManager.new(resource).identifier
stream_from "loco:notification_center:#{identifier}"
indexed_permissions = PermissionsPresenter.indexed(loco_permissions)
NotificationCenter.new.received_message(indexed_permissions, payload)
end

def permissions
loco_permissions.compact.index_by do |o|
o.class.name.downcase.to_sym
end
end
protected

def update_connections
permissions.each do |key, val|
next if key == :string

UuidJob.perform_later serialize_resource(val), @uuid, 'update'
PermissionsPresenter.indexed(loco_permissions, except: :uuid).each do |_, resource|
WsConnectionManager.new(resource).update(@uuid)
end
end

def serialize_resource(resource)
{ 'class' => resource.class.name, 'id' => resource.id }
end
end
end
37 changes: 6 additions & 31 deletions app/controllers/loco/notification_center_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,41 +21,16 @@ def fetch_notifications
render json: [[], Time.current.iso8601(6)]
return
end
fetcher = Notification::Fetcher.new notif_fetcher_args
render json: [
fetcher.formatted_notifications,
fetcher.next_sync_time.iso8601(6)
]
end

def notif_fetcher_args
{
synced_at: params[:synced_at],
permissions: permissions,
recipient_token: params[:token]
}
fetcher = Notification::Fetcher.new({ synced_at: params[:synced_at],
permissions: permissions,
recipient_token: params[:token] })
render json: [fetcher.formatted_notifications, fetcher.next_sync_time.iso8601(6)]
end

def permissions
return [] unless defined? loco_permissions
return loco_permissions if params[:uuid].blank?

process_loco_permissions
end
return [] unless defined?(loco_permissions)

def process_loco_permissions
resources_to_del = []
resources_to_add = []
loco_permissions.each do |resource|
next if resource.nil?

ws_conn_manager = WsConnectionManager.new resource
next unless ws_conn_manager.connected?(params[:uuid])

resources_to_del << resource
resources_to_add << resource.class
end
loco_permissions - resources_to_del + resources_to_add.uniq
loco_permissions
end
end
end
2 changes: 1 addition & 1 deletion app/jobs/loco/sender_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class SenderJob < ActiveJob::Base
queue_as :loco

def perform(recipient, data)
Sender.new(recipient, data).emit
Sender.(recipient, data)
end
end
end
51 changes: 0 additions & 51 deletions app/jobs/loco/uuid_job.rb

This file was deleted.

Loading

0 comments on commit f711936

Please sign in to comment.