mini_blog 0.1.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 (64) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +34 -0
  4. data/Rakefile +36 -0
  5. data/app/assets/config/mini_blog_manifest.js +2 -0
  6. data/app/assets/javascripts/mini_blog/application.js +27 -0
  7. data/app/assets/stylesheets/mini_blog/application.scss +30 -0
  8. data/app/controllers/mini_blog/application_controller.rb +5 -0
  9. data/app/controllers/mini_blog/articles_controller.rb +56 -0
  10. data/app/controllers/mini_blog/users/confirmations_controller.rb +34 -0
  11. data/app/controllers/mini_blog/users/omniauth_callbacks_controller.rb +34 -0
  12. data/app/controllers/mini_blog/users/passwords_controller.rb +39 -0
  13. data/app/controllers/mini_blog/users/registrations_controller.rb +66 -0
  14. data/app/controllers/mini_blog/users/sessions_controller.rb +32 -0
  15. data/app/controllers/mini_blog/users/unlocks_controller.rb +34 -0
  16. data/app/helpers/mini_blog/application_helper.rb +4 -0
  17. data/app/jobs/mini_blog/application_job.rb +4 -0
  18. data/app/mailers/mini_blog/application_mailer.rb +6 -0
  19. data/app/models/mini_blog/application_record.rb +5 -0
  20. data/app/models/mini_blog/article.rb +10 -0
  21. data/app/models/mini_blog/article_tag_relation.rb +9 -0
  22. data/app/models/mini_blog/image.rb +6 -0
  23. data/app/models/mini_blog/tag.rb +8 -0
  24. data/app/models/mini_blog/user.rb +10 -0
  25. data/app/uploaders/mini_blog/image_content_uploader.rb +49 -0
  26. data/app/views/kaminari/bootstrap4/_first_page.html.slim +2 -0
  27. data/app/views/kaminari/bootstrap4/_gap.html.slim +2 -0
  28. data/app/views/kaminari/bootstrap4/_last_page.html.slim +2 -0
  29. data/app/views/kaminari/bootstrap4/_next_page.html.slim +2 -0
  30. data/app/views/kaminari/bootstrap4/_page.html.slim +6 -0
  31. data/app/views/kaminari/bootstrap4/_paginator.html.slim +12 -0
  32. data/app/views/kaminari/bootstrap4/_prev_page.html.slim +2 -0
  33. data/app/views/layouts/mini_blog/application.html.slim +28 -0
  34. data/app/views/mini_blog/articles/_form.html.slim +9 -0
  35. data/app/views/mini_blog/articles/edit.html.slim +1 -0
  36. data/app/views/mini_blog/articles/index.html.slim +14 -0
  37. data/app/views/mini_blog/articles/new.html.slim +1 -0
  38. data/app/views/mini_blog/articles/show.json.ruby +3 -0
  39. data/app/views/mini_blog/users/confirmations/new.html.erb +16 -0
  40. data/app/views/mini_blog/users/mailer/confirmation_instructions.html.erb +5 -0
  41. data/app/views/mini_blog/users/mailer/email_changed.html.erb +7 -0
  42. data/app/views/mini_blog/users/mailer/password_change.html.erb +3 -0
  43. data/app/views/mini_blog/users/mailer/reset_password_instructions.html.erb +8 -0
  44. data/app/views/mini_blog/users/mailer/unlock_instructions.html.erb +7 -0
  45. data/app/views/mini_blog/users/passwords/edit.html.slim +22 -0
  46. data/app/views/mini_blog/users/passwords/new.html.slim +10 -0
  47. data/app/views/mini_blog/users/registrations/edit.html.erb +43 -0
  48. data/app/views/mini_blog/users/registrations/new.html.erb +29 -0
  49. data/app/views/mini_blog/users/sessions/new.html.slim +17 -0
  50. data/app/views/mini_blog/users/shared/_links.html.erb +25 -0
  51. data/app/views/mini_blog/users/unlocks/new.html.erb +16 -0
  52. data/config/initializers/devise.rb +279 -0
  53. data/config/locales/devise.en.yml +64 -0
  54. data/config/routes.rb +7 -0
  55. data/db/migrate/20180224022923_create_mini_blog_articles.rb +12 -0
  56. data/db/migrate/20180224025140_create_mini_blog_images.rb +9 -0
  57. data/db/migrate/20180224090210_devise_create_mini_blog_users.rb +45 -0
  58. data/db/migrate/20180224112101_create_mini_blog_tags.rb +9 -0
  59. data/db/migrate/20180224113006_create_mini_blog_article_tag_relations.rb +12 -0
  60. data/lib/mini_blog.rb +11 -0
  61. data/lib/mini_blog/engine.rb +5 -0
  62. data/lib/mini_blog/version.rb +3 -0
  63. data/lib/tasks/mini_blog_tasks.rake +4 -0
  64. metadata +260 -0
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniBlog
4
+ module Users
5
+ class UnlocksController < Devise::UnlocksController
6
+ # GET /resource/unlock/new
7
+ # def new
8
+ # super
9
+ # end
10
+
11
+ # POST /resource/unlock
12
+ # def create
13
+ # super
14
+ # end
15
+
16
+ # GET /resource/unlock?unlock_token=abcdef
17
+ # def show
18
+ # super
19
+ # end
20
+
21
+ # protected
22
+
23
+ # The path used after sending unlock password instructions
24
+ # def after_sending_unlock_instructions_path_for(resource)
25
+ # super(resource)
26
+ # end
27
+
28
+ # The path used after unlocking the resource
29
+ # def after_unlock_path_for(resource)
30
+ # super(resource)
31
+ # end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,4 @@
1
+ module MiniBlog
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module MiniBlog
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module MiniBlog
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module MiniBlog
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ module MiniBlog
2
+ class Article < ApplicationRecord
3
+ has_many :article_tag_relations, dependent: :destroy
4
+ has_many :tags, through: :article_tag_relations
5
+
6
+ validates :title, presence: true
7
+ validates :body, presence: true
8
+ validates :status, presence: true
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module MiniBlog
2
+ class ArticleTagRelation < ApplicationRecord
3
+ belongs_to :article
4
+ belongs_to :tag
5
+
6
+ validates :article, presence: true
7
+ validates :tag, presence: true
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ module MiniBlog
2
+ class Image < ApplicationRecord
3
+ mount_uploader :content, MiniBlog::ImageContentUploader
4
+ validates :content, presence: true
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ module MiniBlog
2
+ class Tag < ApplicationRecord
3
+ has_many :article_tag_relations, dependent: :destroy
4
+ has_many :articles, through: :article_tag_relations
5
+
6
+ validates :name, presence: true
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ module MiniBlog
2
+ class User < ApplicationRecord
3
+ # Include default devise modules. Others available are:
4
+ # :confirmable, :lockable, :timeoutable and :omniauthable
5
+ devise :database_authenticatable, :recoverable,
6
+ :rememberable, :trackable, :validatable
7
+
8
+ validates :name, presence: true
9
+ end
10
+ end
@@ -0,0 +1,49 @@
1
+ module MiniBlog
2
+ class ImageContentUploader < CarrierWave::Uploader::Base
3
+ # Include RMagick or MiniMagick support:
4
+ # include CarrierWave::RMagick
5
+ # include CarrierWave::MiniMagick
6
+
7
+ # Choose what kind of storage to use for this uploader:
8
+ storage :file
9
+ # storage :fog
10
+
11
+ # Override the directory where uploaded files will be stored.
12
+ # This is a sensible default for uploaders that are meant to be mounted:
13
+ def store_dir
14
+ "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
15
+ end
16
+
17
+ # Provide a default URL as a default if there hasn't been a file uploaded:
18
+ # def default_url(*args)
19
+ # # For Rails 3.1+ asset pipeline compatibility:
20
+ # # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
21
+ #
22
+ # "/images/fallback/" + [version_name, "default.png"].compact.join('_')
23
+ # end
24
+
25
+ # Process files as they are uploaded:
26
+ # process scale: [200, 300]
27
+ #
28
+ # def scale(width, height)
29
+ # # do something
30
+ # end
31
+
32
+ # Create different versions of your uploaded files:
33
+ # version :thumb do
34
+ # process resize_to_fit: [50, 50]
35
+ # end
36
+
37
+ # Add a white list of extensions which are allowed to be uploaded.
38
+ # For images you might use something like this:
39
+ # def extension_whitelist
40
+ # %w(jpg jpeg gif png)
41
+ # end
42
+
43
+ # Override the filename of the uploaded files:
44
+ # Avoid using model.id or version_name here, see uploader/store.rb for details.
45
+ # def filename
46
+ # "something.jpg" if original_filename
47
+ # end
48
+ end
49
+ end
@@ -0,0 +1,2 @@
1
+ li.page-item
2
+ = link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, remote: remote, class: 'page-link'
@@ -0,0 +1,2 @@
1
+ li.page-item.disabled
2
+ = link_to raw(t 'views.pagination.truncate'), '#', class: 'page-link'
@@ -0,0 +1,2 @@
1
+ li.page-item
2
+ = link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, remote: remote, class: 'page-link'
@@ -0,0 +1,2 @@
1
+ li.page-item
2
+ = link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, rel: 'next', remote: remote, class: 'page-link'
@@ -0,0 +1,6 @@
1
+ - if page.current?
2
+ li.page-item.active
3
+ = content_tag :a, page, remote: remote, rel: (page.next? ? 'next' : (page.prev? ? 'prev' : nil)), class: 'page-link'
4
+ - else
5
+ li.page-item
6
+ = link_to page, url, remote: remote, rel: (page.next? ? 'next' : (page.prev? ? 'prev' : nil)), class: 'page-link'
@@ -0,0 +1,12 @@
1
+ = paginator.render do
2
+ nav
3
+ ul.pagination
4
+ == first_page_tag unless current_page.first?
5
+ == prev_page_tag unless current_page.first?
6
+ - each_page do |page|
7
+ - if page.left_outer? || page.right_outer? || page.inside_window?
8
+ == page_tag page
9
+ - elsif !page.was_truncated?
10
+ == gap_tag
11
+ == next_page_tag unless current_page.last?
12
+ == last_page_tag unless current_page.last?
@@ -0,0 +1,2 @@
1
+ li.page-item
2
+ = link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, rel: 'prev', remote: remote, class: 'page-link'
@@ -0,0 +1,28 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title
5
+ | Mini blog
6
+ meta name="viewport" content="width=device-width,initial-scale=1"
7
+ = stylesheet_link_tag 'mini_blog/application', media: 'all'
8
+ = javascript_include_tag 'mini_blog/application'
9
+ = csrf_meta_tags
10
+ body
11
+ header
12
+ nav.navbar.navbar-expand-lg.navbar-light.bg-light
13
+ .container
14
+ button.navbar-toggler data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"
15
+ span.navbar-toggler-icon
16
+ = link_to 'MiniBlog', root_path, class: 'navbar-brand'
17
+ #navbarSupportedContent.collapse.navbar-collapse
18
+ ul.navbar-nav.mr-auto
19
+ - if user_signed_in?
20
+ li.nav-item
21
+ = link_to 'Sign out', destroy_user_session_path, class: 'nav-link', method: :delete
22
+ li.nav-item
23
+ = link_to 'New Article', new_article_path, class: 'nav-link'
24
+ - else
25
+ li.nav-item
26
+ = link_to 'Sign in', new_user_session_path, class: 'nav-link'
27
+ .container.main-contents
28
+ = yield
@@ -0,0 +1,9 @@
1
+ = form_with model: article, local: true do |f|
2
+ .form-group
3
+ = f.text_field :title, class: 'form-control', placeholder: 'Title'
4
+ .form-group
5
+ = f.text_area :body, class: 'form-control form-control-lg', placeholder: 'Body'
6
+ .form-group
7
+ = text_field :article, :tags, value: article.tags.pluck(:name).join('; '), class: 'form-control', placeholder: 'Tag'
8
+ button.btn.btn-info type="submit"
9
+ | send
@@ -0,0 +1 @@
1
+ = render 'form', article: @article
@@ -0,0 +1,14 @@
1
+ ul
2
+ - @articles.each do |article|
3
+ li.card
4
+ .card-header
5
+ h2
6
+ = "#{l(article.created_at)} #{article.title}"
7
+ ul
8
+ - article.tags.each do |tag|
9
+ li.badge.badge-dark.tag
10
+ = tag.name
11
+ article.article.card-body.collapse*{id: "article-#{article.id}", 'data-id': article.id}
12
+ button.btn.btn-link.btn-sm.text-left data-toggle="collapse" href="#article-#{article.id}" role="button" aria-expanded="false" aria-controls="article-#{article.id}"
13
+ | open...
14
+ = paginate @articles, theme: :bootstrap4
@@ -0,0 +1 @@
1
+ = render 'form', article: @article
@@ -0,0 +1,3 @@
1
+ {
2
+ body: @article.body
3
+ }.to_json
@@ -0,0 +1,16 @@
1
+ <h2>Resend confirmation instructions</h2>
2
+
3
+ <%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
4
+ <%= devise_error_messages! %>
5
+
6
+ <div class="field">
7
+ <%= f.label :email %><br />
8
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
9
+ </div>
10
+
11
+ <div class="actions">
12
+ <%= f.submit "Resend confirmation instructions" %>
13
+ </div>
14
+ <% end %>
15
+
16
+ <%= render "mini_blog/users/shared/links" %>
@@ -0,0 +1,5 @@
1
+ <p>Welcome <%= @email %>!</p>
2
+
3
+ <p>You can confirm your account email through the link below:</p>
4
+
5
+ <p><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></p>
@@ -0,0 +1,7 @@
1
+ <p>Hello <%= @email %>!</p>
2
+
3
+ <% if @resource.try(:unconfirmed_email?) %>
4
+ <p>We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.</p>
5
+ <% else %>
6
+ <p>We're contacting you to notify you that your email has been changed to <%= @resource.email %>.</p>
7
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <p>Hello <%= @resource.email %>!</p>
2
+
3
+ <p>We're contacting you to notify you that your password has been changed.</p>
@@ -0,0 +1,8 @@
1
+ <p>Hello <%= @resource.email %>!</p>
2
+
3
+ <p>Someone has requested a link to change your password. You can do this through the link below.</p>
4
+
5
+ <p><%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %></p>
6
+
7
+ <p>If you didn't request this, please ignore this email.</p>
8
+ <p>Your password won't change until you access the link above and create a new one.</p>
@@ -0,0 +1,7 @@
1
+ <p>Hello <%= @resource.email %>!</p>
2
+
3
+ <p>Your account has been locked due to an excessive number of unsuccessful sign in attempts.</p>
4
+
5
+ <p>Click the link below to unlock your account:</p>
6
+
7
+ <p><%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %></p>
@@ -0,0 +1,22 @@
1
+ h2
2
+ | Change your password
3
+ = form_with model: resource, url: user_password_path, html: {method: :put}, local: true do |f|
4
+ = devise_error_messages!
5
+ = f.hidden_field :reset_password_token
6
+ .form-group
7
+ = f.label :password, 'New password'
8
+ br
9
+ - if @minimum_password_length
10
+ em
11
+ | (
12
+ = @minimum_password_length
13
+ | characters minimum)
14
+ br
15
+ = f.password_field :password, autofocus: true, autocomplete: 'off', class: 'form-controller'
16
+ .field
17
+ = f.label :password_confirmation, 'Confirm new password'
18
+ br
19
+ = f.password_field :password_confirmation, autocomplete: 'off'
20
+ button.btn.btn-outline-success
21
+ | Send me reset password instructions
22
+ = render 'mini_blog/users/shared/links'
@@ -0,0 +1,10 @@
1
+ h2
2
+ | Forgot your password?
3
+ = form_with model: resource, url: user_password_path, local: true do |f|
4
+ = devise_error_messages!
5
+ .form-group
6
+ = f.label :email
7
+ = f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control'
8
+ button.btn.btn-outline-success
9
+ | Send me reset password instructions
10
+ = render 'mini_blog/users/shared/links'
@@ -0,0 +1,43 @@
1
+ <h2>Edit <%= resource_name.to_s.humanize %></h2>
2
+
3
+ <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
4
+ <%= devise_error_messages! %>
5
+
6
+ <div class="field">
7
+ <%= f.label :email %><br />
8
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
9
+ </div>
10
+
11
+ <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
12
+ <div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
13
+ <% end %>
14
+
15
+ <div class="field">
16
+ <%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
17
+ <%= f.password_field :password, autocomplete: "off" %>
18
+ <% if @minimum_password_length %>
19
+ <br />
20
+ <em><%= @minimum_password_length %> characters minimum</em>
21
+ <% end %>
22
+ </div>
23
+
24
+ <div class="field">
25
+ <%= f.label :password_confirmation %><br />
26
+ <%= f.password_field :password_confirmation, autocomplete: "off" %>
27
+ </div>
28
+
29
+ <div class="field">
30
+ <%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
31
+ <%= f.password_field :current_password, autocomplete: "off" %>
32
+ </div>
33
+
34
+ <div class="actions">
35
+ <%= f.submit "Update" %>
36
+ </div>
37
+ <% end %>
38
+
39
+ <h3>Cancel my account</h3>
40
+
41
+ <p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p>
42
+
43
+ <%= link_to "Back", :back %>
@@ -0,0 +1,29 @@
1
+ <h2>Sign up</h2>
2
+
3
+ <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
4
+ <%= devise_error_messages! %>
5
+
6
+ <div class="field">
7
+ <%= f.label :email %><br />
8
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
9
+ </div>
10
+
11
+ <div class="field">
12
+ <%= f.label :password %>
13
+ <% if @minimum_password_length %>
14
+ <em>(<%= @minimum_password_length %> characters minimum)</em>
15
+ <% end %><br />
16
+ <%= f.password_field :password, autocomplete: "off" %>
17
+ </div>
18
+
19
+ <div class="field">
20
+ <%= f.label :password_confirmation %><br />
21
+ <%= f.password_field :password_confirmation, autocomplete: "off" %>
22
+ </div>
23
+
24
+ <div class="actions">
25
+ <%= f.submit "Sign up" %>
26
+ </div>
27
+ <% end %>
28
+
29
+ <%= render "mini_blog/users/shared/links" %>