friendly_slug 0.1.5 → 0.1.6

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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +63 -6
  3. data/friendly_slug.gemspec +1 -1
  4. data/friendly_slug_gem_test/app/assets/javascripts/topics.coffee +3 -0
  5. data/friendly_slug_gem_test/app/assets/stylesheets/topics.scss +3 -0
  6. data/friendly_slug_gem_test/app/controllers/topics_controller.rb +60 -0
  7. data/friendly_slug_gem_test/app/helpers/topics_helper.rb +2 -0
  8. data/friendly_slug_gem_test/app/models/topic.rb +3 -0
  9. data/friendly_slug_gem_test/app/views/topics/_form.html.erb +37 -0
  10. data/friendly_slug_gem_test/app/views/topics/_topic.json.jbuilder +2 -0
  11. data/friendly_slug_gem_test/app/views/topics/edit.html.erb +6 -0
  12. data/friendly_slug_gem_test/app/views/topics/index.html.erb +33 -0
  13. data/friendly_slug_gem_test/app/views/topics/index.json.jbuilder +1 -0
  14. data/friendly_slug_gem_test/app/views/topics/new.html.erb +5 -0
  15. data/friendly_slug_gem_test/app/views/topics/show.html.erb +24 -0
  16. data/friendly_slug_gem_test/app/views/topics/show.json.jbuilder +1 -0
  17. data/friendly_slug_gem_test/config/routes.rb +2 -1
  18. data/friendly_slug_gem_test/db/migrate/20180630053922_create_topics.rb +13 -0
  19. data/friendly_slug_gem_test/db/schema.rb +11 -1
  20. data/friendly_slug_gem_test/db/seeds.rb +9 -7
  21. data/friendly_slug_gem_test/test/controllers/topics_controller_test.rb +48 -0
  22. data/friendly_slug_gem_test/test/fixtures/topics.yml +27 -0
  23. data/friendly_slug_gem_test/test/integration/main_slug_test.rb +21 -1
  24. data/friendly_slug_gem_test/test/models/blog_test.rb +11 -1
  25. data/friendly_slug_gem_test/test/models/topic_test.rb +18 -0
  26. data/friendly_slug_gem_test/test/system/topics_test.rb +49 -0
  27. data/friendly_slug_gem_test/test/test_helper.rb +16 -1
  28. data/lib/friendly_slug/active_record/base.rb +49 -14
  29. data/lib/friendly_slug/version.rb +1 -1
  30. metadata +22 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cdf3e33c4a0ed977b854abcedff9ae14513596457d6c60af7c57917fdd41de50
4
- data.tar.gz: af600c5eb04a71ae2529e5e18120a0bfb3c9d22e232a2328c4367799f5ee4966
3
+ metadata.gz: 1e79a38e32914ee0cfa33348dd8cc5e4fefce5faa2b7c51606c29077295d2e05
4
+ data.tar.gz: 42d7423d04a1f0ef06f4fbfbc5c5de2c9e081d436b0b41cec277f8f47fd74b62
5
5
  SHA512:
6
- metadata.gz: 762fd755935b66381459775b2aae4228ac9a8de5eb3a36a6b59197e44e0600278bc24b287d999d87d9418d5497e0697218a66b670c5fde107b2e973fa5fec6cb
7
- data.tar.gz: 5508c0f0041ee7fe4548b97c12d5a510b1dd2b2ed5eff670710871caa312629aad4583797c9fac2f7ab16c9f82b5347c61a6d2c27d7c910cc1ef496a5b814def
6
+ metadata.gz: d854a4e025260e0facf44522ada919d375649f140d04614e62e766afbad23805bb8cdbb573f3573d3a8db8e48434897ccc0e82b36ce464fc7da3fa3da19f72a0
7
+ data.tar.gz: 20e105d99b7b2e703abe3a1244178609b39fe30325b646152b67f2634837c95b81b8bd1e17c9031f95d56d4563084d4ad980a137865f080e929a1789fd8f964b
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # FriendlySlug
2
2
 
3
3
  Friendly Slug is meant to dynamically create SEO friendly URL links. It is extremely lightweight and non resource intensive. Friendly Slug ties directly into the Rails URL Helpers so you dont
4
- have to change anything. There is no need to create a Rails Migration as this gem does not add anything to your current database. You must have Active Model in your code base for this to work.
4
+ have to change anything. There is no need to create a Rails Migration as this gem does not add anything to your current database unless you want to use the database option. You must have Active Model in your code base for this to work.
5
5
 
6
6
  ## Installation
7
7
 
@@ -19,7 +19,7 @@ Or install it yourself as:
19
19
 
20
20
  $ gem install friendly_slug
21
21
 
22
- ## Usage
22
+ ## Non Database Usage
23
23
 
24
24
  In the model you want to add your slug to, simply put the following code in it:
25
25
 
@@ -28,8 +28,8 @@ In the model you want to add your slug to, simply put the following code in it:
28
28
  build_friendly_slug :title, :id, use: :last
29
29
  ```
30
30
 
31
- You must provide one unique indexed attribute that you can search by to retrieve a database row and one other attribute you want to appear in the URL. The method accepts three parameters, the last parameter defines where the unique key is in the string that will be looked up.
32
- The only symbols accepted are `:first` or `:last`. Since our unique id in the example above is in the second parameter spot, we will `:last` for second.
31
+ You must provide one unique indexed attribute that you can search by to retrieve a database row and one other attribute you want to appear in the URL. The unique attribute key that will be used should not have any spaces or `-` in it. The method accepts as many symbolic chained paramters you need, the **last** or **first** parameter defines where the unique key is in the paramater list that will be looked up.
32
+ The algorithm will not work if the unique key is not the first or the last in the last. The only symbols accepted for `use:` in non database usage accepted are `:first` or `:last`. Since our unique id in the example above is in the second parameter spot, we will use `:last`.
33
33
 
34
34
  For example, if I have a blog post with a `title` and `id`, `id` being a primary key and also an indexed table attribute, my slugged link would look like this:
35
35
 
@@ -62,10 +62,67 @@ def set_blog
62
62
  end
63
63
  ```
64
64
 
65
- You can also set a static string in place of an attribute if you wanted URL's to be similar in wording.
65
+ ## Database Usage
66
+
67
+ For the main part, the syntax of the gem remains the same, except for `use:`, you will put `use: :database` and not use `:first` or `:last`. Attributes that are chained on as symbols will construct the entire slug.
68
+
69
+ For exmaple, you can use as little as one attribute, such as the `title` of the blog post. The model's URL helper methods are dynamically updated as well.
70
+
71
+ ```ruby
72
+ # models/your_model.rb
73
+ build_friendly_slug :title, use: :database
74
+
75
+ # views/blogs/index.rb
76
+ link_to @blog.title, blog_path(@blog) # => http://localhost:3000/blogs/the-great-friendly-slug
77
+ ```
78
+
79
+ or you can chanin on attribute symbols to generate a slug with a `:title` and a `:author`:
80
+
81
+ ```ruby
82
+ # models/your_model.rb
83
+ build_friendly_slug :title, :author, use: :database
84
+
85
+ # views/blogs/index.rb
86
+ link_to @blog.title, blog_path(@blog) # => http://localhost:3000/blogs/the-great-friendly-slug-sam-holst
87
+ ```
88
+
89
+ You will need to generate a migration file that adds a slug as type string to your desired model along with it being indexed and unique.
90
+
91
+ ```ruby
92
+ # db/migrations/my_migration_file.rb
93
+ class AddSlugToBlogs < ActiveRecord::Migration[5.2]
94
+ def change
95
+ add_column :blogs, :slug, :string
96
+ add_index :blogs, :slug, unique: true
97
+ end
98
+ end
99
+ ```
100
+
101
+ After this is run, you'll want to update all of your current model to have a slug. Keep in mind that string database types can only have up to 255 characters.
102
+
103
+ ```ruby
104
+ Blog.all.map(&:save)
105
+ ```
106
+
107
+ Friendly Slug automatically checks whenever the model is saved/updated to see if the title has changed. If the title has changed, it will update the slug to the newest updated parameter chain you provided in the model.
108
+ If the slug already exists in that model's database table, it will append a randomly generated hash to the end of it.
109
+
110
+ In your controller, you will need to use:
111
+
112
+ ```ruby
113
+ # controllers/blogs_controller.rb
114
+ def set_blog
115
+ @blog = Blog.find_by_slug(params[:id]) # => id: "the-great-friendly-slug" => id: 1
116
+ end
117
+ ```
118
+
119
+ When this is set in your controller, it will find the row with that specific `slug` and return it to you. In this specific case, the post with that `slug` has an `id` of 1. You need to leave `params[:id]` as is. The `id` coming in
120
+ is the slugged title, which is then taken by the model's `find_by_slug` class method for the search. The gem's magic allows you to not have to worry about many intricate things such as this.
121
+
122
+ If you decide to change the format of how your slugs are generated through the `build_friendly_slug` after you have originally created them, you will need to run the following snippet:
66
123
 
67
124
  ```ruby
68
- build_friendly_slug :id, "My Static String", use: :first # => http://localhost:3000/blogs/:id-my-static-string
125
+ Blog.all.map{|b| b.slug = nil; b.save}
69
126
  ```
70
127
 
71
128
  ## Development
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.email = ["sawohol@gmail.com"]
10
10
 
11
11
  spec.summary = %q{Generate a model specific SEO URL friendly slug.}
12
- spec.description = %q{A simple SEO URL friendly slug model generator. Friendly Slug is meant to dynamically create SEO friendly URL links. It is extremely lightweight and non resource intensive. Friendly Slug ties directly into the Rails URL Helpers so you dont have to change anything. There is no need to create a Rails Migration as this gem does not add anything to your current database. You must have Active Model in your code base for this to work.}
12
+ spec.description = %q{A simple SEO URL friendly slug model generator. Friendly Slug is meant to dynamically create SEO friendly URL links. It is extremely lightweight and non resource intensive. Friendly Slug ties directly into the Rails URL Helpers so you dont have to change anything. There is no need to create a Rails Migration as this gem does not add anything to your current database unless you want to use the database option. You must have Active Model in your code base for this to work.}
13
13
  spec.homepage = "https://github.com/samholst/friendly_slug"
14
14
  spec.license = "MIT"
15
15
 
@@ -0,0 +1,3 @@
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
+ # You can use CoffeeScript in this file: http://coffeescript.org/
@@ -0,0 +1,3 @@
1
+ // Place all the styles related to the topics controller here.
2
+ // They will automatically be included in application.css.
3
+ // You can use Sass (SCSS) here: http://sass-lang.com/
@@ -0,0 +1,60 @@
1
+ class TopicsController < ApplicationController
2
+ before_action :set_topic, only: [:show, :edit, :update, :destroy]
3
+
4
+ def index
5
+ @topics = Topic.all
6
+ end
7
+
8
+ def show
9
+ end
10
+
11
+ def new
12
+ @topic = Topic.new
13
+ end
14
+
15
+ def edit
16
+ end
17
+
18
+ def create
19
+ @topic = Topic.new(topic_params)
20
+
21
+ respond_to do |format|
22
+ if @topic.save
23
+ format.html { redirect_to @topic, notice: 'Topic was successfully created.' }
24
+ format.json { render :show, status: :created, location: @topic }
25
+ else
26
+ format.html { render :new }
27
+ format.json { render json: @topic.errors, status: :unprocessable_entity }
28
+ end
29
+ end
30
+ end
31
+
32
+ def update
33
+ respond_to do |format|
34
+ if @topic.update(topic_params)
35
+ format.html { redirect_to @topic, notice: 'Topic was successfully updated.' }
36
+ format.json { render :show, status: :ok, location: @topic }
37
+ else
38
+ format.html { render :edit }
39
+ format.json { render json: @topic.errors, status: :unprocessable_entity }
40
+ end
41
+ end
42
+ end
43
+
44
+ def destroy
45
+ @topic.destroy
46
+ respond_to do |format|
47
+ format.html { redirect_to topics_url, notice: 'Topic was successfully destroyed.' }
48
+ format.json { head :no_content }
49
+ end
50
+ end
51
+
52
+ private
53
+ def set_topic
54
+ @topic = Topic.find_by_slug(params[:id])
55
+ end
56
+
57
+ def topic_params
58
+ params.require(:topic).permit(:title, :slug, :color, :views)
59
+ end
60
+ end
@@ -0,0 +1,2 @@
1
+ module TopicsHelper
2
+ end
@@ -0,0 +1,3 @@
1
+ class Topic < ApplicationRecord
2
+ build_friendly_slug :title, use: :database
3
+ end
@@ -0,0 +1,37 @@
1
+ <%= form_with(model: topic, local: true) do |form| %>
2
+ <% if topic.errors.any? %>
3
+ <div id="error_explanation">
4
+ <h2><%= pluralize(topic.errors.count, "error") %> prohibited this topic from being saved:</h2>
5
+
6
+ <ul>
7
+ <% topic.errors.full_messages.each do |message| %>
8
+ <li><%= message %></li>
9
+ <% end %>
10
+ </ul>
11
+ </div>
12
+ <% end %>
13
+
14
+ <div class="field">
15
+ <%= form.label :title %>
16
+ <%= form.text_field :title %>
17
+ </div>
18
+
19
+ <div class="field">
20
+ <%= form.label :slug %>
21
+ <%= form.text_field :slug %>
22
+ </div>
23
+
24
+ <div class="field">
25
+ <%= form.label :color %>
26
+ <%= form.text_field :color %>
27
+ </div>
28
+
29
+ <div class="field">
30
+ <%= form.label :views %>
31
+ <%= form.text_field :views %>
32
+ </div>
33
+
34
+ <div class="actions">
35
+ <%= form.submit %>
36
+ </div>
37
+ <% end %>
@@ -0,0 +1,2 @@
1
+ json.extract! topic, :id, :title, :slug, :type, :views, :created_at, :updated_at
2
+ json.url topic_url(topic, format: :json)
@@ -0,0 +1,6 @@
1
+ <h1>Editing Topic</h1>
2
+
3
+ <%= render 'form', topic: @topic %>
4
+
5
+ <%= link_to 'Show', @topic %> |
6
+ <%= link_to 'Back', topics_path %>
@@ -0,0 +1,33 @@
1
+ <p id="notice"><%= notice %></p>
2
+
3
+ <h1>Topics</h1>
4
+
5
+ <table>
6
+ <thead>
7
+ <tr>
8
+ <th>Title</th>
9
+ <th>Slug</th>
10
+ <th>Color</th>
11
+ <th>Views</th>
12
+ <th colspan="3"></th>
13
+ </tr>
14
+ </thead>
15
+
16
+ <tbody>
17
+ <% @topics.each do |topic| %>
18
+ <tr>
19
+ <td><%= topic.title %></td>
20
+ <td><%= topic.slug %></td>
21
+ <td><%= topic.color %></td>
22
+ <td><%= topic.views %></td>
23
+ <td><%= link_to 'Show', topic %></td>
24
+ <td><%= link_to 'Edit', edit_topic_path(topic) %></td>
25
+ <td><%= link_to 'Destroy', topic, method: :delete, data: { confirm: 'Are you sure?' } %></td>
26
+ </tr>
27
+ <% end %>
28
+ </tbody>
29
+ </table>
30
+
31
+ <br>
32
+
33
+ <%= link_to 'New Topic', new_topic_path %>
@@ -0,0 +1 @@
1
+ json.array! @topics, partial: 'topics/topic', as: :topic
@@ -0,0 +1,5 @@
1
+ <h1>New Topic</h1>
2
+
3
+ <%= render 'form', topic: @topic %>
4
+
5
+ <%= link_to 'Back', topics_path %>
@@ -0,0 +1,24 @@
1
+ <p id="notice"><%= notice %></p>
2
+
3
+ <p>
4
+ <strong>Title:</strong>
5
+ <%= @topic.title %>
6
+ </p>
7
+
8
+ <p>
9
+ <strong>Slug:</strong>
10
+ <%= @topic.slug %>
11
+ </p>
12
+
13
+ <p>
14
+ <strong>Color:</strong>
15
+ <%= @topic.color %>
16
+ </p>
17
+
18
+ <p>
19
+ <strong>Views:</strong>
20
+ <%= @topic.views %>
21
+ </p>
22
+
23
+ <%= link_to 'Edit', edit_topic_path(@topic) %> |
24
+ <%= link_to 'Back', topics_path %>
@@ -0,0 +1 @@
1
+ json.partial! "topics/topic", topic: @topic
@@ -1,4 +1,5 @@
1
1
  Rails.application.routes.draw do
2
+ resources :topics
2
3
  resources :blogs
3
- # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
4
+ root to: "blogs#index"
4
5
  end
@@ -0,0 +1,13 @@
1
+ class CreateTopics < ActiveRecord::Migration[5.2]
2
+ def change
3
+ create_table :topics do |t|
4
+ t.string :title
5
+ t.string :slug
6
+ t.string :color
7
+ t.bigint :views
8
+
9
+ t.timestamps
10
+ end
11
+ add_index :topics, :slug, unique: true
12
+ end
13
+ end
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema.define(version: 2018_06_28_233122) do
13
+ ActiveRecord::Schema.define(version: 2018_06_30_053922) do
14
14
 
15
15
  create_table "blogs", force: :cascade do |t|
16
16
  t.string "title"
@@ -20,4 +20,14 @@ ActiveRecord::Schema.define(version: 2018_06_28_233122) do
20
20
  t.datetime "updated_at", null: false
21
21
  end
22
22
 
23
+ create_table "topics", force: :cascade do |t|
24
+ t.string "title"
25
+ t.string "slug"
26
+ t.string "color"
27
+ t.bigint "views"
28
+ t.datetime "created_at", null: false
29
+ t.datetime "updated_at", null: false
30
+ t.index ["slug"], name: "index_topics_on_slug", unique: true
31
+ end
32
+
23
33
  end
@@ -1,7 +1,9 @@
1
- # This file should contain all the record creation needed to seed the database with its default values.
2
- # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup).
3
- #
4
- # Examples:
5
- #
6
- # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
7
- # Character.create(name: 'Luke', movie: movies.first)
1
+ titles = ["Wazzzup People", "Howdy all me peeps!", "this is the time to rock $15"]
2
+
3
+ 3.times do |i|
4
+ Blog.create!(title: titles[i], body: "Just blank text.")
5
+ end
6
+
7
+ 3.times do |i|
8
+ Topic.create!(title: titles[i])
9
+ end
@@ -0,0 +1,48 @@
1
+ require 'test_helper'
2
+
3
+ class TopicsControllerTest < ActionDispatch::IntegrationTest
4
+ setup do
5
+ @topic = topics(:one)
6
+ end
7
+
8
+ test "should get index" do
9
+ get topics_url
10
+ assert_response :success
11
+ end
12
+
13
+ test "should get new" do
14
+ get new_topic_url
15
+ assert_response :success
16
+ end
17
+
18
+ test "should create topic" do
19
+ assert_difference('Topic.count') do
20
+ post topics_url, params: { topic: { slug: @topic.slug, title: @topic.title + "123", color: @topic.color, views: @topic.views } }
21
+ end
22
+
23
+ assert_redirected_to topic_url(Topic.last)
24
+ end
25
+
26
+ test "should show topic" do
27
+ get topic_url(@topic)
28
+ assert_response :success
29
+ end
30
+
31
+ test "should get edit" do
32
+ get edit_topic_url(@topic)
33
+ assert_response :success
34
+ end
35
+
36
+ test "should update topic" do
37
+ patch topic_url(@topic), params: { topic: { slug: @topic.slug, title: @topic.title, color: @topic.color, views: @topic.views } }
38
+ assert_redirected_to topic_url(@topic)
39
+ end
40
+
41
+ test "should destroy topic" do
42
+ assert_difference('Topic.count', -1) do
43
+ delete topic_url(@topic)
44
+ end
45
+
46
+ assert_redirected_to topics_url
47
+ end
48
+ end
@@ -0,0 +1,27 @@
1
+ one:
2
+ id: 1
3
+ title: My String!!#$#@#%#
4
+ slug: my-string
5
+ color: Blue
6
+ views: 1
7
+
8
+ two:
9
+ id: 2
10
+ title: My String 2
11
+ slug: my-string-2
12
+ color: Red
13
+ views: 2
14
+
15
+ three:
16
+ id: 3
17
+ title: MyString3
18
+ slug: mystring3
19
+ color: White
20
+ views: 3
21
+
22
+ three:
23
+ id: 4
24
+ title: My .String--4
25
+ slug: my-string-4
26
+ color: White
27
+ views: 4
@@ -8,7 +8,27 @@ class MainSlugTest < ActionDispatch::IntegrationTest
8
8
  test "friendly slug vist the filtered out ##{blog.id} title" do
9
9
  get blog_url(blog)
10
10
  assert_response :success
11
- assert_equal(request.env["REQUEST_URI"], "/blogs/#{TestAnswers::TEST_ANSWERS[blog.id]}")
11
+ assert_equal("/blogs/#{TestAnswers::TWO_PARAM_ANSWERS[blog.id]}", request.env["REQUEST_URI"])
12
+ end
13
+ end
14
+
15
+ class Blog < ApplicationRecord
16
+ build_friendly_slug :id, use: :first
17
+ end
18
+
19
+ Blog.all.each do |blog|
20
+ test "friendly slug vist the filtered out ##{blog.id}" do
21
+ get blog_url(blog)
22
+ assert_response :success
23
+ assert_equal("/blogs/#{TestAnswers::ONE_PARAM_ANSWERS[blog.id]}", request.env["REQUEST_URI"])
24
+ end
25
+ end
26
+
27
+ Topic.all.each do |topic|
28
+ test "friendly database slug filters out ##{topic.id} title" do
29
+ get topic_url(topic)
30
+ assert_response :success
31
+ assert_equal("/topics/#{TestAnswers::TOPIC_ONE_PARAM_ANSWERS[topic.id]}", request.env["REQUEST_URI"])
12
32
  end
13
33
  end
14
34
  end
@@ -3,7 +3,17 @@ require 'test_helper'
3
3
  class BlogTest < ActiveSupport::TestCase
4
4
  Blog.all.each do |blog|
5
5
  test "friendly slug filters out ##{blog.id} title" do
6
- assert_equal(blog.to_param, TestAnswers::TEST_ANSWERS[blog.id])
6
+ assert_equal(TestAnswers::TWO_PARAM_ANSWERS[blog.id], blog.to_param)
7
+ end
8
+ end
9
+
10
+ class Blog < ApplicationRecord
11
+ build_friendly_slug :id, use: :first
12
+ end
13
+
14
+ Blog.all.each do |blog|
15
+ test "friendly slug filters out ##{blog.id}" do
16
+ assert_equal(TestAnswers::ONE_PARAM_ANSWERS[blog.id], blog.to_param)
7
17
  end
8
18
  end
9
19
  end
@@ -0,0 +1,18 @@
1
+ require 'test_helper'
2
+
3
+ class TopicTest < ActiveSupport::TestCase
4
+ Topic.all.each do |topic|
5
+ test "friendly database slug filters out ##{topic.id} title" do
6
+ topic.slug = nil
7
+ assert_equal(TestAnswers::TOPIC_ONE_PARAM_ANSWERS[topic.id], topic.to_param)
8
+ end
9
+ end
10
+
11
+ test "friendly database slug filters out a new topic title" do
12
+ topic = Topic.create!(title: "Baby This Is a New Topic")
13
+ assert_equal(topic.to_param, "baby-this-is-a-new-topic")
14
+
15
+ topic.update(title: "My Newly Updated Title")
16
+ assert_equal(topic.to_param, "my-newly-updated-title")
17
+ end
18
+ end
@@ -0,0 +1,49 @@
1
+ require "application_system_test_case"
2
+
3
+ class TopicsTest < ApplicationSystemTestCase
4
+ setup do
5
+ @topic = topics(:one)
6
+ end
7
+
8
+ test "visiting the index" do
9
+ visit topics_url
10
+ assert_selector "h1", text: "Topics"
11
+ end
12
+
13
+ test "creating a Topic" do
14
+ visit topics_url
15
+ click_on "New Topic"
16
+
17
+ fill_in "Slug", with: @topic.slug
18
+ fill_in "Title", with: @topic.title
19
+ fill_in "Type", with: @topic.type
20
+ fill_in "Views", with: @topic.views
21
+ click_on "Create Topic"
22
+
23
+ assert_text "Topic was successfully created"
24
+ click_on "Back"
25
+ end
26
+
27
+ test "updating a Topic" do
28
+ visit topics_url
29
+ click_on "Edit", match: :first
30
+
31
+ fill_in "Slug", with: @topic.slug
32
+ fill_in "Title", with: @topic.title
33
+ fill_in "Type", with: @topic.type
34
+ fill_in "Views", with: @topic.views
35
+ click_on "Update Topic"
36
+
37
+ assert_text "Topic was successfully updated"
38
+ click_on "Back"
39
+ end
40
+
41
+ test "destroying a Topic" do
42
+ visit topics_url
43
+ page.accept_confirm do
44
+ click_on "Destroy", match: :first
45
+ end
46
+
47
+ assert_text "Topic was successfully destroyed"
48
+ end
49
+ end
@@ -13,10 +13,25 @@ class ActionDispatch::IntegrationTest
13
13
  end
14
14
 
15
15
  module TestAnswers
16
- TEST_ANSWERS = [
16
+ ONE_PARAM_ANSWERS = [
17
+ "",
18
+ "1",
19
+ "2",
20
+ "3",
21
+ ]
22
+
23
+ TWO_PARAM_ANSWERS = [
17
24
  "",
18
25
  "this-is-the-greatest-post-23_-asf-1",
19
26
  "just-a-normal-for-15-test-string-2",
20
27
  "just-george-normal-for-15-test-string-3",
21
28
  ]
29
+
30
+ TOPIC_ONE_PARAM_ANSWERS = [
31
+ "",
32
+ "my-string",
33
+ "my-string-2",
34
+ "mystring3",
35
+ "my-string-4",
36
+ ]
22
37
  end
@@ -1,38 +1,74 @@
1
1
  module FriendlySlug
2
2
  module ActiveRecord
3
3
  module Base
4
- def build_friendly_slug(first_attribute_key, second_attribute_key, use: nil)
5
- instance_variable_set("@first_attribute_key", first_attribute_key)
6
- instance_variable_set("@second_attribute_key", second_attribute_key)
7
- instance_variable_set("@use_key", use)
4
+ def build_friendly_slug(*attribute_list, use: nil)
5
+ instance_variable_set("@_friendly_attribute_list", attribute_list)
6
+ instance_variable_set("@_friendly_use_key", use)
8
7
 
9
8
  instance_eval do
10
- def first_attribute_key
11
- @first_attribute_key
9
+ def _friendly_attribute_list
10
+ @_friendly_attribute_list
12
11
  end
13
12
 
14
- def second_attribute_key
15
- @second_attribute_key
13
+ _friendly_attribute_list.each do |attribute|
14
+ define_singleton_method :"_friendly_#{attribute.to_s}_key" do
15
+ instance_variable_set("@_friendly_#{attribute.to_s}_key", attribute)
16
+ end
16
17
  end
17
18
 
18
- def use_key
19
- @use_key
19
+ def _friendly_use_key
20
+ @_friendly_use_key
20
21
  end
21
22
 
22
23
  def find_slugged(id)
23
- find(id.split("-").send(use_key))
24
+ find(id.split("-").send(_friendly_use_key))
24
25
  end
25
26
  end
26
27
 
27
28
  class_eval do
29
+ before_save :_update_slug
30
+
28
31
  def to_param
29
- "#{lookup_key(self.class.first_attribute_key)}-#{lookup_key(self.class.second_attribute_key)}".to_s.gsub(/<\/?[^>]*>|[^\w\s-]/, '').strip.downcase.gsub(/\s{1,}/, '-')
32
+ if self.respond_to?(:slug)
33
+ if self.slug.nil? || _unique_attribute_changed?
34
+ _create_slug
35
+ else
36
+ self.slug
37
+ end
38
+ else
39
+ _create_slug
40
+ end
30
41
  end
31
42
 
32
43
  private
33
- def lookup_key(k)
44
+ def _lookup_key(k)
34
45
  k.is_a?(Symbol)? self.send(k) : k.to_s
35
46
  end
47
+
48
+ def _update_slug
49
+ if self.class._friendly_use_key == :database
50
+ current_slug = self.to_param
51
+ unless _slug_exists?(current_slug)
52
+ self.slug = current_slug
53
+ else
54
+ self.slug = [current_slug.to_s, SecureRandom.hex(6)].join("-")
55
+ end
56
+ end
57
+ end
58
+
59
+ def _create_slug
60
+ self.class._friendly_attribute_list.map do |attribute|
61
+ _lookup_key(self.class.send("_friendly_#{attribute.to_s}_key")).to_s
62
+ end.join("-").gsub(/<\/?[^>]*>|[^\.\w\s-]/, '').strip.downcase.gsub(/\s{1,}|\./, '-').gsub(/-{2,}/, "-")
63
+ end
64
+
65
+ def _unique_attribute_changed?
66
+ self.send("#{self.class.send("_friendly_attribute_list").first.to_s}_changed?".to_sym)
67
+ end
68
+
69
+ def _slug_exists?(current_slug)
70
+ self.class.where("slug = ? AND id != ?", current_slug, self.id.nil? ? "NULL" : self.id).any?
71
+ end
36
72
  end
37
73
  end
38
74
  end
@@ -42,4 +78,3 @@ end
42
78
  class ActiveRecord::Base
43
79
  extend FriendlySlug::ActiveRecord::Base
44
80
  end
45
-
@@ -1,3 +1,3 @@
1
1
  module FriendlySlug
2
- VERSION = "0.1.5"
2
+ VERSION = "0.1.6"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: friendly_slug
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Holst
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-06-29 00:00:00.000000000 Z
11
+ date: 2018-06-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -56,8 +56,8 @@ description: A simple SEO URL friendly slug model generator. Friendly Slug is me
56
56
  to dynamically create SEO friendly URL links. It is extremely lightweight and non
57
57
  resource intensive. Friendly Slug ties directly into the Rails URL Helpers so you
58
58
  dont have to change anything. There is no need to create a Rails Migration as this
59
- gem does not add anything to your current database. You must have Active Model in
60
- your code base for this to work.
59
+ gem does not add anything to your current database unless you want to use the database
60
+ option. You must have Active Model in your code base for this to work.
61
61
  email:
62
62
  - sawohol@gmail.com
63
63
  executables: []
@@ -87,21 +87,26 @@ files:
87
87
  - friendly_slug_gem_test/app/assets/javascripts/blogs.coffee
88
88
  - friendly_slug_gem_test/app/assets/javascripts/cable.js
89
89
  - friendly_slug_gem_test/app/assets/javascripts/channels/.keep
90
+ - friendly_slug_gem_test/app/assets/javascripts/topics.coffee
90
91
  - friendly_slug_gem_test/app/assets/stylesheets/application.css
91
92
  - friendly_slug_gem_test/app/assets/stylesheets/blogs.scss
92
93
  - friendly_slug_gem_test/app/assets/stylesheets/scaffolds.scss
94
+ - friendly_slug_gem_test/app/assets/stylesheets/topics.scss
93
95
  - friendly_slug_gem_test/app/channels/application_cable/channel.rb
94
96
  - friendly_slug_gem_test/app/channels/application_cable/connection.rb
95
97
  - friendly_slug_gem_test/app/controllers/application_controller.rb
96
98
  - friendly_slug_gem_test/app/controllers/blogs_controller.rb
97
99
  - friendly_slug_gem_test/app/controllers/concerns/.keep
100
+ - friendly_slug_gem_test/app/controllers/topics_controller.rb
98
101
  - friendly_slug_gem_test/app/helpers/application_helper.rb
99
102
  - friendly_slug_gem_test/app/helpers/blogs_helper.rb
103
+ - friendly_slug_gem_test/app/helpers/topics_helper.rb
100
104
  - friendly_slug_gem_test/app/jobs/application_job.rb
101
105
  - friendly_slug_gem_test/app/mailers/application_mailer.rb
102
106
  - friendly_slug_gem_test/app/models/application_record.rb
103
107
  - friendly_slug_gem_test/app/models/blog.rb
104
108
  - friendly_slug_gem_test/app/models/concerns/.keep
109
+ - friendly_slug_gem_test/app/models/topic.rb
105
110
  - friendly_slug_gem_test/app/views/blogs/_blog.json.jbuilder
106
111
  - friendly_slug_gem_test/app/views/blogs/_form.html.erb
107
112
  - friendly_slug_gem_test/app/views/blogs/edit.html.erb
@@ -113,6 +118,14 @@ files:
113
118
  - friendly_slug_gem_test/app/views/layouts/application.html.erb
114
119
  - friendly_slug_gem_test/app/views/layouts/mailer.html.erb
115
120
  - friendly_slug_gem_test/app/views/layouts/mailer.text.erb
121
+ - friendly_slug_gem_test/app/views/topics/_form.html.erb
122
+ - friendly_slug_gem_test/app/views/topics/_topic.json.jbuilder
123
+ - friendly_slug_gem_test/app/views/topics/edit.html.erb
124
+ - friendly_slug_gem_test/app/views/topics/index.html.erb
125
+ - friendly_slug_gem_test/app/views/topics/index.json.jbuilder
126
+ - friendly_slug_gem_test/app/views/topics/new.html.erb
127
+ - friendly_slug_gem_test/app/views/topics/show.html.erb
128
+ - friendly_slug_gem_test/app/views/topics/show.json.jbuilder
116
129
  - friendly_slug_gem_test/bin/bundle
117
130
  - friendly_slug_gem_test/bin/rails
118
131
  - friendly_slug_gem_test/bin/rake
@@ -145,6 +158,7 @@ files:
145
158
  - friendly_slug_gem_test/config/spring.rb
146
159
  - friendly_slug_gem_test/config/storage.yml
147
160
  - friendly_slug_gem_test/db/migrate/20180628233122_create_blogs.rb
161
+ - friendly_slug_gem_test/db/migrate/20180630053922_create_topics.rb
148
162
  - friendly_slug_gem_test/db/schema.rb
149
163
  - friendly_slug_gem_test/db/seeds.rb
150
164
  - friendly_slug_gem_test/lib/assets/.keep
@@ -161,17 +175,21 @@ files:
161
175
  - friendly_slug_gem_test/test/application_system_test_case.rb
162
176
  - friendly_slug_gem_test/test/controllers/.keep
163
177
  - friendly_slug_gem_test/test/controllers/blogs_controller_test.rb
178
+ - friendly_slug_gem_test/test/controllers/topics_controller_test.rb
164
179
  - friendly_slug_gem_test/test/fixtures/.keep
165
180
  - friendly_slug_gem_test/test/fixtures/blogs.yml
166
181
  - friendly_slug_gem_test/test/fixtures/files/.keep
182
+ - friendly_slug_gem_test/test/fixtures/topics.yml
167
183
  - friendly_slug_gem_test/test/helpers/.keep
168
184
  - friendly_slug_gem_test/test/integration/.keep
169
185
  - friendly_slug_gem_test/test/integration/main_slug_test.rb
170
186
  - friendly_slug_gem_test/test/mailers/.keep
171
187
  - friendly_slug_gem_test/test/models/.keep
172
188
  - friendly_slug_gem_test/test/models/blog_test.rb
189
+ - friendly_slug_gem_test/test/models/topic_test.rb
173
190
  - friendly_slug_gem_test/test/system/.keep
174
191
  - friendly_slug_gem_test/test/system/blogs_test.rb
192
+ - friendly_slug_gem_test/test/system/topics_test.rb
175
193
  - friendly_slug_gem_test/test/test_helper.rb
176
194
  - friendly_slug_gem_test/tmp/.keep
177
195
  - friendly_slug_gem_test/vendor/.keep