wcc-contentful 0.3.0 → 1.0.0.pre.rc2

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.
Files changed (99) hide show
  1. checksums.yaml +5 -5
  2. data/.rspec +1 -1
  3. data/Guardfile +43 -0
  4. data/README.md +161 -11
  5. data/Rakefile +3 -6
  6. data/app/controllers/wcc/contentful/webhook_controller.rb +25 -24
  7. data/app/jobs/wcc/contentful/webhook_enable_job.rb +36 -2
  8. data/bin/console +4 -3
  9. data/bin/rails +2 -0
  10. data/config/routes.rb +1 -1
  11. data/doc +1 -0
  12. data/lib/tasks/download_schema.rake +12 -0
  13. data/lib/wcc/contentful.rb +69 -45
  14. data/lib/wcc/contentful/active_record_shim.rb +72 -0
  15. data/lib/wcc/contentful/configuration.rb +177 -46
  16. data/lib/wcc/contentful/content_type_indexer.rb +14 -0
  17. data/lib/wcc/contentful/downloads_schema.rb +112 -0
  18. data/lib/wcc/contentful/engine.rb +33 -14
  19. data/lib/wcc/contentful/event.rb +171 -0
  20. data/lib/wcc/contentful/events.rb +41 -0
  21. data/lib/wcc/contentful/exceptions.rb +3 -33
  22. data/lib/wcc/contentful/indexed_representation.rb +2 -2
  23. data/lib/wcc/contentful/instrumentation.rb +31 -0
  24. data/lib/wcc/contentful/link.rb +28 -0
  25. data/lib/wcc/contentful/link_visitor.rb +122 -0
  26. data/lib/wcc/contentful/middleware.rb +7 -0
  27. data/lib/wcc/contentful/middleware/store.rb +158 -0
  28. data/lib/wcc/contentful/middleware/store/caching_middleware.rb +114 -0
  29. data/lib/wcc/contentful/model.rb +37 -4
  30. data/lib/wcc/contentful/model_builder.rb +1 -0
  31. data/lib/wcc/contentful/model_methods.rb +40 -15
  32. data/lib/wcc/contentful/model_singleton_methods.rb +47 -30
  33. data/lib/wcc/contentful/rake.rb +4 -0
  34. data/lib/wcc/contentful/rspec.rb +46 -0
  35. data/lib/wcc/contentful/services.rb +61 -27
  36. data/lib/wcc/contentful/simple_client.rb +81 -25
  37. data/lib/wcc/contentful/simple_client/management.rb +43 -10
  38. data/lib/wcc/contentful/simple_client/response.rb +61 -22
  39. data/lib/wcc/contentful/simple_client/typhoeus_adapter.rb +17 -17
  40. data/lib/wcc/contentful/store.rb +7 -66
  41. data/lib/wcc/contentful/store/README.md +85 -0
  42. data/lib/wcc/contentful/store/base.rb +34 -119
  43. data/lib/wcc/contentful/store/cdn_adapter.rb +71 -12
  44. data/lib/wcc/contentful/store/factory.rb +186 -0
  45. data/lib/wcc/contentful/store/instrumentation.rb +55 -0
  46. data/lib/wcc/contentful/store/interface.rb +82 -0
  47. data/lib/wcc/contentful/store/memory_store.rb +27 -24
  48. data/lib/wcc/contentful/store/postgres_store.rb +268 -101
  49. data/lib/wcc/contentful/store/postgres_store/schema_1.sql +73 -0
  50. data/lib/wcc/contentful/store/postgres_store/schema_2.sql +21 -0
  51. data/lib/wcc/contentful/store/query.rb +246 -0
  52. data/lib/wcc/contentful/store/query/interface.rb +63 -0
  53. data/lib/wcc/contentful/store/rspec_examples.rb +48 -0
  54. data/lib/wcc/contentful/store/rspec_examples/basic_store.rb +629 -0
  55. data/lib/wcc/contentful/store/rspec_examples/include_param.rb +283 -0
  56. data/lib/wcc/contentful/store/rspec_examples/nested_queries.rb +342 -0
  57. data/lib/wcc/contentful/sync_engine.rb +181 -0
  58. data/lib/wcc/contentful/test.rb +7 -0
  59. data/lib/wcc/contentful/test/attributes.rb +56 -0
  60. data/lib/wcc/contentful/test/double.rb +76 -0
  61. data/lib/wcc/contentful/test/factory.rb +101 -0
  62. data/lib/wcc/contentful/version.rb +1 -1
  63. data/wcc-contentful.gemspec +28 -14
  64. metadata +248 -152
  65. data/.circleci/config.yml +0 -51
  66. data/.gitignore +0 -26
  67. data/.rubocop.yml +0 -242
  68. data/.rubocop_todo.yml +0 -19
  69. data/.travis.yml +0 -5
  70. data/CHANGELOG.md +0 -180
  71. data/CODE_OF_CONDUCT.md +0 -74
  72. data/Gemfile +0 -8
  73. data/LICENSE.txt +0 -21
  74. data/app/jobs/wcc/contentful/delayed_sync_job.rb +0 -63
  75. data/lib/generators/wcc/USAGE +0 -24
  76. data/lib/generators/wcc/model_generator.rb +0 -90
  77. data/lib/generators/wcc/templates/.keep +0 -0
  78. data/lib/generators/wcc/templates/Procfile +0 -3
  79. data/lib/generators/wcc/templates/contentful_shell_wrapper +0 -385
  80. data/lib/generators/wcc/templates/menu/generated_add_menus.ts +0 -192
  81. data/lib/generators/wcc/templates/menu/models/menu.rb +0 -23
  82. data/lib/generators/wcc/templates/menu/models/menu_button.rb +0 -23
  83. data/lib/generators/wcc/templates/page/generated_add_pages.ts +0 -50
  84. data/lib/generators/wcc/templates/page/models/page.rb +0 -23
  85. data/lib/generators/wcc/templates/release +0 -9
  86. data/lib/generators/wcc/templates/wcc_contentful.rb +0 -17
  87. data/lib/wcc/contentful/client_ext.rb +0 -28
  88. data/lib/wcc/contentful/graphql.rb +0 -14
  89. data/lib/wcc/contentful/graphql/builder.rb +0 -177
  90. data/lib/wcc/contentful/graphql/types.rb +0 -54
  91. data/lib/wcc/contentful/model/dropdown_menu.rb +0 -7
  92. data/lib/wcc/contentful/model/menu.rb +0 -6
  93. data/lib/wcc/contentful/model/menu_button.rb +0 -16
  94. data/lib/wcc/contentful/model/page.rb +0 -8
  95. data/lib/wcc/contentful/model/redirect.rb +0 -19
  96. data/lib/wcc/contentful/model_validators.rb +0 -121
  97. data/lib/wcc/contentful/model_validators/dsl.rb +0 -166
  98. data/lib/wcc/contentful/simple_client/http_adapter.rb +0 -24
  99. data/lib/wcc/contentful/store/lazy_cache_store.rb +0 -161
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'wcc/contentful/event'
4
+ require 'wisper'
5
+
6
+ begin
7
+ gem 'activejob'
8
+ require 'active_job'
9
+ rescue Gem::LoadError # rubocop:disable Lint/HandleExceptions
10
+ # suppress
11
+ end
12
+
13
+ module WCC::Contentful
14
+ # The SyncEngine is used to keep the currently configured store up to date
15
+ # using the Sync API. It is available on the WCC::Contentful::Services instance,
16
+ # and the application is responsible to periodically call #next in order to hit
17
+ # the sync API and update the store.
18
+ #
19
+ # If you have mounted the WCC::Contentful::Engine, AND the configured store is
20
+ # one that can be synced (i.e. it responds to `:index`), then
21
+ # the WCC::Contentful::WebhookController will call #next automatically anytime
22
+ # a webhook is received. Otherwise you should hook up to the Webhook events
23
+ # and call the sync engine via your initializer:
24
+ # WCC::Contentful::Events.subscribe(proc do |event|
25
+ # WCC::Contentful::Services.instance.sync_engine.next(up_to: event.dig('sys', 'id'))
26
+ # end, with: :call)
27
+ class SyncEngine
28
+ include ::Wisper::Publisher
29
+
30
+ def state
31
+ (@state&.dup || token_wrapper_factory(nil)).freeze
32
+ end
33
+
34
+ attr_reader :store
35
+ attr_reader :client
36
+
37
+ def should_sync?
38
+ store&.index?
39
+ end
40
+
41
+ def initialize(state: nil, store: nil, client: nil, key: nil)
42
+ @state_key = key || "sync:#{object_id}"
43
+ @client = client || WCC::Contentful::Services.instance.client
44
+ @mutex = Mutex.new
45
+
46
+ if store
47
+ unless %i[index index? find].all? { |m| store.respond_to?(m) }
48
+ raise ArgumentError, ':store param must implement the Store interface'
49
+ end
50
+
51
+ @store = store
52
+ @state = read_state if should_sync?
53
+ end
54
+ if state
55
+ @state = token_wrapper_factory(state)
56
+ raise ArgumentError, ':state param must be a String or Hash' unless @state.is_a? Hash
57
+ unless @state.dig('sys', 'type') == 'token'
58
+ raise ArgumentError, ':state param must be of sys.type = "token"'
59
+ end
60
+ end
61
+ raise ArgumentError, 'either :state or :store must be provided' unless @state || @store
62
+ end
63
+
64
+ # Gets the next increment of data from the Sync API.
65
+ # If the configured store responds to `:index`, that will be called with each
66
+ # item in the Sync response to update the store.
67
+ # If a block is passed, that block will be evaluated with each item in the
68
+ # response.
69
+ # @param [String] up_to_id An ID to look for in the response. The method returns
70
+ # true if the ID was found or no up_to_id was given, false if the ID did not come back.
71
+ # @return [Array] A `[Boolean, Integer]` tuple where the first value is whether the ID was found,
72
+ # and the second value is the number of items returned.
73
+ def next(up_to_id: nil)
74
+ id_found = up_to_id.nil?
75
+ all_events = []
76
+
77
+ @mutex.synchronize do
78
+ @state ||= read_state || token_wrapper_factory(nil)
79
+ next_sync_token = @state['token']
80
+
81
+ sync_resp = client.sync(sync_token: next_sync_token)
82
+ sync_resp.items.each do |item|
83
+ id = item.dig('sys', 'id')
84
+ id_found ||= id == up_to_id
85
+
86
+ store.index(item) if store&.index?
87
+ event = WCC::Contentful::Event.from_raw(item, source: self)
88
+ yield(event) if block_given?
89
+ emit_event(event)
90
+ all_events << event
91
+ end
92
+
93
+ @state = @state.merge('token' => sync_resp.next_sync_token)
94
+ write_state
95
+ end
96
+
97
+ emit_sync_complete(all_events)
98
+
99
+ [id_found, all_events.length]
100
+ end
101
+
102
+ def emit_event(event)
103
+ type = event.dig('sys', 'type')
104
+ raise ArgumentError, "Unknown event type #{event}" unless type.present?
105
+
106
+ broadcast(type, event)
107
+ end
108
+
109
+ def emit_sync_complete(events)
110
+ event = WCC::Contentful::Event::SyncComplete.new(events, source: self)
111
+ broadcast('SyncComplete', event)
112
+ end
113
+
114
+ private
115
+
116
+ def read_state
117
+ return unless found = store&.find(@state_key)
118
+
119
+ # backwards compat - migrate existing state
120
+ token_wrapper_factory(found)
121
+ end
122
+
123
+ def write_state
124
+ store.index(@state) if store&.index?
125
+ end
126
+
127
+ def token_wrapper_factory(state)
128
+ state = { 'token' => state } unless state.is_a? Hash
129
+
130
+ state.merge!('sys' => { 'id' => @state_key, 'type' => 'token' }) unless state['sys']
131
+ state
132
+ end
133
+
134
+ # Define the job only if rails is loaded
135
+ if defined?(ActiveJob)
136
+ # This job uses the Contentful Sync API to update the configured store with
137
+ # the latest data from Contentful.
138
+ class Job < ActiveJob::Base
139
+ include WCC::Contentful::ServiceAccessors
140
+
141
+ self.queue_adapter = :async
142
+ queue_as :default
143
+
144
+ def perform(event = nil)
145
+ return unless sync_engine&.should_sync?
146
+
147
+ up_to_id = nil
148
+ up_to_id = event[:up_to_id] || event.dig('sys', 'id') if event
149
+ sync!(up_to_id: up_to_id)
150
+ end
151
+
152
+ # Calls the Contentful Sync API and updates the configured store with the returned
153
+ # data.
154
+ #
155
+ # @param [String] up_to_id
156
+ # An ID that we know has changed and should come back from the sync.
157
+ # If we don't find this ID in the sync data, then drop a job to try
158
+ # the sync again after a few minutes.
159
+ #
160
+ def sync!(up_to_id: nil)
161
+ id_found, count = sync_engine.next(up_to_id: up_to_id)
162
+
163
+ next_sync_token = sync_engine.state['token']
164
+
165
+ logger.info "Synced #{count} entries. Next sync token:\n #{next_sync_token}"
166
+ logger.info "Should enqueue again? [#{!id_found}]"
167
+ # Passing nil to only enqueue the job 1 more time
168
+ sync_later!(up_to_id: nil) unless id_found
169
+ next_sync_token
170
+ end
171
+
172
+ # Drops an ActiveJob job to invoke WCC::Contentful.sync! after a given amount
173
+ # of time.
174
+ def sync_later!(up_to_id: nil, wait: 10.minutes)
175
+ self.class.set(wait: wait)
176
+ .perform_later(up_to_id: up_to_id)
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WCC::Contentful::Test
4
+ end
5
+
6
+ require_relative 'test/double'
7
+ require_relative 'test/factory'
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WCC::Contentful::Test::Attributes
4
+ DEFAULTS = {
5
+ String: 'test',
6
+ Int: 0,
7
+ Float: 0.0,
8
+ DateTime: Time.at(0).to_s,
9
+ Boolean: false,
10
+ Json: ->(_f) { {} },
11
+ Coordinates: ->(_f) { {} },
12
+ Asset: ->(f) {
13
+ WCC::Contentful::Link.new(
14
+ "fake-#{f.name}-#{SecureRandom.urlsafe_base64[1..6]}",
15
+ :Asset
16
+ ).raw
17
+ },
18
+ Link: ->(f) {
19
+ WCC::Contentful::Link.new(
20
+ "fake-#{f.name}-#{SecureRandom.urlsafe_base64[1..6]}",
21
+ :Link
22
+ ).raw
23
+ }
24
+ }.freeze
25
+
26
+ class << self
27
+ def [](key)
28
+ DEFAULTS[key]
29
+ end
30
+
31
+ ##
32
+ # Get a hash of default values for all attributes unique to the given Contentful model.
33
+ def defaults(const)
34
+ unless const < WCC::Contentful::Model
35
+ raise ArgumentError, "#{const} is not a subclass of WCC::Contentful::Model"
36
+ end
37
+
38
+ const.content_type_definition.fields.each_with_object({}) do |(name, f), h|
39
+ h[name.to_sym] = h[name.underscore.to_sym] = default_value(f)
40
+ end
41
+ end
42
+
43
+ ##
44
+ # Gets the default value for a contentful IndexedRepresentation::Field.
45
+ # This comes from the 'content_type_definition' of a contentful model class.
46
+ def default_value(field)
47
+ return [] if field.array
48
+ return unless field.required
49
+
50
+ val = DEFAULTS[field.type]
51
+ return val.call(field) if val.respond_to?(:call)
52
+
53
+ val
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './attributes'
4
+
5
+ module WCC::Contentful::Test::Double
6
+ ##
7
+ # Builds a rspec double of the Contentful model for the given content_type.
8
+ # All attributes that are known to be required fields on the content type
9
+ # will return a default value based on the field type.
10
+ def contentful_double(content_type, **attrs)
11
+ const = WCC::Contentful::Model.resolve_constant(content_type)
12
+ attrs.symbolize_keys!
13
+
14
+ bad_attrs = attrs.reject { |a| const.instance_methods.include?(a) }
15
+ raise ArgumentError, "Attribute(s) do not exist on #{const}: #{bad_attrs.keys}" if bad_attrs.any?
16
+
17
+ double(attrs[:name] || attrs[:id] || nil, defaults(const).merge(attrs))
18
+ end
19
+
20
+ ##
21
+ # Builds an rspec double of a Contentful image asset, including the file
22
+ # URL and details. These fields can be overridden.
23
+ def contentful_image_double(**attrs)
24
+ attrs = {
25
+ title: WCC::Contentful::Test::Attributes[:String],
26
+ description: WCC::Contentful::Test::Attributes[:String],
27
+ file: {
28
+ url: '//images.ctfassets.net/7yx6/2rak/test.jpg',
29
+ details: {
30
+ image: {
31
+ width: 0,
32
+ height: 0
33
+ }
34
+ }
35
+ }
36
+ }.deep_merge!(attrs)
37
+
38
+ attrs[:file] = OpenStruct.new(attrs[:file]) if attrs[:file]
39
+
40
+ attrs[:raw] = {
41
+ sys: {
42
+ space: {
43
+ sys: {
44
+ type: 'Link',
45
+ linkType: 'Space',
46
+ id: ENV['CONTENTFUL_SPACE_ID']
47
+ }
48
+ },
49
+ id: SecureRandom.urlsafe_base64,
50
+ type: 'Asset',
51
+ createdAt: Time.now.to_s(:iso8601),
52
+ updatedAt: Time.now.to_s(:iso8601),
53
+ environment: {
54
+ sys: {
55
+ id: 'master',
56
+ type: 'Link',
57
+ linkType: 'Environment'
58
+ }
59
+ },
60
+ revision: rand(100),
61
+ locale: 'en-US'
62
+ },
63
+ fields: attrs.each_with_object({}) { |(k, v), h| h[k] = { 'en-US' => v } }
64
+ }
65
+
66
+ double(attrs)
67
+ end
68
+
69
+ private
70
+
71
+ def defaults(model)
72
+ attributes = WCC::Contentful::Test::Attributes.defaults(model)
73
+ methods = model.instance_methods - WCC::Contentful::Model.instance_methods
74
+ methods.each_with_object(attributes) { |f, h| h[f] ||= nil }
75
+ end
76
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './attributes'
4
+
5
+ module WCC::Contentful::Test::Factory
6
+ ##
7
+ # Builds a in-memory instance of the Contentful model for the given content_type.
8
+ # All attributes that are known to be required fields on the content type
9
+ # will return a default value based on the field type.
10
+ def contentful_create(content_type, context = nil, **attrs)
11
+ const = WCC::Contentful::Model.resolve_constant(content_type.to_s)
12
+ attrs = attrs.transform_keys { |a| a.to_s.camelize(:lower) }
13
+
14
+ id = attrs.delete('id')
15
+ sys = attrs.delete('sys')
16
+ raw = attrs.delete('raw') || default_raw(const, id)
17
+ bad_attrs = attrs.reject { |a| const.content_type_definition.fields.key?(a) }
18
+ raise ArgumentError, "Attribute(s) do not exist on #{const}: #{bad_attrs.keys}" if bad_attrs.any?
19
+
20
+ raw['sys'].merge!(sys) if sys
21
+
22
+ attrs.each do |k, v|
23
+ field = const.content_type_definition.fields[k]
24
+
25
+ raw_value = v
26
+ raw_value = to_raw(v, field.type) if %i[Asset Link].include?(field.type)
27
+ raw['fields'][field.name][raw.dig('sys', 'locale')] = raw_value
28
+ end
29
+
30
+ instance = const.new(raw, context)
31
+
32
+ attrs.each do |k, v|
33
+ field = const.content_type_definition.fields[k]
34
+ next unless %i[Asset Link].include?(field.type)
35
+
36
+ unless field.array ? v.any? { |i| i.is_a?(String) } : v.is_a?(String)
37
+ instance.instance_variable_set("@#{field.name}_resolved", v)
38
+ end
39
+ end
40
+
41
+ instance
42
+ end
43
+
44
+ private
45
+
46
+ def default_instance(model, id = nil)
47
+ model.new(default_raw(model, id))
48
+ end
49
+
50
+ def default_raw(model, id = nil)
51
+ { sys: contentful_sys(model, id), fields: contentful_fields(model) }.as_json
52
+ end
53
+
54
+ def contentful_sys(model, id = nil)
55
+ {
56
+ space: {
57
+ sys: {
58
+ type: 'Link',
59
+ linkType: 'Space',
60
+ id: ENV['CONTENTFUL_SPACE_ID']
61
+ }
62
+ },
63
+ id: id || SecureRandom.urlsafe_base64,
64
+ type: 'Entry',
65
+ createdAt: Time.now.to_s(:iso8601),
66
+ updatedAt: Time.now.to_s(:iso8601),
67
+ environment: {
68
+ sys: {
69
+ id: 'master',
70
+ type: 'Link',
71
+ linkType: 'Environment'
72
+ }
73
+ },
74
+ revision: rand(100),
75
+ contentType: {
76
+ sys: {
77
+ type: 'Link',
78
+ linkType: 'ContentType',
79
+ id: model.content_type
80
+ }
81
+ },
82
+ locale: 'en-US'
83
+ }
84
+ end
85
+
86
+ def contentful_fields(model)
87
+ WCC::Contentful::Test::Attributes.defaults(model).each_with_object({}) do |(k, v), h|
88
+ h[k] = { 'en-US' => v }
89
+ end
90
+ end
91
+
92
+ def to_raw(val, field_type)
93
+ if val.is_a? Array
94
+ val.map { |i| to_raw(i, field_type) }
95
+ elsif val.is_a? String
96
+ WCC::Contentful::Link.new(val, field_type).raw
97
+ elsif val
98
+ val.raw
99
+ end
100
+ end
101
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module WCC
4
4
  module Contentful
5
- VERSION = '0.3.0'
5
+ VERSION = '1.0.0-rc2'
6
6
  end
7
7
  end
@@ -4,17 +4,24 @@ lib = File.expand_path('lib', __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  require 'wcc/contentful/version'
6
6
 
7
+ doc_version = Gem::Version.new(WCC::Contentful::VERSION).release.to_s.sub(/\.\d+$/, '')
8
+
9
+ # rubocop:disable Metrics/LineLength
7
10
  Gem::Specification.new do |spec|
8
11
  spec.name = 'wcc-contentful'
9
12
  spec.version = WCC::Contentful::VERSION
10
13
  spec.authors = ['Watermark Dev']
11
14
  spec.email = ['dev@watermark.org']
12
15
 
13
- spec.summary = File.readlines('README.md').join
14
- spec.description = 'Contentful API wrapper library for Watermark apps'
15
- spec.homepage = 'https://github.com/watermarkchurch/wcc-contentful'
16
+ spec.summary = File.readlines(File.expand_path('README.md', __dir__)).join
17
+ spec.description = 'Contentful API wrapper library exposing an ActiveRecord-like interface'
18
+ spec.homepage = 'https://github.com/watermarkchurch/wcc-contentful/wcc-contentful'
16
19
  spec.license = 'MIT'
17
20
 
21
+ spec.metadata = {
22
+ 'documentation_uri' => "https://watermarkchurch.github.io/wcc-contentful/#{doc_version}/wcc-contentful"
23
+ }
24
+
18
25
  spec.required_ruby_version = '>= 2.3'
19
26
 
20
27
  spec.files =
@@ -24,36 +31,43 @@ Gem::Specification.new do |spec|
24
31
 
25
32
  spec.require_paths = ['lib']
26
33
 
34
+ spec.add_development_dependency 'byebug', '~> 11.0.1'
35
+ spec.add_development_dependency 'coveralls'
27
36
  spec.add_development_dependency 'dotenv', '~> 2.2'
37
+ spec.add_development_dependency 'erb_lint', '~> 0.0.26'
28
38
  spec.add_development_dependency 'httplog', '~> 1.0'
29
- spec.add_development_dependency 'rake', '~> 10.0'
39
+ spec.add_development_dependency 'rake', '~> 13.0'
30
40
  spec.add_development_dependency 'rspec', '~> 3.0'
31
- spec.add_development_dependency 'rspec_junit_formatter', '~> 0.3.0'
32
- spec.add_development_dependency 'rubocop', '~> 0.52'
33
- spec.add_development_dependency 'vcr', '~> 4.0'
41
+ spec.add_development_dependency 'rspec-instrumentation-matcher'
42
+ spec.add_development_dependency 'rspec_junit_formatter', '~> 0.4.1'
43
+ spec.add_development_dependency 'rubocop', '0.68'
44
+ spec.add_development_dependency 'simplecov', '~> 0.16.1'
45
+ spec.add_development_dependency 'vcr', '~> 5.0'
34
46
  spec.add_development_dependency 'webmock', '~> 3.0'
47
+ spec.add_development_dependency 'wisper-rspec'
35
48
 
36
49
  # Makes testing easy via `bundle exec guard`
37
50
  spec.add_development_dependency 'guard', '~> 2.14'
38
51
  spec.add_development_dependency 'guard-rspec', '~> 4.7'
39
52
  spec.add_development_dependency 'guard-rubocop', '~> 1.3.0'
53
+ spec.add_development_dependency 'guard-shell', '~> 0.7.1'
40
54
 
41
55
  # for generators
42
56
  spec.add_development_dependency 'generator_spec', '~> 0.9.4'
43
- spec.add_development_dependency 'rails', '~> 5.1'
44
- spec.add_development_dependency 'rspec-rails', '~> 3.7'
45
- spec.add_development_dependency 'sqlite3'
57
+ # spec.add_development_dependency 'rails', '~> 5.0'
58
+ # spec.add_development_dependency 'rspec-rails', '~> 3.7'
59
+ spec.add_development_dependency 'sqlite3', '~> 1.3.6'
46
60
  spec.add_development_dependency 'timecop', '~> 0.9.1'
47
61
 
48
62
  # optional dependencies
49
- spec.add_development_dependency 'contentful', '2.6.0'
50
- spec.add_development_dependency 'contentful-management', '2.0.2'
51
- spec.add_development_dependency 'graphql', '~> 1.7'
63
+ spec.add_development_dependency 'connection_pool', '~> 2.2'
64
+ spec.add_development_dependency 'faraday', '~> 0.9'
52
65
  spec.add_development_dependency 'http', '> 1.0', '< 3.0'
53
66
  spec.add_development_dependency 'pg', '~> 1.0'
54
67
  spec.add_development_dependency 'typhoeus', '~> 1.3'
55
68
 
56
69
  spec.add_dependency 'activesupport', '>= 5'
57
- spec.add_dependency 'dry-validation', '~> 0.11.1'
58
70
  spec.add_dependency 'wcc-base', '~> 0.3.1'
71
+ spec.add_dependency 'wisper', '~> 2.0.0'
59
72
  end
73
+ # rubocop:enable Metrics/LineLength