pub_sub_model_sync 0.2.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6108e077451c0ffc3d3a4b277528ccf036a64edf85c84bb810bc7ce2769bdc35
4
- data.tar.gz: 63924273967917c2b5f740f0f917fa772341bc7fbd7c77b52037e9c264e5a080
3
+ metadata.gz: 04acaa2b5befa05fd52978367afdb24c6a1a5858e10441620a2860a57ea9cd55
4
+ data.tar.gz: 25297748dd5e1099f9f251d73f7f94c0680e9c04820c3396143f9b3ca17170ad
5
5
  SHA512:
6
- metadata.gz: 480a5c3d69cf06837e443ed6abfef5d927ddfc3df1c48e9948487e470666cdbb11711bb73e17c85d594c29c176a3fc0108867563a14d630152f078f2034d54af
7
- data.tar.gz: 57ec430feef5ed8a5b2318703620b2d5f0d6faba14d1227f108a5411ee6fde587219e00fd3cedb8735211c9ff24dac5127af4b88428749164ce3631eb5691e45
6
+ metadata.gz: dea58e39fd6d77599000a932f7db942052d5309069c8adb0e571fcb2b7be4a5d4429048371b5bac51c18ff9f016dc88fc0e85ea73ca04bc93f36536c580ea544
7
+ data.tar.gz: cead04daf416641b5d900d4c8c6a13509c49fa8256dd343552c70b578f5af3a3ba39465069999688418e4de60ba4f9a62b4e40e3b54c0c55dd89dcf366f28ab2
@@ -0,0 +1,33 @@
1
+ name: Ruby Gem
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request:
8
+
9
+
10
+ jobs:
11
+ build:
12
+ name: Tests and Code Style
13
+ runs-on: ubuntu-latest
14
+
15
+ steps:
16
+ - uses: actions/checkout@v2
17
+ - name: Set up Ruby 2.6
18
+ uses: actions/setup-ruby@v1
19
+ with:
20
+ ruby-version: 2.6.x
21
+
22
+ - name: Install sqlite3
23
+ run: sudo apt-get install libsqlite3-dev
24
+
25
+ - name: Bundle install
26
+ run: |
27
+ gem install bundler
28
+ bundle install --jobs 4 --retry 3
29
+ - name: Tests (rspec)
30
+ run: |
31
+ bundle exec rspec
32
+ - name: Code style (Rubocop)
33
+ run: bundle exec rubocop
@@ -0,0 +1,8 @@
1
+ # Default ignored files
2
+ /shelf/
3
+ /workspace.xml
4
+ # Datasource local storage ignored files
5
+ /dataSources/
6
+ /dataSources.local.xml
7
+ # Editor-based HTTP Client requests
8
+ /httpRequests/
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Settings><!--This file was automatically generated by Ruby plugin.
3
+ You are allowed to:
4
+ 1. Remove rake task
5
+ 2. Add existing rake tasks
6
+ To add existing rake tasks automatically delete this file and reload the project.
7
+ --><RakeGroup description="" fullCmd="" taksId="rake"><RakeTask description="Build pub_sub_model_sync-0.1.0.gem into the pkg directory" fullCmd="build" taksId="build" /><RakeTask description="Remove any temporary products" fullCmd="clean" taksId="clean" /><RakeTask description="Remove any generated files" fullCmd="clobber" taksId="clobber" /><RakeTask description="Build and install pub_sub_model_sync-0.1.0.gem into system gems" fullCmd="install" taksId="install" /><RakeGroup description="" fullCmd="" taksId="install"><RakeTask description="Build and install pub_sub_model_sync-0.1.0.gem into system gems without network access" fullCmd="install:local" taksId="local" /></RakeGroup><RakeTask description="Create tag v0.1.0 and build and push pub_sub_model_sync-0.1.0.gem to TODO: Set to 'http://mygemserver.com'" fullCmd="release[remote]" taksId="release[remote]" /><RakeTask description="Run RSpec code examples" fullCmd="spec" taksId="spec" /><RakeTask description="" fullCmd="default" taksId="default" /><RakeTask description="" fullCmd="release" taksId="release" /><RakeGroup description="" fullCmd="" taksId="release"><RakeTask description="" fullCmd="release:guard_clean" taksId="guard_clean" /><RakeTask description="" fullCmd="release:rubygem_push" taksId="rubygem_push" /><RakeTask description="" fullCmd="release:source_control_push" taksId="source_control_push" /></RakeGroup></RakeGroup></Settings>
@@ -0,0 +1,5 @@
1
+ <component name="ProjectCodeStyleConfiguration">
2
+ <state>
3
+ <option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
4
+ </state>
5
+ </component>
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="Encoding" addBOMForNewFiles="with NO BOM" />
4
+ </project>
@@ -0,0 +1,16 @@
1
+ <component name="InspectionProjectProfileManager">
2
+ <profile version="1.0">
3
+ <option name="myName" value="Project Default" />
4
+ <inspection_tool class="Rubocop" enabled="true" level="WARNING" enabled_by_default="true">
5
+ <option name="mySeverityMap">
6
+ <map>
7
+ <entry key="convention" value="ERROR" />
8
+ <entry key="error" value="ERROR" />
9
+ <entry key="fatal" value="ERROR" />
10
+ <entry key="refactor" value="ERROR" />
11
+ <entry key="warning" value="ERROR" />
12
+ </map>
13
+ </option>
14
+ </inspection_tool>
15
+ </profile>
16
+ </component>
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="JavaScriptSettings">
4
+ <option name="languageLevel" value="ES6" />
5
+ </component>
6
+ <component name="ProjectRootManager" version="2" project-jdk-name="RVM: ruby-2.5.3" project-jdk-type="RUBY_SDK" />
7
+ </project>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/pub_sub_model_sync.iml" filepath="$PROJECT_DIR$/.idea/pub_sub_model_sync.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
@@ -0,0 +1,96 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="RUBY_MODULE" version="4">
3
+ <component name="ModuleRunConfigurationManager">
4
+ <shared />
5
+ </component>
6
+ <component name="NewModuleRootManager">
7
+ <content url="file://$MODULE_DIR$" />
8
+ <orderEntry type="jdk" jdkName="RVM: ruby-2.6.5" jdkType="RUBY_SDK" />
9
+ <orderEntry type="sourceFolder" forTests="false" />
10
+ <orderEntry type="library" scope="PROVIDED" name="actioncable (v6.0.2.2, RVM: ruby-2.6.5) [gem]" level="application" />
11
+ <orderEntry type="library" scope="PROVIDED" name="actionmailbox (v6.0.2.2, RVM: ruby-2.6.5) [gem]" level="application" />
12
+ <orderEntry type="library" scope="PROVIDED" name="actionmailer (v6.0.2.2, RVM: ruby-2.6.5) [gem]" level="application" />
13
+ <orderEntry type="library" scope="PROVIDED" name="actionpack (v6.0.2.2, RVM: ruby-2.6.5) [gem]" level="application" />
14
+ <orderEntry type="library" scope="PROVIDED" name="actiontext (v6.0.2.2, RVM: ruby-2.6.5) [gem]" level="application" />
15
+ <orderEntry type="library" scope="PROVIDED" name="actionview (v6.0.2.2, RVM: ruby-2.6.5) [gem]" level="application" />
16
+ <orderEntry type="library" scope="PROVIDED" name="activejob (v6.0.2.2, RVM: ruby-2.6.5) [gem]" level="application" />
17
+ <orderEntry type="library" scope="PROVIDED" name="activemodel (v6.0.2.2, RVM: ruby-2.6.5) [gem]" level="application" />
18
+ <orderEntry type="library" scope="PROVIDED" name="activerecord (v6.0.2.2, RVM: ruby-2.6.5) [gem]" level="application" />
19
+ <orderEntry type="library" scope="PROVIDED" name="activestorage (v6.0.2.2, RVM: ruby-2.6.5) [gem]" level="application" />
20
+ <orderEntry type="library" scope="PROVIDED" name="activesupport (v6.0.2.2, RVM: ruby-2.6.5) [gem]" level="application" />
21
+ <orderEntry type="library" scope="PROVIDED" name="addressable (v2.7.0, RVM: ruby-2.6.5) [gem]" level="application" />
22
+ <orderEntry type="library" scope="PROVIDED" name="amq-protocol (v2.3.0, RVM: ruby-2.6.5) [gem]" level="application" />
23
+ <orderEntry type="library" scope="PROVIDED" name="ast (v2.4.0, RVM: ruby-2.6.5) [gem]" level="application" />
24
+ <orderEntry type="library" scope="PROVIDED" name="builder (v3.2.4, RVM: ruby-2.6.5) [gem]" level="application" />
25
+ <orderEntry type="library" scope="PROVIDED" name="bundler (v2.1.4, RVM: ruby-2.6.5) [gem]" level="application" />
26
+ <orderEntry type="library" scope="PROVIDED" name="bunny (v2.14.3, RVM: ruby-2.6.5) [gem]" level="application" />
27
+ <orderEntry type="library" scope="PROVIDED" name="concurrent-ruby (v1.1.6, RVM: ruby-2.6.5) [gem]" level="application" />
28
+ <orderEntry type="library" scope="PROVIDED" name="crass (v1.0.6, RVM: ruby-2.6.5) [gem]" level="application" />
29
+ <orderEntry type="library" scope="PROVIDED" name="database_cleaner (v1.8.4, RVM: ruby-2.6.5) [gem]" level="application" />
30
+ <orderEntry type="library" scope="PROVIDED" name="database_cleaner-active_record (v1.8.0, RVM: ruby-2.6.5) [gem]" level="application" />
31
+ <orderEntry type="library" scope="PROVIDED" name="diff-lcs (v1.3, RVM: ruby-2.6.5) [gem]" level="application" />
32
+ <orderEntry type="library" scope="PROVIDED" name="digest-crc (v0.5.1, RVM: ruby-2.6.5) [gem]" level="application" />
33
+ <orderEntry type="library" scope="PROVIDED" name="erubi (v1.9.0, RVM: ruby-2.6.5) [gem]" level="application" />
34
+ <orderEntry type="library" scope="PROVIDED" name="faraday (v0.17.3, RVM: ruby-2.6.5) [gem]" level="application" />
35
+ <orderEntry type="library" scope="PROVIDED" name="globalid (v0.4.2, RVM: ruby-2.6.5) [gem]" level="application" />
36
+ <orderEntry type="library" scope="PROVIDED" name="google-cloud-core (v1.3.2, RVM: ruby-2.6.5) [gem]" level="application" />
37
+ <orderEntry type="library" scope="PROVIDED" name="google-cloud-env (v1.2.1, RVM: ruby-2.6.5) [gem]" level="application" />
38
+ <orderEntry type="library" scope="PROVIDED" name="google-cloud-pubsub (v1.0.2, RVM: ruby-2.6.5) [gem]" level="application" />
39
+ <orderEntry type="library" scope="PROVIDED" name="google-gax (v1.7.1, RVM: ruby-2.6.5) [gem]" level="application" />
40
+ <orderEntry type="library" scope="PROVIDED" name="google-protobuf (v3.11.4, RVM: ruby-2.6.5) [gem]" level="application" />
41
+ <orderEntry type="library" scope="PROVIDED" name="googleapis-common-protos (v1.3.9, RVM: ruby-2.6.5) [gem]" level="application" />
42
+ <orderEntry type="library" scope="PROVIDED" name="googleapis-common-protos-types (v1.0.4, RVM: ruby-2.6.5) [gem]" level="application" />
43
+ <orderEntry type="library" scope="PROVIDED" name="googleauth (v0.9.0, RVM: ruby-2.6.5) [gem]" level="application" />
44
+ <orderEntry type="library" scope="PROVIDED" name="grpc (v1.27.0, RVM: ruby-2.6.5) [gem]" level="application" />
45
+ <orderEntry type="library" scope="PROVIDED" name="grpc-google-iam-v1 (v0.6.9, RVM: ruby-2.6.5) [gem]" level="application" />
46
+ <orderEntry type="library" scope="PROVIDED" name="i18n (v1.8.2, RVM: ruby-2.6.5) [gem]" level="application" />
47
+ <orderEntry type="library" scope="PROVIDED" name="jaro_winkler (v1.5.4, RVM: ruby-2.6.5) [gem]" level="application" />
48
+ <orderEntry type="library" scope="PROVIDED" name="jwt (v2.2.1, RVM: ruby-2.6.5) [gem]" level="application" />
49
+ <orderEntry type="library" scope="PROVIDED" name="loofah (v2.5.0, RVM: ruby-2.6.5) [gem]" level="application" />
50
+ <orderEntry type="library" scope="PROVIDED" name="mail (v2.7.1, RVM: ruby-2.6.5) [gem]" level="application" />
51
+ <orderEntry type="library" scope="PROVIDED" name="marcel (v0.3.3, RVM: ruby-2.6.5) [gem]" level="application" />
52
+ <orderEntry type="library" scope="PROVIDED" name="memoist (v0.16.2, RVM: ruby-2.6.5) [gem]" level="application" />
53
+ <orderEntry type="library" scope="PROVIDED" name="method_source (v1.0.0, RVM: ruby-2.6.5) [gem]" level="application" />
54
+ <orderEntry type="library" scope="PROVIDED" name="mimemagic (v0.3.4, RVM: ruby-2.6.5) [gem]" level="application" />
55
+ <orderEntry type="library" scope="PROVIDED" name="mini_mime (v1.0.2, RVM: ruby-2.6.5) [gem]" level="application" />
56
+ <orderEntry type="library" scope="PROVIDED" name="mini_portile2 (v2.4.0, RVM: ruby-2.6.5) [gem]" level="application" />
57
+ <orderEntry type="library" scope="PROVIDED" name="minitest (v5.14.0, RVM: ruby-2.6.5) [gem]" level="application" />
58
+ <orderEntry type="library" scope="PROVIDED" name="multi_json (v1.14.1, RVM: ruby-2.6.5) [gem]" level="application" />
59
+ <orderEntry type="library" scope="PROVIDED" name="multipart-post (v2.1.1, RVM: ruby-2.6.5) [gem]" level="application" />
60
+ <orderEntry type="library" scope="PROVIDED" name="nio4r (v2.5.2, RVM: ruby-2.6.5) [gem]" level="application" />
61
+ <orderEntry type="library" scope="PROVIDED" name="nokogiri (v1.10.9, RVM: ruby-2.6.5) [gem]" level="application" />
62
+ <orderEntry type="library" scope="PROVIDED" name="os (v1.0.1, RVM: ruby-2.6.5) [gem]" level="application" />
63
+ <orderEntry type="library" scope="PROVIDED" name="parallel (v1.19.1, RVM: ruby-2.6.5) [gem]" level="application" />
64
+ <orderEntry type="library" scope="PROVIDED" name="parser (v2.7.0.4, RVM: ruby-2.6.5) [gem]" level="application" />
65
+ <orderEntry type="library" scope="PROVIDED" name="public_suffix (v4.0.3, RVM: ruby-2.6.5) [gem]" level="application" />
66
+ <orderEntry type="library" scope="PROVIDED" name="rack (v2.2.2, RVM: ruby-2.6.5) [gem]" level="application" />
67
+ <orderEntry type="library" scope="PROVIDED" name="rack-test (v1.1.0, RVM: ruby-2.6.5) [gem]" level="application" />
68
+ <orderEntry type="library" scope="PROVIDED" name="rails (v6.0.2.2, RVM: ruby-2.6.5) [gem]" level="application" />
69
+ <orderEntry type="library" scope="PROVIDED" name="rails-dom-testing (v2.0.3, RVM: ruby-2.6.5) [gem]" level="application" />
70
+ <orderEntry type="library" scope="PROVIDED" name="rails-html-sanitizer (v1.3.0, RVM: ruby-2.6.5) [gem]" level="application" />
71
+ <orderEntry type="library" scope="PROVIDED" name="railties (v6.0.2.2, RVM: ruby-2.6.5) [gem]" level="application" />
72
+ <orderEntry type="library" scope="PROVIDED" name="rainbow (v3.0.0, RVM: ruby-2.6.5) [gem]" level="application" />
73
+ <orderEntry type="library" scope="PROVIDED" name="rake (v13.0.1, RVM: ruby-2.6.5) [gem]" level="application" />
74
+ <orderEntry type="library" scope="PROVIDED" name="rexml (v3.2.4, RVM: ruby-2.6.5) [gem]" level="application" />
75
+ <orderEntry type="library" scope="PROVIDED" name="rly (v0.2.3, RVM: ruby-2.6.5) [gem]" level="application" />
76
+ <orderEntry type="library" scope="PROVIDED" name="rspec (v3.9.0, RVM: ruby-2.6.5) [gem]" level="application" />
77
+ <orderEntry type="library" scope="PROVIDED" name="rspec-core (v3.9.1, RVM: ruby-2.6.5) [gem]" level="application" />
78
+ <orderEntry type="library" scope="PROVIDED" name="rspec-expectations (v3.9.0, RVM: ruby-2.6.5) [gem]" level="application" />
79
+ <orderEntry type="library" scope="PROVIDED" name="rspec-mocks (v3.9.1, RVM: ruby-2.6.5) [gem]" level="application" />
80
+ <orderEntry type="library" scope="PROVIDED" name="rspec-support (v3.9.2, RVM: ruby-2.6.5) [gem]" level="application" />
81
+ <orderEntry type="library" scope="PROVIDED" name="rubocop (v0.80.1, RVM: ruby-2.6.5) [gem]" level="application" />
82
+ <orderEntry type="library" scope="PROVIDED" name="ruby-kafka (v1.0.0, RVM: ruby-2.6.5) [gem]" level="application" />
83
+ <orderEntry type="library" scope="PROVIDED" name="ruby-progressbar (v1.10.1, RVM: ruby-2.6.5) [gem]" level="application" />
84
+ <orderEntry type="library" scope="PROVIDED" name="signet (v0.11.0, RVM: ruby-2.6.5) [gem]" level="application" />
85
+ <orderEntry type="library" scope="PROVIDED" name="sprockets (v4.0.0, RVM: ruby-2.6.5) [gem]" level="application" />
86
+ <orderEntry type="library" scope="PROVIDED" name="sprockets-rails (v3.2.1, RVM: ruby-2.6.5) [gem]" level="application" />
87
+ <orderEntry type="library" scope="PROVIDED" name="sqlite3 (v1.4.2, RVM: ruby-2.6.5) [gem]" level="application" />
88
+ <orderEntry type="library" scope="PROVIDED" name="thor (v1.0.1, RVM: ruby-2.6.5) [gem]" level="application" />
89
+ <orderEntry type="library" scope="PROVIDED" name="thread_safe (v0.3.6, RVM: ruby-2.6.5) [gem]" level="application" />
90
+ <orderEntry type="library" scope="PROVIDED" name="tzinfo (v1.2.7, RVM: ruby-2.6.5) [gem]" level="application" />
91
+ <orderEntry type="library" scope="PROVIDED" name="unicode-display_width (v1.6.1, RVM: ruby-2.6.5) [gem]" level="application" />
92
+ <orderEntry type="library" scope="PROVIDED" name="websocket-driver (v0.7.1, RVM: ruby-2.6.5) [gem]" level="application" />
93
+ <orderEntry type="library" scope="PROVIDED" name="websocket-extensions (v0.1.4, RVM: ruby-2.6.5) [gem]" level="application" />
94
+ <orderEntry type="library" scope="PROVIDED" name="zeitwerk (v2.3.0, RVM: ruby-2.6.5) [gem]" level="application" />
95
+ </component>
96
+ </module>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
5
+ </component>
6
+ </project>
@@ -1,5 +1,27 @@
1
1
  # Change Log
2
2
 
3
+ # 0.3.0 (April 29, 2020)
4
+ - Support for multiple identifiers when syncing
5
+ - Add klass.ps_find_model method for a custom model finder
6
+
7
+ # 0.2.4 (April 28, 2020)
8
+ - Delegate .publish to the .publisher for better understanding
9
+
10
+ # 0.2.3 (April 15, 2020)
11
+ - Improve helper names
12
+ - feat: perform manual sync with custom settings
13
+ - fix for "IO timeout when reading 7 bytes" error (Rabbit)
14
+ - style: do not print processed message when failed
15
+ - feat: retry delivery message when failed (RabbitMQ)
16
+
17
+
18
+ # 0.2.2 (March 27, 2020)
19
+ - fix default value for cattr_accessor in ror < 5.2
20
+ - add callbacks when publishing a message
21
+
22
+ # 0.2.1
23
+ - Add on demand model sync method
24
+
3
25
  # 0.2.0
4
26
  - Add apache kafka support
5
27
  - Add Service interface for future references
data/Gemfile CHANGED
@@ -5,5 +5,9 @@ gem 'bunny' # rabbit-mq
5
5
  gem 'google-cloud-pubsub' # google pub/sub
6
6
  gem 'ruby-kafka' # kafka pub/sub
7
7
 
8
+ group :test do
9
+ gem 'database_cleaner-active_record'
10
+ end
11
+
8
12
  # Specify your gem's dependencies in pub_sub_model_sync.gemspec
9
13
  gemspec
@@ -1,64 +1,81 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pub_sub_model_sync (0.2.0)
4
+ pub_sub_model_sync (0.3.0)
5
5
  activesupport
6
6
  rails
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- actioncable (5.2.4.1)
12
- actionpack (= 5.2.4.1)
11
+ actioncable (6.0.2.2)
12
+ actionpack (= 6.0.2.2)
13
13
  nio4r (~> 2.0)
14
14
  websocket-driver (>= 0.6.1)
15
- actionmailer (5.2.4.1)
16
- actionpack (= 5.2.4.1)
17
- actionview (= 5.2.4.1)
18
- activejob (= 5.2.4.1)
15
+ actionmailbox (6.0.2.2)
16
+ actionpack (= 6.0.2.2)
17
+ activejob (= 6.0.2.2)
18
+ activerecord (= 6.0.2.2)
19
+ activestorage (= 6.0.2.2)
20
+ activesupport (= 6.0.2.2)
21
+ mail (>= 2.7.1)
22
+ actionmailer (6.0.2.2)
23
+ actionpack (= 6.0.2.2)
24
+ actionview (= 6.0.2.2)
25
+ activejob (= 6.0.2.2)
19
26
  mail (~> 2.5, >= 2.5.4)
20
27
  rails-dom-testing (~> 2.0)
21
- actionpack (5.2.4.1)
22
- actionview (= 5.2.4.1)
23
- activesupport (= 5.2.4.1)
28
+ actionpack (6.0.2.2)
29
+ actionview (= 6.0.2.2)
30
+ activesupport (= 6.0.2.2)
24
31
  rack (~> 2.0, >= 2.0.8)
25
32
  rack-test (>= 0.6.3)
26
33
  rails-dom-testing (~> 2.0)
27
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
28
- actionview (5.2.4.1)
29
- activesupport (= 5.2.4.1)
34
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
35
+ actiontext (6.0.2.2)
36
+ actionpack (= 6.0.2.2)
37
+ activerecord (= 6.0.2.2)
38
+ activestorage (= 6.0.2.2)
39
+ activesupport (= 6.0.2.2)
40
+ nokogiri (>= 1.8.5)
41
+ actionview (6.0.2.2)
42
+ activesupport (= 6.0.2.2)
30
43
  builder (~> 3.1)
31
44
  erubi (~> 1.4)
32
45
  rails-dom-testing (~> 2.0)
33
- rails-html-sanitizer (~> 1.0, >= 1.0.3)
34
- activejob (5.2.4.1)
35
- activesupport (= 5.2.4.1)
46
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
47
+ activejob (6.0.2.2)
48
+ activesupport (= 6.0.2.2)
36
49
  globalid (>= 0.3.6)
37
- activemodel (5.2.4.1)
38
- activesupport (= 5.2.4.1)
39
- activerecord (5.2.4.1)
40
- activemodel (= 5.2.4.1)
41
- activesupport (= 5.2.4.1)
42
- arel (>= 9.0)
43
- activestorage (5.2.4.1)
44
- actionpack (= 5.2.4.1)
45
- activerecord (= 5.2.4.1)
50
+ activemodel (6.0.2.2)
51
+ activesupport (= 6.0.2.2)
52
+ activerecord (6.0.2.2)
53
+ activemodel (= 6.0.2.2)
54
+ activesupport (= 6.0.2.2)
55
+ activestorage (6.0.2.2)
56
+ actionpack (= 6.0.2.2)
57
+ activejob (= 6.0.2.2)
58
+ activerecord (= 6.0.2.2)
46
59
  marcel (~> 0.3.1)
47
- activesupport (5.2.4.1)
60
+ activesupport (6.0.2.2)
48
61
  concurrent-ruby (~> 1.0, >= 1.0.2)
49
62
  i18n (>= 0.7, < 2)
50
63
  minitest (~> 5.1)
51
64
  tzinfo (~> 1.1)
65
+ zeitwerk (~> 2.2)
52
66
  addressable (2.7.0)
53
67
  public_suffix (>= 2.0.2, < 5.0)
54
68
  amq-protocol (2.3.0)
55
- arel (9.0.0)
56
69
  ast (2.4.0)
57
70
  builder (3.2.4)
58
71
  bunny (2.14.3)
59
72
  amq-protocol (~> 2.3, >= 2.3.0)
60
73
  concurrent-ruby (1.1.6)
61
74
  crass (1.0.6)
75
+ database_cleaner (1.8.4)
76
+ database_cleaner-active_record (1.8.0)
77
+ activerecord
78
+ database_cleaner (~> 1.8.0)
62
79
  diff-lcs (1.3)
63
80
  digest-crc (0.5.1)
64
81
  erubi (1.9.0)
@@ -106,7 +123,7 @@ GEM
106
123
  concurrent-ruby (~> 1.0)
107
124
  jaro_winkler (1.5.4)
108
125
  jwt (2.2.1)
109
- loofah (2.4.0)
126
+ loofah (2.5.0)
110
127
  crass (~> 1.0.2)
111
128
  nokogiri (>= 1.5.9)
112
129
  mail (2.7.1)
@@ -114,7 +131,7 @@ GEM
114
131
  marcel (0.3.3)
115
132
  mimemagic (~> 0.3.2)
116
133
  memoist (0.16.2)
117
- method_source (0.9.2)
134
+ method_source (1.0.0)
118
135
  mimemagic (0.3.4)
119
136
  mini_mime (1.0.2)
120
137
  mini_portile2 (2.4.0)
@@ -132,30 +149,32 @@ GEM
132
149
  rack (2.2.2)
133
150
  rack-test (1.1.0)
134
151
  rack (>= 1.0, < 3)
135
- rails (5.2.4.1)
136
- actioncable (= 5.2.4.1)
137
- actionmailer (= 5.2.4.1)
138
- actionpack (= 5.2.4.1)
139
- actionview (= 5.2.4.1)
140
- activejob (= 5.2.4.1)
141
- activemodel (= 5.2.4.1)
142
- activerecord (= 5.2.4.1)
143
- activestorage (= 5.2.4.1)
144
- activesupport (= 5.2.4.1)
152
+ rails (6.0.2.2)
153
+ actioncable (= 6.0.2.2)
154
+ actionmailbox (= 6.0.2.2)
155
+ actionmailer (= 6.0.2.2)
156
+ actionpack (= 6.0.2.2)
157
+ actiontext (= 6.0.2.2)
158
+ actionview (= 6.0.2.2)
159
+ activejob (= 6.0.2.2)
160
+ activemodel (= 6.0.2.2)
161
+ activerecord (= 6.0.2.2)
162
+ activestorage (= 6.0.2.2)
163
+ activesupport (= 6.0.2.2)
145
164
  bundler (>= 1.3.0)
146
- railties (= 5.2.4.1)
165
+ railties (= 6.0.2.2)
147
166
  sprockets-rails (>= 2.0.0)
148
167
  rails-dom-testing (2.0.3)
149
168
  activesupport (>= 4.2.0)
150
169
  nokogiri (>= 1.6)
151
170
  rails-html-sanitizer (1.3.0)
152
171
  loofah (~> 2.3)
153
- railties (5.2.4.1)
154
- actionpack (= 5.2.4.1)
155
- activesupport (= 5.2.4.1)
172
+ railties (6.0.2.2)
173
+ actionpack (= 6.0.2.2)
174
+ activesupport (= 6.0.2.2)
156
175
  method_source
157
176
  rake (>= 0.8.7)
158
- thor (>= 0.19.0, < 2.0)
177
+ thor (>= 0.20.3, < 2.0)
159
178
  rainbow (3.0.0)
160
179
  rake (13.0.1)
161
180
  rexml (3.2.4)
@@ -189,7 +208,7 @@ GEM
189
208
  faraday (~> 0.9)
190
209
  jwt (>= 1.5, < 3.0)
191
210
  multi_json (~> 1.10)
192
- sprockets (3.7.2)
211
+ sprockets (4.0.0)
193
212
  concurrent-ruby (~> 1.0)
194
213
  rack (> 1, < 3)
195
214
  sprockets-rails (3.2.1)
@@ -199,12 +218,13 @@ GEM
199
218
  sqlite3 (1.4.2)
200
219
  thor (1.0.1)
201
220
  thread_safe (0.3.6)
202
- tzinfo (1.2.6)
221
+ tzinfo (1.2.7)
203
222
  thread_safe (~> 0.1)
204
223
  unicode-display_width (1.6.1)
205
224
  websocket-driver (0.7.1)
206
225
  websocket-extensions (>= 0.1.0)
207
226
  websocket-extensions (0.1.4)
227
+ zeitwerk (2.3.0)
208
228
 
209
229
  PLATFORMS
210
230
  ruby
@@ -212,6 +232,7 @@ PLATFORMS
212
232
  DEPENDENCIES
213
233
  bundler
214
234
  bunny
235
+ database_cleaner-active_record
215
236
  google-cloud-pubsub
216
237
  pub_sub_model_sync!
217
238
  rake
@@ -221,4 +242,4 @@ DEPENDENCIES
221
242
  sqlite3
222
243
 
223
244
  BUNDLED WITH
224
- 2.0.2
245
+ 2.1.4
data/README.md CHANGED
@@ -67,7 +67,7 @@ And then execute: $ bundle install
67
67
  # attributes: name email age
68
68
  class User < ActiveRecord::Base
69
69
  include PubSubModelSync::PublisherConcern
70
- ps_publish(%i[name email])
70
+ ps_publish(%i[id name email])
71
71
  end
72
72
 
73
73
  # App 2 (Subscriber)
@@ -83,6 +83,8 @@ end
83
83
 
84
84
  # Samples
85
85
  User.create(name: 'test user', email: 'sample@gmail.com') # Review your App 2 to see the created user (only name will be saved)
86
+ User.new(name: 'test user').ps_perform_sync(:create) # similar to above to perform sync on demand
87
+
86
88
  User.ps_class_publish({ msg: 'Hello' }, action: :greeting) # User.greeting method (Class method) will be called in App2
87
89
  PubSubModelSync::Publisher.new.publish_data(User, { msg: 'Hello' }, :greeting) # similar to above when not included publisher concern
88
90
  ```
@@ -93,26 +95,118 @@ PubSubModelSync::Publisher.new.publish_data(User, { msg: 'Hello' }, :greeting) #
93
95
  class User < ActiveRecord::Base
94
96
  self.table_name = 'publisher_users'
95
97
  include PubSubModelSync::PublisherConcern
96
- ps_publish(%i[name:full_name email], actions: %i[update], as_klass: 'Client', id: :client_id)
98
+ ps_publish(%i[id:client_id name:full_name email], actions: %i[update], as_klass: 'Client')
97
99
 
98
- def ps_skip_for?(_action)
100
+ def ps_skip_callback?(_action)
99
101
  false # here logic with action to skip push message
100
102
  end
103
+
104
+ def ps_skip_sync?(_action)
105
+ false # here logic with action to skip push message
106
+ end
101
107
  end
102
108
 
103
109
  # App 2 (Subscriber)
104
110
  class User < ActiveRecord::Base
105
111
  self.table_name = 'subscriber_users'
106
112
  include PubSubModelSync::SubscriberConcern
107
- ps_subscribe(%i[name], actions: %i[update], as_klass: 'Client', id: :custom_id)
113
+ ps_subscribe(%i[name], actions: %i[update], as_klass: 'Client', id: %i[client_id email])
108
114
  ps_class_subscribe(:greeting, as_action: :custom_greeting, as_klass: 'CustomUser')
115
+ alias_attribute :full_name, :name
109
116
 
110
117
  def self.greeting(data)
111
118
  puts 'Class message called through custom_greeting'
112
119
  end
120
+
121
+ # def self.ps_find_model(data, settings)
122
+ # where(email: data[:email], ...).first_or_initialize
123
+ # end
113
124
  end
114
125
  ```
115
126
 
127
+ Note: Be careful with collision of names
128
+ ```
129
+ class User
130
+ # ps_publish %i[name_data:name name:key] # key will be replaced with name_data
131
+ ps_publish %i[name_data:name key_data:key] # use alias to avoid collision
132
+
133
+ def key_data
134
+ name
135
+ end
136
+ end
137
+ ```
138
+
139
+ ## API
140
+ ### Subscribers
141
+ - Permit to configure class level listeners
142
+ ```ps_class_subscribe(action_name, as_action: nil, as_klass: nil)```
143
+ * as_action: (Optional) Source method name
144
+ * as_klass: (Optional) Source class name
145
+
146
+ - Permit to configure instance level listeners (CRUD)
147
+ ```ps_subscribe(attrs, as_klass: nil, actions: nil, id: nil)```
148
+ * attrs: (Array/Required) Array of all attributes to be synced
149
+ * as_klass: (String/Optional) Source class name (Instead of the model class name, will use this value)
150
+ * actions: (Array/Optional, default: create/update/destroy) permit to customize action names
151
+ * id: (Sym|Array/Optional, default: id) Attr identifier(s) to find the corresponding model
152
+
153
+ - Permit to configure a custom model finder
154
+ ```ps_find_model(data, settings)```
155
+ * data: (Hash) Data received from sync
156
+ * settings: (Hash(:klass, :action)) Class and action name from sync
157
+ Must return an existent or a new model object
158
+
159
+ - Get crud subscription configured for the class
160
+ ```User.ps_subscriber(action_name)```
161
+ * action_name (default :create, :sym): can be :create, :update, :destroy
162
+
163
+ - Inspect all configured listeners
164
+ ```PubSubModelSync::Config.listeners```
165
+
166
+ ### Publishers
167
+ - Permit to configure crud publishers
168
+ ```ps_publish(attrs, actions: nil, as_klass: nil)```
169
+ * attrs: (Array/Required) Array of attributes to be published
170
+ * actions: (Array/Optional, default: create/update/destroy) permit to customize action names
171
+ * as_klass: (String/Optional) Output class name (Instead of the model class name, will use this value)
172
+
173
+ - Permit to cancel sync called after create/update/destroy (Before initializing sync service)
174
+ ```model.ps_skip_callback?(action)```
175
+ Note: Return true to cancel sync
176
+
177
+ - Callback called before preparing data for sync (Permit to stop sync)
178
+ ```model.ps_skip_sync?(action)```
179
+ Note: return true to cancel sync
180
+
181
+ - Callback called before sync (After preparing data)
182
+ ```model.ps_before_sync(action, data_to_deliver)```
183
+ Note: If the method returns ```:cancel```, the sync will be stopped (message will not be published)
184
+
185
+ - Callback called after sync
186
+ ```model.ps_after_sync(action, data_delivered)```
187
+
188
+ - Perform sync on demand (:create, :update, :destroy):
189
+ The target model will receive a notification to perform the indicated action
190
+ ```my_model.ps_perform_sync(action_name, custom_settings = {})```
191
+ * custom_settings: override default settings defined for action_name ({ attrs: [], as_klass: nil })
192
+
193
+ - Publish a class level notification:
194
+ ```User.ps_class_publish(data, action: action_name, as_klass: custom_klass_name)```
195
+ Target class ```User.action_name``` will be called when message is received
196
+ * data: (required, :hash) message value to deliver
197
+ * action_name: (required, :sim) Action name
198
+ * as_klass: (optional, :string) Custom class name (Default current model name)
199
+
200
+ - Publish a class level notification (Same as above: on demand call)
201
+ ```PubSubModelSync::Publisher.new.publish_data(Klass_name, data, action_name)```
202
+ * klass_name: (required, Class) same class name as defined in ps_class_subscribe(...)
203
+ * data: (required, :hash) message value to deliver
204
+ * action_name: (required, :sim) same action name as defined in ps_class_subscribe(...)
205
+
206
+ - Get crud publisher configured for the class
207
+ ```User.ps_publisher(action_name)```
208
+ * action_name (default :create, :sym): can be :create, :update, :destroy
209
+
116
210
  ## Testing with RSpec
117
211
  - Config: (spec/rails_helper.rb)
118
212
  ```ruby
@@ -144,12 +238,10 @@ end
144
238
  # Subscriber
145
239
  it 'receive model message' do
146
240
  action = :create
147
- data = { name: 'name' }
148
- user_id = 999
149
- attrs = PubSubModelSync::Publisher.build_attrs('User', action, user_id)
150
- publisher = PubSubModelSync::MessageProcessor.new(data, 'User', action, id: user_id)
241
+ data = { name: 'name', id: 999 }
242
+ publisher = PubSubModelSync::MessageProcessor.new(data, 'User', action)
151
243
  publisher.process
152
- expect(User.where(id: user_id).any?).to be_truth
244
+ expect(User.where(id: data[:id]).any?).to be_truth
153
245
  end
154
246
 
155
247
  it 'receive class message' do
@@ -178,15 +270,6 @@ end
178
270
  expect_any_instance_of(publisher).to receive(:publish_data).with('User', data, action)
179
271
  end
180
272
  ```
181
-
182
- There are two special methods to extract crud configuration settings (attrs, id, ...):
183
-
184
- Subscribers: ```User.ps_subscriber```
185
-
186
- Publishers: ```User.ps_publisher```
187
-
188
- Note: Inspect all configured listeners with:
189
- ``` PubSubModelSync::Config.listeners ```
190
273
 
191
274
  ## Contributing
192
275
 
@@ -2,9 +2,9 @@
2
2
 
3
3
  module PubSubModelSync
4
4
  class Config
5
- cattr_accessor :listeners, default: []
6
- cattr_accessor :publishers, default: []
7
- cattr_accessor :service_name, default: :google
5
+ cattr_accessor(:listeners) { [] }
6
+ cattr_accessor(:publishers) { [] }
7
+ cattr_accessor(:service_name) { :google }
8
8
  cattr_accessor :logger
9
9
 
10
10
  # google service
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'google/cloud/pubsub'
4
3
  module PubSubModelSync
5
4
  class Connector
6
5
  attr_accessor :service
@@ -2,21 +2,23 @@
2
2
 
3
3
  module PubSubModelSync
4
4
  class MessageProcessor
5
- attr_accessor :data, :attrs, :settings
5
+ attr_accessor :data, :settings, :message_id
6
6
 
7
7
  # @param data (Hash): any hash value to deliver
8
- # @param settings (optional): { id: id_val }
9
- def initialize(data, klass, action, settings = {})
8
+ def initialize(data, klass, action)
10
9
  @data = data
11
- @settings = settings
12
- @attrs = settings.merge(klass: klass, action: action)
10
+ @settings = { klass: klass, action: action }
11
+ @message_id = [klass, action, Time.current.to_i].join('-')
13
12
  end
14
13
 
15
14
  def process
16
- log 'processing message'
15
+ @failed = false
16
+ log "processing message: #{[data, settings]}"
17
17
  listeners = filter_listeners
18
- eval_message(listeners) if listeners.any?
19
- log 'processed message'
18
+ return log 'Skipped: No listeners' unless listeners.any?
19
+
20
+ eval_message(listeners)
21
+ log 'processed message' unless @failed
20
22
  end
21
23
 
22
24
  private
@@ -36,12 +38,13 @@ module PubSubModelSync
36
38
  model_class.send(listener[:action], data)
37
39
  rescue => e
38
40
  log("Error listener (#{listener}): #{e.message}", :error)
41
+ @failed = true
39
42
  end
40
43
 
41
44
  # support for: create, update, destroy
42
45
  def call_listener(listener)
43
46
  model = find_model(listener)
44
- if attrs[:action].to_sym == :destroy
47
+ if settings[:action].to_sym == :destroy
45
48
  model.destroy!
46
49
  else
47
50
  populate_model(model, listener)
@@ -49,13 +52,22 @@ module PubSubModelSync
49
52
  end
50
53
  rescue => e
51
54
  log("Error listener (#{listener}): #{e.message}", :error)
55
+ @failed = true
52
56
  end
53
57
 
54
58
  def find_model(listener)
55
59
  model_class = listener[:klass].constantize
56
- identifier = listener[:settings][:id] || :id
57
- model_class.where(identifier => attrs[:id]).first ||
58
- model_class.new(identifier => attrs[:id])
60
+ if model_class.respond_to?(:ps_find_model)
61
+ return model_class.ps_find_model(data, settings)
62
+ end
63
+
64
+ model_class.where(model_identifiers(listener)).first_or_initialize
65
+ end
66
+
67
+ def model_identifiers(listener)
68
+ identifiers = listener[:settings][:id]
69
+ identifiers = [identifiers] unless identifiers.is_a?(Array)
70
+ identifiers.map { |key| [key, data[key.to_sym]] }.to_h
59
71
  end
60
72
 
61
73
  def populate_model(model, listener)
@@ -68,13 +80,13 @@ module PubSubModelSync
68
80
  def filter_listeners
69
81
  listeners = PubSubModelSync::Config.listeners
70
82
  listeners.select do |listener|
71
- listener[:as_klass].to_s == attrs[:klass].to_s &&
72
- listener[:as_action].to_s == attrs[:action].to_s
83
+ listener[:as_klass].to_s == settings[:klass].to_s &&
84
+ listener[:as_action].to_s == settings[:action].to_s
73
85
  end
74
86
  end
75
87
 
76
88
  def log(message, kind = :info)
77
- PubSubModelSync::Config.log "#{message} ==> #{[data, attrs]}", kind
89
+ PubSubModelSync::Config.log "(ID: #{message_id}) #{message}", kind
78
90
  end
79
91
  end
80
92
  end
@@ -30,6 +30,10 @@ module PubSubModelSync
30
30
  def topic(*_args)
31
31
  @topic ||= MockTopic.new
32
32
  end
33
+
34
+ def close
35
+ true
36
+ end
33
37
  end
34
38
 
35
39
  def create_channel(*_args)
@@ -3,30 +3,33 @@
3
3
  module PubSubModelSync
4
4
  class Publisher
5
5
  attr_accessor :connector
6
+ delegate :publish, to: :connector
7
+
6
8
  def initialize
7
9
  @connector = PubSubModelSync::Connector.new
8
10
  end
9
11
 
10
12
  def publish_data(klass, data, action)
11
13
  attributes = self.class.build_attrs(klass, action)
12
- connector.publish(data, attributes)
14
+ publish(data, attributes)
13
15
  end
14
16
 
15
- # @param settings (Hash): { attrs: [], as_klass: nil, id: nil }
16
- def publish_model(model, action, settings = nil)
17
- settings ||= model.class.ps_publisher_info(action)
17
+ # @param custom_settings (Hash): { attrs: [], as_klass: nil }
18
+ def publish_model(model, action, custom_settings = {})
19
+ return if model.ps_skip_sync?(action)
20
+
21
+ settings = model.class.ps_publisher(action).merge(custom_settings)
18
22
  attributes = build_model_attrs(model, action, settings)
19
- data = {}
20
- data = build_model_data(model, settings[:attrs]) if action != :destroy
21
- connector.publish(data.symbolize_keys, attributes)
23
+ data = build_model_data(model, settings[:attrs])
24
+ res_before = model.ps_before_sync(action, data)
25
+ return if res_before == :cancel
26
+
27
+ publish(data.symbolize_keys, attributes)
28
+ model.ps_after_sync(action, data)
22
29
  end
23
30
 
24
- def self.build_attrs(klass, action, id = nil)
25
- {
26
- klass: klass.to_s,
27
- action: action.to_sym,
28
- id: id
29
- }
31
+ def self.build_attrs(klass, action)
32
+ { klass: klass.to_s, action: action.to_sym }
30
33
  end
31
34
 
32
35
  private
@@ -44,8 +47,7 @@ module PubSubModelSync
44
47
 
45
48
  def build_model_attrs(model, action, settings)
46
49
  as_klass = (settings[:as_klass] || model.class.name).to_s
47
- id_val = model.send(settings[:id] || :id)
48
- self.class.build_attrs(as_klass, action, id_val)
50
+ self.class.build_attrs(as_klass, action)
49
51
  end
50
52
 
51
53
  def log(msg)
@@ -6,34 +6,55 @@ module PubSubModelSync
6
6
  base.extend(ClassMethods)
7
7
  end
8
8
 
9
- # Permit to skip a publish callback
10
- def ps_skip_for?(_action)
9
+ # Before initializing sync service (callbacks: after create/update/destroy)
10
+ def ps_skip_callback?(_action)
11
11
  false
12
12
  end
13
13
 
14
+ # before preparing data to sync
15
+ def ps_skip_sync?(_action)
16
+ false
17
+ end
18
+
19
+ # before delivering data
20
+ def ps_before_sync(_action, _data); end
21
+
22
+ # after delivering data
23
+ def ps_after_sync(_action, _data); end
24
+
25
+ # To perform sync on demand
26
+ # @param custom_settings (Hash): { attrs: [], as_klass: nil }
27
+ def ps_perform_sync(action = :create, custom_settings = {})
28
+ service = self.class.ps_publisher_service
29
+ model_settings = self.class.ps_publisher(action) || {}
30
+ service.publish_model(self, action, model_settings.merge(custom_settings))
31
+ end
32
+
14
33
  module ClassMethods
15
- # Permit to publish crud actions (:create, :update, :destroy)
16
- # @param settings (Hash): { actions: nil, as_klass: nil, id: nil }
17
- def ps_publish(attrs, settings = {})
18
- actions = settings.delete(:actions) || %i[create update destroy]
34
+ # Permit to configure to publish crud actions (:create, :update, :destroy)
35
+ def ps_publish(attrs, actions: %i[create update destroy], as_klass: nil)
36
+ as_klass ||= name
19
37
  actions.each do |action|
20
- info = settings.merge(klass: name, action: action, attrs: attrs)
38
+ info = { klass: name, action: action, attrs: attrs,
39
+ as_klass: as_klass }
21
40
  PubSubModelSync::Config.publishers << info
22
41
  ps_register_callback(action.to_sym, info)
23
42
  end
24
43
  end
25
44
 
26
- def ps_publisher_info(action = :create)
27
- PubSubModelSync::Config.publishers.select do |listener|
28
- listener[:klass] == name && listener[:action] == action
29
- end.last
30
- end
31
-
45
+ # On demand class level publisher
32
46
  def ps_class_publish(data, action:, as_klass: nil)
33
47
  as_klass = (as_klass || name).to_s
34
48
  ps_publisher_service.publish_data(as_klass, data, action.to_sym)
35
49
  end
36
50
 
51
+ # Publisher info for specific action
52
+ def ps_publisher(action = :create)
53
+ PubSubModelSync::Config.publishers.find do |listener|
54
+ listener[:klass] == name && listener[:action] == action
55
+ end
56
+ end
57
+
37
58
  def ps_publisher_service
38
59
  PubSubModelSync::Publisher.new
39
60
  end
@@ -42,7 +63,7 @@ module PubSubModelSync
42
63
 
43
64
  def ps_register_callback(action, info)
44
65
  after_commit(on: action) do |model|
45
- unless model.ps_skip_for?(action)
66
+ unless model.ps_skip_callback?(action)
46
67
  service = model.class.ps_publisher_service
47
68
  service.publish_model(model, action.to_sym, info)
48
69
  end
@@ -18,11 +18,11 @@ module PubSubModelSync
18
18
 
19
19
  private
20
20
 
21
- # @param payload (String JSON): '{"data":{},"attributes":{..}}'
21
+ # @param payload (String JSON): '{"data":{}, "attributes":{..}}'
22
22
  # refer: PubSubModelSync::Publisher (.publish_model | .publish_data)
23
23
  def perform_message(payload)
24
24
  data, attrs = parse_message_payload(payload)
25
- args = [data, attrs[:klass], attrs[:action], attrs]
25
+ args = [data, attrs[:klass], attrs[:action]]
26
26
  PubSubModelSync::MessageProcessor.new(*args).process
27
27
  end
28
28
 
@@ -28,9 +28,12 @@ module PubSubModelSync
28
28
 
29
29
  def publish(data, attributes)
30
30
  log("Publishing: #{[data, attributes]}")
31
- subscribe_to_queue
32
- payload = { data: data, attributes: attributes }
33
- topic.publish(payload.to_json, message_settings)
31
+ deliver_data(data, attributes)
32
+ # TODO: max retry
33
+ rescue Timeout::Error => e
34
+ log("Error publishing (retrying....): #{e.message}", :error)
35
+ initialize
36
+ retry
34
37
  rescue => e
35
38
  info = [data, attributes, e.message, e.backtrace]
36
39
  log("Error publishing: #{info}", :error)
@@ -72,5 +75,16 @@ module PubSubModelSync
72
75
  def log(msg, kind = :info)
73
76
  config.log("Rabbit Service ==> #{msg}", kind)
74
77
  end
78
+
79
+ def deliver_data(data, attributes)
80
+ subscribe_to_queue
81
+ payload = { data: data, attributes: attributes }
82
+ topic.publish(payload.to_json, message_settings)
83
+
84
+ # Ugly fix: "IO timeout when reading 7 bytes"
85
+ # https://stackoverflow.com/questions/39039129/rabbitmq-timeouterror-io-timeout-when-reading-7-bytes
86
+ channel.close
87
+ service.close
88
+ end
75
89
  end
76
90
  end
@@ -7,11 +7,9 @@ module PubSubModelSync
7
7
  end
8
8
 
9
9
  module ClassMethods
10
- # @param settings (Hash): { as_klass: nil, actions: nil, id: nil }
11
- def ps_subscribe(attrs, settings = {})
12
- as_klass = (settings[:as_klass] || name).to_s
13
- actions = settings.delete(:actions) || %i[create update destroy]
14
- settings = settings.merge(attrs: attrs)
10
+ def ps_subscribe(attrs, as_klass: nil, actions: nil, id: :id)
11
+ actions ||= %i[create update destroy]
12
+ settings = { attrs: attrs, id: id }
15
13
  actions.each do |action|
16
14
  add_ps_subscriber(as_klass, action, action, false, settings)
17
15
  end
@@ -22,13 +20,14 @@ module PubSubModelSync
22
20
  end
23
21
 
24
22
  def ps_subscriber(action = :create)
25
- PubSubModelSync::Config.listeners.select do |listener|
23
+ PubSubModelSync::Config.listeners.find do |listener|
26
24
  listener[:klass] == name && listener[:action] == action
27
- end.last
25
+ end
28
26
  end
29
27
 
30
28
  private
31
29
 
30
+ # @param settings (Hash): { id:, attrs: }
32
31
  def add_ps_subscriber(as_klass, action, as_action, direct_mode, settings)
33
32
  listener = {
34
33
  klass: name,
@@ -38,7 +37,7 @@ module PubSubModelSync
38
37
  direct_mode: direct_mode,
39
38
  settings: settings
40
39
  }
41
- PubSubModelSync::Config.listeners << listener
40
+ PubSubModelSync::Config.listeners.push(listener) && listener
42
41
  end
43
42
  end
44
43
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PubSubModelSync
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pub_sub_model_sync
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Owen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-26 00:00:00.000000000 Z
11
+ date: 2020-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -101,10 +101,19 @@ executables: []
101
101
  extensions: []
102
102
  extra_rdoc_files: []
103
103
  files:
104
+ - ".github/workflows/ruby.yml"
104
105
  - ".gitignore"
106
+ - ".idea/.gitignore"
107
+ - ".idea/.rakeTasks"
108
+ - ".idea/codeStyles/codeStyleConfig.xml"
109
+ - ".idea/encodings.xml"
110
+ - ".idea/inspectionProfiles/Project_Default.xml"
111
+ - ".idea/misc.xml"
112
+ - ".idea/modules.xml"
113
+ - ".idea/pub_sub_model_sync.iml"
114
+ - ".idea/vcs.xml"
105
115
  - ".rspec"
106
116
  - ".rubocop.yml"
107
- - ".travis.yml"
108
117
  - CHANGELOG.md
109
118
  - CODE_OF_CONDUCT.md
110
119
  - Gemfile
@@ -155,8 +164,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
155
164
  - !ruby/object:Gem::Version
156
165
  version: '0'
157
166
  requirements: []
158
- rubyforge_project:
159
- rubygems_version: 2.7.7
167
+ rubygems_version: 3.0.8
160
168
  signing_key:
161
169
  specification_version: 4
162
170
  summary: Permit to sync models between apps through pub/sub
@@ -1,13 +0,0 @@
1
- ---
2
- sudo: false
3
- language: ruby
4
- cache: bundler
5
- rvm:
6
- - 2.3.1
7
- before_install: gem install bundler
8
-
9
- script:
10
- - bundle install
11
- # disabled cause of: An error occurred while Style/PercentLiteralDelimiters cop
12
- # - bundle exec rubocop
13
- - bundle exec rspec