rsl-stringex 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +74 -0
  3. data/Rakefile +28 -0
  4. data/init.rb +7 -0
  5. data/lib/lucky_sneaks/acts_as_url.rb +82 -0
  6. data/lib/lucky_sneaks/string_extensions.rb +165 -0
  7. data/lib/lucky_sneaks/unidecoder.rb +48 -0
  8. data/lib/lucky_sneaks/unidecoder_data/x00.yml +257 -0
  9. data/lib/lucky_sneaks/unidecoder_data/x01.yml +257 -0
  10. data/lib/lucky_sneaks/unidecoder_data/x02.yml +256 -0
  11. data/lib/lucky_sneaks/unidecoder_data/x03.yml +256 -0
  12. data/lib/lucky_sneaks/unidecoder_data/x04.yml +256 -0
  13. data/lib/lucky_sneaks/unidecoder_data/x05.yml +256 -0
  14. data/lib/lucky_sneaks/unidecoder_data/x06.yml +256 -0
  15. data/lib/lucky_sneaks/unidecoder_data/x07.yml +256 -0
  16. data/lib/lucky_sneaks/unidecoder_data/x09.yml +256 -0
  17. data/lib/lucky_sneaks/unidecoder_data/x0a.yml +256 -0
  18. data/lib/lucky_sneaks/unidecoder_data/x0b.yml +256 -0
  19. data/lib/lucky_sneaks/unidecoder_data/x0c.yml +256 -0
  20. data/lib/lucky_sneaks/unidecoder_data/x0d.yml +256 -0
  21. data/lib/lucky_sneaks/unidecoder_data/x0e.yml +256 -0
  22. data/lib/lucky_sneaks/unidecoder_data/x0f.yml +256 -0
  23. data/lib/lucky_sneaks/unidecoder_data/x10.yml +256 -0
  24. data/lib/lucky_sneaks/unidecoder_data/x11.yml +256 -0
  25. data/lib/lucky_sneaks/unidecoder_data/x12.yml +257 -0
  26. data/lib/lucky_sneaks/unidecoder_data/x13.yml +256 -0
  27. data/lib/lucky_sneaks/unidecoder_data/x14.yml +257 -0
  28. data/lib/lucky_sneaks/unidecoder_data/x15.yml +257 -0
  29. data/lib/lucky_sneaks/unidecoder_data/x16.yml +256 -0
  30. data/lib/lucky_sneaks/unidecoder_data/x17.yml +256 -0
  31. data/lib/lucky_sneaks/unidecoder_data/x18.yml +256 -0
  32. data/lib/lucky_sneaks/unidecoder_data/x1e.yml +256 -0
  33. data/lib/lucky_sneaks/unidecoder_data/x1f.yml +256 -0
  34. data/lib/lucky_sneaks/unidecoder_data/x20.yml +256 -0
  35. data/lib/lucky_sneaks/unidecoder_data/x21.yml +256 -0
  36. data/lib/lucky_sneaks/unidecoder_data/x22.yml +256 -0
  37. data/lib/lucky_sneaks/unidecoder_data/x23.yml +256 -0
  38. data/lib/lucky_sneaks/unidecoder_data/x24.yml +256 -0
  39. data/lib/lucky_sneaks/unidecoder_data/x25.yml +256 -0
  40. data/lib/lucky_sneaks/unidecoder_data/x26.yml +256 -0
  41. data/lib/lucky_sneaks/unidecoder_data/x27.yml +256 -0
  42. data/lib/lucky_sneaks/unidecoder_data/x28.yml +257 -0
  43. data/lib/lucky_sneaks/unidecoder_data/x2e.yml +256 -0
  44. data/lib/lucky_sneaks/unidecoder_data/x2f.yml +256 -0
  45. data/lib/lucky_sneaks/unidecoder_data/x30.yml +256 -0
  46. data/lib/lucky_sneaks/unidecoder_data/x31.yml +256 -0
  47. data/lib/lucky_sneaks/unidecoder_data/x32.yml +256 -0
  48. data/lib/lucky_sneaks/unidecoder_data/x33.yml +256 -0
  49. data/lib/lucky_sneaks/unidecoder_data/x4d.yml +256 -0
  50. data/lib/lucky_sneaks/unidecoder_data/x4e.yml +257 -0
  51. data/lib/lucky_sneaks/unidecoder_data/x4f.yml +257 -0
  52. data/lib/lucky_sneaks/unidecoder_data/x50.yml +257 -0
  53. data/lib/lucky_sneaks/unidecoder_data/x51.yml +257 -0
  54. data/lib/lucky_sneaks/unidecoder_data/x52.yml +257 -0
  55. data/lib/lucky_sneaks/unidecoder_data/x53.yml +257 -0
  56. data/lib/lucky_sneaks/unidecoder_data/x54.yml +257 -0
  57. data/lib/lucky_sneaks/unidecoder_data/x55.yml +257 -0
  58. data/lib/lucky_sneaks/unidecoder_data/x56.yml +257 -0
  59. data/lib/lucky_sneaks/unidecoder_data/x57.yml +257 -0
  60. data/lib/lucky_sneaks/unidecoder_data/x58.yml +257 -0
  61. data/lib/lucky_sneaks/unidecoder_data/x59.yml +257 -0
  62. data/lib/lucky_sneaks/unidecoder_data/x5a.yml +257 -0
  63. data/lib/lucky_sneaks/unidecoder_data/x5b.yml +257 -0
  64. data/lib/lucky_sneaks/unidecoder_data/x5c.yml +257 -0
  65. data/lib/lucky_sneaks/unidecoder_data/x5d.yml +257 -0
  66. data/lib/lucky_sneaks/unidecoder_data/x5e.yml +257 -0
  67. data/lib/lucky_sneaks/unidecoder_data/x5f.yml +257 -0
  68. data/lib/lucky_sneaks/unidecoder_data/x60.yml +257 -0
  69. data/lib/lucky_sneaks/unidecoder_data/x61.yml +257 -0
  70. data/lib/lucky_sneaks/unidecoder_data/x62.yml +257 -0
  71. data/lib/lucky_sneaks/unidecoder_data/x63.yml +257 -0
  72. data/lib/lucky_sneaks/unidecoder_data/x64.yml +257 -0
  73. data/lib/lucky_sneaks/unidecoder_data/x65.yml +257 -0
  74. data/lib/lucky_sneaks/unidecoder_data/x66.yml +257 -0
  75. data/lib/lucky_sneaks/unidecoder_data/x67.yml +257 -0
  76. data/lib/lucky_sneaks/unidecoder_data/x68.yml +257 -0
  77. data/lib/lucky_sneaks/unidecoder_data/x69.yml +257 -0
  78. data/lib/lucky_sneaks/unidecoder_data/x6a.yml +257 -0
  79. data/lib/lucky_sneaks/unidecoder_data/x6b.yml +257 -0
  80. data/lib/lucky_sneaks/unidecoder_data/x6c.yml +257 -0
  81. data/lib/lucky_sneaks/unidecoder_data/x6d.yml +257 -0
  82. data/lib/lucky_sneaks/unidecoder_data/x6e.yml +257 -0
  83. data/lib/lucky_sneaks/unidecoder_data/x6f.yml +257 -0
  84. data/lib/lucky_sneaks/unidecoder_data/x70.yml +257 -0
  85. data/lib/lucky_sneaks/unidecoder_data/x71.yml +257 -0
  86. data/lib/lucky_sneaks/unidecoder_data/x72.yml +257 -0
  87. data/lib/lucky_sneaks/unidecoder_data/x73.yml +257 -0
  88. data/lib/lucky_sneaks/unidecoder_data/x74.yml +257 -0
  89. data/lib/lucky_sneaks/unidecoder_data/x75.yml +257 -0
  90. data/lib/lucky_sneaks/unidecoder_data/x76.yml +257 -0
  91. data/lib/lucky_sneaks/unidecoder_data/x77.yml +257 -0
  92. data/lib/lucky_sneaks/unidecoder_data/x78.yml +257 -0
  93. data/lib/lucky_sneaks/unidecoder_data/x79.yml +257 -0
  94. data/lib/lucky_sneaks/unidecoder_data/x7a.yml +257 -0
  95. data/lib/lucky_sneaks/unidecoder_data/x7b.yml +257 -0
  96. data/lib/lucky_sneaks/unidecoder_data/x7c.yml +257 -0
  97. data/lib/lucky_sneaks/unidecoder_data/x7d.yml +257 -0
  98. data/lib/lucky_sneaks/unidecoder_data/x7e.yml +257 -0
  99. data/lib/lucky_sneaks/unidecoder_data/x7f.yml +257 -0
  100. data/lib/lucky_sneaks/unidecoder_data/x80.yml +257 -0
  101. data/lib/lucky_sneaks/unidecoder_data/x81.yml +257 -0
  102. data/lib/lucky_sneaks/unidecoder_data/x82.yml +257 -0
  103. data/lib/lucky_sneaks/unidecoder_data/x83.yml +257 -0
  104. data/lib/lucky_sneaks/unidecoder_data/x84.yml +257 -0
  105. data/lib/lucky_sneaks/unidecoder_data/x85.yml +257 -0
  106. data/lib/lucky_sneaks/unidecoder_data/x86.yml +257 -0
  107. data/lib/lucky_sneaks/unidecoder_data/x87.yml +257 -0
  108. data/lib/lucky_sneaks/unidecoder_data/x88.yml +257 -0
  109. data/lib/lucky_sneaks/unidecoder_data/x89.yml +257 -0
  110. data/lib/lucky_sneaks/unidecoder_data/x8a.yml +257 -0
  111. data/lib/lucky_sneaks/unidecoder_data/x8b.yml +257 -0
  112. data/lib/lucky_sneaks/unidecoder_data/x8c.yml +257 -0
  113. data/lib/lucky_sneaks/unidecoder_data/x8d.yml +257 -0
  114. data/lib/lucky_sneaks/unidecoder_data/x8e.yml +257 -0
  115. data/lib/lucky_sneaks/unidecoder_data/x8f.yml +257 -0
  116. data/lib/lucky_sneaks/unidecoder_data/x90.yml +257 -0
  117. data/lib/lucky_sneaks/unidecoder_data/x91.yml +257 -0
  118. data/lib/lucky_sneaks/unidecoder_data/x92.yml +257 -0
  119. data/lib/lucky_sneaks/unidecoder_data/x93.yml +257 -0
  120. data/lib/lucky_sneaks/unidecoder_data/x94.yml +257 -0
  121. data/lib/lucky_sneaks/unidecoder_data/x95.yml +257 -0
  122. data/lib/lucky_sneaks/unidecoder_data/x96.yml +257 -0
  123. data/lib/lucky_sneaks/unidecoder_data/x97.yml +257 -0
  124. data/lib/lucky_sneaks/unidecoder_data/x98.yml +257 -0
  125. data/lib/lucky_sneaks/unidecoder_data/x99.yml +257 -0
  126. data/lib/lucky_sneaks/unidecoder_data/x9a.yml +257 -0
  127. data/lib/lucky_sneaks/unidecoder_data/x9b.yml +257 -0
  128. data/lib/lucky_sneaks/unidecoder_data/x9c.yml +257 -0
  129. data/lib/lucky_sneaks/unidecoder_data/x9d.yml +257 -0
  130. data/lib/lucky_sneaks/unidecoder_data/x9e.yml +257 -0
  131. data/lib/lucky_sneaks/unidecoder_data/x9f.yml +256 -0
  132. data/lib/lucky_sneaks/unidecoder_data/xa0.yml +257 -0
  133. data/lib/lucky_sneaks/unidecoder_data/xa1.yml +257 -0
  134. data/lib/lucky_sneaks/unidecoder_data/xa2.yml +257 -0
  135. data/lib/lucky_sneaks/unidecoder_data/xa3.yml +257 -0
  136. data/lib/lucky_sneaks/unidecoder_data/xa4.yml +256 -0
  137. data/lib/lucky_sneaks/unidecoder_data/xac.yml +257 -0
  138. data/lib/lucky_sneaks/unidecoder_data/xad.yml +257 -0
  139. data/lib/lucky_sneaks/unidecoder_data/xae.yml +257 -0
  140. data/lib/lucky_sneaks/unidecoder_data/xaf.yml +257 -0
  141. data/lib/lucky_sneaks/unidecoder_data/xb0.yml +257 -0
  142. data/lib/lucky_sneaks/unidecoder_data/xb1.yml +257 -0
  143. data/lib/lucky_sneaks/unidecoder_data/xb2.yml +257 -0
  144. data/lib/lucky_sneaks/unidecoder_data/xb3.yml +257 -0
  145. data/lib/lucky_sneaks/unidecoder_data/xb4.yml +257 -0
  146. data/lib/lucky_sneaks/unidecoder_data/xb5.yml +257 -0
  147. data/lib/lucky_sneaks/unidecoder_data/xb6.yml +257 -0
  148. data/lib/lucky_sneaks/unidecoder_data/xb7.yml +257 -0
  149. data/lib/lucky_sneaks/unidecoder_data/xb8.yml +257 -0
  150. data/lib/lucky_sneaks/unidecoder_data/xb9.yml +257 -0
  151. data/lib/lucky_sneaks/unidecoder_data/xba.yml +257 -0
  152. data/lib/lucky_sneaks/unidecoder_data/xbb.yml +257 -0
  153. data/lib/lucky_sneaks/unidecoder_data/xbc.yml +257 -0
  154. data/lib/lucky_sneaks/unidecoder_data/xbd.yml +257 -0
  155. data/lib/lucky_sneaks/unidecoder_data/xbe.yml +257 -0
  156. data/lib/lucky_sneaks/unidecoder_data/xbf.yml +257 -0
  157. data/lib/lucky_sneaks/unidecoder_data/xc0.yml +257 -0
  158. data/lib/lucky_sneaks/unidecoder_data/xc1.yml +257 -0
  159. data/lib/lucky_sneaks/unidecoder_data/xc2.yml +257 -0
  160. data/lib/lucky_sneaks/unidecoder_data/xc3.yml +257 -0
  161. data/lib/lucky_sneaks/unidecoder_data/xc4.yml +257 -0
  162. data/lib/lucky_sneaks/unidecoder_data/xc5.yml +257 -0
  163. data/lib/lucky_sneaks/unidecoder_data/xc6.yml +257 -0
  164. data/lib/lucky_sneaks/unidecoder_data/xc7.yml +257 -0
  165. data/lib/lucky_sneaks/unidecoder_data/xc8.yml +257 -0
  166. data/lib/lucky_sneaks/unidecoder_data/xc9.yml +257 -0
  167. data/lib/lucky_sneaks/unidecoder_data/xca.yml +257 -0
  168. data/lib/lucky_sneaks/unidecoder_data/xcb.yml +257 -0
  169. data/lib/lucky_sneaks/unidecoder_data/xcc.yml +257 -0
  170. data/lib/lucky_sneaks/unidecoder_data/xcd.yml +257 -0
  171. data/lib/lucky_sneaks/unidecoder_data/xce.yml +257 -0
  172. data/lib/lucky_sneaks/unidecoder_data/xcf.yml +257 -0
  173. data/lib/lucky_sneaks/unidecoder_data/xd0.yml +257 -0
  174. data/lib/lucky_sneaks/unidecoder_data/xd1.yml +257 -0
  175. data/lib/lucky_sneaks/unidecoder_data/xd2.yml +257 -0
  176. data/lib/lucky_sneaks/unidecoder_data/xd3.yml +257 -0
  177. data/lib/lucky_sneaks/unidecoder_data/xd4.yml +257 -0
  178. data/lib/lucky_sneaks/unidecoder_data/xd5.yml +257 -0
  179. data/lib/lucky_sneaks/unidecoder_data/xd6.yml +257 -0
  180. data/lib/lucky_sneaks/unidecoder_data/xd7.yml +256 -0
  181. data/lib/lucky_sneaks/unidecoder_data/xf9.yml +257 -0
  182. data/lib/lucky_sneaks/unidecoder_data/xfa.yml +256 -0
  183. data/lib/lucky_sneaks/unidecoder_data/xfb.yml +257 -0
  184. data/lib/lucky_sneaks/unidecoder_data/xfc.yml +257 -0
  185. data/lib/lucky_sneaks/unidecoder_data/xfd.yml +256 -0
  186. data/lib/lucky_sneaks/unidecoder_data/xfe.yml +257 -0
  187. data/lib/lucky_sneaks/unidecoder_data/xff.yml +257 -0
  188. data/stringex.gemspec +208 -0
  189. data/test/acts_as_url_test.rb +140 -0
  190. data/test/string_extensions_test.rb +138 -0
  191. data/test/unidecoder_test.rb +68 -0
  192. metadata +245 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Lucky Sneaks
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,74 @@
1
+ = Stringex
2
+
3
+ Some [hopefully] useful extensions to Ruby's String class. It is made up of three libraries: ActsAsUrl, Unidecoder, and StringExtensions.
4
+
5
+ == ActsAsUrl
6
+
7
+ This library is designed to create URI-friendly representations of an attribute, for use in generating urls from your attributes. Basic usage is just calling the method:
8
+
9
+ acts_as_url :title
10
+
11
+ which will populate the <tt>url</tt> attribute on the object with the converted contents of the <tt>title</tt> attribute. This behavior can be customized by adding the following options to the arguments of the <tt>acts_as_url</tt> method:
12
+
13
+ <tt>:url_attribute</tt>:: The name of the attribute to use for storing the generated url string.
14
+ Default is <tt>:url</tt>
15
+ <tt>:scope</tt>:: The name of model attribute to scope unique urls to. There is no default here.
16
+ <tt>:sync_url</tt>:: If set to true, the url field will be updated when changes are made to the
17
+ attribute it is based on. Default is false.
18
+
19
+ In order to use the generated url attribute, you will probably want to override <tt>to_param</tt> like so, in your Model:
20
+
21
+ def to_param
22
+ url # or whatever you set :url_attribute to
23
+ end
24
+
25
+ Routing called via named routes like <tt>foo_path(@foo)</tt> will automatically use the url. In your controllers you will need to call <tt>Foo.find_by_url(params[:id])</tt> instead of the regular find. Don't look for <tt>params[:url]</tt> unless you set it explicitly in the routing, <tt>to_param</tt> will generate <tt>params[:id]</tt>.
26
+
27
+ Unlike other permalink solutions, ActsAsUrl doesn't rely on Iconv (which is inconsistent across platforms and doesn't provide great transliteration as is) but instead uses a transliteration scheme (see the code for Unidecoder) which produces much better results for Unicode characters. It also mixes in some custom helpers to translate common characters into a more URI-friendly format rather than just dump them completely. Examples:
28
+
29
+ # A simple prelude
30
+ "simple English".to_url => "simple-english"
31
+ "it's nothing at all".to_url => "its-nothing-at-all"
32
+ "rock & roll".to_url => "rock-and-roll"
33
+
34
+ # Let's show off
35
+ "$12 worth of Ruby power".to_url => "12-dollars-worth-of-ruby-power"
36
+ "10% off if you act now".to_url => "10-percent-off-if-you-act-now"
37
+
38
+ # You don't even wanna trust Iconv for this next part
39
+ "kick it en Français".to_url => "kick-it-en-francais"
40
+ "rock it Español style".to_url => "rock-it-espanol-style"
41
+ "tell your readers 你好".to_url => "tell-your-readers-ni-hao"
42
+
43
+ Compare those results with the ones produced on my Intel Mac by a leading permalink plugin:
44
+
45
+ "simple English" # => "simple-english"
46
+ "it's nothing at all" # => "it-s-nothing-at-all"
47
+ "rock & roll" # => "rock-roll"
48
+
49
+ "$12 worth of Ruby power" # => "12-worth-of-ruby-power"
50
+ "10% off if you act now" # => "10-off-if-you-act-now"
51
+
52
+ "kick it en Français" # => "kick-it-en-francais"
53
+ "rock it Español style" # => "rock-it-espan-ol-style"
54
+ "tell your readers 你好" # => "tell-your-readers"
55
+
56
+ Not so great, actually.
57
+
58
+ Note: No offense is intended to the author[s] of whatever plugins might produce such results. It's not your faults Iconv sucks.
59
+
60
+ == Unidecoder
61
+
62
+ This library converts Unicode [and accented Ascii] characters to their plain-text Ascii equivalents. This is a port of Perl's Unidecode and provides eminently superior and more reliable results than Iconv. (Seriously, Iconv... A plague on both your houses! [sic])
63
+
64
+ You probably won't ever need to run Unidecoder by itself. StringExtensions adds String#to_ascii which wraps all of Unidecoder's functionality. For anyone interested, details of the implementation can be read about in the original implementation of Text::Unidecode[http://interglacial.com/~sburke/tpj/as_html/tpj22.html]. Extensive examples can be found in the tests.
65
+
66
+ == StringExtensions
67
+
68
+ A collection of extensions on Ruby's String class. Please see the documentation for string_extensions module for more information. There's not much to explain about them really.
69
+
70
+ == Thanks & Acknowledgements
71
+
72
+ If it's not obvious, some of the code for ActsAsUrl is based on Rick Olsen's permalink_fu[http://svn.techno-weenie.net/projects/plugins/permalink_fu/] plugin. Unidecoder is a Ruby port of Sean Burke's Text::Unidecode[http://interglacial.com/~sburke/tpj/as_html/tpj22.html] module for Perl. And, finally, the bulk of strip_html_tags[link:classes/LuckySneaks/StringExtensions.html#M000005] in StringExtensions was stolen from Tobias Lütke's Regex in Typo[http://typosphere.org/].
73
+
74
+ copyright (c) 2008 Lucky Sneaks, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => [:refresh_db, :test]
7
+
8
+ desc 'Remove old sqlite file'
9
+ task :refresh_db do
10
+ `rm -f #{File.dirname(__FILE__)}/test/acts_as_url.sqlite3`
11
+ end
12
+
13
+ desc 'Test the stringex plugin.'
14
+ Rake::TestTask.new(:test) do |t|
15
+ t.libs << 'lib'
16
+ t.pattern = 'test/**/*_test.rb'
17
+ t.verbose = true
18
+ end
19
+
20
+ desc 'Generate documentation for the stringex plugin.'
21
+ Rake::RDocTask.new(:rdoc) do |rdoc|
22
+ rdoc.rdoc_dir = 'doc'
23
+ rdoc.title = 'Stringex: A String Extension Pack'
24
+ rdoc.options << '--line-numbers' << '--inline-source'
25
+ rdoc.options << '--charset' << 'utf-8'
26
+ rdoc.rdoc_files.include('README.rdoc')
27
+ rdoc.rdoc_files.include('lib/**/*.rb')
28
+ end
data/init.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'lucky_sneaks/acts_as_url'
2
+ require 'lucky_sneaks/string_extensions'
3
+ require 'lucky_sneaks/unidecoder'
4
+
5
+ String.send :include, LuckySneaks::StringExtensions
6
+
7
+ ActiveRecord::Base.send :include, LuckySneaks::ActsAsUrl
@@ -0,0 +1,82 @@
1
+ module LuckySneaks
2
+ module ActsAsUrl # :nodoc:
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods # :doc:
8
+ # Creates a callback to automatically create an url-friendly representation
9
+ # of the <tt>attribute</tt> argument. Example:
10
+ #
11
+ # act_as_url :title
12
+ #
13
+ # will use the string contents of the <tt>title</tt> attribute
14
+ # to create the permalink. <strong>Note:</strong> you can also use a non-database-backed
15
+ # method to supply the string contents for the permalink. Just use that method's name
16
+ # as the argument as you would an attribute.
17
+ #
18
+ # The default attribute <tt>acts_as_url</tt> uses to save the permalink is <tt>url</tt>
19
+ # but this can be changed in the options hash. Available options are:
20
+ #
21
+ # <tt>:url_attribute</tt>:: The name of the attribute to use for storing the generated url string.
22
+ # Default is <tt>:url</tt>
23
+ # <tt>:scope</tt>:: The name of model attribute to scope unique urls to. There is no default here.
24
+ # <tt>:sync_url</tt>:: If set to true, the url field will be updated when changes are made to the
25
+ # attribute it is based on. Default is false.
26
+ def acts_as_url(attribute, options = {})
27
+ cattr_accessor :attribute_to_urlify
28
+ cattr_accessor :scope_for_url
29
+ cattr_accessor :url_attribute # The attribute on the DB
30
+
31
+ if options[:sync_url]
32
+ before_validation :ensure_unique_url
33
+ else
34
+ before_validation_on_create :ensure_unique_url
35
+ end
36
+
37
+ self.attribute_to_urlify = attribute
38
+ self.scope_for_url = options[:scope]
39
+ self.url_attribute = options[:url_attribute] || "url"
40
+ end
41
+
42
+ # Initialize the url fields for the records that need it. Designed for people who add
43
+ # <tt>acts_as_url</tt> support once there's already development/production data they'd
44
+ # like to keep around.
45
+ #
46
+ # Note: This method can get very expensive, very fast. If you're planning on using this
47
+ # on a large selection, you will get much better results writing your own version with
48
+ # using pagination.
49
+ def initialize_urls
50
+ find(:all, :conditions => {self.url_attribute => nil}).each do |instance|
51
+ instance.send :ensure_unique_url
52
+ instance.save
53
+ end
54
+ end
55
+ end
56
+
57
+ private
58
+ def ensure_unique_url
59
+ url_attribute = self.class.url_attribute
60
+ base_url = self.send(self.class.attribute_to_urlify).to_s.to_url
61
+ conditions = ["#{url_attribute} LIKE ?", base_url+'%']
62
+ unless new_record?
63
+ conditions.first << " and id != ?"
64
+ conditions << id
65
+ end
66
+ if self.class.scope_for_url
67
+ conditions.first << " and #{self.class.scope_for_url} = ?"
68
+ conditions << send(self.class.scope_for_url)
69
+ end
70
+ url_owners = self.class.find(:all, :conditions => conditions)
71
+ if url_owners.size > 0
72
+ n = 1
73
+ while url_owners.detect{|u| u.send(url_attribute) == "#{base_url}-#{n}"}
74
+ n = n.succ
75
+ end
76
+ write_attribute url_attribute, "#{base_url}-#{n}"
77
+ else
78
+ write_attribute url_attribute, base_url
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,165 @@
1
+ module LuckySneaks
2
+ # These methods are all added on String class.
3
+ module StringExtensions
4
+ def self.included(base) # :nodoc:
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ # Returns the string converted (via Textile/RedCloth) to HTML format or
9
+ # self if Redcloth is not available.
10
+ #
11
+ # Using <tt>:lite</tt> argument will cause RedCloth to not wrap the HTML in a container
12
+ # P element, which is useful behavior for generating header element text, etc.
13
+ # This is roughly equivalent to ActionView's <tt>textilize_without_paragraph</tt>
14
+ # except that it makes RedCloth do all the work instead of just gsubbing the return
15
+ # from RedCloth.
16
+ def to_html(lite_mode = false)
17
+ if defined?(RedCloth)
18
+ if lite_mode
19
+ RedCloth.new(self, [:lite_mode]).to_html
20
+ else
21
+ if self =~ /<pre>/
22
+ RedCloth.new(self).to_html.tr("\t", "")
23
+ else
24
+ RedCloth.new(self).to_html.tr("\t", "").gsub(/\n\n/, "")
25
+ end
26
+ end
27
+ else
28
+ self
29
+ end
30
+ end
31
+
32
+ # Create a URI-friendly representation of the string. This is used internally by
33
+ # acts_as_url[link:classes/LuckySneaks/ActsAsUrl/ClassMethods.html#M000012]
34
+ # but can be called manually in order to generate an URI-friendly version of any string.
35
+ def to_url
36
+ remove_formatting.downcase.replace_whitespace("-").collapse("-")
37
+ end
38
+
39
+ # Performs multiple text manipulations. Essentially a shortcut for typing them all. View source
40
+ # below to see which methods are run.
41
+ def remove_formatting
42
+ to_ascii.strip_html_tags.convert_accented_entities.convert_misc_entities.convert_misc_characters.collapse
43
+ end
44
+
45
+ # Removes HTML tags from text. This code is simplified from Tobias Luettke's regular expression
46
+ # in Typo[http://typosphere.org].
47
+ def strip_html_tags(leave_whitespace = false)
48
+ name = /[\w:_-]+/
49
+ value = /([A-Za-z0-9]+|('[^']*?'|"[^"]*?"))/
50
+ attr = /(#{name}(\s*=\s*#{value})?)/
51
+ rx = /<[!\/?\[]?(#{name}|--)(\s+(#{attr}(\s+#{attr})*))?\s*([!\/?\]]+|--)?>/
52
+ (leave_whitespace) ? gsub(rx, "").strip : gsub(rx, "").gsub(/\s+/, " ").strip
53
+ end
54
+
55
+ # Converts HTML entities into the respective non-accented letters. Examples:
56
+ #
57
+ # "&aacute;".convert_accented_entities # => "a"
58
+ # "&ccedil;".convert_accented_entities # => "c"
59
+ # "&egrave;".convert_accented_entities # => "e"
60
+ # "&icirc;".convert_accented_entities # => "i"
61
+ # "&oslash;".convert_accented_entities # => "o"
62
+ # "&uuml;".convert_accented_entities # => "u"
63
+ #
64
+ # Note: This does not do any conversion of Unicode/Ascii accented-characters. For that
65
+ # functionality please use <tt>to_ascii</tt>.
66
+ def convert_accented_entities
67
+ gsub(/&([A-Za-z])(grave|acute|circ|tilde|uml|ring|cedil|slash);/, '\1')
68
+ end
69
+
70
+ # Converts HTML entities (taken from common Textile/RedCloth formattings) into plain text formats.
71
+ #
72
+ # Note: This isn't an attempt at complete conversion of HTML entities, just those most likely
73
+ # to be generated by Textile.
74
+ def convert_misc_entities
75
+ dummy = dup
76
+ {
77
+ "#822[01]" => "\"",
78
+ "#821[67]" => "'",
79
+ "#8230" => "...",
80
+ "#8211" => "-",
81
+ "#8212" => "--",
82
+ "#215" => "x",
83
+ "gt" => ">",
84
+ "lt" => "<",
85
+ "(#8482|trade)" => "(tm)",
86
+ "(#174|reg)" => "(r)",
87
+ "(#169|copy)" => "(c)",
88
+ "(#38|amp)" => "and",
89
+ "nbsp" => " ",
90
+ "(#162|cent)" => " cent",
91
+ "(#163|pound)" => " pound",
92
+ "(#188|frac14)" => "one fourth",
93
+ "(#189|frac12)" => "half",
94
+ "(#190|frac34)" => "three fourths",
95
+ "(#176|deg)" => " degrees"
96
+ }.each do |textiled, normal|
97
+ dummy.gsub!(/&#{textiled};/, normal)
98
+ end
99
+ dummy.gsub(/&[^;]+;/, "")
100
+ end
101
+
102
+ # Converts various common plaintext characters to a more URI-friendly representation.
103
+ # Examples:
104
+ #
105
+ # "foo & bar".convert_misc_characters # => "foo and bar"
106
+ # "Chanel #9".convert_misc_characters # => "Chanel number nine"
107
+ # "user@host".convert_misc_characters # => "user at host"
108
+ # "google.com".convert_misc_characters # => "google dot com"
109
+ # "$10".convert_misc_characters # => "10 dollars"
110
+ # "*69".convert_misc_characters # => "star 69"
111
+ # "100%".convert_misc_characters # => "100 percent"
112
+ # "windows/mac/linux".convert_misc_characters # => "windows slash mac slash linux"
113
+ #
114
+ # Note: Because this method will convert any & symbols to the string "and",
115
+ # you should run any methods which convert HTML entities (convert_html_entities and convert_misc_entities)
116
+ # before running this method.
117
+ def convert_misc_characters
118
+ dummy = dup.gsub(/\.{3,}/, " dot dot dot ") # Catch ellipses before single dot rule!
119
+ {
120
+ /\s*&\s*/ => "and",
121
+ /\s*#/ => "number",
122
+ /\s*@\s*/ => "at",
123
+ /(\S|^)\.(\S)/ => '\1 dot \2',
124
+ /(\s|^)\$(\d*)(\s|$)/ => '\2 dollars',
125
+ /\s*\*\s*/ => "star",
126
+ /\s*%\s*/ => "percent",
127
+ /\s*(\\|\/)\s*/ => "slash",
128
+ }.each do |found, replaced|
129
+ replaced = " #{replaced} " unless replaced =~ /\\1/
130
+ dummy.gsub!(found, replaced)
131
+ end
132
+ dummy = dummy.gsub(/(^|\w)'(\w|$)/, '\1\2').gsub(/[\.,:;()\[\]\/\?!\^'"_]/, " ")
133
+ end
134
+
135
+ # Replace runs of whitespace in string. Defaults to a single space but any replacement
136
+ # string may be specified as an argument. Examples:
137
+ #
138
+ # "Foo bar".replace_whitespace # => "Foo bar"
139
+ # "Foo bar".replace_whitespace("-") # => "Foo-bar"
140
+ def replace_whitespace(replace = " ")
141
+ gsub(/\s+/, replace)
142
+ end
143
+
144
+ # Removes specified character from the beginning and/or end of the string and then performs
145
+ # <tt>String#squeeze(character)</tt>, condensing runs of the character within the string.
146
+ #
147
+ # Note: This method has been superceded by ActiveSupport's squish method.
148
+ def collapse(character = " ")
149
+ sub(/^#{character}*/, "").sub(/#{character}*$/, "").squeeze(character)
150
+ end
151
+
152
+ module ClassMethods
153
+ # Returns string of random characters with a length matching the specified limit. Excludes 0
154
+ # to avoid confusion between 0 and O.
155
+ def random(limit)
156
+ strong_alphanumerics = %w{
157
+ a b c d e f g h i j k l m n o p q r s t u v w x y z
158
+ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
159
+ 1 2 3 4 5 6 7 8 9
160
+ }
161
+ Array.new(limit, "").collect{strong_alphanumerics[rand(61)]}.join
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,48 @@
1
+ require "yaml"
2
+
3
+ module LuckySneaks
4
+ module Unidecoder
5
+ # Contains Unicode codepoints, loading as needed from YAML files
6
+ CODEPOINTS = Hash.new { |h, k|
7
+ h[k] = YAML::load_file(File.join(File.dirname(__FILE__), "unidecoder_data", "#{k}.yml"))
8
+ } unless defined?(CODEPOINTS)
9
+
10
+ class << self
11
+ # Returns string with its UTF-8 characters transliterated to ASCII ones
12
+ #
13
+ # You're probably better off just using the added String#to_ascii
14
+ def decode(string)
15
+ string.gsub(/[^\x00-\x7f]/u) do |codepoint|
16
+ begin
17
+ CODEPOINTS[code_group(codepoint)][grouped_point(codepoint)]
18
+ rescue
19
+ # Hopefully this won't come up much
20
+ "?"
21
+ end
22
+ end
23
+ end
24
+
25
+ private
26
+ # Returns the Unicode codepoint grouping for the given character
27
+ def code_group(character)
28
+ "x%02x" % (character.unpack("U")[0] >> 8)
29
+ end
30
+
31
+ # Returns the index of the given character in the YAML file for its codepoint group
32
+ def grouped_point(character)
33
+ character.unpack("U")[0] & 255
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ module LuckySneaks
40
+ module StringExtensions
41
+ # Returns string with its UTF-8 characters transliterated to ASCII ones. Example:
42
+ #
43
+ # "⠋⠗⠁⠝⠉⠑".to_ascii #=> "braille"
44
+ def to_ascii
45
+ LuckySneaks::Unidecoder::decode(self)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,257 @@
1
+ ---
2
+ - "\z"
3
+ - "\x01"
4
+ - "\x02"
5
+ - "\x03"
6
+ - "\x04"
7
+ - "\x05"
8
+ - "\x06"
9
+ - "\a"
10
+ - "\x08"
11
+ - ' '
12
+ - "\n"
13
+ - "\v"
14
+ - "\f"
15
+ - "\r"
16
+ - "\x0e"
17
+ - "\x0f"
18
+ - "\x10"
19
+ - "\x11"
20
+ - "\x12"
21
+ - "\x13"
22
+ - "\x14"
23
+ - "\x15"
24
+ - "\x16"
25
+ - "\x17"
26
+ - "\x18"
27
+ - "\x19"
28
+ - "\x1a"
29
+ - "\e"
30
+ - "\x1c"
31
+ - "\x1d"
32
+ - "\x1e"
33
+ - "\x1f"
34
+ - ' '
35
+ - '!'
36
+ - '"'
37
+ - '#'
38
+ - $
39
+ - '%'
40
+ - '&'
41
+ - "'"
42
+ - (
43
+ - )
44
+ - '*'
45
+ - +
46
+ - ','
47
+ - -
48
+ - .
49
+ - /
50
+ - 0
51
+ - 1
52
+ - 2
53
+ - 3
54
+ - 4
55
+ - 5
56
+ - 6
57
+ - 7
58
+ - 8
59
+ - 9
60
+ - ':'
61
+ - ;
62
+ - <
63
+ - =
64
+ - '>'
65
+ - '?'
66
+ - '@'
67
+ - A
68
+ - B
69
+ - C
70
+ - D
71
+ - E
72
+ - F
73
+ - G
74
+ - H
75
+ - I
76
+ - J
77
+ - K
78
+ - L
79
+ - M
80
+ - N
81
+ - O
82
+ - P
83
+ - Q
84
+ - R
85
+ - S
86
+ - T
87
+ - U
88
+ - V
89
+ - W
90
+ - X
91
+ - Y
92
+ - Z
93
+ - ']'
94
+ - \
95
+ - ']'
96
+ - '^'
97
+ - _
98
+ - '`'
99
+ - a
100
+ - b
101
+ - c
102
+ - d
103
+ - e
104
+ - f
105
+ - g
106
+ - h
107
+ - i
108
+ - j
109
+ - k
110
+ - l
111
+ - m
112
+ - n
113
+ - o
114
+ - p
115
+ - q
116
+ - r
117
+ - s
118
+ - t
119
+ - u
120
+ - v
121
+ - w
122
+ - x
123
+ - y
124
+ - z
125
+ - '{'
126
+ - '|'
127
+ - '}'
128
+ - '~'
129
+ - 
130
+ - ''
131
+ - ''
132
+ - ''
133
+ - ''
134
+ - ''
135
+ - ''
136
+ - ''
137
+ - ''
138
+ - ''
139
+ - ''
140
+ - ''
141
+ - ''
142
+ - ''
143
+ - ''
144
+ - ''
145
+ - ''
146
+ - ''
147
+ - ''
148
+ - ''
149
+ - ''
150
+ - ''
151
+ - ''
152
+ - ''
153
+ - ''
154
+ - ''
155
+ - ''
156
+ - ''
157
+ - ''
158
+ - ''
159
+ - ''
160
+ - ''
161
+ - ''
162
+ - ' '
163
+ - '!'
164
+ - C/
165
+ - PS
166
+ - $?
167
+ - Y=
168
+ - '|'
169
+ - SS
170
+ - '"'
171
+ - (c)
172
+ - a
173
+ - <<
174
+ - '!'
175
+ - ''
176
+ - (r)
177
+ - -
178
+ - deg
179
+ - +-
180
+ - 2
181
+ - 3
182
+ - "'"
183
+ - u
184
+ - P
185
+ - '*'
186
+ - ','
187
+ - 1
188
+ - o
189
+ - '>>'
190
+ - 1/4
191
+ - 1/2
192
+ - 3/4
193
+ - '?'
194
+ - A
195
+ - A
196
+ - A
197
+ - A
198
+ - A
199
+ - A
200
+ - AE
201
+ - C
202
+ - E
203
+ - E
204
+ - E
205
+ - E
206
+ - I
207
+ - I
208
+ - I
209
+ - I
210
+ - D
211
+ - N
212
+ - O
213
+ - O
214
+ - O
215
+ - O
216
+ - O
217
+ - x
218
+ - O
219
+ - U
220
+ - U
221
+ - U
222
+ - U
223
+ - U
224
+ - Th
225
+ - ss
226
+ - a
227
+ - a
228
+ - a
229
+ - a
230
+ - a
231
+ - a
232
+ - ae
233
+ - c
234
+ - e
235
+ - e
236
+ - e
237
+ - e
238
+ - i
239
+ - i
240
+ - i
241
+ - i
242
+ - d
243
+ - n
244
+ - o
245
+ - o
246
+ - o
247
+ - o
248
+ - o
249
+ - /
250
+ - o
251
+ - u
252
+ - u
253
+ - u
254
+ - u
255
+ - y
256
+ - th
257
+ - y