stringex 1.3.2 → 1.3.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +8 -3
- data/Rakefile +1 -1
- data/lib/stringex.rb +3 -4
- data/lib/stringex/acts_as_url.rb +9 -5
- data/lib/stringex/string_extensions.rb +34 -7
- data/stringex.gemspec +2 -2
- data/test/acts_as_url_test.rb +14 -0
- data/test/string_extensions_test.rb +59 -2
- metadata +4 -4
data/README.rdoc
CHANGED
@@ -14,11 +14,16 @@ which will populate the <tt>url</tt> attribute on the object with the converted
|
|
14
14
|
<tt>:url_attribute</tt>:: The name of the attribute to use for storing the generated url string.
|
15
15
|
Default is <tt>:url</tt>
|
16
16
|
<tt>:scope</tt>:: The name of model attribute to scope unique urls to. There is no default here.
|
17
|
-
<tt>:only_when_blank</tt>:: If true, the url generation will only happen when <tt>:url_attribute</tt> is
|
18
|
-
blank. Default is false (meaning url generation will happen always)
|
17
|
+
<tt>:only_when_blank</tt>:: If set to true, the url generation will only happen when <tt>:url_attribute</tt> is
|
18
|
+
blank. Default is false (meaning url generation will happen always).
|
19
19
|
<tt>:sync_url</tt>:: If set to true, the url field will be updated when changes are made to the
|
20
20
|
attribute it is based on. Default is false.
|
21
21
|
<tt>:allow_slash</tt>:: If set to true, the url field will not convert slashes. Default is false.
|
22
|
+
<tt>:allow_duplicates</tt>:: If set to true, unique urls will not be enforced.
|
23
|
+
Default is false. <em>NOTE: This is strongly not recommended
|
24
|
+
if you are routing solely on the generated slug as you will no longer
|
25
|
+
be guaranteed to lookup the expected record based on a duplicate slug.</em>
|
26
|
+
|
22
27
|
|
23
28
|
In order to use the generated url attribute, you will probably want to override <tt>to_param</tt> like so, in your Model:
|
24
29
|
|
@@ -29,7 +34,7 @@ In order to use the generated url attribute, you will probably want to override
|
|
29
34
|
|
30
35
|
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>.
|
31
36
|
|
32
|
-
Note that if you add <tt>acts_as_url</tt> to an old model, the <tt>url</tt> database column will inititally be blank. To set this column for your old instances, you can use the <tt>initialize_urls</tt> method. So if your class is <tt>Post</tt>, just say <tt>Post.
|
37
|
+
Note that if you add <tt>acts_as_url</tt> to an old model, the <tt>url</tt> database column will inititally be blank. To set this column for your old instances, you can use the <tt>initialize_urls</tt> method. So if your class is <tt>Post</tt>, just say <tt>Post.initialize_urls</tt>.
|
33
38
|
|
34
39
|
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:
|
35
40
|
|
data/Rakefile
CHANGED
data/lib/stringex.rb
CHANGED
@@ -4,7 +4,6 @@ require 'stringex/unidecoder'
|
|
4
4
|
|
5
5
|
String.send :include, Stringex::StringExtensions
|
6
6
|
|
7
|
-
if defined?(ActiveRecord)
|
8
|
-
|
9
|
-
|
10
|
-
end
|
7
|
+
require 'stringex/acts_as_url' if defined?(ActiveRecord) || defined?(Mongoid::Document)
|
8
|
+
|
9
|
+
ActiveRecord::Base.send :include, Stringex::ActsAsUrl if defined?(ActiveRecord)
|
data/lib/stringex/acts_as_url.rb
CHANGED
@@ -33,6 +33,7 @@ module Stringex
|
|
33
33
|
cattr_accessor :only_when_blank
|
34
34
|
cattr_accessor :duplicate_count_separator
|
35
35
|
cattr_accessor :allow_slash
|
36
|
+
cattr_accessor :allow_duplicates
|
36
37
|
|
37
38
|
if options[:sync_url]
|
38
39
|
before_validation(:ensure_unique_url)
|
@@ -50,6 +51,7 @@ module Stringex
|
|
50
51
|
self.only_when_blank = options[:only_when_blank] || false
|
51
52
|
self.duplicate_count_separator = options[:duplicate_count_separator] || "-"
|
52
53
|
self.allow_slash = options[:allow_slash] || false
|
54
|
+
self.allow_duplicates = options[:allow_duplicates] || false
|
53
55
|
|
54
56
|
class_eval <<-"END"
|
55
57
|
def #{url_attribute}
|
@@ -95,12 +97,14 @@ module Stringex
|
|
95
97
|
end
|
96
98
|
url_owners = self.class.find(:all, :conditions => conditions)
|
97
99
|
write_attribute url_attribute, base_url
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
100
|
+
unless self.class.allow_duplicates
|
101
|
+
if url_owners.any?{|owner| owner.send(url_attribute) == base_url}
|
102
|
+
n = 1
|
103
|
+
while url_owners.any?{|owner| owner.send(url_attribute) == "#{base_url}#{separator}#{n}"}
|
104
|
+
n = n.succ
|
105
|
+
end
|
106
|
+
write_attribute url_attribute, "#{base_url}#{separator}#{n}"
|
102
107
|
end
|
103
|
-
write_attribute url_attribute, "#{base_url}#{separator}#{n}"
|
104
108
|
end
|
105
109
|
end
|
106
110
|
end
|
@@ -42,7 +42,7 @@ module Stringex
|
|
42
42
|
# Performs multiple text manipulations. Essentially a shortcut for typing them all. View source
|
43
43
|
# below to see which methods are run.
|
44
44
|
def remove_formatting(options = {})
|
45
|
-
strip_html_tags.convert_smart_punctuation.convert_accented_entities.convert_misc_entities.convert_misc_characters(options).to_ascii.collapse
|
45
|
+
strip_html_tags.convert_smart_punctuation.convert_accented_entities.convert_vulgar_fractions.convert_misc_entities.convert_misc_characters(options).to_ascii.collapse
|
46
46
|
end
|
47
47
|
|
48
48
|
# Removes HTML tags from text. This code is simplified from Tobias Luettke's regular expression
|
@@ -67,7 +67,7 @@ module Stringex
|
|
67
67
|
# Note: This does not do any conversion of Unicode/ASCII accented-characters. For that
|
68
68
|
# functionality please use <tt>to_ascii</tt>.
|
69
69
|
def convert_accented_entities
|
70
|
-
gsub(/&([A-Za-z])(grave|acute|circ|tilde|uml|ring|cedil|slash);/, '\1')
|
70
|
+
gsub(/&([A-Za-z])(grave|acute|circ|tilde|uml|ring|cedil|slash);/, '\1').strip
|
71
71
|
end
|
72
72
|
|
73
73
|
# Converts HTML entities (taken from common Textile/RedCloth formattings) into plain text formats.
|
@@ -95,11 +95,37 @@ module Stringex
|
|
95
95
|
"(#188|frac14)" => "one fourth",
|
96
96
|
"(#189|frac12)" => "half",
|
97
97
|
"(#190|frac34)" => "three fourths",
|
98
|
-
"(#176|deg)" => " degrees"
|
98
|
+
"(#176|deg)" => " degrees "
|
99
99
|
}.each do |textiled, normal|
|
100
100
|
dummy.gsub!(/&#{textiled};/, normal)
|
101
101
|
end
|
102
|
-
dummy.gsub(/&[^;]+;/, "")
|
102
|
+
dummy.gsub(/&[^;]+;/, "").strip
|
103
|
+
end
|
104
|
+
|
105
|
+
# Converts vulgar fractions from supported html entities and unicode to
|
106
|
+
# plain text formats.
|
107
|
+
def convert_vulgar_fractions
|
108
|
+
dummy = dup
|
109
|
+
{
|
110
|
+
"(¼|¼|¼)" => "one fourth",
|
111
|
+
"(½|½|½)" => "half",
|
112
|
+
"(¾|¾|¾)" => "three fourths",
|
113
|
+
"(⅓|⅓)" => "one third",
|
114
|
+
"(⅔|⅔)" => "two thirds",
|
115
|
+
"(⅕|⅕)" => "one fifth",
|
116
|
+
"(⅖|⅖)" => "two fifths",
|
117
|
+
"(⅗|⅗)" => "three fifths",
|
118
|
+
"(⅘|⅘)" => "four fifths",
|
119
|
+
"(⅙|⅙)" => "one sixth",
|
120
|
+
"(⅚|⅚)" => "five sixths",
|
121
|
+
"(⅛|⅛)" => "one eighth",
|
122
|
+
"(⅜|⅜)" => "three eighths",
|
123
|
+
"(⅝|⅝)" => "five eighths",
|
124
|
+
"(⅞|⅞)" => "seven eighths"
|
125
|
+
}.each do |textiled, normal|
|
126
|
+
dummy.gsub!(/#{textiled}/, normal)
|
127
|
+
end
|
128
|
+
dummy
|
103
129
|
end
|
104
130
|
|
105
131
|
# Converts MS Word 'smart punctuation' to ASCII
|
@@ -114,7 +140,7 @@ module Stringex
|
|
114
140
|
}.each do |smart, normal|
|
115
141
|
dummy.gsub!(/#{smart}/, normal)
|
116
142
|
end
|
117
|
-
dummy
|
143
|
+
dummy.strip
|
118
144
|
end
|
119
145
|
|
120
146
|
# Converts various common plaintext characters to a more URI-friendly representation.
|
@@ -155,14 +181,15 @@ module Stringex
|
|
155
181
|
/\s*\*\s*/ => "star",
|
156
182
|
/\s*%\s*/ => "percent",
|
157
183
|
/(\s*=\s*)/ => " equals ",
|
158
|
-
/\s*\+\s*/ => "plus"
|
184
|
+
/\s*\+\s*/ => "plus",
|
185
|
+
/\s*°\s*/ => "degrees"
|
159
186
|
}
|
160
187
|
misc_characters[/\s*(\\|\/)\s*/] = 'slash' unless options[:allow_slash]
|
161
188
|
misc_characters.each do |found, replaced|
|
162
189
|
replaced = " #{replaced} " unless replaced =~ /\\1/
|
163
190
|
dummy.gsub!(found, replaced)
|
164
191
|
end
|
165
|
-
dummy = dummy.gsub(/(
|
192
|
+
dummy = dummy.gsub(/(^|[[:alpha:]])'([[:alpha:]]|$)/, '\1\2').gsub(/[\.,:;()\[\]\/\?!\^'ʼ"_]/, " ").strip
|
166
193
|
end
|
167
194
|
|
168
195
|
# Replace runs of whitespace in string. Defaults to a single space but any replacement
|
data/stringex.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{stringex}
|
8
|
-
s.version = "1.3.
|
8
|
+
s.version = "1.3.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Russell Norris"]
|
12
|
-
s.date = %q{2012-
|
12
|
+
s.date = %q{2012-04-24}
|
13
13
|
s.description = %q{Some [hopefully] useful extensions to Ruby's String class. Stringex is made up of three libraries: ActsAsUrl [permalink solution with better character translation], Unidecoder [Unicode to ASCII transliteration], and StringExtensions [miscellaneous helper methods for the String class].}
|
14
14
|
s.email = %q{rsl@luckysneaks.com}
|
15
15
|
s.extra_rdoc_files = [
|
data/test/acts_as_url_test.rb
CHANGED
@@ -49,6 +49,10 @@ ActiveRecord::Schema.define(:version => 1) do
|
|
49
49
|
create_table :validatuments, :force => true do |t|
|
50
50
|
t.string :title, :url, :other
|
51
51
|
end
|
52
|
+
|
53
|
+
create_table :ununiquments, :force => true do |t|
|
54
|
+
t.string :title, :url, :other
|
55
|
+
end
|
52
56
|
end
|
53
57
|
ActiveRecord::Migration.verbose = true
|
54
58
|
|
@@ -89,6 +93,10 @@ class Validatument < ActiveRecord::Base
|
|
89
93
|
validates_presence_of :title
|
90
94
|
end
|
91
95
|
|
96
|
+
class Ununiqument < ActiveRecord::Base
|
97
|
+
acts_as_url :title, :allow_duplicates => true
|
98
|
+
end
|
99
|
+
|
92
100
|
class ActsAsUrlTest < Test::Unit::TestCase
|
93
101
|
def test_should_create_url
|
94
102
|
@doc = Document.create(:title => "Let's Make a Test Title, <em>Okay</em>?")
|
@@ -100,6 +108,12 @@ class ActsAsUrlTest < Test::Unit::TestCase
|
|
100
108
|
@other_doc = Document.create!(:title => "Unique")
|
101
109
|
assert_equal "unique-1", @other_doc.url
|
102
110
|
end
|
111
|
+
|
112
|
+
def test_should_not_create_unique_url
|
113
|
+
@doc = Ununiqument.create!(:title => "I am not a clone")
|
114
|
+
@other_doc = Ununiqument.create!(:title => "I am not a clone")
|
115
|
+
assert_equal "i-am-not-a-clone", @other_doc.url
|
116
|
+
end
|
103
117
|
|
104
118
|
def test_should_not_succ_on_repeated_saves
|
105
119
|
@doc = Document.new(:title => "Continuous or Constant")
|
@@ -49,8 +49,26 @@ class StringExtensionsTest < Test::Unit::TestCase
|
|
49
49
|
"im-just-making-sure-theres-nothing-wrong-with-things",
|
50
50
|
"foo = bar and bar=foo" =>
|
51
51
|
"foo-equals-bar-and-bar-equals-foo",
|
52
|
+
"Period.period" =>
|
53
|
+
"period-dot-period",
|
52
54
|
"Will…This Work?" =>
|
53
|
-
"will-dot-dot-dot-this-work"
|
55
|
+
"will-dot-dot-dot-this-work",
|
56
|
+
"¼ pound with cheese" =>
|
57
|
+
"one-fourth-pound-with-cheese",
|
58
|
+
"Will's Ferrel" =>
|
59
|
+
"wills-ferrel",
|
60
|
+
"Капитал" =>
|
61
|
+
"kapital",
|
62
|
+
"Ελλάδα" =>
|
63
|
+
"ellada",
|
64
|
+
"中文" =>
|
65
|
+
"zhong-wen",
|
66
|
+
"Paul Cézanne" =>
|
67
|
+
"paul-cezanne",
|
68
|
+
"21'17ʼ51" =>
|
69
|
+
"21-17-51",
|
70
|
+
"ITCZ 1 (21°17ʼ51.78”N / 89°35ʼ28.18”O / 26-04-08 / 09:00 am)" =>
|
71
|
+
"itcz-1-21-degrees-17-51-dot-78-n-slash-89-degrees-35-28-dot-18-o-slash-26-04-08-slash-09-00-am"
|
54
72
|
}.each do |html, plain|
|
55
73
|
assert_equal plain, html.to_url
|
56
74
|
end
|
@@ -95,6 +113,46 @@ class StringExtensionsTest < Test::Unit::TestCase
|
|
95
113
|
end
|
96
114
|
end
|
97
115
|
|
116
|
+
def test_convert_vulgar_fractions
|
117
|
+
{
|
118
|
+
"¼" => "one fourth",
|
119
|
+
"¼" => "one fourth",
|
120
|
+
"¼" => "one fourth",
|
121
|
+
"½" => "half",
|
122
|
+
"½" => "half",
|
123
|
+
"½" => "half",
|
124
|
+
"¾" => "three fourths",
|
125
|
+
"¾" => "three fourths",
|
126
|
+
"¾" => "three fourths",
|
127
|
+
"⅓" => "one third",
|
128
|
+
"⅓" => "one third",
|
129
|
+
"⅔" => "two thirds",
|
130
|
+
"⅔" => "two thirds",
|
131
|
+
"⅕" => "one fifth",
|
132
|
+
"⅕" => "one fifth",
|
133
|
+
"⅖" => "two fifths",
|
134
|
+
"⅖" => "two fifths",
|
135
|
+
"⅗" => "three fifths",
|
136
|
+
"⅗" => "three fifths",
|
137
|
+
"⅘" => "four fifths",
|
138
|
+
"⅘" => "four fifths",
|
139
|
+
"⅙" => "one sixth",
|
140
|
+
"⅙" => "one sixth",
|
141
|
+
"⅚" => "five sixths",
|
142
|
+
"⅚" => "five sixths",
|
143
|
+
"⅛" => "one eighth",
|
144
|
+
"⅛" => "one eighth",
|
145
|
+
"⅜" => "three eighths",
|
146
|
+
"⅜" => "three eighths",
|
147
|
+
"⅝" => "five eighths",
|
148
|
+
"⅝" => "five eighths",
|
149
|
+
"⅞" => "seven eighths",
|
150
|
+
"⅞" => "seven eighths"
|
151
|
+
}.each do |entitied, plain|
|
152
|
+
assert_equal plain, entitied.convert_vulgar_fractions
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
98
156
|
def test_convert_misc_entities
|
99
157
|
{
|
100
158
|
"America™" => "America(tm)",
|
@@ -102,7 +160,6 @@ class StringExtensionsTest < Test::Unit::TestCase
|
|
102
160
|
"To be continued…" => "To be continued...",
|
103
161
|
"Foo Bar" => "Foo Bar",
|
104
162
|
"100£" => "100 pound",
|
105
|
-
"½ a dollar" => "half a dollar",
|
106
163
|
"35°" => "35 degrees"
|
107
164
|
}.each do |entitied, plain|
|
108
165
|
assert_equal plain, entitied.convert_misc_entities
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stringex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 29
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 3
|
9
|
-
-
|
10
|
-
version: 1.3.
|
9
|
+
- 3
|
10
|
+
version: 1.3.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Russell Norris
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-04-24 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|