diff --git a/Gemfile b/Gemfile index f3f79b89..48cddfbe 100644 --- a/Gemfile +++ b/Gemfile @@ -107,6 +107,7 @@ gem 'acts_as_follower', '0.1.1' gem 'color' gem 'createsend' gem 'fog' +gem 'friendly_id', '4.0.10.1' gem 'geocoder' gem 'hashie' gem 'linkedin' diff --git a/Gemfile.lock b/Gemfile.lock index 11a90fa8..92f96050 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -258,6 +258,8 @@ GEM dotenv (~> 0.11.1) thor (~> 0.19.1) formatador (0.2.5) + friendly_id (4.0.10.1) + activerecord (>= 3.0, < 4.0) fssm (0.2.10) fukuzatsu (0.9.16) ephemeral @@ -739,6 +741,7 @@ DEPENDENCIES flog fog foreman + friendly_id (= 4.0.10.1) fukuzatsu fuubar (= 2.0.0.rc1) geocoder diff --git a/app/assets/javascripts/backbone/routers/ProtipRouter.js.coffee b/app/assets/javascripts/backbone/routers/ProtipRouter.js.coffee index c0a7abe8..d3a9d3ae 100644 --- a/app/assets/javascripts/backbone/routers/ProtipRouter.js.coffee +++ b/app/assets/javascripts/backbone/routers/ProtipRouter.js.coffee @@ -4,14 +4,11 @@ window.ProtipRouter = Backbone.Router.extend( '*path': 'closeProtip' fetchProtip: (id)-> - if(id.match(/^[\dA-Z\-_]{6}$/i)) $.ajax '/p/' + id, type: 'GET' data: mode: 'popup' dataType: 'script' - else - @.closeProtip() closeProtip: -> $('#x-active-preview-pane').remove() diff --git a/app/controllers/protips_controller.rb b/app/controllers/protips_controller.rb index d19b16a9..48f699ca 100644 --- a/app/controllers/protips_controller.rb +++ b/app/controllers/protips_controller.rb @@ -135,6 +135,8 @@ def show end return redirect_to protip_missing_destination, notice: "The pro tip you were looking for no longer exists" if @protip.nil? + return redirect_to protip_path(@protip.public_id<<'/'<<@protip.friendly_id, :p => params[:p], :q => params[:q]) if params[:slug]!=@protip.friendly_id + @comments = @protip.comments @reply_to = show_params[:reply_to] @next_protip = Protip.search_next(show_params[:q], show_params[:t], show_params[:i], show_params[:p]) if is_admin? diff --git a/app/models/protip.rb b/app/models/protip.rb index 33f61265..586700e3 100644 --- a/app/models/protip.rb +++ b/app/models/protip.rb @@ -7,6 +7,9 @@ require 'search' class Protip < ActiveRecord::Base + extend FriendlyId + friendly_id :slug_format, :use => :slugged + include Featurable # TODO: Break out the various responsibilities on the Protip into modules/concerns. @@ -951,7 +954,11 @@ def matching_jobs def to_html CFM::Markdown.render self.body end - + + def slug_format + "#{title}" + end + protected def check_links errors[:body] << "one or more of the links are invalid or not publicly reachable/require login" unless valid_links? @@ -969,7 +976,7 @@ def adjust_like_value(user, like_value) def analyze_spam AnalyzeSpamJob.perform_async({ id: id, klass: self.class.name }) end - + end # == Schema Information diff --git a/config/routes.rb b/config/routes.rb index 5aa68c27..7dd79422 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -287,8 +287,9 @@ get '/jobs(/:location(/:skill))' => 'opportunities#index', as: :jobs get '/jobs-map' => 'opportunities#map', as: :jobs_map - resources :protips, :path => '/p', :constraints => {id: /[\dA-Z\-_]{6}/i} do + resources :protips, :path => '/p' do collection do + get ':id/:slug' => 'protips#show', as: :slug, :constraints => {:slug => /(?!.*?edit).*/} get 'random' get 'search' => 'protips#search', as: :search post 'search' => 'protips#search' diff --git a/db/migrate/20141015182230_add_slug_to_protips.rb b/db/migrate/20141015182230_add_slug_to_protips.rb new file mode 100644 index 00000000..882b8494 --- /dev/null +++ b/db/migrate/20141015182230_add_slug_to_protips.rb @@ -0,0 +1,6 @@ +class AddSlugToProtips < ActiveRecord::Migration + def change + add_column :protips, :slug, :string + add_index :protips, :slug + end +end diff --git a/db/schema.rb b/db/schema.rb index e6738cdb..e460520d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20140807214719) do +ActiveRecord::Schema.define(:version => 20141015182230) do add_extension "citext" @@ -254,9 +254,11 @@ t.float "boost_factor", :default => 1.0 t.integer "inappropriate", :default => 0 t.integer "likes_count", :default => 0 + t.string "slug" end add_index "protips", ["public_id"], :name => "index_protips_on_public_id" + add_index "protips", ["slug"], :name => "index_protips_on_slug" add_index "protips", ["user_id"], :name => "index_protips_on_user_id" create_table "purchased_bundles", :force => true do |t| diff --git a/lib/tasks/generate_protip_slugs.rake b/lib/tasks/generate_protip_slugs.rake new file mode 100644 index 00000000..27fcc0b5 --- /dev/null +++ b/lib/tasks/generate_protip_slugs.rake @@ -0,0 +1,10 @@ +desc 'Generate slugs for existing protips' +task :generate_protip_slugs => :environment do + begin + Protip.all.each do |pt| + pt.save + end + rescue => e + puts "Rake task protip slugs failed: #{e}" + end +end diff --git a/spec/controllers/protips_controller_spec.rb b/spec/controllers/protips_controller_spec.rb index ab2de649..999dd110 100644 --- a/spec/controllers/protips_controller_spec.rb +++ b/spec/controllers/protips_controller_spec.rb @@ -49,10 +49,20 @@ def valid_session # end # end - describe "GET show" do - it "assigns the requested protip as @protip" do + describe "GET show using public_id" do + it "redirects to GET show using slug" do protip = Protip.create! valid_attributes + protip.save get :show, {id: protip.to_param}, valid_session + expect(response).to redirect_to slug_protips_path(protip,protip.friendly_id) + end + end + + describe "GET show using slug" do + it "assigns the requested protip as @protip" do + protip = Protip.create! valid_attributes + protip.save + get :show, {id: protip.public_id, slug: protip.friendly_id}, valid_session expect(assigns(:protip)).to eq(protip) end end