public
Description: Ruby on Rails
Homepage: http://rubyonrails.org
Clone URL: git://github.com/rails/rails.git
Added Inflector#parameterize for easy slug generation ("Donald E. 
Knuth".parameterize => "donald-e-knuth") #713 [Matt Darby]
dhh (author)
Tue Sep 09 22:26:50 -0700 2008
commit  b8e8be83e952163e225f9b38bd7251cba9c44f38
tree    42887c2ce47fd11cf634406e1a109f35f020bdf8
parent  b141624abbd1be6aa9836708fe4c20c03af5ab3b
...
1
2
 
 
3
4
5
...
1
2
3
4
5
6
7
0
@@ -1,5 +1,7 @@
0
 *Edge*
0
 
0
+* Added Inflector#parameterize for easy slug generation ("Donald E. Knuth".parameterize => "donald-e-knuth") #713 [Matt Darby]
0
+
0
 * Changed cache benchmarking to be reported in milliseconds [DHH]
0
 
0
 * Fix Ruby's Time marshaling bug in pre-1.9 versions of Ruby: utc instances are now correctly unmarshaled with a utc zone instead of the system local zone [#900 state:resolved] [Luca Guidi, Geoff Buesing]
...
87
88
89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
91
92
...
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
0
@@ -87,6 +87,25 @@ module ActiveSupport #:nodoc:
0
           Inflector.demodulize(self)
0
         end
0
 
0
+        # Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
0
+        # 
0
+        # ==== Examples
0
+        #
0
+        #   class Person
0
+        #     def to_param
0
+        #       "#{id}-#{name.parameterize}"
0
+        #     end
0
+        #   end
0
+        # 
0
+        #   @person = Person.find(1)
0
+        #   # => #<Person id: 1, name: "Donald E. Knuth">
0
+        # 
0
+        #   <%= link_to(@person.name, person_path %>
0
+        #   # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
0
+        def parameterize
0
+          Inflector.parameterize(self)
0
+        end
0
+
0
         # Creates the name of a table like Rails does for models to table names. This method
0
         # uses the +pluralize+ method on the last word in the string.
0
         #
...
240
241
242
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
244
245
...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
0
@@ -240,6 +240,25 @@ module ActiveSupport
0
     def demodulize(class_name_in_module)
0
       class_name_in_module.to_s.gsub(/^.*::/, '')
0
     end
0
+    
0
+    # Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
0
+    # 
0
+    # ==== Examples
0
+    #
0
+    #   class Person
0
+    #     def to_param
0
+    #       "#{id}-#{name.parameterize}"
0
+    #     end
0
+    #   end
0
+    # 
0
+    #   @person = Person.find(1)
0
+    #   # => #<Person id: 1, name: "Donald E. Knuth">
0
+    # 
0
+    #   <%= link_to(@person.name, person_path %>
0
+    #   # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
0
+    def parameterize(string, sep = '-')
0
+      string.gsub(/[^a-z0-9]+/i, sep).downcase
0
+    end
0
 
0
     # Create the name of a table like Rails does for models to table names. This method
0
     # uses the +pluralize+ method on the last word in the string.
...
98
99
100
 
 
 
 
 
 
101
102
103
...
98
99
100
101
102
103
104
105
106
107
108
109
0
@@ -98,6 +98,12 @@ class InflectorTest < Test::Unit::TestCase
0
     end
0
   end
0
 
0
+  def test_parameterize
0
+    StringToParameterized.each do |some_string, parameterized_string|
0
+      assert_equal(parameterized_string, ActiveSupport::Inflector.parameterize(some_string))
0
+    end
0
+  end
0
+
0
   def test_classify
0
     ClassNameToTableName.each do |class_name, table_name|
0
       assert_equal(class_name, ActiveSupport::Inflector.classify(table_name))
...
142
143
144
 
 
 
 
 
145
146
147
...
142
143
144
145
146
147
148
149
150
151
152
0
@@ -142,6 +142,11 @@ module InflectorTestCases
0
     "NodeChild"        => "node_children"
0
   }
0
 
0
+  StringToParameterized = {
0
+    "Donald E. Knuth"                     => "donald-e-knuth",
0
+    "Random text with *(bad)* characters" => "random-text-with-bad-characters"
0
+  }
0
+
0
   UnderscoreToHuman = {
0
     "employee_salary" => "Employee salary",
0
     "employee_id"     => "Employee",

Comments

tilsammans Wed Sep 10 01:00:49 -0700 2008

Would have been even better with stringex, since that catches foreign characters much better (i.e. at all).

http://github.com/rsl/stringex/tree/master

henrik Wed Sep 10 06:54:15 -0700 2008

stringex looks very cool, but seems like it could be overkill here. Slugalizer is very little code but handles accented characters as well as some corner cases that parameterize doesn’t:

http://github.com/henrik/slugalizer/tree/master

karmi Wed Sep 10 08:18:23 -0700 2008

The idea is great, but unfortunately this is of no use for any accented characters (Czech, Polish, other alphabets).

Example:

puts parameterize('Žluťoučký kůň skákal přes rozpálené koleje')
# => -lu-ou-k-k-sk-kal-p-es-rozp-len-koleje
puts parameterize('Garçons')
# => gar-ons
puts parameterize('Malmö')
# => malm-

Stringex has very good implementation.

We have been using with good results Iconv for this in Czech context:

puts Iconv.new('ascii//translit', 'utf-8').iconv("Žluťoučký kůň skákal v tůňce na Öresündu ©").tr(' ', '-').downcase.gsub(/[^0-9a-z-]/, '') 
=> zlutoucky-kun-skakal-v-tunce-na-oresundu-c

Even beter solution is this one from http://workingwithrails.com/person/12298-adam-cig-nek:>

(See the “cig-nek” in URL? That should be “ciganek”. )

string.chars.normalize(:kd).to_s.gsub(/[^\x00-\x7F]/, '')

See http://forum.rubyonrails.cz/forums/1/topics/9?page=2#posts-227 for explanation if you can read Czech, otherwise write here.

For the purpose is the implementation certainly insufficient. In case of names (see above “cig-nek”) maybe downright insulting :)

karmi Wed Sep 10 08:29:55 -0700 2008

Sorry about the train-wreck with the pre tags.

And more one thing to clarify the point: to me, Rails is above all about best practices in web development. Dropping letters from people’s names with accented chars, as you see on so many Rails-based websites (WWR, Slideshare, etc) is certainly not best practice.

masterkain Wed Sep 10 08:33:09 -0700 2008

I vote for slugalizer

bumi Wed Sep 10 09:55:18 -0700 2008

Rails should be as lightweight as possible that’s why I think this implementation is great and is good for 80% ppl using it. For the rest of us we could use plugins like slugarizer oder stringex, which are really awesome, too!

mdarby Wed Sep 10 10:14:51 -0700 2008

I agree. 80% rule applies here.

karmi Wed Sep 10 11:32:38 -0700 2008

Of course, for an English text, it’s almost 100% accurate. The problem with such implementation is that it promises some functionality which is in principle insufficient and for every non-English text broken.

djanowski Wed Sep 10 13:18:11 -0700 2008

Please remember we are not talking about L18N here. Even in the US and UK (is that what others regard as 80%?) there are people with names from different cultures. I don’t see why you would make any application so English-centric and not allow foreign names on it (or make them look ugly).

chuyeow Wed Sep 10 19:53:03 -0700 2008

I think karmi hit the nail on the head – the documentation ’’‘promises’’’ some functionality that is insufficient.

Perhaps a simple change of documentation to make a note about non-English characters not being parameterized properly is sufficient? Maybe a recommendation to use Slugalizer or some suitable plugin too.

NZKoz Thu Sep 11 00:44:31 -0700 2008

If we can make something less dumb using the String#chars stuff we already have, then we should. Otherwise we can just update the documentation. I don’t want to depend on iconv.

FWIW, the irony of this changeset mangling david’s new home town isn’t lost on me ;)

karmi Thu Sep 11 03:20:56 -0700 2008
Even in the US and UK (...) there are people with names from different cultures…

Precisely. It still hurts me that WWR drops two letters from my name so I end up being “karel-mina-k” there. Much more examples in almost every Rails application on the web.

I vote for the documentation caveat.

Moreover, as stated on Lighthouse, we use with very good results this little cryptic, but working code:

string.chars.normalize(:kd).to_s.gsub(/[\x00-\x7F]/, '')

Try this in script/console:

“Malm\303\266”.chars.normalize(:kd).to_s.gsub(/[\x00-\x7F]/, ’’) => “Malmo”

(Note: Mac Terminal escapes non-ASCII)

NZKoz Thu Sep 11 06:52:35 -0700 2008

Karmi, why wouldn’t we change the parameterize implementation to use your example above?

NZKoz Thu Sep 11 07:02:23 -0700 2008

Perhaps something like this: http://gist.github.com/10227

NZKoz Thu Sep 11 07:05:51 -0700 2008

gist appears to be determined to wreck the non-ascii characters in that patch, but you get the gist.

henrik Thu Sep 11 09:07:14 -0700 2008

While stringex is huge, note that Slugalizer is a oneliner, if you ignore the whitespace-for-readability and validating the argument. With all that, it’s about ten short lines. Most of slugalizer.rb are tests.

I think Slugalizer strikes a good balance between lightweight and best practice.

karmi Fri Sep 12 00:53:30 -0700 2008

NZKoz, that would go much better with accented chars, although I don’t think the first line of the method body is needed? Ie.:

>> "All the gar\303\247ons from Malm\303\266".chars.normalize(:kd).to_s.gsub(/[^\x00-\x7F]+/, '').gsub(/[^a-z0-9_\-]+/i, sep).downcase
=> "all-the-garcons-from-malmo" 

(Note please that I am not the author of the code.)

karmi Fri Sep 12 03:48:08 -0700 2008

... don’t think the first line of the method body is needed?

Eh, I am sorry. Apparently I need to slow down a bit to be able to read colorized (!) diff at least :)

henrik Fri Sep 12 07:40:25 -0700 2008

Now handles accented characters – further discussion here: http://github.com/rails/rails/commit/1ddde91303883b47f2215779cf45d7008377bd0d#comments

Bounga Tue Sep 23 05:31:32 -0700 2008

I’ve got a plugin that does the same, but the string sanitizing seems better.

You should take a look at http://github.com/Bounga/acts_as_nice_url/

henrik Wed Sep 24 03:07:20 -0700 2008

Bounga: There have been more commits since this discussion. See my link above.

I see your plugin uses iconv. That has some issues. See the README of Slugalizer, linked above.

henrik Wed Sep 24 03:08:30 -0700 2008

Bounga: There have been more commits since this discussion. See my link above.

I see your plugin uses iconv. That has some issues. See the README of Slugalizer, linked above.