Skip to content

Commit

Permalink
Create pretty URLs using FriendlyId
Browse files Browse the repository at this point in the history
  • Loading branch information
lujanfernaud committed May 18, 2018
1 parent 70d9984 commit c30b3db
Show file tree
Hide file tree
Showing 17 changed files with 257 additions and 14 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ gem 'figaro', '~> 1.1', '>= 1.1.1'
gem 'gravatar_image_tag', '~> 1.2'
gem 'inline_svg', '~> 1.3', '>= 1.3.1'
gem 'faker', '~> 1.8', '>= 1.8.7'
gem 'friendly_id', '~> 5.2', '>= 5.2.4'

# Used for bulk inserting data into database using ActiveRecord.
gem 'activerecord-import', '~> 0.23.0'
Expand Down
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ GEM
figaro (1.1.1)
thor (~> 0.14)
formatador (0.2.5)
friendly_id (5.2.4)
activerecord (>= 4.0.0)
geocoder (1.4.7)
globalid (0.4.1)
activesupport (>= 4.2.0)
Expand Down Expand Up @@ -401,6 +403,7 @@ DEPENDENCIES
devise (~> 4.4, >= 4.4.3)
faker (~> 1.8, >= 1.8.7)
figaro (~> 1.1, >= 1.1.1)
friendly_id (~> 5.2, >= 5.2.4)
geocoder (~> 1.4, >= 1.4.5)
gravatar_image_tag (~> 1.2)
guard (= 2.14.0)
Expand Down
32 changes: 25 additions & 7 deletions app/models/event.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
class Event < ApplicationRecord
include FriendlyId
friendly_id :slug_candidates, use: :scoped, scope: :group

include Storext.model

store_attributes :updated_fields do
Expand All @@ -20,7 +23,7 @@ class Event < ApplicationRecord

delegate :place_name, :street1, :street2, :city,
:state, :post_code, :country,
:full_address, :full_address_changed?, to: :address
:full_address, :full_address_changed?, to: :address, allow_nil: true

has_many :attendances, foreign_key: "attended_event_id"
has_many :attendees, through: :attendances
Expand Down Expand Up @@ -62,12 +65,19 @@ def short_description

private

def no_past_date
if start_date < Time.zone.now
errors.add(:start_date, "can't be in the past")
elsif end_date < start_date
errors.add(:start_date, "can't be later than end date")
end
def should_generate_new_friendly_id?
title_changed?
end

def slug_candidates
[
:title,
[:title, :date]
]
end

def date
start_date.strftime("%b %d %Y")
end

def titleize_title
Expand Down Expand Up @@ -111,4 +121,12 @@ def store_updated_address
def touch_group
group.touch
end

def no_past_date
if start_date < Time.zone.now
errors.add(:start_date, "can't be in the past")
elsif end_date < start_date
errors.add(:start_date, "can't be later than end date")
end
end
end
24 changes: 24 additions & 0 deletions app/models/group.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
class Group < ApplicationRecord
include PgSearch

include FriendlyId
friendly_id :slug_candidates, use: :slugged

resourcify

before_save :titleize_name
Expand Down Expand Up @@ -65,6 +68,27 @@ def remove_from_organizers(member)

private

def should_generate_new_friendly_id?
name_changed?
end

def slug_candidates
[
:name,
[:name, :location],
[:name, :location, :owner_name],
[:name, :location, :owner_name, :owner_id]
]
end

def owner_name
owner.name
end

def owner_id
owner.id
end

def titleize_name
self.name = name.titleize
end
Expand Down
1 change: 1 addition & 0 deletions app/models/sample_event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def build_event
)

@event.build_address(event_address)
@event.send(:set_slug)

# We don't validate because we are not setting the image,
# so it's going to use the default one set by EventImageUploader.
Expand Down
2 changes: 2 additions & 0 deletions app/models/sample_group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ def create_group
sample_group: true
)

@group.send(:set_slug)

# We don't validate because we are not setting the image,
# so it's going to use the default one set by GroupImageUploader.
@group.save(validate: false)
Expand Down
14 changes: 14 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ class User < ApplicationRecord
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable

include FriendlyId
friendly_id :slug_candidates, use: :slugged

before_save :titleize_name
before_update :titleize_location
before_update :capitalize_bio
Expand Down Expand Up @@ -96,6 +99,17 @@ def upcoming_attended_events

private

def should_generate_new_friendly_id?
name_changed?
end

def slug_candidates
[
:name,
[:name, :id]
]
end

def titleize_name
self.name = name.titleize
end
Expand Down
107 changes: 107 additions & 0 deletions config/initializers/friendly_id.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# FriendlyId Global Configuration
#
# Use this to set up shared configuration options for your entire application.
# Any of the configuration options shown here can also be applied to single
# models by passing arguments to the `friendly_id` class method or defining
# methods in your model.
#
# To learn more, check out the guide:
#
# http://norman.github.io/friendly_id/file.Guide.html

FriendlyId.defaults do |config|
# ## Reserved Words
#
# Some words could conflict with Rails's routes when used as slugs, or are
# undesirable to allow as slugs. Edit this list as needed for your app.
config.use :reserved

config.reserved_words = %w(new edit index session login logout users admin
stylesheets assets javascripts images)

# This adds an option to to treat reserved words as conflicts rather than exceptions.
# When there is no good candidate, a UUID will be appended, matching the existing
# conflict behavior.

# config.treat_reserved_as_conflict = true

# ## Friendly Finders
#
# Uncomment this to use friendly finders in all models. By default, if
# you wish to find a record by its friendly id, you must do:
#
# MyModel.friendly.find('foo')
#
# If you uncomment this, you can do:
#
# MyModel.find('foo')
#
# This is significantly more convenient but may not be appropriate for
# all applications, so you must explicity opt-in to this behavior. You can
# always also configure it on a per-model basis if you prefer.
#
# Something else to consider is that using the :finders addon boosts
# performance because it will avoid Rails-internal code that makes runtime
# calls to `Module.extend`.
#
config.use :finders
#
# ## Slugs
#
# Most applications will use the :slugged module everywhere. If you wish
# to do so, uncomment the following line.
#
# config.use :slugged
#
# By default, FriendlyId's :slugged addon expects the slug column to be named
# 'slug', but you can change it if you wish.
#
# config.slug_column = 'slug'
#
# By default, slug has no size limit, but you can change it if you wish.
#
# config.slug_limit = 255
#
# When FriendlyId can not generate a unique ID from your base method, it appends
# a UUID, separated by a single dash. You can configure the character used as the
# separator. If you're upgrading from FriendlyId 4, you may wish to replace this
# with two dashes.
#
# config.sequence_separator = '-'
#
# Note that you must use the :slugged addon **prior** to the line which
# configures the sequence separator, or else FriendlyId will raise an undefined
# method error.
#
# ## Tips and Tricks
#
# ### Controlling when slugs are generated
#
# As of FriendlyId 5.0, new slugs are generated only when the slug field is
# nil, but if you're using a column as your base method can change this
# behavior by overriding the `should_generate_new_friendly_id?` method that
# FriendlyId adds to your model. The change below makes FriendlyId 5.0 behave
# more like 4.0.
# Note: Use(include) Slugged module in the config if using the anonymous module.
# If you have `friendly_id :name, use: slugged` in the model, Slugged module
# is included after the anonymous module defined in the initializer, so it
# overrides the `should_generate_new_friendly_id?` method from the anonymous module.
#
# config.use :slugged
# config.use Module.new {
# def should_generate_new_friendly_id?
# slug.blank? || <your_column_name_here>_changed?
# end
# }
#
# FriendlyId uses Rails's `parameterize` method to generate slugs, but for
# languages that don't use the Roman alphabet, that's not usually sufficient.
# Here we use the Babosa library to transliterate Russian Cyrillic slugs to
# ASCII. If you use this, don't forget to add "babosa" to your Gemfile.
#
# config.use Module.new {
# def normalize_friendly_id(text)
# text.to_slug.normalize! :transliterations => [:russian, :latin]
# end
# }
end
22 changes: 22 additions & 0 deletions db/migrate/20180517163813_create_friendly_id_slugs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
migration_class =
if ActiveRecord::VERSION::MAJOR >= 5
ActiveRecord::Migration[4.2]
else
ActiveRecord::Migration
end

class CreateFriendlyIdSlugs < migration_class
def change
create_table :friendly_id_slugs do |t|
t.string :slug, :null => false
t.integer :sluggable_id, :null => false
t.string :sluggable_type, :limit => 50
t.string :scope
t.datetime :created_at
end
add_index :friendly_id_slugs, :sluggable_id
add_index :friendly_id_slugs, [:slug, :sluggable_type], length: { slug: 140, sluggable_type: 50 }
add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], length: { slug: 70, sluggable_type: 50, scope: 70 }, unique: true
add_index :friendly_id_slugs, :sluggable_type
end
end
6 changes: 6 additions & 0 deletions db/migrate/20180517170138_add_slug_to_groups.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class AddSlugToGroups < ActiveRecord::Migration[5.1]
def change
add_column :groups, :slug, :string
add_index :groups, :slug
end
end
6 changes: 6 additions & 0 deletions db/migrate/20180517190202_add_slug_to_events.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class AddSlugToEvents < ActiveRecord::Migration[5.1]
def change
add_column :events, :slug, :string
add_index :events, :slug
end
end
6 changes: 6 additions & 0 deletions db/migrate/20180517212505_add_slug_to_users.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class AddSlugToUsers < ActiveRecord::Migration[5.1]
def change
add_column :users, :slug, :string
add_index :users, :slug
end
end
20 changes: 19 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20180517084540) do
ActiveRecord::Schema.define(version: 20180517212505) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -53,8 +53,22 @@
t.bigint "group_id"
t.jsonb "updated_fields", default: {}, null: false
t.boolean "sample_event", default: false
t.string "slug"
t.index ["group_id"], name: "index_events_on_group_id"
t.index ["organizer_id"], name: "index_events_on_organizer_id"
t.index ["slug"], name: "index_events_on_slug"
end

create_table "friendly_id_slugs", id: :serial, force: :cascade do |t|
t.string "slug", null: false
t.integer "sluggable_id", null: false
t.string "sluggable_type", limit: 50
t.string "scope"
t.datetime "created_at"
t.index ["slug", "sluggable_type", "scope"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type_and_scope", unique: true
t.index ["slug", "sluggable_type"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type"
t.index ["sluggable_id"], name: "index_friendly_id_slugs_on_sluggable_id"
t.index ["sluggable_type"], name: "index_friendly_id_slugs_on_sluggable_type"
end

create_table "group_memberships", force: :cascade do |t|
Expand All @@ -77,7 +91,9 @@
t.bigint "user_id"
t.string "location"
t.boolean "sample_group", default: false
t.string "slug"
t.index ["location"], name: "index_groups_on_location"
t.index ["slug"], name: "index_groups_on_slug"
t.index ["user_id"], name: "index_groups_on_user_id"
end

Expand Down Expand Up @@ -141,8 +157,10 @@
t.string "unconfirmed_email"
t.boolean "sample_user", default: false
t.boolean "admin", default: false
t.string "slug"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["slug"], name: "index_users_on_slug"
end

create_table "users_roles", id: false, force: :cascade do |t|
Expand Down
5 changes: 4 additions & 1 deletion test/controllers/events_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,11 @@ class EventsControllerTest < ActionDispatch::IntegrationTest

patch group_event_url(@group, @event), params: event_params_updated

group = Group.find(@group.id)
event = Event.find(@event.id)

assert_emails_sent_to attendees_emails
assert_redirected_to group_event_url(@group, @event)
assert_redirected_to group_event_url(group, event)
end

test "should destroy event" do
Expand Down
Loading

0 comments on commit c30b3db

Please sign in to comment.