thredded 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +27 -19
- data/app/commands/thredded/create_messageboard.rb +2 -2
- data/app/forms/thredded/private_topic_form.rb +1 -1
- data/app/helpers/thredded/application_helper.rb +2 -2
- data/app/helpers/thredded/urls_helper.rb +2 -0
- data/app/mailer_previews/thredded/base_mailer_preview.rb +5 -5
- data/app/models/concerns/thredded/friendly_id_reserved_words_and_pagination.rb +1 -1
- data/app/models/concerns/thredded/notifier_preference.rb +1 -1
- data/app/models/concerns/thredded/post_common.rb +3 -3
- data/app/models/thredded/post_moderation_record.rb +4 -4
- data/app/view_models/thredded/topics_page_view.rb +1 -0
- data/app/views/thredded/moderation/_post_moderation_record.html.erb +3 -3
- data/app/views/thredded/shared/nav/_standalone.html.erb +2 -2
- data/bin/rubocop +29 -0
- data/bin/run_specs_repeatedly +9 -0
- data/config/i18n-tasks.yml +1 -1
- data/db/upgrade_migrations/20161019150201_upgrade_v0_7_to_v0_8.rb +5 -5
- data/lib/generators/thredded/install/templates/initializer.rb +1 -1
- data/lib/thredded/arel_compat.rb +1 -1
- data/lib/thredded/base_migration.rb +1 -1
- data/lib/thredded/collection_to_strings_with_cache_renderer.rb +29 -39
- data/lib/thredded/compat.rb +0 -6
- data/lib/thredded/content_formatter.rb +23 -8
- data/lib/thredded/db_tools.rb +2 -6
- data/lib/thredded/engine.rb +1 -1
- data/lib/thredded/formatting_demo_content.rb +21 -21
- data/lib/thredded/html_pipeline/at_mention_filter.rb +1 -1
- data/lib/thredded/html_pipeline/onebox_filter.rb +3 -3
- data/lib/thredded/html_pipeline/utils.rb +1 -1
- data/lib/thredded/version.rb +1 -1
- metadata +24 -15
- data/lib/thredded/rails_lt_5_2_arel_case_node.rb +0 -119
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 19cde941264a9f9b9427694c27cc5f2a668fd18debbfe459d3fc58087e5636bf
|
4
|
+
data.tar.gz: 0d4eec1e395f2265783f9283ad958ec2a7f486a017bb961af87b26c2fd711c20
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f7d6aa4fd6d1eaecda06e839b757d6fa2bbf7fd24ae6ef4ba3c238a5d8880baeff1be6b9ee2aae21b34c8f844d0aa5eb5c41a1f70e7841cbe867a900e29b980a
|
7
|
+
data.tar.gz: 04af99833299683e35b13468e957bd75880a1627db1ff6c9ab53e1678f99842b273cc50ed529c9cddeedb7600c0d54b404550fd8858a7901346d6c72df77bdb2
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
# Thredded [![Code Climate](https://codeclimate.com/github/thredded/thredded/badges/gpa.svg)](https://codeclimate.com/github/thredded/thredded) [![Travis-CI](https://api.travis-ci.org/thredded/thredded.svg?branch=
|
1
|
+
# Thredded [![Code Climate](https://codeclimate.com/github/thredded/thredded/badges/gpa.svg)](https://codeclimate.com/github/thredded/thredded) [![Travis-CI](https://api.travis-ci.org/thredded/thredded.svg?branch=main)](https://travis-ci.org/thredded/thredded/) [![Test Coverage](https://codeclimate.com/github/thredded/thredded/badges/coverage.svg)](https://codeclimate.com/github/thredded/thredded/coverage) [![Inline docs](http://inch-ci.org/github/thredded/thredded.svg?branch=main)](http://inch-ci.org/github/thredded/thredded) [![Gitter](https://badges.gitter.im/thredded/thredded.svg)](https://gitter.im/thredded/thredded?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
2
2
|
|
3
|
-
_Thredded_ is a Rails
|
3
|
+
_Thredded_ is a Rails 6.0+ forum/messageboard engine. Its goal is to be as simple and feature rich as possible.
|
4
4
|
|
5
5
|
Some of the features currently in Thredded:
|
6
6
|
|
@@ -38,6 +38,7 @@ Table of Contents
|
|
38
38
|
* [Adding Thredded to an existing Rails app](#adding-thredded-to-an-existing-rails-app)
|
39
39
|
* [Upgrading an existing install](#upgrading-an-existing-install)
|
40
40
|
* [Migrating from Forem](#migrating-from-forem)
|
41
|
+
* [Rails compatibility](#rails-compatibility)
|
41
42
|
* [Views and other assets](#views-and-other-assets)
|
42
43
|
* [Standalone layout](#standalone-layout)
|
43
44
|
* [Application layout](#application-layout)
|
@@ -96,7 +97,7 @@ Then, see the rest of this Readme for more information about using and customizi
|
|
96
97
|
Add the gem to your Gemfile:
|
97
98
|
|
98
99
|
```ruby
|
99
|
-
gem 'thredded', '~> 1.
|
100
|
+
gem 'thredded', '~> 1.1'
|
100
101
|
```
|
101
102
|
|
102
103
|
Add the Thredded [initializer] to your parent app by running the install generator.
|
@@ -155,6 +156,15 @@ to Thredded.
|
|
155
156
|
[forem-to-thredded]: https://github.com/thredded/thredded/wiki/Migrate-from-Forem
|
156
157
|
[Forem]: https://github.com/rubysherpas/forem
|
157
158
|
|
159
|
+
## Rails compatibility
|
160
|
+
|
161
|
+
| Rails | Latest Thredded |
|
162
|
+
| ------------- |------------------|
|
163
|
+
| Rails 6.0+ | Thredded 1.1+ |
|
164
|
+
| Rails 5.2 | Thredded 1.0.1 |
|
165
|
+
| Rails 4.2 | Thredded 0.16.16 |
|
166
|
+
|
167
|
+
|
158
168
|
## Views and other assets
|
159
169
|
|
160
170
|
### Standalone layout
|
@@ -352,7 +362,7 @@ with a "themed" version of thredded.
|
|
352
362
|
#### Styles
|
353
363
|
|
354
364
|
Thredded comes with a light Sass theme controlled by a handful of variables that can be found here:
|
355
|
-
https://github.com/thredded/thredded/blob/
|
365
|
+
https://github.com/thredded/thredded/blob/main/app/assets/stylesheets/thredded/base/_variables.scss.
|
356
366
|
|
357
367
|
To override the styles, override the variables *before* importing Thredded styles, e.g.:
|
358
368
|
|
@@ -366,8 +376,8 @@ If you are writing a Thredded plugin, import the [`thredded/base`][thredded-scss
|
|
366
376
|
The `base` package only defines variables, mixins, and %-placeholders, so it can be imported safely without producing
|
367
377
|
any duplicate CSS.
|
368
378
|
|
369
|
-
[thredded-scss-dependencies]: https://github.com/thredded/thredded/blob/
|
370
|
-
[thredded-scss-base]: https://github.com/thredded/thredded/blob/
|
379
|
+
[thredded-scss-dependencies]: https://github.com/thredded/thredded/blob/main/app/assets/stylesheets/thredded/_dependencies.scss
|
380
|
+
[thredded-scss-base]: https://github.com/thredded/thredded/blob/main/app/assets/stylesheets/thredded/_base.scss
|
371
381
|
|
372
382
|
### Email and other notifications
|
373
383
|
|
@@ -513,35 +523,35 @@ Below is an overview of the default permissions, with links to the implementatio
|
|
513
523
|
<tbody>
|
514
524
|
<tr>
|
515
525
|
<th align="center">Logged in</th>
|
516
|
-
<td align="center" rowspan="2"><a href="https://github.com/thredded/thredded/blob/
|
526
|
+
<td align="center" rowspan="2"><a href="https://github.com/thredded/thredded/blob/main/app/models/thredded/user_permissions/read/all.rb">
|
517
527
|
✅ All
|
518
528
|
</a></td>
|
519
|
-
<td align="center"><a href="https://github.com/thredded/thredded/blob/
|
529
|
+
<td align="center"><a href="https://github.com/thredded/thredded/blob/main/app/models/thredded/user_permissions/write/all.rb">
|
520
530
|
✅ All
|
521
531
|
</a></td>
|
522
|
-
<td align="center"><a href="https://github.com/thredded/thredded/blob/
|
532
|
+
<td align="center"><a href="https://github.com/thredded/thredded/blob/main/app/models/thredded/user_permissions/message/readers_of_writeable_boards.rb">
|
523
533
|
Readers of the messageboards<br>the user can post in
|
524
534
|
</a></td>
|
525
|
-
<td align="center"><a href="https://github.com/thredded/thredded/blob/
|
535
|
+
<td align="center"><a href="https://github.com/thredded/thredded/blob/main/app/models/thredded/user_permissions/moderate/if_moderator_column_true.rb">
|
526
536
|
<code>moderator_column</code>
|
527
537
|
</a></td>
|
528
|
-
<td align="center"><a href="https://github.com/thredded/thredded/blob/
|
538
|
+
<td align="center"><a href="https://github.com/thredded/thredded/blob/main/app/models/thredded/user_permissions/admin/if_admin_column_true.rb">
|
529
539
|
<code>admin_column</code>
|
530
540
|
</a></td>
|
531
541
|
</tr>
|
532
542
|
<tr>
|
533
543
|
<th align="center">Not logged in</th>
|
534
544
|
<!-- rowspan -->
|
535
|
-
<td align="center"><a href="https://github.com/thredded/thredded/blob/
|
545
|
+
<td align="center"><a href="https://github.com/thredded/thredded/blob/main/app/models/thredded/user_permissions/write/none.rb">
|
536
546
|
❌ No
|
537
547
|
</a></td>
|
538
|
-
<td align="center"><a href="https://github.com/thredded/thredded/blob/
|
548
|
+
<td align="center"><a href="https://github.com/thredded/thredded/blob/main/app/models/thredded/user_permissions/message/readers_of_writeable_boards.rb">
|
539
549
|
❌ No
|
540
550
|
</a></td>
|
541
|
-
<td align="center"><a href="https://github.com/thredded/thredded/blob/
|
551
|
+
<td align="center"><a href="https://github.com/thredded/thredded/blob/main/app/models/thredded/user_permissions/moderate/none.rb">
|
542
552
|
❌ No
|
543
553
|
</a></td>
|
544
|
-
<td align="center"><a href="https://github.com/thredded/thredded/blob/
|
554
|
+
<td align="center"><a href="https://github.com/thredded/thredded/blob/main/app/models/thredded/user_permissions/admin/none.rb">
|
545
555
|
❌ No
|
546
556
|
</a></td>
|
547
557
|
</tr>
|
@@ -551,7 +561,7 @@ Below is an overview of the default permissions, with links to the implementatio
|
|
551
561
|
### Handling "Permission denied" and "Not found" errors
|
552
562
|
|
553
563
|
Thredded defines a number of Exception classes for not found / permission denied errors.
|
554
|
-
The complete list can be found [here](https://github.com/thredded/thredded/blob/
|
564
|
+
The complete list can be found [here](https://github.com/thredded/thredded/blob/main/app/controllers/thredded/application_controller.rb#L18-L40).
|
555
565
|
|
556
566
|
Currently, the default behaviour is to render an error message with an appropriate response code within the Thredded
|
557
567
|
layout. You may want to override the handling for `Thredded::Errors::LoginRequired` to render a login form instead.
|
@@ -596,8 +606,6 @@ Rails.application.config.to_prepare do
|
|
596
606
|
Thredded::ApplicationController.module_eval do
|
597
607
|
# Require authentication to access the forums:
|
598
608
|
before_action :thredded_require_login!
|
599
|
-
# NB: in rails 4.2 you will need to change this to:
|
600
|
-
# before_action { thredded_require_login! }
|
601
609
|
|
602
610
|
# You may also want to render a login form after the
|
603
611
|
# "Please sign in first" message:
|
@@ -809,4 +817,4 @@ docker-compose run web bundle exec rake
|
|
809
817
|
|
810
818
|
The docker container uses PostgreSQL.
|
811
819
|
|
812
|
-
[initializer]: https://github.com/thredded/thredded/blob/
|
820
|
+
[initializer]: https://github.com/thredded/thredded/blob/main/lib/generators/thredded/install/templates/initializer.rb
|
@@ -34,8 +34,8 @@ module Thredded
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def first_topic_content
|
37
|
-
|
38
|
-
#{I18n.t('thredded.messageboard_first_topic.content', thredded_version: Thredded::VERSION)}
|
37
|
+
<<~MARKDOWN
|
38
|
+
#{I18n.t('thredded.messageboard_first_topic.content', thredded_version: Thredded::VERSION)}
|
39
39
|
MARKDOWN
|
40
40
|
end
|
41
41
|
end
|
@@ -130,7 +130,7 @@ module Thredded
|
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
133
|
-
def parse_names(text) # rubocop:disable Metrics/
|
133
|
+
def parse_names(text) # rubocop:disable Metrics/MethodLength
|
134
134
|
result = []
|
135
135
|
current = +''
|
136
136
|
in_name = in_quoted = false
|
@@ -108,9 +108,9 @@ module Thredded
|
|
108
108
|
# @param follow_reason ['manual', 'posted', 'mentioned', 'auto', nil]
|
109
109
|
def topic_follow_reason_text(follow_reason)
|
110
110
|
if follow_reason
|
111
|
-
# rubocop:disable
|
111
|
+
# rubocop:disable Layout/LineLength
|
112
112
|
# i18n-tasks-use t('thredded.topics.following.manual') t('thredded.topics.following.posted') t('thredded.topics.following.mentioned') t('thredded.topics.following.auto')
|
113
|
-
# rubocop:enable
|
113
|
+
# rubocop:enable Layout/LineLength
|
114
114
|
t("thredded.topics.following.#{follow_reason}")
|
115
115
|
else
|
116
116
|
t('thredded.topics.not_following')
|
@@ -11,13 +11,13 @@ module Thredded
|
|
11
11
|
protected
|
12
12
|
|
13
13
|
def mock_content(mention_users: [])
|
14
|
-
|
15
|
-
Hey #{mention_users.map { |u| "@#{u}" } * ', '}!
|
16
|
-
All of the basic [Markdown](https://kramdown.gettalong.org/quickref.html) formatting is supported (powered by [Kramdown](https://kramdown.gettalong.org)).
|
14
|
+
<<~MARKDOWN
|
15
|
+
Hey #{mention_users.map { |u| "@#{u}" } * ', '}!
|
16
|
+
All of the basic [Markdown](https://kramdown.gettalong.org/quickref.html) formatting is supported (powered by [Kramdown](https://kramdown.gettalong.org)).
|
17
17
|
|
18
|
-
Additionally, Markdown is extended to support the following:
|
18
|
+
Additionally, Markdown is extended to support the following:
|
19
19
|
|
20
|
-
#{Thredded::FormattingDemoContent.parts.join("\n")}
|
20
|
+
#{Thredded::FormattingDemoContent.parts.join("\n")}
|
21
21
|
MARKDOWN
|
22
22
|
end
|
23
23
|
|
@@ -8,7 +8,7 @@ module Thredded
|
|
8
8
|
delegate :human_name, to: :notifier, prefix: true
|
9
9
|
|
10
10
|
def self.detect_or_default(prefs, notifier)
|
11
|
-
(prefs
|
11
|
+
(prefs&.find { |pref| pref.notifier_key == notifier.key }) || default(notifier)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
@@ -26,8 +26,8 @@ module Thredded
|
|
26
26
|
preloader = Thredded::Compat.association_preloader(
|
27
27
|
records: owners_by_id.values, associations: [:first_post],
|
28
28
|
scope: unscoped.where(<<~SQL.delete("\n"))
|
29
|
-
|
30
|
-
|
29
|
+
#{posts_table_name}.created_at = (
|
30
|
+
SELECT MAX(p2.created_at) from #{posts_table_name} p2 WHERE p2.postable_id = #{posts_table_name}.postable_id)
|
31
31
|
SQL
|
32
32
|
)
|
33
33
|
preloader[0].preloaded_records.each do |post|
|
@@ -68,7 +68,7 @@ module Thredded
|
|
68
68
|
def mark_as_unread(user)
|
69
69
|
if previous_post.nil?
|
70
70
|
read_state = postable.user_read_states.find_by(user_id: user.id)
|
71
|
-
read_state
|
71
|
+
read_state&.destroy
|
72
72
|
else
|
73
73
|
postable.user_read_states.touch!(user.id, previous_post, overwrite_newer: true)
|
74
74
|
end
|
@@ -33,12 +33,12 @@ module Thredded
|
|
33
33
|
h[r.post.postable_id] = r.post.postable if r.post
|
34
34
|
end
|
35
35
|
next result if owners_by_id.empty?
|
36
|
-
preloader =
|
37
|
-
owners_by_id.values, :first_post,
|
38
|
-
Thredded::Post.unscoped.where(<<~SQL.delete("\n"))
|
36
|
+
preloader = Thredded::Compat.association_preloader(
|
37
|
+
records: owners_by_id.values, associations: [:first_post],
|
38
|
+
scope: Thredded::Post.unscoped.where(<<~SQL.delete("\n"))
|
39
39
|
#{posts_table_name}.created_at = (
|
40
40
|
SELECT MAX(p2.created_at) from #{posts_table_name} p2 WHERE p2.postable_id = #{posts_table_name}.postable_id)
|
41
|
-
|
41
|
+
SQL
|
42
42
|
)
|
43
43
|
preloader[0].preloaded_records.each do |post|
|
44
44
|
topic = owners_by_id.delete(post.postable_id)
|
@@ -13,13 +13,13 @@
|
|
13
13
|
time_ago: time_ago(record.created_at)
|
14
14
|
}
|
15
15
|
%>
|
16
|
-
<article class="thredded--post-moderation-record thredded--post-moderation-record-<%= record.moderation_state %>">
|
16
|
+
<article id="<%= dom_id(post) %>" class="thredded--post-moderation-record thredded--post-moderation-record-<%= record.moderation_state %>">
|
17
17
|
<header class="thredded--post-moderation-record--header">
|
18
18
|
<p class="thredded--post-moderation-record--moderation-state-notice">
|
19
19
|
<% if record.approved? %>
|
20
|
-
<%= t('thredded.moderation.post_approved_html', moderation_state_notice_args) %>
|
20
|
+
<%= t('thredded.moderation.post_approved_html', **moderation_state_notice_args) %>
|
21
21
|
<% elsif record.blocked? %>
|
22
|
-
<%= t('thredded.moderation.post_blocked_html', moderation_state_notice_args) %>
|
22
|
+
<%= t('thredded.moderation.post_blocked_html', **moderation_state_notice_args) %>
|
23
23
|
<% end %>
|
24
24
|
</p>
|
25
25
|
<% if post && post.content != record.post_content %>
|
@@ -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
|
-
t('thredded.nav.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
|
-
t('thredded.nav.sign_in')
|
9
|
+
<%= t('thredded.nav.sign_in') %>
|
10
10
|
<% end %>
|
11
11
|
<% end %>
|
12
12
|
</li>
|
data/bin/rubocop
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rubocop' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'pathname'
|
12
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path('bundle', __dir__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if /This file was generated by Bundler/.match?(File.read(bundle_binstub, 300))
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'rubygems'
|
27
|
+
require 'bundler/setup'
|
28
|
+
|
29
|
+
load Gem.bin_path('rubocop', 'rubocop')
|
data/config/i18n-tasks.yml
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# i18n-tasks finds and manages missing and unused translations
|
2
|
-
# See https://github.com/glebm/i18n-tasks/blob/
|
2
|
+
# See https://github.com/glebm/i18n-tasks/blob/main/templates/config/i18n-tasks.yml
|
3
3
|
|
4
4
|
# Do not consider these keys missing:
|
5
5
|
ignore_missing:
|
@@ -6,11 +6,11 @@ class UpgradeV07ToV08 < Thredded::BaseMigration
|
|
6
6
|
def up
|
7
7
|
closed_messageboards = Thredded::Messageboard.unscoped.where(closed: true).to_a
|
8
8
|
if closed_messageboards.present?
|
9
|
-
fail ActiveRecord::MigrationError,
|
10
|
-
There are #{closed_messageboards.length} closed Messageboards:
|
11
|
-
#{closed_messageboards.map { |m| "#{m.name} (id=#{m.id})" }.join("\n")}
|
12
|
-
Support for closed messageboards has been removed in thredded v0.8.0.
|
13
|
-
Delete or un-close these messageboards and consider using the "paranoia" gem to support soft deletion instead.
|
9
|
+
fail ActiveRecord::MigrationError, <<~TEXT
|
10
|
+
There are #{closed_messageboards.length} closed Messageboards:
|
11
|
+
#{closed_messageboards.map { |m| "#{m.name} (id=#{m.id})" }.join("\n")}
|
12
|
+
Support for closed messageboards has been removed in thredded v0.8.0.
|
13
|
+
Delete or un-close these messageboards and consider using the "paranoia" gem to support soft deletion instead.
|
14
14
|
TEXT
|
15
15
|
end
|
16
16
|
remove_index :thredded_messageboards, name: :index_thredded_messageboards_on_closed
|
@@ -124,7 +124,7 @@ Thredded.layout = 'thredded/application'
|
|
124
124
|
# Change the HTML sanitization settings used by Thredded.
|
125
125
|
# See the Sanitize docs for more information on the underlying library: https://github.com/rgrove/sanitize/#readme
|
126
126
|
# E.g. to allow a custom element <custom-element>:
|
127
|
-
# Thredded::ContentFormatter.
|
127
|
+
# Thredded::ContentFormatter.allowlist[:elements] += %w(custom-element)
|
128
128
|
|
129
129
|
# ==> User autocompletion (Private messages and @-mentions)
|
130
130
|
# Thredded.autocomplete_min_length = 2 lower to 1 if have 1-letter names -- increase if you want
|
data/lib/thredded/arel_compat.rb
CHANGED
@@ -9,7 +9,7 @@ module Thredded
|
|
9
9
|
# @param [Arel::Nodes::Node] b integer node
|
10
10
|
# @return [Arel::Nodes::Node] a / b
|
11
11
|
def integer_division(engine, a, b)
|
12
|
-
if
|
12
|
+
if /mysql|mariadb/i.match?(engine.connection.adapter_name)
|
13
13
|
Arel::Nodes::InfixOperation.new('DIV', a, b)
|
14
14
|
else
|
15
15
|
Arel::Nodes::Division.new(a, b)
|
@@ -15,7 +15,7 @@ module Thredded
|
|
15
15
|
|
16
16
|
# @return [Integer, nil] the maximum number of codepoints that can be indexed for a primary key or index.
|
17
17
|
def max_key_length
|
18
|
-
return nil unless
|
18
|
+
return nil unless /mysql|maria/i.match?(connection.adapter_name)
|
19
19
|
# Conservatively assume that innodb_large_prefix is **disabled**.
|
20
20
|
# If it were enabled, the maximum key length would instead be 768 utf8mb4 characters.
|
21
21
|
191
|
@@ -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
|
-
|
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
|
@@ -82,18 +83,29 @@ 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
|
|
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
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
97
109
|
if Thredded::Compat.rails_gte_61?
|
98
110
|
# @param [Array<Object>] collection
|
99
111
|
# @param [Hash] opts
|
@@ -121,42 +133,20 @@ module Thredded
|
|
121
133
|
view.combined_fragment_cache_key(key)
|
122
134
|
end
|
123
135
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
136
|
+
def cache_fragment_name(view, key, virtual_path:, digest_path:)
|
137
|
+
if Thredded::Compat.rails_gte_61?
|
138
|
+
view.cache_fragment_name(key, digest_path: digest_path)
|
139
|
+
else
|
140
|
+
view.cache_fragment_name(key, virtual_path: virtual_path, digest_path: digest_path)
|
147
141
|
end
|
142
|
+
end
|
148
143
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
finder: @lookup_context,
|
153
|
-
dependencies: view.view_cache_dependencies
|
154
|
-
).presence
|
155
|
-
end
|
144
|
+
def digest_path_from_template(view, template)
|
145
|
+
view.digest_path_from_template(template)
|
146
|
+
end
|
156
147
|
|
157
|
-
|
158
|
-
|
159
|
-
end
|
148
|
+
def render_partial(partial_renderer, view_context, opts)
|
149
|
+
partial_renderer.render(view_context, opts, nil).body
|
160
150
|
end
|
161
151
|
end
|
162
152
|
end
|
data/lib/thredded/compat.rb
CHANGED
@@ -3,12 +3,6 @@
|
|
3
3
|
module Thredded
|
4
4
|
module Compat
|
5
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
6
|
# @api private
|
13
7
|
def rails_gte_61?
|
14
8
|
@rails_gte_61 = (Rails.gem_version >= Gem::Version.new('6.1.0')) if @rails_gte_61.nil?
|
@@ -4,8 +4,19 @@ module Thredded
|
|
4
4
|
# Generates HTML from content source.
|
5
5
|
class ContentFormatter
|
6
6
|
class << self
|
7
|
-
# Sanitization
|
8
|
-
attr_accessor :
|
7
|
+
# Sanitization allowlist options.
|
8
|
+
attr_accessor :allowlist
|
9
|
+
|
10
|
+
# TODO: v2.0: drop alias and just use 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
|
9
20
|
|
10
21
|
# Filters that run before processing the markup.
|
11
22
|
# input: markup, output: markup.
|
@@ -26,16 +37,20 @@ module Thredded
|
|
26
37
|
# Filters that run after sanitization
|
27
38
|
# input: sanitized html, output: html
|
28
39
|
attr_accessor :after_sanitization_filters
|
40
|
+
|
41
|
+
def sanitization_filter_allowlist_config
|
42
|
+
HTML::Pipeline::SanitizationFilter::ALLOWLIST
|
43
|
+
end
|
29
44
|
end
|
30
45
|
|
31
|
-
self.
|
32
|
-
elements:
|
33
|
-
transformers:
|
46
|
+
self.allowlist = sanitization_filter_allowlist_config.deep_merge(
|
47
|
+
elements: sanitization_filter_allowlist_config[:elements] + %w[abbr iframe span figure figcaption],
|
48
|
+
transformers: sanitization_filter_allowlist_config[:transformers] + [
|
34
49
|
->(env) {
|
35
50
|
next unless env[:node_name] == 'a'
|
36
51
|
a_tag = env[:node]
|
37
52
|
a_tag['href'] ||= '#'
|
38
|
-
if
|
53
|
+
if %r{^(?:[a-z]+:)?//}.match?(a_tag['href'])
|
39
54
|
a_tag['target'] = '_blank'
|
40
55
|
a_tag['rel'] = 'nofollow noopener'
|
41
56
|
end
|
@@ -49,7 +64,7 @@ module Thredded
|
|
49
64
|
'img' => %w[src longdesc class],
|
50
65
|
'th' => %w[style],
|
51
66
|
'td' => %w[style],
|
52
|
-
:all =>
|
67
|
+
:all => sanitization_filter_allowlist_config[:attributes][:all] +
|
53
68
|
%w[aria-expanded aria-label aria-labelledby aria-live aria-hidden aria-pressed role],
|
54
69
|
},
|
55
70
|
css: {
|
@@ -136,7 +151,7 @@ module Thredded
|
|
136
151
|
def content_pipeline_options
|
137
152
|
{
|
138
153
|
asset_root: Rails.application.config.action_controller.asset_host || '',
|
139
|
-
|
154
|
+
allowlist: ContentFormatter.allowlist
|
140
155
|
}
|
141
156
|
end
|
142
157
|
end
|
data/lib/thredded/db_tools.rb
CHANGED
@@ -10,11 +10,7 @@ module Thredded
|
|
10
10
|
verbose_was = ActiveRecord::Migration.verbose
|
11
11
|
ActiveRecord::Migration.verbose = !quiet
|
12
12
|
migrate =
|
13
|
-
|
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, ActiveRecord::SchemaMigration).migrate(nil, &filter) }
|
18
14
|
if quiet
|
19
15
|
silence_active_record(&migrate)
|
20
16
|
else
|
@@ -59,7 +55,7 @@ module Thredded
|
|
59
55
|
silence_active_record do
|
60
56
|
ActiveRecord::Base.transaction do
|
61
57
|
statements.each do |statement|
|
62
|
-
connection.execute(statement) unless
|
58
|
+
connection.execute(statement) unless /(BEGIN TRANSACTION|COMMIT)/.match?(statement)
|
63
59
|
end
|
64
60
|
end
|
65
61
|
end
|
data/lib/thredded/engine.rb
CHANGED
@@ -12,7 +12,7 @@ module Thredded
|
|
12
12
|
|
13
13
|
config.to_prepare do
|
14
14
|
Thredded::AllViewHooks.reset_instance!
|
15
|
-
Thredded.user_class
|
15
|
+
Thredded.user_class&.send(:include, Thredded::UserExtender)
|
16
16
|
end
|
17
17
|
|
18
18
|
initializer 'thredded.setup_assets' do
|
@@ -7,35 +7,35 @@ module Thredded
|
|
7
7
|
attr_accessor :parts
|
8
8
|
end
|
9
9
|
self.parts = [
|
10
|
-
|
11
|
-
#### Spoilers
|
10
|
+
<<~'MARKDOWN',
|
11
|
+
#### Spoilers
|
12
12
|
|
13
|
-
Use `<spoiler></spoiler>` tags to create spoiler boxes like this one:
|
13
|
+
Use `<spoiler></spoiler>` tags to create spoiler boxes like this one:
|
14
14
|
|
15
|
-
<spoiler>
|
16
|
-
Harry Potter books are better than the movies.
|
15
|
+
<spoiler>
|
16
|
+
Harry Potter books are better than the movies.
|
17
17
|
|
18
|
-
![nyancat](https://storage.googleapis.com/glebm-stuff/nyancat.gif)
|
18
|
+
![nyancat](https://storage.googleapis.com/glebm-stuff/nyancat.gif)
|
19
19
|
|
20
|
-
https://www.youtube.com/watch?v=5lBBUPVuusM
|
21
|
-
</spoiler>
|
20
|
+
https://www.youtube.com/watch?v=5lBBUPVuusM
|
21
|
+
</spoiler>
|
22
22
|
|
23
|
-
#### Oneboxes
|
23
|
+
#### Oneboxes
|
24
24
|
|
25
|
-
URLs of supported resources are replaced with boxes like these:
|
25
|
+
URLs of supported resources are replaced with boxes like these:
|
26
26
|
|
27
|
-
**Twitter** `https://twitter.com/thredded/status/838824533477982209`:
|
28
|
-
https://twitter.com/thredded/status/838824533477982209
|
29
|
-
**StackExchange** `http://codegolf.stackexchange.com/questions/45701`:
|
30
|
-
http://codegolf.stackexchange.com/questions/45701
|
31
|
-
**Amazon** `https://www.amazon.co.uk/dp/0521797071`:
|
32
|
-
https://www.amazon.co.uk/dp/0521797071
|
33
|
-
**YouTube** `https://www.youtube.com/watch?v=1QP7elXwpLw`:
|
34
|
-
https://www.youtube.com/watch?v=1QP7elXwpLw
|
35
|
-
**Google Maps** `https://goo.gl/maps/R6nj3Qwf2LR2`:
|
36
|
-
https://goo.gl/maps/R6nj3Qwf2LR2
|
27
|
+
**Twitter** `https://twitter.com/thredded/status/838824533477982209`:
|
28
|
+
https://twitter.com/thredded/status/838824533477982209
|
29
|
+
**StackExchange** `http://codegolf.stackexchange.com/questions/45701`:
|
30
|
+
http://codegolf.stackexchange.com/questions/45701
|
31
|
+
**Amazon** `https://www.amazon.co.uk/dp/0521797071`:
|
32
|
+
https://www.amazon.co.uk/dp/0521797071
|
33
|
+
**YouTube** `https://www.youtube.com/watch?v=1QP7elXwpLw`:
|
34
|
+
https://www.youtube.com/watch?v=1QP7elXwpLw
|
35
|
+
**Google Maps** `https://goo.gl/maps/R6nj3Qwf2LR2`:
|
36
|
+
https://goo.gl/maps/R6nj3Qwf2LR2
|
37
37
|
|
38
|
-
Many more resources are [supported](https://github.com/discourse/onebox/tree/
|
38
|
+
Many more resources are [supported](https://github.com/discourse/onebox/tree/main/lib/onebox/engine). Powered by the [onebox](https://github.com/discourse/onebox) library.
|
39
39
|
MARKDOWN
|
40
40
|
]
|
41
41
|
end
|
@@ -47,7 +47,7 @@ module Thredded
|
|
47
47
|
return if names.blank? || @users_provider.nil?
|
48
48
|
@users_provider.call(names, @users_provider_scope).each do |user|
|
49
49
|
name = user.send(Thredded.user_name_column)
|
50
|
-
maybe_quoted_name =
|
50
|
+
maybe_quoted_name = /[., ()]/.match?(name) ? %("#{name}") : name
|
51
51
|
url = Thredded.user_path(@view_context, user)
|
52
52
|
text_node_html.gsub!(
|
53
53
|
/(^|[\s>])(@#{Regexp.escape maybe_quoted_name})([^a-z\d]|$)/i,
|
@@ -24,7 +24,7 @@ module Thredded
|
|
24
24
|
next unless env[:node_name] == 'a'
|
25
25
|
a_tag = env[:node]
|
26
26
|
a_tag['href'] ||= '#'
|
27
|
-
if
|
27
|
+
if %r{^(?:[a-z]+:)?//}.match?(a_tag['href'])
|
28
28
|
a_tag['target'] = '_blank'
|
29
29
|
a_tag['rel'] = 'nofollow noopener'
|
30
30
|
else
|
@@ -68,9 +68,9 @@ module Thredded
|
|
68
68
|
|
69
69
|
def render_onebox(url)
|
70
70
|
preview = Onebox.preview(url, onebox_options(url))
|
71
|
-
if context[:onebox_placeholders]
|
71
|
+
if context[:onebox_placeholders] && (placeholder_html = preview.placeholder_html.presence)
|
72
72
|
%(<p><a href="#{ERB::Util.html_escape(url)}" target="_blank" rel="nofollow noopener">) \
|
73
|
-
"#{
|
73
|
+
"#{placeholder_html}</a></p>"
|
74
74
|
else
|
75
75
|
preview.to_s.strip
|
76
76
|
end
|
data/lib/thredded/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thredded
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel Oliveira
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2023-02-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: active_record_union
|
@@ -127,20 +127,14 @@ dependencies:
|
|
127
127
|
name: rails
|
128
128
|
requirement: !ruby/object:Gem::Requirement
|
129
129
|
requirements:
|
130
|
-
- - "
|
131
|
-
- !ruby/object:Gem::Version
|
132
|
-
version: 4.2.10
|
133
|
-
- - "!="
|
130
|
+
- - ">"
|
134
131
|
- !ruby/object:Gem::Version
|
135
132
|
version: 6.0.0.rc2
|
136
133
|
type: :runtime
|
137
134
|
prerelease: false
|
138
135
|
version_requirements: !ruby/object:Gem::Requirement
|
139
136
|
requirements:
|
140
|
-
- - "
|
141
|
-
- !ruby/object:Gem::Version
|
142
|
-
version: 4.2.10
|
143
|
-
- - "!="
|
137
|
+
- - ">"
|
144
138
|
- !ruby/object:Gem::Version
|
145
139
|
version: 6.0.0.rc2
|
146
140
|
- !ruby/object:Gem::Dependency
|
@@ -163,14 +157,14 @@ dependencies:
|
|
163
157
|
requirements:
|
164
158
|
- - ">="
|
165
159
|
- !ruby/object:Gem::Version
|
166
|
-
version:
|
160
|
+
version: 2.14.1
|
167
161
|
type: :runtime
|
168
162
|
prerelease: false
|
169
163
|
version_requirements: !ruby/object:Gem::Requirement
|
170
164
|
requirements:
|
171
165
|
- - ">="
|
172
166
|
- !ruby/object:Gem::Version
|
173
|
-
version:
|
167
|
+
version: 2.14.1
|
174
168
|
- !ruby/object:Gem::Dependency
|
175
169
|
name: kramdown
|
176
170
|
requirement: !ruby/object:Gem::Requirement
|
@@ -311,6 +305,20 @@ dependencies:
|
|
311
305
|
- - "~>"
|
312
306
|
- !ruby/object:Gem::Version
|
313
307
|
version: '3.0'
|
308
|
+
- !ruby/object:Gem::Dependency
|
309
|
+
name: capybara-screenshot
|
310
|
+
requirement: !ruby/object:Gem::Requirement
|
311
|
+
requirements:
|
312
|
+
- - ">="
|
313
|
+
- !ruby/object:Gem::Version
|
314
|
+
version: '0'
|
315
|
+
type: :development
|
316
|
+
prerelease: false
|
317
|
+
version_requirements: !ruby/object:Gem::Requirement
|
318
|
+
requirements:
|
319
|
+
- - ">="
|
320
|
+
- !ruby/object:Gem::Version
|
321
|
+
version: '0'
|
314
322
|
- !ruby/object:Gem::Dependency
|
315
323
|
name: cuprite
|
316
324
|
requirement: !ruby/object:Gem::Requirement
|
@@ -620,7 +628,7 @@ dependencies:
|
|
620
628
|
- !ruby/object:Gem::Version
|
621
629
|
version: '0'
|
622
630
|
description: |-
|
623
|
-
The best Rails
|
631
|
+
The best Rails 6.0+ forums engine ever. Its goal is to be as simple and feature rich as possible.
|
624
632
|
Thredded works with SQLite, MySQL (v5.6.4+), and PostgreSQL. See the demo at https://thredded.org/.
|
625
633
|
email:
|
626
634
|
- joel@thredded.com
|
@@ -970,6 +978,8 @@ files:
|
|
970
978
|
- app/views/thredded/users/_posts.html.erb
|
971
979
|
- bin/create_migration_fixture
|
972
980
|
- bin/rails
|
981
|
+
- bin/rubocop
|
982
|
+
- bin/run_specs_repeatedly
|
973
983
|
- config/i18n-tasks.yml
|
974
984
|
- config/locales/de.yml
|
975
985
|
- config/locales/en.yml
|
@@ -1019,7 +1029,6 @@ files:
|
|
1019
1029
|
- lib/thredded/html_pipeline/spoiler_tag_filter.rb
|
1020
1030
|
- lib/thredded/html_pipeline/utils.rb
|
1021
1031
|
- lib/thredded/html_pipeline/wrap_iframes_filter.rb
|
1022
|
-
- lib/thredded/rails_lt_5_2_arel_case_node.rb
|
1023
1032
|
- lib/thredded/users_provider.rb
|
1024
1033
|
- lib/thredded/version.rb
|
1025
1034
|
- lib/thredded/view_hooks/config.rb
|
@@ -1049,7 +1058,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
1049
1058
|
- !ruby/object:Gem::Version
|
1050
1059
|
version: '0'
|
1051
1060
|
requirements: []
|
1052
|
-
rubygems_version: 3.
|
1061
|
+
rubygems_version: 3.2.32
|
1053
1062
|
signing_key:
|
1054
1063
|
specification_version: 4
|
1055
1064
|
summary: The best Rails forums engine ever.
|
@@ -1,119 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Arel
|
4
|
-
module Nodes
|
5
|
-
class Case < Arel::Nodes::Node
|
6
|
-
include Arel::OrderPredications
|
7
|
-
include Arel::Predications
|
8
|
-
include Arel::AliasPredication
|
9
|
-
|
10
|
-
attr_accessor :case, :conditions, :default
|
11
|
-
|
12
|
-
def initialize(expression = nil, default = nil)
|
13
|
-
@case = expression
|
14
|
-
@conditions = []
|
15
|
-
@default = default
|
16
|
-
end
|
17
|
-
|
18
|
-
def when(condition, expression = nil)
|
19
|
-
@conditions << When.new(Nodes.build_quoted(condition), expression)
|
20
|
-
self
|
21
|
-
end
|
22
|
-
|
23
|
-
def then(expression)
|
24
|
-
@conditions.last.right = Nodes.build_quoted(expression)
|
25
|
-
self
|
26
|
-
end
|
27
|
-
|
28
|
-
def else(expression)
|
29
|
-
@default = Else.new Nodes.build_quoted(expression)
|
30
|
-
self
|
31
|
-
end
|
32
|
-
|
33
|
-
def initialize_copy(other)
|
34
|
-
super
|
35
|
-
@case = @case.clone if @case
|
36
|
-
@conditions = @conditions.map(&:clone)
|
37
|
-
@default = @default.clone if @default
|
38
|
-
end
|
39
|
-
|
40
|
-
def hash
|
41
|
-
[@case, @conditions, @default].hash
|
42
|
-
end
|
43
|
-
|
44
|
-
def eql?(other)
|
45
|
-
self.class == other.class &&
|
46
|
-
self.case == other.case &&
|
47
|
-
conditions == other.conditions &&
|
48
|
-
default == other.default
|
49
|
-
end
|
50
|
-
|
51
|
-
alias == eql?
|
52
|
-
end
|
53
|
-
|
54
|
-
class When < Binary
|
55
|
-
end
|
56
|
-
|
57
|
-
class Else < Unary
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
module Arel
|
63
|
-
module Visitors
|
64
|
-
class DepthFirst < Arel::Visitors::Visitor
|
65
|
-
alias visit_Arel_Nodes_Else unary
|
66
|
-
|
67
|
-
def visit_Arel_Nodes_Case(o) # rubocop:disable Naming/MethodName
|
68
|
-
visit o.case
|
69
|
-
visit o.conditions
|
70
|
-
visit o.default
|
71
|
-
end
|
72
|
-
|
73
|
-
alias visit_Arel_Nodes_When binary
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
module Arel
|
79
|
-
module Predications
|
80
|
-
def when(right)
|
81
|
-
Nodes::Case.new(self).when quoted_node(right)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
module Arel
|
87
|
-
module Visitors
|
88
|
-
class ToSql < Arel::Visitors::Reduce
|
89
|
-
def visit_Arel_Nodes_Case(o, collector) # rubocop:disable Naming/MethodName
|
90
|
-
collector << 'CASE '
|
91
|
-
if o.case
|
92
|
-
visit o.case, collector
|
93
|
-
collector << ' '
|
94
|
-
end
|
95
|
-
o.conditions.each do |condition|
|
96
|
-
visit condition, collector
|
97
|
-
collector << ' '
|
98
|
-
end
|
99
|
-
if o.default
|
100
|
-
visit o.default, collector
|
101
|
-
collector << ' '
|
102
|
-
end
|
103
|
-
collector << 'END'
|
104
|
-
end
|
105
|
-
|
106
|
-
def visit_Arel_Nodes_When(o, collector) # rubocop:disable Naming/MethodName
|
107
|
-
collector << 'WHEN '
|
108
|
-
visit o.left, collector
|
109
|
-
collector << ' THEN '
|
110
|
-
visit o.right, collector
|
111
|
-
end
|
112
|
-
|
113
|
-
def visit_Arel_Nodes_Else(o, collector) # rubocop:disable Naming/MethodName
|
114
|
-
collector << 'ELSE '
|
115
|
-
visit o.expr, collector
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|