rubyblok 1.1.0 → 1.2.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: 387e9c3d34367e3cbc2f7bd45bb3c38358e203e009f0d7a79f77c2f0569e590c
4
- data.tar.gz: 13d329d7a0d6498124ee602b9252722051b4bf6f01e62186cc9c6b61fd26da69
3
+ metadata.gz: 7012bc69cffbe4334c2176ae14a987af28f812c0a5587aecf579f13ce67e8b01
4
+ data.tar.gz: ace98ab94b1c2b10526c29fe24724d3cdcef12a76a172ea832d3c8ba38986d07
5
5
  SHA512:
6
- metadata.gz: 663185c2a74652136c644306457bb110d5c67290826f100e14c4e033e543f7db68bdb24b1f8d5843d78d20e32760ddc861b8f57abedba05cf9da73092f36041f
7
- data.tar.gz: b2000c3d50bc5eef358a3b2cffc43cb2e980f89f63cf0b8a8acc7ac83bfc568e3b0440fd0029473a4f3ba792fd9e416d7e85164eafcc03a4677f81e0082239be
6
+ metadata.gz: 8f420d367f94c54c3a2c7185e3785f72cbc7445e1323205bc962b6e48565f221c52ddcc534662864b7995ec8f1587dfd1ea915d4fe4479d507e61ae771562be9
7
+ data.tar.gz: c1f1266ef1eb592eb466bca2ba321bb1a7625248b2461e5696020234014f98d4d3d4d5b5ccbb3f234bcdcbf6689ff719c8bd093bc6b82446d4bc7d0b8fc6d87f
data/.rubocop.yml CHANGED
@@ -6,146 +6,7 @@ AllCops:
6
6
  NewCops: enable
7
7
  TargetRubyVersion: 3.1.X
8
8
 
9
- Capybara/VisibilityMatcher:
10
- Enabled: false
11
-
12
9
  Documentation:
13
10
  Enabled: false
14
-
15
- Layout/HashAlignment:
16
- Enabled: false
17
11
  Layout/LineLength:
18
12
  Max: 120
19
- Layout/MultilineMethodCallIndentation:
20
- EnforcedStyle: aligned
21
- Lint/AmbiguousBlockAssociation:
22
- Exclude:
23
- - spec/**/*
24
- Lint/IneffectiveAccessModifier:
25
- Enabled: false
26
- Lint/MissingSuper:
27
- Enabled: false
28
- Lint/RedundantSplatExpansion:
29
- Enabled: false
30
- Lint/UriEscapeUnescape:
31
- Enabled: false
32
- Metrics/BlockLength:
33
- Enabled: false
34
- Metrics/ClassLength:
35
- Max: 500
36
- Metrics/ModuleLength:
37
- Max: 250
38
- Exclude:
39
- - spec/**/*
40
- Naming/AccessorMethodName:
41
- Enabled: false
42
- Naming/BlockForwarding:
43
- Enabled: false
44
- Naming/InclusiveLanguage:
45
- Enabled: false
46
- Naming/FileName:
47
- Enabled: false
48
- Naming/MemoizedInstanceVariableName:
49
- Enabled: false
50
- Naming/PredicateName:
51
- Enabled: false
52
- Naming/VariableNumber:
53
- Enabled: false
54
- Style/ClassAndModuleChildren:
55
- Enabled: false
56
- Style/Documentation:
57
- Enabled: false
58
- Style/ExponentialNotation:
59
- Enabled: false
60
- Style/FetchEnvVar:
61
- Enabled: true
62
- Exclude:
63
- - spec/**/*
64
- Style/FormatString:
65
- Enabled: false
66
- Style/FormatStringToken:
67
- Enabled: false
68
- Style/FrozenStringLiteralComment:
69
- Enabled: false
70
- Style/HashSyntax:
71
- EnforcedStyle: ruby19
72
- Style/InPatternThen:
73
- Enabled: false
74
- Style/MultilineInPatternThen:
75
- Enabled: false
76
- Style/MapToHash:
77
- Enabled: false
78
- Style/NumericLiteralPrefix:
79
- Enabled: false
80
- Style/NumericPredicate:
81
- Enabled: false
82
- Style/OpenStructUse:
83
- Enabled: false
84
- Style/SafeNavigation:
85
- Enabled: false
86
- Style/StringLiterals:
87
- EnforcedStyle: double_quotes
88
- Style/TrailingCommaInArrayLiteral:
89
- EnforcedStyleForMultiline: comma
90
- Style/TrailingCommaInHashLiteral:
91
- EnforcedStyleForMultiline: comma
92
-
93
- RSpec/AnyInstance:
94
- Enabled: false
95
- RSpec/ChangeByZero:
96
- Enabled: false
97
- RSpec/ContextWording:
98
- Enabled: false
99
- RSpec/DescribedClass:
100
- Enabled: false
101
- RSpec/DescribeClass:
102
- Enabled: false
103
- RSpec/ExampleLength:
104
- Enabled: false
105
- RSpec/ExampleWording:
106
- Enabled: false
107
- RSpec/ExpectInHook:
108
- Enabled: false
109
- RSpec/HooksBeforeExamples:
110
- Enabled: false
111
- RSpec/ImplicitBlockExpectation:
112
- Enabled: false
113
- RSpec/InstanceVariable:
114
- Exclude:
115
- - spec/views/**/*
116
- RSpec/LetBeforeExamples:
117
- Enabled: false
118
- RSpec/LetSetup:
119
- Enabled: false
120
- RSpec/MessageChain:
121
- Enabled: false
122
- RSpec/MessageSpies:
123
- Enabled: false
124
- RSpec/MultipleDescribes:
125
- Enabled: false
126
- RSpec/MultipleExpectations:
127
- Enabled: false
128
- RSpec/MultipleMemoizedHelpers:
129
- Enabled: false
130
- RSpec/NamedSubject:
131
- Enabled: false
132
- RSpec/NestedGroups:
133
- Enabled: false
134
- RSpec/RepeatedDescription:
135
- Enabled: false
136
- RSpec/RepeatedExampleGroupBody:
137
- Enabled: false
138
- RSpec/RepeatedExampleGroupDescription:
139
- Enabled: false
140
- RSpec/StubbedMock:
141
- Enabled: false
142
- RSpec/VerifiedDoubles:
143
- Enabled: false
144
- RSpec/VerifiedDoubleReference:
145
- Enabled: false
146
- RSpec/FilePath:
147
- Enabled: false
148
- RSpec/SpecFilePathFormat:
149
- Enabled: false
150
- Style/FetchEnvVar:
151
- Enabled: false
data/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.2.0] - 2024-06-17
6
+
7
+ ### Added
8
+ - Caching Storyblok images on S3
9
+ - Fragment caching of Rubyblok components
10
+
5
11
  ## [1.1.0] - 2024-05-02
6
12
 
7
13
  ### Added
data/README.md CHANGED
@@ -8,13 +8,18 @@ This integration allows you to edit your content online, preview it in real-time
8
8
  In addition, Rubyblok provides an abstraction layer and stores all your content locally, reducing data usage and enhancing performance. This enables new functionalities that leverage the local data, such as global content search, sitemaps, and listings. Ultimately, this setup increases resilience by eliminating dependency on external data sources.
9
9
 
10
10
  ## Table of Contents
11
- 1. [Installation](#installation)
12
- 2. [Getting Started](#getting-started)
13
- 3. [Rubyblok tags](#rubyblok-tags)
14
- 4. [How to Run Tests](#how-to-run-tests)
15
- 5. [Guide for Contributing](#guide-for-contributing)
16
- 6. [How to Contact Us](#how-to-contact-us)
17
- 7. [License](#license)
11
+ 1. [Installation](#installation)
12
+ 2. [Getting Started](#getting-started)
13
+ 3. [Rubyblok tags](#rubyblok-tags)
14
+ 4. [Rubyblok workflows](#rubyblok-workflows)
15
+ 5. [Rubyblok webhook](#rubyblok-webhook)
16
+ 6. [Caching storyblok images](#caching-storyblok-images)
17
+ 7. [Caching views](#caching-views)
18
+ 8. [Sitemap configuration](#sitemap-configuration)
19
+ 9. [How to Run Tests](#how-to-run-tests)
20
+ 10. [Guide for Contributing](#guide-for-contributing)
21
+ 11. [How to Contact Us](#how-to-contact-us)
22
+ 12. [License](#license)
18
23
 
19
24
  ## Installation
20
25
  Rubyblok 1.0 works with Rails 6.0 onwards. Run:
@@ -29,28 +34,14 @@ Create a new Space, in _My Spaces > Add Space_. Select the free Community plan b
29
34
 
30
35
  Get your Storyblok API token in your account, at _Storyblok Space > Settings > Access tokens_ page. Copy the "Preview" access level key.
31
36
 
32
- Add the key to your `STORYBLOK_API_TOKEN` in your env file, like this example:
37
+ Add the API token to your `config/initializers/rubyblok.rb` file:
33
38
 
34
39
  ```
35
- STORYBLOK_API_TOKEN=<your API token>
36
- ```
37
- You will also need to add the variables below to your env file:
38
- ```
39
- STORYBLOK_VERSION=draft
40
- STORYBLOK_WEBHOOK_SECRET=''
40
+ config.api_token = <your API token>
41
41
  ```
42
42
 
43
43
  ## Getting Started
44
44
 
45
- ### Necessary gems
46
- You need to add some gems to your app for Rubyblok to work correctly. Run the following commands:
47
-
48
- ```bash
49
- bundle add "storyblok"
50
- bundle add "hash_dot"
51
- bundle add "dotenv-rails"
52
- ```
53
-
54
45
  ### Hello world - Your first Rubyblok page
55
46
  Let's get started with Rubyblok by creating your first page in three steps.
56
47
  Note that it is important that you have your Storyblok space set up as described above, in the `Storyblok account and new space` section.
@@ -62,23 +53,23 @@ rails g rubyblok:install
62
53
 
63
54
  2. Now let's generate and run a migration to create the `pages` table and the `Page` model:
64
55
  ```bash
65
- rails g rubyblok:migration page
56
+ rails g rubyblok:migration PAGE
66
57
 
67
58
  rails db:migrate
68
59
  ```
69
60
 
70
61
  3. Finally, let's generate your first page:
71
62
  ```bash
72
- rails g rubyblok:hello_world page
63
+ rails g rubyblok:hello_world PAGE
73
64
  ```
74
- This will automatically create a new route, controller, views and styling for your hello world page.
65
+ This will automatically create a new `/pages` route, a `PagesController`, views and styling for your hello world page.
75
66
 
76
- For this example, go to the `rubyblok.rb` file and turn the caching option off:
67
+ For this example, go to the `config/initializers/rubyblok.rb` file and turn the caching option off:
77
68
  ```
78
69
  config.cached = false
79
70
  ```
80
71
 
81
- Now you have created your first Hello World page! Start your Rails server, access the '/pages' route and you will be able to see the page.
72
+ Now you have created your first Hello World page! Start your Rails server, access the `/pages` route and you will be able to see the page.
82
73
 
83
74
  ### Activate the visual editor
84
75
  Here are the steps to configure the visual editor at Storyblok. This allows you to see a preview of your changes in the Storyblok interface as you edit and save.
@@ -111,14 +102,6 @@ This will start a proxy server.
111
102
 
112
103
  By doing this initial setup, you are able to see your first Storyblok page inside your app and edit its content in the Storyblok admin interface 🎉
113
104
 
114
- ### Storyblok webhook
115
- The Storyblok webhook will be responsible for updating and deleting content in the local database in case of changes. [Learn more here.](https://www.storyblok.com/docs/guide/in-depth/webhooks)
116
-
117
- Generate the webhook controller:
118
- ```bash
119
- rails g rubyblok:webhook_controller storyblok_webhook
120
- ```
121
-
122
105
  ## Rubyblok tags
123
106
 
124
107
  ### rubyblok_story_tag
@@ -127,7 +110,7 @@ Use this tag to render stories:
127
110
  # Slug: full_slug of the storyblok story
128
111
  <%= rubyblok_story_tag(slug) %>
129
112
  ```
130
- The name of the storyblok blok should match the rails partial, ie the `header` storyblok blok should have a corresponding `_header.html.erb` partial in the `config.component_path` directory. The partial is called with a `blok` local variable which contains the storyblok blok properties.
113
+ The name of the storyblok block should match the rails partial, ie if the block is named `header`, it should have a corresponding `_header.html.erb` partial in the directory defined in `config.component_path`. The partial is called with a `blok` local variable which contains the storyblok block properties.
131
114
 
132
115
  ### rubyblok_content_tag
133
116
  It renders content of Text, TextArea, Markdown or Richtext storyblok fields.
@@ -165,10 +148,95 @@ Use this tag to render more than one component:
165
148
  In case you need to update the caching layer with new content added to Storyblok, run the following command:
166
149
  ```
167
150
  # Slug: full_slug of the storyblok story
168
- storyblok_story_content = Rubyblok::Services::GetStoryblokStory.call(slug: slug)
169
- <MODEL_NAME>.find_or_initialize_by(storyblok_story_slug: page)
170
- .update(storyblok_story_content:, storyblok_story_id: storyblok_story_content["id"])
151
+ storyblok_story = Rubyblok::Services::GetStoryblokStory.call(slug: slug)
152
+ <MODEL_NAME>.find_or_create(storyblok_story)
153
+ ```
154
+
155
+ ## Rubyblok workflows
156
+
157
+ ### Non-cached mode (default)
158
+ Rubyblok fetches the content via the Storyblok API and the content is not cached locally.
159
+
160
+ ### Cached mode
161
+ Rubyblok fetches the content from the local database. This mode is useful ie. if you don't want to call the API on every page request or you want to index the content locally. To enable this mode you need to set the cached feature on in the `config/initializers/rubyblok.rb` file:
162
+ ```
163
+ config.cached = true
164
+ ```
165
+
166
+ If you want to update the local cache on every page request (ie. the content is not updated via the webhook), you need to set the auto_update feature on in the `config/initializers/rubyblok.rb` file:
167
+ ```
168
+ config.auto_update = true
169
+ ```
170
+
171
+ ## Storyblok webhook
172
+ The Storyblok webhook will be responsible for updating and deleting content in the local database in case of changes. [Learn more here.](https://www.storyblok.com/docs/guide/in-depth/webhooks)
173
+
174
+ Generate the webhook controller:
175
+ ```bash
176
+ rails g rubyblok:webhook_controller STORYBLOK_WEBHOOK
177
+ ```
178
+
179
+ ## Caching storyblok images
180
+ You can store your storyblok images and videos on your own S3 storage by enabling this rubyblok feature.
181
+
182
+ 1. First, you need to run the image cache generator, which will create the model file, the uploader file and the carrierwave config file for you:
183
+ ```bash
184
+ rails g rubyblok:image_cache STORYBLOK_IMAGE
185
+ ```
186
+
187
+ 2. Add the following gems to your Gemfile:
188
+ ```
189
+ bundle add 'carrierwave'
190
+ bundle add 'fog-aws'
191
+
192
+ ```
193
+
194
+ 3. Now let's run a migration to create the `storyblok_images` table:
195
+ ```bash
196
+ rails db:migrate
197
+ ```
198
+
199
+ 4. Finally, enable the image cache feature in the `config/initializers/rubyblok.rb` file:
200
+ ```
201
+ config.use_cdn_images = true
202
+ ```
203
+
204
+ Please note that it caches only images added as an `Asset` field type in Storyblok.
205
+
206
+ ## Caching views
207
+
208
+ You can enable fragment caching on rubyblok components by setting the cache_views feature on in the `config/initializers/rubyblok.rb` file:
209
+ ```
210
+ config.cache_views = true
211
+ ```
212
+ Please note that if any of the component partials changes you need to clear the cache.
213
+
214
+ ## Sitemap configuration
215
+ You can generate a sitemap configuration for your website with the following command:
216
+ ```
217
+ rails g rubyblok:sitemap_config
218
+ ```
219
+ This generator will create a sitemap configuration for the `sitemap_generator` gem and add the gem to your Gemfile in case it's not already there.
220
+ The sitemap is generated only for cached content. Please make sure that the `cached` configuration value is `true` on `config/initializers/rubyblok.rb`.
221
+ Open `config/sitemap.rb` and add your hostname:
222
+ ```
223
+ # ...
224
+
225
+ # TODO: Configure your hostname here
226
+ SitemapGenerator::Sitemap.default_host = ''
227
+
228
+ # ...
229
+ ```
230
+ For example:
231
+ ```
232
+ # ...
233
+
234
+ # TODO: Configure your hostname here
235
+ SitemapGenerator::Sitemap.default_host = 'https://myhost.com'
236
+
237
+ # ...
171
238
  ```
239
+ In order to generate your sitemap or customize this configuration, please read [the sitemap_generator gem documentation](https://github.com/kjvarga/sitemap_generator).
172
240
 
173
241
  ## How to Run Tests
174
242
  You can run unit tests for RubyBlok with the following command:
data/Rakefile CHANGED
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/gem_tasks"
3
+ require 'bundler/gem_tasks'
4
4
  task default: %i[]
@@ -1,9 +1,11 @@
1
- require "rails/generators"
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
2
4
 
3
5
  module Rubyblok
4
6
  module Generators
5
7
  class HelloWorldGenerator < Rails::Generators::NamedBase
6
- source_root File.expand_path("../templates", __dir__)
8
+ source_root File.expand_path('../templates', __dir__)
7
9
 
8
10
  desc 'Generates a "Hello, world" Rubyblok page.'
9
11
 
@@ -16,23 +18,23 @@ module Rubyblok
16
18
  private
17
19
 
18
20
  def add_controller
19
- template("hello_world_generator/controller.rb.erb", "app/controllers/#{plural_file_name}_controller.rb")
21
+ template('hello_world_generator/controller.rb.erb', "app/controllers/#{plural_file_name}_controller.rb")
20
22
  end
21
23
 
22
24
  def add_route
23
- insert_into_file("config/routes.rb", after: "Rails.application.routes.draw do") do
25
+ insert_into_file('config/routes.rb', after: 'Rails.application.routes.draw do') do
24
26
  "\n get '/#{plural_file_name}' => '#{plural_file_name}#index'\n"
25
27
  end
26
28
  end
27
29
 
28
30
  def copy_views
29
- template("hello_world_generator/index.html.erb", "app/views/#{plural_file_name}/index.html.erb")
30
- template("hello_world_generator/_feature.html.erb", "app/views/#{destination_partial_path}/_feature.html.erb")
31
- template("hello_world_generator/_page.html.erb", "app/views/#{destination_partial_path}/_page.html.erb")
32
- template("hello_world_generator/_grid.html.erb", "app/views/#{destination_partial_path}/_grid.html.erb")
33
- template("hello_world_generator/_teaser.html.erb", "app/views/#{destination_partial_path}/_teaser.html.erb")
31
+ template('hello_world_generator/index.html.erb', "app/views/#{plural_file_name}/index.html.erb")
32
+ template('hello_world_generator/_feature.html.erb', "app/views/#{destination_partial_path}/_feature.html.erb")
33
+ template('hello_world_generator/_page.html.erb', "app/views/#{destination_partial_path}/_page.html.erb")
34
+ template('hello_world_generator/_grid.html.erb', "app/views/#{destination_partial_path}/_grid.html.erb")
35
+ template('hello_world_generator/_teaser.html.erb', "app/views/#{destination_partial_path}/_teaser.html.erb")
34
36
 
35
- copy_file("hello_world_generator/styles.css", "app/assets/stylesheets/hello.css")
37
+ copy_file('hello_world_generator/styles.css', 'app/assets/stylesheets/hello.css')
36
38
  end
37
39
 
38
40
  def destination_partial_path
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/active_record'
5
+
6
+ module Rubyblok
7
+ module Generators
8
+ class ImageCacheGenerator < ::Rails::Generators::NamedBase
9
+ include ::Rails::Generators::Migration
10
+ source_root File.expand_path('../templates', __dir__)
11
+ desc 'Installs image cache migration and model files.'
12
+
13
+ def install
14
+ if table_exist?
15
+ migration_template('migration_update_image_cache.rb.erb', "db/migrate/update_#{plural_file_name}.rb",
16
+ migration_version:)
17
+ else
18
+ migration_template('migration_create_image_cache.rb.erb', "db/migrate/create_#{plural_file_name}.rb",
19
+ migration_version:)
20
+ end
21
+
22
+ create_or_update_model
23
+ add_model_name_to_config
24
+ end
25
+
26
+ def self.next_migration_number(dirname)
27
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
28
+ end
29
+
30
+ private
31
+
32
+ def migration_version
33
+ "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
34
+ end
35
+
36
+ def table_exist?
37
+ ActiveRecord::Base.connection.table_exists?(plural_file_name.to_sym)
38
+ end
39
+
40
+ def create_or_update_model
41
+ if table_exist?
42
+ add_mixins_to_existing_model
43
+ else
44
+ generate_new_model_from_template
45
+ end
46
+ end
47
+
48
+ def add_mixins_to_existing_model
49
+ model_path = "app/models/#{file_name}.rb"
50
+ mixins_code = "\n mount_uploader :image, #{class_name}Uploader\n"
51
+
52
+ insert_into_file model_path, after: "class #{class_name} < ApplicationRecord" do
53
+ mixins_code
54
+ end
55
+
56
+ add_uploader_and_config
57
+ end
58
+
59
+ def generate_new_model_from_template
60
+ template('image_cache_model.rb.erb', "app/models/#{file_name}.rb")
61
+ add_uploader_and_config
62
+ end
63
+
64
+ def add_uploader_and_config
65
+ template('image_cache_uploader.rb.erb', "app/uploaders/#{file_name}_uploader.rb")
66
+ template('carrier_wave_config.rb.erb', 'config/initializers/carrier_wave.rb')
67
+ end
68
+
69
+ def add_model_name_to_config
70
+ model_path = 'config/initializers/rubyblok.rb'
71
+ insert_into_file model_path, after: 'config.image_model_name = "' do
72
+ class_name
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -1,15 +1,17 @@
1
- require "rails/generators"
2
- require "rails/generators/active_record"
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/active_record'
3
5
 
4
6
  module Rubyblok
5
7
  module Generators
6
8
  class InstallGenerator < Rails::Generators::Base
7
- source_root File.expand_path("../templates", __dir__)
9
+ source_root File.expand_path('../templates', __dir__)
8
10
 
9
- desc "Generates an initializer file."
11
+ desc 'Generates an initializer file.'
10
12
 
11
13
  def copy_initializer
12
- template("rubyblok.rb", "config/initializers/rubyblok.rb")
14
+ template('rubyblok.rb', 'config/initializers/rubyblok.rb')
13
15
  end
14
16
  end
15
17
  end
@@ -1,19 +1,21 @@
1
- require "rails/generators"
2
- require "rails/generators/active_record"
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/active_record'
3
5
 
4
6
  module Rubyblok
5
7
  module Generators
6
8
  class MigrationGenerator < ::Rails::Generators::NamedBase
7
9
  include ::Rails::Generators::Migration
8
- source_root File.expand_path("../templates", __dir__)
9
- desc "Installs Rubyblok migration and model files."
10
+ source_root File.expand_path('../templates', __dir__)
11
+ desc 'Installs Rubyblok migration and model files.'
10
12
 
11
13
  def install
12
14
  if table_exist?
13
- migration_template("migration_update.rb.erb", "db/migrate/update_rubyblok_#{plural_file_name}.rb",
15
+ migration_template('migration_update.rb.erb', "db/migrate/update_rubyblok_#{plural_file_name}.rb",
14
16
  migration_version:)
15
17
  else
16
- migration_template("migration_create.rb.erb", "db/migrate/create_rubyblok_#{plural_file_name}.rb",
18
+ migration_template('migration_create.rb.erb', "db/migrate/create_rubyblok_#{plural_file_name}.rb",
17
19
  migration_version:)
18
20
  end
19
21
 
@@ -53,11 +55,11 @@ module Rubyblok
53
55
  end
54
56
 
55
57
  def generate_new_model_from_template
56
- template("model.rb.erb", "app/models/#{file_name}.rb")
58
+ template('model.rb.erb', "app/models/#{file_name}.rb")
57
59
  end
58
60
 
59
61
  def add_model_name_to_config
60
- model_path = "config/initializers/rubyblok.rb"
62
+ model_path = 'config/initializers/rubyblok.rb'
61
63
  insert_into_file model_path, after: 'config.model_name = "' do
62
64
  class_name
63
65
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+
5
+ module Rubyblok
6
+ module Generators
7
+ class SitemapConfigGenerator < Rails::Generators::Base
8
+ source_root File.expand_path('../templates', __dir__)
9
+
10
+ desc 'Generates a configuration for the sitemap_generator gem'
11
+
12
+ def generate_sitemap_config
13
+ copy_config
14
+ add_sitemap_generator_gem
15
+ end
16
+
17
+ private
18
+
19
+ def copy_config
20
+ template('sitemap.rb.erb', 'config/sitemap.rb')
21
+ end
22
+
23
+ def add_sitemap_generator_gem
24
+ append_to_file(gemfile_path) { "gem 'sitemap_generator'" } if add_sitemap_generator_gem?
25
+ end
26
+
27
+ def add_sitemap_generator_gem?
28
+ !gemfile_content.match?(/\bsitemap_generator\b/)
29
+ end
30
+
31
+ def gemfile_content
32
+ File.read(gemfile_path)
33
+ end
34
+
35
+ def gemfile_path
36
+ Rails.root.join('Gemfile')
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,20 +1,22 @@
1
- require "rails/generators"
2
- require "rails/generators/active_record"
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/active_record'
3
5
 
4
6
  module Rubyblok
5
7
  module Generators
6
8
  class WebhookControllerGenerator < ::Rails::Generators::NamedBase
7
- source_root File.expand_path("../templates", __dir__)
9
+ source_root File.expand_path('../templates', __dir__)
8
10
 
9
- desc "Generates a webhook controller and a route file."
11
+ desc 'Generates a webhook controller and a route file.'
10
12
 
11
13
  def copy_initializer
12
- template("webhook_controller.rb.erb", "app/controllers/#{file_name}_controller.rb")
14
+ template('webhook_controller.rb.erb', "app/controllers/#{file_name}_controller.rb")
13
15
  end
14
16
 
15
17
  def routes_config
16
- destination_path = "config/routes.rb"
17
- insert_into_file destination_path, after: "Rails.application.routes.draw do" do
18
+ destination_path = 'config/routes.rb'
19
+ insert_into_file destination_path, after: 'Rails.application.routes.draw do' do
18
20
  routes_content
19
21
  end
20
22
  end
@@ -0,0 +1,13 @@
1
+ CarrierWave.configure do |config|
2
+ config.fog_credentials = {
3
+ provider: 'AWS',
4
+ aws_access_key_id: ENV.fetch('AWS_ACCESS_KEY_ID', nil),
5
+ aws_secret_access_key: ENV.fetch('AWS_SECRET_ACCESS_KEY', nil),
6
+ region: ENV.fetch('AWS_DEFAULT_REGION', nil),
7
+ }
8
+
9
+ config.asset_host = ENV.fetch('AWS_CLOUDFRONT_UPLOADS_HOST', nil)
10
+ config.fog_directory = ENV.fetch('AWS_UPLOADS_DIRECTORY', nil)
11
+ config.fog_public = true
12
+ config.fog_attributes = { cache_control: "public, max-age=#{365.days.to_i}" }
13
+ end
@@ -0,0 +1,3 @@
1
+ class <%= class_name %> < ApplicationRecord
2
+ mount_uploader :image, <%= class_name %>Uploader
3
+ end
@@ -0,0 +1,12 @@
1
+ class <%= class_name %>Uploader < CarrierWave::Uploader::Base
2
+ storage :fog
3
+
4
+ def store_dir
5
+ model_name = model.model_name.singular
6
+ "uploads/#{model_name}/#{mounted_as}/#{model.id}"
7
+ end
8
+
9
+ def extension_allowlist
10
+ %w[gif jpeg jpg png svg mp4]
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ class Create<%= class_name.pluralize %> < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :<%= table_name %> do |t|
4
+ t.string :original_image_url
5
+ t.string :image
6
+
7
+ t.timestamps
8
+ end
9
+
10
+ add_index :<%= table_name %>, :original_image_url, unique: true
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ class Update<%= class_name.pluralize %> < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ add_column :<%= table_name %>, :original_image_url, :string
4
+ add_column :<%= table_name %>, :image, :string
5
+
6
+ add_index :<%= table_name %>, :storyblok_story_slug, unique: true
7
+ end
8
+ end
@@ -1,12 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Rubyblok.configure do |config|
4
- config.cached = true
4
+ # Storyblok API token
5
+ config.api_token = ENV.fetch('STORYBLOK_API_TOKEN', nil)
5
6
 
6
- config.api_token = ENV["STORYBLOK_API_TOKEN"]
7
- config.version = ENV["STORYBLOK_VERSION"]
8
- config.webhook_secret = ENV["STORYBLOK_WEBHOOK_SECRET"]
7
+ # Storyblok story version (draft or published)
8
+ config.version = ENV.fetch('STORYBLOK_VERSION', 'draft')
9
9
 
10
- config.model_name = ""
11
- config.component_path = "shared/storyblok"
10
+ # Name of the model that stores Storyblok stories
11
+ config.model_name = ''
12
+
13
+ # Directory contains rubyblok partials (relative to app/views)
14
+ config.component_path = 'shared/storyblok'
15
+
16
+ # Feature flag that enables cached mode
17
+ config.cached = ActiveModel::Type::Boolean.new.cast(ENV.fetch('STORYBLOK_CACHED', false))
18
+
19
+ # Feature flag that enables auto update of local cache
20
+ config.auto_update = ActiveModel::Type::Boolean.new.cast(ENV.fetch('STORYBLOK_AUTOUPDATE', false))
21
+
22
+ # Feature flag that enables caching storyblok images
23
+ config.use_cdn_images = ActiveModel::Type::Boolean.new.cast(ENV.fetch('STORYBLOK_CDN_IMAGES', false))
24
+
25
+ # Name of the model that stores Storyblok cached images
26
+ config.image_model_name = ''
27
+
28
+ # Storyblok webhook token
29
+ config.webhook_secret = ENV.fetch('STORYBLOK_WEBHOOK_SECRET', nil)
30
+
31
+ # Feature flag that enables caching view fragments
32
+ config.cache_views = ActiveModel::Type::Boolean.new.cast(ENV.fetch('STORYBLOK_CACHE_VIEWS', false))
12
33
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sitemap_generator'
4
+
5
+ # TODO: Configure your hostname here
6
+ SitemapGenerator::Sitemap.default_host = ''
7
+
8
+ if Rubyblok.configuration.cached
9
+ SitemapGenerator::Sitemap.create do
10
+ model = Rubyblok.configuration.model_name.classify.constantize
11
+ pages = model.all.select { |page| page.storyblok_story_content.dig('content', 'no_index') == false }
12
+ pages.each { |page| add(page.storyblok_story_slug, lastmod: page.updated_at) }
13
+ end
14
+ end
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rubyblok
2
4
  class Configuration
3
- attr_accessor :api_token, :version, :component_path, :webhook_secret, :model_name, :cached
4
-
5
- attr_writer :property
5
+ attr_accessor :api_token, :auto_update, :cached, :cache_views, :component_path, :image_model_name,
6
+ :model_name, :use_cdn_images, :version, :webhook_secret
6
7
  end
7
8
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module StoryblokHelper
2
4
  def rubyblok_content_tag(content)
3
5
  return if content.blank?
@@ -10,8 +12,17 @@ module StoryblokHelper
10
12
  end
11
13
 
12
14
  def rubyblok_story_tag(slug)
13
- content = get_story(slug)
14
- rubyblok_component_tag(partial: content.component, blok: content)
15
+ story = get_story(slug)
16
+ content = story['content'].to_dot
17
+
18
+ template = <<-ERB
19
+ <% cache_key = "rubyblok/#{story['id']}-#{Digest::SHA1.hexdigest(story.to_json)}" %>
20
+ <% cache_if use_cache, cache_key do %>
21
+ <%= rubyblok_component_tag(partial: content.component, blok: content) %>
22
+ <% end %>
23
+ ERB
24
+
25
+ render_inline_partial template:, locals: { content:, use_cache: Rubyblok.configuration.cache_views }
15
26
  end
16
27
 
17
28
  def rubyblok_component_tag(blok:, partial: blok.component)
@@ -37,34 +48,48 @@ module StoryblokHelper
37
48
  render_inline_partial template:, locals: { bloks: }
38
49
  end
39
50
 
51
+ def get_story_content(slug)
52
+ get_story(slug)['content'].to_dot
53
+ end
54
+
55
+ private
56
+
40
57
  def get_story(slug)
41
- if use_cache?
42
- get_story_via_cache(slug)["content"].to_dot
58
+ return get_story_via_api(slug) unless cached?
59
+
60
+ if update_storyblok?
61
+ get_story_via_api(slug, save: true)
43
62
  else
44
- get_story_via_api(slug)["content"].to_dot
63
+ get_story_via_cache(slug)
45
64
  end
46
65
  end
47
66
 
48
- private
49
-
50
- def get_story_via_api(slug)
51
- Rubyblok::Services::GetStoryblokStory.call(slug:)
67
+ def get_story_via_api(slug, save: false)
68
+ story = Rubyblok::Services::GetStoryblokStory.call(slug:)
69
+ replace_storyblok_url(story).tap do |storyblok_story|
70
+ model.find_or_create(storyblok_story) if save
71
+ end
52
72
  end
53
73
 
54
74
  def get_story_via_cache(slug)
55
- Rubyblok.configuration.model_name.classify.constantize.fetch_content(slug)
75
+ model.fetch_content(slug)
76
+ end
77
+
78
+ def replace_storyblok_url(story)
79
+ Rubyblok::Services::ReplaceStoryblokUrl.call(story:)
80
+ end
81
+
82
+ def model
83
+ Rubyblok.configuration.model_name.classify.constantize
56
84
  end
57
85
 
58
- def rich_text_renderer # rubocop:disable Metrics/MethodLength
86
+ def rich_text_renderer
59
87
  ctx = {}
60
88
  path = component_path
61
89
  @rich_text_renderer ||=
62
90
  Storyblok::Richtext::HtmlRenderer.new.tap do |html_renderer|
63
91
  html_renderer.set_component_resolver(lambda { |component, data|
64
- ApplicationController.render(
65
- partial: "#{path}/#{component}",
66
- locals: ctx.merge(blok: data)
67
- )
92
+ ApplicationController.render(partial: "#{path}/#{component}", locals: ctx.merge(blok: data))
68
93
  })
69
94
  end
70
95
  end
@@ -75,7 +100,7 @@ module StoryblokHelper
75
100
 
76
101
  # rubocop:disable Rails/OutputSafety
77
102
  def rubyblok_editable_tag(component)
78
- component["_editable"]&.html_safe
103
+ component['_editable']&.html_safe
79
104
  end
80
105
  # rubocop:enable Rails/OutputSafety
81
106
 
@@ -91,7 +116,15 @@ module StoryblokHelper
91
116
  ApplicationController.render inline: template, locals:
92
117
  end
93
118
 
94
- def use_cache?
119
+ def cached?
95
120
  Rubyblok.configuration.cached
96
121
  end
122
+
123
+ def auto_update?
124
+ Rubyblok.configuration.auto_update
125
+ end
126
+
127
+ def update_storyblok?
128
+ auto_update? || params[:storyblok] == 'update'
129
+ end
97
130
  end
@@ -1,4 +1,6 @@
1
- require "active_support/concern"
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
2
4
 
3
5
  module Rubyblok
4
6
  module Mixins
@@ -17,6 +19,16 @@ module Rubyblok
17
19
  def fetch_content(slug)
18
20
  find_by(storyblok_story_slug: slug)&.storyblok_story_content
19
21
  end
22
+
23
+ def find_or_create(story)
24
+ return if story.blank?
25
+
26
+ find_or_initialize_by(storyblok_story_id: story['id']).tap do |model|
27
+ model.storyblok_story_content = story
28
+ model.storyblok_story_slug = story['full_slug']
29
+ model.save
30
+ end
31
+ end
20
32
  end
21
33
  end
22
34
  end
@@ -1,4 +1,6 @@
1
- require "active_support/concern"
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
2
4
 
3
5
  module Rubyblok
4
6
  module Mixins
@@ -11,11 +13,11 @@ module Rubyblok
11
13
  def create
12
14
  payload = JSON.parse(request.raw_post)
13
15
 
14
- storyblok_story_content = Rubyblok::Services::GetStoryblokStory.call(slug: payload["story_id"])
15
- model.find_or_initialize_by(storyblok_story_id: payload["story_id"])
16
- .update(storyblok_story_content:, storyblok_story_slug: storyblok_story_content["full_slug"])
16
+ storyblok_story = Rubyblok::Services::GetStoryblokStory.call(slug: payload['story_id'])
17
+ storyblok_story = Rubyblok::Services::ReplaceStoryblokUrl.call(story: storyblok_story)
18
+ model.find_or_create(storyblok_story)
17
19
 
18
- render json: { success: true }
20
+ render(json: { success: true })
19
21
  end
20
22
 
21
23
  private
@@ -1,8 +1,10 @@
1
- require_relative "helpers/storyblok_helper"
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helpers/storyblok_helper'
2
4
 
3
5
  module Rubyblok
4
6
  class Railtie < Rails::Railtie
5
- initializer "rubyblok.storyblok_helper" do
7
+ initializer 'rubyblok.storyblok_helper' do
6
8
  ActiveSupport.on_load(:action_view) { include StoryblokHelper }
7
9
  end
8
10
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rubyblok
2
4
  module Services
3
5
  class GetStoryblokStory
@@ -15,15 +17,15 @@ module Rubyblok
15
17
 
16
18
  private
17
19
 
18
- def storyblok_client(version: "draft") # rubocop:disable Lint/UnusedMethodArgument
20
+ def storyblok_client
19
21
  Storyblok::Client.new(
20
22
  token: Rubyblok.configuration.api_token,
21
23
  version: Rubyblok.configuration.version
22
24
  )
23
25
  end
24
26
 
25
- def get_story
26
- storyblok_client.story(@slug)["data"]["story"]
27
+ def get_story # rubocop:disable Naming/AccessorMethodName
28
+ storyblok_client.story(@slug)['data']['story']
27
29
  end
28
30
  end
29
31
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubyblok
4
+ module Services
5
+ class ReplaceStoryblokUrl
6
+ attr_reader :story
7
+
8
+ def self.call(story:)
9
+ new(story:).call
10
+ end
11
+
12
+ def initialize(story:)
13
+ @story = story
14
+ end
15
+
16
+ def call
17
+ return story unless use_cdn_images?
18
+
19
+ image_fields = find_image_fields(story['content'])
20
+ image_fields.each do |image_field|
21
+ replace_url(image_field)
22
+ end
23
+ story
24
+ end
25
+
26
+ private
27
+
28
+ # It finds image fields based on their "fieldtype"=>"asset" attribute.
29
+ # Please note that it caches only images added as an 'Asset' field type in Storyblok.
30
+ def find_image_fields(content)
31
+ return [content] if image_field?(content)
32
+
33
+ content.values.each_with_object([]) do |field_value, image_fields|
34
+ case field_value
35
+ when Array
36
+ field_value.each { |component| image_fields << find_image_fields(component) }
37
+ when Hash
38
+ image_fields << find_image_fields(field_value)
39
+ end
40
+ end.flatten
41
+ end
42
+
43
+ def image_field?(field)
44
+ field['fieldtype'] == 'asset'
45
+ end
46
+
47
+ def replace_url(image_field)
48
+ url = image_field['filename']
49
+ image =
50
+ image_model.create_with(remote_image_url: url).find_or_create_by(original_image_url: url)
51
+
52
+ image_field['filename'] = image.image.url if image.persisted?
53
+ end
54
+
55
+ def image_model
56
+ Rubyblok.configuration.image_model_name.classify.constantize
57
+ end
58
+
59
+ def use_cdn_images?
60
+ Rubyblok.configuration.use_cdn_images
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rubyblok
4
- VERSION = "1.1.0"
4
+ VERSION = '1.2.0'
5
5
  end
data/lib/rubyblok.rb CHANGED
@@ -1,20 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "storyblok"
4
- require "redcarpet"
5
- require "hash_dot"
3
+ require 'storyblok'
4
+ require 'redcarpet'
5
+ require 'hash_dot'
6
6
 
7
- require_relative "rubyblok/helpers/storyblok_helper"
8
- require_relative "rubyblok/version"
9
- require_relative "rubyblok/configuration"
10
- require_relative "rubyblok/railtie" if defined?(Rails)
11
- require_relative "rubyblok/services/get_storyblok_story"
12
- require_relative "rubyblok/mixins/model"
13
- require_relative "rubyblok/mixins/webhook"
14
- require_relative "generators/rubyblok/migration_generator"
15
- require_relative "generators/rubyblok/install_generator"
16
- require_relative "generators/rubyblok/webhook_controller_generator"
17
- require_relative "generators/rubyblok/hello_world_generator"
7
+ require_relative 'rubyblok/helpers/storyblok_helper'
8
+ require_relative 'rubyblok/version'
9
+ require_relative 'rubyblok/configuration'
10
+ require_relative 'rubyblok/railtie' if defined?(Rails)
11
+ require_relative 'rubyblok/services/get_storyblok_story'
12
+ require_relative 'rubyblok/services/replace_storyblok_url'
13
+ require_relative 'rubyblok/mixins/model'
14
+ require_relative 'rubyblok/mixins/webhook'
15
+ require_relative 'generators/rubyblok/migration_generator'
16
+ require_relative 'generators/rubyblok/install_generator'
17
+ require_relative 'generators/rubyblok/webhook_controller_generator'
18
+ require_relative 'generators/rubyblok/hello_world_generator'
19
+ require_relative 'generators/rubyblok/sitemap_config_generator'
20
+ require_relative 'generators/rubyblok/image_cache_generator'
18
21
 
19
22
  module Rubyblok
20
23
  def self.configuration
data/rubyblok.gemspec CHANGED
@@ -1,20 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "lib/rubyblok/version"
3
+ require_relative 'lib/rubyblok/version'
4
4
 
5
5
  Gem::Specification.new do |spec|
6
- spec.name = "rubyblok"
6
+ spec.name = 'rubyblok'
7
7
  spec.version = Rubyblok::VERSION
8
- spec.license = "MIT"
9
- spec.authors = ["100 Starlings"]
10
- spec.email = ["rubyblok@100starlings.com"]
8
+ spec.license = 'MIT'
9
+ spec.authors = ['100 Starlings']
10
+ spec.email = ['rubyblok@100starlings.com']
11
11
 
12
- spec.summary = "Simple Storyblok CMS integration for Rails"
13
- spec.homepage = "http://www.rubyblok.com"
14
- spec.required_ruby_version = ">= 3.1.0"
12
+ spec.summary = 'Simple Storyblok CMS integration for Rails'
13
+ spec.homepage = 'http://www.rubyblok.com'
14
+ spec.required_ruby_version = '>= 3.1.0'
15
15
 
16
- spec.metadata["homepage_uri"] = spec.homepage
17
- spec.metadata["source_code_uri"] = "https://github.com/100Starlings/rubyblok"
16
+ spec.metadata['homepage_uri'] = spec.homepage
17
+ spec.metadata['source_code_uri'] = 'https://github.com/100Starlings/rubyblok'
18
18
 
19
19
  spec.files = Dir.chdir(__dir__) do
20
20
  `git ls-files -z`.split("\x0").reject do |f|
@@ -22,14 +22,14 @@ Gem::Specification.new do |spec|
22
22
  f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
23
23
  end
24
24
  end
25
- spec.bindir = "exe"
25
+ spec.bindir = 'exe'
26
26
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
27
- spec.require_paths = ["lib"]
27
+ spec.require_paths = ['lib']
28
28
 
29
- spec.add_dependency "hash_dot", "~> 2.5"
30
- spec.add_dependency "railties", "~> 7.1"
31
- spec.add_dependency "redcarpet", "~> 3.6.0"
32
- spec.add_dependency "storyblok", "~> 3.2.0"
29
+ spec.add_dependency 'hash_dot', '~> 2.5'
30
+ spec.add_dependency 'railties', '~> 7.1'
31
+ spec.add_dependency 'redcarpet', '~> 3.6.0'
32
+ spec.add_dependency 'storyblok', '~> 3.2.0'
33
33
 
34
- spec.metadata["rubygems_mfa_required"] = "true"
34
+ spec.metadata['rubygems_mfa_required'] = 'true'
35
35
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubyblok
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - 100 Starlings
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-05-07 00:00:00.000000000 Z
11
+ date: 2024-06-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hash_dot
@@ -80,9 +80,12 @@ files:
80
80
  - README.md
81
81
  - Rakefile
82
82
  - lib/generators/rubyblok/hello_world_generator.rb
83
+ - lib/generators/rubyblok/image_cache_generator.rb
83
84
  - lib/generators/rubyblok/install_generator.rb
84
85
  - lib/generators/rubyblok/migration_generator.rb
86
+ - lib/generators/rubyblok/sitemap_config_generator.rb
85
87
  - lib/generators/rubyblok/webhook_controller_generator.rb
88
+ - lib/generators/templates/carrier_wave_config.rb.erb
86
89
  - lib/generators/templates/hello_world_generator/_feature.html.erb
87
90
  - lib/generators/templates/hello_world_generator/_grid.html.erb
88
91
  - lib/generators/templates/hello_world_generator/_page.html.erb
@@ -90,10 +93,15 @@ files:
90
93
  - lib/generators/templates/hello_world_generator/controller.rb.erb
91
94
  - lib/generators/templates/hello_world_generator/index.html.erb
92
95
  - lib/generators/templates/hello_world_generator/styles.css
96
+ - lib/generators/templates/image_cache_model.rb.erb
97
+ - lib/generators/templates/image_cache_uploader.rb.erb
93
98
  - lib/generators/templates/migration_create.rb.erb
99
+ - lib/generators/templates/migration_create_image_cache.rb.erb
94
100
  - lib/generators/templates/migration_update.rb.erb
101
+ - lib/generators/templates/migration_update_image_cache.rb.erb
95
102
  - lib/generators/templates/model.rb.erb
96
103
  - lib/generators/templates/rubyblok.rb
104
+ - lib/generators/templates/sitemap.rb.erb
97
105
  - lib/generators/templates/webhook_controller.rb.erb
98
106
  - lib/rubyblok.rb
99
107
  - lib/rubyblok/configuration.rb
@@ -102,6 +110,7 @@ files:
102
110
  - lib/rubyblok/mixins/webhook.rb
103
111
  - lib/rubyblok/railtie.rb
104
112
  - lib/rubyblok/services/get_storyblok_story.rb
113
+ - lib/rubyblok/services/replace_storyblok_url.rb
105
114
  - lib/rubyblok/version.rb
106
115
  - rubyblok.gemspec
107
116
  homepage: http://www.rubyblok.com