logux_rails 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.pryrc +8 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +33 -0
  6. data/.rubocop_todo.yml +17 -0
  7. data/.travis.yml +11 -0
  8. data/Appraisals +13 -0
  9. data/Gemfile +8 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +68 -0
  12. data/Rakefile +10 -0
  13. data/app/controllers/logux_controller.rb +41 -0
  14. data/app/helpers/logux_helper.rb +4 -0
  15. data/app/logux/actions.rb +3 -0
  16. data/app/logux/policies.rb +3 -0
  17. data/bin/console +15 -0
  18. data/bin/setup +8 -0
  19. data/config/routes.rb +5 -0
  20. data/docker-compose.yml +29 -0
  21. data/lib/generators/logux/model/USAGE +11 -0
  22. data/lib/generators/logux/model/model_generator.rb +28 -0
  23. data/lib/generators/logux/model/templates/migration.rb.erb +14 -0
  24. data/lib/logux.rb +107 -0
  25. data/lib/logux/action_caller.rb +42 -0
  26. data/lib/logux/action_controller.rb +6 -0
  27. data/lib/logux/actions.rb +29 -0
  28. data/lib/logux/add.rb +37 -0
  29. data/lib/logux/auth.rb +6 -0
  30. data/lib/logux/base_controller.rb +37 -0
  31. data/lib/logux/channel_controller.rb +24 -0
  32. data/lib/logux/class_finder.rb +61 -0
  33. data/lib/logux/client.rb +21 -0
  34. data/lib/logux/engine.rb +6 -0
  35. data/lib/logux/error_renderer.rb +40 -0
  36. data/lib/logux/meta.rb +36 -0
  37. data/lib/logux/model.rb +39 -0
  38. data/lib/logux/model/dsl.rb +15 -0
  39. data/lib/logux/model/proxy.rb +24 -0
  40. data/lib/logux/model/updater.rb +39 -0
  41. data/lib/logux/model/updates_deprecator.rb +54 -0
  42. data/lib/logux/node.rb +37 -0
  43. data/lib/logux/policy.rb +14 -0
  44. data/lib/logux/policy_caller.rb +34 -0
  45. data/lib/logux/process.rb +9 -0
  46. data/lib/logux/process/action.rb +60 -0
  47. data/lib/logux/process/auth.rb +27 -0
  48. data/lib/logux/process/batch.rb +59 -0
  49. data/lib/logux/response.rb +18 -0
  50. data/lib/logux/stream.rb +25 -0
  51. data/lib/logux/test.rb +35 -0
  52. data/lib/logux/test/helpers.rb +75 -0
  53. data/lib/logux/test/matchers.rb +10 -0
  54. data/lib/logux/test/matchers/base.rb +25 -0
  55. data/lib/logux/test/matchers/response_chunks.rb +48 -0
  56. data/lib/logux/test/matchers/send_to_logux.rb +51 -0
  57. data/lib/logux/test/store.rb +21 -0
  58. data/lib/logux/version.rb +5 -0
  59. data/lib/logux_rails.rb +3 -0
  60. data/lib/tasks/logux_tasks.rake +46 -0
  61. data/logux_rails.gemspec +46 -0
  62. metadata +398 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e2ddee267525e7f6503a6a64b42b4b18322dd7a597c1f535f5c3341921bad354
4
+ data.tar.gz: b7f32ba3524a11d9a81872064231cc939773cc68a22029a3cf52e48253fafeb6
5
+ SHA512:
6
+ metadata.gz: 5a82d8cf1df3c2c228b4e6a371cf027d146cb59bc67f338cbef4103745132047f56224024fa9b24347d5b3734ead07afe4d0ca744b0b7d8eef07fe3a246d8e38
7
+ data.tar.gz: 7d9efeb074215bb2ac5c3d601ded5425f2c927efe5f6ed7c4f749c6f49b9f1fb33b4b40abdd0b9145912f1ecc17c08b3bca99580cd9f8a49fecbe2997a1869c3
@@ -0,0 +1,20 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+
13
+ .DS_Store
14
+
15
+ **/*.log
16
+ .*.swp
17
+ coverage
18
+ Gemfile.lock
19
+ spec/dummy/db/*.sqlite3
20
+ /gemfiles/*
data/.pryrc ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(PryByebug)
4
+ Pry.commands.alias_command 'c', 'continue'
5
+ Pry.commands.alias_command 's', 'step'
6
+ Pry.commands.alias_command 'n', 'next'
7
+ Pry.commands.alias_command 'f', 'finish'
8
+ end
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format progress
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,33 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ require: rubocop-rspec
4
+
5
+ AllCops:
6
+ Exclude:
7
+ - 'spec/dummy/db/**/*.rb'
8
+ - 'vendor/**/*'
9
+ - 'gemfiles/*'
10
+ TargetRubyVersion: 2.4
11
+
12
+ Metrics/BlockLength:
13
+ Exclude:
14
+ - 'spec/**/*.rb'
15
+ - 'logux_rails.gemspec'
16
+ - 'lib/logux/test/helpers.rb'
17
+
18
+ RSpec/DescribeClass:
19
+ Exclude:
20
+ - 'spec/logux/tasks/*.rb'
21
+ - 'spec/requests/*.rb'
22
+
23
+ FactoryBot/StaticAttributeDefinedDynamically:
24
+ Enabled: false
25
+
26
+ RSpec/ExpectChange:
27
+ EnforcedStyle: block
28
+
29
+ Style/RescueStandardError:
30
+ EnforcedStyle: implicit
31
+
32
+ Style/DateTime:
33
+ Enabled: false
@@ -0,0 +1,17 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2018-07-26 13:29:22 +0300 using RuboCop version 0.57.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 17
10
+ Style/Documentation:
11
+ Enabled: false
12
+
13
+ # Offense count: 2
14
+ Style/MixinUsage:
15
+ Exclude:
16
+ - 'spec/dummy/bin/setup'
17
+ - 'spec/dummy/bin/update'
@@ -0,0 +1,11 @@
1
+ sudo: require
2
+ services:
3
+ - docker
4
+
5
+ before_script:
6
+ - unset BUNDLE_GEMFILE
7
+ - docker-compose run app bundle install
8
+ - docker-compose run app bundle exec appraisal install
9
+ script:
10
+ - docker-compose run app bundle exec appraisal rspec
11
+ - docker-compose run app bundle exec rubocop
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ appraise '5.0' do
4
+ gem 'activerecord', '~> 5.0'
5
+ end
6
+
7
+ appraise '5.1' do
8
+ gem 'activerecord', '~> 5.1'
9
+ end
10
+
11
+ appraise '5.2' do
12
+ gem 'activerecord', '~> 5.2'
13
+ end
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in logux_rails.gemspec
8
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 WildDima
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,68 @@
1
+ # Logux Rails
2
+
3
+ [![Build Status](https://travis-ci.org/logux/logux_rails.svg?branch=master)](https://travis-ci.org/logux/logux_rails) [![Coverage Status](https://coveralls.io/repos/github/logux/logux_rails/badge.svg?branch=master)](https://coveralls.io/github/logux/logux_rails?branch=master)
4
+
5
+ Add WebSockets, live-updates and offline-first to Ruby on Rails with [Logux](https://github.com/logux/logux/). This gem will add [Logux Back-end Protocol](https://github.com/logux/logux/blob/master/backend-protocol/spec.md) to Ruby on Rails and then you can use Logux Server as a proxy between WebSocket and your Rails application.
6
+
7
+ Read [Creating Logux Proxy](https://github.com/logux/logux/blob/master/2-starting/2-creating-proxy.md) guide.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'logux_rails'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ ```bash
20
+ bundle
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ First of all, you have to configure Logux, by defining server address in, for example, `config/initializers/logux.rb`:
26
+
27
+ ```ruby
28
+ Logux.configuration do |config|
29
+ config.logux_host = 'http://localhost:31338'
30
+ end
31
+ ```
32
+
33
+ Mount logux in routes:
34
+
35
+ ```ruby
36
+ mount Logux::Engine => '/'
37
+ ```
38
+
39
+ After this, POST requests to `/logux` will be processed by `LoguxController`. You can redefine it or inherit from, if it necessary, for example, for implementing custom authorization flow.
40
+
41
+ Logux Rails will try to find Action for the specific message from Logux Server. For example, for `project/rename` action, you should define `Action::Project` class, inherited from `Logux::Action` base class, and implement `rename` method.
42
+
43
+ You can execute `rake logux:actions` to get the list of available action types, or `rake logux:channels` to get the list of available channels.
44
+
45
+ ## Development with Docker
46
+
47
+ After checking out the repo, run:
48
+
49
+ ```bash
50
+ docker-compose run app bundle install
51
+ docker-compose run app bundle exec appraisal install
52
+ ```
53
+
54
+ Run tests with:
55
+
56
+ ```bash
57
+ docker-compose run app bundle exec appraisal rspec
58
+ ```
59
+
60
+ Run RuboCop with:
61
+
62
+ ```bash
63
+ docker-compose run app bundle exec rubocop
64
+ ```
65
+
66
+ ## License
67
+
68
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'rubocop/rake_task'
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+ RuboCop::RakeTask.new
9
+
10
+ task default: %i[rubocop spec]
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LoguxController < ActionController::Base
4
+ include ActionController::Live
5
+
6
+ def create
7
+ logux_stream.write('[')
8
+ Logux.verify_request_meta_data(meta_params)
9
+ Logux.process_batch(stream: logux_stream, batch: command_params)
10
+ rescue => ex
11
+ handle_processing_errors(ex)
12
+ ensure
13
+ logux_stream.write(']')
14
+ logux_stream.close
15
+ end
16
+
17
+ private
18
+
19
+ def unsafe_params
20
+ params.to_unsafe_h
21
+ end
22
+
23
+ def command_params
24
+ unsafe_params.dig('commands')
25
+ end
26
+
27
+ def meta_params
28
+ unsafe_params&.slice(:version, :password)
29
+ end
30
+
31
+ def logux_stream
32
+ @logux_stream ||= Logux::Stream.new(response.stream)
33
+ end
34
+
35
+ def handle_processing_errors(exception)
36
+ Logux.configuration.on_error.call(exception)
37
+ Logux.logger.error("#{exception}\n#{exception.backtrace.join("\n")}")
38
+ ensure
39
+ logux_stream.write(Logux::ErrorRenderer.new(exception).message)
40
+ end
41
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LoguxHelper
4
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Actions; end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Policies; end
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'logux_rails'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Logux::Engine.routes.draw do
4
+ resource :logux, only: %i[create], controller: :logux
5
+ end
@@ -0,0 +1,29 @@
1
+ version: '3.4'
2
+
3
+ services:
4
+ app:
5
+ image: ruby:2.5.1
6
+ environment:
7
+ - BUNDLE_PATH=/bundle
8
+ - BUNDLE_CONFIG=/app/.bundle/config
9
+
10
+ - DB_HOST=db
11
+ - DB_NAME=logux_rails
12
+ - DB_USERNAME=postgres
13
+ command: bash
14
+ working_dir: /app
15
+ volumes:
16
+ - .:/app:cached
17
+ - bundler_data:/bundle
18
+ tmpfs:
19
+ - /tmp
20
+ depends_on:
21
+ - db
22
+
23
+ db:
24
+ image: postgres:10
25
+ environment:
26
+ - POSTGRES_DB=logux_rails
27
+
28
+ volumes:
29
+ bundler_data:
@@ -0,0 +1,11 @@
1
+ Description:
2
+ Generates the necessary migration to enable field tracking for logux updates
3
+
4
+ Examples:
5
+ rails generate logux:model User
6
+
7
+ This will generate the migration to add a column with update time data.
8
+
9
+ rails generate logux:model User --nullable
10
+
11
+ This will generate the migration to add a column with update time data and add `null: false` constraint. Be careful, adding the constraint to the table with a big number of rows can cause lots of locks.
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/active_record/migration/migration_generator'
5
+
6
+ module Logux
7
+ module Generators
8
+ class ModelGenerator < ::ActiveRecord::Generators::Base # :nodoc:
9
+ source_root File.expand_path('templates', __dir__)
10
+
11
+ class_option :nullable,
12
+ type: :boolean,
13
+ optional: true,
14
+ desc: 'Define whether field should have not-null constraint'
15
+
16
+ def generate_migration
17
+ migration_template(
18
+ 'migration.rb.erb',
19
+ "db/migrate/add_logux_fields_updated_at_to_#{plural_table_name}.rb"
20
+ )
21
+ end
22
+
23
+ def nullable?
24
+ options.fetch(:nullable, false)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,14 @@
1
+ class <%= @migration_class_name %> < ActiveRecord::Migration<%= ActiveRecord::VERSION::MAJOR < 5 ? '' : '[5.0]' %>
2
+ def up
3
+ <% if nullable? %>
4
+ add_column :<%= plural_table_name %>, :logux_fields_updated_at, :jsonb, null: false, default: {}
5
+ <% else %>
6
+ add_column :<%= plural_table_name %>, :logux_fields_updated_at, :jsonb, null: true
7
+ change_column_default :<%= plural_table_name %>, :logux_fields_updated_at, {}
8
+ <% end %>
9
+ end
10
+
11
+ def down
12
+ remove_column :<%= plural_table_name %>, :logux_fields_updated_at
13
+ end
14
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'configurations'
4
+ require 'rest-client'
5
+ require 'rails/engine'
6
+ require 'active_support'
7
+ require 'action_controller'
8
+ require 'logux/engine'
9
+ require 'nanoid'
10
+ require 'colorize'
11
+
12
+ module Logux
13
+ extend ActiveSupport::Autoload
14
+ include Configurations
15
+
16
+ PROTOCOL_VERSION = 1
17
+
18
+ class WithMetaError < StandardError
19
+ attr_reader :meta
20
+
21
+ def initialize(msg, meta: nil)
22
+ @meta = meta
23
+ super(msg)
24
+ end
25
+ end
26
+
27
+ UnknownActionError = Class.new(WithMetaError)
28
+ UnknownChannelError = Class.new(WithMetaError)
29
+ UnauthorizedError = Class.new(StandardError)
30
+
31
+ autoload :Client, 'logux/client'
32
+ autoload :Meta, 'logux/meta'
33
+ autoload :Actions, 'logux/actions'
34
+ autoload :Auth, 'logux/auth'
35
+ autoload :BaseController, 'logux/base_controller'
36
+ autoload :ActionController, 'logux/action_controller'
37
+ autoload :ChannelController, 'logux/channel_controller'
38
+ autoload :ClassFinder, 'logux/class_finder'
39
+ autoload :ActionCaller, 'logux/action_caller'
40
+ autoload :PolicyCaller, 'logux/policy_caller'
41
+ autoload :Policy, 'logux/policy'
42
+ autoload :Add, 'logux/add'
43
+ autoload :Node, 'logux/node'
44
+ autoload :Response, 'logux/response'
45
+ autoload :Stream, 'logux/stream'
46
+ autoload :Process, 'logux/process'
47
+ autoload :Logger, 'logux/logger'
48
+ autoload :Version, 'logux/version'
49
+ autoload :Test, 'logux/test'
50
+ autoload :ErrorRenderer, 'logux/error_renderer'
51
+ autoload :Model, 'logux/model'
52
+
53
+ configurable :logux_host, :verify_authorized,
54
+ :password, :logger,
55
+ :on_error, :auth_rule,
56
+ :render_backtrace_on_error
57
+
58
+ configuration_defaults do |config|
59
+ config.logux_host = 'localhost:1338'
60
+ config.verify_authorized = true
61
+ config.logger = ActiveSupport::Logger.new(STDOUT)
62
+ if defined?(Rails) && Rails.respond_to?(:logger)
63
+ config.logger = Rails.logger
64
+ end
65
+ config.on_error = proc {}
66
+ config.auth_rule = proc { false }
67
+ config.render_backtrace_on_error = true
68
+ end
69
+
70
+ def self.add(action, meta = Meta.new)
71
+ Logux::Add.new.call([[action, meta]])
72
+ end
73
+
74
+ def self.add_batch(commands)
75
+ Logux::Add.new.call(commands)
76
+ end
77
+
78
+ def self.undo(meta, reason: nil, data: {})
79
+ add(
80
+ data.merge(type: 'logux/undo', id: meta.id, reason: reason),
81
+ Logux::Meta.new(clients: [meta.client_id])
82
+ )
83
+ end
84
+
85
+ def self.verify_request_meta_data(meta_params)
86
+ if Logux.configuration.password.nil?
87
+ logger.warn(%(Please, add password for logux server:
88
+ Logux.configure do |c|
89
+ c.password = 'your-password'
90
+ end))
91
+ end
92
+ auth = Logux.configuration.password == meta_params&.dig(:password)
93
+ raise Logux::UnauthorizedError, 'Incorrect password' unless auth
94
+ end
95
+
96
+ def self.process_batch(stream:, batch:)
97
+ Logux::Process::Batch.new(stream: stream, batch: batch).call
98
+ end
99
+
100
+ def self.generate_action_id
101
+ Logux::Node.instance.generate_action_id
102
+ end
103
+
104
+ def self.logger
105
+ configuration.logger
106
+ end
107
+ end