tagging bs

+2
Gemfile
··· 50 50 51 51 gem "pry" 52 52 53 + gem 'friendly_id', '~> 5.5.0' 54 + 53 55 group :development, :test do 54 56 # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem 55 57 gem "debug", platforms: %i[ mri mswin mswin64 mingw x64_mingw ]
+3
Gemfile.lock
··· 112 112 warden (~> 1.2.3) 113 113 drb (2.2.1) 114 114 erubi (1.12.0) 115 + friendly_id (5.5.1) 116 + activerecord (>= 4.0.0) 115 117 globalid (1.2.1) 116 118 activesupport (>= 6.1) 117 119 i18n (1.14.4) ··· 272 274 capybara 273 275 debug 274 276 devise 277 + friendly_id (~> 5.5.0) 275 278 importmap-rails 276 279 jbuilder 277 280 paperclip (~> 5.1.0)
+2 -1
app/controllers/articles_controller.rb
··· 3 3 4 4 def index 5 5 @article = Article.all 6 + @tags = Tag.all 6 7 @profiles = Profile.all 7 8 respond_to do |format| 8 9 format.html ··· 57 58 58 59 private 59 60 def article_params 60 - params.require(:article).permit(:title, :body, :status, :icon, :all_tags) 61 + params.require(:article).permit(:title, :body, :status, :icon, :tag_id) 61 62 end 62 63 end
+62 -11
app/controllers/tags_controller.rb
··· 1 1 class TagsController < ApplicationController 2 + # before_action :set_tag, only: %i[ show edit update destroy ] 3 + before_action :authenticate_user!, :except => [:show] 4 + 5 + # GET /tags or /tags.json 2 6 def index 3 - @tags = Tag.all 7 + @tag = Tag.all 8 + end 9 + 10 + # GET /tags/1 or /tags/1.json 11 + def show 12 + @tag = Tag.friendly.find(params[:id]) 13 + # @article = Article.friendly.find_by(params[:id]) 14 + end 15 + 16 + # GET /tags/new 17 + def new 18 + @tag = Tag.new 19 + end 20 + 21 + # GET /tags/1/edit 22 + def edit 23 + @tag = Tag.friendly.find(params[:id]) 4 24 end 5 25 26 + # POST /tags or /tags.json 6 27 def create 7 - @tags = Tag.new(tag_params) 28 + @tag = Tag.new(tag_params) 8 29 9 - if @tags.save 10 - redirect_to @tags 11 - else 12 - render :new, status: :unprocessable_entity 30 + respond_to do |format| 31 + if @tag.save 32 + format.html { redirect_to tag_url(@tag), notice: "Tag was successfully created." } 33 + format.json { render :show, status: :created, location: @tag } 34 + else 35 + format.html { render :new, status: :unprocessable_entity } 36 + format.json { render json: @tag.errors, status: :unprocessable_entity } 37 + end 38 + end 39 + end 40 + 41 + # PATCH/PUT /tags/1 or /tags/1.json 42 + def update 43 + respond_to do |format| 44 + if @tag.update(tag_params) 45 + format.html { redirect_to tag_url(@tag), notice: "Tag was successfully updated." } 46 + format.json { render :show, status: :ok, location: @tag } 47 + else 48 + format.html { render :edit, status: :unprocessable_entity } 49 + format.json { render json: @tag.errors, status: :unprocessable_entity } 50 + end 13 51 end 14 52 end 15 53 16 - def show 17 - @tags = Tag.find(params[:id]) 54 + # DELETE /tags/1 or /tags/1.json 55 + def destroy 56 + @tag = Tag.friendly.find(params[:id]) 57 + if @tag.present? 58 + @tag.destroy! 59 + end 60 + respond_to do |format| 61 + format.html { redirect_to tags_url, notice: "Tag was successfully destroyed." } 62 + format.json { head :no_content } 63 + end 18 64 end 19 65 20 66 private 21 - def tag_params 22 - params.require(:tags).permit(:name) 23 - end 67 + # Use callbacks to share common setup or constraints between actions. 68 + # def set_tag 69 + # @tag = Tag.friendly.find(params[:id]) 70 + # end 24 71 72 + # Only allow a list of trusted parameters through. 73 + def tag_params 74 + params.require(:tag).permit(:id, :name, :slug) 75 + end 25 76 end
+1 -1
app/models/article.rb
··· 7 7 has_one_attached :icon, dependent: :detach 8 8 9 9 has_many :taggings 10 - has_many :tags, through: :taggings 10 + belongs_to :tag, dependent: :destroy 11 11 12 12 validates :title, presence: true 13 13 validates :body, presence: true, length: { minimum: 10 }
+3 -3
app/models/tag.rb
··· 1 1 class Tag < ApplicationRecord 2 - 3 - has_many :taggings 4 - has_many :articles, through: :taggings 2 + extend FriendlyId 3 + friendly_id :name, use: :slugged 5 4 5 + has_many :articles 6 6 end
+2 -2
app/views/articles/_form.html.erb
··· 26 26 </div> 27 27 28 28 <div> 29 - <%= form.label :all_tags %><br> 30 - <%= form.text_field :all_tags %> 29 + <%= form.label :tags %><br> 30 + <%= form.collection_select :slug, Tag.all, :id, :name %> 31 31 </div> 32 32 33 33 <div>
-2
app/views/articles/show.html.erb
··· 8 8 9 9 <p class="article_date"><%= @article.created_at.strftime('%Y %b %d') %></p> 10 10 11 - <p class="article_date"><%= raw tag_links(@article.all_tags) %></p> 12 - 13 11 <div class="pbody"><%== @article.body %></div> 14 12 15 13 <% if user_signed_in? %>
+15
app/views/tags/_form.html.erb
··· 1 + <%= form_with(model: tag) do |form| %> 2 + <div> 3 + <%= form.label :name %> 4 + <%= form.text_field :name %> 5 + </div> 6 + 7 + <div> 8 + <%= form.label :slug %> 9 + <%= form.text_field :slug %> 10 + </div> 11 + 12 + <div> 13 + <%= form.submit %> 14 + </div> 15 + <% end %>
+1
app/views/tags/_tag.html.erb
··· 1 + <%= link_to tag.name, tag %>
+2
app/views/tags/_tag.json.jbuilder
··· 1 + json.extract! tag, :id, :name, :slug, :created_at, :updated_at 2 + json.url tag_url(tag, format: :json)
+10
app/views/tags/edit.html.erb
··· 1 + <h1>Editing tag</h1> 2 + 3 + <%= render "form", tag: @tag %> 4 + 5 + <br> 6 + 7 + <div> 8 + <%= link_to "Show this tag", @tag %> | 9 + <%= link_to "Back to tags", tags_path %> 10 + </div>
+6 -4
app/views/tags/index.html.erb
··· 1 1 <div class="main"> 2 2 3 + <h1>🐬 tags</h1> 4 + 3 5 <ul> 4 - <% Tag.all.each do |tag| %> 5 - <li> 6 - <%= link_to tag.name, tag_path(tag.name) %> 7 - </li> 6 + <% @tag.each do |tag| %> 7 + <li> 8 + <%= render tag %> 9 + </li> 8 10 <% end %> 9 11 </ul> 10 12
+1
app/views/tags/index.json.jbuilder
··· 1 + json.array! @tags, partial: "tags/tag", as: :tag
+9
app/views/tags/new.html.erb
··· 1 + <h1>New tag</h1> 2 + 3 + <%= render "form", tag: @tag %> 4 + 5 + <br> 6 + 7 + <div> 8 + <%= link_to "Back to tags", tags_path %> 9 + </div>
+10 -7
app/views/tags/show.html.erb
··· 1 1 <div class="main"> 2 2 3 - <ul> 4 - <% Tag.articles.each do |articles| %> 5 - <li> 6 - <%= link_to article.title, article %> 7 - </li> 8 - <% end %> 9 - </ul> 3 + <h1><%= render @tag %></h1> 4 + 5 + <p> 6 + <%= link_to "edit", edit_tag_path(@tag) %> | 7 + <% if user_signed_in? %> 8 + <%= link_to "delete", @tag, method: :delete %> 9 + <% else %> 10 + <%= link_to "back", tags_path %> 11 + <% end %> 12 + </p> 10 13 11 14 </div>
+1
app/views/tags/show.json.jbuilder
··· 1 + json.partial! "tags/tag", tag: @tag
+107
config/initializers/friendly_id.rb
··· 1 + # FriendlyId Global Configuration 2 + # 3 + # Use this to set up shared configuration options for your entire application. 4 + # Any of the configuration options shown here can also be applied to single 5 + # models by passing arguments to the `friendly_id` class method or defining 6 + # methods in your model. 7 + # 8 + # To learn more, check out the guide: 9 + # 10 + # http://norman.github.io/friendly_id/file.Guide.html 11 + 12 + FriendlyId.defaults do |config| 13 + # ## Reserved Words 14 + # 15 + # Some words could conflict with Rails's routes when used as slugs, or are 16 + # undesirable to allow as slugs. Edit this list as needed for your app. 17 + config.use :reserved 18 + 19 + config.reserved_words = %w[new edit index session login logout users admin 20 + stylesheets assets javascripts images] 21 + 22 + # This adds an option to treat reserved words as conflicts rather than exceptions. 23 + # When there is no good candidate, a UUID will be appended, matching the existing 24 + # conflict behavior. 25 + 26 + # config.treat_reserved_as_conflict = true 27 + 28 + # ## Friendly Finders 29 + # 30 + # Uncomment this to use friendly finders in all models. By default, if 31 + # you wish to find a record by its friendly id, you must do: 32 + # 33 + # MyModel.friendly.find('foo') 34 + # 35 + # If you uncomment this, you can do: 36 + # 37 + # MyModel.find('foo') 38 + # 39 + # This is significantly more convenient but may not be appropriate for 40 + # all applications, so you must explicitly opt-in to this behavior. You can 41 + # always also configure it on a per-model basis if you prefer. 42 + # 43 + # Something else to consider is that using the :finders addon boosts 44 + # performance because it will avoid Rails-internal code that makes runtime 45 + # calls to `Module.extend`. 46 + # 47 + # config.use :finders 48 + # 49 + # ## Slugs 50 + # 51 + # Most applications will use the :slugged module everywhere. If you wish 52 + # to do so, uncomment the following line. 53 + # 54 + # config.use :slugged 55 + # 56 + # By default, FriendlyId's :slugged addon expects the slug column to be named 57 + # 'slug', but you can change it if you wish. 58 + # 59 + # config.slug_column = 'slug' 60 + # 61 + # By default, slug has no size limit, but you can change it if you wish. 62 + # 63 + # config.slug_limit = 255 64 + # 65 + # When FriendlyId can not generate a unique ID from your base method, it appends 66 + # a UUID, separated by a single dash. You can configure the character used as the 67 + # separator. If you're upgrading from FriendlyId 4, you may wish to replace this 68 + # with two dashes. 69 + # 70 + # config.sequence_separator = '-' 71 + # 72 + # Note that you must use the :slugged addon **prior** to the line which 73 + # configures the sequence separator, or else FriendlyId will raise an undefined 74 + # method error. 75 + # 76 + # ## Tips and Tricks 77 + # 78 + # ### Controlling when slugs are generated 79 + # 80 + # As of FriendlyId 5.0, new slugs are generated only when the slug field is 81 + # nil, but if you're using a column as your base method can change this 82 + # behavior by overriding the `should_generate_new_friendly_id?` method that 83 + # FriendlyId adds to your model. The change below makes FriendlyId 5.0 behave 84 + # more like 4.0. 85 + # Note: Use(include) Slugged module in the config if using the anonymous module. 86 + # If you have `friendly_id :name, use: slugged` in the model, Slugged module 87 + # is included after the anonymous module defined in the initializer, so it 88 + # overrides the `should_generate_new_friendly_id?` method from the anonymous module. 89 + # 90 + # config.use :slugged 91 + # config.use Module.new { 92 + # def should_generate_new_friendly_id? 93 + # slug.blank? || <your_column_name_here>_changed? 94 + # end 95 + # } 96 + # 97 + # FriendlyId uses Rails's `parameterize` method to generate slugs, but for 98 + # languages that don't use the Roman alphabet, that's not usually sufficient. 99 + # Here we use the Babosa library to transliterate Russian Cyrillic slugs to 100 + # ASCII. If you use this, don't forget to add "babosa" to your Gemfile. 101 + # 102 + # config.use Module.new { 103 + # def normalize_friendly_id(text) 104 + # text.to_slug.normalize! :transliterations => [:russian, :latin] 105 + # end 106 + # } 107 + end
+6 -9
config/routes.rb
··· 1 1 Rails.application.routes.draw do 2 - get 'tags/index' 3 - get 'tags/create' 4 - resources :galleries 5 2 # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html 6 3 7 4 # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500. ··· 13 10 end 14 11 15 12 Rails.application.routes.draw do 16 - get 'tags/index' 17 - get 'tags/create' 18 13 get "/tags", to: "tags#index" 14 + # get "tags/:edit" => "tags#edit", as: :edit_tag_path 19 15 get "tags/:id" => "tags#show", as: :tag 16 + get "tags/:new" => "tags#new" 17 + # resources :tag, path: '', only: [:show] 18 + resources :tags 19 + end 20 20 21 - resources :galleries 21 + Rails.application.routes.draw do 22 22 get "/articles", to: "articles#index" 23 - # get 'tags/:tag', to: 'articles#index', as: "tag" 24 23 # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html 25 24 end 26 25 27 26 Rails.application.routes.draw do 28 - resources :galleries 29 27 get "/users", to: "users#index", via: "get" 30 28 end 31 29 32 30 Rails.application.routes.draw do 33 - resources :galleries 34 31 root "articles#index" 35 32 36 33 resources :profiles
+38 -3
test/controllers/tags_controller_test.rb
··· 1 1 require "test_helper" 2 2 3 3 class TagsControllerTest < ActionDispatch::IntegrationTest 4 + setup do 5 + @tag = tags(:one) 6 + end 7 + 4 8 test "should get index" do 5 - get tags_index_url 9 + get tags_url 10 + assert_response :success 11 + end 12 + 13 + test "should get new" do 14 + get new_tag_url 15 + assert_response :success 16 + end 17 + 18 + test "should create tag" do 19 + assert_difference("Tag.count") do 20 + post tags_url, params: { tag: { name: @tag.name, slug: @tag.slug } } 21 + end 22 + 23 + assert_redirected_to tag_url(Tag.last) 24 + end 25 + 26 + test "should show tag" do 27 + get tag_url(@tag) 6 28 assert_response :success 7 29 end 8 30 9 - test "should get create" do 10 - get tags_create_url 31 + test "should get edit" do 32 + get edit_tag_url(@tag) 11 33 assert_response :success 34 + end 35 + 36 + test "should update tag" do 37 + patch tag_url(@tag), params: { tag: { name: @tag.name, slug: @tag.slug } } 38 + assert_redirected_to tag_url(@tag) 39 + end 40 + 41 + test "should destroy tag" do 42 + assert_difference("Tag.count", -1) do 43 + delete tag_url(@tag) 44 + end 45 + 46 + assert_redirected_to tags_url 12 47 end 13 48 end
+2
test/fixtures/tags.yml
··· 2 2 3 3 one: 4 4 name: MyString 5 + slug: MyString 5 6 6 7 two: 7 8 name: MyString 9 + slug: MyString
+43
test/system/tags_test.rb
··· 1 + require "application_system_test_case" 2 + 3 + class TagsTest < ApplicationSystemTestCase 4 + setup do 5 + @tag = tags(:one) 6 + end 7 + 8 + test "visiting the index" do 9 + visit tags_url 10 + assert_selector "h1", text: "Tags" 11 + end 12 + 13 + test "should create tag" do 14 + visit tags_url 15 + click_on "New tag" 16 + 17 + fill_in "Name", with: @tag.name 18 + fill_in "Slug", with: @tag.slug 19 + click_on "Create Tag" 20 + 21 + assert_text "Tag was successfully created" 22 + click_on "Back" 23 + end 24 + 25 + test "should update Tag" do 26 + visit tag_url(@tag) 27 + click_on "Edit this tag", match: :first 28 + 29 + fill_in "Name", with: @tag.name 30 + fill_in "Slug", with: @tag.slug 31 + click_on "Update Tag" 32 + 33 + assert_text "Tag was successfully updated" 34 + click_on "Back" 35 + end 36 + 37 + test "should destroy Tag" do 38 + visit tag_url(@tag) 39 + click_on "Destroy this tag", match: :first 40 + 41 + assert_text "Tag was successfully destroyed" 42 + end 43 + end