thredded 0.16.14 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -7
  3. data/app/assets/javascripts/thredded/components/preview_area.es6 +3 -1
  4. data/app/controllers/concerns/thredded/render_preview.rb +6 -0
  5. data/app/controllers/thredded/application_controller.rb +5 -10
  6. data/app/controllers/thredded/topics_controller.rb +0 -6
  7. data/app/helpers/thredded/application_helper.rb +1 -1
  8. data/app/helpers/thredded/icon_helper.rb +1 -1
  9. data/app/helpers/thredded/urls_helper.rb +1 -1
  10. data/app/models/concerns/thredded/post_common.rb +5 -4
  11. data/app/models/concerns/thredded/topic_common.rb +1 -1
  12. data/app/models/concerns/thredded/user_topic_read_state_common.rb +7 -8
  13. data/app/models/thredded/messageboard.rb +5 -4
  14. data/app/models/thredded/notifications_for_followed_topics.rb +1 -1
  15. data/app/models/thredded/post.rb +3 -2
  16. data/app/models/thredded/post_moderation_record.rb +8 -9
  17. data/app/models/thredded/private_post.rb +2 -2
  18. data/app/models/thredded/private_topic.rb +4 -2
  19. data/app/models/thredded/topic.rb +2 -2
  20. data/app/models/thredded/user_detail.rb +1 -1
  21. data/app/models/thredded/user_extender.rb +1 -1
  22. data/app/models/thredded/user_private_topic_read_state.rb +3 -0
  23. data/app/models/thredded/user_topic_read_state.rb +3 -0
  24. data/app/view_models/thredded/post_view.rb +1 -0
  25. data/app/view_models/thredded/posts_page_view.rb +10 -9
  26. data/app/view_models/thredded/private_topics_page_view.rb +10 -6
  27. data/app/view_models/thredded/topic_posts_page_view.rb +16 -3
  28. data/app/view_models/thredded/topics_page_view.rb +9 -6
  29. data/app/views/layouts/thredded/application.html.erb +2 -2
  30. data/app/views/thredded/messageboards/_form.html.erb +3 -3
  31. data/app/views/thredded/posts_common/_actions.html.erb +1 -1
  32. data/app/views/thredded/posts_common/_content.html.erb +1 -1
  33. data/app/views/thredded/posts_common/actions/_quote.html.erb +1 -1
  34. data/app/views/thredded/shared/_page.html.erb +1 -1
  35. data/app/views/thredded/shared/nav/_moderation.html.erb +3 -3
  36. data/app/views/thredded/shared/nav/_notification_preferences.html.erb +1 -1
  37. data/app/views/thredded/shared/nav/_private_topics.html.erb +3 -3
  38. data/app/views/thredded/shared/nav/_standalone.html.erb +2 -2
  39. data/app/views/thredded/topics/_header.html.erb +1 -1
  40. data/bin/create_migration_fixture +23 -0
  41. data/config/i18n-tasks.yml +5 -0
  42. data/config/locales/de.yml +2 -0
  43. data/config/locales/en.yml +3 -1
  44. data/config/locales/es.yml +2 -0
  45. data/config/locales/fr.yml +2 -0
  46. data/config/locales/it.yml +4 -2
  47. data/config/locales/pl.yml +2 -0
  48. data/config/locales/pt-BR.yml +2 -0
  49. data/config/locales/ru.yml +2 -0
  50. data/config/locales/zh-CN.yml +2 -0
  51. data/db/upgrade_migrations/20180110200009_upgrade_thredded_v0_14_to_v0_15.rb +1 -1
  52. data/lib/generators/thredded/install/templates/initializer.rb +2 -2
  53. data/lib/thredded/arel_compat.rb +0 -32
  54. data/lib/thredded/base_migration.rb +1 -1
  55. data/lib/thredded/collection_to_strings_with_cache_renderer.rb +91 -28
  56. data/lib/thredded/compat.rb +35 -0
  57. data/lib/thredded/database_seeder.rb +11 -4
  58. data/lib/thredded/db_tools.rb +2 -4
  59. data/lib/thredded/email_transformer.rb +2 -2
  60. data/lib/thredded/html_pipeline/onebox_filter.rb +1 -4
  61. data/lib/thredded/version.rb +1 -1
  62. data/lib/thredded.rb +3 -23
  63. metadata +43 -31
  64. data/bin/rubocop +0 -18
@@ -1,16 +1,16 @@
1
1
  <%= form_for messageboard, html: { class: 'thredded--form' } do |f| %>
2
2
  <ul class="thredded--form-list">
3
3
  <li>
4
- <%= f.label t('thredded.messageboard.form.title_label') %>
4
+ <%= f.label :name, t('thredded.messageboard.form.title_label') %>
5
5
  <%= f.text_field :name, required: true %>
6
6
  <%= render 'thredded/shared/field_errors', messages: f.object.errors[:name] %>
7
7
  </li>
8
8
  <li>
9
- <%= f.label t('thredded.messageboard.form.description_label') %>
9
+ <%= f.label :description, t('thredded.messageboard.form.description_label') %>
10
10
  <%= f.text_field :description %>
11
11
  </li>
12
12
  <li>
13
- <%= f.label t('thredded.messageboard.form.messageboard_group_id_label') %>
13
+ <%= f.label :messageboard_group_id, t('thredded.messageboard.form.messageboard_group_id_label') %>
14
14
  <%= f.collection_select :messageboard_group_id, Thredded::MessageboardGroup.all, :id, :name,
15
15
  include_blank: t('thredded.messageboard.form.no_group') %>
16
16
  </li>
@@ -20,7 +20,7 @@
20
20
 
21
21
  <%- if actions_html.present? %>
22
22
  <div class='thredded--post--dropdown'>
23
- <%= inline_svg 'thredded/three-dot-menu.svg', class: 'thredded--post--dropdown--toggle' %>
23
+ <%= inline_svg_tag 'thredded/three-dot-menu.svg', class: 'thredded--post--dropdown--toggle' %>
24
24
  <div class='thredded--post--dropdown--actions'>
25
25
  <%= actions_html %>
26
26
  </div>
@@ -1,3 +1,3 @@
1
1
  <div class="thredded--post--content">
2
- <%= post.filtered_content(self, local_assigns[:options] || {}) %>
2
+ <%= post.filtered_content(self, **(local_assigns[:options] || {})) %>
3
3
  </div>
@@ -1,4 +1,4 @@
1
- <%= link_to t('thredded.posts.quote_btn'), url_for(post.quote_url_params),
1
+ <%= link_to t('thredded.posts.quote_btn'), post.quote_url_params,
2
2
  'data-thredded-quote-post' => post.quote_path,
3
3
  class: 'thredded--post--quote thredded--post--dropdown--actions--item',
4
4
  rel: 'nofollow' %>
@@ -8,7 +8,7 @@
8
8
  <%# If thredded JS is loaded via an [async] script, the JS may
9
9
  run before or after DOMContentLoaded. Expose a flag to Thredded
10
10
  so it can initialize correctly. %>
11
- <%= javascript_tag 'data-turbolinks-eval': 'false', **(Thredded.rails_supports_csp_nonce? ? {nonce: true} : {}) do %>
11
+ <%= javascript_tag 'data-turbolinks-eval': 'false', nonce: true do %>
12
12
  document.addEventListener('DOMContentLoaded', function() {
13
13
  (window.Thredded = window.Thredded || {}).DOMContentLoadedFired = true;
14
14
  });
@@ -2,9 +2,9 @@
2
2
  <% current = current_page_moderation? %>
3
3
  <li class="thredded--user-navigation--item thredded--user-navigation--moderation<%= ' thredded--is-current' if current %>">
4
4
  <%= link_to current ? nav_back_path : pending_moderation_path, rel: 'nofollow' do %>
5
- <%= inline_svg 'thredded/moderation.svg',
6
- class: 'thredded--icon',
7
- title: t('thredded.nav.moderation') %>
5
+ <%= inline_svg_tag 'thredded/moderation.svg',
6
+ class: 'thredded--icon',
7
+ title: t('thredded.nav.moderation') %>
8
8
  <span class="thredded--nav-text"><%= t 'thredded.nav.moderation' %></span>
9
9
  <% if posts_pending_moderation_count > 0 %>
10
10
  <span class="thredded--user-navigation--moderation--pending-count"><%= posts_pending_moderation_count %></span>
@@ -1,7 +1,7 @@
1
1
  <% current = current_page_preferences? %>
2
2
  <li class="thredded--user-navigation--settings thredded--user-navigation--item<%= ' thredded--is-current' if current %>">
3
3
  <%= link_to current ? nav_back_path(messageboard) : edit_preferences_path(messageboard), rel: 'nofollow' do %>
4
- <%= inline_svg 'thredded/settings.svg', class: 'thredded--icon', title: t('thredded.nav.settings') %>
4
+ <%= inline_svg_tag 'thredded/settings.svg', class: 'thredded--icon', title: t('thredded.nav.settings') %>
5
5
  <span class="thredded--nav-text"><%= t('thredded.nav.settings') %></span>
6
6
  <% end %>
7
7
  </li>
@@ -1,9 +1,9 @@
1
1
  <% current = current_page_private_topics? %>
2
2
  <li class="thredded--user-navigation--item thredded--user-navigation--private-topics<%= ' thredded--is-current' if current %>">
3
3
  <%= link_to current ? nav_back_path : private_topics_path, rel: 'nofollow' do %>
4
- <%= inline_svg 'thredded/private-messages.svg',
5
- class: 'thredded--icon',
6
- title: t('thredded.nav.private_topics') %>
4
+ <%= inline_svg_tag 'thredded/private-messages.svg',
5
+ class: 'thredded--icon',
6
+ title: t('thredded.nav.private_topics') %>
7
7
  <span class="thredded--nav-text"><%= t('thredded.nav.private_topics') %></span>
8
8
  <% if unread_private_topics_count > 0 -%>
9
9
  <span class="thredded--user-navigation--private-topics--unread"><%= unread_private_topics_count %></span>
@@ -2,11 +2,11 @@
2
2
  <% resource_name = Thredded.user_class_name.demodulize.underscore %>
3
3
  <% if thredded_signed_in? %>
4
4
  <%= link_to main_app.send(:"destroy_#{resource_name}_session_path"), method: :delete do %>
5
- Sign Out
5
+ t('thredded.nav.sign_out')
6
6
  <% end %>
7
7
  <% else %>
8
8
  <%= link_to main_app.send(:"new_#{resource_name}_session_path") do %>
9
- Sign In / Register
9
+ t('thredded.nav.sign_in')
10
10
  <% end %>
11
11
  <% end %>
12
12
  </li>
@@ -20,7 +20,7 @@
20
20
  </span>
21
21
  <%= button_to topic.unfollow_path, form: {class: 'thredded--topic-header--follow-info--unfollow'} do %>
22
22
  <%= t('thredded.topics.unfollow') %>
23
- <%= inline_svg 'thredded/follow.svg', class: 'thredded--topic-header--follow-icon' %>
23
+ <%= inline_svg_tag 'thredded/follow.svg', class: 'thredded--topic-header--follow-icon' %>
24
24
  <% end %>
25
25
  </div>
26
26
  <% else %>
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # edit constants in lib/thredded/db_tools.rb
4
+ # git checkout v0.x.0 -b create-0.x-migration-fixture
5
+ # bundle
6
+
7
+ DIR=$(pwd)
8
+
9
+ cd spec/dummy && DB=sqlite3 rails db:environment:set RAILS_ENV=development
10
+ cd $DIR
11
+ DB=sqlite3 rake db:drop db:create db:migrate db:miniseed_dump
12
+
13
+ cd spec/dummy && DB=postgresql rails db:environment:set RAILS_ENV=development
14
+ cd $DIR
15
+ DB=postgresql rake db:drop db:create db:migrate db:miniseed_dump
16
+
17
+ cd spec/dummy && DB=mysql2 rails db:environment:set RAILS_ENV=development
18
+ cd $DIR
19
+ DB=mysql2 rake db:drop db:create db:migrate db:miniseed_dump
20
+
21
+ git add `pwd`/spec/migration/*.dump
22
+
23
+ git commit -em "add sample data for 0.8 base for migration spec"
@@ -15,3 +15,8 @@ data:
15
15
  yaml:
16
16
  write:
17
17
  line_width: 110
18
+
19
+ ## Exclude these keys from the `i18n-tasks check-consistent-interpolations` report:
20
+ ignore_inconsistent_interpolations:
21
+ - 'thredded.users.posts_count.one'
22
+ - 'thredded.users.started_topics_count.one'
@@ -128,6 +128,8 @@ de:
128
128
  moderation_users: Nutzer
129
129
  private_topics: Private Unterhaltungen
130
130
  settings: Benachrichtigungseinstellungen
131
+ sign_in: Anmelden
132
+ sign_out: Ausloggen
131
133
  unread_topics: Ungelesen
132
134
  null_user_name: Gelöschte Nutzer
133
135
  posts:
@@ -127,6 +127,8 @@ en:
127
127
  moderation_users: Users
128
128
  private_topics: Private Messages
129
129
  settings: Notification Settings
130
+ sign_in: Sign In / Register
131
+ sign_out: Sign Out
130
132
  unread_topics: Unread
131
133
  null_user_name: Deleted user
132
134
  posts:
@@ -262,7 +264,7 @@ en:
262
264
  last_active_html: Last active %{time_ago}
263
265
  posted_in_topic_html: Posted in %{topic_link}
264
266
  posts_count:
265
- one: Posted %{count} time
267
+ one: Posted once
266
268
  other: Posted %{count} times
267
269
  recent_activity: :thredded.recent_activity
268
270
  send_private_message: Send private message
@@ -129,6 +129,8 @@ es:
129
129
  moderation_users: Usuarios
130
130
  private_topics: Mensajes Privados
131
131
  settings: Ajuste de Notificaciones
132
+ sign_in: Registrarse
133
+ sign_out: Desconectar
132
134
  unread_topics: No leído
133
135
  null_user_name: Usuario eliminado
134
136
  posts:
@@ -127,6 +127,8 @@ fr:
127
127
  moderation_users: Utilisateurs
128
128
  private_topics: Messages privés
129
129
  settings: Paramètres de notifications
130
+ sign_in: Se connecter
131
+ sign_out: Déconnexion
130
132
  unread_topics: Non lu
131
133
  null_user_name: Utilisateur effacé
132
134
  posts:
@@ -127,6 +127,8 @@ it:
127
127
  moderation_users: Utenti
128
128
  private_topics: Messaggi Privati
129
129
  settings: Impostazioni Notifiche
130
+ sign_in: Entra
131
+ sign_out: Esci
130
132
  unread_topics: Non letto
131
133
  null_user_name: Utente cancellato
132
134
  posts:
@@ -266,13 +268,13 @@ it:
266
268
  last_active_html: Ultima attività %{time_ago}
267
269
  posted_in_topic_html: Ha scritto in %{topic_link}
268
270
  posts_count:
269
- one: Ha commentato %{count} volta
271
+ one: Ha commentato una volta
270
272
  other: Ha commentato %{count} volte
271
273
  recent_activity: :thredded.recent_activity
272
274
  send_private_message: Invia un messaggio privato
273
275
  started_topic_html: Ha iniziato %{topic_link}
274
276
  started_topics_count:
275
- one: Ha iniziato %{count} discussione
277
+ one: Ha iniziato una discussione
276
278
  other: Ha iniziato %{count} discussioni
277
279
  user_posted_in_topic_html: "%{user_link} ha commentato in %{topic_link}"
278
280
  user_since_html: Utente da %{time_ago}
@@ -127,6 +127,8 @@ pl:
127
127
  moderation_users: Użytkownicy
128
128
  private_topics: Prywatne wiadomości
129
129
  settings: Ustawienia powiadomień
130
+ sign_in: Zaloguj
131
+ sign_out: Wyloguj się
130
132
  unread_topics: Nieprzeczytane
131
133
  null_user_name: Usunięci użytkownicy
132
134
  posts:
@@ -129,6 +129,8 @@ pt-BR:
129
129
  moderation_users: Usuários
130
130
  private_topics: Mensagens Privadas
131
131
  settings: Configurações de Notificação
132
+ sign_in: Entrar / Cadastrar
133
+ sign_out: Sair
132
134
  unread_topics: Não lida
133
135
  null_user_name: Usuário deletado
134
136
  posts:
@@ -125,6 +125,8 @@ ru:
125
125
  moderation_users: Пользователи
126
126
  private_topics: Личное
127
127
  settings: Настройки
128
+ sign_in: Войти / Зарегистрироваться
129
+ sign_out: Выход
128
130
  unread_topics: Непрочитанные
129
131
  null_user_name: Пользователь удален
130
132
  posts:
@@ -120,6 +120,8 @@ zh-CN:
120
120
  moderation_users: 用户
121
121
  private_topics: 私人对话
122
122
  settings: 通知设置
123
+ sign_in: 登录/注册
124
+ sign_out: 登出
123
125
  unread_topics: 未读
124
126
  null_user_name: 删除用户
125
127
  posts:
@@ -86,6 +86,6 @@ class UpgradeThreddedV014ToV015 < Thredded::BaseMigration
86
86
  def remove_string_limit(table, column, type: :text, indices: [])
87
87
  indices.each { |(_, options)| remove_index table, name: options[:name] }
88
88
  change_column table, column, type, limit: nil
89
- indices.each { |args| add_index table, *args }
89
+ indices.each { |(columns, options)| add_index table, columns, **options }
90
90
  end
91
91
  end
@@ -29,8 +29,8 @@ Thredded.user_path = ->(user) {
29
29
  # This method is used by Thredded controllers and views to fetch the currently signed-in user
30
30
  Thredded.current_user_method = :"current_#{Thredded.user_class_name.demodulize.underscore}"
31
31
 
32
- # User avatar URL. rb-gravatar gem is used by default:
33
- Thredded.avatar_url = ->(user) { Gravatar.src(user.email, 156, 'mm') }
32
+ # User avatar URL. rails_gravatar gem is used by default:
33
+ Thredded.avatar_url = ->(user) { RailsGravatar.src(user.email, 156, 'mm') }
34
34
 
35
35
  # ==> Permissions Configuration
36
36
  # By default, thredded uses a simple permission model, where all the users can post to all message boards,
@@ -1,27 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- unless Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR >= 2 || Rails::VERSION::MAJOR > 5
4
- require 'thredded/rails_lt_5_2_arel_case_node.rb'
5
- end
6
-
7
- if Rails::VERSION::MAJOR == 4
8
- # Make `pluck` compatible with Arel.
9
- require 'active_record/relation'
10
- ActiveRecord::Relation.prepend(Module.new do
11
- def pluck(*column_names)
12
- super(*column_names.map do |n|
13
- if n.is_a?(Arel::Node)
14
- Arel.sql(n.to_sql)
15
- elsif n.is_a?(Arel::Attributes::Attribute)
16
- n.name
17
- else
18
- n
19
- end
20
- end)
21
- end
22
- end)
23
- end
24
-
25
3
  module Thredded
26
4
  module ArelCompat
27
5
  module_function
@@ -37,15 +15,5 @@ module Thredded
37
15
  Arel::Nodes::Division.new(a, b)
38
16
  end
39
17
  end
40
-
41
- if Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR >= 2 || Rails::VERSION::MAJOR > 5
42
- def true_value(_engine)
43
- true
44
- end
45
- else
46
- def true_value(engine)
47
- engine.connection.adapter_name =~ /sqlite|mysql|mariadb/i ? 1 : true
48
- end
49
- end
50
18
  end
51
19
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Thredded
4
- class BaseMigration < (Thredded.rails_gte_51? ? ActiveRecord::Migration[5.1] : ActiveRecord::Migration)
4
+ class BaseMigration < ActiveRecord::Migration[5.1]
5
5
  protected
6
6
 
7
7
  def user_id_type
@@ -22,17 +22,18 @@ module Thredded
22
22
  def render_collection_to_strings_with_cache( # rubocop:disable Metrics/ParameterLists
23
23
  view_context, collection:, partial:, expires_in:, render_threads: self.class.render_threads, locals: {}, **opts
24
24
  )
25
- template = @lookup_context.find_template(partial, [], true, locals, {})
25
+ template = @lookup_context.find_template(partial, [], true, locals.keys, {})
26
26
  collection = collection.to_a
27
- instrument(:collection, identifier: template.identifier, count: collection.size) do |instrumentation_payload|
27
+ ActiveSupport::Notifications.instrument(:collection,
28
+ identifier: template.identifier,
29
+ count: collection.size) do |instrumentation_payload|
28
30
  return [] if collection.blank?
29
- keyed_collection = collection.each_with_object({}) do |item, hash|
30
- key = ActiveSupport::Cache.expand_cache_key(
31
- view_context.cache_fragment_name(item, virtual_path: template.virtual_path), :views
32
- )
33
- # #read_multi & #write may require key mutability, Dalli 2.6.0.
34
- hash[key.frozen? ? key.dup : key] = item
35
- end
31
+
32
+ # Result is a hash with the key represents the
33
+ # key used for cache lookup and the value is the item
34
+ # on which the partial is being rendered
35
+ keyed_collection, ordered_keys = collection_by_cache_keys(collection, view_context, template)
36
+
36
37
  cache = collection_cache
37
38
  cached_partials = cache.read_multi(*keyed_collection.keys)
38
39
  instrumentation_payload[:cache_hits] = cached_partials.size if instrumentation_payload
@@ -44,9 +45,9 @@ module Thredded
44
45
  partial: partial, locals: locals, **opts
45
46
  ).each
46
47
 
47
- keyed_collection.map do |cache_key, item|
48
- [item, cached_partials[cache_key] || rendered_partials.next.tap do |rendered|
49
- cache.write(cache_key, rendered, expires_in: expires_in)
48
+ ordered_keys.map do |cache_key|
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
51
  end]
51
52
  end
52
53
  end
@@ -54,16 +55,24 @@ module Thredded
54
55
 
55
56
  private
56
57
 
57
- def collection_cache
58
- if ActionView::PartialRenderer.respond_to?(:collection_cache)
59
- # Rails 5.0+
60
- ActionView::PartialRenderer.collection_cache
61
- else
62
- # Rails 4.2.x
63
- Rails.application.config.action_controller.cache_store
58
+ def collection_by_cache_keys(collection, view, template)
59
+ digest_path = digest_path_from_template(view, template)
60
+
61
+ collection.each_with_object([{}, []]) do |item, (hash, ordered_keys)|
62
+ key = expanded_cache_key(item, view, template, digest_path)
63
+ ordered_keys << key
64
+ hash[key] = item
64
65
  end
65
66
  end
66
67
 
68
+ def expanded_cache_key(key, view, template, digest_path)
69
+ key = combined_fragment_cache_key(
70
+ view,
71
+ cache_fragment_name(view, key, virtual_path: template.virtual_path, digest_path: digest_path)
72
+ )
73
+ key.frozen? ? key.dup : key # #read_multi & #write may require mutability, Dalli 2.6.0.
74
+ end
75
+
67
76
  # @return [Array<String>]
68
77
  def render_partials(view_context, collection:, render_threads:, **opts)
69
78
  return [] if collection.empty?
@@ -72,25 +81,79 @@ module Thredded
72
81
  render_partials_serial(view_context, collection, opts)
73
82
  else
74
83
  collection.each_slice(collection.size / num_threads).map do |slice|
75
- Thread.start { render_partials_serial(view_context.dup, slice, opts) }
84
+ 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
+ ActiveRecord::Base.connection_pool.with_connection do
90
+ render_partials_serial(view_context.dup, slice, opts)
91
+ end
92
+ end
76
93
  end.flat_map(&:value)
77
94
  end
78
95
  end
79
96
 
80
- # @param [Array<Object>] collection
81
- # @param [Hash] opts
82
- # @param view_context
83
- # @return [Array<String>]
84
- def render_partials_serial(view_context, collection, opts)
85
- partial_renderer = ActionView::PartialRenderer.new(@lookup_context)
86
- collection.map { |object| render_partial(partial_renderer, view_context, opts.merge(object: object)) }
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
108
+ 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)) }
113
+ end
87
114
  end
88
115
 
89
- if Rails::VERSION::MAJOR >= 6
116
+ def collection_cache
117
+ ActionView::PartialRenderer.collection_cache
118
+ end
119
+
120
+ def combined_fragment_cache_key(view, key)
121
+ view.combined_fragment_cache_key(key)
122
+ end
123
+
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
+
90
137
  def render_partial(partial_renderer, view_context, opts)
91
138
  partial_renderer.render(view_context, opts, nil).body
92
139
  end
93
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
148
+
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
156
+
94
157
  def render_partial(partial_renderer, view_context, opts)
95
158
  partial_renderer.render(view_context, opts, nil)
96
159
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thredded
4
+ module Compat
5
+ class << self
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
10
+ end
11
+
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
16
+ end
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
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'factory_bot'
4
- require_relative '../../spec/support/features/fake_content'
4
+ require_relative '../../spec/support/system/fake_content'
5
5
 
6
6
  # rubocop:disable HandleExceptions
7
7
  begin
@@ -40,6 +40,7 @@ module Thredded
40
40
  end
41
41
 
42
42
  include LogTime
43
+ include Thredded::ApplicationHelper
43
44
 
44
45
  SKIP_CALLBACKS = [
45
46
  [Thredded::Post, :commit, :after, :update_parent_last_user_and_time_from_last_post, on: %i[create destroy]],
@@ -131,7 +132,13 @@ module Thredded
131
132
  end
132
133
 
133
134
  def fake_post_contents
134
- @fake_post_contents ? @fake_post_contents.sample : FakeContent.post_content
135
+ with_mentions(@fake_post_contents ? @fake_post_contents.sample : FakeContent.post_content)
136
+ end
137
+
138
+ def with_mentions(post, mentions_count: rand(3))
139
+ return post if mentions_count.zero?
140
+
141
+ ([post] + Array.new(mentions_count).map { user_mention(@users.sample) }).join(' ')
135
142
  end
136
143
 
137
144
  def first_user
@@ -255,9 +262,9 @@ module Thredded
255
262
 
256
263
  delegate :log, to: :seeder
257
264
 
258
- def find_or_create(*args)
265
+ def find_or_create(*args, **kwargs)
259
266
  return @stored if @stored
260
- @stored = (find || create(*args))
267
+ @stored = (find || create(*args, **kwargs))
261
268
  end
262
269
 
263
270
  def range_of_dates_in_order(up_to: Time.zone.now, count: 1)
@@ -10,12 +10,10 @@ module Thredded
10
10
  verbose_was = ActiveRecord::Migration.verbose
11
11
  ActiveRecord::Migration.verbose = !quiet
12
12
  migrate =
13
- if Rails.gem_version >= Gem::Version.new('6.0.0.rc2')
13
+ if Thredded::Compat.rails_gte_60?
14
14
  -> { ActiveRecord::MigrationContext.new(paths, ActiveRecord::SchemaMigration).migrate(nil, &filter) }
15
- elsif Rails::VERSION::STRING >= '5.2'
15
+ else # Rails 5.2
16
16
  -> { ActiveRecord::MigrationContext.new(paths).migrate(nil, &filter) }
17
- else
18
- -> { ActiveRecord::Migrator.migrate(paths, &filter) }
19
17
  end
20
18
  if quiet
21
19
  silence_active_record(&migrate)
@@ -17,8 +17,8 @@ module Thredded
17
17
  end
18
18
  @transformers = [Onebox, Spoiler]
19
19
 
20
- # @param doc [Nokogiri::HTML::Document]
21
- def self.call(doc)
20
+ # @param dom [Nokogiri::HTML::Document]
21
+ def self.call(doc, *)
22
22
  transformers.each { |transformer| transformer.call(doc) }
23
23
  end
24
24
  end
@@ -39,12 +39,10 @@ module Thredded
39
39
  # Regardless, always store the onebox in a file cache to enable offline development,
40
40
  # persistence between test runs, and to improve performance.
41
41
  attr_accessor :onebox_views_cache
42
- attr_accessor :onebox_data_cache
43
42
  end
44
43
 
45
44
  if Rails.env.development? || Rails.env.test?
46
45
  self.onebox_views_cache = ActiveSupport::Cache::FileStore.new('tmp/cache/onebox-views')
47
- self.onebox_data_cache = ActiveSupport::Cache::FileStore.new('tmp/cache/onebox-data')
48
46
  end
49
47
 
50
48
  def call
@@ -84,8 +82,7 @@ module Thredded
84
82
  end
85
83
 
86
84
  def onebox_options(_url)
87
- { cache: context[:onebox_data_cache] || self.class.onebox_data_cache || Rails.cache,
88
- sanitize_config: SANITIZE_CONFIG }
85
+ { sanitize_config: SANITIZE_CONFIG }
89
86
  end
90
87
 
91
88
  def onebox_views_cache
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Thredded
4
- VERSION = '0.16.14'
4
+ VERSION = '1.0.0'
5
5
  end