blog_boi 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +96 -0
  4. data/Rakefile +32 -0
  5. data/app/assets/config/blog_boi_manifest.js +2 -0
  6. data/app/assets/images/blog_boi/logo.png +0 -0
  7. data/app/assets/images/blog_boi/star.svg +9 -0
  8. data/app/assets/javascripts/blog_boi/application.js +18 -0
  9. data/app/assets/javascripts/blog_boi/global.js +13 -0
  10. data/app/assets/stylesheets/blog_boi/_blog.scss +11 -0
  11. data/app/assets/stylesheets/blog_boi/_footer.scss +7 -0
  12. data/app/assets/stylesheets/blog_boi/_header.scss +13 -0
  13. data/app/assets/stylesheets/blog_boi/application.scss +48 -0
  14. data/app/assets/stylesheets/blog_boi/bootstrap_overrides.scss +7 -0
  15. data/app/assets/stylesheets/bootstrap_customizer.scss +2 -0
  16. data/app/controllers/blog_boi/application_controller.rb +26 -0
  17. data/app/controllers/blog_boi/articles_controller.rb +77 -0
  18. data/app/controllers/blog_boi/comments_controller.rb +21 -0
  19. data/app/helpers/blog_boi/application_helper.rb +31 -0
  20. data/app/helpers/blog_boi/articles_helper.rb +4 -0
  21. data/app/helpers/blog_boi/comments_helper.rb +4 -0
  22. data/app/jobs/blog_boi/application_job.rb +4 -0
  23. data/app/mailers/blog_boi/application_mailer.rb +6 -0
  24. data/app/models/blog_boi/application_record.rb +5 -0
  25. data/app/models/blog_boi/article.rb +70 -0
  26. data/app/models/blog_boi/category.rb +12 -0
  27. data/app/models/blog_boi/comment.rb +11 -0
  28. data/app/views/blog_boi/articles/_article.html.erb +41 -0
  29. data/app/views/blog_boi/articles/_article_body.html.erb +5 -0
  30. data/app/views/blog_boi/articles/_category.html.erb +7 -0
  31. data/app/views/blog_boi/articles/_form.html.erb +55 -0
  32. data/app/views/blog_boi/articles/_index_header.html.erb +31 -0
  33. data/app/views/blog_boi/articles/edit.html.erb +24 -0
  34. data/app/views/blog_boi/articles/index.html.erb +25 -0
  35. data/app/views/blog_boi/articles/new.html.erb +23 -0
  36. data/app/views/blog_boi/articles/show.html.erb +63 -0
  37. data/app/views/blog_boi/comments/_comment.erb +1 -0
  38. data/app/views/blog_boi/comments/_form.erb +8 -0
  39. data/app/views/layouts/blog_boi/_footer.html.erb +21 -0
  40. data/app/views/layouts/blog_boi/_head_meta_tags.html.erb +7 -0
  41. data/app/views/layouts/blog_boi/_header.html.erb +43 -0
  42. data/app/views/layouts/blog_boi/application.html.erb +22 -0
  43. data/config/routes.rb +9 -0
  44. data/db/migrate/20190718130349_create_blog_boi_articles.rb +10 -0
  45. data/db/migrate/20190721015943_create_blog_boi_comments.rb +10 -0
  46. data/db/migrate/20190721212916_add_author_id_to_blog_boi_articles.rb +5 -0
  47. data/db/migrate/20190728014133_create_blog_boi_categories.rb +9 -0
  48. data/db/migrate/20190729022622_create_join_table_article_category.rb +8 -0
  49. data/db/migrate/20190820235901_add_description_to_blog_boi_articles.rb +5 -0
  50. data/db/migrate/20190911003115_add_slug_to_article.rb +6 -0
  51. data/db/seeds.rb +23 -0
  52. data/lib/blog_boi/engine.rb +14 -0
  53. data/lib/blog_boi/version.rb +3 -0
  54. data/lib/blog_boi.rb +29 -0
  55. data/lib/tasks/blog_boi_tasks.rake +4 -0
  56. metadata +190 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '09ac692bef7327594881b23ee820dd5e3ae06550a6473637451e0b4f6590e9bd'
4
+ data.tar.gz: 464037cdd2cf13aa869203177137aba5d6df3a81036f409cbfe2b7eece2eb16f
5
+ SHA512:
6
+ metadata.gz: 457d65d6077b50b949c843205ad8e8c7392aa3471154e72dc7e5fcfa6323df272972cff11da62cffa4bf88fead7d65db88401674149a42fa00edabf7b6e139ef
7
+ data.tar.gz: 719979962e8672cb6706d009e974d75375334fac2f52f61fdaaf94a6f93987f52ddbbc3b02088dacbd9d5ac472e0c39f73a56594d7c058ea41a9efa536a56eb6
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2019
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,96 @@
1
+ # BlogBoi
2
+
3
+ BlogBoi is a rails gem / engine that provides out-of-the-box blogging functionality to your rails application
4
+
5
+ ## A) Installation
6
+
7
+ 1. Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'blog_boi',
11
+ git: "git@github.com:charliereese/blog_boi.git",
12
+ tag: "v0.0.1"
13
+ ```
14
+
15
+ _Note: path may move to github and not be relative_
16
+
17
+ 2. Add these lines to your config/application.rb file:
18
+
19
+ ```ruby
20
+ require "active_storage/engine" # If doesn't exist already
21
+
22
+ ActiveStorage::Engine.config
23
+ .active_storage
24
+ .content_types_to_serve_as_binary
25
+ .delete('image/svg+xml')
26
+ ```
27
+
28
+ 3. And then execute:
29
+
30
+ ```bash
31
+ $ bundle
32
+ $ rake active_storage:install `# If hasn't been run yet`
33
+ $ rails blog_boi:install:migrations `# not needed for dummy test application`
34
+ $ rails db:migrate
35
+ ```
36
+
37
+ Your storage.yml file should now have this code:
38
+
39
+ ```
40
+ test:
41
+ service: Disk
42
+ root: <%= Rails.root.join("tmp/storage") %>
43
+
44
+ local:
45
+ service: Disk
46
+ root: <%= Rails.root.join("storage") %>
47
+ ```
48
+
49
+ _Note: $ rails railties:install:migrations to copy all new migrations_
50
+ _Note: to run migrations only for certain engine, rails db:migrate SCOPE=blog_boi. To revert all migrations from blog_boi engine you can run rails db:migrate SCOPE=blog_boi VERSION=0_
51
+
52
+ 4. And then mount the engine in your app's config/routes.rb file:
53
+
54
+ ```ruby
55
+ mount BlogBoi::Engine, at: "/blog"
56
+ ```
57
+
58
+ 5. Customize scss colour scheme by creating `stylesheets/bootstrap_customizer.scss`. See example in `test/dummy/app/assets/stylesheets`.
59
+
60
+ 6. And then add the engines configuration options in an initializer file (named blog_boi.rb):
61
+
62
+ ```ruby
63
+ BlogBoi.author_class = "User" # name of active record model containing authors with property called name (for author's name)
64
+ ```
65
+
66
+ See lib/blog_boi.rb for all customization options.
67
+
68
+ ## B) Running tests
69
+
70
+ - Run all tests other than system: `rake app:test`
71
+ - Run system tests: `rake app:test:system`
72
+ - Run all tests: `rake` or `rake test`
73
+ - In normal rails apps: running single test is possible, but not sure how (or if functionality included out of box for engine). Can also run a particular test method from the test case by providing the -n or --name flag. Can also run an entire directory of tests by providing the path to the directory. I don't think any of this works out of the box for engines. How can I get this to work?
74
+
75
+ **B.1 Test coverage**
76
+
77
+ Exists in `coverage` folder in root directory.
78
+
79
+ To view in a browser, execute from root directory: `open coverage/index.html`
80
+
81
+ ## C) Custom style / SCSS
82
+
83
+ To override SCSS bootstrap mixins / styles, add a file in parent application called blog_boi/bootstrap_overrides.scss.
84
+ - See test/dummy app for example
85
+ - https://getbootstrap.com/docs/4.0/getting-started/theming/ for theming examples (any colours you add, like c1, can be used like btn-c1. Very cool)
86
+
87
+ Override the header or footer by placing your own files at `views/layouts/_header` or `views/layouts/_footer`
88
+
89
+ ## D) Other useful commands
90
+
91
+ - `rake db:drop db:create db:migrate db:seed` or `db:migrate:reset db:seed` will remove and recreate and reseed db. Avoid using if possible.
92
+ - `rake db:migrate:reset db:seed`
93
+
94
+ ## E) License
95
+
96
+ The gem is currently private property. It will eventually be made available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'BlogBoi'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+ load 'rails/tasks/statistics.rake'
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'test'
28
+ t.pattern = 'test/**/*_test.rb'
29
+ t.verbose = false
30
+ end
31
+
32
+ task default: :test
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts/blog_boi .js
2
+ //= link_directory ../stylesheets/blog_boi .css
Binary file
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg width="30px" height="30px" viewBox="0 0 30 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3
+ <!-- Generator: Sketch 57.1 (83088) - https://sketch.com -->
4
+ <title>Star</title>
5
+ <desc>Created with Sketch.</desc>
6
+ <g id="Star" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
7
+ <polygon fill="#F5A623" points="15 22.5 6.18322122 27.1352549 7.86707613 17.3176275 0.734152256 10.3647451 10.5916106 8.93237254 15 0 19.4083894 8.93237254 29.2658477 10.3647451 22.1329239 17.3176275 23.8167788 27.1352549"></polygon>
8
+ </g>
9
+ </svg>
@@ -0,0 +1,18 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require jquery3
14
+ //= require popper
15
+ //= require bootstrap-sprockets
16
+ //= require rails-ujs
17
+ //= require activestorage
18
+ //= require_tree .
@@ -0,0 +1,13 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
3
+
4
+ $(function() {
5
+ adjustMainPadding();
6
+ });
7
+
8
+ function adjustMainPadding() {
9
+ var footerHeight = +$('footer.blog-boi-blog').css('height').slice(0,-2);
10
+ footerHeight += 10;
11
+
12
+ $('main.blog-boi').css('padding-bottom', footerHeight + 'px');
13
+ }
@@ -0,0 +1,11 @@
1
+ main.blog-boi.blog.index {
2
+
3
+ a.article {
4
+ text-decoration: none;
5
+ }
6
+
7
+ .star {
8
+ margin-bottom: -29px;
9
+ }
10
+
11
+ }
@@ -0,0 +1,7 @@
1
+ footer.blog-boi-blog {
2
+
3
+ position: fixed;
4
+ bottom: 0;
5
+ width: 100%;
6
+
7
+ }
@@ -0,0 +1,13 @@
1
+ header.blog-boi-blog {
2
+
3
+ img.logo {
4
+ height: 35px;
5
+ }
6
+
7
+ nav {
8
+ flex-wrap: nowrap;
9
+ overflow-x: auto;
10
+ white-space: nowrap;
11
+ }
12
+
13
+ }
@@ -0,0 +1,48 @@
1
+ @import 'blog_boi/_blog';
2
+ @import 'blog_boi/_header';
3
+ @import 'blog_boi/_footer';
4
+
5
+ [class^="btn"] {
6
+ padding: 8px;
7
+ display: inline-block;
8
+ }
9
+
10
+ h1, h2, h3, h4, h5, h6 {
11
+ margin-bottom: 0;
12
+ }
13
+
14
+ .lead {
15
+ font-size: 18px;
16
+ }
17
+
18
+ .mb-neg-md-125-sm-50 {
19
+ margin-bottom: -125px;
20
+ }
21
+
22
+ .mt-md-125-sm-50 {
23
+ margin-top: 125px;
24
+ }
25
+
26
+ .font-16px {
27
+ font-size: 16px !important;
28
+ }
29
+
30
+ @media (max-width: 768px) {
31
+
32
+ .border-top-sm-only {
33
+ border-top: 1px solid white;
34
+ }
35
+
36
+ .lead {
37
+ font-size: 16px;
38
+ }
39
+
40
+ .mb-neg-md-125-sm-50 {
41
+ margin-bottom: -50px;
42
+ }
43
+
44
+ .mt-md-125-sm-50 {
45
+ margin-top: 50px;
46
+ }
47
+
48
+ }
@@ -0,0 +1,7 @@
1
+ // To override, create boostrap_customizer.scss file in parent application
2
+ // and @import your overrides and then bootstrap
3
+
4
+ // To see available overrides, check out _variables.scss
5
+ // in Bootstrap source code
6
+
7
+ $font-family-sans-serif: 'Roboto';
@@ -0,0 +1,2 @@
1
+ @import 'blog_boi/bootstrap_overrides';
2
+ @import 'bootstrap';
@@ -0,0 +1,26 @@
1
+ module BlogBoi
2
+ class ApplicationController < ::ApplicationController
3
+ protect_from_forgery with: :exception
4
+ before_action :create_meta_tags_obj
5
+
6
+ # TBU
7
+ def is_admin
8
+ if true
9
+ true
10
+ else
11
+ redirect_to root_path
12
+ end
13
+ end
14
+
15
+ protected
16
+
17
+ def set_categories
18
+ @categories = Category.all
19
+ end
20
+
21
+ def create_meta_tags_obj
22
+ @meta_tags = {}
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,77 @@
1
+ require_dependency "blog_boi/application_controller"
2
+
3
+ module BlogBoi
4
+ class ArticlesController < ApplicationController
5
+ before_action :set_article, only: [:edit, :update, :destroy]
6
+ before_action :set_article_by_slug, only: [:show]
7
+ before_action :is_admin, only: [:new, :create, :edit, :update, :destroy]
8
+ before_action :set_categories
9
+
10
+ # GET /articles
11
+ def index
12
+ @articles = Article.all
13
+ end
14
+
15
+ # GET /articles/1
16
+ def show
17
+ @meta_tags[:title] = @article[:title]
18
+ end
19
+
20
+ # GET /articles/new
21
+ def new
22
+ @article = Article.new
23
+ end
24
+
25
+ # GET /articles/1/edit
26
+ def edit
27
+ end
28
+
29
+ # POST /articles
30
+ def create
31
+ @article = Article.new(article_params)
32
+
33
+ if @article.save
34
+ redirect_to article_path(@article.slug), notice: 'Article was successfully created.'
35
+ else
36
+ render :new
37
+ end
38
+ end
39
+
40
+ # PATCH/PUT /articles/1
41
+ def update
42
+ if @article.update(article_params)
43
+ redirect_to article_path(@article.slug), notice: 'Article was successfully updated.'
44
+ else
45
+ render :edit
46
+ end
47
+ end
48
+
49
+ # DELETE /articles/1
50
+ def destroy
51
+ @article.destroy
52
+ redirect_to articles_url, notice: 'Article was successfully destroyed.'
53
+ end
54
+
55
+ # GET /articles/category/cool_category
56
+ def category_index
57
+ @articles = Article.with_category_name(params[:category_name])
58
+ @category_title = params[:category_name].titleize
59
+ render :index
60
+ end
61
+
62
+ private
63
+ def set_article_by_slug
64
+ @article = Article.find_by(slug: params[:id])
65
+ end
66
+
67
+ # Use callbacks to share common setup or constraints between actions.
68
+ def set_article
69
+ @article = Article.find(params[:id])
70
+ end
71
+
72
+ # Only allow a trusted parameter "white list" through.
73
+ def article_params
74
+ params.require(:article).permit(:title, :text, :author_name, :category_names, :slug, :description, :image)
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,21 @@
1
+ require_dependency "blog_boi/application_controller"
2
+
3
+ module BlogBoi
4
+ class CommentsController < ApplicationController
5
+ before_action :set_categories, :is_admin
6
+
7
+ def create
8
+ @article = Article.find(params[:article_id])
9
+ @comment = @article.comments.create(comment_params)
10
+ flash[:notice] = "Comment has been created!"
11
+ redirect_to articles_path
12
+ end
13
+
14
+ private
15
+
16
+ def comment_params
17
+ params.require(:comment).permit(:text)
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,31 @@
1
+ require 'byebug'
2
+
3
+ module BlogBoi
4
+
5
+ module ApplicationHelper
6
+
7
+ # Iterates over BlogBoi module class variables
8
+ # and creates helper methods for them
9
+
10
+ BlogBoi.class_variables.each do |class_var|
11
+
12
+ mattr_name = class_var.to_s[2..-1]
13
+
14
+ define_method(mattr_name) do
15
+ BlogBoi.send(mattr_name)
16
+ end
17
+
18
+ end
19
+
20
+ # TBU
21
+ def is_admin
22
+ true
23
+ end
24
+
25
+ def controller_action
26
+ action_name
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,4 @@
1
+ module BlogBoi
2
+ module ArticlesHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module BlogBoi
2
+ module CommentsHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module BlogBoi
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module BlogBoi
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module BlogBoi
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,70 @@
1
+ require 'kramdown'
2
+
3
+ module BlogBoi
4
+ class Article < ApplicationRecord
5
+ attr_writer :category_names, :author_name
6
+
7
+ #########################
8
+ # Associations
9
+ #########################
10
+
11
+ has_many :comments
12
+ has_and_belongs_to_many :categories, foreign_key: "blog_boi_category_id", association_foreign_key: "blog_boi_article_id"
13
+ belongs_to :author, class_name: BlogBoi.author_class.to_s
14
+ has_one_attached :image
15
+
16
+ #########################
17
+ # Before hooks
18
+ #########################
19
+
20
+ before_validation :set_author, :set_categories
21
+
22
+ #########################
23
+ # Scopes
24
+ #########################
25
+
26
+ scope :with_category_name, lambda { |category_name|
27
+ joins(:categories).merge(Category.where({name: category_name}))
28
+ }
29
+
30
+ #########################
31
+ # Methods
32
+ #########################
33
+
34
+ def read_time
35
+ length = (self.text.split.length / 200.0).ceil.to_s
36
+
37
+ if length != '1'
38
+ length + " minutes"
39
+ else
40
+ length + " minute"
41
+ end
42
+ end
43
+
44
+ def author_name
45
+ @author_name || author && author.name || nil
46
+ end
47
+
48
+ def category_names
49
+ @category_names || categories.pluck(:name).join(', ')
50
+ end
51
+
52
+ def markdown_text
53
+ Kramdown::Document.new(text).to_html
54
+ end
55
+
56
+ private
57
+
58
+ def set_author
59
+ self.author = BlogBoi.author_class.find_or_create_by(name: author_name)
60
+ end
61
+
62
+ def set_categories
63
+ self.categories = []
64
+
65
+ category_names.split(', ').each do |category_name|
66
+ self.categories << Category.find_or_create_by(name: category_name)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,12 @@
1
+ module BlogBoi
2
+ class Category < ApplicationRecord
3
+ #########################
4
+ # Associations
5
+ #########################
6
+ has_and_belongs_to_many :articles, foreign_key: "blog_boi_article_id", association_foreign_key: "blog_boi_category_id"
7
+
8
+ #########################
9
+ # Methods
10
+ #########################
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ module BlogBoi
2
+ class Comment < ApplicationRecord
3
+ #########################
4
+ # Associations
5
+ #########################
6
+
7
+ #########################
8
+ # Methods
9
+ #########################
10
+ end
11
+ end
@@ -0,0 +1,41 @@
1
+ <div class="col-12 col-md-9 col-lg-6 d-block mx-auto mt-4">
2
+
3
+ <%= render partial: 'category', collection: article.categories %>
4
+
5
+ <a class="d-block article mt-2"
6
+ href="/blog/<%= article.slug %>">
7
+
8
+ <h2 class="text-blue mb-0">
9
+ <%= article.title %>
10
+ </h2>
11
+
12
+ </a>
13
+
14
+ <div class="mt-2">
15
+
16
+ <ul class="list-inline mb-0">
17
+
18
+ <li class="list-inline-item">
19
+ <%= article.author.name %>
20
+ </li>
21
+
22
+ <li class="list-inline-item">
23
+ <%= article.updated_at.strftime("%B %e, %Y") %>
24
+ </li>
25
+
26
+ </ul>
27
+
28
+ </div>
29
+
30
+
31
+ <% if is_admin %>
32
+
33
+ <div class="mt-2">
34
+ <%= link_to 'Show', article %> |
35
+ <%= link_to 'Edit', edit_article_path(article) %> |
36
+ <%= link_to 'Destroy', article, method: :delete, data: { confirm: 'Are you sure?' } %>
37
+ </div>
38
+
39
+ <% end %>
40
+
41
+ </div>
@@ -0,0 +1,5 @@
1
+ <div class="col-12 mt-5 text-left">
2
+
3
+ <%= raw @article.markdown_text %>
4
+
5
+ </div>
@@ -0,0 +1,7 @@
1
+ <% badge_class ||= nil %>
2
+
3
+ <a class="badge border border-primary mx-1 text-primary <%= badge_class %>" href="/blog/category/<%= category.name.downcase %>">
4
+
5
+ <%= category.name.capitalize %>
6
+
7
+ </a>