What do you think is the best way to create SEO friendly URLs (dynamically) in Rails?
Override the to_param method in your model classes so that the default numeric ID is replaced with a meaningful string. For example, this very question uses best-permalinking-for-rails in the URL.
Ryan Bates has a Railscast on this topic.
ActiveSupport has a new method in Rails to aid this - String#parameterize. The relevant commit is here; the example given in the commit message is "Donald E. Knuth".parameterize => "donald-e-knuth"
In combination with the to_param override mentioned by John Topley, this makes friendlier URLs much easier.
rsl's stringex is pretty awesome, think of it as permalink_fu done right.
I largely use to_param as suggested by John Topley.
Remember to put indexes such that whatever you're using in to_param is quickly searchable, or you'll end up with a full table scan on each access. (Not a performance-enhancer!)
A quick work-around is to put the ID somewhere in there, in which case ActiveRecord will ignore the rest of it as cruft and just search on the id. This is why you see a lot of Rails sites with URLs like http://example.com/someController/123-a-half-readable-title .
For more detail on this and other SEO observations from my experience with Rails, you may find this page on my site useful.
For me friendly_id works fine, it can generate slugs too, so You don't need to matter about duplicated urls, scopes are also supported.
Check out the permalink_fu plugin (extracted from Mephisto)... the Git repository is located here.
I have made a small and simple gem which makes it easier to override the to_param method. It can be found here.
Related
Tweet to tweets is no big deal.
And I assume Country would become countries without any fuss. I'd even credit them with Cactus becoming cacti (correct me if I'm wrong).
But what if I have class Barnabus? Do I call its table barnabuss, barnabi, or barnabusses?
What about zxzzy? How are non-english words handled?
Are there any non-messy options for overriding the decapitalization and pluralization?
I'm new to Rails and the modified table names have me a bit off-put.
Links to relevant documentation are appreciated. This is the only documentation I have seen relevant to this, and its not helpful for understanding odd exceptions for table names.
Forgive me if this is already covered, the results I got were not exactly what I'm trying to figure out.
String#pluralize uses a heuristic with the rather simple general rules for the English language as a base and adding some known common exceptions.
Using ActiveSupport::Inflector you can customize its behavior.
In Rails projects you can find the inflections config at config/initializers/inflections.rb.
If you'd like to create a Barnabus model with a corresponding barnabi table then all you have to do is uncomment some inflections.rb content and explicitly tell Rails how to treat this word.
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'barnabus', 'barnabi'
end
P.S. Thanks to Holger Just for the edit.
You can trust it for most of the cases but if you have some doubtful model name with awkward plural names, you can always specify the table name in Model. e.g.
class SomeName < ActiveRecord::Base
self.table_name = 'any_name'
end
If pluralization in views/error messages are your concern, in the situation that Rails fails magnificently with pluralizing a tricky table name you can add some localization to your app and tell it what you want it to use under various contexts. See http://guides.rubyonrails.org/i18n.html#translations-for-active-record-models
Here's an example of pluralization rules in action:
activerecord:
models:
barnabus:
zero: Barnabi
one: Barnabus
few: Barnabi
other: Barnabi
I have an Active Resource model that needs to set a header before posting/putting through save and update_attributes. The issue is that the header value needs to be different for each user, so it can't be set at the model level. I've seen examples for setting headers as part of a find, get, or custom methods, but no way to add it to a #myclass.save. Something like prefix_options but for headers would be ideal (#myclass.prefix_options[:myvar] = 'abcd') but I haven't found anything like that. Any insight would be appreciated.
I just had a similar problem and overrode .headers on my ActiveResource class. ActiveResource::Base.headers is just a hash by default, but you can override it to be a method! <3 U Ruby.
http://rmosolgo.github.io/blog/2014/02/05/dynamically-generated-headers-for-activeresource-requests/
I just checked in the code for 3.2.8, and it looks like it's not supported. I also don't see much opportunity for monkeypatching it.
https://github.com/rails/rails/blob/c2193c11ad215d3a2d7d35960630b3e1902a5082/activeresource/lib/active_resource/base.rb#L1359
It woud be a great patch to submit though, especially now that activeresource has been split off into its own gem for 4.0.
update
actually you can specify headers with a raw post request. you just can't specify them with the more abstract methods like create:
https://github.com/rails/rails/blob/c2193c11ad215d3a2d7d35960630b3e1902a5082/activeresource/lib/active_resource/connection.rb#L97
How much of this is auto-generated methods in Rails "User.find_or_create_from_auth_hash(auth_hash)". That is just wondering how this works, and what would have been the minimum coding the dev would have to do? That is, what method would they have had to implement in the User model? or would they need a method named exactly that. So just wondering if the "find", "create", "from" keywords are Rails specials here.
Taken from here: https://github.com/intridea/omniauth
Rails provides the dynamic finders find_by_*, find_or_initialize_by_*, and find_or_create_by_* via method_missing for each attribute of your model.
The self.find_or_create_from_auth_hash(auth_hash) method however isn't at all provided by rails and is simply using the same type of naming conventions for readability.
EDIT:
Apparently it also provides find_last_by_* and find_all_by_* as well. I've personally never used them though.
I am using Ruby on Rails 3.1 and I would like to know how, when and why I should use one of the following code rather than another on internationalizing my application (I18n gem):
t(:test_key)
t('test_key')
t('.test_key')
That is, what is the "subtle" difference between using t(:test_key), t('test_key') and t('.test_key')? What are best practices about this issue?
I think first two are equivalent and you just refer to main key in your translations, fo example
t('hello_world')
# t(:hello_world) is an equivalent
would reffer to
en:
hello_world: "Hello world"
However if you use dot notation, its called lazy lookup
and it will look deeper in your translation structure based on controller/action notation
So if you use this inside users/index template
t('.hello_world')
it will be resolved to
pl:
users:
index:
hello_world: "Witaj świecie"
You'll find more about internalization in Rails Guides
I guess it's a but up to you to decide when you actually want to use the different ones, but I'd prefer to use lazy lookup as much as possible in my views, unless you need to translate some generic component whose keys does not live in the scope of your view.
The reason why I prefer the lazy lookup is that it makes the code look cleaner, and as long as you're familiar with how the i18n gem works, you shouldn't have any trouble knowing where to look for the keys.
On the other hand, if you have such components, they should really live in a partial, a cell or something similar.
One thing worth mentioning abouth the non-lazy ones, are that you can provide them with a scope for where to look for the key in question. Again, it's up to you whether you like t('foo.bar.baz.test_key') or t(:test_key, :scope => 'foo.bar.baz').
It also takes a bunch of other options, but all of this is neatly documented in the rails guide, so I won't explain it further here.
I'm still new to ROR, so pardon the simplicity of the question...
So http://www.example.com/controller/:id displays a record in my table, with :id being a number (1,2,3 etc.).
Is there a way I can have :id in the URL be the value of a field in the displayed record? Such that I can have http://www.example.com/controller/record_field? I want to have a human-friendly reference to specific records in my table. I'm sure this must be possible. Do I change something in routes.rb?
Thanks for the help!
The cleanest way is to add a new find method in your model (or simply use the find_by_fieldname Rails gives you in your control). Then you'll have your controller use that method instead of the regular find(params[:id]) to pull your model record.
Check out Ryan B's screencast on this here. It's pretty easy, and he's a good teacher, so you shouldn't have any problems.
I use the excellent rails plugin named friendly_id.
http://github.com/norman/friendly_id/tree/master
That should sort you out nicely. It is well documented too.
Take care around fields that might have modern Greek characters—might need to figure a work around for those.
Jon Smock's solution will work, too. I tend to prefer the following.
class Hamburger << ActiveRecord::Base
#this normally defaults to id
def to_param
name
end
end
class SomeModelController << ApplicationController
def show
#hamburger = Hamburger.find(params[:id]) #still default code
end
end
#goes in some view
This is the <%= link_to "tastiest hamburger ever", url_for(#hamburger) %>.
This is, loosely speaking, an SEO technique (beautiful URLs are also user-friendly and I suggest them to absolutely everyone even if you don't care about SEO, for example on pages behind a login). I have a more extended discussion of Rails SEO, which includes other tips like this, here.
Important tip: You should consider, at design-time, what you are going to do if the param should change. For example, in my hamburger scenario, it is entirely possible that I might rename "Sinfully Delicious Cheeseburger" to "Triple Bypass". If that changes URLs, there are some possible implications, such as breakage of customer links to my website. Accordingly, for production use I usually give these models an immutable permalink attribute which I initialize to be human-meaningful exactly once. If the object later changes, oh well, the URL stays the same. (There are other solutions -- that is just the easiest one.)