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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 258e4fe9fbea3099f0d78615c79b1e49658841c7b706f296a1d5402115c5ed7d
4
- data.tar.gz: 8d47ad368c228b87a1f04fc5d1c4c236c16d6d3106bae1dda6a49de0a74a9c9d
3
+ metadata.gz: 14eda0769dbcdc516d59a9a4252fafb23eb6380ce0224a276e101f81d649c950
4
+ data.tar.gz: aa4aaf421824043dbea600c1cfabca2633a8d3470ed2a14aba5685b8bf8acd90
5
5
  SHA512:
6
- metadata.gz: 18b498a8a55c814edf02fabec13c54d93a6653aae0e1e87143ea9e2d637d3eb61e6beaf045201660ad065bb745f4df97b411fa6f451bbd02858d45b1e041aa24
7
- data.tar.gz: 841216be1a7c0149aea9e6136369cdd2bd3a1e5d9edb9854b677f98d2ef1596b7df3273370054713915fd798016578263398f24d8e23460360caa2bb5751871d
6
+ metadata.gz: 9ac1a66dda08d88396d83c736308516a798503afa519cd673e76f668d771dc933b6068a2e03c1f75926ec8980ad273c0936af6e2058d84d5919a250a498ebbd7
7
+ data.tar.gz: 260c353fe5eb6c646473830ecdec0a094016bf0f0e518ef357ceb7cc03862f38e76e4cccbcf656a4c368fad72641ef60788d13a5c76e768a5b8fc9652b375c96
data/.rubocop.yml CHANGED
@@ -1,9 +1 @@
1
-
2
-
3
-
4
- Metrics/LineLength:
5
- Max: 120
6
-
7
- Metrics/BlockLength:
8
- Exclude:
9
- - spec/**/*
1
+ inherit_from: .rubocop_todo.yml
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.5.5
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
- before_install: gem install bundler -v 1.16.1
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.2.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
- term-ansicolor
8
+ rainbow
8
9
 
9
10
  GEM
10
11
  remote: https://rubygems.org/
11
12
  specs:
12
- activemodel (5.2.2)
13
- activesupport (= 5.2.2)
14
- activesupport (5.2.2)
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
- addressable (2.6.0)
20
- public_suffix (>= 2.0.2, < 4.0)
21
- bson (4.4.2)
22
- concurrent-ruby (1.1.4)
23
- contentful (2.11.1)
24
- http (> 0.8, < 4.0)
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.20180417)
31
+ domain_name (0.5.20190701)
28
32
  unf (>= 0.0.5, < 1.0.0)
29
- http (3.3.0)
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.0)
33
- http_parser.rb (~> 0.6.0)
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.1.1)
37
- http_parser.rb (0.6.0)
38
- i18n (1.5.3)
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
- minitest (5.11.3)
41
- mongo (2.7.0)
42
- bson (>= 4.4.2, < 5.0.0)
43
- mongoid (7.0.2)
44
- activemodel (>= 5.1, < 6.0.0)
45
- mongo (>= 2.5.1, < 3.0.0)
46
- multi_json (1.13.1)
47
- public_suffix (3.0.3)
48
- rake (10.5.0)
49
- rspec (3.8.0)
50
- rspec-core (~> 3.8.0)
51
- rspec-expectations (~> 3.8.0)
52
- rspec-mocks (~> 3.8.0)
53
- rspec-core (3.8.0)
54
- rspec-support (~> 3.8.0)
55
- rspec-expectations (3.8.2)
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.8.0)
58
- rspec-mocks (3.8.0)
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.8.0)
61
- rspec-support (3.8.0)
62
- term-ansicolor (1.7.1)
63
- tins (~> 1.0)
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
- tins (1.20.2)
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.5)
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 (~> 1.16)
100
+ bundler (~> 2)
77
101
  consyncful!
78
- rake (~> 10.0)
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 availble locally, finding and interact with contentful data is as easy as
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 intergration with the management api or any way to update contentful models from the local store. It is strictly read only.
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 mongoid, generate a mongoid.yml by running:
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
- Consyncful uses [contentful.rb](https://github.com/contentful/contentful.rb) so client options are as documented there.
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 rails app
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 dynamicly 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:
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 mongoid associations, Consyncful provides the following helpers to set up the correct relationships:
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
- ### Syncronizing contentful data
82
+ ### Synchronizing contentful data
86
83
 
87
- To run a syncronization process run:
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 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.
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 syncronise from scratch run:
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 rails models
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 mongoid [queries](https://docs.mongodb.com/mongoid/current/tutorials/mongoid-queries/).
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 woule expect:
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 contentful. If you want them in the order they appare in contentful, use the `.in_order` helper:
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 contentful
124
+ instance.other_things.in_order # ordered the same as in Contentful
128
125
  ```
129
126
 
130
- #### Finding entrys from different content types
127
+ #### Finding entries from different content types
131
128
 
132
- 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:
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
- ## Limitations
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
- Current Consyncful only uses one globally configured locale to map the data to the database.
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', '~> 1.16'
34
- spec.add_development_dependency 'rake', '~> 10.0'
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 'term-ansicolor'
42
+ spec.add_dependency 'rainbow'
40
43
  end
data/lib/consyncful.rb CHANGED
@@ -27,7 +27,7 @@ module Consyncful
27
27
  @contentful_client_options = {
28
28
  api_url: 'cdn.contentful.com'
29
29
  }
30
- @locale = 'en-US'
30
+ @locale = 'en-NZ'
31
31
  end
32
32
  end
33
33
 
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Consyncful
4
+ ##
5
+ # Provides common functionality of Mongoid models created from contentful
6
+ # entries
4
7
  class Base
5
8
  include Mongoid::Document
6
9
  include Mongoid::Attributes::Dynamic
@@ -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(locale)
28
+ def mapped_fields(default_locale)
26
29
  fields = generic_fields
27
30
 
28
- @item.fields_with_locales.each do |field, value_with_locales|
29
- value = value_with_locales[locale.to_sym]
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 raw_file(locale)
53
- file_json = @item.raw.fetch('fields', {}).fetch('file', nil)
54
- file_json[locale] unless file_json.nil?
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
@@ -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
@@ -1,18 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'term/ansicolor'
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
- DEFAULT_LOCALE = 'en-NZ'
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
- self.next_url = sync.next_sync_url
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
- if item.deletion?
80
- delete_model(item.id, stats)
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.eager_load!
23
- puts 'Updating model names:'.blue
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Consyncful
4
- VERSION = '0.3.0'
4
+ VERSION = '0.5.0'
5
5
  end
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.3.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: 2019-07-02 00:00:00.000000000 Z
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: '1.16'
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: '1.16'
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: '10.0'
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: '10.0'
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: term-ansicolor
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
- rubyforge_project:
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