gorynich 1.1.0.142046

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +19 -0
  3. data/README.md +414 -0
  4. data/Rakefile +20 -0
  5. data/config/locales/en.yml +6 -0
  6. data/lib/generators/gorynich/gorynich_generator.rb +9 -0
  7. data/lib/generators/gorynich/install_generator.rb +17 -0
  8. data/lib/generators/templates/gorynich.rb +32 -0
  9. data/lib/gorynich/config.rb +241 -0
  10. data/lib/gorynich/configuration.rb +17 -0
  11. data/lib/gorynich/current.rb +13 -0
  12. data/lib/gorynich/engine.rb +11 -0
  13. data/lib/gorynich/fetcher.rb +54 -0
  14. data/lib/gorynich/fetchers/consul.rb +19 -0
  15. data/lib/gorynich/fetchers/file.rb +22 -0
  16. data/lib/gorynich/head/action_cable.rb +44 -0
  17. data/lib/gorynich/head/active_job.rb +36 -0
  18. data/lib/gorynich/head/active_record.rb +46 -0
  19. data/lib/gorynich/head/delayed_job.rb +22 -0
  20. data/lib/gorynich/head/rack_middleware.rb +34 -0
  21. data/lib/gorynich/head.rb +9 -0
  22. data/lib/gorynich/switcher.rb +48 -0
  23. data/lib/gorynich/version.rb +3 -0
  24. data/lib/gorynich.rb +137 -0
  25. data/lib/tasks/gorynich_tasks.rake +27 -0
  26. data/spec/dummy/Rakefile +6 -0
  27. data/spec/dummy/app/assets/config/manifest.js +3 -0
  28. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  29. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  30. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  31. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  32. data/spec/dummy/app/controllers/users_controller.rb +10 -0
  33. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  34. data/spec/dummy/app/javascript/packs/application.js +15 -0
  35. data/spec/dummy/app/jobs/application_job.rb +7 -0
  36. data/spec/dummy/app/jobs/test_job.rb +6 -0
  37. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  38. data/spec/dummy/app/models/application_record.rb +3 -0
  39. data/spec/dummy/app/models/user.rb +2 -0
  40. data/spec/dummy/app/views/layouts/application.html.erb +13 -0
  41. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  42. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  43. data/spec/dummy/app/views/users/index.html.erb +9 -0
  44. data/spec/dummy/bin/rails +4 -0
  45. data/spec/dummy/bin/rake +4 -0
  46. data/spec/dummy/bin/setup +33 -0
  47. data/spec/dummy/config/application.rb +34 -0
  48. data/spec/dummy/config/boot.rb +5 -0
  49. data/spec/dummy/config/cable.yml +10 -0
  50. data/spec/dummy/config/database.yml +3 -0
  51. data/spec/dummy/config/environment.rb +5 -0
  52. data/spec/dummy/config/environments/development.rb +46 -0
  53. data/spec/dummy/config/environments/production.rb +112 -0
  54. data/spec/dummy/config/environments/test.rb +49 -0
  55. data/spec/dummy/config/gorynich_config.yml +49 -0
  56. data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
  57. data/spec/dummy/config/initializers/assets.rb +12 -0
  58. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  59. data/spec/dummy/config/initializers/content_security_policy.rb +28 -0
  60. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  61. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  62. data/spec/dummy/config/initializers/gorynich.rb +34 -0
  63. data/spec/dummy/config/initializers/inflections.rb +16 -0
  64. data/spec/dummy/config/initializers/middleware.rb +0 -0
  65. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  66. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  67. data/spec/dummy/config/locales/en.yml +33 -0
  68. data/spec/dummy/config/puma.rb +38 -0
  69. data/spec/dummy/config/routes.rb +5 -0
  70. data/spec/dummy/config/spring.rb +6 -0
  71. data/spec/dummy/config/storage.yml +34 -0
  72. data/spec/dummy/config.ru +5 -0
  73. data/spec/dummy/db/migrate/20220907101031_create_users.rb +8 -0
  74. data/spec/dummy/public/404.html +67 -0
  75. data/spec/dummy/public/422.html +67 -0
  76. data/spec/dummy/public/500.html +66 -0
  77. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  78. data/spec/dummy/public/apple-touch-icon.png +0 -0
  79. data/spec/dummy/public/favicon.ico +0 -0
  80. data/spec/dummy/tmp/development_secret.txt +1 -0
  81. data/spec/fixtures/env_test_database.yml +7 -0
  82. data/spec/fixtures/fetchers/consul_config.yml +1 -0
  83. data/spec/fixtures/fetchers/file_config.yml +46 -0
  84. data/spec/fixtures/fetchers/wrong_file_config.yml +44 -0
  85. data/spec/fixtures/test_database.yml +1 -0
  86. data/spec/lib/gorynich/config_spec.rb +428 -0
  87. data/spec/lib/gorynich/fetcher_spec.rb +112 -0
  88. data/spec/lib/gorynich/fetchers/consul_spec.rb +58 -0
  89. data/spec/lib/gorynich/fetchers/file_spec.rb +29 -0
  90. data/spec/lib/gorynich/version_spec.rb +6 -0
  91. data/spec/lib/gorynich_spec.rb +99 -0
  92. data/spec/rails_helper.rb +8 -0
  93. data/spec/spec_helper.rb +48 -0
  94. metadata +458 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d1d0c21b8e0cbfde7c435ee42f9a3a7a4c25cf73579c715ecb8b8854ccb2b73c
4
+ data.tar.gz: 24c73120bd544cea50228a905f002f54bcbf240a155391a4d6cca3df1adefd8d
5
+ SHA512:
6
+ metadata.gz: 765d8a73f9c06784e280f54e4c8b760a0f6d66fd0c9536c202a7db5faaeecc04b262368e21aa8653b349c5b8b922fc1985bab49506eccdb81beb6b3041838b59
7
+ data.tar.gz: c2fab106eb7faf514fc441fccf56f38d940f4261cf445e5dfd870259ba8a372ec8aa718a2a81052090269483155ff6ee6c20087afeff94e2eee2e4754d26adc4
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2014-2023 Рнд Софт
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,414 @@
1
+ # Gorynich
2
+
3
+ <div align="center">
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/gorynich.svg)](https://rubygems.org/gems/gorynich)
6
+ [![Gem](https://img.shields.io/gem/dt/gorynich.svg)](https://rubygems.org/gems/gorynich/versions)
7
+ [![YARD](https://badgen.net/badge/YARD/doc/blue)](http://www.rubydoc.info/gems/gorynich)
8
+
9
+
10
+ [![coverage](https://lysander.rnds.pro/api/v1/badges/gorynich_coverage.svg)](https://lysander.rnds.pro/api/v1/badges/gorynich_coverage.html)
11
+ [![quality](https://lysander.rnds.pro/api/v1/badges/gorynich_quality.svg)](https://lysander.rnds.pro/api/v1/badges/gorynich_quality.html)
12
+ [![outdated](https://lysander.rnds.pro/api/v1/badges/gorynich_outdated.svg)](https://lysander.rnds.pro/api/v1/badges/gorynich_outdated.html)
13
+ [![vulnerable](https://lysander.rnds.pro/api/v1/badges/gorynich_vulnerable.svg)](https://lysander.rnds.pro/api/v1/badges/gorynich_vulnerable.html)
14
+
15
+ </div>
16
+
17
+ `Gorynich` это гем для реализации [мультитенантности](https://ru.wikipedia.org/wiki/Мультиарендность) (мультиарендности) в Ruby on Rails приложении. Позволяет обеспечить строгую изоляцию данных в нескольких СУБД, поддерживаемых в ActiveRecord.
18
+
19
+ Поскольку мультитенантное приложение тесно связано с разделение данных, которые в совю очередь могут находиться в разных источниках (СУБД, S3, Redis и пр), а также с их обработкой в разных подсистемах (ActiveJob, ActionCable), мы выбрали название ["Горыныч"](https://ru.wikipedia.org/wiki/Змей_Горыныч), что бы подчеркнуть ~многоголовость~ многогранность интеграций.
20
+
21
+ ---
22
+
23
+ `Gorynich` provides tools for creating [Multitenancy](https://en.wikipedia.org/wiki/Multitenancy) Ruby on Rails application. If you need to have strong data segregation and isolated DBMS's with diffrent providers (supported by ActiveRecord) and credentials, `Gorynich` can help.
24
+
25
+ Since a multi-tenant application is closely related to the separation of data, which in turn can be located in different sources (DBMS, S3, Redis, etc.), as well as their processing in different subsystems (ActiveJob, ActionCable), we chose the name ["Gorynych"](https://en.wikipedia.org/wiki/Zmei_(Russian)#Multiheadedness), which to emphasize the ~multiheadedness~ versatility of integrations.
26
+
27
+ <div align="left">
28
+ <a href="https://rnds.pro/" >
29
+ <img src="https://library.rnds.pro/repository/public-blob/logo/RNDS.svg" alt="Supported by RNDSOFT" height="35">
30
+ </a>
31
+ </div>
32
+
33
+ ## Возможности / Features
34
+
35
+ - Прозрачное переключение БД/СУБД на основании данных запросы / Transparent request based DB/DBMS switching
36
+ - Интеграция с / Integrations:
37
+ - ActiveRecord
38
+ - ActionCable
39
+ - ActiveJob
40
+ - DelayedJob
41
+ - Получение параметров из [Consul KV](https://developer.hashicorp.com/consul/docs/dynamic-app-config/kv) / Stoting configuration in [Consul KV](https://developer.hashicorp.com/consul/docs/dynamic-app-config/kv)
42
+ - Получение параметров из файла / Storing configuration in file
43
+ - Разделение секретов / Secret storing and isolation
44
+ - Обновление конфигурации "на лету" / update configuration "on the fly"
45
+ - Статическая генерация `database.yml` / Static `database.yml` generation
46
+
47
+
48
+ ## Начало работы / Getting started
49
+
50
+ ```sh
51
+ gem install gorynich
52
+ ```
53
+
54
+ При установке `Gorynich` через bundler добавьте следующую строку в `Gemfile`:
55
+
56
+ ---
57
+
58
+ If you'd rather install `Gorynichr` using bundler, add a line for it in your `Gemfile`:
59
+
60
+ ```sh
61
+ gem 'gorynich'
62
+ ```
63
+
64
+ Затем выполнить / Then run:
65
+
66
+ ```sh
67
+ bundle install # для установки гема / gem installation
68
+
69
+ rails generate gorynich:install # для добавления шаблонов конфигурации / install configuration templates
70
+ ```
71
+
72
+ ## Что такое тенант? / What tenant is?
73
+
74
+ Тенант в данном случае это активное подключение к СУБД, а также доступный в любом месте объект `Gorynich::Current`, в котором находятся параметры текущего тенанта. К нему можно обратиться в любом месте.
75
+
76
+ ---
77
+
78
+ In this case tenant is an active connection to the DBMS, as well as a `Gorynich::Current` object available anywhere, which contains the parameters of the current tenant. You can refer to it anywhere, for example when sending emails:
79
+
80
+
81
+ ```ruby
82
+ Gorynich::Current.tap do |t|
83
+ t.tenant # tenant_name
84
+ t.uri # https://app.domain.org
85
+ t.host # app.domain.org
86
+ t.secrets # { key1 => value1, key2 => value2}
87
+ t.database # { adapter => postgresql, host => localhost, port => 5432, username => xxx, password => xxx }
88
+ end
89
+ ```
90
+
91
+ ## Как это работает / How it works
92
+
93
+ Перед обработкой запроса с помощью [Gorynich::Rack::RackMiddleware](./lib/gorynich/head/rack_middleware.rb) соединение Active Record переключается на указанную БД, а с помощью [ActiveSupport::CurrentAttributes](https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html) в любом месте приложения становятся доступны дополнительные параметры через обращение к `Gorynich::Current`. ActionCable, ActiveJob и другие "головы" используют настройки из `Gorynich::Current` для сохранения контекста и дальнейшего исполнения.
94
+
95
+ Например при отправке писем изнутри ActiveJob, использование выглядит так:
96
+
97
+ ---
98
+
99
+ Before request processing [Gorynich::Rack::RackMiddleware](./lib/gorynich/head/rack_middleware.rb) ActiveRecord connection switching to apropriate database. Additional tenant properties available in any part of application through [ActiveSupport::CurrentAttributes](https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html) as `Gorynich::Current` instance. ActionCable, ActiveJob and other "heads" also uses `Gorynich::Current` to store context and evaluate it later.
100
+
101
+ For example, when sending emails from within ActiveJob, the usage looks like this:
102
+
103
+ ```ruby
104
+ #app/mailers/application_mailer.rb
105
+
106
+ class ApplicationMailer < ActionMailer::Base
107
+ helper :application
108
+
109
+ def self.email_settings
110
+ (
111
+ Gorynich::Current.secrets[:email_settings] || Rails.application.secrets.email_settings || {}
112
+ ).with_indifferent_access
113
+ end
114
+
115
+ default from: email_settings[:from], content_type: 'text/plain'
116
+
117
+ def mail(args)
118
+ @host = Gorynich.instance.hosts(Gorynich::Current.tenant).first || Rails.application.secrets.domain
119
+
120
+ @settings ||= smtp_settings.merge(application_host: @host)
121
+
122
+ super(args).tap |m|
123
+ m.from = @settings[:from]
124
+ m.delivery_method.settings = @settings unless Rails.env.development?
125
+ end
126
+ end
127
+
128
+ end
129
+ ```
130
+
131
+ ## Использование / Usage
132
+
133
+ ### Настройка источника данных / Configuration source
134
+
135
+ Для использования необходимо в файле `config/application.rb` добавить источник данных. Сейчас доступны 2 источника:
136
+
137
+ ---
138
+
139
+ Now you need to select configuration source in `config/application.rb`. Yuo can choose from 2 source types now:
140
+
141
+
142
+ ```ruby
143
+ Gorynich::Fetchers::File.new(file_path: [FILE_PATH]) # из файла / from file
144
+
145
+ Gorynich::Fetchers::Consul.new(storage: [CONSUL_KEY], **options) # из консула / from consul (options - from Dimplomat gem https://github.com/WeAreFarmGeek/diplomat)
146
+ ```
147
+
148
+ Пример / Example:
149
+
150
+ ```ruby
151
+ # из одного / from single source
152
+ Gorynich.configuration.fetcher = Gorynich::Fetchers::File.new(file_path: Rails.root.join('config', 'gorynich_config.yml'))
153
+
154
+ # из нескольких (данные берутся от первого успешного fetcher)
155
+ # from multiple sources - first succesful source is used
156
+ Gorynich.configuration.fetcher = [
157
+ Gorynich::Fetchers::Consul.new(storage: 'gorynich_project/config'),
158
+ Gorynich::Fetchers::File.new(file_path: Rails.root.join('config', 'gorynich_config.yml'))
159
+ ]
160
+ ```
161
+
162
+ ### Настройка интеграций ("голов") / Integration "Heads"
163
+
164
+ `Gorynich` настраивается в обычных инициалайзерах / `Gorynich` configured in initializer:
165
+
166
+ ```ruby
167
+ # config/initializers/gorynich.rb
168
+
169
+ Gorynich.configure do |config|
170
+ # config cache of gorynich
171
+ config.cache = Rails.cache
172
+
173
+ # config cache namespace
174
+ config.namespace = ENV.fetch('YOUR_NAMESPACE_ENV', 'your_namespace')
175
+
176
+ # config how long your source cache will be alive in seconds
177
+ config.cache_expiration = 'your_value'
178
+
179
+ # Custom handler for swithing tenants in gorynich rack middleware
180
+ config.rack_env_handler =
181
+ lambda do |env|
182
+ host = env['SERVER_NAME']
183
+ tenant = Gorynich.instance.tenant_by_host(host)
184
+ uri = Gorynich.instance.uri_by_host(host, tenant)
185
+
186
+ Sentry.set_tags(tenant: tenant) if Sentry.get_current_scope.present?
187
+
188
+ [tenant, { host: host, uri: uri }]
189
+ end
190
+ end
191
+
192
+ # Add cable head
193
+ ActiveSupport.on_load(:action_cable_connection) do
194
+ include Gorynich::Head::ActionCable::Connection
195
+ end
196
+
197
+ ActiveSupport.on_load(:action_cable_channel) do
198
+ prepend Gorynich::Head::ActionCable::Channel
199
+ end
200
+
201
+ # Add active job head
202
+ ActiveSupport.on_load(:active_job) do
203
+ include Gorynich::Head::ActiveJob
204
+ end
205
+ ```
206
+
207
+ ### Rake Tasks
208
+
209
+ Запуск `Rails console` внутри тенанта / Run `rails console` inside tenant:
210
+
211
+ ```bash
212
+ TENANT=tenant rails gc # default tenant name id 'default'
213
+ ```
214
+
215
+ Для создания статичного файла `database.yml` из источника данных (Fetcher), используйте:
216
+
217
+ ---
218
+
219
+ For static `database.yml` generation from configured source (Fetcher) use:
220
+
221
+ ```bash
222
+ rails gc:db:prepare
223
+ ```
224
+
225
+ ### Настройка конфигурации БД / Database configuration
226
+
227
+ 1. Статическая генерация / static generation
228
+
229
+ Первый, самый простой способ работы, подходящий для локальной разработки, это статическая генерация `database.yml`.
230
+
231
+ ---
232
+
233
+ First and most simple using of Gorynich handy for local development is static `database.yml` generation.
234
+
235
+ запуск rake-задачи / runing rake task:
236
+
237
+ ```bash
238
+ rails gc:db:prepare
239
+ ```
240
+
241
+ 2. Полуавтоматический режим / Semi-automated mode
242
+
243
+ Второй вариант это создание конфигурации `database.yml` при старте Rails приложения - данные буду прочитаны из настроенного источника. В этом случае конфигурация СУБД может изменяться только при перезапуске приложения, но остальные настройки, такие как привязка тенантов к доменам и secrets будут подхватываться "на лету" непосредственно во время работы приложения. Rake-задачи `db:create`, `db:migrate` работают для всех тенантов на момент запуска.
244
+
245
+ ---
246
+
247
+ Second option is dynamic `database.yml` creation while starting Rails application. Configuration will be readed from selected source. In this case database configuration can change only when application restarts, but other configuration such a domain to tenant binding and application secrets wil be updated "on the fly" while application running. Rake tasks `db:create` and `db:migrate` works as expected for all tenant in order.
248
+
249
+ > ВНИМАНИЕ! `db:rollback` не работает в мультитенантном режиме.
250
+
251
+ > WARNING! `db:rollback` is not working in multitenancy mode.
252
+
253
+ В `database.yml` прописать следующее / In `database.yml` set:
254
+
255
+ ```yaml
256
+ # config/database.yml
257
+ <%= Gorynich.instance.database_config %>
258
+ ```
259
+
260
+ 3. Дополнительные БД / Additional datbases
261
+
262
+ Если вам нужны дополнительные БД, не являющиеся тенантами, например, общая БД, то в `database.yml` можно дописать всё необходимое как в обычном Rails приложении:
263
+
264
+ ---
265
+
266
+ If you need additional DB, not for tenants Ex. generic database you can configure it in `database.yml` like in regular Rails application:
267
+
268
+ ```yaml
269
+ # config/database.yml
270
+ <%= Gorynich.instance.database_config('development') %>
271
+
272
+ your_database:
273
+ <<: *configs_for_your_database
274
+
275
+ <%= Gorynich.instance.database_config('test') %>
276
+ your_database:
277
+ <<: *configs_for_your_database
278
+
279
+ <%= Gorynich.instance.database_config('production') %>
280
+ your_database:
281
+ <<: *configs_for_your_database
282
+ ```
283
+
284
+ ### В коде / Inside code
285
+ Проверить в каком вы тенанте можно с помощью / Check in which tenant you are:
286
+
287
+ ```ruby
288
+ Gorynich::Current.tenant
289
+ ```
290
+
291
+ Переключение тенантов работает автоматичеки и внутри Rails приложения не нужно предпринимать никаких дополнительных действий - вы всегда подключены к той базе данных к которой привязан домен текущего запроса (или иной параметр). Но если необходимо явно выполнить действия в контексте конкретного тената это можно сделать:
292
+
293
+ ---
294
+
295
+ Switching tenants is automatic and no additional steps need to be taken inside a Rails application - you always connected to database associated with currently procesed request. But if you want take action inside specific tenant context you can use:
296
+
297
+ ```ruby
298
+ # для выполнения в конкретном тенанте / run block inside specific tenant
299
+ Gorynich.with('tenant_name') do
300
+ # your code
301
+ end
302
+
303
+ # для выполнения в каждом тенанте / run block inside each tenant
304
+ Gorynich.with_each_tenant do |tenant|
305
+ # your code
306
+ end
307
+ ```
308
+
309
+ ## Примеры дополнительных интеграций и использвоаний / Additional integration examples
310
+
311
+ ### Redis / Rails.cache
312
+
313
+ ```ruby
314
+ #config/environments/production.rb
315
+
316
+ config.cache_store = :redis_cache_store, {
317
+ url: ENV.fetch('REDIS_URL', nil),
318
+ expires_in: 90.minutes,
319
+ connect_timeout: 3,
320
+ reconnect_attempts: 3,
321
+ namespace: -> { "#{Gorynich.configuration.namespace}#{Gorynich::Current.tenant}" }
322
+ }
323
+ ```
324
+
325
+ ### Sentry
326
+
327
+ ```ruby
328
+ #config/initializers/gorynich.rb
329
+
330
+ Gorynich.configure do |config|
331
+ config.rack_env_handler =
332
+ lambda do |env|
333
+ host = env['SERVER_NAME']
334
+ tenant = Gorynich.instance.tenant_by_host(host)
335
+ uri = Gorynich.instance.uri_by_host(host, tenant)
336
+
337
+ Sentry.set_tags(tenant: tenant) if Sentry.get_current_scope.present?
338
+
339
+ [tenant, { host: host, uri: uri }]
340
+ end
341
+ end
342
+ ```
343
+
344
+ ### Telegram
345
+
346
+ ```ruby
347
+ #config/environments/production.rb
348
+
349
+ config.telegram_updates_controller.session_store = :redis_cache_store, {
350
+ url: ENV.fetch('REDIS_URL', nil),
351
+ expires_in: 90.minutes,
352
+ namespace: -> { "#{Gorynich.configuration.namespace}#{Gorynich::Current.tenant}" }
353
+ }
354
+ ```
355
+
356
+ ### Shrine
357
+
358
+ ```ruby
359
+ #lib/shrine/plugins/tenant_location.rb
360
+
361
+ class Shrine
362
+ module Plugins
363
+ module TenantLocation
364
+ module InstanceMethods
365
+ def generate_location(io, **options)
366
+ "#{Gorynich::Current.tenant}/#{super}"
367
+ end
368
+ end
369
+ end
370
+
371
+ register_plugin(:tenant_location, TenantLocation)
372
+ end
373
+ end
374
+
375
+ #config/initializers/shrine.rb
376
+
377
+ Shrine.plugin :tenant_location
378
+ ```
379
+
380
+ ### ApplicationController
381
+
382
+ ```ruby
383
+ class ApplicationController < ActionController::Base
384
+ around_action :around_action_notification
385
+
386
+ def around_action_notification(&block)
387
+ ActiveSupport::Notifications.instrument(
388
+ 'around_action.action_controller',
389
+ current_user: original_user,
390
+ request: request,
391
+ tenant: Gorynich::Current.tenant, &block
392
+ )
393
+ end
394
+ end
395
+ ```
396
+
397
+ ### DelayedJob
398
+
399
+ ```ruby
400
+ #config/initializers/delayed_job.rb
401
+
402
+ require 'gorynich/head/delayed_job'
403
+
404
+ Delayed::Worker.plugins << Gorynich::Head::DelayedJob
405
+ ```
406
+
407
+ ## Лицензия / License
408
+
409
+ Библиотека доступна с открытым исходным кодом в соответствии с условиями [лицензии MIT](./LICENSE).
410
+
411
+ ---
412
+
413
+ The gem is available as open source under the terms of the [MIT License](./LICENSE).
414
+
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rspec/core'
8
+ require 'rspec/core/rake_task'
9
+
10
+ desc 'Run all specs in spec directory (excluding plugin specs)'
11
+ RSpec::Core::RakeTask.new(spec: 'app:db:test:prepare')
12
+
13
+ task default: :spec
14
+
15
+ APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
16
+ load 'rails/tasks/engine.rake'
17
+
18
+ load 'rails/tasks/statistics.rake'
19
+
20
+ require 'bundler/gem_tasks'
@@ -0,0 +1,6 @@
1
+ en:
2
+ gorynich:
3
+ tenant_not_found: Tenant %{tenant} not found
4
+ host_not_found: Host %{host} not found
5
+ uri_not_found: URI %{uri} not found
6
+ internal_error: Gorynich internal error
@@ -0,0 +1,9 @@
1
+ # :nocov:
2
+ module Gorynich
3
+ module Generators
4
+ class GorynichGenerator < Rails::Generators::NamedBase
5
+ namespace 'gorynich'
6
+ end
7
+ end
8
+ end
9
+ # :nocov:
@@ -0,0 +1,17 @@
1
+ # :nocov:
2
+ require 'rails/generators/base'
3
+
4
+ module Gorynich
5
+ module Generators
6
+ class InstallGenerator < Rails::Generators::Base
7
+ source_root File.expand_path("../../templates", __FILE__)
8
+
9
+ desc 'Creates a Gorynich initializer'
10
+
11
+ def copy_initializer
12
+ template 'gorynich.rb', Rails.root.join('config', 'initializers', 'gorynich.rb')
13
+ end
14
+ end
15
+ end
16
+ end
17
+ # :nocov:
@@ -0,0 +1,32 @@
1
+ # :nocov:
2
+ Gorynich.configure do |config|
3
+ # config cache of gorynich
4
+ config.cache = Rails.cache
5
+
6
+ # config cache namespace
7
+ # config.namespace = 'gorynich'
8
+
9
+ # config how long your cache will be alive in seconds
10
+ # config.cache_expiration = 30
11
+
12
+ # handler for gorynich rack middleware
13
+ # config.rack_env_handler =
14
+ # lambda do |env|
15
+ # # your code
16
+ # end
17
+ end
18
+
19
+ # Add cable head
20
+ # ActiveSupport.on_load(:action_cable_connection) do
21
+ # include Gorynich::Head::ActionCable::Connection
22
+ # end
23
+ #
24
+ # ActiveSupport.on_load(:action_cable_channel) do
25
+ # prepend Gorynich::Head::ActionCable::Channel
26
+ # end
27
+
28
+ # Add active job head
29
+ # ActiveSupport.on_load(:active_job) do
30
+ # include Gorynich::Head::ActiveJob
31
+ # end
32
+ # :nocov: