pub_sub_model_sync 0.4.0 → 0.5.0.1
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 +4 -4
- data/.github/workflows/ruby.yml +30 -8
- data/.rubocop.yml +6 -1
- data/CHANGELOG.md +27 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +25 -23
- data/README.md +55 -29
- data/gemfiles/Gemfile_4 +16 -0
- data/gemfiles/Gemfile_5 +14 -0
- data/gemfiles/Gemfile_6 +14 -0
- data/lib/pub_sub_model_sync.rb +1 -0
- data/lib/pub_sub_model_sync/base.rb +17 -0
- data/lib/pub_sub_model_sync/config.rb +18 -3
- data/lib/pub_sub_model_sync/connector.rb +1 -0
- data/lib/pub_sub_model_sync/message_processor.rb +27 -21
- data/lib/pub_sub_model_sync/message_publisher.rb +25 -9
- data/lib/pub_sub_model_sync/mock_rabbit_service.rb +5 -0
- data/lib/pub_sub_model_sync/payload.rb +45 -0
- data/lib/pub_sub_model_sync/publisher.rb +1 -0
- data/lib/pub_sub_model_sync/publisher_concern.rb +3 -1
- data/lib/pub_sub_model_sync/service_base.rb +25 -13
- data/lib/pub_sub_model_sync/service_google.rb +4 -18
- data/lib/pub_sub_model_sync/service_kafka.rb +4 -17
- data/lib/pub_sub_model_sync/service_rabbit.rb +20 -27
- data/lib/pub_sub_model_sync/subscriber.rb +3 -3
- data/lib/pub_sub_model_sync/subscriber_concern.rb +6 -0
- data/lib/pub_sub_model_sync/version.rb +1 -1
- data/pub_sub_model_sync.gemspec +1 -1
- metadata +11 -29
- data/.idea/.gitignore +0 -8
- data/.idea/.rakeTasks +0 -7
- data/.idea/codeStyles/codeStyleConfig.xml +0 -5
- data/.idea/encodings.xml +0 -4
- data/.idea/inspectionProfiles/Project_Default.xml +0 -16
- data/.idea/misc.xml +0 -7
- data/.idea/modules.xml +0 -8
- data/.idea/pub_sub_model_sync.iml +0 -96
- data/.idea/vcs.xml +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f08249ef28730afdc2a0f3b025ef66d5117694fdc4ed995e9eb907e37e60defb
|
4
|
+
data.tar.gz: cc8d337de6952db6e9747912178ce7fd09f93c3a600d1d4756671fb6bcefadad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cdca1e7397fa5fae46198fce8ae24036318d619d53cff5c06e8e8c266423a5681e48588eddcced3271747242a35fc77b1c75bf567d5461c4fa7f573ae29c4c15
|
7
|
+
data.tar.gz: 0c48d83f3904c5872cdc3b48f2d341be84cbe84899a3fdc0738c9ad290e5e6c1c49ca2ab226e12df4f097d6f927e603b4691f64760a8d4dd9c703431711e71ec
|
data/.github/workflows/ruby.yml
CHANGED
@@ -6,28 +6,50 @@ on:
|
|
6
6
|
- master
|
7
7
|
pull_request:
|
8
8
|
|
9
|
-
|
10
9
|
jobs:
|
11
10
|
build:
|
12
11
|
name: Tests and Code Style
|
13
12
|
runs-on: ubuntu-latest
|
13
|
+
strategy:
|
14
|
+
matrix:
|
15
|
+
ruby: [2.4, 2.5, 2.6]
|
16
|
+
rails: [4, 5, 6]
|
17
|
+
include:
|
18
|
+
- ruby: 2.7
|
19
|
+
rails: 6
|
20
|
+
exclude: # rails 6 requires ruby >= 2.5
|
21
|
+
- ruby: 2.4
|
22
|
+
rails: 6
|
14
23
|
|
15
24
|
steps:
|
16
25
|
- uses: actions/checkout@v2
|
17
|
-
- name: Set up Ruby
|
26
|
+
- name: Set up Ruby
|
18
27
|
uses: actions/setup-ruby@v1
|
19
28
|
with:
|
20
|
-
ruby-version:
|
21
|
-
|
29
|
+
ruby-version: ${{ matrix.ruby }}
|
22
30
|
- name: Install sqlite3
|
23
31
|
run: sudo apt-get install libsqlite3-dev
|
24
32
|
|
25
|
-
- name:
|
33
|
+
- name: Install bundler
|
34
|
+
env:
|
35
|
+
GEMFILE_PATH: gemfiles/Gemfile_${{ matrix.rails }}
|
36
|
+
RAILS_V: ${{ matrix.rails }}
|
26
37
|
run: |
|
27
|
-
|
28
|
-
|
38
|
+
rm -f Gemfile.lock && rm -f Gemfile
|
39
|
+
cp $GEMFILE_PATH ./Gemfile
|
40
|
+
bundler_v='2.1.4'
|
41
|
+
if [ $RAILS_V = "4" ]; then bundler_v="1.16.6"; fi
|
42
|
+
gem install bundler -v "~> $bundler_v"
|
43
|
+
bundle _${bundler_v}_ install --jobs 4 --retry 3
|
44
|
+
|
45
|
+
# remote ssh debugger
|
46
|
+
# - name: Setup tmate session (remote session debugger)
|
47
|
+
# uses: mxschmitt/action-tmate@v3
|
48
|
+
|
29
49
|
- name: Tests (rspec)
|
30
50
|
run: |
|
31
51
|
bundle exec rspec
|
52
|
+
|
32
53
|
- name: Code style (Rubocop)
|
33
|
-
run: bundle exec rubocop
|
54
|
+
run: bundle exec rubocop
|
55
|
+
if: matrix.ruby == '2.6' && matrix.rails == '6'
|
data/.rubocop.yml
CHANGED
@@ -1,22 +1,27 @@
|
|
1
1
|
# This is the configuration used to check the rubocop source code.
|
2
2
|
|
3
3
|
AllCops:
|
4
|
+
TargetRubyVersion: 2.6
|
4
5
|
Exclude:
|
5
6
|
- 'spec/spec_helper.rb'
|
6
7
|
- 'Gemfile'
|
7
8
|
- 'Rakefile'
|
8
9
|
- 'bin/*'
|
9
|
-
TargetRubyVersion: 2.3
|
10
10
|
|
11
11
|
Metrics/BlockLength:
|
12
12
|
Exclude:
|
13
13
|
- 'spec/**/*.rb'
|
14
14
|
|
15
|
+
Layout/LineLength:
|
16
|
+
Max: 120
|
15
17
|
|
16
18
|
Style/SymbolArray:
|
17
19
|
Exclude:
|
18
20
|
- 'Gemfile'
|
19
21
|
|
22
|
+
Lint/MissingSuper:
|
23
|
+
Enabled: false
|
24
|
+
|
20
25
|
Style/Documentation:
|
21
26
|
Enabled: false
|
22
27
|
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,32 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
# 0.5.0.1 (December 22, 2020)
|
4
|
+
- fix: add missing rabbit mock method
|
5
|
+
|
6
|
+
# 0.5.0 (December 22, 2020)
|
7
|
+
- feat: add :publish! and :process! methods to payloads
|
8
|
+
- feat: add ability to disable publisher globally
|
9
|
+
- fix: skip notifications from the same application
|
10
|
+
- fix: rabbitmq use fanout instead of queue to deliver messages to multiple apps
|
11
|
+
- refactor: include payload object to carry message info
|
12
|
+
- feat: include notification events (when publishing and when processing messages)
|
13
|
+
|
14
|
+
# 0.4.2.2 (November 29, 2020, deleted cause of typo)
|
15
|
+
- feat: rabbitMQ skip receiving messages from the same app
|
16
|
+
- feat: rabbitmq use fanout instead of queue to deliver messages to multiple apps
|
17
|
+
|
18
|
+
# 0.4.2.1 (August 20, 2020)
|
19
|
+
- Improve ```ps_subscriber_changed?``` to run validations and check for changes
|
20
|
+
|
21
|
+
# 0.4.2 (May 12, 2020)
|
22
|
+
- chore: remove typo
|
23
|
+
|
24
|
+
# 0.4.1 (May 12, 2020)
|
25
|
+
- chore: improve log messages
|
26
|
+
- feat: do not update model if no changes
|
27
|
+
- feat: skip publisher after updating if no changes
|
28
|
+
|
29
|
+
|
3
30
|
# 0.4.0 (May 06, 2020)
|
4
31
|
- rename as_klass to from_klass and as_action to from_action for subscribers
|
5
32
|
- refactor subscribers to be independent
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pub_sub_model_sync (0.
|
5
|
-
activesupport
|
4
|
+
pub_sub_model_sync (0.5.0.1)
|
6
5
|
rails
|
7
6
|
|
8
7
|
GEM
|
@@ -66,7 +65,7 @@ GEM
|
|
66
65
|
addressable (2.7.0)
|
67
66
|
public_suffix (>= 2.0.2, < 5.0)
|
68
67
|
amq-protocol (2.3.0)
|
69
|
-
ast (2.4.
|
68
|
+
ast (2.4.1)
|
70
69
|
builder (3.2.4)
|
71
70
|
bunny (2.14.3)
|
72
71
|
amq-protocol (~> 2.3, >= 2.3.0)
|
@@ -78,7 +77,7 @@ GEM
|
|
78
77
|
database_cleaner (~> 1.8.0)
|
79
78
|
diff-lcs (1.3)
|
80
79
|
digest-crc (0.5.1)
|
81
|
-
erubi (1.
|
80
|
+
erubi (1.10.0)
|
82
81
|
faraday (0.17.3)
|
83
82
|
multipart-post (>= 1.2, < 3)
|
84
83
|
globalid (0.4.2)
|
@@ -121,9 +120,8 @@ GEM
|
|
121
120
|
grpc (~> 1.0)
|
122
121
|
i18n (1.8.2)
|
123
122
|
concurrent-ruby (~> 1.0)
|
124
|
-
jaro_winkler (1.5.4)
|
125
123
|
jwt (2.2.1)
|
126
|
-
loofah (2.
|
124
|
+
loofah (2.8.0)
|
127
125
|
crass (~> 1.0.2)
|
128
126
|
nokogiri (>= 1.5.9)
|
129
127
|
mail (2.7.1)
|
@@ -132,21 +130,21 @@ GEM
|
|
132
130
|
mimemagic (~> 0.3.2)
|
133
131
|
memoist (0.16.2)
|
134
132
|
method_source (1.0.0)
|
135
|
-
mimemagic (0.3.
|
133
|
+
mimemagic (0.3.5)
|
136
134
|
mini_mime (1.0.2)
|
137
135
|
mini_portile2 (2.4.0)
|
138
136
|
minitest (5.14.0)
|
139
137
|
multi_json (1.14.1)
|
140
138
|
multipart-post (2.1.1)
|
141
|
-
nio4r (2.5.
|
142
|
-
nokogiri (1.10.
|
139
|
+
nio4r (2.5.4)
|
140
|
+
nokogiri (1.10.10)
|
143
141
|
mini_portile2 (~> 2.4.0)
|
144
142
|
os (1.0.1)
|
145
|
-
parallel (1.
|
146
|
-
parser (2.7.0
|
147
|
-
ast (~> 2.4.
|
143
|
+
parallel (1.20.1)
|
144
|
+
parser (2.7.2.0)
|
145
|
+
ast (~> 2.4.1)
|
148
146
|
public_suffix (4.0.3)
|
149
|
-
rack (2.2.
|
147
|
+
rack (2.2.3)
|
150
148
|
rack-test (1.1.0)
|
151
149
|
rack (>= 1.0, < 3)
|
152
150
|
rails (6.0.2.2)
|
@@ -177,6 +175,7 @@ GEM
|
|
177
175
|
thor (>= 0.20.3, < 2.0)
|
178
176
|
rainbow (3.0.0)
|
179
177
|
rake (13.0.1)
|
178
|
+
regexp_parser (2.0.1)
|
180
179
|
rexml (3.2.4)
|
181
180
|
rly (0.2.3)
|
182
181
|
rspec (3.9.0)
|
@@ -192,14 +191,17 @@ GEM
|
|
192
191
|
diff-lcs (>= 1.2.0, < 2.0)
|
193
192
|
rspec-support (~> 3.9.0)
|
194
193
|
rspec-support (3.9.2)
|
195
|
-
rubocop (
|
196
|
-
jaro_winkler (~> 1.5.1)
|
194
|
+
rubocop (1.6.1)
|
197
195
|
parallel (~> 1.10)
|
198
|
-
parser (>= 2.7.
|
196
|
+
parser (>= 2.7.1.5)
|
199
197
|
rainbow (>= 2.2.2, < 4.0)
|
198
|
+
regexp_parser (>= 1.8, < 3.0)
|
200
199
|
rexml
|
200
|
+
rubocop-ast (>= 1.2.0, < 2.0)
|
201
201
|
ruby-progressbar (~> 1.7)
|
202
|
-
unicode-display_width (>= 1.4.0, <
|
202
|
+
unicode-display_width (>= 1.4.0, < 2.0)
|
203
|
+
rubocop-ast (1.3.0)
|
204
|
+
parser (>= 2.7.1.5)
|
203
205
|
ruby-kafka (1.0.0)
|
204
206
|
digest-crc
|
205
207
|
ruby-progressbar (1.10.1)
|
@@ -208,10 +210,10 @@ GEM
|
|
208
210
|
faraday (~> 0.9)
|
209
211
|
jwt (>= 1.5, < 3.0)
|
210
212
|
multi_json (~> 1.10)
|
211
|
-
sprockets (4.0.
|
213
|
+
sprockets (4.0.2)
|
212
214
|
concurrent-ruby (~> 1.0)
|
213
215
|
rack (> 1, < 3)
|
214
|
-
sprockets-rails (3.2.
|
216
|
+
sprockets-rails (3.2.2)
|
215
217
|
actionpack (>= 4.0)
|
216
218
|
activesupport (>= 4.0)
|
217
219
|
sprockets (>= 3.0.0)
|
@@ -220,10 +222,10 @@ GEM
|
|
220
222
|
thread_safe (0.3.6)
|
221
223
|
tzinfo (1.2.7)
|
222
224
|
thread_safe (~> 0.1)
|
223
|
-
unicode-display_width (1.
|
224
|
-
websocket-driver (0.7.
|
225
|
+
unicode-display_width (1.7.0)
|
226
|
+
websocket-driver (0.7.3)
|
225
227
|
websocket-extensions (>= 0.1.0)
|
226
|
-
websocket-extensions (0.1.
|
228
|
+
websocket-extensions (0.1.5)
|
227
229
|
zeitwerk (2.3.0)
|
228
230
|
|
229
231
|
PLATFORMS
|
@@ -237,7 +239,7 @@ DEPENDENCIES
|
|
237
239
|
pub_sub_model_sync!
|
238
240
|
rake
|
239
241
|
rspec
|
240
|
-
rubocop
|
242
|
+
rubocop (~> 1.6.0)
|
241
243
|
ruby-kafka
|
242
244
|
sqlite3
|
243
245
|
|
data/README.md
CHANGED
@@ -61,6 +61,9 @@ And then execute: $ bundle install
|
|
61
61
|
```
|
62
62
|
Note: Publishers do not need todo this
|
63
63
|
|
64
|
+
- Check the service status with:
|
65
|
+
```PubSubModelSync::MessagePublisher.publish_data('Test message', {sample_value: 10}, :create)```
|
66
|
+
|
64
67
|
## Examples
|
65
68
|
```ruby
|
66
69
|
# App 1 (Publisher)
|
@@ -118,7 +121,7 @@ class User < ActiveRecord::Base
|
|
118
121
|
puts 'Class message called through custom_greeting'
|
119
122
|
end
|
120
123
|
|
121
|
-
# def self.ps_find_model(data
|
124
|
+
# def self.ps_find_model(data)
|
122
125
|
# where(email: data[:email], ...).first_or_initialize
|
123
126
|
# end
|
124
127
|
end
|
@@ -126,24 +129,18 @@ end
|
|
126
129
|
|
127
130
|
Note: Be careful with collision of names
|
128
131
|
```
|
129
|
-
|
130
|
-
|
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
|
132
|
+
# ps_publish %i[name_data:name name:key] # key will be replaced with name_data
|
133
|
+
ps_publish %i[name_data:name key_data:key] # use alias to avoid collision
|
137
134
|
```
|
138
135
|
|
139
136
|
## API
|
140
137
|
### Subscribers
|
141
|
-
- Permit to configure class level
|
138
|
+
- Permit to configure class level subscriptions
|
142
139
|
```ps_class_subscribe(action_name, from_action: nil, from_klass: nil)```
|
143
140
|
* from_action: (Optional) Source method name
|
144
141
|
* from_klass: (Optional) Source class name
|
145
142
|
|
146
|
-
- Permit to configure instance level
|
143
|
+
- Permit to configure instance level subscriptions (CRUD)
|
147
144
|
```ps_subscribe(attrs, from_klass: nil, actions: nil, id: nil)```
|
148
145
|
* attrs: (Array/Required) Array of all attributes to be synced
|
149
146
|
* from_klass: (String/Optional) Source class name (Instead of the model class name, will use this value)
|
@@ -151,17 +148,20 @@ end
|
|
151
148
|
* id: (Sym|Array/Optional, default: id) Attr identifier(s) to find the corresponding model
|
152
149
|
|
153
150
|
- Permit to configure a custom model finder
|
154
|
-
```ps_find_model(data
|
151
|
+
```ps_find_model(data)```
|
155
152
|
* data: (Hash) Data received from sync
|
156
|
-
* settings: (Hash(:klass, :action)) Class and action name from sync
|
157
153
|
Must return an existent or a new model object
|
158
154
|
|
159
155
|
- Get crud subscription configured for the class
|
160
156
|
```User.ps_subscriber(action_name)```
|
161
157
|
* action_name (default :create, :sym): can be :create, :update, :destroy
|
162
158
|
|
163
|
-
- Inspect all configured
|
164
|
-
```PubSubModelSync::Config.
|
159
|
+
- Inspect all configured subscribers
|
160
|
+
```PubSubModelSync::Config.subscribers```
|
161
|
+
|
162
|
+
- Permit to customize the way to detect if the subscribed model was changed (Only for update action).
|
163
|
+
```.ps_subscriber_changed?(data)```
|
164
|
+
By default: ```model.changed?```
|
165
165
|
|
166
166
|
### Publishers
|
167
167
|
- Permit to configure crud publishers
|
@@ -172,6 +172,7 @@ end
|
|
172
172
|
|
173
173
|
- Permit to cancel sync called after create/update/destroy (Before initializing sync service)
|
174
174
|
```model.ps_skip_callback?(action)```
|
175
|
+
Default: False
|
175
176
|
Note: Return true to cancel sync
|
176
177
|
|
177
178
|
- Callback called before preparing data for sync (Permit to stop sync)
|
@@ -197,11 +198,11 @@ end
|
|
197
198
|
* action_name: (required, :sim) Action name
|
198
199
|
* as_klass: (optional, :string) Custom class name (Default current model name)
|
199
200
|
|
200
|
-
- Publish a class level notification (Same as above:
|
201
|
-
```
|
202
|
-
|
203
|
-
|
204
|
-
|
201
|
+
- Publish a class level notification (Same as above: manual call)
|
202
|
+
```ruby
|
203
|
+
payload = PubSubModelSync::Payload.new({ title: 'hello' }, { action: :greeting, klass: 'User' })
|
204
|
+
payload.publish!
|
205
|
+
```
|
205
206
|
|
206
207
|
- Get crud publisher configured for the class
|
207
208
|
```User.ps_publisher(action_name)```
|
@@ -237,27 +238,23 @@ end
|
|
237
238
|
```ruby
|
238
239
|
# Subscriber
|
239
240
|
it 'receive model message' do
|
240
|
-
action = :create
|
241
241
|
data = { name: 'name', id: 999 }
|
242
|
-
|
243
|
-
|
242
|
+
payload = PubSubModelSync::Payload.new(data, { klass: 'User', action: :create })
|
243
|
+
payload.process!
|
244
244
|
expect(User.where(id: data[:id]).any?).to be_truth
|
245
245
|
end
|
246
246
|
|
247
247
|
it 'receive class message' do
|
248
|
-
action = :greeting
|
249
248
|
data = { msg: 'hello' }
|
250
|
-
|
251
|
-
|
249
|
+
action = :greeting
|
250
|
+
payload = PubSubModelSync::Payload.new(data, { klass: 'User', action: action })
|
251
|
+
payload.process!
|
252
252
|
expect(User).to receive(action)
|
253
253
|
end
|
254
254
|
|
255
255
|
# Publisher
|
256
256
|
it 'publish model action' do
|
257
257
|
publisher = PubSubModelSync::MessagePublisher
|
258
|
-
data = { name: 'hello'}
|
259
|
-
action = :create
|
260
|
-
User.ps_class_publish(data, action: action)
|
261
258
|
user = User.create(name: 'name', email: 'email')
|
262
259
|
expect(publisher).to receive(:publish_model).with(user, :create, anything)
|
263
260
|
end
|
@@ -271,6 +268,35 @@ end
|
|
271
268
|
end
|
272
269
|
```
|
273
270
|
|
271
|
+
## Extra configurations
|
272
|
+
```ruby
|
273
|
+
config = PubSubModelSync::Config
|
274
|
+
config.debug = true
|
275
|
+
```
|
276
|
+
|
277
|
+
- ```debug = true```
|
278
|
+
(true/false*) => show advanced log messages
|
279
|
+
- ```logger = Rails.logger```
|
280
|
+
(Logger) => define custom logger
|
281
|
+
- ```disabled = true```
|
282
|
+
(true/false*) => if true, does not publish model messages (Create/Update/Destroy)
|
283
|
+
- ```on_process_success = ->(payload, subscriber) { puts payload }```
|
284
|
+
(Proc) => called when a message was successfully processed
|
285
|
+
- ```on_process_error = ->(exception, payload) { sleep 1; payload.process! }```
|
286
|
+
(Proc) => called when a message failed when processing
|
287
|
+
- ```on_before_publish = ->(payload) { puts payload }```
|
288
|
+
(Proc) => called before publishing a message
|
289
|
+
- ```on_after_publish = ->(payload) { puts payload }```
|
290
|
+
(Proc) => called after publishing a message
|
291
|
+
- ```on_publish_error = ->(exception, payload) { sleep 1; payload.publish! }```
|
292
|
+
(Proc) => called when failed publishing a message
|
293
|
+
|
294
|
+
## TODO
|
295
|
+
- Add alias attributes when subscribing (similar to publisher)
|
296
|
+
- Add flag ```model.ps_processing``` to indicate that the current transaction is being processed by pub/sub
|
297
|
+
- Auto publish update only if payload has changed
|
298
|
+
- On delete, payload must only be composed by ids
|
299
|
+
|
274
300
|
## Contributing
|
275
301
|
|
276
302
|
Bug reports and pull requests are welcome on GitHub at https://github.com/owen2345/pub_sub_model_sync. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
data/gemfiles/Gemfile_4
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
gem 'rubocop'
|
4
|
+
gem 'bunny' # rabbit-mq
|
5
|
+
gem 'google-cloud-pubsub' # google pub/sub
|
6
|
+
gem 'ruby-kafka' # kafka pub/sub
|
7
|
+
gem 'rails', '~> 4'
|
8
|
+
gem 'bundler'
|
9
|
+
gem 'sqlite3', '1.3.13'
|
10
|
+
|
11
|
+
group :test do
|
12
|
+
gem 'database_cleaner-active_record'
|
13
|
+
end
|
14
|
+
|
15
|
+
# Specify your gem's dependencies in pub_sub_model_sync.gemspec
|
16
|
+
gemspec
|