cloudenvoy 0.1.0.dev → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +41 -0
  3. data/.gitignore +3 -0
  4. data/.rubocop.yml +1 -0
  5. data/Appraisals +25 -0
  6. data/CHANGELOG.md +32 -0
  7. data/Gemfile.lock +215 -1
  8. data/README.md +581 -7
  9. data/app/controllers/cloudenvoy/application_controller.rb +8 -0
  10. data/app/controllers/cloudenvoy/subscriber_controller.rb +59 -0
  11. data/cloudenvoy.gemspec +15 -2
  12. data/config/routes.rb +5 -0
  13. data/examples/rails/.ruby-version +1 -0
  14. data/examples/rails/Gemfile +15 -0
  15. data/examples/rails/Gemfile.lock +207 -0
  16. data/examples/rails/Procfile +1 -0
  17. data/examples/rails/README.md +31 -0
  18. data/examples/rails/Rakefile +8 -0
  19. data/examples/rails/app/assets/config/manifest.js +2 -0
  20. data/examples/rails/app/assets/images/.keep +0 -0
  21. data/examples/rails/app/assets/stylesheets/application.css +15 -0
  22. data/examples/rails/app/channels/application_cable/channel.rb +6 -0
  23. data/examples/rails/app/channels/application_cable/connection.rb +6 -0
  24. data/examples/rails/app/controllers/application_controller.rb +4 -0
  25. data/examples/rails/app/controllers/concerns/.keep +0 -0
  26. data/examples/rails/app/helpers/application_helper.rb +4 -0
  27. data/examples/rails/app/javascript/packs/application.js +15 -0
  28. data/examples/rails/app/jobs/application_job.rb +9 -0
  29. data/examples/rails/app/mailers/application_mailer.rb +6 -0
  30. data/examples/rails/app/models/application_record.rb +5 -0
  31. data/examples/rails/app/models/concerns/.keep +0 -0
  32. data/examples/rails/app/publishers/hello_publisher.rb +34 -0
  33. data/examples/rails/app/subscribers/hello_subscriber.rb +16 -0
  34. data/examples/rails/app/views/layouts/application.html.erb +14 -0
  35. data/examples/rails/app/views/layouts/mailer.html.erb +13 -0
  36. data/examples/rails/app/views/layouts/mailer.text.erb +1 -0
  37. data/examples/rails/bin/rails +6 -0
  38. data/examples/rails/bin/rake +6 -0
  39. data/examples/rails/bin/setup +35 -0
  40. data/examples/rails/config.ru +7 -0
  41. data/examples/rails/config/application.rb +19 -0
  42. data/examples/rails/config/boot.rb +7 -0
  43. data/examples/rails/config/cable.yml +10 -0
  44. data/examples/rails/config/credentials.yml.enc +1 -0
  45. data/examples/rails/config/database.yml +25 -0
  46. data/examples/rails/config/environment.rb +7 -0
  47. data/examples/rails/config/environments/development.rb +65 -0
  48. data/examples/rails/config/environments/production.rb +114 -0
  49. data/examples/rails/config/environments/test.rb +50 -0
  50. data/examples/rails/config/initializers/application_controller_renderer.rb +9 -0
  51. data/examples/rails/config/initializers/assets.rb +14 -0
  52. data/examples/rails/config/initializers/backtrace_silencers.rb +8 -0
  53. data/examples/rails/config/initializers/cloudenvoy.rb +22 -0
  54. data/examples/rails/config/initializers/content_security_policy.rb +29 -0
  55. data/examples/rails/config/initializers/cookies_serializer.rb +7 -0
  56. data/examples/rails/config/initializers/filter_parameter_logging.rb +6 -0
  57. data/examples/rails/config/initializers/inflections.rb +17 -0
  58. data/examples/rails/config/initializers/mime_types.rb +5 -0
  59. data/examples/rails/config/initializers/wrap_parameters.rb +16 -0
  60. data/examples/rails/config/locales/en.yml +33 -0
  61. data/examples/rails/config/master.key +1 -0
  62. data/examples/rails/config/puma.rb +37 -0
  63. data/examples/rails/config/routes.rb +4 -0
  64. data/examples/rails/config/spring.rb +8 -0
  65. data/examples/rails/config/storage.yml +34 -0
  66. data/examples/rails/db/development.sqlite3 +0 -0
  67. data/examples/rails/db/test.sqlite3 +0 -0
  68. data/examples/rails/lib/assets/.keep +0 -0
  69. data/examples/rails/log/.keep +0 -0
  70. data/examples/rails/public/404.html +67 -0
  71. data/examples/rails/public/422.html +67 -0
  72. data/examples/rails/public/500.html +66 -0
  73. data/examples/rails/public/apple-touch-icon-precomposed.png +0 -0
  74. data/examples/rails/public/apple-touch-icon.png +0 -0
  75. data/examples/rails/public/favicon.ico +0 -0
  76. data/examples/rails/storage/.keep +0 -0
  77. data/gemfiles/rails_5.2.gemfile +7 -0
  78. data/gemfiles/rails_5.2.gemfile.lock +251 -0
  79. data/gemfiles/rails_6.0.gemfile +7 -0
  80. data/gemfiles/rails_6.0.gemfile.lock +267 -0
  81. data/gemfiles/semantic_logger_3.4.gemfile +7 -0
  82. data/gemfiles/semantic_logger_3.4.gemfile.lock +265 -0
  83. data/gemfiles/semantic_logger_4.6.gemfile +7 -0
  84. data/gemfiles/semantic_logger_4.6.gemfile.lock +265 -0
  85. data/gemfiles/semantic_logger_4.7.0.gemfile +7 -0
  86. data/gemfiles/semantic_logger_4.7.0.gemfile.lock +265 -0
  87. data/gemfiles/semantic_logger_4.7.2.gemfile +7 -0
  88. data/gemfiles/semantic_logger_4.7.2.gemfile.lock +265 -0
  89. data/lib/cloudenvoy.rb +96 -2
  90. data/lib/cloudenvoy/authentication_error.rb +6 -0
  91. data/lib/cloudenvoy/authenticator.rb +57 -0
  92. data/lib/cloudenvoy/backend/google_pub_sub.rb +146 -0
  93. data/lib/cloudenvoy/backend/memory_pub_sub.rb +89 -0
  94. data/lib/cloudenvoy/config.rb +165 -0
  95. data/lib/cloudenvoy/engine.rb +20 -0
  96. data/lib/cloudenvoy/invalid_subscriber_error.rb +6 -0
  97. data/lib/cloudenvoy/logger_wrapper.rb +167 -0
  98. data/lib/cloudenvoy/message.rb +96 -0
  99. data/lib/cloudenvoy/middleware/chain.rb +250 -0
  100. data/lib/cloudenvoy/pub_sub_client.rb +76 -0
  101. data/lib/cloudenvoy/publisher.rb +211 -0
  102. data/lib/cloudenvoy/publisher_logger.rb +32 -0
  103. data/lib/cloudenvoy/subscriber.rb +222 -0
  104. data/lib/cloudenvoy/subscriber_logger.rb +26 -0
  105. data/lib/cloudenvoy/subscription.rb +19 -0
  106. data/lib/cloudenvoy/testing.rb +106 -0
  107. data/lib/cloudenvoy/topic.rb +19 -0
  108. data/lib/cloudenvoy/version.rb +1 -1
  109. data/lib/tasks/cloudenvoy.rake +61 -0
  110. metadata +263 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd2ec9d1e57064ce857c167a6ec7059790daa1f6c55dfab0131ba29bc133b2db
4
- data.tar.gz: c15723602a03899edd1abe13bab834609af75ce5dc6eb8487640bfcd1aa56a2f
3
+ metadata.gz: b9322dedfad594507282b8a475531a48ed6c9cae16bb098e115a8a71c9c88485
4
+ data.tar.gz: b44299bb873d6a07028eca918826702c63d75460a7fa7986251792dc5fae81ac
5
5
  SHA512:
6
- metadata.gz: 2f6a835095c6b26d0601a9e244a1b9f4c1262f5ef0f42c7ae9a3b5b8e3d04a9618b8fd8cfef06b419cabb1e11a85d64986b56dbac07b3b1f20f40623a26877d6
7
- data.tar.gz: 6de2c1aa9543cd354e6a304c63f7ceebd97920d68c4f5241f7b478e7887067558f19d24360c1546f8b49e94238ce2eae4e61a227c4d13f94c9a1464ded34a880
6
+ metadata.gz: 512419c58e7eaf81cb9e6de2496cfffd72469a1e4fa5c6b07939645f2ab3b2af5e419b155cae026f5e13910fad762cdc1cdec44b282ed93ef9ac79eb77b8475b
7
+ data.tar.gz: 9f9852d9afbba64c20a855c55e6e08f196bcc6f1a5f72f379e0839a118e638a7c96b98f7c2de227835c8b38c4cf9d4abbb19b29421cb58e85ad9637086d8db2a
@@ -0,0 +1,41 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ branches: [ master ]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ ruby:
15
+ - '2.5.x'
16
+ - '2.6.x'
17
+ appraisal:
18
+ - 'rails-5.2'
19
+ - 'rails-6.0'
20
+ - 'semantic_logger-3.4'
21
+ - 'semantic_logger-4.6'
22
+ - 'semantic_logger-4.7.0'
23
+ - 'semantic_logger-4.7.2'
24
+ steps:
25
+ - name: Setup System
26
+ run: sudo apt-get install libsqlite3-dev
27
+ - uses: actions/checkout@v2
28
+ - uses: zhulik/redis-action@1.1.0
29
+ - name: Set up Ruby 2.6
30
+ uses: actions/setup-ruby@v1
31
+ with:
32
+ ruby-version: ${{ matrix.ruby }}
33
+ - name: Build and test with Rake
34
+ env:
35
+ APPRAISAL_CONTEXT: ${{ matrix.appraisal }}
36
+ run: |
37
+ gem install bundler
38
+ bundle install --jobs 4 --retry 3
39
+ bundle exec rubocop
40
+ bundle exec appraisal ${APPRAISAL_CONTEXT} bundle
41
+ bundle exec appraisal ${APPRAISAL_CONTEXT} rspec
data/.gitignore CHANGED
@@ -3,9 +3,12 @@
3
3
  /_yardoc/
4
4
  /coverage/
5
5
  /doc/
6
+ /examples/rails/log/*.log
7
+ /examples/rails/tmp/
6
8
  /pkg/
7
9
  /spec/reports/
8
10
  /tmp/
11
+ /log/*.log
9
12
 
10
13
  # rspec failure tracking
11
14
  .rspec_status
@@ -32,6 +32,7 @@ RSpec/ScatteredSetup:
32
32
  Metrics/BlockLength:
33
33
  Exclude:
34
34
  - cloudenvoy.gemspec
35
+ - lib/tasks/**/*
35
36
  - 'spec/**/*'
36
37
 
37
38
  Style/Documentation:
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ appraise 'rails-5.2' do
4
+ gem 'rails', '5.2'
5
+ end
6
+
7
+ appraise 'rails-6.0' do
8
+ gem 'rails', '6.0'
9
+ end
10
+
11
+ appraise 'semantic_logger-3.4' do
12
+ gem 'semantic_logger', '3.4.1'
13
+ end
14
+
15
+ appraise 'semantic_logger-4.6' do
16
+ gem 'semantic_logger', '4.6.1'
17
+ end
18
+
19
+ appraise 'semantic_logger-4.7.0' do
20
+ gem 'semantic_logger', '4.7.0'
21
+ end
22
+
23
+ appraise 'semantic_logger-4.7.2' do
24
+ gem 'semantic_logger', '4.7.2'
25
+ end
@@ -0,0 +1,32 @@
1
+ # Changelog
2
+
3
+ ## [v0.4.0](https://github.com/keypup-io/cloudenvoy/tree/v0.4.0) (2020-10-05)
4
+
5
+ [Full Changelog](https://github.com/keypup-io/cloudenvoy/compare/v0.3.1...v0.4.0)
6
+
7
+ **Bug fix:**
8
+ - Logging: fix log processing with `semantic_logger` `v4.7.2`. Accept any args on block passed to the logger.
9
+
10
+ ## [v0.3.1](https://github.com/keypup-io/cloudenvoy/tree/v0.3.1) (2020-10-05)
11
+
12
+ [Full Changelog](https://github.com/keypup-io/cloudenvoy/compare/v0.3.0...v0.3.1)
13
+
14
+ **Improvements:**
15
+ - Development: auto-create topics in development mode when registering subscriptions.
16
+
17
+ ## [v0.3.0](https://github.com/keypup-io/cloudenvoy/tree/v0.3.0) (2020-09-26)
18
+
19
+ [Full Changelog](https://github.com/keypup-io/cloudenvoy/compare/v0.2.0...v0.3.0)
20
+
21
+ **Bug fix:**
22
+ - Subscriptions: fix creation. `v0.2.0` introduced a bug as part of support subscription options (e.g. `retain_acked`)
23
+
24
+ ## [v0.2.0](https://github.com/keypup-io/cloudenvoy/tree/v0.2.0) (2020-09-22)
25
+
26
+ [Full Changelog](https://github.com/keypup-io/cloudenvoy/compare/v0.1.0...v0.2.0)
27
+
28
+ **Improvements:**
29
+ - Subscriptions: support creation options such as `retained_acked` and `deadline`
30
+
31
+ ## [v0.1.0](https://github.com/keypup-io/cloudenvoy/tree/v0.1.0) (2020-09-16)
32
+ Initial release
@@ -1,19 +1,189 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cloudenvoy (0.1.0.dev)
4
+ cloudenvoy (0.4.0)
5
+ activesupport
6
+ google-cloud-pubsub (~> 2.0)
7
+ jwt
8
+ retriable
5
9
 
6
10
  GEM
7
11
  remote: https://rubygems.org/
8
12
  specs:
13
+ actioncable (6.0.3.2)
14
+ actionpack (= 6.0.3.2)
15
+ nio4r (~> 2.0)
16
+ websocket-driver (>= 0.6.1)
17
+ actionmailbox (6.0.3.2)
18
+ actionpack (= 6.0.3.2)
19
+ activejob (= 6.0.3.2)
20
+ activerecord (= 6.0.3.2)
21
+ activestorage (= 6.0.3.2)
22
+ activesupport (= 6.0.3.2)
23
+ mail (>= 2.7.1)
24
+ actionmailer (6.0.3.2)
25
+ actionpack (= 6.0.3.2)
26
+ actionview (= 6.0.3.2)
27
+ activejob (= 6.0.3.2)
28
+ mail (~> 2.5, >= 2.5.4)
29
+ rails-dom-testing (~> 2.0)
30
+ actionpack (6.0.3.2)
31
+ actionview (= 6.0.3.2)
32
+ activesupport (= 6.0.3.2)
33
+ rack (~> 2.0, >= 2.0.8)
34
+ rack-test (>= 0.6.3)
35
+ rails-dom-testing (~> 2.0)
36
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
37
+ actiontext (6.0.3.2)
38
+ actionpack (= 6.0.3.2)
39
+ activerecord (= 6.0.3.2)
40
+ activestorage (= 6.0.3.2)
41
+ activesupport (= 6.0.3.2)
42
+ nokogiri (>= 1.8.5)
43
+ actionview (6.0.3.2)
44
+ activesupport (= 6.0.3.2)
45
+ builder (~> 3.1)
46
+ erubi (~> 1.4)
47
+ rails-dom-testing (~> 2.0)
48
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
49
+ activejob (6.0.3.2)
50
+ activesupport (= 6.0.3.2)
51
+ globalid (>= 0.3.6)
52
+ activemodel (6.0.3.2)
53
+ activesupport (= 6.0.3.2)
54
+ activerecord (6.0.3.2)
55
+ activemodel (= 6.0.3.2)
56
+ activesupport (= 6.0.3.2)
57
+ activestorage (6.0.3.2)
58
+ actionpack (= 6.0.3.2)
59
+ activejob (= 6.0.3.2)
60
+ activerecord (= 6.0.3.2)
61
+ marcel (~> 0.3.1)
62
+ activesupport (6.0.3.2)
63
+ concurrent-ruby (~> 1.0, >= 1.0.2)
64
+ i18n (>= 0.7, < 2)
65
+ minitest (~> 5.1)
66
+ tzinfo (~> 1.1)
67
+ zeitwerk (~> 2.2, >= 2.2.2)
68
+ addressable (2.7.0)
69
+ public_suffix (>= 2.0.2, < 5.0)
70
+ appraisal (2.3.0)
71
+ bundler
72
+ rake
73
+ thor (>= 0.14.0)
9
74
  ast (2.4.1)
75
+ builder (3.2.4)
76
+ concurrent-ruby (1.1.6)
77
+ crack (0.4.3)
78
+ safe_yaml (~> 1.0.0)
79
+ crass (1.0.6)
10
80
  diff-lcs (1.4.2)
81
+ erubi (1.9.0)
82
+ faraday (1.0.1)
83
+ multipart-post (>= 1.2, < 3)
84
+ gapic-common (0.3.4)
85
+ google-protobuf (~> 3.12, >= 3.12.2)
86
+ googleapis-common-protos (>= 1.3.9, < 2.0)
87
+ googleapis-common-protos-types (>= 1.0.4, < 2.0)
88
+ googleauth (~> 0.9)
89
+ grpc (~> 1.25)
90
+ globalid (0.4.2)
91
+ activesupport (>= 4.2.0)
92
+ google-cloud-core (1.5.0)
93
+ google-cloud-env (~> 1.0)
94
+ google-cloud-errors (~> 1.0)
95
+ google-cloud-env (1.3.3)
96
+ faraday (>= 0.17.3, < 2.0)
97
+ google-cloud-errors (1.0.1)
98
+ google-cloud-pubsub (2.0.0)
99
+ concurrent-ruby (~> 1.1)
100
+ google-cloud-core (~> 1.5)
101
+ google-cloud-pubsub-v1 (~> 0.0)
102
+ google-cloud-pubsub-v1 (0.1.2)
103
+ gapic-common (~> 0.3)
104
+ google-cloud-errors (~> 1.0)
105
+ grpc-google-iam-v1 (>= 0.6.10, < 2.0)
106
+ google-protobuf (3.13.0-universal-darwin)
107
+ googleapis-common-protos (1.3.10)
108
+ google-protobuf (~> 3.11)
109
+ googleapis-common-protos-types (>= 1.0.5, < 2.0)
110
+ grpc (~> 1.27)
111
+ googleapis-common-protos-types (1.0.5)
112
+ google-protobuf (~> 3.11)
113
+ googleauth (0.13.1)
114
+ faraday (>= 0.17.3, < 2.0)
115
+ jwt (>= 1.4, < 3.0)
116
+ memoist (~> 0.16)
117
+ multi_json (~> 1.11)
118
+ os (>= 0.9, < 2.0)
119
+ signet (~> 0.14)
120
+ grpc (1.32.0-universal-darwin)
121
+ google-protobuf (~> 3.13)
122
+ googleapis-common-protos-types (~> 1.0)
123
+ grpc-google-iam-v1 (0.6.10)
124
+ google-protobuf (~> 3.11)
125
+ googleapis-common-protos (>= 1.3.10, < 2.0)
126
+ grpc (~> 1.27)
127
+ hashdiff (1.0.1)
128
+ i18n (1.8.5)
129
+ concurrent-ruby (~> 1.0)
11
130
  jaro_winkler (1.5.4)
131
+ jwt (2.2.2)
132
+ loofah (2.6.0)
133
+ crass (~> 1.0.2)
134
+ nokogiri (>= 1.5.9)
135
+ mail (2.7.1)
136
+ mini_mime (>= 0.1.1)
137
+ marcel (0.3.3)
138
+ mimemagic (~> 0.3.2)
139
+ memoist (0.16.2)
140
+ method_source (1.0.0)
141
+ mimemagic (0.3.5)
142
+ mini_mime (1.0.2)
143
+ mini_portile2 (2.4.0)
144
+ minitest (5.14.1)
145
+ multi_json (1.15.0)
146
+ multipart-post (2.1.1)
147
+ nio4r (2.5.2)
148
+ nokogiri (1.10.10)
149
+ mini_portile2 (~> 2.4.0)
150
+ os (1.1.1)
12
151
  parallel (1.19.2)
13
152
  parser (2.7.1.4)
14
153
  ast (~> 2.4.1)
154
+ public_suffix (4.0.5)
155
+ rack (2.2.3)
156
+ rack-test (1.1.0)
157
+ rack (>= 1.0, < 3)
158
+ rails (6.0.3.2)
159
+ actioncable (= 6.0.3.2)
160
+ actionmailbox (= 6.0.3.2)
161
+ actionmailer (= 6.0.3.2)
162
+ actionpack (= 6.0.3.2)
163
+ actiontext (= 6.0.3.2)
164
+ actionview (= 6.0.3.2)
165
+ activejob (= 6.0.3.2)
166
+ activemodel (= 6.0.3.2)
167
+ activerecord (= 6.0.3.2)
168
+ activestorage (= 6.0.3.2)
169
+ activesupport (= 6.0.3.2)
170
+ bundler (>= 1.3.0)
171
+ railties (= 6.0.3.2)
172
+ sprockets-rails (>= 2.0.0)
173
+ rails-dom-testing (2.0.3)
174
+ activesupport (>= 4.2.0)
175
+ nokogiri (>= 1.6)
176
+ rails-html-sanitizer (1.3.0)
177
+ loofah (~> 2.3)
178
+ railties (6.0.3.2)
179
+ actionpack (= 6.0.3.2)
180
+ activesupport (= 6.0.3.2)
181
+ method_source
182
+ rake (>= 0.8.7)
183
+ thor (>= 0.20.3, < 2.0)
15
184
  rainbow (3.0.0)
16
185
  rake (13.0.1)
186
+ retriable (3.1.2)
17
187
  rspec (3.9.0)
18
188
  rspec-core (~> 3.9.0)
19
189
  rspec-expectations (~> 3.9.0)
@@ -26,6 +196,14 @@ GEM
26
196
  rspec-mocks (3.9.1)
27
197
  diff-lcs (>= 1.2.0, < 2.0)
28
198
  rspec-support (~> 3.9.0)
199
+ rspec-rails (4.0.1)
200
+ actionpack (>= 4.2)
201
+ activesupport (>= 4.2)
202
+ railties (>= 4.2)
203
+ rspec-core (~> 3.9)
204
+ rspec-expectations (~> 3.9)
205
+ rspec-mocks (~> 3.9)
206
+ rspec-support (~> 3.9)
29
207
  rspec-support (3.9.3)
30
208
  rubocop (0.76.0)
31
209
  jaro_winkler (~> 1.5.1)
@@ -37,17 +215,53 @@ GEM
37
215
  rubocop-rspec (1.37.0)
38
216
  rubocop (>= 0.68.1)
39
217
  ruby-progressbar (1.10.1)
218
+ safe_yaml (1.0.5)
219
+ semantic_logger (4.7.2)
220
+ concurrent-ruby (~> 1.0)
221
+ signet (0.14.0)
222
+ addressable (~> 2.3)
223
+ faraday (>= 0.17.3, < 2.0)
224
+ jwt (>= 1.5, < 3.0)
225
+ multi_json (~> 1.10)
226
+ sprockets (4.0.2)
227
+ concurrent-ruby (~> 1.0)
228
+ rack (> 1, < 3)
229
+ sprockets-rails (3.2.1)
230
+ actionpack (>= 4.0)
231
+ activesupport (>= 4.0)
232
+ sprockets (>= 3.0.0)
233
+ sqlite3 (1.4.2)
234
+ thor (1.0.1)
235
+ thread_safe (0.3.6)
236
+ timecop (0.9.1)
237
+ tzinfo (1.2.7)
238
+ thread_safe (~> 0.1)
40
239
  unicode-display_width (1.6.1)
240
+ webmock (3.8.3)
241
+ addressable (>= 2.3.6)
242
+ crack (>= 0.3.2)
243
+ hashdiff (>= 0.4.0, < 2.0.0)
244
+ websocket-driver (0.7.3)
245
+ websocket-extensions (>= 0.1.0)
246
+ websocket-extensions (0.1.5)
247
+ zeitwerk (2.4.0)
41
248
 
42
249
  PLATFORMS
43
250
  ruby
44
251
 
45
252
  DEPENDENCIES
253
+ appraisal
46
254
  cloudenvoy!
255
+ rails
47
256
  rake (>= 12.3.3)
48
257
  rspec (~> 3.0)
258
+ rspec-rails
49
259
  rubocop (= 0.76.0)
50
260
  rubocop-rspec (= 1.37.0)
261
+ semantic_logger
262
+ sqlite3
263
+ timecop
264
+ webmock
51
265
 
52
266
  BUNDLED WITH
53
267
  2.1.4
data/README.md CHANGED
@@ -1,8 +1,33 @@
1
+ ![Build Status](https://github.com/keypup-io/cloudenvoy/workflows/Test/badge.svg) [![Gem Version](https://badge.fury.io/rb/cloudenvoy.svg)](https://badge.fury.io/rb/cloudenvoy)
2
+
3
+ **Note**: this gem is currently in alpha stage and has not been tested in production yet.
4
+
1
5
  # Cloudenvoy
2
6
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/cloudenvoy`. To experiment with that code, run `bin/console` for an interactive prompt.
7
+ Cross-application messaging framework for GCP Pub/Sub.
8
+
9
+ Cloudenvoy provides an easy to use interface to GCP Pub/Sub. Using Cloudenvoy you can simplify cross-application event messaging by using a publish/subscribe approach. Pub/Sub is particularly suited for micro-service architectures where a great number of components need to be aware of other components' activities. In these architectures using point to point communication via API can quickly become messy and hard to maintain due to the number of interconnections to maintain.
10
+
11
+ Pub/Sub solves that event distribution problem by allowing developers to define topics, publishers and subscribers to distribute and process event messages. Cloudenvoy furthers simplifies the process of setting up Pub/Sub by giving developers an object-oriented way of managing publishers and subscribers.
12
+
13
+ Cloudenvoy works with the local pub/sub emulator as well, meaning that you can work offline without access to GCP.
14
+
15
+ ## Summary
4
16
 
5
- TODO: Delete this and the text above, and describe your gem
17
+ 1. [Installation](#installation)
18
+ 2. [Get started with Rails](#get-started-with-rails)
19
+ 3. [Configuring Cloudenvoy](#configuring-cloudenvoy)
20
+ 1. [Pub/Sub authentication & permissions](#pubsub-authentication--permissions)
21
+ 2. [Cloudenvoy initializer](#cloudenvoy-initializer)
22
+ 4. [Creating topics and subscriptions](#creating-topics-and-subscriptions)
23
+ 1. [Sending messages](#sending-messages)
24
+ 2. [Publisher implementation layout](#publisher-implementation-layout)
25
+ 5. [Receiving messages](#receiving-messages)
26
+ 6. [Error Handling](#error-handling)
27
+ 7. [Testing](#testing)
28
+ 1. [Test helper setup](#test-helper-setup)
29
+ 2. [In-memory queues](#in-memory-queues)
30
+ 3. [Unit tests](#unit-tests)
6
31
 
7
32
  ## Installation
8
33
 
@@ -20,9 +45,559 @@ Or install it yourself as:
20
45
 
21
46
  $ gem install cloudenvoy
22
47
 
23
- ## Usage
48
+ ## Get started with Rails
49
+
50
+ Cloudenvoy is pre-integrated with Rails. Follow the steps below to get started.
51
+
52
+ Install the pub/sub local emulator
53
+ ```bash
54
+ gcloud components install pubsub-emulator
55
+ gcloud components update
56
+ ```
57
+
58
+ Add the following initializer
59
+ ```ruby
60
+ # config/initializers/cloudenvoy.rb
61
+
62
+ Cloudenvoy.configure do |config|
63
+ #
64
+ # GCP Configuration
65
+ #
66
+ config.gcp_project_id = 'some-project'
67
+ config.gcp_sub_prefix = 'my-app'
68
+
69
+ #
70
+ # Adapt the server port to be the one used by your Rails web process
71
+ #
72
+ config.processor_host = 'http://localhost:3000'
73
+
74
+ #
75
+ # If you do not have any Rails secret_key_base defined, uncomment the following
76
+ # This secret is used to authenticate messages sent to the processing endpoint
77
+ # of your application.
78
+ #
79
+ # config.secret = 'some-long-token'
80
+ end
81
+ ```
82
+
83
+ Define a publisher:
84
+ ```ruby
85
+ # app/publishers/dummy_publisher.rb
86
+
87
+ class DummyPublisher
88
+ include Cloudenvoy::Publisher
89
+
90
+ cloudenvoy_options topic: 'test-msgs'
91
+
92
+ # Format the message payload. The payload can be a hash
93
+ # or a string.
94
+ def payload(msg)
95
+ {
96
+ type: 'message',
97
+ content: msg
98
+ }
99
+ end
100
+ end
101
+ ```
102
+
103
+ Define a subscriber:
104
+ ```ruby
105
+ # app/subscribers/dummy_subscriber.rb
106
+
107
+ class HelloSubscriber
108
+ include Cloudenvoy::Subscriber
109
+
110
+ cloudenvoy_options topics: ['test-msgs']
111
+
112
+ # Do something with the message
113
+ def process(message)
114
+ logger.info("Received message #{message.payload.dig('content')}")
115
+ end
116
+ end
117
+ ```
118
+
119
+ Launch the pub/sub emulator:
120
+ ```bash
121
+ gcloud beta emulators pubsub start
122
+ ```
123
+
124
+ Use cloudenvoy to setup your topic and subscription
125
+ ```bash
126
+ bundle exec rake cloudenvoy:setup
127
+ ```
128
+
129
+ Launch Rails
130
+ ```bash
131
+ rails s -p 3000
132
+ ```
133
+
134
+ Open a Rails console and send a message
135
+ ```ruby
136
+ DummyPublisher.publish('Hello pub/sub')
137
+ ```
138
+
139
+ Your Rails logs should display the following:
140
+ ```log
141
+ Started POST "/cloudenvoy/receive?token=1234" for 66.102.6.140 at 2020-09-16 11:12:47 +0200
142
+ Processing by Cloudenvoy::SubscriberController#receive as JSON
143
+ Parameters: {"message"=>{"attributes"=>{"kind"=>"hello"}, "data"=>"eyJ0eXBlIjoibWVzc2FnZSIsImNvbnRlbnQiOiJIZWxsbyBmcmllbmQifQ==", "messageId"=>"1501653492745522", "message_id"=>"1501653492745522", "publishTime"=>"2020-09-16T09:12:45.214Z", "publish_time"=>"2020-09-16T09:12:45.214Z"}, "subscription"=>"projects/keypup-dev/subscriptions/my-app.hello_subscriber.test-msgs", "token"=>"eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MDAyNDc0OTh9.5SbVsDCZLcyoFXseCpPuvE7KY7WXqIQtO6ceoFXcrdw", "subscriber"=>{"message"=>{"attributes"=>{"kind"=>"hello"}, "data"=>"eyJ0eXBlIjoibWVzc2FnZSIsImNvbnRlbnQiOiJIZWxsbyBmcmllbmQifQ==", "messageId"=>"1501653492745522", "message_id"=>"1501653492745522", "publishTime"=>"2020-09-16T09:12:45.214Z", "publish_time"=>"2020-09-16T09:12:45.214Z"}, "subscription"=>"projects/keypup-dev/subscriptions/my-app.hello_subscriber.test-msgs"}}
144
+ [Cloudenvoy][HelloSubscriber][1501653492745522] Processing message... -- {:id=>"1501653492745522", :metadata=>{}, :topic=>"test-msgs"}
145
+ [Cloudenvoy][HelloSubscriber][1501653492745522] Received message Hello pub/sub -- {:id=>"1501653492745522", :metadata=>{}, :topic=>"test-msgs"}
146
+ [Cloudenvoy][HelloSubscriber][1501653492745522] Processing done after 0.001s -- {:id=>"1501653492745522", :metadata=>{}, :topic=>"test-msgs", :duration=>0.001}
147
+ Completed 204 No Content in 1ms (ActiveRecord: 0.0ms | Allocations: 500)
148
+ ```
149
+
150
+ Hurray! Your published message was immediately processed by the subscriber.
151
+
152
+ ## Configuring Cloudenvoy
153
+
154
+ ### Pub/Sub authentication & permissions
155
+
156
+ The Google Cloud library authenticates via the Google Cloud SDK by default. If you do not have it setup then we recommend you [install it](https://cloud.google.com/sdk/docs/quickstarts).
157
+
158
+ Other options are available such as using a service account. You can see all authentication options in the [Google Cloud Authentication guide](https://github.com/googleapis/google-cloud-ruby/blob/master/google-cloud-bigquery/AUTHENTICATION.md).
159
+
160
+ In order to function properly Cloudenvoy requires the authenticated account to have the following IAM permissions:
161
+ - `pubsub.subscriptions.create`
162
+ - `pubsub.subscriptions.get`
163
+ - `pubsub.topics.create`
164
+ - `pubsub.topics.get`
165
+ - `pubsub.topics.publish`
166
+
167
+ To get started quickly you can add the `roles/pubsub.admin` role to your account via the [IAM Console](https://console.cloud.google.com/iam-admin/iam). This is not required if your account is a project admin account.
168
+
169
+ ### Cloudenvoy initializer
170
+
171
+ The gem can be configured through an initializer. See below all the available configuration options.
172
+
173
+ ```ruby
174
+ # config/initializers/cloudenvoy.rb
175
+
176
+ Cloudenvoy.configure do |config|
177
+ #
178
+ # If you do not have any Rails secret_key_base defined, uncomment the following.
179
+ # This secret is used to authenticate messages sent to the processing endpoint
180
+ # of your application.
181
+ #
182
+ # Default with Rails: Rails.application.credentials.secret_key_base
183
+ #
184
+ # config.secret = 'some-long-token'
185
+
186
+ #
187
+ # GCP Configuration
188
+ #
189
+ config.gcp_project_id = 'some-project'
190
+
191
+ #
192
+ # Specify the namespace for your subscriptions
193
+ #
194
+ # The gem attempts to keep GCP subscriptions organized by
195
+ # properly namespacing them. Each subscription has the following
196
+ # format:
197
+ # > projects/<gcp_project_id>/subscriptions/<gcp_sub_prefix>.<subscriber_class>.<topic>
198
+ #
199
+ config.gcp_sub_prefix = 'my-app'
200
+
201
+ #
202
+ # Specify the publicly accessible host for your application
203
+ #
204
+ # > E.g. in development, using the pub/sub local emulator
205
+ # config.processor_host = 'http://localhost:3000'
206
+ #
207
+ # > E.g. in development, using `config.mode = :production` and ngrok
208
+ # config.processor_host = 'https://111111.ngrok.io'
209
+ #
210
+ config.processor_host = 'https://app.mydomain.com'
211
+
212
+ #
213
+ # Specify the mode of operation:
214
+ # - :development => messages will be pushed to the local pub/sub emulator
215
+ # - :production => messages will be pushed to Google Cloud Pub/Sub. Requires a publicly accessible domain.
216
+ #
217
+ # Defaults to :development unless CLOUDENVOY_ENV or RAILS_ENV or RACK_ENV is set to something else.
218
+ #
219
+ # config.mode = Rails.env.production? || Rails.env.my_other_env? ? :production : :development
220
+
221
+ #
222
+ # Specify the logger to use
223
+ #
224
+ # Default with Rails: Rails.logger
225
+ # Default without Rails: Logger.new(STDOUT)
226
+ #
227
+ # config.logger = MyLogger.new(STDOUT)
228
+ end
229
+ ```
230
+
231
+ ### Creating topics and subscriptions
232
+
233
+ Topics and subscriptions can be created using the provided Rake tasks:
234
+ ```bash
235
+ # Setup publishers (topics) and subscribers (subscriptions) in one go
236
+ bundle exec rake cloudenvoy:setup
237
+
238
+ # Or set them up individually
239
+ bundle exec rake cloudenvoy:setup_publishers
240
+ bundle exec rake cloudenvoy:setup_subscribers
241
+ ```
242
+
243
+ For non-rails applications you can run the following in a console to setup your publishers and subscribers:
244
+ ```ruby
245
+ DummyPublisher.setup
246
+ DummySubscriber.setup
247
+ ```
248
+
249
+ ## Publishing messages
250
+
251
+ ### Sending messages
252
+
253
+ Cloudenvoy provides a helper method to publish arbitrary messages to any topic.
254
+ ```ruby
255
+ Cloudenvoy.publish('my-topic', { 'some' => 'payload' }, { 'optional' => 'message attribute' })
256
+ ```
257
+
258
+ This helper is useful for sending basic messages however it is not the preferred way of sending messages as you will quickly clutter your application with message formatting logic over time.
259
+
260
+ Cloudenvoy provides an object-oriented way of sending messages allowing developers to separate their core business logic from any kind of message formatting logic. These are called `Publishers`.
261
+
262
+ The example below shows you how to publish new users to a topic using Cloudenvoy publishers:
263
+ ```ruby
264
+ # app/publishers/user_publisher.rb
265
+
266
+ # The publisher is responsible for configuring and formatting
267
+ # the pub/sub message.
268
+ class UserPublisher
269
+ include Cloudenvoy::Publisher
270
+
271
+ cloudenvoy_options topic: 'system-users'
272
+
273
+ # Publishers must at least implement the `payload` method,
274
+ # which specifies how the message should be formatted.
275
+ def payload(user)
276
+ {
277
+ id: user.id,
278
+ name: user.name,
279
+ email: user.email
280
+ }
281
+ end
282
+ end
283
+ ```
284
+
285
+ Then in your user model you can do the following:
286
+ ```ruby
287
+ # app/users/user_publisher.rb
288
+
289
+ class User < ApplicationRecord
290
+ after_create :publish_user
291
+
292
+ private
293
+
294
+ # Publish users after they have been created
295
+ def publish_user
296
+ UserPublisher.publish(self)
297
+ end
298
+ end
299
+ ```
300
+
301
+ ### Publisher implementation layout
302
+
303
+ A full publisher implementation looks like this:
304
+ ```ruby
305
+ class MyPublisher
306
+ include Cloudenvoy::Publisher
307
+
308
+ # The topic option defines the default topic messages will be
309
+ # sent to. The publishing topic can be overriden on a per message
310
+ # basis. See the #topic method below.
311
+ cloudenvoy_options topic: 'my-topic'
312
+
313
+ # Evaluate the topic at runtime based on publishing arguments.
314
+ # Returning `nil` makes the publisher use the default topic
315
+ # defined via cloudenvoy_options.
316
+ #
317
+ # Note: runtime topics do not get created by the rake tasks. You
318
+ # must create them manually at this stage.
319
+ def topic(arg1, arg2)
320
+ arg1 == 'other' ? 'some-other-topic' : nil
321
+ end
322
+
323
+ # Attach pub/sub attributes to the message. Pub/sub attributes
324
+ # can be used for message filtering.
325
+ def metadata(arg1, arg2)
326
+ { reference: "#{arg1}_#{arg2}" }
327
+ end
328
+
329
+ # Publishers must at least implement the `payload` method,
330
+ # which specifies how arguments should be transformed into
331
+ # a message payload (Hash or String).
332
+ def payload(arg1, arg2)
333
+ {
334
+ foo: arg1,
335
+ bar: arg2
336
+ }
337
+ end
338
+
339
+ # This hook is invoked when the message fails to be formatted and published.
340
+ # If something wrong happens in the methods above, this hook will be triggered.
341
+ def on_error(error)
342
+ logger.error("Oops! Something wrong happened!")
343
+ end
344
+ end
345
+ ```
346
+
347
+ ## Receiving messages
348
+
349
+ After you have subscribed to a topic, Pub/Sub sends messages to your application via webhook on the `/cloudenvoy/receive` endpoint. Cloudenvoy then automatically dispatches the message to the right subscriber for processing.
350
+
351
+ Following up on the previous user publishing example, you might define the following subscriber in another Rails application:
352
+
353
+ ```ruby
354
+ # app/subscribers/user_subscriber.rb
355
+
356
+ class UserSubscriber
357
+ include Cloudenvoy::Subscriber
358
+
359
+ # Subscribers can subscribe to multiple topics
360
+ #
361
+ # You can subscribe to multiple topics:
362
+ # > cloudenvoy_options topics: ['system-users', 'events']
363
+ #
364
+ # You can specify subscription options for each topic
365
+ # by passing a hash (target: v0.2.0)
366
+ #
367
+ # > cloudenvoy_options topics: ['system-users', { name: 'events', retain_acked: true }]
368
+ #
369
+ # See the Pub/Sub documentation of the list of available subscription options:
370
+ # https://googleapis.dev/ruby/google-cloud-pubsub/latest/Google/Cloud/PubSub/Topic.html#subscribe-instance_method
371
+ #
372
+ cloudenvoy_options topic: 'system-users'
373
+
374
+ # Create the user locally if it does not exist already
375
+ #
376
+ # A message has the following attributes:
377
+ # id: the pub/sub message id
378
+ # payload: the content of the message (String or Hash)
379
+ # metadata: the pub/sub message attributes
380
+ # sub_uri: the pub/sub subscription URI
381
+ # topic: the topic the message comes from
382
+ #
383
+ def process(message)
384
+ payload = message.payload
385
+
386
+ User.create_or_find_by(system_id: payload['id']) do |u|
387
+ u.first_name = payload['name']
388
+ u.email = payload['email']
389
+ end
390
+ end
391
+
392
+ # This hook will be invoked if the message processing fails
393
+ def on_error(error)
394
+ logger.error("The following error happened: #{error}")
395
+ end
396
+ end
397
+ ```
398
+
399
+ ## Logging
400
+ There are several options available to configure logging and logging context.
401
+
402
+ ### Configuring a logger
403
+ Cloudenvoy uses `Rails.logger` if Rails is available and falls back on a plain ruby logger `Logger.new(STDOUT)` if not.
404
+
405
+ It is also possible to configure your own logger. For example you can setup Cloudenvoy with [semantic_logger](http://rocketjob.github.io/semantic_logger) by doing the following in your initializer:
406
+ ```ruby
407
+ # config/initializers/cloudenvoy.rb
408
+
409
+ Cloudenvoy.configure do |config|
410
+ config.logger = SemanticLogger[Cloudenvoy]
411
+ end
412
+ ```
413
+
414
+ ### Logging context
415
+ Cloudenvoy provides publisher/subscriber contextual information to the `logger` methods.
416
+
417
+ For example:
418
+ ```ruby
419
+ # app/subscribers/dummy_subscriber.rb
420
+
421
+ class DummySubscriber
422
+ include Cloudenvoy::Subscriber
423
+
424
+ cloudenvoy_options topics: ['my-topic']
425
+
426
+ def process(message)
427
+ logger.info("Subscriber processed with #{message.inspect}. This is working!")
428
+ end
429
+ end
430
+ ```
431
+
432
+ Will generate the following log with context `{:id=>..., :metadata=>..., :topic=>...}`
433
+ ```log
434
+ [Cloudenvoy][DummySubscriber][1501678353930997] Subscriber processed with ###. This is working! -- {:id=>"1501678353930997", :metadata=>{"some"=>"meta"}, :topic=>"my-topic"}
435
+ ```
436
+
437
+ The way contextual information is displayed depends on the logger itself. For example with [semantic_logger](http://rocketjob.github.io/semantic_logger) contextual information might not appear in the log message but show up as payload data on the log entry itself (e.g. using the fluentd adapter).
438
+
439
+ Contextual information can be customised globally and locally using a log context_processor. By default the loggers are configured this way:
440
+ ```ruby
441
+ # Publishers
442
+ Cloudenvoy::PublisherLogger.log_context_processor = ->(publisher) { publisher.message&.to_h&.slice(:id, :metadata, :topic) || {} }
443
+
444
+ # Subscribers
445
+ Cloudenvoy::SubscriberLogger.log_context_processor = ->(subscriber) { subscriber.message.to_h.slice(:id, :metadata, :topic) }
446
+ ```
447
+
448
+ You can decide to add a global identifier for your publisher logs using the following:
449
+ ```ruby
450
+ # config/initializers/cloudenvoy.rb
451
+
452
+ Cloudenvoy::PublisherLogger.log_context_processor = lambda { |publisher|
453
+ publisher.message.to_h.slice(:id, :metadata, :topic).merge(app: 'my-app')
454
+ }
455
+ ```
456
+
457
+ You could also decide to log all available context - including the message payload - for specific subscribers only:
458
+ ```ruby
459
+ # app/subscribers/full_context_subscriber.rb
460
+
461
+ class FullContextSubscriber
462
+ include Cloudenvoy::Subscriber
463
+
464
+ cloudenvoy_options topics: ['my-topic'], log_context_processor: ->(s) { s.message.to_h }
465
+
466
+ def process(message)
467
+ logger.info("This log entry will have full context!")
468
+ end
469
+ end
470
+ ```
471
+
472
+ See the [Cloudenvoy::Publisher](lib/cloudenvoy/publisher.rb), [Cloudenvoy::Subscriber](lib/cloudenvoy/subscriber.rb) and [Cloudenvoy::Message](lib/cloudenvoy/message.rb) for more information on attributes available to be logged in your `log_context_processor` proc.
473
+
474
+ ## Error Handling
475
+
476
+ Message failures will return an HTTP error to Pub/Sub and trigger a retry at a later time. By default Pub/Sub will retry sending the message until the acknowledgment deadline expires. A number of retries can be explicitly configured by setting up a dead-letter queue.
477
+
478
+ ### HTTP Error codes
479
+
480
+ When Cloudenvoy fails to process a message it returns the following HTTP error code to Pub/Sub, based on the actual reason:
24
481
 
25
- TODO: Write usage instructions here
482
+ | Code | Description |
483
+ |------|-------------|
484
+ | 204 | The message was processed successfully |
485
+ | 404 | The message subscriber does not exist. |
486
+ | 422 | An error occured during the processing of the message (`process` method) |
487
+
488
+ ### Error callbacks
489
+
490
+ Publishers and subscribers can implement the `on_error(error)` callback to do things when a message fails to be published or received:
491
+
492
+ E.g.
493
+ ```ruby
494
+ # app/publisher/handle_error_publisher.rb
495
+
496
+ class HandleErrorPublisher
497
+ include Cloudenvoy::Publisher
498
+
499
+ cloudenvoy_options topic: 'my-topic'
500
+
501
+ def payload(arg)
502
+ raise(ArgumentError)
503
+ end
504
+
505
+ # The runtime error is passed as an argument.
506
+ def on_error(error)
507
+ logger.error("The following error occured: #{error}")
508
+ end
509
+ end
510
+ ```
511
+
512
+ ## Testing
513
+ Cloudenvoy provides several options to test your publishers and subscribers.
514
+
515
+ ### Test helper setup
516
+ Require `cloudenvoy/testing` in your `rails_helper.rb` (Rspec Rails) or `spec_helper.rb` (Rspec) or test unit helper file then enable one of the two modes:
517
+
518
+ ```ruby
519
+ require 'cloudenvoy/testing'
520
+
521
+ # Mode 1 (default): Push messages to GCP Pub/Sub (env != development)
522
+ Cloudenvoy::Testing.enable!
523
+
524
+ # Mode 2: Push message to in-memory queues. You will be responsible for clearing the
525
+ # topic queues using `Cloudenvoy::Testing.clear_all` or `Cloudenvoy::Testing.clear('my-topic')`
526
+ Cloudenvoy::Testing.fake!
527
+ ```
528
+
529
+ You can query the current testing mode with:
530
+ ```ruby
531
+ Cloudenvoy::Testing.enabled?
532
+ Cloudenvoy::Testing.fake?
533
+ ```
534
+
535
+ Each testing mode accepts a block argument to temporarily switch to it:
536
+ ```ruby
537
+ # Enable fake mode for all tests
538
+ Cloudenvoy::Testing.fake!
539
+
540
+ # Enable real mode temporarily for a given test
541
+ Cloudenvoy.enable! do
542
+ MyPublisher.publish(1,2)
543
+ end
544
+ ```
545
+
546
+ Note that extension middlewares - if any has been registered - run in test mode. You can disable middlewares in your tests by adding the following to your test helper:
547
+ ```ruby
548
+ # Remove all middlewares
549
+ Cloudenvoy.configure do |c|
550
+ c.publisher_middleware.clear
551
+ c.subscriber_middleware.clear
552
+ end
553
+
554
+ # Remove all specific middlewares
555
+ Cloudenvoy.configure do |c|
556
+ c.publisher_middleware.remove(MyMiddleware::Publisher)
557
+ c.subscriber_middleware.remove(MyMiddleware::Subscriber)
558
+ end
559
+ ```
560
+
561
+ ### In-memory queues
562
+ The `fake!` modes uses in-memory queues for topics, which can be queried and controlled using the following methods:
563
+
564
+ ```ruby
565
+ # Clear all messages across all topics
566
+ Cloudenvoy::Testing.clear_all
567
+
568
+ # Remove all messages in a given topic
569
+ Cloudenvoy::Testing.clear('my-top')
570
+
571
+ # Get all messages for a given topic
572
+ Cloudenvoy::Testing.queue('my-top')
573
+ ```
574
+
575
+ ### Unit tests
576
+ Below are examples of rspec tests. It is assumed that `Cloudenvoy::Testing.fake!` has been set in the test helper.
577
+
578
+ **Example 1**: Testing publishers
579
+ ```ruby
580
+ describe 'message publishing'
581
+ subject(:publish_message) { MyPublisher.publish(1,2) }
582
+
583
+ let(:queue) { Cloudenvoy::Testing.queue('my-topic') }
584
+
585
+ it { expect { publish_message }.to change(queue, :size).by(1) }
586
+ it { is_expected.to have_attributes(payload: { 'foo' => 'bar' }) }
587
+ end
588
+ ```
589
+
590
+ **Example 2**: Testing subscribers
591
+ ```ruby
592
+ describe 'message processing'
593
+ subject { VerifyDataViaApiSubscriber.new(message: message).execute } }
594
+
595
+ let(:message) { Cloudenvoy::Message.new(payload: { 'some' => 'payload' }) }
596
+
597
+ before { expect(MyApi).to receive(:fetch).and_return([]) }
598
+ it { is_expected.to be_truthy }
599
+ end
600
+ ```
26
601
 
27
602
  ## Development
28
603
 
@@ -32,8 +607,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
32
607
 
33
608
  ## Contributing
34
609
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/alachaum/cloudenvoy. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/alachaum/cloudenvoy/blob/master/CODE_OF_CONDUCT.md).
36
-
610
+ Bug reports and pull requests are welcome on GitHub at https://github.com/keypup-io/cloudenvoy. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/keypup-io/cloudenvoy/blob/master/CODE_OF_CONDUCT.md).
37
611
 
38
612
  ## License
39
613
 
@@ -41,4 +615,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
41
615
 
42
616
  ## Code of Conduct
43
617
 
44
- Everyone interacting in the Cloudenvoy project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/alachaum/cloudenvoy/blob/master/CODE_OF_CONDUCT.md).
618
+ Everyone interacting in the Cloudenvoy project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/keypup-io/cloudenvoy/blob/master/CODE_OF_CONDUCT.md).