rubyblok 1.1.0 → 1.3.0

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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -137
  3. data/CHANGELOG.md +13 -0
  4. data/README.md +130 -41
  5. data/Rakefile +1 -1
  6. data/lib/generators/rubyblok/hello_world_generator.rb +12 -10
  7. data/lib/generators/rubyblok/image_cache_generator.rb +77 -0
  8. data/lib/generators/rubyblok/install_generator.rb +7 -5
  9. data/lib/generators/rubyblok/migration_generator.rb +10 -8
  10. data/lib/generators/rubyblok/sitemap_config_generator.rb +40 -0
  11. data/lib/generators/rubyblok/webhook_controller_generator.rb +9 -7
  12. data/lib/generators/templates/carrier_wave_config.rb.erb +13 -0
  13. data/lib/generators/templates/image_cache_model.rb.erb +3 -0
  14. data/lib/generators/templates/image_cache_uploader.rb.erb +12 -0
  15. data/lib/generators/templates/migration_create_image_cache.rb.erb +12 -0
  16. data/lib/generators/templates/migration_update_image_cache.rb.erb +8 -0
  17. data/lib/generators/templates/rubyblok.rb +27 -6
  18. data/lib/generators/templates/sitemap.rb.erb +14 -0
  19. data/lib/rubyblok/configuration.rb +4 -3
  20. data/lib/rubyblok/helpers/storyblok_helper.rb +56 -17
  21. data/lib/rubyblok/mixins/model.rb +13 -1
  22. data/lib/rubyblok/mixins/model_cache_class.rb +15 -0
  23. data/lib/rubyblok/mixins/webhook.rb +32 -9
  24. data/lib/rubyblok/railtie.rb +8 -2
  25. data/lib/rubyblok/services/get_storyblok_story.rb +5 -3
  26. data/lib/rubyblok/services/replace_storyblok_url.rb +64 -0
  27. data/lib/rubyblok/version.rb +1 -1
  28. data/lib/rubyblok.rb +18 -15
  29. data/lib/tasks/rubyblok/create_or_update_content.rake +36 -0
  30. data/rubyblok.gemspec +17 -17
  31. metadata +13 -2
@@ -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,4 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubyblok/mixins/model_cache_class'
4
+
1
5
  module StoryblokHelper
6
+ include Rubyblok::Mixins::ModelCacheClass
7
+
2
8
  def rubyblok_content_tag(content)
3
9
  return if content.blank?
4
10
 
@@ -10,14 +16,29 @@ module StoryblokHelper
10
16
  end
11
17
 
12
18
  def rubyblok_story_tag(slug)
13
- content = get_story(slug)
14
- rubyblok_component_tag(partial: content.component, blok: content)
19
+ story = get_story(slug)
20
+ content = story['content'].to_dot
21
+
22
+ template = <<-ERB
23
+ <% cache_key = "rubyblok/#{story['id']}-#{Digest::SHA1.hexdigest(story.to_json)}" %>
24
+ <% cache_if use_cache, cache_key do %>
25
+ <%= rubyblok_component_tag(partial: content.component, blok: content) %>
26
+ <% end %>
27
+ ERB
28
+
29
+ render_inline_partial template:, locals: { content:, use_cache: Rubyblok.configuration.cache_views }
15
30
  end
16
31
 
17
32
  def rubyblok_component_tag(blok:, partial: blok.component)
33
+ return if invisible_on_production?(blok)
34
+
18
35
  render_partial(partial:, locals: { blok: }).prepend(rubyblok_editable_tag(blok).to_s)
19
36
  end
20
37
 
38
+ def invisible_on_production?(blok)
39
+ Rails.env.production? && blok.fetch('invisible_on_production', false)
40
+ end
41
+
21
42
  # rubocop:disable Rails/OutputSafety
22
43
  def rubyblok_markdown_tag(content)
23
44
  markdown_renderer.render(content).html_safe
@@ -37,34 +58,44 @@ module StoryblokHelper
37
58
  render_inline_partial template:, locals: { bloks: }
38
59
  end
39
60
 
61
+ def get_story_content(slug)
62
+ get_story(slug)['content'].to_dot
63
+ end
64
+
65
+ private
66
+
40
67
  def get_story(slug)
41
- if use_cache?
42
- get_story_via_cache(slug)["content"].to_dot
68
+ return get_story_via_api(slug) unless cached?
69
+
70
+ if update_storyblok?
71
+ get_story_via_api(slug, save: true)
43
72
  else
44
- get_story_via_api(slug)["content"].to_dot
73
+ get_story_via_cache(slug)
45
74
  end
46
75
  end
47
76
 
48
- private
49
-
50
- def get_story_via_api(slug)
51
- Rubyblok::Services::GetStoryblokStory.call(slug:)
77
+ def get_story_via_api(slug, save: false)
78
+ story = Rubyblok::Services::GetStoryblokStory.call(slug:)
79
+ replace_storyblok_url(story).tap do |storyblok_story|
80
+ model_cache_class.find_or_create(storyblok_story) if save
81
+ end
52
82
  end
53
83
 
54
84
  def get_story_via_cache(slug)
55
- Rubyblok.configuration.model_name.classify.constantize.fetch_content(slug)
85
+ model_cache_class.fetch_content(slug)
56
86
  end
57
87
 
58
- def rich_text_renderer # rubocop:disable Metrics/MethodLength
88
+ def replace_storyblok_url(story)
89
+ Rubyblok::Services::ReplaceStoryblokUrl.call(story:)
90
+ end
91
+
92
+ def rich_text_renderer
59
93
  ctx = {}
60
94
  path = component_path
61
95
  @rich_text_renderer ||=
62
96
  Storyblok::Richtext::HtmlRenderer.new.tap do |html_renderer|
63
97
  html_renderer.set_component_resolver(lambda { |component, data|
64
- ApplicationController.render(
65
- partial: "#{path}/#{component}",
66
- locals: ctx.merge(blok: data)
67
- )
98
+ ApplicationController.render(partial: "#{path}/#{component}", locals: ctx.merge(blok: data))
68
99
  })
69
100
  end
70
101
  end
@@ -75,7 +106,7 @@ module StoryblokHelper
75
106
 
76
107
  # rubocop:disable Rails/OutputSafety
77
108
  def rubyblok_editable_tag(component)
78
- component["_editable"]&.html_safe
109
+ component['_editable']&.html_safe
79
110
  end
80
111
  # rubocop:enable Rails/OutputSafety
81
112
 
@@ -91,7 +122,15 @@ module StoryblokHelper
91
122
  ApplicationController.render inline: template, locals:
92
123
  end
93
124
 
94
- def use_cache?
125
+ def cached?
95
126
  Rubyblok.configuration.cached
96
127
  end
128
+
129
+ def auto_update?
130
+ Rubyblok.configuration.auto_update
131
+ end
132
+
133
+ def update_storyblok?
134
+ auto_update? || params[:storyblok] == 'update'
135
+ end
97
136
  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
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+
5
+ module Rubyblok
6
+ module Mixins
7
+ module ModelCacheClass
8
+ extend ActiveSupport::Concern
9
+
10
+ def model_cache_class
11
+ Rubyblok.configuration.model_name.classify.constantize
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,27 +1,50 @@
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
5
7
  module Webhook
6
8
  extend ActiveSupport::Concern
9
+ include Rubyblok::Mixins::ModelCacheClass
7
10
 
8
- included do
11
+ included do # rubocop:disable Metrics/BlockLength
9
12
  skip_before_action :verify_authenticity_token, only: [:create]
10
13
 
11
14
  def create
12
- payload = JSON.parse(request.raw_post)
15
+ verify_signature(request.raw_post) if webhook_secret.present?
13
16
 
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"])
17
+ payload = JSON.parse(request.raw_post)
18
+ case payload['action']
19
+ when 'published'
20
+ update_story(payload)
21
+ when 'unpublished', 'deleted'
22
+ model_cache_class.find_by(storyblok_story_id: payload['story_id'])&.destroy!
23
+ end
17
24
 
18
- render json: { success: true }
25
+ render(json: { success: true })
19
26
  end
20
27
 
21
28
  private
22
29
 
23
- def model
24
- Rubyblok.configuration.model_name.classify.constantize
30
+ def update_story(payload)
31
+ storyblok_story = Rubyblok::Services::GetStoryblokStory.call(slug: payload['story_id'])
32
+ storyblok_story = Rubyblok::Services::ReplaceStoryblokUrl.call(story: storyblok_story)
33
+ model_cache_class.find_or_create(storyblok_story)
34
+ end
35
+
36
+ def verify_signature(payload_body)
37
+ signature =
38
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), webhook_secret, payload_body)
39
+ raise StandardError, 'Signature check failed!' unless verified?(signature)
40
+ end
41
+
42
+ def verified?(signature)
43
+ Rack::Utils.secure_compare(signature, request.env['HTTP_WEBHOOK_SIGNATURE'])
44
+ end
45
+
46
+ def webhook_secret
47
+ Rubyblok.configuration.webhook_secret
25
48
  end
26
49
  end
27
50
  end
@@ -1,9 +1,15 @@
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
10
+
11
+ rake_tasks do
12
+ load 'tasks/rubyblok/create_or_update_content.rake'
13
+ end
8
14
  end
9
15
  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.3.0'
5
5
  end
data/lib/rubyblok.rb CHANGED
@@ -1,20 +1,24 @@
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::Railtie)
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/model_cache_class'
15
+ require_relative 'rubyblok/mixins/webhook'
16
+ require_relative 'generators/rubyblok/migration_generator'
17
+ require_relative 'generators/rubyblok/install_generator'
18
+ require_relative 'generators/rubyblok/webhook_controller_generator'
19
+ require_relative 'generators/rubyblok/hello_world_generator'
20
+ require_relative 'generators/rubyblok/sitemap_config_generator'
21
+ require_relative 'generators/rubyblok/image_cache_generator'
18
22
 
19
23
  module Rubyblok
20
24
  def self.configuration
@@ -26,5 +30,4 @@ module Rubyblok
26
30
  end
27
31
 
28
32
  class Error < StandardError; end
29
- # Your code goes here...
30
33
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ PER_PAGE = 50
4
+
5
+ namespace :rubyblok do
6
+ desc 'Create or update cached Storyblok stories'
7
+ task :create_or_update_content, [:version] => [:environment] do |_task, args|
8
+ include Rubyblok::Mixins::ModelCacheClass
9
+
10
+ args.with_defaults(version: Rubyblok.configuration.version)
11
+
12
+ raise ArgumentError, "Invalid version \"#{args[:version]}\"" unless %w[draft published].include?(args[:version])
13
+
14
+ client = storyblok_client(args[:version])
15
+ page = 1
16
+
17
+ loop do
18
+ stories = get_stories(client:, page:)
19
+ break if stories.empty?
20
+
21
+ stories.each { |story| model_cache_class.find_or_create(story) }
22
+
23
+ page += 1
24
+
25
+ sleep 1
26
+ end
27
+ end
28
+
29
+ def storyblok_client(version)
30
+ Storyblok::Client.new(token: Rubyblok.configuration.api_token, version:)
31
+ end
32
+
33
+ def get_stories(client:, page:)
34
+ client.stories(page:, per_page: PER_PAGE).dig('data', 'stories')
35
+ end
36
+ end
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