rtcl 1.0.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 (48) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +14 -0
  5. data/app/assets/config/rtcl_manifest.js +3 -0
  6. data/app/assets/images/user-sample.jpg +0 -0
  7. data/app/assets/javascripts/rtcl/application.js +174 -0
  8. data/app/assets/javascripts/rtcl/name-that-color.js +1 -0
  9. data/app/assets/stylesheets/rtcl/application.css +7 -0
  10. data/app/assets/stylesheets/rtcl/highlight-android.css +1 -0
  11. data/app/assets/stylesheets/rtcl/styles.css +2578 -0
  12. data/app/assets/stylesheets/rtcl/trix.css +556 -0
  13. data/app/controllers/rtcl/application_controller.rb +18 -0
  14. data/app/controllers/rtcl/articles/comments_controller.rb +11 -0
  15. data/app/controllers/rtcl/articles_controller.rb +65 -0
  16. data/app/controllers/rtcl/comments_controller.rb +27 -0
  17. data/app/helpers/rtcl/application_helper.rb +4 -0
  18. data/app/jobs/rtcl/application_job.rb +4 -0
  19. data/app/mailers/rtcl/application_mailer.rb +6 -0
  20. data/app/models/ability.rb +20 -0
  21. data/app/models/concerns/rtcl/blogger.rb +9 -0
  22. data/app/models/rtcl/application_record.rb +5 -0
  23. data/app/models/rtcl/article.rb +53 -0
  24. data/app/views/layouts/rtcl/application.html.erb +19 -0
  25. data/app/views/rtcl/articles/_article.html.erb +33 -0
  26. data/app/views/rtcl/articles/_article.json.jbuilder +5 -0
  27. data/app/views/rtcl/articles/_form.html.erb +69 -0
  28. data/app/views/rtcl/articles/edit.html.erb +17 -0
  29. data/app/views/rtcl/articles/index.html.erb +32 -0
  30. data/app/views/rtcl/articles/new.html.erb +16 -0
  31. data/app/views/rtcl/articles/show.html.erb +64 -0
  32. data/app/views/rtcl/comments/_comment.html.erb +47 -0
  33. data/app/views/rtcl/comments/_comments.html.erb +18 -0
  34. data/app/views/rtcl/comments/_form.html.erb +11 -0
  35. data/app/views/rtcl/comments/new.html.erb +7 -0
  36. data/app/views/rtcl/comments/show.html.erb +1 -0
  37. data/config/routes.rb +11 -0
  38. data/db/migrate/20220208044728_create_articles.rb +16 -0
  39. data/lib/generators/rtcl/install/install_generator.rb +28 -0
  40. data/lib/generators/rtcl/templates/active_model_serializers.rb +1 -0
  41. data/lib/generators/rtcl/templates/rtcl.rb +10 -0
  42. data/lib/rtcl/configuration.rb +7 -0
  43. data/lib/rtcl/engine.rb +33 -0
  44. data/lib/rtcl/railtie.rb +4 -0
  45. data/lib/rtcl/version.rb +3 -0
  46. data/lib/rtcl.rb +30 -0
  47. data/lib/tasks/rtcl_tasks.rake +4 -0
  48. metadata +252 -0
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Ability
4
+ include CanCan::Ability
5
+
6
+ def initialize(user)
7
+ can :read, User
8
+ can :read, Rtcl::Article, is_private: false
9
+
10
+ return unless user.present?
11
+
12
+ can :manage, Rtcl::Article, author: user
13
+ can :manage, Polivalente::Comment, user: user
14
+ can :manage, User, id: user.id
15
+
16
+ return unless user.is_admin?
17
+
18
+ can :create, Rtcl::Article
19
+ end
20
+ end
@@ -0,0 +1,9 @@
1
+ module Rtcl
2
+ module Blogger
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ has_many :articles, foreign_key: :author_id, class_name: "Rtcl::Article"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ module Rtcl
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,53 @@
1
+ module Rtcl
2
+ class Article < ApplicationRecord
3
+ include Polivalente::Archivable
4
+ include Polivalente::Commentable
5
+ include Polivalente::ContentHashable
6
+ include Polivalente::Reactable
7
+ include Polivalente::Sortable
8
+ include Polivalente::Taggable
9
+ include Polivalente::Trashable
10
+ include Polivalente::Visibility
11
+
12
+ has_rich_text :content
13
+
14
+ belongs_to :author, class_name: "User"
15
+ alias :user :author
16
+
17
+ enum language: Hash[LanguageList::COMMON_LANGUAGES.map { |l| [l.name, l.iso_639_1] }]
18
+
19
+ scope :published, -> { where(status: :published) }
20
+ scope :with_author, -> { includes(:author) }
21
+
22
+ validates_presence_of :content
23
+ validates_presence_of :language
24
+
25
+ validates :title, length: { minimum: 3, maximum: 50}
26
+ validates :language, inclusion: { in: languages.keys }
27
+
28
+ def summary
29
+ "#{truncate(content)}" if excerpt.nil?
30
+ excerpt
31
+ end
32
+
33
+ def byline
34
+ "by #{author.name}"
35
+ end
36
+
37
+ def time_since_created
38
+ ActionController::Base.helpers.distance_of_time_in_words(Time.now, created_at)
39
+ end
40
+
41
+ def language_code
42
+ LanguageList::LanguageInfo.find(language).iso_639_1.inspect.gsub(/\"/i, '') unless self.new_record?
43
+ end
44
+
45
+ def colors
46
+ tag_list.split(',').each do |tag|
47
+ return tag if Rtcl.config.supported_tags.include? tag.strip
48
+ end
49
+
50
+ "base"
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,19 @@
1
+ <!DOCTYPE html>
2
+ <html data-controller="theme" data-theme-target="theme">
3
+ <head>
4
+ <title><%= @page_title.nil? ? "" : "#{@page_title} | " %><%= Rails.application.class %></title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track": "reload" %>
9
+ <%= javascript_include_tag "rtcl/application", media: "all", "data-turbolinks-track": "reload" %>
10
+ </head>
11
+ <body>
12
+
13
+ <%= render 'polivalente/shared/offline_indicator' %>
14
+ <%= render 'polivalente/shared/notices' %>
15
+
16
+ <%= yield %>
17
+
18
+ </body>
19
+ </html>
@@ -0,0 +1,33 @@
1
+ <%= turbo_frame_tag(dom_id(article), class: "relative colored-article p-5 rounded-lg shadow-md #{article.colors}") do %>
2
+ <div class="flex justify-between pb-3">
3
+ <p class="uppercase text-sm"><%= article.time_since_created %></p>
4
+ <div class="flex items-center space-x-1.5">
5
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="bi bi-collection-play" viewBox="0 0 16 16">
6
+ <path d="M2 3a.5.5 0 0 0 .5.5h11a.5.5 0 0 0 0-1h-11A.5.5 0 0 0 2 3zm2-2a.5.5 0 0 0 .5.5h7a.5.5 0 0 0 0-1h-7A.5.5 0 0 0 4 1zm2.765 5.576A.5.5 0 0 0 6 7v5a.5.5 0 0 0 .765.424l4-2.5a.5.5 0 0 0 0-.848l-4-2.5z"/>
7
+ <path d="M1.5 14.5A1.5 1.5 0 0 1 0 13V6a1.5 1.5 0 0 1 1.5-1.5h13A1.5 1.5 0 0 1 16 6v7a1.5 1.5 0 0 1-1.5 1.5h-13zm13-1a.5.5 0 0 0 .5-.5V6a.5.5 0 0 0-.5-.5h-13A.5.5 0 0 0 1 6v7a.5.5 0 0 0 .5.5h13z"/>
8
+ </svg>
9
+ <%= link_to polivalente.user_path(article.author), class: "secondary rounded-sm px-1 text-sm font-semibold" do %>
10
+ <%= article.author.name %>
11
+ <% end %>
12
+ </div>
13
+ </div>
14
+
15
+ <%= link_to article, class: "mt-2 block", data: "#{article.title}" do %>
16
+ <h3 class="pb-4 font-bold font-serif text-2xl md:truncate capitalize">
17
+ <%= article.title %>
18
+ </h3>
19
+ <% end %>
20
+
21
+ <p class="pb-6"><%= article.excerpt %></p>
22
+
23
+ <% if article.is_private %>
24
+ <div class="flex flex-row space-x-0.5 items-center absolute bottom-2">
25
+
26
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
27
+ <path fill-rule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clip-rule="evenodd" />
28
+ </svg>
29
+
30
+ <span class="text-sm font-semibold"><%= t("views.visibility.private") %></span>
31
+ </div>
32
+ <% end %>
33
+ <% end %>
@@ -0,0 +1,5 @@
1
+ json.extract! article, :id, :author_id, :title, :created_at, :updated_at
2
+ json.name article.title
3
+ json.sgid article.attachable_sgid
4
+ json.content render(partial: "rtcl/articles/article", locals: { article: article }, formats: [:html])
5
+ json.url article_url(article)
@@ -0,0 +1,69 @@
1
+ <%= form_with(model: article, :html => {class: 'form flex flex-col text-primary'}) do |form| %>
2
+ <% if article.errors.any? %>
3
+ <div id="error_explanation" class="text-primary">
4
+ <h2><%= pluralize(article.errors.count, "error") %> prohibited this article from being saved:</h2>
5
+
6
+ <ul>
7
+ <% article.errors.each do |error| %>
8
+ <li><%= error.full_message %></li>
9
+ <% end %>
10
+ </ul>
11
+ </div>
12
+ <% end %>
13
+
14
+ <div class="my-2">
15
+ <%= form.text_field :title, class: "input-base bg-primary text-secondary-x border-tertiary text-2xl leading-8 font-extrabold tracking-tight sm:text-4xl" %>
16
+ </div>
17
+
18
+ <div class="my-2">
19
+ <%= form.label :tag_list, "Tags (separated by commas)", class: "text-sm" %>
20
+ <%= form.text_field :tag_list, class: "input-base bg-primary border-tertiary leading-8 tracking-tight", data: {controller: "trix-autocomplete", trix_autocomplete_url_value: polivalente.autocomplete_tags_path, trix_autocomplete_auto_value: "true", trix_autocomplete_trigger_value: "#"} %>
21
+
22
+ <div class="text-xs lowercase pt-1.5">
23
+ <%= render "polivalente/tags/list", taggable: article %>
24
+ </div>
25
+ </div>
26
+
27
+ <div class="my-2 relative" data-controller="text-counter" data-text-counter-limit-value="144" data-text-counter-error-class="text-red-500 border-red-400 font-bold">
28
+ <%= form.label :excerpt, class: "text-sm" %>
29
+ <%= form.text_area :excerpt, class: "input-base bg-primary border-tertiary max-h-24", data: {text_counter_target: "source"}, rows: "3", min: "3", max: "144" %>
30
+ <span class="absolute text-sm border bg-secondary border-tertiary rounded px-2 right-2 bottom-5" data-text-counter-target="output"></span>
31
+ </div>
32
+
33
+ <div class="my-8 relative" data-controller="text-counter plyr" data-text-counter-word-mode-value="true" >
34
+ <%= form.rich_text_area :content, class: "input-base bg-primary border-tertiary", data: {controller: "trix-autocomplete", trix_autocomplete_url_value: polivalente.autocomplete_users_path, trix_autocomplete_trigger_value: "@", text_counter_target: "source", plyr_target: 'editor' } %>
35
+ <span class="absolute text-sm border border-tertiary bg-primary rounded px-2 right-2 bottom-2" data-text-counter-target="output"></span>
36
+ </div>
37
+
38
+ <div class="-mx-3 my-2">
39
+ <div class="md:w-full px-3">
40
+ <%= form.label :language, class: "block uppercase tracking-wide text-xs font-bold mb-2" %>
41
+ <div class="relative">
42
+ <%= form.select :language, options_for_select(Rtcl::Article.languages, selected: article.language_code), { :class => "block appearance-none w-full bg-secondary border border-tertiary py-3 px-4 pr-8 rounded" } %>
43
+ </div>
44
+ </div>
45
+ </div>
46
+
47
+ <div class="-mx-3 my-2">
48
+ <div class="md:w-full px-3">
49
+ <div class="flex items-center">
50
+ <%= form.check_box :is_private, class: "appearance-none bg-secondary border border-tertiary rounded focus:ring-indigo-500 h-4 w-4 mb-3 p-2" %>
51
+ <%= form.label :is_private, class: "block uppercase tracking-wide text-xs font-bold mb-3 ml-3" %>
52
+ </div>
53
+ </div>
54
+ </div>
55
+
56
+ <div class="actions">
57
+ <div class="flex justify-between">
58
+ <%= form.submit nil, class: "py-2 px-4 mb-6 bg-green-600 hover:bg-green-800 text-white text-sm font-bold rounded transition duration-200" %>
59
+
60
+ <div class="flex space-x-3">
61
+ <%= link_to "Delete", article_url, method: :delete, class: "py-2 px-4 mb-6 bg-red-600 hover:bg-red-800 text-white text-sm font-bold rounded transition duration-200" unless article.new_record? %>
62
+
63
+ <%= link_to 'Cancel', article.new_record? ? articles_url : article_url, class: "py-2 px-4 mb-6 bg-secondary border-2 hover:bg-tertiary text-primary text-sm font-bold rounded transition duration-200" %>
64
+ </div>
65
+
66
+ </div>
67
+ </div>
68
+ <% end %>
69
+
@@ -0,0 +1,17 @@
1
+ <section class="relative py-8 overflow-hidden">
2
+ <div class="relative px-4 sm:px-6 lg:px-8">
3
+ <div class="text-lg max-w-prose mx-auto">
4
+ <h1>
5
+ <span class="block text-center text-xs text-blue-600 font-semibold tracking-wide uppercase">
6
+ <%= link_to t("views.articles"), articles_path, class: "text-indigo-600 underline" %><span> - </span>
7
+ <%= @article.created_at.to_formatted_s(:long) %> <span> - </span>
8
+ <%= link_to @article.author.name, @article.author, class: "text-indigo-600 underline" %>
9
+ (<%= @article.time_since_created %>)
10
+ </span>
11
+ </h1>
12
+ </div>
13
+ <div class="mt-6 prose prose-indigo prose-xl text-primary mx-auto">
14
+ <%= render "rtcl/articles/form", article: @article %>
15
+ </div>
16
+ </div>
17
+ </section>
@@ -0,0 +1,32 @@
1
+ <%= turbo_stream_from "articles" %>
2
+
3
+ <main class="mx-auto px-5 md:px-10 lg:px-20">
4
+ <div class="pt-10 pb-12 px-4 sm:px-6 lg:pt-24 lg:pb-10 lg:px-8">
5
+ <div class="relative max-w-7xl mx-auto lg:max-w-7xl">
6
+ <div id="article-list-header">
7
+ <h2 class="text-4xl tracking-tight font-extrabold text-primary sm:text-6xl">
8
+ Blog
9
+ </h2>
10
+ <div class="mt-3 sm:mt-4 lg:grid lg:grid-cols-2 lg:gap-5 lg:items-center">
11
+ <p class="text-xl text-secondary">
12
+ Ideas worth sharing.
13
+ </p>
14
+ </div>
15
+ </div>
16
+
17
+ <div class="py-4 flex justify-between">
18
+ <%= render 'polivalente/tags/cloud', collection: Rtcl::Article.kept.accessible_by(current_ability) %>
19
+
20
+ <% if can? :create, Rtcl::Article %>
21
+ <%= link_to "New Article", new_article_path, class: "py-0.5 px-2 h-8 text-primary border-b hover:border-b-4 border-primary text-sm font-bold transition duration-200" %>
22
+ <% end %>
23
+ </div>
24
+
25
+ <%= turbo_frame_tag("articles", class: "py-4 grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3") do %>
26
+ <%= render @articles %>
27
+ <% end %>
28
+
29
+ <%== pagy_nav(@paginator) if @paginator.pages > 1 %>
30
+ </div>
31
+ </div>
32
+ </main>
@@ -0,0 +1,16 @@
1
+ <section class="relative py-8 overflow-hidden">
2
+ <div class="relative px-4 sm:px-6 lg:px-8">
3
+ <div class="text-lg max-w-prose mx-auto">
4
+ <h1>
5
+ <span class="block text-center text-xs text-blue-600 font-semibold tracking-wide uppercase">
6
+ <%= link_to t("views.articles"), articles_path, class: "text-indigo-600 underline" %><span> - </span>
7
+ <%= Time.now.to_formatted_s(:long) %> <span> - </span>
8
+ <%= link_to current_user.name, current_user, class: "text-indigo-600 underline" %>
9
+ </span>
10
+ </h1>
11
+ </div>
12
+ <div class="mt-6 prose prose-indigo prose-xl text-primary mx-auto">
13
+ <%= render "rtcl/articles/form", article: @article %>
14
+ </div>
15
+ </div>
16
+ </section>
@@ -0,0 +1,64 @@
1
+ <section class="relative py-8 overflow-hidden">
2
+ <div class="relative px-4 sm:px-6 lg:px-8 text-primary">
3
+ <div class="text-lg max-w-prose mx-auto">
4
+ <h1>
5
+ <div class="mt-8">
6
+ <div class="md:flex md:items-center md:justify-center">
7
+ <div class="md:flex-shrink-0">
8
+ <%= image_tag main_app.url_for(user_avatar(@article.author)), class: "mx-auto h-10 w-10 rounded-full" %>
9
+ </div>
10
+ <div class="mt-3 text-center md:mt-0 md:ml-4 md:flex md:items-center">
11
+ <%= link_to @article.author.name, polivalente.user_path(@article.author), class: "text-base font-medium text-indigo-600" %>
12
+
13
+ <svg class="hidden md:block mx-1 h-5 w-5 text-indigo-600" fill="currentColor" viewBox="0 0 20 20">
14
+ <path d="M11 0h3L9 20H6l5-20z" />
15
+ </svg>
16
+
17
+ <div class="text-base font-medium text-secondary"><%= @article.created_at.to_formatted_s(:short) %></div>
18
+
19
+ <svg class="hidden md:block mx-1 h-5 w-5 text-indigo-600" fill="currentColor" viewBox="0 0 20 20">
20
+ <path d="M11 0h3L9 20H6l5-20z" />
21
+ </svg>
22
+
23
+ <%= link_to "All articles", articles_path, class: "text-base text-indigo-600 font-medium" %>
24
+
25
+ <% if can? :manage, @article %>
26
+ <svg class="hidden md:block mx-1 h-5 w-5 text-indigo-600" fill="currentColor" viewBox="0 0 20 20">
27
+ <path d="M11 0h3L9 20H6l5-20z" />
28
+ </svg>
29
+
30
+ <%= link_to "Edit", edit_article_path, class: "text-base text-indigo-600" %>
31
+ <% end %>
32
+ </div>
33
+ </div>
34
+ </div>
35
+
36
+ <p class="my-4 block text-3xl font-serif text-center leading-8 font-extrabold tracking-tight sm:text-5xl capitalize">
37
+ <%= @article.title %>
38
+ </p>
39
+
40
+ <div class="text-center">
41
+ <%= render "polivalente/tags/list", taggable: @article %>
42
+ </div>
43
+ </h1>
44
+ </div>
45
+
46
+ <div class="text-secondary text-center text-sm pt-2">
47
+ <% if @article.is_private %>
48
+ <p>This is a private article</p>
49
+ <% end %>
50
+ </div>
51
+
52
+ <article class="mt-6 text-primary prose prose-indigo prose-xl mx-auto" data-controller="trix-clipboard trix-highlight trix-color trix-plyr">
53
+ <%= @article.content %>
54
+ </article>
55
+
56
+ <div class="pt-16">
57
+ <div class="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
58
+ <div class="relative py-16 px-8 bg-secondary rounded-xl overflow-hidden lg:px-16 border-tertiary">
59
+ <%= render 'rtcl/comments/comments' , commentable: @article %>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ </section>
@@ -0,0 +1,47 @@
1
+ <div class="py-2 mb-2 flex flex-col space-y-4 rounded">
2
+ <div class="flex flex-row items-center space-x-2 justify-between">
3
+ <p class="text-xs text-secondary">
4
+ <% if defined? user %>
5
+ <%= link_to user do %>
6
+ <span class="font-semibold text-blue-600"><%= user.name %></span>
7
+ <% end %>
8
+ <% else %>
9
+ <% if can? :read, comment.commentable %>
10
+ <%= link_to comment.commentable do %>
11
+ <span class="font-semibold text-blue-600 capitalize"><%= comment.commentable.title %></span>
12
+ <% end %>
13
+ <% else %>
14
+ <span class="font-semibold"><%= comment.commentable.title %></span>
15
+ <% end %>
16
+ <% end %>
17
+
18
+ <span class="italic">
19
+ on <%= comment.created_at.to_formatted_s(:long) %>
20
+ </span>
21
+ </p>
22
+
23
+ <div class="flex flex-row items-center space-x-2 text-sm">
24
+ <% if can? :manage, comment %>
25
+ <%= button_to url_for([comment.commentable, comment]), method: :delete, class: "button-red" do %>
26
+ <svg class="w-4 h-4 text-red-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
27
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
28
+ </svg>
29
+ <% end %>
30
+ <% end %>
31
+
32
+ <%#= link_to 'Reply', '', class: "text-blue-600 font-semibold" %>
33
+ </div>
34
+ </div>
35
+
36
+ <div class="flex flex-row align-center space-x-1.5">
37
+ <% if defined? user %>
38
+ <%= link_to user_url(user), class: "rounded-full flex flex-shrink-0 p-0" do %>
39
+ <%= image_tag main_app.url_for(user_avatar(user)), class: "w-8 h-8 rounded-full object-fit", alt: user.name %>
40
+ <% end %>
41
+ <% end %>
42
+
43
+ <article class="text-primary">
44
+ <%= comment.content %>
45
+ </article>
46
+ </div>
47
+ </div>
@@ -0,0 +1,18 @@
1
+ <div class="mx-auto comments--section py-2">
2
+ <p class="text-2xl font-semibold py-2">Comments</p>
3
+
4
+ <% if commentable.comments.latest.each do |comment| %>
5
+ <%= render "rtcl/comments/comment", comment: comment, commentable: commentable, user: comment.user %>
6
+ <% end.empty? %>
7
+ <p class="text-secondary">No comments yet.</p>
8
+ <% end %>
9
+
10
+ <% if current_user %>
11
+ <%= render 'rtcl/comments/form', commentable: commentable %>
12
+ <% else %>
13
+ <p class="text-secondary pt-12">
14
+ Join the conversation by
15
+ <%= link_to 'signing in.', main_app.new_user_session_path, class: "text-blue-600" %>
16
+ </p>
17
+ <% end %>
18
+ </div>
@@ -0,0 +1,11 @@
1
+ <div class="new--comment-form py-4">
2
+ <%= form_for [commentable, Polivalente::Comment.new] do |form| %>
3
+ <div class="mb-2">
4
+ <%= form.rich_text_area :content, data: { controller: "autocomplete trix-attachment-blocker", autocomplete_url_value: polivalente.autocomplete_users_path } %>
5
+ </div>
6
+
7
+ <div class="mb-2">
8
+ <%= form.submit nil, class: "mb-6 px-2 py-2 bg-indigo-600 hover:bg-indigo-700 text-white font-bold rounded shadow-lg hover:shadow-xl transition duration-200" %>
9
+ </div>
10
+ <% end %>
11
+ </div>
@@ -0,0 +1,7 @@
1
+ <% if !is_logged_in? %>
2
+ <%= render 'rtcl/comments/form', commentable: commentable %>
3
+ <% else %>
4
+ <p class="text-secondary">
5
+ Join the conversation by <%= link_to 'signing in', sign_in_path, class: "text-blue-600" %>.
6
+ </p>
7
+ <% end %>
@@ -0,0 +1 @@
1
+ <h1>Comments#Show</h1>
data/config/routes.rb ADDED
@@ -0,0 +1,11 @@
1
+ Rtcl::Engine.routes.draw do
2
+ root to: "articles#index"
3
+
4
+ resources :articles do
5
+ resources :comments, only: [:create, :show, :destroy], module: :articles
6
+ end
7
+
8
+ get 'articles/tags/:tag', to: 'articles#index', as: :tag
9
+
10
+ mount Polivalente::Engine => ""
11
+ end
@@ -0,0 +1,16 @@
1
+ class CreateArticles < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :articles do |t|
4
+ t.references :author, null: false, index: true, foreign_key: { to_table: :users, on_delete: :cascade }
5
+ t.string :title, null: false
6
+ t.string :language, null: true
7
+ t.string :excerpt, null: true, limit: 144
8
+ t.string :content_hash, null: false, index: true
9
+ t.json :metadata, null: false, default: "{}"
10
+ t.boolean :is_private, null: false, index: true, default: false
11
+ t.datetime :discarded_at, null: true, index: true
12
+
13
+ t.timestamps
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,28 @@
1
+ module Rtcl
2
+ module Generators
3
+ class InstallGenerator < ::Rails::Generators::Base
4
+ source_root File.expand_path("../../templates", __FILE__)
5
+
6
+ def copy_migrations
7
+ # Copy all migrations including ActionMailbox, ActionText, and ActiveStorage
8
+ rails_command "railties:install:migrations FROM=rtcl", inline: true
9
+ end
10
+
11
+ def copy_initializer
12
+ template "rtcl.rb", "config/initializers/rtcl.rb"
13
+ end
14
+
15
+ def copy_active_model_serializers_initializer
16
+ template "active_model_serializers.rb", "config/initializers/active_model_serializers.rb"
17
+ end
18
+
19
+ def add_route
20
+ route "mount Rtcl::Engine => '/blog'"
21
+ end
22
+
23
+ def show_readme
24
+ readme "README" if behavior == :invoke
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1 @@
1
+ ActiveModelSerializers.config.adapter = :json_api
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ Rtcl.configure do |config|
4
+ # ==> Controller configuration
5
+ config.base_controller = "::ApplicationController"
6
+ config.base_api_controller = "::ApplicationController"
7
+
8
+ # ==> Tag configuration
9
+ config.supported_tags = []
10
+ end
@@ -0,0 +1,7 @@
1
+ module Rtcl
2
+ class Configuration
3
+ attr_accessor :base_controller
4
+ attr_accessor :base_api_controller
5
+ attr_accessor :supported_tags
6
+ end
7
+ end
@@ -0,0 +1,33 @@
1
+ module Rtcl
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Rtcl
4
+
5
+ config.generators do |g|
6
+ g.scaffold_stylesheet false
7
+ g.assets false
8
+ g.test_framework :rspec
9
+ g.fixture_replacement :factory_bot
10
+ g.factory_bot dir: "spec/factories"
11
+ end
12
+
13
+ # Loads engine helpers in main application
14
+ initializer "local_helper.action_controller" do
15
+ ActiveSupport.on_load :action_controller do
16
+ helper Devise::Engine.helpers
17
+ helper Polivalente::Engine.helpers
18
+ helper Rtcl::Engine.helpers
19
+ end
20
+ end
21
+
22
+ initializer "rtcl.assets" do |app|
23
+ if Rails.application.config.respond_to?(:assets)
24
+ app.config.assets.precompile << "rtcl/application.js"
25
+ app.config.assets.precompile << "rtcl/application.css"
26
+ end
27
+ end
28
+
29
+ # Do not prefix table names with `rtcl_`
30
+ def self.table_name_prefix
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,4 @@
1
+ module Rtcl
2
+ class Railtie < ::Rails::Railtie
3
+ end
4
+ end
@@ -0,0 +1,3 @@
1
+ module Rtcl
2
+ VERSION = "1.0.0"
3
+ end
data/lib/rtcl.rb ADDED
@@ -0,0 +1,30 @@
1
+ require "rtcl/version"
2
+ require "rtcl/railtie"
3
+ require "rtcl/engine"
4
+ require "rtcl/configuration"
5
+
6
+ # Third-party
7
+
8
+ require "active_model_serializers"
9
+ require "cancancan"
10
+ require "hotwire-rails"
11
+ require "image_processing"
12
+ require "jbuilder"
13
+ require "language_list"
14
+ require "pagy"
15
+ require "polivalente"
16
+
17
+ module Rtcl
18
+ class << self
19
+ attr_reader :config
20
+
21
+ def configure
22
+ @config = Configuration.new
23
+ yield config
24
+ end
25
+ end
26
+
27
+ # Do not prefix table names with `rtcl_`
28
+ def self.table_name_prefix
29
+ end
30
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :rtcl do
3
+ # # Task goes here
4
+ # end