thredded 1.0.1 → 1.2.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.
@@ -47,7 +47,8 @@ module Thredded
47
47
 
48
48
  ordered_keys.map do |cache_key|
49
49
  [keyed_collection[cache_key], cached_partials[cache_key] || rendered_partials.next.tap do |rendered|
50
- cached_partials[cache_key] = cache.write(cache_key, rendered, expires_in: expires_in)
50
+ cache.write(cache_key, rendered, expires_in: expires_in)
51
+ cached_partials[cache_key] = rendered
51
52
  end]
52
53
  end
53
54
  end
@@ -59,16 +60,16 @@ module Thredded
59
60
  digest_path = digest_path_from_template(view, template)
60
61
 
61
62
  collection.each_with_object([{}, []]) do |item, (hash, ordered_keys)|
62
- key = expanded_cache_key(item, view, template, digest_path)
63
+ key = expanded_cache_key(item, view, digest_path)
63
64
  ordered_keys << key
64
65
  hash[key] = item
65
66
  end
66
67
  end
67
68
 
68
- def expanded_cache_key(key, view, template, digest_path)
69
+ def expanded_cache_key(key, view, digest_path)
69
70
  key = combined_fragment_cache_key(
70
71
  view,
71
- cache_fragment_name(view, key, virtual_path: template.virtual_path, digest_path: digest_path)
72
+ cache_fragment_name(view, key, digest_path: digest_path)
72
73
  )
73
74
  key.frozen? ? key.dup : key # #read_multi & #write may require mutability, Dalli 2.6.0.
74
75
  end
@@ -82,34 +83,38 @@ module Thredded
82
83
  else
83
84
  collection.each_slice(collection.size / num_threads).map do |slice|
84
85
  Thread.start do
85
- # `ActionView::PartialRenderer` mutates the contents of `opts[:locals]`, `opts[:locals][:as]` in particular:
86
- # https://github.com/rails/rails/blob/v6.0.2.1/actionview/lib/action_view/renderer/partial_renderer.rb#L379
87
- # https://github.com/rails/rails/blob/v6.0.2.1/actionview/lib/action_view/renderer/partial_renderer.rb#L348-L356
88
- opts[:locals] = opts[:locals].dup if opts[:locals]
89
86
  ActiveRecord::Base.connection_pool.with_connection do
90
- render_partials_serial(view_context.dup, slice, opts)
87
+ render_partials_serial(view_context.dup, slice, dup_opts(opts))
91
88
  end
92
89
  end
93
90
  end.flat_map(&:value)
94
91
  end
95
92
  end
96
93
 
97
- if Thredded::Compat.rails_gte_61?
98
- # @param [Array<Object>] collection
99
- # @param [Hash] opts
100
- # @param view_context
101
- # @return [Array<String>]
102
- def render_partials_serial(view_context, collection, opts)
103
- # https://github.com/rails/rails/pull/38594
104
- collection.map do |object|
105
- renderer = ActionView::ObjectRenderer.new(@lookup_context, opts)
106
- renderer.render_object_with_partial(object, opts[:partial], view_context, nil).body
107
- end
94
+ # `ActionView::PartialRenderer` mutates the contents of `opts[:locals]`, `opts[:locals][:as]` in particular:
95
+ # https://github.com/rails/rails/blob/v6.0.2.1/actionview/lib/action_view/renderer/partial_renderer.rb#L379
96
+ # https://github.com/rails/rails/blob/v6.0.2.1/actionview/lib/action_view/renderer/partial_renderer.rb#L348-L356
97
+ def dup_opts(opts)
98
+ if opts[:locals]
99
+ opts = opts.dup
100
+ # sometimes have a thread safe :users_provider, preserve that for performance reasons
101
+ # hence not doing a deep_dup
102
+ opts[:locals] = opts[:locals].dup
103
+ opts
104
+ else
105
+ opts.dup
108
106
  end
109
- else
110
- def render_partials_serial(view_context, collection, opts)
111
- partial_renderer = ActionView::PartialRenderer.new(@lookup_context)
112
- collection.map { |object| render_partial(partial_renderer, view_context, **opts.merge(object: object)) }
107
+ end
108
+
109
+ # @param [Array<Object>] collection
110
+ # @param [Hash] opts
111
+ # @param view_context
112
+ # @return [Array<String>]
113
+ def render_partials_serial(view_context, collection, opts)
114
+ # https://github.com/rails/rails/pull/38594
115
+ collection.map do |object|
116
+ renderer = ActionView::ObjectRenderer.new(@lookup_context, opts)
117
+ renderer.render_object_with_partial(object, opts[:partial], view_context, nil).body
113
118
  end
114
119
  end
115
120
 
@@ -121,42 +126,16 @@ module Thredded
121
126
  view.combined_fragment_cache_key(key)
122
127
  end
123
128
 
124
- if Thredded::Compat.rails_gte_60?
125
- def cache_fragment_name(view, key, virtual_path:, digest_path:)
126
- if Thredded::Compat.rails_gte_61?
127
- view.cache_fragment_name(key, digest_path: digest_path)
128
- else
129
- view.cache_fragment_name(key, virtual_path: virtual_path, digest_path: digest_path)
130
- end
131
- end
132
-
133
- def digest_path_from_template(view, template)
134
- view.digest_path_from_template(template)
135
- end
136
-
137
- def render_partial(partial_renderer, view_context, opts)
138
- partial_renderer.render(view_context, opts, nil).body
139
- end
140
- else
141
- def cache_fragment_name(_view, key, virtual_path:, digest_path:)
142
- if digest_path
143
- ["#{virtual_path}:#{digest_path}", key]
144
- else
145
- [virtual_path, key]
146
- end
147
- end
129
+ def cache_fragment_name(view, key, digest_path:)
130
+ view.cache_fragment_name(key, digest_path: digest_path)
131
+ end
148
132
 
149
- def digest_path_from_template(view, template)
150
- ActionView::Digestor.digest(
151
- name: template.virtual_path,
152
- finder: @lookup_context,
153
- dependencies: view.view_cache_dependencies
154
- ).presence
155
- end
133
+ def digest_path_from_template(view, template)
134
+ view.digest_path_from_template(template)
135
+ end
156
136
 
157
- def render_partial(partial_renderer, view_context, opts)
158
- partial_renderer.render(view_context, opts, nil)
159
- end
137
+ def render_partial(partial_renderer, view_context, opts)
138
+ partial_renderer.render(view_context, opts, nil).body
160
139
  end
161
140
  end
162
141
  end
@@ -4,31 +4,23 @@ module Thredded
4
4
  module Compat
5
5
  class << self
6
6
  # @api private
7
- def rails_gte_60?
8
- @rails_gte_60 = (Rails.gem_version >= Gem::Version.new('6.0.0')) if @rails_gte_60.nil?
9
- @rails_gte_60
7
+ def rails_gte_71?
8
+ @rails_gte_71 = (Rails.gem_version >= Gem::Version.new('7.1.0')) if @rails_gte_71.nil?
9
+ @rails_gte_71
10
10
  end
11
11
 
12
12
  # @api private
13
- def rails_gte_61?
14
- @rails_gte_61 = (Rails.gem_version >= Gem::Version.new('6.1.0')) if @rails_gte_61.nil?
15
- @rails_gte_61
13
+ def rails_gte_72?
14
+ @rails_gte_72 = (Rails.gem_version >= Gem::Version.new('7.2.0')) if @rails_gte_72.nil?
15
+ @rails_gte_72
16
16
  end
17
17
 
18
- if Rails.gem_version >= Gem::Version.new('7.0.0')
19
- # @api private
20
- def association_preloader(records:, associations:, scope:)
21
- ActiveRecord::Associations::Preloader.new(
22
- records: records, associations: associations, scope: scope
23
- ).call
24
- end
25
- else
26
- # @api private
27
- def association_preloader(records:, associations:, scope:)
28
- ActiveRecord::Associations::Preloader.new.preload(
29
- records, associations, scope
30
- )
31
- end
18
+ # @api private
19
+ # TODO: inline this or put it somewhere else as it's no longer specific to a rails version
20
+ def association_preloader(records:, associations:, scope:)
21
+ ActiveRecord::Associations::Preloader.new(
22
+ records: records, associations: associations, scope: scope
23
+ ).call
32
24
  end
33
25
  end
34
26
  end
@@ -8,7 +8,15 @@ module Thredded
8
8
  attr_accessor :allowlist
9
9
 
10
10
  # TODO: v2.0: drop alias and just use allowlist
11
- alias_attribute :whitelist, :allowlist
11
+ # @deprecated use allowlist
12
+ def whitelist
13
+ ActiveSupport::Deprecation.warn(<<~MESSAGE.squish)
14
+ ContentFormatter.whitelist is deprecated and will be removed in Thredded 2.0.
15
+ Use ContentFormatter.allowlist instead
16
+ MESSAGE
17
+
18
+ allowlist
19
+ end
12
20
 
13
21
  # Filters that run before processing the markup.
14
22
  # input: markup, output: markup.
@@ -30,17 +38,8 @@ module Thredded
30
38
  # input: sanitized html, output: html
31
39
  attr_accessor :after_sanitization_filters
32
40
 
33
- # TODO: v1.1: only allow html-pipeline >= 2.14.1 and drop this
34
- def sanitization_filter_uses_allowlist?
35
- defined?(HTML::Pipeline::SanitizationFilter::ALLOWLIST)
36
- end
37
-
38
41
  def sanitization_filter_allowlist_config
39
- if sanitization_filter_uses_allowlist?
40
- HTML::Pipeline::SanitizationFilter::ALLOWLIST
41
- else
42
- HTML::Pipeline::SanitizationFilter::WHITELIST
43
- end
42
+ HTML::Pipeline::SanitizationFilter::ALLOWLIST
44
43
  end
45
44
  end
46
45
 
@@ -150,15 +149,9 @@ module Thredded
150
149
 
151
150
  # @return [Hash] options for HTML::Pipeline.new
152
151
  def content_pipeline_options
153
- option = if self.class.sanitization_filter_uses_allowlist?
154
- :allowlist
155
- else
156
- # TODO: v1.1: only allow html-pipeline >= 2.14.1 and drop this
157
- :whitelist
158
- end
159
152
  {
160
153
  asset_root: Rails.application.config.action_controller.asset_host || '',
161
- option => ContentFormatter.allowlist
154
+ allowlist: ContentFormatter.allowlist
162
155
  }
163
156
  end
164
157
  end
@@ -10,6 +10,7 @@ rescue NameError
10
10
  end
11
11
  # rubocop:enable HandleExceptions
12
12
  module Thredded
13
+ # rubocop:disable Metrics/MethodLength
13
14
  class DatabaseSeeder # rubocop:disable Metrics/ClassLength
14
15
  module LogTime
15
16
  def self.included(base)
@@ -60,8 +61,16 @@ module Thredded
60
61
  def self.with_seeder_tweaks
61
62
  # Disable callbacks to avoid creating notifications and performing unnecessary updates
62
63
  DISABLE_COUNTER_CACHE.each do |klass|
63
- klass.send(:alias_method, :original_each_counter_cached_associations, :each_counter_cached_associations)
64
- klass.send(:define_method, :each_counter_cached_associations) {}
64
+ if Thredded::Compat.rails_gte_72?
65
+ klass.class_eval do
66
+ class_attribute :original_counter_cached_association_names
67
+ self.original_counter_cached_association_names = counter_cached_association_names
68
+ self.counter_cached_association_names = []
69
+ end
70
+ else
71
+ klass.send(:alias_method, :original_each_counter_cached_associations, :each_counter_cached_associations)
72
+ klass.send(:define_method, :each_counter_cached_associations) {}
73
+ end
65
74
  end
66
75
  SKIP_CALLBACKS.each { |(klass, *args)| delete_callbacks(klass, *args) }
67
76
  WRITEABLE_READONLY_ATTRIBUTES.each { |(klass, attr)| klass.readonly_attributes.delete(attr) }
@@ -71,9 +80,15 @@ module Thredded
71
80
  ensure
72
81
  # Re-enable callbacks and counter cache
73
82
  DISABLE_COUNTER_CACHE.each do |klass|
74
- klass.send(:remove_method, :each_counter_cached_associations)
75
- klass.send(:alias_method, :each_counter_cached_associations, :original_each_counter_cached_associations)
76
- klass.send(:remove_method, :original_each_counter_cached_associations)
83
+ if Thredded::Compat.rails_gte_72?
84
+ klass.class_eval do
85
+ self.counter_cached_association_names = original_counter_cached_association_names
86
+ end
87
+ else
88
+ klass.send(:remove_method, :each_counter_cached_associations)
89
+ klass.send(:alias_method, :each_counter_cached_associations, :original_each_counter_cached_associations)
90
+ klass.send(:remove_method, :original_each_counter_cached_associations)
91
+ end
77
92
  end
78
93
  SKIP_CALLBACKS.each do |(klass, *args)|
79
94
  args = args.dup
@@ -150,9 +165,7 @@ module Thredded
150
165
  end
151
166
 
152
167
  def user_details
153
- @user_details ||= users.each_with_object({}) do |user, hash|
154
- hash[user] = user.thredded_user_detail
155
- end
168
+ @user_details ||= users.index_with(&:thredded_user_detail)
156
169
  end
157
170
 
158
171
  def first_messageboard
@@ -175,7 +188,7 @@ module Thredded
175
188
  end
176
189
  end
177
190
 
178
- log_method_time def update_messageboards_data(**) # `**` for Ruby < 2.5, see https://bugs.ruby-lang.org/issues/10856
191
+ log_method_time def update_messageboards_data(**)
179
192
  log 'Updating messageboards data...'
180
193
  Messageboard.all.each do |messageboard|
181
194
  messageboard.update_last_topic!
@@ -330,7 +343,7 @@ module Thredded
330
343
  class FirstMessageboard < FirstSeedData
331
344
  MODEL_CLASS = Messageboard
332
345
 
333
- log_method_time def create(**) # `**` for Ruby < 2.5, see https://bugs.ruby-lang.org/issues/10856
346
+ log_method_time def create(**)
334
347
  log 'Creating a messageboard...'
335
348
  @first_messageboard = FactoryBot.create(
336
349
  :messageboard,
@@ -384,7 +397,8 @@ module Thredded
384
397
  class Posts < CollectionSeedData
385
398
  MODEL_CLASS = Post
386
399
 
387
- log_method_time def create(count: (1..1), topics: seeder.topics) # rubocop:disable Metrics/MethodLength
400
+ log_method_time def create(count: (1..1), topics: seeder.topics)
401
+ # rubocop:disable Metrics/MethodLength
388
402
  log "Creating #{count} additional posts in each topic..."
389
403
  topics.flat_map do |topic|
390
404
  last_post_at = random_duration(0..256.hours).ago
@@ -446,4 +460,5 @@ module Thredded
446
460
  end
447
461
  end
448
462
  end
463
+ # rubocop:enable Metrics/MethodLength
449
464
  end
@@ -10,11 +10,7 @@ module Thredded
10
10
  verbose_was = ActiveRecord::Migration.verbose
11
11
  ActiveRecord::Migration.verbose = !quiet
12
12
  migrate =
13
- if Thredded::Compat.rails_gte_60?
14
- -> { ActiveRecord::MigrationContext.new(paths, ActiveRecord::SchemaMigration).migrate(nil, &filter) }
15
- else # Rails 5.2
16
- -> { ActiveRecord::MigrationContext.new(paths).migrate(nil, &filter) }
17
- end
13
+ -> { ActiveRecord::MigrationContext.new(paths).migrate(nil, &filter) }
18
14
  if quiet
19
15
  silence_active_record(&migrate)
20
16
  else
@@ -20,5 +20,14 @@ module Thredded
20
20
  thredded_manifest.js
21
21
  ]
22
22
  end
23
+
24
+ config.after_initialize do |app|
25
+ if !Rails.env.production? && app.assets.preprocessors.keys.exclude?('text/scss')
26
+ fail [
27
+ 'Thredded requires a Sass compiler to be registered in Sprockets.',
28
+ %(Please add "sassc-rails" or "dartsass-sprockets" to your application Gemfile.),
29
+ ].join(' ')
30
+ end
31
+ end
23
32
  end
24
33
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Thredded
4
- VERSION = '1.0.1'
4
+ VERSION = '1.2.0'
5
5
  end
data/lib/thredded.rb CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  # Backend
4
4
  require 'pundit'
5
- require 'active_record_union'
6
5
  require 'db_text_search'
7
6
  require 'friendly_id'
8
7
  require 'html/pipeline'
@@ -29,7 +28,6 @@ require 'thredded/users_provider'
29
28
  require 'autoprefixer-rails'
30
29
  require 'timeago_js'
31
30
  require 'sprockets/es6'
32
- require 'sassc-rails'
33
31
 
34
32
  require 'thredded/version'
35
33
  require 'thredded/compat'