consyncful 0.3.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -9
- data/.rubocop_todo.yml +48 -0
- data/.ruby-version +1 -1
- data/.travis.yml +8 -2
- data/Gemfile.lock +73 -45
- data/README.md +43 -30
- data/consyncful.gemspec +8 -5
- data/lib/consyncful.rb +1 -1
- data/lib/consyncful/base.rb +3 -0
- data/lib/consyncful/item_mapper.rb +31 -12
- data/lib/consyncful/persisted_item.rb +76 -0
- data/lib/consyncful/stats.rb +6 -2
- data/lib/consyncful/sync.rb +43 -69
- data/lib/consyncful/tasks/consyncful.rake +10 -3
- data/lib/consyncful/version.rb +1 -1
- metadata +54 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 14eda0769dbcdc516d59a9a4252fafb23eb6380ce0224a276e101f81d649c950
|
4
|
+
data.tar.gz: aa4aaf421824043dbea600c1cfabca2633a8d3470ed2a14aba5685b8bf8acd90
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ac1a66dda08d88396d83c736308516a798503afa519cd673e76f668d771dc933b6068a2e03c1f75926ec8980ad273c0936af6e2058d84d5919a250a498ebbd7
|
7
|
+
data.tar.gz: 260c353fe5eb6c646473830ecdec0a094016bf0f0e518ef357ceb7cc03862f38e76e4cccbcf656a4c368fad72641ef60788d13a5c76e768a5b8fc9652b375c96
|
data/.rubocop.yml
CHANGED
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2021-03-01 10:54:42 +1300 using RuboCop version 0.79.0.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 1
|
10
|
+
# Cop supports --auto-correct.
|
11
|
+
# Configuration parameters: AllowInHeredoc.
|
12
|
+
Layout/TrailingWhitespace:
|
13
|
+
Exclude:
|
14
|
+
- 'lib/consyncful/item_mapper.rb'
|
15
|
+
|
16
|
+
# Offense count: 1
|
17
|
+
Metrics/AbcSize:
|
18
|
+
Max: 19
|
19
|
+
|
20
|
+
# Offense count: 10
|
21
|
+
# Configuration parameters: CountComments, ExcludedMethods.
|
22
|
+
# ExcludedMethods: refine
|
23
|
+
Metrics/BlockLength:
|
24
|
+
Max: 175
|
25
|
+
|
26
|
+
# Offense count: 3
|
27
|
+
# Cop supports --auto-correct.
|
28
|
+
# Configuration parameters: AutoCorrect, EnforcedStyle.
|
29
|
+
# SupportedStyles: nested, compact
|
30
|
+
Style/ClassAndModuleChildren:
|
31
|
+
Exclude:
|
32
|
+
- 'lib/consyncful/railtie.rb'
|
33
|
+
- 'spec/consyncful/base_spec.rb'
|
34
|
+
|
35
|
+
# Offense count: 3
|
36
|
+
Style/Documentation:
|
37
|
+
Exclude:
|
38
|
+
- 'spec/**/*'
|
39
|
+
- 'test/**/*'
|
40
|
+
- 'lib/consyncful.rb'
|
41
|
+
- 'lib/consyncful/railtie.rb'
|
42
|
+
|
43
|
+
# Offense count: 56
|
44
|
+
# Cop supports --auto-correct.
|
45
|
+
# Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
46
|
+
# URISchemes: http, https
|
47
|
+
Layout/LineLength:
|
48
|
+
Max: 226
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.7.1
|
data/.travis.yml
CHANGED
@@ -1,8 +1,14 @@
|
|
1
|
+
---
|
1
2
|
sudo: false
|
2
3
|
language: ruby
|
4
|
+
cache: bundler
|
3
5
|
rvm:
|
4
6
|
- 2.5.1
|
5
|
-
|
6
|
-
|
7
|
+
- 2.6.5
|
8
|
+
before_install: gem install bundler -v 2.1.0
|
7
9
|
services:
|
8
10
|
- mongodb
|
11
|
+
|
12
|
+
script:
|
13
|
+
- bundle exec rake spec
|
14
|
+
- bundle exec rubocop -P
|
data/Gemfile.lock
CHANGED
@@ -1,80 +1,108 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
consyncful (0.
|
4
|
+
consyncful (0.5.0)
|
5
5
|
contentful (>= 2.11.1, < 3.0.0)
|
6
|
+
hooks (>= 0.4.1)
|
6
7
|
mongoid (>= 7.0.2, < 8.0.0)
|
7
|
-
|
8
|
+
rainbow
|
8
9
|
|
9
10
|
GEM
|
10
11
|
remote: https://rubygems.org/
|
11
12
|
specs:
|
12
|
-
activemodel (
|
13
|
-
activesupport (=
|
14
|
-
activesupport (
|
13
|
+
activemodel (6.0.3.4)
|
14
|
+
activesupport (= 6.0.3.4)
|
15
|
+
activesupport (6.0.3.4)
|
15
16
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
16
17
|
i18n (>= 0.7, < 2)
|
17
18
|
minitest (~> 5.1)
|
18
19
|
tzinfo (~> 1.1)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
zeitwerk (~> 2.2, >= 2.2.2)
|
21
|
+
addressable (2.7.0)
|
22
|
+
public_suffix (>= 2.0.2, < 5.0)
|
23
|
+
ast (2.4.0)
|
24
|
+
bson (4.11.1)
|
25
|
+
concurrent-ruby (1.1.8)
|
26
|
+
contentful (2.15.4)
|
27
|
+
http (> 0.8, < 5.0)
|
25
28
|
multi_json (~> 1)
|
29
|
+
database_cleaner (1.8.3)
|
26
30
|
diff-lcs (1.3)
|
27
|
-
domain_name (0.5.
|
31
|
+
domain_name (0.5.20190701)
|
28
32
|
unf (>= 0.0.5, < 1.0.0)
|
29
|
-
|
33
|
+
ffi (1.14.2)
|
34
|
+
ffi-compiler (1.0.1)
|
35
|
+
ffi (>= 1.0.0)
|
36
|
+
rake
|
37
|
+
hooks (0.4.1)
|
38
|
+
uber (~> 0.0.14)
|
39
|
+
http (4.4.1)
|
30
40
|
addressable (~> 2.3)
|
31
41
|
http-cookie (~> 1.0)
|
32
|
-
http-form_data (~> 2.
|
33
|
-
|
42
|
+
http-form_data (~> 2.2)
|
43
|
+
http-parser (~> 1.2.0)
|
34
44
|
http-cookie (1.0.3)
|
35
45
|
domain_name (~> 0.5)
|
36
|
-
http-form_data (2.
|
37
|
-
|
38
|
-
|
46
|
+
http-form_data (2.3.0)
|
47
|
+
http-parser (1.2.3)
|
48
|
+
ffi-compiler (>= 1.0, < 2.0)
|
49
|
+
i18n (1.8.7)
|
39
50
|
concurrent-ruby (~> 1.0)
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
51
|
+
jaro_winkler (1.5.4)
|
52
|
+
minitest (5.14.3)
|
53
|
+
mongo (2.14.0)
|
54
|
+
bson (>= 4.8.2, < 5.0.0)
|
55
|
+
mongoid (7.2.0)
|
56
|
+
activemodel (>= 5.1, < 6.1)
|
57
|
+
mongo (>= 2.10.5, < 3.0.0)
|
58
|
+
multi_json (1.15.0)
|
59
|
+
parallel (1.19.1)
|
60
|
+
parser (2.7.0.2)
|
61
|
+
ast (~> 2.4.0)
|
62
|
+
public_suffix (4.0.6)
|
63
|
+
rainbow (3.0.0)
|
64
|
+
rake (13.0.1)
|
65
|
+
rspec (3.9.0)
|
66
|
+
rspec-core (~> 3.9.0)
|
67
|
+
rspec-expectations (~> 3.9.0)
|
68
|
+
rspec-mocks (~> 3.9.0)
|
69
|
+
rspec-core (3.9.1)
|
70
|
+
rspec-support (~> 3.9.1)
|
71
|
+
rspec-expectations (3.9.0)
|
56
72
|
diff-lcs (>= 1.2.0, < 2.0)
|
57
|
-
rspec-support (~> 3.
|
58
|
-
rspec-mocks (3.
|
73
|
+
rspec-support (~> 3.9.0)
|
74
|
+
rspec-mocks (3.9.1)
|
59
75
|
diff-lcs (>= 1.2.0, < 2.0)
|
60
|
-
rspec-support (~> 3.
|
61
|
-
rspec-support (3.
|
62
|
-
|
63
|
-
|
76
|
+
rspec-support (~> 3.9.0)
|
77
|
+
rspec-support (3.9.2)
|
78
|
+
rubocop (0.79.0)
|
79
|
+
jaro_winkler (~> 1.5.1)
|
80
|
+
parallel (~> 1.10)
|
81
|
+
parser (>= 2.7.0.1)
|
82
|
+
rainbow (>= 2.2.2, < 4.0)
|
83
|
+
ruby-progressbar (~> 1.7)
|
84
|
+
unicode-display_width (>= 1.4.0, < 1.7)
|
85
|
+
ruby-progressbar (1.10.1)
|
64
86
|
thread_safe (0.3.6)
|
65
|
-
|
66
|
-
tzinfo (1.2.5)
|
87
|
+
tzinfo (1.2.9)
|
67
88
|
thread_safe (~> 0.1)
|
89
|
+
uber (0.0.15)
|
68
90
|
unf (0.1.4)
|
69
91
|
unf_ext
|
70
|
-
unf_ext (0.0.7.
|
92
|
+
unf_ext (0.0.7.7)
|
93
|
+
unicode-display_width (1.6.1)
|
94
|
+
zeitwerk (2.4.2)
|
71
95
|
|
72
96
|
PLATFORMS
|
73
97
|
ruby
|
74
98
|
|
75
99
|
DEPENDENCIES
|
76
|
-
bundler (~>
|
100
|
+
bundler (~> 2)
|
77
101
|
consyncful!
|
78
|
-
|
102
|
+
database_cleaner
|
103
|
+
rake (~> 13.0)
|
79
104
|
rspec (~> 3.0)
|
105
|
+
rubocop (= 0.79.0)
|
80
106
|
|
107
|
+
BUNDLED WITH
|
108
|
+
2.1.0
|
data/README.md
CHANGED
@@ -1,20 +1,16 @@
|
|
1
|
-
# Consyncful
|
2
|
-
[![Build Status](https://travis-ci.com/boost/consyncful.svg?branch=master)](https://travis-ci.com/boost/consyncful)
|
1
|
+
# Consyncful [![Build Status](https://travis-ci.org/boost/consyncful.svg?branch=master)](https://travis-ci.org/boost/consyncful)
|
3
2
|
|
4
3
|
Contentful -> local database synchronisation for Rails
|
5
4
|
|
6
|
-
Requesting complicated models from the Contentful Delivery API in Rails applications is often
|
7
|
-
too slow, and makes testing applications painful. Consyncful uses Contentful's syncronisation API
|
8
|
-
to keep a local copy of the entire content in a Mongo database up to date.
|
5
|
+
Requesting complicated models from the Contentful Delivery API in Rails applications is often too slow, and makes testing applications painful. Consyncful uses Contentful's synchronisation API to keep a local, up-to-date copy of the entire content in a Mongo database.
|
9
6
|
|
10
|
-
Once the content is
|
11
|
-
using [Mongoid](https://docs.mongodb.com/mongoid/current/tutorials/mongoid-documents/) ODM.
|
7
|
+
Once the content is available locally, finding and interact with contentful data is as easy as using [Mongoid](https://docs.mongodb.com/mongoid/current/tutorials/mongoid-documents/) ODM.
|
12
8
|
|
13
|
-
This gem doesn't provide any
|
9
|
+
This gem doesn't provide any integration with the management API, or any way to update Contentful models from the local store. It is strictly read only.
|
14
10
|
|
15
11
|
## Why do I have to use MongoDB?
|
16
12
|
|
17
|
-
Consyncful currently only supports Mongoid ODM because models have dynamic schemas. And that's all we've had a chance to work out so far. :)
|
13
|
+
Consyncful currently only supports Mongoid ODM because models have dynamic schemas. And that's all we've had a chance to work out so far. :)
|
18
14
|
The same pattern might be able to be extended to work with ActiveRecord, but having to migrate the local database as well as your contentful content type's seems tedious.
|
19
15
|
|
20
16
|
## Installation
|
@@ -29,12 +25,13 @@ And then execute:
|
|
29
25
|
|
30
26
|
$ bundle
|
31
27
|
|
32
|
-
If you don't already use
|
28
|
+
If you don't already use Mongoid, generate a mongoid.yml by running:
|
33
29
|
|
34
30
|
$ rake g mongoid:config
|
35
31
|
|
36
32
|
Add an initializer:
|
37
|
-
|
33
|
+
|
34
|
+
Consyncful uses [contentful.rb](https://github.com/contentful/contentful.rb); client options are as documented there.
|
38
35
|
```ruby
|
39
36
|
Consyncful.configure do |config|
|
40
37
|
config.locale = 'en-NZ'
|
@@ -50,7 +47,7 @@ Consyncful uses [contentful.rb](https://github.com/contentful/contentful.rb) so
|
|
50
47
|
|
51
48
|
## Usage
|
52
49
|
|
53
|
-
### Creating contentful models in your
|
50
|
+
### Creating contentful models in your Rails app
|
54
51
|
|
55
52
|
Create models by inheriting from `Consyncful::Base`
|
56
53
|
|
@@ -60,9 +57,9 @@ class ModelName < Consyncful::Base
|
|
60
57
|
end
|
61
58
|
```
|
62
59
|
|
63
|
-
Model fields will be
|
60
|
+
Model fields will be dynamically assigned, but Mongoid dynamic fields are not accessible if the entry has an empty field. If you want the accessor methods to be reliably available for fields it is recommended to define the fields in the model:
|
64
61
|
|
65
|
-
```ruby
|
62
|
+
```ruby
|
66
63
|
class ModelName < Consyncful::Base
|
67
64
|
contentful_model_name 'contentfulTypeName'
|
68
65
|
|
@@ -71,9 +68,9 @@ class ModelName < Consyncful::Base
|
|
71
68
|
end
|
72
69
|
```
|
73
70
|
|
74
|
-
Contentful reference fields are a bit special compared with standard
|
71
|
+
Contentful reference fields are a bit special compared with standard Mongoid associations. Consyncful provides the following helpers to set up the correct relationships:
|
75
72
|
|
76
|
-
```ruby
|
73
|
+
```ruby
|
77
74
|
class ModelWithReferences < Consyncful::Base
|
78
75
|
contentful_model_name 'contentfulTypeName'
|
79
76
|
|
@@ -82,26 +79,26 @@ class ModelWithReferences < Consyncful::Base
|
|
82
79
|
end
|
83
80
|
```
|
84
81
|
|
85
|
-
###
|
82
|
+
### Synchronizing contentful data
|
86
83
|
|
87
|
-
To run a
|
84
|
+
To run a synchronization process run:
|
88
85
|
|
89
86
|
$ rake consyncful:sync
|
90
87
|
|
91
|
-
The first time you run this it will download all the
|
88
|
+
The first time you run this it will download all the Contentful content. It will then check every 15 seconds for changes to the content and update/delete records in the database when changes are made in Contentful.
|
92
89
|
|
93
|
-
If you want to
|
90
|
+
If you want to synchronise from scratch, run:
|
94
91
|
|
95
92
|
$ rake consyncful:refresh
|
96
93
|
|
97
94
|
It is recommended to refresh your data if you change model names.
|
98
95
|
|
99
|
-
Now you've synced your data, it is all available via your
|
96
|
+
Now you've synced your data, it is all available via your Rails models.
|
100
97
|
|
101
98
|
### Finding and interacting with models
|
102
99
|
|
103
100
|
#### Querying
|
104
|
-
Models are available using standard
|
101
|
+
Models are available using standard Mongoid [queries](https://docs.mongodb.com/mongoid/current/tutorials/mongoid-queries/).
|
105
102
|
|
106
103
|
```ruby
|
107
104
|
instance = ModelName.find_by(instance: 'foo')
|
@@ -110,7 +107,7 @@ instance.is_awesome # true
|
|
110
107
|
```
|
111
108
|
|
112
109
|
#### References
|
113
|
-
References work like you
|
110
|
+
References work like you would expect:
|
114
111
|
|
115
112
|
```ruby
|
116
113
|
|
@@ -121,25 +118,41 @@ instance.other_things # all the referenced things, polymorphic, so might be diff
|
|
121
118
|
```
|
122
119
|
|
123
120
|
**Except**:
|
124
|
-
`references_many` associations return objects in a different order from how they are ordered in
|
121
|
+
`references_many` associations return objects in a different order from how they are ordered in Contentful. If you want them in the order they appear in Contentful, use the `.in_order` helper:
|
125
122
|
|
126
123
|
```ruby
|
127
|
-
instance.other_things.in_order # ordered the same as in
|
124
|
+
instance.other_things.in_order # ordered the same as in Contentful
|
128
125
|
```
|
129
126
|
|
130
|
-
#### Finding
|
127
|
+
#### Finding entries from different content types
|
131
128
|
|
132
|
-
Because all
|
129
|
+
Because all Contentful models are stored as polymorphic subtypes of `Consyncful::Base`, you can query all entries without knowing what type you are looking for:
|
133
130
|
|
134
131
|
```ruby
|
135
132
|
Consyncful::Base.where(title: 'a title') # [ #<ModelName>, #<OtherModelName> ]
|
136
133
|
```
|
137
134
|
|
138
|
-
|
135
|
+
### Sync callbacks
|
136
|
+
|
137
|
+
You may want to attach some application logic to happen before or after a sync run, for example to update caches.
|
138
|
+
|
139
|
+
Callbacks can be registered using:
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
Consyncful::Sync.before_run do
|
143
|
+
# do something before the run
|
144
|
+
end
|
145
|
+
```
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
Consyncful::Sync.after_run do |updated_ids|
|
149
|
+
# invalidate cache for updated_ids, or something
|
150
|
+
end
|
151
|
+
```
|
139
152
|
|
140
|
-
### Locales
|
153
|
+
### Using Locales for specific fields
|
141
154
|
|
142
|
-
|
155
|
+
If fields have multiple locales then the default locale will be mapped to the field name. Additional locales will have a suffix (lower snake case) on the field name. e.g title (default), title_mi_nz (New Zealand Maori mi-NZ)
|
143
156
|
|
144
157
|
## Development
|
145
158
|
|
data/consyncful.gemspec
CHANGED
@@ -7,8 +7,8 @@ require 'consyncful/version'
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
8
|
spec.name = 'consyncful'
|
9
9
|
spec.version = Consyncful::VERSION
|
10
|
-
spec.authors = ['Andy Anastasiadis-Gray', 'Montgomery Anderson']
|
11
|
-
spec.email = ['andy@boost.co.nz', 'montgomery@boost.co.nz']
|
10
|
+
spec.authors = ['Andy Anastasiadis-Gray', 'Montgomery Anderson', 'Greg Rogan']
|
11
|
+
spec.email = ['andy@boost.co.nz', 'montgomery@boost.co.nz', 'greg@boost.co.nz']
|
12
12
|
|
13
13
|
spec.summary = 'Contentful to local database synchronisation for Rails'
|
14
14
|
spec.homepage = 'https://github.com/boost/consyncful'
|
@@ -30,11 +30,14 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
31
|
spec.require_paths = ['lib']
|
32
32
|
|
33
|
-
spec.add_development_dependency 'bundler', '~>
|
34
|
-
spec.add_development_dependency '
|
33
|
+
spec.add_development_dependency 'bundler', '~> 2'
|
34
|
+
spec.add_development_dependency 'database_cleaner'
|
35
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
35
36
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
37
|
+
spec.add_development_dependency 'rubocop', '0.79.0'
|
36
38
|
|
37
39
|
spec.add_dependency 'contentful', ['>=2.11.1', '<3.0.0']
|
40
|
+
spec.add_dependency 'hooks', '>=0.4.1'
|
38
41
|
spec.add_dependency 'mongoid', ['>=7.0.2', '<8.0.0']
|
39
|
-
spec.add_dependency '
|
42
|
+
spec.add_dependency 'rainbow'
|
40
43
|
end
|
data/lib/consyncful.rb
CHANGED
data/lib/consyncful/base.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Consyncful
|
4
|
+
##
|
5
|
+
# Responsible for mapping an update received from Contentful's syncronisation API
|
6
|
+
# into useful fields for Consyncful::PersistedItem to store in the database.
|
4
7
|
class ItemMapper
|
5
8
|
def initialize(item)
|
6
9
|
@item = item
|
@@ -22,17 +25,11 @@ module Consyncful
|
|
22
25
|
@item.id
|
23
26
|
end
|
24
27
|
|
25
|
-
def mapped_fields(
|
28
|
+
def mapped_fields(default_locale)
|
26
29
|
fields = generic_fields
|
27
30
|
|
28
|
-
|
29
|
-
|
30
|
-
next if value.is_a? Contentful::File # it is special
|
31
|
-
|
32
|
-
assign_field(fields, field, value)
|
33
|
-
end
|
34
|
-
|
35
|
-
fields[:file] = raw_file(locale) if type == 'asset'
|
31
|
+
fields.merge!(localized_fields(default_locale))
|
32
|
+
fields.merge!(localized_asset_fields(default_locale)) if type == 'asset'
|
36
33
|
|
37
34
|
fields
|
38
35
|
end
|
@@ -49,9 +46,31 @@ module Consyncful
|
|
49
46
|
fields
|
50
47
|
end
|
51
48
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
49
|
+
def localized_fields(default_locale)
|
50
|
+
fields = {}
|
51
|
+
|
52
|
+
@item.fields_with_locales.each do |field, value_with_locales|
|
53
|
+
value_with_locales.each do |locale_code, value|
|
54
|
+
next if value.is_a? Contentful::File # assets are handeled below
|
55
|
+
|
56
|
+
fieldname = locale_code == default_locale.to_sym ? field : "#{field}_#{locale_code.to_s.underscore}".to_sym
|
57
|
+
assign_field(fields, fieldname, value)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
fields
|
62
|
+
end
|
63
|
+
|
64
|
+
def localized_asset_fields(default_locale)
|
65
|
+
fields = {}
|
66
|
+
files_by_locale = @item.raw.dig('fields', 'file') || {}
|
67
|
+
|
68
|
+
files_by_locale.each do |locale_code, details|
|
69
|
+
fieldname = locale_code == default_locale ? 'file' : "file_#{locale_code.to_s.underscore}"
|
70
|
+
fields[fieldname.to_sym] = details
|
71
|
+
end
|
72
|
+
|
73
|
+
fields
|
55
74
|
end
|
56
75
|
|
57
76
|
def reference_value?(value)
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Consyncful
|
4
|
+
##
|
5
|
+
# Takes a mapped item from Contentful and creates/updates/deletes
|
6
|
+
# the relevant model in the local database.
|
7
|
+
class PersistedItem
|
8
|
+
DEFAULT_LOCALE = 'en-NZ'
|
9
|
+
|
10
|
+
def initialize(item, sync_id, stats)
|
11
|
+
@item = item
|
12
|
+
@sync_id = sync_id
|
13
|
+
@stats = stats
|
14
|
+
end
|
15
|
+
|
16
|
+
def persist
|
17
|
+
puts Rainbow("syncing: #{@item.id}").yellow
|
18
|
+
if @item.deletion?
|
19
|
+
delete_model(@item.id, @stats)
|
20
|
+
else
|
21
|
+
create_or_update_model(@item, @sync_id, @stats)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def delete_model(id, stats)
|
28
|
+
Base.find_by(id: id).destroy
|
29
|
+
stats.record_deleted
|
30
|
+
rescue Mongoid::Errors::DocumentNotFound
|
31
|
+
puts Rainbow("Deleted record not found: #{id}").yellow
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_or_update_model(item, sync_id, stats)
|
36
|
+
return if item.type.nil?
|
37
|
+
|
38
|
+
instance = find_or_initialize_item(item)
|
39
|
+
update_stats(instance, stats)
|
40
|
+
|
41
|
+
reset_fields(instance)
|
42
|
+
|
43
|
+
item.mapped_fields(DEFAULT_LOCALE).each do |field, value|
|
44
|
+
instance[field] = value
|
45
|
+
end
|
46
|
+
|
47
|
+
instance[:sync_id] = sync_id
|
48
|
+
|
49
|
+
instance.save
|
50
|
+
end
|
51
|
+
|
52
|
+
def find_or_initialize_item(item)
|
53
|
+
model_class(item.type).find_or_initialize_by(id: item.id)
|
54
|
+
end
|
55
|
+
|
56
|
+
def update_stats(instance, stats)
|
57
|
+
if instance.persisted?
|
58
|
+
stats.record_updated
|
59
|
+
else
|
60
|
+
stats.record_added
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def model_class(type)
|
65
|
+
Base.model_map[type] || Base
|
66
|
+
end
|
67
|
+
|
68
|
+
def reset_fields(instance)
|
69
|
+
instance.attributes.each do |field_name, _value|
|
70
|
+
next if field_name.in? %w[_id _type]
|
71
|
+
|
72
|
+
instance[field_name] = nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/consyncful/stats.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'rainbow'
|
4
|
+
|
3
5
|
module Consyncful
|
6
|
+
##
|
7
|
+
# Responsible for recording changes during a sync for outputting in logs
|
4
8
|
class Stats
|
5
9
|
def initialize
|
6
10
|
@stats = {
|
@@ -23,9 +27,9 @@ module Consyncful
|
|
23
27
|
end
|
24
28
|
|
25
29
|
def print_stats
|
26
|
-
puts "Added: #{@stats[:records_added]}, \
|
30
|
+
puts Rainbow("Added: #{@stats[:records_added]}, \
|
27
31
|
updated: #{@stats[:records_updated]}, \
|
28
|
-
deleted: #{@stats[:records_deleted]}".blue
|
32
|
+
deleted: #{@stats[:records_deleted]}").blue
|
29
33
|
end
|
30
34
|
end
|
31
35
|
end
|
data/lib/consyncful/sync.rb
CHANGED
@@ -1,18 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'rainbow'
|
4
4
|
require 'consyncful/item_mapper'
|
5
|
+
require 'consyncful/persisted_item'
|
5
6
|
require 'consyncful/stats'
|
6
|
-
|
7
|
-
class String
|
8
|
-
include Term::ANSIColor
|
9
|
-
end
|
7
|
+
require 'hooks'
|
10
8
|
|
11
9
|
module Consyncful
|
10
|
+
##
|
11
|
+
# A mongoid model that stores the state of a syncronisation feed. Stores the
|
12
|
+
# next URL provided by Contentfuls Sync API.
|
13
|
+
#
|
14
|
+
# Sync's are affectivly singletons,
|
15
|
+
# there should only ever be one in the database
|
16
|
+
#
|
17
|
+
# Is also the entrypoint of a Syncronization run
|
12
18
|
class Sync
|
13
19
|
include Mongoid::Document
|
20
|
+
include Hooks
|
14
21
|
|
15
|
-
|
22
|
+
define_hook :before_run
|
23
|
+
define_hook :after_run
|
16
24
|
|
17
25
|
field :next_url
|
18
26
|
field :last_run_at, type: DateTime
|
@@ -21,31 +29,41 @@ module Consyncful
|
|
21
29
|
last || new
|
22
30
|
end
|
23
31
|
|
32
|
+
##
|
33
|
+
# Delete the previous sync chains from database and create a fresh one.
|
34
|
+
# Used to completely resync all items from Contentful.
|
24
35
|
def self.fresh
|
25
36
|
destroy_all
|
26
37
|
latest
|
27
38
|
end
|
28
39
|
|
40
|
+
##
|
41
|
+
# Makes sure that the database contains only records that have been provided
|
42
|
+
# during this chain of syncronisation.
|
29
43
|
def drop_stale
|
30
44
|
stale = Base.where(:sync_id.ne => id, :sync_id.exists => true)
|
31
|
-
puts "Dropping #{stale.count} records that haven't been touched in this sync".red
|
45
|
+
puts Rainbow("Dropping #{stale.count} records that haven't been touched in this sync").red
|
32
46
|
stale.destroy
|
33
47
|
end
|
34
48
|
|
49
|
+
##
|
50
|
+
# Entry point to a syncronization run. Is responsible for updating Sync state
|
35
51
|
def run
|
52
|
+
run_hook :before_run
|
53
|
+
|
36
54
|
stats = Consyncful::Stats.new
|
37
55
|
load_all_models
|
38
56
|
|
39
57
|
sync = start_sync
|
40
58
|
|
41
|
-
sync_items(sync, stats)
|
59
|
+
changed_ids = sync_items(sync, stats)
|
42
60
|
|
43
61
|
drop_stale
|
44
62
|
|
45
|
-
|
46
|
-
self.last_run_at = Time.current
|
47
|
-
save
|
63
|
+
update_run(sync.next_sync_url)
|
48
64
|
stats.print_stats
|
65
|
+
|
66
|
+
run_hook :after_run, changed_ids
|
49
67
|
end
|
50
68
|
|
51
69
|
private
|
@@ -56,80 +74,36 @@ module Consyncful
|
|
56
74
|
Rails.application.eager_load!
|
57
75
|
end
|
58
76
|
|
77
|
+
def update_run(next_url)
|
78
|
+
self.next_url = next_url
|
79
|
+
self.last_run_at = Time.current
|
80
|
+
save
|
81
|
+
end
|
82
|
+
|
59
83
|
def start_sync
|
60
84
|
if next_url.present?
|
61
|
-
puts "Starting update, last update: #{last_run_at} (#{(Time.current - last_run_at).round(3)}s ago)".blue
|
85
|
+
puts Rainbow("Starting update, last update: #{last_run_at} (#{(Time.current - last_run_at).round(3)}s ago)").blue
|
62
86
|
Consyncful.client.sync(next_url)
|
63
87
|
else
|
64
|
-
puts 'Starting full refresh'.blue
|
88
|
+
puts Rainbow('Starting full refresh').blue
|
65
89
|
Consyncful.client.sync(initial: true)
|
66
90
|
end
|
67
91
|
end
|
68
92
|
|
69
93
|
def sync_items(sync, stats)
|
94
|
+
ids = []
|
70
95
|
sync.each_page do |page|
|
71
96
|
page.items.each do |item|
|
72
|
-
sync_item(ItemMapper.new(item), stats)
|
97
|
+
ids << sync_item(ItemMapper.new(item), stats)
|
73
98
|
end
|
74
99
|
end
|
100
|
+
ids
|
75
101
|
end
|
76
102
|
|
77
103
|
def sync_item(item, stats)
|
78
|
-
puts "syncing: #{item.id}".yellow
|
79
|
-
|
80
|
-
|
81
|
-
else
|
82
|
-
create_or_update_model(item, stats)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def delete_model(id, stats)
|
87
|
-
Base.find_by(id: id).destroy
|
88
|
-
stats.record_deleted
|
89
|
-
rescue Mongoid::Errors::DocumentNotFound
|
90
|
-
puts "Deleted record not found: #{id}".yellow
|
91
|
-
nil
|
92
|
-
end
|
93
|
-
|
94
|
-
def create_or_update_model(item, stats)
|
95
|
-
return if item.type.nil?
|
96
|
-
|
97
|
-
instance = find_or_initialize_item(item)
|
98
|
-
update_stats(instance, stats)
|
99
|
-
|
100
|
-
reset_fields(instance)
|
101
|
-
|
102
|
-
item.mapped_fields(DEFAULT_LOCALE).each do |field, value|
|
103
|
-
instance[field] = value
|
104
|
-
end
|
105
|
-
|
106
|
-
instance[:sync_id] = id
|
107
|
-
|
108
|
-
instance.save
|
109
|
-
end
|
110
|
-
|
111
|
-
def find_or_initialize_item(item)
|
112
|
-
model_class(item.type).find_or_initialize_by(id: item.id)
|
113
|
-
end
|
114
|
-
|
115
|
-
def update_stats(instance, stats)
|
116
|
-
if instance.persisted?
|
117
|
-
stats.record_updated
|
118
|
-
else
|
119
|
-
stats.record_added
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
def model_class(type)
|
124
|
-
Base.model_map[type] || Base
|
125
|
-
end
|
126
|
-
|
127
|
-
def reset_fields(instance)
|
128
|
-
instance.attributes.each do |field_name, _value|
|
129
|
-
next if field_name.in? %w[_id _type]
|
130
|
-
|
131
|
-
instance[field_name] = nil
|
132
|
-
end
|
104
|
+
puts Rainbow("syncing: #{item.id}").yellow
|
105
|
+
PersistedItem.new(item, id, stats).persist
|
106
|
+
item.id
|
133
107
|
end
|
134
108
|
end
|
135
109
|
end
|
@@ -19,12 +19,19 @@ namespace :consyncful do
|
|
19
19
|
end
|
20
20
|
|
21
21
|
task update_model_names: [:environment] do
|
22
|
-
Rails.application.
|
23
|
-
|
22
|
+
if Object.const_defined?('Zeitwerk::Loader') && Rails.application.config.autoloader.to_s == 'zeitwerk'
|
23
|
+
Zeitwerk::Loader.eager_load_all
|
24
|
+
else
|
25
|
+
Rails.application.eager_load!
|
26
|
+
end
|
27
|
+
|
28
|
+
puts Rainbow('Updating model names:').blue
|
29
|
+
|
24
30
|
Consyncful::Base.model_map.each do |contentful_name, constant|
|
25
|
-
puts "#{contentful_name}: #{constant}".yellow
|
31
|
+
puts Rainbow("#{contentful_name}: #{constant}").yellow
|
26
32
|
Consyncful::Base.where(contentful_type: contentful_name).update_all(_type: constant.to_s)
|
27
33
|
end
|
34
|
+
|
28
35
|
Consyncful::Base.where(:contentful_type.nin => Consyncful::Base.model_map.keys).update_all(_type: 'Consyncful::Base')
|
29
36
|
end
|
30
37
|
end
|
data/lib/consyncful/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: consyncful
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Anastasiadis-Gray
|
8
8
|
- Montgomery Anderson
|
9
|
+
- Greg Rogan
|
9
10
|
autorequire:
|
10
11
|
bindir: exe
|
11
12
|
cert_chain: []
|
12
|
-
date:
|
13
|
+
date: 2021-03-01 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: bundler
|
@@ -17,28 +18,42 @@ dependencies:
|
|
17
18
|
requirements:
|
18
19
|
- - "~>"
|
19
20
|
- !ruby/object:Gem::Version
|
20
|
-
version: '
|
21
|
+
version: '2'
|
21
22
|
type: :development
|
22
23
|
prerelease: false
|
23
24
|
version_requirements: !ruby/object:Gem::Requirement
|
24
25
|
requirements:
|
25
26
|
- - "~>"
|
26
27
|
- !ruby/object:Gem::Version
|
27
|
-
version: '
|
28
|
+
version: '2'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: database_cleaner
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
type: :development
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
28
43
|
- !ruby/object:Gem::Dependency
|
29
44
|
name: rake
|
30
45
|
requirement: !ruby/object:Gem::Requirement
|
31
46
|
requirements:
|
32
47
|
- - "~>"
|
33
48
|
- !ruby/object:Gem::Version
|
34
|
-
version: '
|
49
|
+
version: '13.0'
|
35
50
|
type: :development
|
36
51
|
prerelease: false
|
37
52
|
version_requirements: !ruby/object:Gem::Requirement
|
38
53
|
requirements:
|
39
54
|
- - "~>"
|
40
55
|
- !ruby/object:Gem::Version
|
41
|
-
version: '
|
56
|
+
version: '13.0'
|
42
57
|
- !ruby/object:Gem::Dependency
|
43
58
|
name: rspec
|
44
59
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,6 +68,20 @@ dependencies:
|
|
53
68
|
- - "~>"
|
54
69
|
- !ruby/object:Gem::Version
|
55
70
|
version: '3.0'
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: rubocop
|
73
|
+
requirement: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - '='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.79.0
|
78
|
+
type: :development
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - '='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 0.79.0
|
56
85
|
- !ruby/object:Gem::Dependency
|
57
86
|
name: contentful
|
58
87
|
requirement: !ruby/object:Gem::Requirement
|
@@ -73,6 +102,20 @@ dependencies:
|
|
73
102
|
- - "<"
|
74
103
|
- !ruby/object:Gem::Version
|
75
104
|
version: 3.0.0
|
105
|
+
- !ruby/object:Gem::Dependency
|
106
|
+
name: hooks
|
107
|
+
requirement: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 0.4.1
|
112
|
+
type: :runtime
|
113
|
+
prerelease: false
|
114
|
+
version_requirements: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 0.4.1
|
76
119
|
- !ruby/object:Gem::Dependency
|
77
120
|
name: mongoid
|
78
121
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,7 +137,7 @@ dependencies:
|
|
94
137
|
- !ruby/object:Gem::Version
|
95
138
|
version: 8.0.0
|
96
139
|
- !ruby/object:Gem::Dependency
|
97
|
-
name:
|
140
|
+
name: rainbow
|
98
141
|
requirement: !ruby/object:Gem::Requirement
|
99
142
|
requirements:
|
100
143
|
- - ">="
|
@@ -111,6 +154,7 @@ description:
|
|
111
154
|
email:
|
112
155
|
- andy@boost.co.nz
|
113
156
|
- montgomery@boost.co.nz
|
157
|
+
- greg@boost.co.nz
|
114
158
|
executables: []
|
115
159
|
extensions: []
|
116
160
|
extra_rdoc_files: []
|
@@ -118,6 +162,7 @@ files:
|
|
118
162
|
- ".gitignore"
|
119
163
|
- ".rspec"
|
120
164
|
- ".rubocop.yml"
|
165
|
+
- ".rubocop_todo.yml"
|
121
166
|
- ".ruby-version"
|
122
167
|
- ".travis.yml"
|
123
168
|
- Gemfile
|
@@ -133,6 +178,7 @@ files:
|
|
133
178
|
- lib/consyncful.rb
|
134
179
|
- lib/consyncful/base.rb
|
135
180
|
- lib/consyncful/item_mapper.rb
|
181
|
+
- lib/consyncful/persisted_item.rb
|
136
182
|
- lib/consyncful/railtie.rb
|
137
183
|
- lib/consyncful/stats.rb
|
138
184
|
- lib/consyncful/sync.rb
|
@@ -157,8 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
157
203
|
- !ruby/object:Gem::Version
|
158
204
|
version: '0'
|
159
205
|
requirements: []
|
160
|
-
|
161
|
-
rubygems_version: 2.7.6.2
|
206
|
+
rubygems_version: 3.1.2
|
162
207
|
signing_key:
|
163
208
|
specification_version: 4
|
164
209
|
summary: Contentful to local database synchronisation for Rails
|