spina-conferences-primer_theme-fork 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +33 -0
  4. data/Rakefile +36 -0
  5. data/app/assets/config/spina_conferences_primer_theme_manifest.js +9 -0
  6. data/app/assets/javascripts/spina/conferences/primer_theme/application.js +7 -0
  7. data/app/assets/javascripts/spina/conferences/primer_theme/controllers/slideshow_controller.es6 +68 -0
  8. data/app/assets/stylesheets/spina/conferences/primer_theme/_custom.sass +0 -0
  9. data/app/assets/stylesheets/spina/conferences/primer_theme/_custom_variables.sass +0 -0
  10. data/app/assets/stylesheets/spina/conferences/primer_theme/application.sass +44 -0
  11. data/app/controllers/spina/conferences/primer_theme/conferences/application_controller.rb +16 -0
  12. data/app/controllers/spina/conferences/primer_theme/conferences/conferences_controller.rb +72 -0
  13. data/app/controllers/spina/conferences/primer_theme/conferences/presentations_controller.rb +42 -0
  14. data/app/controllers/spina/conferences/primer_theme/journal/application_controller.rb +13 -0
  15. data/app/controllers/spina/conferences/primer_theme/journal/articles_controller.rb +69 -0
  16. data/app/controllers/spina/conferences/primer_theme/journal/issues_controller.rb +53 -0
  17. data/app/helpers/spina/conferences/primer_theme/application_helper.rb +36 -0
  18. data/app/helpers/spina/conferences/primer_theme/asset_helper.rb +51 -0
  19. data/app/helpers/spina/conferences/primer_theme/navigations_helper.rb +24 -0
  20. data/app/views/conferences_primer_theme/pages/about.html.haml +92 -0
  21. data/app/views/conferences_primer_theme/pages/committee.html.haml +42 -0
  22. data/app/views/conferences_primer_theme/pages/embedded_form.html.haml +9 -0
  23. data/app/views/conferences_primer_theme/pages/events.html.haml +59 -0
  24. data/app/views/conferences_primer_theme/pages/homepage.html.haml +34 -0
  25. data/app/views/conferences_primer_theme/pages/information.html.haml +5 -0
  26. data/app/views/conferences_primer_theme/pages/show.html.haml +2 -0
  27. data/app/views/layouts/conferences_primer_theme/application.html.haml +7 -0
  28. data/app/views/layouts/spina/conferences/primer_theme/application.html.haml +40 -0
  29. data/app/views/layouts/spina/conferences/primer_theme/conferences/conferences.html.haml +5 -0
  30. data/app/views/layouts/spina/conferences/primer_theme/conferences/presentations.html.haml +6 -0
  31. data/app/views/layouts/spina/conferences/primer_theme/journal/articles.html.haml +2 -0
  32. data/app/views/layouts/spina/conferences/primer_theme/journal/issues.html.haml +2 -0
  33. data/app/views/spina/admin/layout_partables/texts/_form.html.haml +4 -0
  34. data/app/views/spina/application/_cookies.html.haml +7 -0
  35. data/app/views/spina/application/_current_conference_alert.html.haml +4 -0
  36. data/app/views/spina/application/_footer.html.haml +17 -0
  37. data/app/views/spina/application/_footer_content.html.haml +13 -0
  38. data/app/views/spina/application/_list_item.html.haml +1 -0
  39. data/app/views/spina/application/_logo.html.haml +1 -0
  40. data/app/views/spina/application/_mobile_navigation_item.html.haml +12 -0
  41. data/app/views/spina/application/_mobile_navigation_items.html.haml +4 -0
  42. data/app/views/spina/application/_navigation.html.haml +11 -0
  43. data/app/views/spina/application/_navigation_item.html.haml +11 -0
  44. data/app/views/spina/application/_text.html.haml +1 -0
  45. data/app/views/spina/conferences/primer_theme/conferences/conferences/_conference.html.haml +15 -0
  46. data/app/views/spina/conferences/primer_theme/conferences/conferences/_event.html.haml +12 -0
  47. data/app/views/spina/conferences/primer_theme/conferences/conferences/_events.html.haml +16 -0
  48. data/app/views/spina/conferences/primer_theme/conferences/conferences/_header.html.haml +47 -0
  49. data/app/views/spina/conferences/primer_theme/conferences/conferences/_institution.html.haml +5 -0
  50. data/app/views/spina/conferences/primer_theme/conferences/conferences/_institution_logo.html.haml +4 -0
  51. data/app/views/spina/conferences/primer_theme/conferences/conferences/_presentation.html.haml +15 -0
  52. data/app/views/spina/conferences/primer_theme/conferences/conferences/_presentation_type.html.haml +5 -0
  53. data/app/views/spina/conferences/primer_theme/conferences/conferences/_presentations.html.haml +29 -0
  54. data/app/views/spina/conferences/primer_theme/conferences/conferences/_submission_flash.html.haml +10 -0
  55. data/app/views/spina/conferences/primer_theme/conferences/conferences/index.html.haml +23 -0
  56. data/app/views/spina/conferences/primer_theme/conferences/conferences/show.html.haml +37 -0
  57. data/app/views/spina/conferences/primer_theme/conferences/kaminari/_gap.html.haml +2 -0
  58. data/app/views/spina/conferences/primer_theme/conferences/kaminari/_next_page.html.haml +9 -0
  59. data/app/views/spina/conferences/primer_theme/conferences/kaminari/_page.html.haml +10 -0
  60. data/app/views/spina/conferences/primer_theme/conferences/kaminari/_paginator.html.haml +17 -0
  61. data/app/views/spina/conferences/primer_theme/conferences/kaminari/_prev_page.html.haml +9 -0
  62. data/app/views/spina/conferences/primer_theme/conferences/presentations/_abstract.html.haml +2 -0
  63. data/app/views/spina/conferences/primer_theme/conferences/presentations/_attachment.html.haml +5 -0
  64. data/app/views/spina/conferences/primer_theme/conferences/presentations/_attachments.html.haml +5 -0
  65. data/app/views/spina/conferences/primer_theme/conferences/presentations/show.html.haml +18 -0
  66. data/app/views/spina/conferences/primer_theme/journal/articles/_authorship.html.haml +6 -0
  67. data/app/views/spina/conferences/primer_theme/journal/articles/_metadata.html.haml +36 -0
  68. data/app/views/spina/conferences/primer_theme/journal/articles/show.html.haml +70 -0
  69. data/app/views/spina/conferences/primer_theme/journal/issues/_article.html.haml +15 -0
  70. data/app/views/spina/conferences/primer_theme/journal/issues/_issue.html.haml +17 -0
  71. data/app/views/spina/conferences/primer_theme/journal/issues/_issue_cover.html.haml +6 -0
  72. data/app/views/spina/conferences/primer_theme/journal/issues/index.html.haml +30 -0
  73. data/app/views/spina/conferences/primer_theme/journal/issues/show.html.haml +27 -0
  74. data/config/initializers/assets.rb +13 -0
  75. data/config/initializers/mime_types.rb +1 -0
  76. data/config/initializers/primer.rb +3 -0
  77. data/config/initializers/themes/conferences_primer_theme.rb +237 -0
  78. data/config/locales/en.rb +16 -0
  79. data/config/locales/en.yml +198 -0
  80. data/config/routes.rb +17 -0
  81. data/db/migrate/20210206170704_change_current_conference_alert_to_text.rb +46 -0
  82. data/lib/spina/conferences/primer_theme.rb +17 -0
  83. data/lib/spina/conferences/primer_theme/breadcrumbs/builder.rb +34 -0
  84. data/lib/spina/conferences/primer_theme/engine.rb +13 -0
  85. data/lib/spina/conferences/primer_theme/version.rb +9 -0
  86. data/lib/tasks/spina/conferences/primer_theme_tasks.rake +6 -0
  87. metadata +396 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2b41b19868f22af87cabcb8fa4e7870af8ce7279fd1eab85aa18a04216fe144c
4
+ data.tar.gz: aee63eea74ed9866b3e372787deb9b99d43e7f9b950e00f1c7c6a7c6c695704a
5
+ SHA512:
6
+ metadata.gz: eb0c0e9ecfabed0e692ccb9912f8d4f2eef62b0ebe3d2b296b9ffd23556181334c34d6fc5115704292d631855137ee3634c9f3db18da23e96ac6c3403f25a5ca
7
+ data.tar.gz: 95380eba88dadb590eb19b7167169226c592a0c3d6e81b2f15117046f8e61689d228f6fdd1245c46014bdcb7fab3200a30e245859be89427f6fd8bb5f4f5b84c
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2019 Justin Malčić
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # PrimerTheme
2
+ A plugin for `Spina` that provides a frontend for `Spina::Admin::Conferences` using components from `Primer::ViewComponents`.
3
+
4
+ ## Usage
5
+ See the README for `Spina::Admin::Conferences`. Also make sure to ensure compilation of the assets for this gem by adding the following line to your
6
+ `manifest.js` file.
7
+
8
+ ```js
9
+ //= link spina_conferences_primer_theme_manifest.js
10
+ ```
11
+
12
+ ## Installation
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'spina-conferences-primer_theme'
17
+ ```
18
+
19
+ And then execute:
20
+ ```bash
21
+ $ bundle
22
+ ```
23
+
24
+ Or install it yourself as:
25
+ ```bash
26
+ $ gem install spina-conferences-primer_theme
27
+ ```
28
+
29
+ ## Contributing
30
+ Contributions welcome, open an issue first please.
31
+
32
+ ## License
33
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rdoc/task'
10
+
11
+ RDoc::Task.new(:rdoc) do |rdoc|
12
+ rdoc.rdoc_dir = 'rdoc'
13
+ rdoc.title = 'Spina::Conferences::PrimerTheme'
14
+ rdoc.options << '--line-numbers'
15
+ rdoc.rdoc_files.include('README.md')
16
+ rdoc.rdoc_files.include('lib/**/*.rb')
17
+ end
18
+
19
+ APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__)
20
+ load 'rails/tasks/engine.rake'
21
+
22
+ load 'rails/tasks/statistics.rake'
23
+
24
+ require 'bundler/gem_tasks'
25
+
26
+ require 'rake/testtask'
27
+
28
+ Rake::TestTask.new(:test) do |t|
29
+ t.libs << 'test'
30
+ t.pattern = 'test/**/*_test.rb'
31
+ t.verbose = false
32
+ t.warning = false
33
+ t.ruby_opts = %w[-W:no-deprecated]
34
+ end
35
+
36
+ task default: :test
@@ -0,0 +1,9 @@
1
+ //= link_directory ../stylesheets/spina/conferences/primer_theme .css
2
+ //= link_directory ../javascripts/spina/conferences/primer_theme .js
3
+ //= link spina_admin_conferences_manifest.js
4
+ //= link spina_admin_journal_manifest.js
5
+
6
+ //= link @github/details-dialog-element/dist/index.js
7
+ //= link @github/include-fragment-element/dist/index.js
8
+ //= link @github/filter-input-element/dist/index.js
9
+ //= link @github/details-menu-element/dist/index.js
@@ -0,0 +1,7 @@
1
+ //= require stimulus
2
+ //= require_directory ./controllers
3
+
4
+ // eslint-disable-next-line no-undef
5
+ const application = Stimulus.Application.start()
6
+ // eslint-disable-next-line no-undef
7
+ application.register('slideshow', SlideshowController)
@@ -0,0 +1,68 @@
1
+ // eslint-disable-next-line no-unused-vars,no-undef
2
+ class SlideshowController extends Stimulus.Controller {
3
+ static get targets() {
4
+ return [
5
+ /**
6
+ * @private
7
+ * @property {HTMLElement[]} slideTargets - The slide elements.
8
+ */
9
+ 'slide'
10
+ ]
11
+ }
12
+
13
+ /**
14
+ * Returns the incrementer.
15
+ * @private
16
+ * @return {Number} The value for the incrementer.
17
+ */
18
+ get incrementer() {
19
+ return Number.parseInt(this.data.get('incrementer'))
20
+ }
21
+
22
+ /**
23
+ * Sets the incrementer.
24
+ * @private
25
+ * @param value {Number} The new value for the incrementer.
26
+ */
27
+ set incrementer(value) {
28
+ this.data.set('incrementer', value.toString())
29
+ this.showSlide()
30
+ }
31
+
32
+ /**
33
+ * Hook to start advancing the slides if necessary.
34
+ * @private
35
+ */
36
+ connect() {
37
+ this.showSlide()
38
+ if (this.data.has('advance')) {
39
+ setInterval(() => this.next(), 10000)
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Shows the slide that corresponds to the incrementer and hides all others.
45
+ * @private
46
+ */
47
+ showSlide() {
48
+ this.slideTargets.forEach((target, index) => target.hidden = index !== this.incrementer)
49
+ }
50
+
51
+ /**
52
+ * Increases the incrementer if there are more slides, or sets it to 0 if showing the last slide.
53
+ * @private
54
+ */
55
+ next() {
56
+ const incrementer = this.incrementer
57
+ this.incrementer = incrementer === this.slideTargets.length - 1 ? 0 : incrementer + 1
58
+ }
59
+
60
+ /**
61
+ * Decreases the incrementer if there are more slides, or sets it to 0 if showing the first slide.
62
+ * @private
63
+ */
64
+ previous() {
65
+ const incrementer = this.incrementer
66
+ this.incrementer = incrementer === 0 ? this.slideTargets.length - 1 : incrementer - 1
67
+ }
68
+ }
@@ -0,0 +1,44 @@
1
+ @import "@github/details-dialog-element/dist"
2
+
3
+ @import spina/conferences/primer_theme/custom
4
+ @import "@primer/css"
5
+ @import "@primer/octicons"
6
+ @import spina/conferences/primer_theme/custom_variables
7
+
8
+ @font-face
9
+ font-family: Inter
10
+ font-style: normal
11
+ font-weight: $font-weight-normal
12
+ src: local("Inter"), local("Inter-Regular"), asset-url("@primer/css/fonts/Inter-Regular.woff") format("woff")
13
+ font-display: swap
14
+
15
+
16
+ @font-face
17
+ font-family: Inter
18
+ font-style: normal
19
+ font-weight: $font-weight-semibold
20
+ src: local("Inter Medium"), local("Inter-Medium"), asset-url("@primer/css/fonts/Inter-Medium.woff") format("woff")
21
+ font-display: swap
22
+
23
+ @font-face
24
+ font-family: Inter
25
+ font-style: normal
26
+ font-weight: $font-weight-bold
27
+ src: local("Inter Bold"), local("Inter-Bold"), asset-url("@primer/css/fonts/Inter-Bold.woff") format("woff")
28
+ font-display: swap
29
+
30
+ .admin
31
+ @import "spina/admin_editing"
32
+ #admin_bar
33
+ font-family: 'Metropolis', sans-serif
34
+
35
+ .turbo-progress-bar
36
+ background-color: white
37
+
38
+ address
39
+ font-style: normal
40
+
41
+ #form_frame
42
+ border: none
43
+ width: 100%
44
+ height: 70vh
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Conferences
5
+ module PrimerTheme
6
+ module Conferences
7
+ # Base class from which controllers inherit
8
+ class ApplicationController < Spina::ApplicationController
9
+ def cookies_info
10
+ render partial: 'cookies'
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Conferences
5
+ module PrimerTheme
6
+ module Conferences
7
+ # User-facing controller for conferences, serving both html and ics
8
+ class ConferencesController < ApplicationController
9
+ before_action :set_conference, :set_tab, :set_presentation_type, :set_presentations, :set_breadcrumb, only: :show
10
+ before_action :set_metadata
11
+
12
+ def index
13
+ @conferences = Admin::Conferences::Conference.sorted.includes(institutions: [:logo])
14
+ respond_to do |format|
15
+ format.html
16
+ format.ics { render body: @conferences.to_ics }
17
+ end
18
+ end
19
+
20
+ def show
21
+ add_breadcrumb @conference.name
22
+ respond_to do |format|
23
+ format.html
24
+ format.ics { render body: @conference.to_ics }
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def set_conference
31
+ @conference = Admin::Conferences::Conference.includes(:events, :institutions,
32
+ presentation_types: [:translations],
33
+ presentations: [session: [:room], presenters: [:institution]])
34
+ .find(params[:id])
35
+ @conference.view_context = view_context
36
+ rescue ActiveRecord::RecordNotFound
37
+ send_file Rails.root.join('public/404.html'), type: 'text/html; charset=utf-8', status: 404
38
+ end
39
+
40
+ def set_tab
41
+ @tab = params[:tab] || 'information'
42
+ end
43
+
44
+ def set_presentation_type
45
+ if params[:presentation_type].present?
46
+ @presentation_type = @conference.presentation_types
47
+ .includes(presentations: [session: [:room], presenters: [:institution]])
48
+ .find(params[:presentation_type])
49
+ end
50
+ end
51
+
52
+ def set_presentations
53
+ @presentations = if @presentation_type.present?
54
+ @presentation_type.presentations.page(params[:page]).per(15)
55
+ else
56
+ @conference.presentations.page(params[:page]).per(15)
57
+ end
58
+ end
59
+
60
+ def set_breadcrumb
61
+ add_breadcrumb Admin::Conferences::Conference.model_name.human.pluralize, frontend_conferences_path
62
+ end
63
+
64
+ def set_metadata
65
+ @title = @conference.present? ? @conference.name : Admin::Conferences::Conference.model_name.human(count: 0)
66
+ @description = @conference.present? && @conference.has_content?(:text) ? helpers.strip_tags(@conference.content(:text)) : ''
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Conferences
5
+ module PrimerTheme
6
+ module Conferences
7
+ # User-facing controller for presentations, serving both html and ics
8
+ class PresentationsController < ApplicationController
9
+ before_action :set_presentation, :set_conference, :set_breadcrumb
10
+
11
+ def show
12
+ add_breadcrumb @presentation.name
13
+ respond_to do |format|
14
+ format.html
15
+ format.ics { head :not_found }
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def set_presentation
22
+ @presentation = Admin::Conferences::Presentation.includes(:presenters, attachments: [attachment_type: [:translations]])
23
+ .find(params[:id])
24
+ rescue ActiveRecord::RecordNotFound
25
+ send_file Rails.root.join('public/404.html'), type: 'text/html; charset=utf-8', status: 404
26
+ end
27
+
28
+ def set_conference
29
+ @conference = Admin::Conferences::Conference.find(params[:conference_id]) if params[:conference_id].present?
30
+ end
31
+
32
+ def set_breadcrumb
33
+ return if @conference.blank?
34
+
35
+ add_breadcrumb Admin::Conferences::Conference.model_name.human.pluralize, frontend_conferences_path
36
+ add_breadcrumb @conference.name, frontend_conference_path(@conference)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Conferences
5
+ module PrimerTheme
6
+ module Journal
7
+ # Base class from which controllers related to the journal plugin inherit
8
+ class ApplicationController < Spina::ApplicationController
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Conferences
5
+ module PrimerTheme
6
+ module Journal
7
+ # User-facing controller for journal articles
8
+ class ArticlesController < ApplicationController
9
+ before_action :set_article, :set_issue, :set_journal, :set_licence, :set_breadcrumb, :set_metadata, :require_admin_for_invisible_article
10
+
11
+ def show
12
+ respond_to do |format|
13
+ format.html { add_breadcrumb @article.title }
14
+ format.pdf do
15
+ if @article.has_content?(:attachment)
16
+ redirect_to main_app.url_for(@article.content(:attachment))
17
+ else
18
+ send_file Rails.root.join('public/404.html'), type: 'text/html; charset=utf-8', status: 404
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def set_article
27
+ @article = Admin::Journal::Article.includes(affiliations: [:institution]).find(params[:id])
28
+ rescue ActiveRecord::RecordNotFound
29
+ send_file Rails.root.join('public/404.html'), type: 'text/html; charset=utf-8', status: 404
30
+ end
31
+
32
+ def set_issue
33
+ @issue = Admin::Journal::Issue.includes(:volume, :articles).find(params[:issue_id])
34
+ rescue ActiveRecord::RecordNotFound
35
+ send_file Rails.root.join('public/404.html'), type: 'text/html; charset=utf-8', status: 404
36
+ end
37
+
38
+ def set_journal
39
+ @journal = @issue.volume.journal
40
+ end
41
+
42
+ def set_licence
43
+ @licence = @article.licence
44
+ end
45
+
46
+ def set_breadcrumb
47
+ return if @issue.blank?
48
+
49
+ add_breadcrumb @journal.name, frontend_issues_path
50
+ add_breadcrumb Admin::Journal::Issue.model_name.human.pluralize, frontend_issues_path
51
+ add_breadcrumb t('spina.conferences.primer_theme.journal.volume_issue', volume_number: @issue.volume.number, issue_number: @issue.number),
52
+ frontend_issue_path(@issue.id)
53
+ end
54
+
55
+ def set_metadata
56
+ @title = @article.title
57
+ @description = @article.content(:abstract).try(:to_plain_text)
58
+ end
59
+
60
+ def require_admin_for_invisible_article
61
+ raise ActiveRecord::RecordNotFound unless current_spina_user.present? || @article.visible?
62
+ rescue ActiveRecord::RecordNotFound # TODO: proper 404 handler
63
+ send_file Rails.root.join('public/404.html'), type: 'text/html; charset=utf-8', status: 404
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end