logux_rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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