changing id/URL slug in rails 3 app using mongoid - ruby-on-rails

Currently in my rails app, records are located at URLs like:
http://www.mysite.com/items/fecffe6aa299a6050274ed22
but instead I'd like them to be going to something like:
http://www.mysite.com/items/item-title-here
any ideas for how I can accomplish this?

I recommend using the mongoid_slug gem: it lets you generate a URL slug or permalink based on one or more fields in a Mongoid model.
Here's their usage example:
## Set up a slug:
class Book
include Mongoid::Document
include Mongoid::Slug
field :title
slug :title
end
## Find a record by its slug:
# GET /books/a-thousand-plateaus
book = Book.find_by_slug params[:book_id]

just modify the "show" function
For instance
def show
item= Item.where({name=>params[:id]}).first
do something...
end
Just put the function to get an object from your own id....

Related

Rails routing using custom attribute rather than table column

Using Rails 4.2, I want to create a custom route using an attr_accessor rather than a table column, but I can't get the resource_path method to work.
I want custom route like this: /foos/the-title-parameterized-1 (where "1" is the id of the object).
Foo model:
#...
attr_accessor :slug
#dynamically generate a slug:
def slug
"#{self.title.parameterize[0..200]}-#{self.id}"
end
#...
routes.rb:
get 'foos/:slug' => 'foos#show', :as => 'foo'
foos_controller.rb:
def show
#foo = Foo.find params[:slug].split("-").last.to_i
end
In my show view, when I use helper method foo_path it returns the route using the id of the object rather than the slug like this: /foos/1. Is it possible to get this helper method to use the accessor method? Am I off track with this approach?
I would prefer to use Friendly Id but I dont believe its possible without creating a slug column in my model table. I dont want to create a column because there are millions of records.
You want to override the to_param method in your model:
class Foo < ActiveRecord::Base
# ...
def to_param
slug
end
# or
alias :to_param :slug
end
You may also want to use the :constraints option in your route so that only URLs that match e.g. /-\d+\z/ are matched.
Keep in mind also that, using this method, the path /foos/i-am-not-this-foos-slug-123 will lead to the same record as /foos/i-am-this-foos-slug-123.

Can I use Friendly Id on a Rails model without having to create a slug column on the model table?

On Rails 4.2, I would like to use Friendly Id for routing to a specific model, but dont wan't to create a slug column on the model table. I would instead prefer to use an accessor method on the model and dynamically generate the slug. Is this possible? I couldn't find this in the documentation.
You can not do this directly using friendly id because of the way it uses the slug to query the database (relevant source).
However it is not difficult to accomplish what you want. What you will need are two methods:
Model#slug method which will give you slug for a specific model
Model.find_by_slug method which will generate the relevant query for a specific slug.
Now in your controllers you can use Model.find_by_slug to get the relevant model from the path params. However implementing this method might be tricky especially if your Model#slug uses a non-reversible slugging implementation like Slugify because it simply get rids of unrecognized characters in text and normalizes multiple things to same character (eg. _ and - to -)
You can use URI::Escape.encode and URI::Escape.decode but you will end up with somewhat ugly slugs.
As discussed here I went with the following approach for custom routing based on dynamic slug.
I want custom route like this: /foos/the-title-parameterized-1 (where "1" is the id of the Foo object).
Foo model:
#...
attr_accessor :slug
#dynamically generate a slug, the Rails `parameterize`
#handles transforming of ugly url characters such as
#unicode or spaces:
def slug
"#{self.title.parameterize[0..200]}-#{self.id}"
end
def to_param
slug
end
#...
routes.rb:
get 'foos/:slug' => 'foos#show', :as => 'foo'
foos_controller.rb:
def show
#foo = Foo.find params[:slug].split("-").last.to_i
end
In my show view, I am able to use the default url helper method foo_path.

friendly_id and routes.rb - rails

I'm currently using friendly_id gem in rails and noticed that if someone names a post "About" that it overwrites the /about path that I have assigned to a static page in my routes.rb file.
This is my current code:
extend FriendlyId
friendly_id :title, use: :history
If there are prior posts with the same name...it adds a --2. But friendly_id seems to ignore static routes in my routes.rb.
Is there a way to make friendly_id recognize and not overwrite these routes?
Thank you
FriendlyID includes a Reserved module which prevents a list of provided words from being used as friendly slugs. You could add your static routes to the reserved words array which would prevent someone from overwriting your routes.
From the FriendlyId RDocs
FriendlyId.defaults do |config|
config.use :reserved
# Reserve words for English and Spanish URLs
config.reserved_words = %w(new edit nueva nuevo editar)
end
If you still want to allow for a title that is reserved you can make a new method that FriendlyId would use for the slug. This piece from the RDocs explains that
Column or Method?
FriendlyId always uses a method as the basis of the slug text - not a column. It first glance, this may sound confusing, but remember that Active Record provides methods for each column in a model's associated table, and that's what FriendlyId uses.
Here's an example of a class that uses a custom method to generate the slug:
class Person < ActiveRecord::Base
friendly_id :name_and_location
def name_and_location
"#{name} from #{location}"
end
end
bob = Person.create! :name => "Bob Smith", :location => "New York City"
bob.friendly_id #=> "bob-smith-from-new-york-city"
You could create a method like :title_with_id or :title_with_rand. it's up to you and how you'd like the slugs to look.
You would also want to make sure your routes.rb has your static routes listed prior to the routes for with the friendly id. The first route dispatcher matches is where the request will be processed.

Model and controller designs in rails for friendly URL

Another question from rails newbie. I am using friendly_id gem with mysql in rails 3.x
This is a design problem (may be easy in rails). I am expecting advises from rails experts. I am building a library listing app. Where user can view library by "metro-area" or by "city" in it. For example:
I wish to have URLs like:
www.list.com/library/san-francisco-bay-area
or
www.list.com/library/san-francisco-bay-area/palo-alto/
In database I have tables:
library
-------
id, name, city_id, slug
name is slugged here and city_id is FK
city
----
city_id, name, metro_area_id, slug
name is slugged here and metro_area_id is FK
metro_area
----------
metro_area_id, name, state, slug
name is slugged here
So when a user points browser to www.list.com/library/san-francisco-bay-area/palo-alto
I wish to get list of libraries in san-francisco-bay-area/palo-alto. But my library table model is containing slug only for library's name. So how this URL can be parsed to find the city_id that can be used in library model and controller to get the list.
Please remember, I cannot rely only on the name of the city. I would have to find city_id of 'palo-alto' which is in metro 'san-francisco-bay-area'. Since slug for metro_area and city is in other tables, so what is the best way to design model and controller.
Show method of controller is:
def show
#user = Library.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #library }
end
end
and model is
class Library < ActiveRecord::Base
attr_accessible :name, :city_id, :slug
extend FriendlyId
friendly_id :name, use: :slugged
end
This will not work per my friendly URL requirement. So I would appreciate advice from experts :)
Maybe you have found your solution, I'm new to rails as well, so I'm just guessing this would work out.
Since you wanna display slugs from two different models. I'm assuming the way to display ids would be something like
www.list.com/libraries/:id/cities/:id/metro_areas/:id
Which can be done through editing the route file by adding Nested Resources
As for just displaying two ids like
www.list.com/library/:city_id/:metro_area_id
Rails guide refers it as Dynamic Segments
After that, it's just a matter of converting the ids to slugs.
I also found this in FriendlyId's documentation which is addressing to your case.
Hope it helps

How to configure WordPress-like permalinks?

I want to be able to have post permalinks appear in the root of the site. So, for example, a post with a permalink "hello-world" should appear as "mysite.com/hello-world", instead of "mysite.com/posts_controller/hello-world."
How would I go about doing something like this?
I believe that you already have a "slug" field in your posts model.
If your post controller has that into account, you just need to add the correct route for instance:
match '/:slug' => "Posts#show"
Otherwise, if don't have the slug in your model, you can use the Stringex plugin. It's an easy way to automatic create slugs for your posts.
class Post < ActiveRecord::Base
acts_as_url :title
end
It will create the slug from your title and save it to the slug column.
In the controller you can find the correct post like this:
def show
#post = Post.find_by_slug(params[:slug])
end
In your routes:
match '/:slug' => "Posts#show"
Then in your controller you could do something like:
Post.find_by_slug(params[:slug])
Note: you will need to generate this slug value and store it in the Post model.
Also have a look at friendly_id for a tried and tested way of doing this (if you need something more complex).

Resources