ckeditor5 1.15.8 → 1.15.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +6 -5
  3. data/README.md +3 -0
  4. data/lib/ckeditor5/rails/cdn/ckbox_bundle.rb +2 -2
  5. data/lib/ckeditor5/rails/cdn/helpers.rb +6 -0
  6. data/lib/ckeditor5/rails/context/helpers.rb +1 -1
  7. data/lib/ckeditor5/rails/editor/editable_height_normalizer.rb +50 -0
  8. data/lib/ckeditor5/rails/editor/helpers/config_helpers.rb +3 -3
  9. data/lib/ckeditor5/rails/editor/helpers/editor_helpers.rb +5 -4
  10. data/lib/ckeditor5/rails/editor/props.rb +3 -20
  11. data/lib/ckeditor5/rails/engine.rb +3 -2
  12. data/lib/ckeditor5/rails/plugins/simple_upload_adapter.rb +1 -1
  13. data/lib/ckeditor5/rails/presets/plugins_builder.rb +9 -9
  14. data/lib/ckeditor5/rails/presets/preset_builder.rb +6 -9
  15. data/lib/ckeditor5/rails/version.rb +1 -1
  16. data/lib/ckeditor5/rails/version_detector.rb +6 -0
  17. data/spec/lib/ckeditor5/rails/assets/asset_bundle_hml_serializer_spec.rb +111 -0
  18. data/spec/lib/ckeditor5/rails/assets/assets_bundle_spec.rb +191 -0
  19. data/spec/lib/ckeditor5/rails/cdn/ckbox_bundle_spec.rb +69 -0
  20. data/spec/lib/ckeditor5/rails/cdn/ckeditor_bundle_spec.rb +72 -0
  21. data/spec/lib/ckeditor5/rails/cdn/helpers_spec.rb +235 -0
  22. data/spec/lib/ckeditor5/rails/context/helpers_spec.rb +67 -0
  23. data/spec/lib/ckeditor5/rails/context/props_spec.rb +70 -0
  24. data/spec/lib/ckeditor5/rails/editor/editable_height_normalizer_spec.rb +50 -0
  25. data/spec/lib/ckeditor5/rails/editor/helpers/config_helpers_spec.rb +52 -0
  26. data/spec/lib/ckeditor5/rails/editor/helpers/editor_helpers_spec.rb +192 -0
  27. data/spec/lib/ckeditor5/rails/editor/props_inline_plugin_spec.rb +43 -0
  28. data/spec/lib/ckeditor5/rails/editor/props_plugin_spec.rb +66 -0
  29. data/spec/lib/ckeditor5/rails/editor/props_spec.rb +104 -0
  30. data/spec/lib/ckeditor5/rails/engine_spec.rb +88 -0
  31. data/spec/lib/ckeditor5/rails/hooks/form_spec.rb +203 -0
  32. data/spec/lib/ckeditor5/rails/hooks/simple_form_spec.rb +100 -0
  33. data/spec/lib/ckeditor5/rails/presets/manager_spec.rb +100 -0
  34. data/spec/lib/ckeditor5/rails/presets/plugins_builder_spec.rb +98 -0
  35. data/spec/lib/ckeditor5/rails/presets/preset_builder_spec.rb +347 -0
  36. data/spec/lib/ckeditor5/rails/presets/toolbar_builder_spec.rb +70 -0
  37. data/spec/lib/ckeditor5/rails/semver_spec.rb +58 -0
  38. data/spec/lib/ckeditor5/rails/version_detector_spec.rb +131 -0
  39. data/spec/lib/ckeditor5/rails/version_spec.rb +25 -0
  40. data/spec/spec_helper.rb +8 -2
  41. data/spec/support/test_models.rb +6 -0
  42. metadata +49 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 127f81c39ea4df2b13a4095bd335f7b8698549249851de0f90ed948e309b2ca0
4
- data.tar.gz: cd9711cd4b70b4c89091b166a694065973b4d12eff6d587e8cb761abdcd5b43e
3
+ metadata.gz: 1cdcc72197788bc6dc57bb3b01158c932dbf27621084414bc832c5d772e01701
4
+ data.tar.gz: 7d6874e08d93346f9d478f46e2240bbc5df78a32700f50087de8fe8e4301cfc8
5
5
  SHA512:
6
- metadata.gz: 0b0fd8c78086d0f46b10b63a01a6394b493958d82d022d9cdebae203df0f57fbe396ed02609fc7049d0dbf6415deb8a35eddc88593dcbda51a84f0ae3fbf4f56
7
- data.tar.gz: 8eeea41e9de0d98c7d3613c915f3fb7e93bb55fd80ebe24aa97d7a3761af1a9ed0f6a1f4e502dcff53bc11d4eb04f7776f26d768da2adfdfe73868f1becd3b65
6
+ metadata.gz: 7dc9311974b2dd72a05fd61cea086585179e64700f800618d06f4bffd079f31ad99d31f1ea5917d7c28cdd4e03d675122b546d25518ed43a99928f2061afd448
7
+ data.tar.gz: 199da996b61646c658dbbe0c8119419220b669442a5eba6fc1c973dad7793e3db8e3a1e8808e075e8d0477236fd8741fa2927eae9c141fc7bb87364df6fee7ae
data/Gemfile CHANGED
@@ -13,14 +13,9 @@ group :development do
13
13
  gem 'pry-rails', '~> 0.3', '>= 0.3.11'
14
14
  gem 'rails', '~> 7.0', '>= 7.0.0'
15
15
  gem 'rake', '~> 13.2', '>= 13.2.1'
16
- gem 'rspec', '~> 3.13'
17
- gem 'rspec-expectations', '~> 3.13'
18
- gem 'rspec-rails', '~> 7.0'
19
16
  gem 'rubocop', '~> 1.66', require: false
20
17
  gem 'rubocop-rails', '~> 2.26', '>= 2.26.2', require: false
21
18
  gem 'rubocop-rails-omakase', '~> 1.0.0', require: false
22
- gem 'simplecov', '~> 0.21', '>= 0.21.2', require: false
23
- gem 'simplecov_json_formatter', '~> 0.1.4', require: false
24
19
  gem 'simple_form', '~> 5.3', '>= 5.3.0'
25
20
  gem 'slim', '~> 5.2', '>= 5.2.0'
26
21
  gem 'sprockets-rails', '~> 3.2', '>= 3.2.2'
@@ -29,6 +24,12 @@ end
29
24
 
30
25
  group :test, :development do
31
26
  gem 'capybara', '~> 3.40'
27
+ gem 'rspec', '~> 3.13'
28
+ gem 'rspec-expectations', '~> 3.13'
29
+ gem 'rspec-html-matchers', '~> 0.10.0'
30
+ gem 'rspec-rails', '~> 7.0'
31
+ gem 'simplecov', '~> 0.21', '>= 0.21.2', require: false
32
+ gem 'simplecov_json_formatter', '~> 0.1.4', require: false
32
33
  end
33
34
 
34
35
  gemspec
data/README.md CHANGED
@@ -74,6 +74,9 @@ CKEditor5::Rails.configure do
74
74
  # Optionally, you can specify version of CKEditor 5 to use.
75
75
  # If it's not specified the default version specified in the gem will be used.
76
76
  # version '43.3.1'
77
+
78
+ # Upload images to the server using the simple upload adapter, instead of Base64 encoding.
79
+ # simple_upload_adapter
77
80
  end
78
81
  ```
79
82
 
@@ -22,9 +22,9 @@ module CKEditor5::Rails
22
22
  @scripts ||= [
23
23
  Assets::JSExportsMeta.new(
24
24
  create_cdn_url('ckbox', version, 'ckbox.js'),
25
- *translations_js_exports_meta,
26
25
  window_name: 'CKBox'
27
- )
26
+ ),
27
+ *translations_js_exports_meta
28
28
  ]
29
29
  end
30
30
 
@@ -1,5 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../semver'
4
+ require_relative '../editor/props'
5
+ require_relative '../editor/helpers/config_helpers'
6
+ require_relative '../presets/manager'
7
+ require_relative '../assets/assets_bundle_html_serializer'
8
+
3
9
  require_relative 'url_generator'
4
10
  require_relative 'ckeditor_bundle'
5
11
  require_relative 'ckbox_bundle'
@@ -7,7 +7,7 @@ module CKEditor5::Rails::Context
7
7
  def ckeditor5_context(**config, &block)
8
8
  context_props = Props.new(config)
9
9
 
10
- tag.send(:'ckeditor-context-component', **context_props.to_attributes, &block)
10
+ tag.public_send(:'ckeditor-context-component', **context_props.to_attributes, &block)
11
11
  end
12
12
  end
13
13
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CKEditor5::Rails::Editor
4
+ class InvalidEditableHeightError < ArgumentError; end
5
+
6
+ class EditableHeightNormalizer
7
+ def initialize(editor_type)
8
+ @editor_type = editor_type
9
+ end
10
+
11
+ def normalize(value)
12
+ return nil if value.nil?
13
+
14
+ validate_editor_type!
15
+ convert_to_pixel_value(value)
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :editor_type
21
+
22
+ def validate_editor_type!
23
+ return if editor_type == :classic
24
+
25
+ raise InvalidEditableHeightError,
26
+ 'editable_height can be used only with ClassicEditor'
27
+ end
28
+
29
+ def convert_to_pixel_value(value)
30
+ case value
31
+ when Integer then "#{value}px"
32
+ when String then convert_string_to_pixel_value(value)
33
+ else
34
+ raise_invalid_height_error(value)
35
+ end
36
+ end
37
+
38
+ def convert_string_to_pixel_value(value)
39
+ return value if value.match?(/^\d+px$/)
40
+
41
+ raise_invalid_height_error(value)
42
+ end
43
+
44
+ def raise_invalid_height_error(value)
45
+ raise InvalidEditableHeightError,
46
+ "editable_height must be an integer representing pixels or string ending with 'px'\n" \
47
+ "(e.g. 500 or '500px'). Got: #{value.inspect}"
48
+ end
49
+ end
50
+ end
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module CKEditor5::Rails
4
- module Editor::Helpers::Config
3
+ module CKEditor5::Rails::Editor::Helpers
4
+ module Config
5
5
  def ckeditor5_element_ref(selector)
6
6
  { '$element': selector }
7
7
  end
8
8
 
9
9
  def ckeditor5_preset(name = nil, &block)
10
- return Engine.find_preset(name) if name
10
+ return CKEditor5::Rails::Engine.find_preset(name) if name
11
11
 
12
12
  raise ArgumentError, 'Configuration block is required for preset definition' unless block_given?
13
13
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../version_detector'
4
+ require_relative '../../presets/preset_builder'
3
5
  require_relative 'config_helpers'
4
6
 
5
7
  module CKEditor5::Rails
@@ -8,7 +10,6 @@ module CKEditor5::Rails
8
10
 
9
11
  class EditorContextError < StandardError; end
10
12
  class PresetNotFoundError < ArgumentError; end
11
- class InvalidEditableHeightError < ArgumentError; end
12
13
 
13
14
  # Creates a CKEditor 5 editor instance
14
15
  #
@@ -45,15 +46,15 @@ module CKEditor5::Rails
45
46
 
46
47
  tag_attributes = html_attributes.merge(editor_props.to_attributes)
47
48
 
48
- tag.send(:'ckeditor-component', **tag_attributes, &block)
49
+ tag.public_send(:'ckeditor-component', **tag_attributes, &block)
49
50
  end
50
51
 
51
52
  def ckeditor5_editable(name = nil, **kwargs, &block)
52
- tag.send(:'ckeditor-editable-component', name: name, **kwargs, &block)
53
+ tag.public_send(:'ckeditor-editable-component', name: name, **kwargs, &block)
53
54
  end
54
55
 
55
56
  def ckeditor5_ui_part(name, **kwargs, &block)
56
- tag.send(:'ckeditor-ui-part-component', name: name, **kwargs, &block)
57
+ tag.public_send(:'ckeditor-ui-part-component', name: name, **kwargs, &block)
57
58
  end
58
59
 
59
60
  def ckeditor5_toolbar(**kwargs)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'props_plugin'
4
+ require_relative 'editable_height_normalizer'
4
5
 
5
6
  module CKEditor5::Rails::Editor
6
7
  class Props
@@ -19,7 +20,7 @@ module CKEditor5::Rails::Editor
19
20
  @watchdog = watchdog
20
21
  @type = type
21
22
  @config = config
22
- @editable_height = normalize_editable_height(editable_height)
23
+ @editable_height = EditableHeightNormalizer.new(type).normalize(editable_height)
23
24
  end
24
25
 
25
26
  def to_attributes
@@ -48,7 +49,7 @@ module CKEditor5::Rails::Editor
48
49
  end
49
50
 
50
51
  def serialize_translations
51
- controller_context[:bundle].translations_scripts.map(&:to_h).to_json
52
+ controller_context[:bundle]&.translations_scripts&.map(&:to_h).to_json || '[]'
52
53
  end
53
54
 
54
55
  def serialize_plugins
@@ -61,23 +62,5 @@ module CKEditor5::Rails::Editor
61
62
  .tap { |cfg| cfg[:licenseKey] = controller_context[:license_key] if controller_context[:license_key] }
62
63
  .to_json
63
64
  end
64
-
65
- def normalize_editable_height(editable_height)
66
- return nil if editable_height.nil?
67
-
68
- unless type == :classic
69
- raise InvalidEditableHeightError,
70
- 'editable_height can be used only with ClassicEditor'
71
- end
72
-
73
- case editable_height
74
- when String, /^\d+px$/ then editable_height
75
- when Integer, /^\d+$/ then "#{editable_height}px"
76
- else
77
- raise InvalidEditableHeightError,
78
- "editable_height must be an integer representing pixels or string ending with 'px'\n" \
79
- "(e.g. 500 or '500px'). Got: #{editable_height.inspect}"
80
- end
81
- end
82
65
  end
83
66
  end
@@ -45,7 +45,7 @@ module CKEditor5::Rails
45
45
  def find_preset(preset)
46
46
  return preset if preset.is_a?(CKEditor5::Rails::Presets::PresetBuilder)
47
47
 
48
- Engine.base.presets[preset]
48
+ base.presets[preset]
49
49
  end
50
50
  end
51
51
 
@@ -54,7 +54,8 @@ module CKEditor5::Rails
54
54
 
55
55
  delegate :version, :gpl, :premium, :cdn, :translations, :license_key,
56
56
  :type, :menubar, :toolbar, :plugins, :plugin, :inline_plugin,
57
- :language, :ckbox, :configure, to: :default_preset
57
+ :language, :ckbox, :configure, :automatic_upgrades, :simple_upload_adapter,
58
+ :editable_height, to: :default_preset
58
59
 
59
60
  def initialize(configuration)
60
61
  @configuration = configuration
@@ -81,7 +81,7 @@ module CKEditor5::Rails::Plugins
81
81
  JAVASCRIPT
82
82
 
83
83
  def initialize
84
- super(:SimpleUpload, PLUGIN_CODE)
84
+ super(:SimpleUploadAdapter, PLUGIN_CODE)
85
85
  end
86
86
  end
87
87
  end
@@ -2,10 +2,10 @@
2
2
 
3
3
  module CKEditor5::Rails
4
4
  class Presets::PluginsBuilder
5
- attr_reader :plugins
5
+ attr_reader :items
6
6
 
7
7
  def initialize(plugins)
8
- @plugins = plugins
8
+ @items = plugins
9
9
  end
10
10
 
11
11
  def self.create_plugin(name, **kwargs)
@@ -17,19 +17,19 @@ module CKEditor5::Rails
17
17
  end
18
18
 
19
19
  def remove(*names)
20
- names.each { |name| plugins.delete_if { |plugin| plugin.name == name } }
20
+ names.each { |name| items.delete_if { |plugin| plugin.name == name } }
21
21
  end
22
22
 
23
23
  def prepend(*names, before: nil, **kwargs)
24
24
  new_plugins = names.map { |name| self.class.create_plugin(name, **kwargs) }
25
25
 
26
26
  if before
27
- index = plugins.index { |p| p.name == before }
27
+ index = items.index { |p| p.name == before }
28
28
  raise ArgumentError, "Plugin '#{before}' not found" unless index
29
29
 
30
- plugins.insert(index, *new_plugins)
30
+ items.insert(index, *new_plugins)
31
31
  else
32
- plugins.insert(0, *new_plugins)
32
+ items.insert(0, *new_plugins)
33
33
  end
34
34
  end
35
35
 
@@ -37,12 +37,12 @@ module CKEditor5::Rails
37
37
  new_plugins = names.map { |name| self.class.create_plugin(name, **kwargs) }
38
38
 
39
39
  if after
40
- index = plugins.index { |p| p.name == after }
40
+ index = items.index { |p| p.name == after }
41
41
  raise ArgumentError, "Plugin '#{after}' not found" unless index
42
42
 
43
- plugins.insert(index + 1, *new_plugins)
43
+ items.insert(index + 1, *new_plugins)
44
44
  else
45
- plugins.push(*new_plugins)
45
+ items.push(*new_plugins)
46
46
  end
47
47
  end
48
48
  end
@@ -104,7 +104,7 @@ module CKEditor5::Rails
104
104
  end
105
105
 
106
106
  def version(version = nil)
107
- return @version.to_s if version.nil?
107
+ return @version&.to_s if version.nil?
108
108
 
109
109
  if @automatic_upgrades && version
110
110
  detected = VersionDetector.latest_safe_version(version)
@@ -162,10 +162,9 @@ module CKEditor5::Rails
162
162
  }
163
163
  end
164
164
 
165
- return unless block
166
-
167
- builder = ArrayBuilder.new(@config[:toolbar][:items])
168
- builder.instance_eval(&block)
165
+ builder = ToolbarBuilder.new(@config[:toolbar][:items])
166
+ builder.instance_eval(&block) if block_given?
167
+ builder
169
168
  end
170
169
 
171
170
  def inline_plugin(name, code)
@@ -174,7 +173,6 @@ module CKEditor5::Rails
174
173
 
175
174
  def plugin(name, **kwargs)
176
175
  plugin_obj = PluginsBuilder.create_plugin(name, **kwargs)
177
-
178
176
  @config[:plugins] << plugin_obj
179
177
  plugin_obj
180
178
  end
@@ -184,10 +182,9 @@ module CKEditor5::Rails
184
182
 
185
183
  names.each { |name| plugin(name, **kwargs) } unless names.empty?
186
184
 
187
- return unless block
188
-
189
185
  builder = PluginsBuilder.new(@config[:plugins])
190
- builder.instance_eval(&block)
186
+ builder.instance_eval(&block) if block_given?
187
+ builder
191
188
  end
192
189
 
193
190
  def language(ui = nil, content: ui) # rubocop:disable Naming/MethodParameterName
@@ -2,7 +2,7 @@
2
2
 
3
3
  module CKEditor5
4
4
  module Rails
5
- VERSION = '1.15.8'
5
+ VERSION = '1.15.10'
6
6
 
7
7
  DEFAULT_CKEDITOR_VERSION = '43.3.1'
8
8
  end
@@ -4,6 +4,8 @@ require 'net/http'
4
4
  require 'json'
5
5
  require 'singleton'
6
6
  require 'monitor'
7
+
8
+ require_relative 'version'
7
9
  require_relative 'semver'
8
10
 
9
11
  module CKEditor5::Rails
@@ -22,6 +24,10 @@ module CKEditor5::Rails
22
24
  @monitor = Monitor.new
23
25
  end
24
26
 
27
+ def clear_cache!
28
+ @monitor.synchronize { @cache.clear }
29
+ end
30
+
25
31
  def latest_safe_version(current_version)
26
32
  @monitor.synchronize do
27
33
  cache_key = "#{current_version}_latest"
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CKEditor5::Rails::Assets::AssetsBundleHtmlSerializer do
6
+ let(:test_bundle_class) do
7
+ Class.new(CKEditor5::Rails::Assets::AssetsBundle) do
8
+ attr_accessor :scripts, :stylesheets
9
+
10
+ def initialize(scripts, stylesheets)
11
+ @scripts = scripts
12
+ @stylesheets = stylesheets
13
+ super()
14
+ end
15
+ end
16
+ end
17
+
18
+ let(:bundle) { test_bundle_class.new(scripts, stylesheets) }
19
+
20
+ let(:scripts) do
21
+ [
22
+ CKEditor5::Rails::Assets::JSExportsMeta.new(
23
+ 'https://cdn.com/script1.js',
24
+ window_name: 'CKEditor5'
25
+ ),
26
+ CKEditor5::Rails::Assets::JSExportsMeta.new(
27
+ 'https://cdn.com/script2.js',
28
+ import_name: '@ckeditor/script2'
29
+ )
30
+ ]
31
+ end
32
+
33
+ let(:stylesheets) { ['https://cdn.com/style1.css', 'https://cdn.com/style2.css'] }
34
+ let(:preloads) { bundle.preloads }
35
+
36
+ subject(:serializer) { described_class.new(bundle) }
37
+
38
+ describe '#initialize' do
39
+ context 'with invalid bundle' do
40
+ let(:bundle) { 'not a bundle' }
41
+
42
+ it 'raises TypeError' do
43
+ expect { serializer }.to raise_error(TypeError, 'bundle must be an instance of AssetsBundle')
44
+ end
45
+ end
46
+ end
47
+
48
+ describe '#to_html' do
49
+ subject(:html) { serializer.to_html }
50
+
51
+ it 'includes window scripts' do
52
+ expect(html).to include(
53
+ '<script src="https://cdn.com/script1.js" nonce="true" crossorigin="anonymous">'
54
+ )
55
+ end
56
+
57
+ it 'includes import map' do
58
+ expect(html).to include('type="importmap"')
59
+ expect(html).to include('"@ckeditor/script2":"https://cdn.com/script2.js"')
60
+ end
61
+
62
+ it 'includes stylesheet links' do
63
+ stylesheets.each do |url|
64
+ expect(html).to include("<link href=\"#{url}\" rel=\"stylesheet\" crossorigin=\"anonymous\">")
65
+ end
66
+ end
67
+
68
+ it 'includes preload links' do
69
+ expect(html).to include(
70
+ '<link href="https://cdn.com/style1.css" rel="preload" as="style" crossorigin="anonymous">'
71
+ )
72
+
73
+ expect(html).to include(
74
+ '<link href="https://cdn.com/style2.css" rel="preload" as="style" crossorigin="anonymous">'
75
+ )
76
+
77
+ expect(html).to include(
78
+ '<link href="https://cdn.com/script1.js" rel="preload" as="script" crossorigin="anonymous">'
79
+ )
80
+
81
+ expect(html).to include(
82
+ '<link href="https://cdn.com/script2.js" rel="preload" as="script" crossorigin="anonymous">'
83
+ )
84
+ end
85
+
86
+ it 'includes web component script' do
87
+ expect(html).to include('<script type="module" nonce="true">')
88
+ end
89
+
90
+ it 'memoizes scripts import map' do
91
+ first_call = serializer.send(:scripts_import_map_tag)
92
+ second_call = serializer.send(:scripts_import_map_tag)
93
+
94
+ expect(first_call.object_id).to eq(second_call.object_id)
95
+ end
96
+ end
97
+
98
+ describe '.url_resource_preload_type' do
99
+ it 'returns correct type for js files' do
100
+ expect(described_class.url_resource_preload_type('file.js')).to eq('script')
101
+ end
102
+
103
+ it 'returns correct type for css files' do
104
+ expect(described_class.url_resource_preload_type('file.css')).to eq('style')
105
+ end
106
+
107
+ it 'returns fetch for unknown extensions' do
108
+ expect(described_class.url_resource_preload_type('file.unknown')).to eq('fetch')
109
+ end
110
+ end
111
+ end