renderful 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +4 -1
- data/.gitignore +1 -0
- data/.rubocop-https---relaxed-ruby-style-rubocop-yml +3 -24
- data/CHANGELOG.md +36 -12
- data/README.md +99 -158
- data/lib/renderful.rb +10 -7
- data/lib/renderful/cache/base.rb +31 -0
- data/lib/renderful/cache/null.rb +31 -0
- data/lib/renderful/cache/redis.rb +17 -4
- data/lib/renderful/client.rb +30 -19
- data/lib/renderful/component/base.rb +18 -0
- data/lib/renderful/content_entry.rb +24 -0
- data/lib/renderful/error/base.rb +7 -0
- data/lib/renderful/error/entry_not_found_error.rb +15 -0
- data/lib/renderful/error/no_component_error.rb +15 -0
- data/lib/renderful/provider/base.rb +25 -0
- data/lib/renderful/provider/contentful.rb +55 -0
- data/lib/renderful/provider/prismic.rb +46 -0
- data/lib/renderful/version.rb +1 -1
- data/renderful.gemspec +9 -3
- metadata +131 -28
- data/lib/renderful/cache.rb +0 -21
- data/lib/renderful/cache_invalidator.rb +0 -26
- data/lib/renderful/no_renderer_error.rb +0 -13
- data/lib/renderful/renderer.rb +0 -36
- data/lib/renderful/renderer/rails.rb +0 -29
data/lib/renderful.rb
CHANGED
@@ -1,14 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
4
|
-
|
5
|
-
require 'renderful/
|
6
|
-
require 'renderful/cache'
|
3
|
+
require 'renderful/error/base'
|
4
|
+
require 'renderful/error/entry_not_found_error'
|
5
|
+
require 'renderful/error/no_component_error'
|
6
|
+
require 'renderful/cache/base'
|
7
7
|
require 'renderful/cache/redis'
|
8
|
-
require 'renderful/
|
8
|
+
require 'renderful/cache/null'
|
9
|
+
require 'renderful/content_entry'
|
10
|
+
require 'renderful/provider/base'
|
11
|
+
require 'renderful/provider/contentful'
|
12
|
+
require 'renderful/provider/prismic'
|
9
13
|
require 'renderful/client'
|
10
|
-
require 'renderful/
|
11
|
-
require 'renderful/renderer/rails'
|
14
|
+
require 'renderful/component/base'
|
12
15
|
require 'renderful/version'
|
13
16
|
|
14
17
|
module Renderful
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Renderful
|
4
|
+
module Cache
|
5
|
+
class Base
|
6
|
+
def exist?(_key)
|
7
|
+
raise NotImplementedError
|
8
|
+
end
|
9
|
+
|
10
|
+
def read(_key)
|
11
|
+
raise NotImplementedError
|
12
|
+
end
|
13
|
+
|
14
|
+
def write(_key, _value)
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete(*_keys)
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
21
|
+
|
22
|
+
def delete_matched(_pattern)
|
23
|
+
raise NotImplementedError
|
24
|
+
end
|
25
|
+
|
26
|
+
def fetch(_key)
|
27
|
+
raise NotImplementedError
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Renderful
|
4
|
+
module Cache
|
5
|
+
class Null < Base
|
6
|
+
def exist?(_key)
|
7
|
+
false
|
8
|
+
end
|
9
|
+
|
10
|
+
def read(_key)
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def write(key, value)
|
15
|
+
# noop
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete(*keys)
|
19
|
+
# noop
|
20
|
+
end
|
21
|
+
|
22
|
+
def delete_matched(pattern)
|
23
|
+
# noop
|
24
|
+
end
|
25
|
+
|
26
|
+
def fetch(_key)
|
27
|
+
yield
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Renderful
|
4
|
-
|
5
|
-
class Redis <
|
4
|
+
module Cache
|
5
|
+
class Redis < Base
|
6
6
|
attr_reader :redis
|
7
7
|
|
8
8
|
def initialize(redis)
|
@@ -21,8 +21,21 @@ module Renderful
|
|
21
21
|
redis.set(key, value)
|
22
22
|
end
|
23
23
|
|
24
|
-
def delete(
|
25
|
-
redis.del(
|
24
|
+
def delete(*keys)
|
25
|
+
redis.del(*keys)
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete_matched(pattern)
|
29
|
+
keys = redis.scan_each(match: pattern).to_a
|
30
|
+
delete(*keys)
|
31
|
+
end
|
32
|
+
|
33
|
+
def fetch(key)
|
34
|
+
return read(key) if exist?(key)
|
35
|
+
|
36
|
+
yield.tap do |value|
|
37
|
+
write(key, value)
|
38
|
+
end
|
26
39
|
end
|
27
40
|
end
|
28
41
|
end
|
data/lib/renderful/client.rb
CHANGED
@@ -2,34 +2,45 @@
|
|
2
2
|
|
3
3
|
module Renderful
|
4
4
|
class Client
|
5
|
-
attr_reader :
|
5
|
+
attr_reader :provider, :components, :cache
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
@
|
9
|
-
@
|
7
|
+
def initialize(provider:, components:, cache: Cache::Null.new)
|
8
|
+
@provider = provider
|
9
|
+
@components = components
|
10
10
|
@cache = cache
|
11
11
|
end
|
12
12
|
|
13
|
-
def render(
|
14
|
-
|
15
|
-
|
13
|
+
def render(entry_id, options = {})
|
14
|
+
cache.fetch(ContentEntry.build_cache_key(provider, id: entry_id)) do
|
15
|
+
content_entry = provider.find_entry(entry_id)
|
16
|
+
component = component_for_entry(content_entry)
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
if component.respond_to?(:render_in)
|
19
|
+
component.render_in(options.fetch(:view_context))
|
20
|
+
else
|
21
|
+
component.render
|
22
|
+
end
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
"contentful/#{entry.fetch(:content_type_id)}/#{entry.fetch(:entry_id)}"
|
26
|
+
def invalidate_cache_from_webhook(body)
|
27
|
+
result = provider.cache_keys_to_invalidate(body)
|
28
|
+
|
29
|
+
cache.delete(*result[:keys])
|
30
|
+
|
31
|
+
result[:patterns].each do |pattern|
|
32
|
+
cache.delete_matched(pattern)
|
32
33
|
end
|
33
34
|
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def component_klass_for_entry(content_entry)
|
39
|
+
components[content_entry.content_type] || fail(Error::NoComponentError, content_entry)
|
40
|
+
end
|
41
|
+
|
42
|
+
def component_for_entry(content_entry)
|
43
|
+
component_klass_for_entry(content_entry).new(entry: content_entry, client: self)
|
44
|
+
end
|
34
45
|
end
|
35
46
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Renderful
|
4
|
+
module Component
|
5
|
+
class Base
|
6
|
+
attr_reader :entry, :client
|
7
|
+
|
8
|
+
def initialize(entry:, client:)
|
9
|
+
@entry = entry
|
10
|
+
@client = client
|
11
|
+
end
|
12
|
+
|
13
|
+
def render
|
14
|
+
fail NotImplementedError
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Renderful
|
4
|
+
class ContentEntry
|
5
|
+
attr_reader :provider, :id, :content_type, :fields
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def build_cache_key(provider, id: nil)
|
9
|
+
['renderful', provider.cache_prefix, id || '*'].join('/')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(provider:, id:, content_type: nil, fields: {})
|
14
|
+
@provider = provider
|
15
|
+
@id = id
|
16
|
+
@content_type = content_type
|
17
|
+
@fields = fields
|
18
|
+
end
|
19
|
+
|
20
|
+
def cache_key
|
21
|
+
self.class.build_cache_key(provider, id: id)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Renderful
|
4
|
+
module Error
|
5
|
+
class EntryNotFoundError < Base
|
6
|
+
attr_reader :entry_id
|
7
|
+
|
8
|
+
def initialize(entry_id, *args)
|
9
|
+
@entry_id = entry_id
|
10
|
+
|
11
|
+
super "Cannot find entry #{@entry_id}", *args
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Renderful
|
4
|
+
module Error
|
5
|
+
class NoComponentError < Base
|
6
|
+
attr_reader :entry
|
7
|
+
|
8
|
+
def initialize(entry, *args)
|
9
|
+
@entry = entry
|
10
|
+
|
11
|
+
super "Cannot find component for content type #{entry.content_type}", *args
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Renderful
|
4
|
+
module Provider
|
5
|
+
class Base
|
6
|
+
attr_reader :options
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def cache_prefix
|
13
|
+
fail NotImplementedError
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_entry(_entry_id)
|
17
|
+
fail NotImplementedError
|
18
|
+
end
|
19
|
+
|
20
|
+
def cache_keys_to_invalidate(_webhook_body)
|
21
|
+
fail NotImplementedError
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Renderful
|
4
|
+
module Provider
|
5
|
+
class Contentful < Base
|
6
|
+
def initialize(options)
|
7
|
+
super
|
8
|
+
|
9
|
+
fail ArgumentError, 'contentful option is required!' unless contentful
|
10
|
+
end
|
11
|
+
|
12
|
+
def cache_prefix
|
13
|
+
:contentful
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_entry(entry_id)
|
17
|
+
entry = contentful.entry(entry_id)
|
18
|
+
raise Error::EntryNotFoundError, entry_id unless entry
|
19
|
+
|
20
|
+
wrap_entry(entry)
|
21
|
+
end
|
22
|
+
|
23
|
+
def cache_keys_to_invalidate(webhook_body)
|
24
|
+
params = webhook_body.is_a?(String) ? JSON.parse(webhook_body) : webhook_body
|
25
|
+
|
26
|
+
keys_to_invalidate = [ContentEntry.build_cache_key(self, id: params['sys']['id'])]
|
27
|
+
keys_to_invalidate += contentful.entries(links_to_entry: params['sys']['id']).map do |entry|
|
28
|
+
ContentEntry.build_cache_key(self, id: entry.id)
|
29
|
+
end
|
30
|
+
|
31
|
+
{
|
32
|
+
keys: keys_to_invalidate,
|
33
|
+
patterns: [],
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def wrap_entry(entry)
|
40
|
+
ContentEntry.new(
|
41
|
+
provider: self,
|
42
|
+
id: entry.id,
|
43
|
+
content_type: entry.content_type.id,
|
44
|
+
fields: entry.fields,
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def entries_linking_to(entry_id); end
|
49
|
+
|
50
|
+
def contentful
|
51
|
+
options[:contentful]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Renderful
|
4
|
+
module Provider
|
5
|
+
class Prismic < Base
|
6
|
+
def initialize(options)
|
7
|
+
super
|
8
|
+
|
9
|
+
fail ArgumentError, 'prismic option is required!' unless prismic
|
10
|
+
end
|
11
|
+
|
12
|
+
def cache_prefix
|
13
|
+
:prismic
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_entry(entry_id)
|
17
|
+
entry = prismic.getByID(entry_id)
|
18
|
+
raise Error::EntryNotFoundError, entry_id unless entry
|
19
|
+
|
20
|
+
wrap_entry(entry)
|
21
|
+
end
|
22
|
+
|
23
|
+
def cache_keys_to_invalidate(_webhook_body)
|
24
|
+
{
|
25
|
+
keys: [],
|
26
|
+
patterns: ['renderful/prismic/*'],
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def wrap_entry(entry)
|
33
|
+
ContentEntry.new(
|
34
|
+
provider: self,
|
35
|
+
id: entry.id,
|
36
|
+
content_type: entry.type,
|
37
|
+
fields: entry.fragments,
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def prismic
|
42
|
+
options[:prismic]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/renderful/version.rb
CHANGED
data/renderful.gemspec
CHANGED
@@ -27,16 +27,22 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
28
|
spec.require_paths = ['lib']
|
29
29
|
|
30
|
-
spec.add_dependency 'contentful', '~> 2.11'
|
31
|
-
spec.add_dependency 'rails', ['>= 5.0.0', '< 7']
|
32
|
-
|
33
30
|
spec.add_development_dependency 'appraisal', '~> 2.2'
|
34
31
|
spec.add_development_dependency 'bundler', '~> 2.1'
|
32
|
+
spec.add_development_dependency 'capybara', '~> 3.32'
|
35
33
|
spec.add_development_dependency 'combustion', '~> 1.1'
|
34
|
+
spec.add_development_dependency 'contentful', '~> 2.11'
|
35
|
+
spec.add_development_dependency 'gem-release', '~> 2.1'
|
36
|
+
spec.add_development_dependency 'github_changelog_generator', '~> 1.15'
|
37
|
+
spec.add_development_dependency 'prismic.io', '~> 1.7'
|
38
|
+
spec.add_development_dependency 'rails', ['>= 5.0.0', '< 7']
|
36
39
|
spec.add_development_dependency 'rake', '~> 10.0'
|
37
40
|
spec.add_development_dependency 'redis', '~> 4.1'
|
38
41
|
spec.add_development_dependency 'rspec-rails', '~> 4.0.0.beta4'
|
39
42
|
spec.add_development_dependency 'rspec_junit_formatter', '~> 0.4.1'
|
40
43
|
spec.add_development_dependency 'rubocop', '~> 0.79.0'
|
41
44
|
spec.add_development_dependency 'rubocop-rspec', '~> 1.37'
|
45
|
+
spec.add_development_dependency 'vcr', '~> 5.1'
|
46
|
+
spec.add_development_dependency 'view_component', '~> 2.2'
|
47
|
+
spec.add_development_dependency 'webmock', '~> 3.8'
|
42
48
|
end
|