stringex 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/Rakefile +4 -3
- data/lib/stringex/acts_as_url.rb +89 -53
- data/lib/stringex/string_extensions.rb +24 -7
- data/lib/stringex/unidecoder.rb +26 -14
- data/lib/stringex/unidecoder_data/x03.yml +15 -15
- data/stringex.gemspec +26 -8
- data/test/acts_as_url_test.rb +25 -3
- data/test/string_extensions_test.rb +36 -7
- data/test/unidecoder_test.rb +14 -1
- metadata +124 -41
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -100,4 +100,4 @@ You'll need to add a <tt>:find_by => :url</tt> to your <tt>load_and_authorize_re
|
|
100
100
|
|
101
101
|
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/Stringex/StringExtensions.html#M000005] in StringExtensions was stolen from Tobias Lütke's Regex in Typo[http://typosphere.org/].
|
102
102
|
|
103
|
-
copyright (c) 2008 Lucky Sneaks, released under the MIT license
|
103
|
+
copyright (c) 2008-2012 Lucky Sneaks, released under the MIT license
|
data/Rakefile
CHANGED
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'rake'
|
4
4
|
require 'rake/testtask'
|
5
|
-
require '
|
5
|
+
require 'rdoc/task'
|
6
|
+
require 'stringex/version'
|
6
7
|
|
7
8
|
begin
|
8
9
|
require 'jeweler'
|
@@ -212,10 +213,10 @@ begin
|
|
212
213
|
}
|
213
214
|
gem.rdoc_options = %w{--main README.rdoc --charset utf-8 --line-numbers}
|
214
215
|
gem.extra_rdoc_files = %w{MIT-LICENSE README.rdoc}
|
215
|
-
gem.version =
|
216
|
+
gem.version = Stringex::Version::STRING
|
216
217
|
end
|
217
218
|
|
218
|
-
Jeweler::
|
219
|
+
Jeweler::RubygemsDotOrgTasks.new
|
219
220
|
rescue LoadError
|
220
221
|
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
221
222
|
end
|
data/lib/stringex/acts_as_url.rb
CHANGED
@@ -5,11 +5,67 @@ module Stringex
|
|
5
5
|
base.extend ClassMethods
|
6
6
|
end
|
7
7
|
|
8
|
+
class Configuration
|
9
|
+
attr_accessor :allow_slash, :allow_duplicates, :attribute_to_urlify, :duplicate_count_separator,
|
10
|
+
:exclude, :only_when_blank, :scope_for_url, :sync_url, :url_attribute, :url_limit
|
11
|
+
|
12
|
+
def initialize(klass, options = {})
|
13
|
+
self.allow_slash = options[:allow_slash]
|
14
|
+
self.allow_duplicates = options[:allow_duplicates]
|
15
|
+
self.attribute_to_urlify = options[:attribute]
|
16
|
+
self.duplicate_count_separator = options[:duplicate_count_separator] || "-"
|
17
|
+
self.exclude = options[:exclude] || []
|
18
|
+
self.only_when_blank = options[:only_when_blank]
|
19
|
+
self.scope_for_url = options[:scope]
|
20
|
+
self.sync_url = options[:sync_url]
|
21
|
+
self.url_attribute = options[:url_attribute] || "url"
|
22
|
+
self.url_limit = options[:limit]
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_base_url!(instance)
|
26
|
+
base_url = instance.send(url_attribute)
|
27
|
+
if base_url.blank? || !only_when_blank
|
28
|
+
root = instance.send(attribute_to_urlify).to_s
|
29
|
+
base_url = root.to_url(:allow_slash => allow_slash, :limit => url_limit, :exclude => exclude)
|
30
|
+
end
|
31
|
+
instance.instance_variable_set "@acts_as_url_base_url", base_url
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_conditions!(instance)
|
35
|
+
conditions = ["#{url_attribute} LIKE ?", instance.instance_variable_get("@acts_as_url_base_url") + '%']
|
36
|
+
unless instance.new_record?
|
37
|
+
conditions.first << " and id != ?"
|
38
|
+
conditions << instance.id
|
39
|
+
end
|
40
|
+
if scope_for_url
|
41
|
+
conditions.first << " and #{scope_for_url} = ?"
|
42
|
+
conditions << instance.send(scope_for_url)
|
43
|
+
end
|
44
|
+
conditions
|
45
|
+
end
|
46
|
+
|
47
|
+
def handle_duplicate_urls!(instance)
|
48
|
+
unless allow_duplicates
|
49
|
+
base_url = instance.instance_variable_get("@acts_as_url_base_url")
|
50
|
+
|
51
|
+
url_owners = instance.class.unscoped.find(:all, :conditions => get_conditions!(instance))
|
52
|
+
if url_owners.any?{|owner| owner.send(url_attribute) == base_url}
|
53
|
+
separator = duplicate_count_separator
|
54
|
+
n = 1
|
55
|
+
while url_owners.any?{|owner| owner.send(url_attribute) == "#{base_url}#{separator}#{n}"}
|
56
|
+
n = n.succ
|
57
|
+
end
|
58
|
+
instance.send :write_attribute, url_attribute, "#{base_url}#{separator}#{n}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
8
64
|
module ClassMethods # :doc:
|
9
65
|
# Creates a callback to automatically create an url-friendly representation
|
10
66
|
# of the <tt>attribute</tt> argument. Example:
|
11
67
|
#
|
12
|
-
#
|
68
|
+
# acts_as_url :title
|
13
69
|
#
|
14
70
|
# will use the string contents of the <tt>title</tt> attribute
|
15
71
|
# to create the permalink. <strong>Note:</strong> you can also use a non-database-backed
|
@@ -19,24 +75,30 @@ module Stringex
|
|
19
75
|
# The default attribute <tt>acts_as_url</tt> uses to save the permalink is <tt>url</tt>
|
20
76
|
# but this can be changed in the options hash. Available options are:
|
21
77
|
#
|
22
|
-
# <tt>:
|
23
|
-
#
|
24
|
-
#
|
78
|
+
# <tt>:allow_slash</tt>:: If true, allow the generated url to contain slashes. Default is false[y].
|
79
|
+
# <tt>:allow_duplicates</tt>:: If true, allow duplicate urls instead of appending numbers to
|
80
|
+
# differentiate between urls. Default is false[y].
|
81
|
+
# <tt>:duplicate_count_separator</tt>:: String to use when forcing unique urls from non-unique strings.
|
82
|
+
# Default is "-".
|
83
|
+
# <tt>:exclude_list</tt>:: List of complete strings that should not be transformed by <tt>acts_as_url</tt>.
|
84
|
+
# Default is empty.
|
25
85
|
# <tt>:only_when_blank</tt>:: If true, the url generation will only happen when <tt>:url_attribute</tt> is
|
26
|
-
# blank. Default is false (meaning url generation will happen always)
|
86
|
+
# blank. Default is false[y] (meaning url generation will happen always).
|
87
|
+
# <tt>:scope</tt>:: The name of model attribute to scope unique urls to. There is no default here.
|
27
88
|
# <tt>:sync_url</tt>:: If set to true, the url field will be updated when changes are made to the
|
28
|
-
# attribute it is based on. Default is false.
|
89
|
+
# attribute it is based on. Default is false[y].
|
90
|
+
# <tt>:url_attribute</tt>:: The name of the attribute to use for storing the generated url string.
|
91
|
+
# Default is <tt>:url</tt>.
|
92
|
+
# <tt>:url_limit</tt>:: The maximum size a generated url should be. <strong>Note:</strong> this does not
|
93
|
+
# include the characters needed to enforce uniqueness on duplicate urls.
|
94
|
+
# Default is nil.
|
29
95
|
def acts_as_url(attribute, options = {})
|
30
|
-
cattr_accessor :
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
cattr_accessor :duplicate_count_separator
|
35
|
-
cattr_accessor :allow_slash
|
36
|
-
cattr_accessor :allow_duplicates
|
37
|
-
cattr_accessor :url_limit
|
96
|
+
cattr_accessor :acts_as_url_configuration
|
97
|
+
|
98
|
+
options[:attribute] = attribute
|
99
|
+
self.acts_as_url_configuration = ActsAsUrl::Configuration.new(self, options)
|
38
100
|
|
39
|
-
if
|
101
|
+
if acts_as_url_configuration.sync_url
|
40
102
|
before_validation(:ensure_unique_url)
|
41
103
|
else
|
42
104
|
if defined?(ActiveModel::Callbacks)
|
@@ -46,21 +108,12 @@ module Stringex
|
|
46
108
|
end
|
47
109
|
end
|
48
110
|
|
49
|
-
self.attribute_to_urlify = attribute
|
50
|
-
self.scope_for_url = options[:scope]
|
51
|
-
self.url_attribute = options[:url_attribute] || "url"
|
52
|
-
self.only_when_blank = options[:only_when_blank] || false
|
53
|
-
self.duplicate_count_separator = options[:duplicate_count_separator] || "-"
|
54
|
-
self.allow_slash = options[:allow_slash] || false
|
55
|
-
self.allow_duplicates = options[:allow_duplicates] || false
|
56
|
-
self.url_limit = options[:limit] || nil
|
57
|
-
|
58
111
|
class_eval <<-"END"
|
59
|
-
def #{url_attribute}
|
60
|
-
if !new_record? && errors[attribute_to_urlify].present?
|
61
|
-
self.class.find(id).send(url_attribute)
|
112
|
+
def #{acts_as_url_configuration.url_attribute}
|
113
|
+
if !new_record? && errors[acts_as_url_configuration.attribute_to_urlify].present?
|
114
|
+
self.class.find(id).send(acts_as_url_configuration.url_attribute)
|
62
115
|
else
|
63
|
-
read_attribute(url_attribute)
|
116
|
+
read_attribute(acts_as_url_configuration.url_attribute)
|
64
117
|
end
|
65
118
|
end
|
66
119
|
END
|
@@ -74,7 +127,7 @@ module Stringex
|
|
74
127
|
# on a large selection, you will get much better results writing your own version with
|
75
128
|
# using pagination.
|
76
129
|
def initialize_urls
|
77
|
-
|
130
|
+
find_each(:conditions => {acts_as_url_configuration.url_attribute => nil}) do |instance|
|
78
131
|
instance.send :ensure_unique_url
|
79
132
|
instance.save
|
80
133
|
end
|
@@ -84,30 +137,13 @@ module Stringex
|
|
84
137
|
private
|
85
138
|
|
86
139
|
def ensure_unique_url
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
conditions << id
|
95
|
-
end
|
96
|
-
if self.class.scope_for_url
|
97
|
-
conditions.first << " and #{self.class.scope_for_url} = ?"
|
98
|
-
conditions << send(self.class.scope_for_url)
|
99
|
-
end
|
100
|
-
url_owners = self.class.find(:all, :conditions => conditions)
|
101
|
-
write_attribute url_attribute, base_url
|
102
|
-
unless self.class.allow_duplicates
|
103
|
-
if url_owners.any?{|owner| owner.send(url_attribute) == base_url}
|
104
|
-
n = 1
|
105
|
-
while url_owners.any?{|owner| owner.send(url_attribute) == "#{base_url}#{separator}#{n}"}
|
106
|
-
n = n.succ
|
107
|
-
end
|
108
|
-
write_attribute url_attribute, "#{base_url}#{separator}#{n}"
|
109
|
-
end
|
110
|
-
end
|
140
|
+
# Just to save some typing
|
141
|
+
config = acts_as_url_configuration
|
142
|
+
url_attribute = config.url_attribute
|
143
|
+
|
144
|
+
config.get_base_url! self
|
145
|
+
write_attribute url_attribute, @acts_as_url_base_url
|
146
|
+
config.handle_duplicate_urls!(self) unless config.allow_duplicates
|
111
147
|
end
|
112
148
|
end
|
113
149
|
end
|
@@ -36,17 +36,29 @@ module Stringex
|
|
36
36
|
# acts_as_url[link:classes/Stringex/ActsAsUrl/ClassMethods.html#M000012]
|
37
37
|
# but can be called manually in order to generate an URI-friendly version of any string.
|
38
38
|
def to_url(options = {})
|
39
|
+
return self if options[:exclude] && options[:exclude].include?(self)
|
39
40
|
remove_formatting(options).downcase.replace_whitespace("-").collapse("-").limit(options[:limit])
|
40
41
|
end
|
41
42
|
|
42
|
-
|
43
|
-
|
43
|
+
# Returns the string limited in size to the value of limit.
|
44
|
+
def limit(limit = nil)
|
45
|
+
limit.nil? ? self : self[0...limit]
|
44
46
|
end
|
45
47
|
|
46
48
|
# Performs multiple text manipulations. Essentially a shortcut for typing them all. View source
|
47
49
|
# below to see which methods are run.
|
48
50
|
def remove_formatting(options = {})
|
49
|
-
strip_html_tags.
|
51
|
+
strip_html_tags.
|
52
|
+
convert_smart_punctuation.
|
53
|
+
convert_accented_entities.
|
54
|
+
convert_vulgar_fractions.
|
55
|
+
convert_misc_entities.
|
56
|
+
convert_misc_characters(options).
|
57
|
+
to_ascii.
|
58
|
+
# NOTE: String#to_ascii may convert some Unicode characters to ascii we'd already transliterated
|
59
|
+
# so we need to do it again just to be safe
|
60
|
+
convert_misc_characters(options).
|
61
|
+
collapse
|
50
62
|
end
|
51
63
|
|
52
64
|
# Removes HTML tags from text. This code is simplified from Tobias Luettke's regular expression
|
@@ -58,7 +70,6 @@ module Stringex
|
|
58
70
|
rx = /<[!\/?\[]?(#{name}|--)(\s+(#{attr}(\s+#{attr})*))?\s*([!\/?\]]+|--)?>/
|
59
71
|
(leave_whitespace) ? gsub(rx, "").strip : gsub(rx, "").gsub(/\s+/, " ").strip
|
60
72
|
end
|
61
|
-
|
62
73
|
# Converts HTML entities into the respective non-accented letters. Examples:
|
63
74
|
#
|
64
75
|
# "á".convert_accented_entities # => "a"
|
@@ -99,6 +110,7 @@ module Stringex
|
|
99
110
|
"(#188|frac14)" => "one fourth",
|
100
111
|
"(#189|frac12)" => "half",
|
101
112
|
"(#190|frac34)" => "three fourths",
|
113
|
+
"(#247|divide)" => "divide",
|
102
114
|
"(#176|deg)" => " degrees "
|
103
115
|
}.each do |textiled, normal|
|
104
116
|
dummy.gsub!(/&#{textiled};/, normal)
|
@@ -172,8 +184,12 @@ module Stringex
|
|
172
184
|
replaced = " #{replaced} " unless replaced =~ /\\1/
|
173
185
|
dummy.gsub!(found, replaced)
|
174
186
|
end
|
187
|
+
# Special rules for abbreviations
|
188
|
+
dummy.gsub!(/(\s|^)([[:alpha:]](\.[[:alpha:]])+(\.?)[[:alpha:]]*(\s|$))/) do |x|
|
189
|
+
x.gsub(".", "")
|
190
|
+
end
|
175
191
|
# Back to normal rules
|
176
|
-
misc_characters =
|
192
|
+
misc_characters =
|
177
193
|
{
|
178
194
|
/\s*&\s*/ => "and",
|
179
195
|
/\s*#/ => "number",
|
@@ -186,14 +202,15 @@ module Stringex
|
|
186
202
|
/\s*%\s*/ => "percent",
|
187
203
|
/(\s*=\s*)/ => " equals ",
|
188
204
|
/\s*\+\s*/ => "plus",
|
205
|
+
/\s*÷\s*/ => "divide",
|
189
206
|
/\s*°\s*/ => "degrees"
|
190
207
|
}
|
191
|
-
misc_characters[/\s*(
|
208
|
+
misc_characters[/\s*(\\|\/|/)\s*/] = 'slash' unless options[:allow_slash]
|
192
209
|
misc_characters.each do |found, replaced|
|
193
210
|
replaced = " #{replaced} " unless replaced =~ /\\1/
|
194
211
|
dummy.gsub!(found, replaced)
|
195
212
|
end
|
196
|
-
dummy = dummy.gsub(/(^|[[:alpha:]])'([[:alpha:]]|$)/, '\1\2').gsub(/[\.,:;()\[\]\/\?!\^'ʼ"_]/, " ").strip
|
213
|
+
dummy = dummy.gsub(/(^|[[:alpha:]])'|`([[:alpha:]]|$)/, '\1\2').gsub(/[\.,:;()\[\]\/\?!\^'ʼ"_\|]/, " ").strip
|
197
214
|
end
|
198
215
|
|
199
216
|
# Replace runs of whitespace in string. Defaults to a single space but any replacement
|
data/lib/stringex/unidecoder.rb
CHANGED
@@ -35,6 +35,11 @@ module Stringex
|
|
35
35
|
["0x#{codepoint}".to_i(16)].pack("U")
|
36
36
|
end
|
37
37
|
|
38
|
+
# Returns Unicode codepoint for the given character
|
39
|
+
def get_codepoint(character)
|
40
|
+
"%04x" % character.unpack("U")[0]
|
41
|
+
end
|
42
|
+
|
38
43
|
# Returns string indicating which file (and line) contains the
|
39
44
|
# transliteration value for the character
|
40
45
|
def in_yaml_file(character)
|
@@ -115,18 +120,22 @@ module Stringex
|
|
115
120
|
# Checks LOCAL_CODEPOINTS's Hash is in the format we expect before assigning it and raises
|
116
121
|
# instructive exception if not
|
117
122
|
def verify_local_codepoints(hash)
|
118
|
-
pass_check
|
119
|
-
# Fuck a duck, eh?
|
120
|
-
[Symbol, String].include?(key.class) && value.is_a?(Hash) &&
|
121
|
-
value.keys.all?{|k| k.is_a?(String)} && value.values.all?{|v| v.is_a?(String)}
|
122
|
-
}
|
123
|
-
if pass_check
|
124
|
-
hash.each do |k, v|
|
125
|
-
LOCAL_CODEPOINTS[k] = v
|
126
|
-
end
|
127
|
-
else
|
123
|
+
if !pass_check(hash)
|
128
124
|
raise ArgumentError, "LOCAL_CODEPOINTS is not correctly defined. Please see the README for more information on how to correctly format this data."
|
129
125
|
end
|
126
|
+
hash.each{|k, v| LOCAL_CODEPOINTS[k] = v}
|
127
|
+
end
|
128
|
+
|
129
|
+
def pass_check(hash)
|
130
|
+
return false if !hash.is_a?(Hash)
|
131
|
+
hash.all?{|key, value| pass_check_key_and_value_test(key, value) }
|
132
|
+
end
|
133
|
+
|
134
|
+
def pass_check_key_and_value_test(key, value)
|
135
|
+
# Fuck a duck, eh?
|
136
|
+
return false unless [Symbol, String].include?(key.class)
|
137
|
+
return false unless value.is_a?(Hash)
|
138
|
+
value.all?{|k, v| k.is_a?(String) && v.is_a?(String)}
|
130
139
|
end
|
131
140
|
end
|
132
141
|
end
|
@@ -135,10 +144,13 @@ module Stringex
|
|
135
144
|
class << self
|
136
145
|
%w{
|
137
146
|
localize_from
|
138
|
-
locale
|
139
|
-
|
147
|
+
locale
|
148
|
+
locale=
|
149
|
+
default_locale
|
150
|
+
default_locale=
|
140
151
|
local_codepoint
|
141
|
-
with_locale
|
152
|
+
with_locale
|
153
|
+
with_default_locale
|
142
154
|
}.each do |name|
|
143
155
|
define_method name do |*args, &block|
|
144
156
|
Unidecoder.send name, *args, &block
|
@@ -151,7 +163,7 @@ module Stringex
|
|
151
163
|
module StringExtensions
|
152
164
|
# Returns string with its UTF-8 characters transliterated to ASCII ones. Example:
|
153
165
|
#
|
154
|
-
# "⠋⠗⠁⠝⠉⠑".to_ascii #=> "
|
166
|
+
# "⠋⠗⠁⠝⠉⠑".to_ascii #=> "france"
|
155
167
|
def to_ascii
|
156
168
|
Stringex::Unidecoder.decode(self)
|
157
169
|
end
|
@@ -136,21 +136,21 @@
|
|
136
136
|
- A
|
137
137
|
- ;
|
138
138
|
- E
|
139
|
-
-
|
139
|
+
- H
|
140
140
|
- I
|
141
141
|
- '[?]'
|
142
142
|
- O
|
143
143
|
- '[?]'
|
144
|
-
-
|
144
|
+
- Y
|
145
145
|
- O
|
146
|
-
-
|
146
|
+
- i
|
147
147
|
- A
|
148
148
|
- B
|
149
149
|
- G
|
150
150
|
- D
|
151
151
|
- E
|
152
152
|
- Z
|
153
|
-
-
|
153
|
+
- H
|
154
154
|
- Th
|
155
155
|
- I
|
156
156
|
- K
|
@@ -164,16 +164,16 @@
|
|
164
164
|
- '[?]'
|
165
165
|
- S
|
166
166
|
- T
|
167
|
-
-
|
167
|
+
- Y
|
168
168
|
- Ph
|
169
|
-
-
|
169
|
+
- Ks
|
170
170
|
- Ps
|
171
171
|
- O
|
172
172
|
- I
|
173
|
-
-
|
173
|
+
- Y
|
174
174
|
- a
|
175
175
|
- e
|
176
|
-
-
|
176
|
+
- i
|
177
177
|
- i
|
178
178
|
- u
|
179
179
|
- a
|
@@ -182,14 +182,14 @@
|
|
182
182
|
- d
|
183
183
|
- e
|
184
184
|
- z
|
185
|
-
-
|
185
|
+
- i
|
186
186
|
- th
|
187
187
|
- i
|
188
188
|
- k
|
189
189
|
- l
|
190
190
|
- m
|
191
191
|
- n
|
192
|
-
-
|
192
|
+
- ks
|
193
193
|
- o
|
194
194
|
- p
|
195
195
|
- r
|
@@ -198,7 +198,7 @@
|
|
198
198
|
- t
|
199
199
|
- u
|
200
200
|
- ph
|
201
|
-
-
|
201
|
+
- x
|
202
202
|
- ps
|
203
203
|
- o
|
204
204
|
- i
|
@@ -209,9 +209,9 @@
|
|
209
209
|
- '[?]'
|
210
210
|
- b
|
211
211
|
- th
|
212
|
-
-
|
213
|
-
-
|
214
|
-
-
|
212
|
+
- I
|
213
|
+
- I
|
214
|
+
- I
|
215
215
|
- ph
|
216
216
|
- p
|
217
217
|
- '&'
|
@@ -253,4 +253,4 @@
|
|
253
253
|
- '[?]'
|
254
254
|
- '[?]'
|
255
255
|
- '[?]'
|
256
|
-
- '[?]'
|
256
|
+
- '[?]'
|
data/stringex.gemspec
CHANGED
@@ -4,14 +4,14 @@
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
|
-
s.name =
|
8
|
-
s.version = "1.
|
7
|
+
s.name = "stringex"
|
8
|
+
s.version = "1.5.0"
|
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 =
|
13
|
-
s.description =
|
14
|
-
s.email =
|
12
|
+
s.date = "2012-11-29"
|
13
|
+
s.description = "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
|
+
s.email = "rsl@luckysneaks.com"
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"MIT-LICENSE",
|
17
17
|
"README.rdoc"
|
@@ -207,20 +207,38 @@ Gem::Specification.new do |s|
|
|
207
207
|
"lib/stringex/unidecoder_data/xff.yml",
|
208
208
|
"stringex.gemspec"
|
209
209
|
]
|
210
|
-
s.homepage =
|
210
|
+
s.homepage = "http://github.com/rsl/stringex"
|
211
211
|
s.rdoc_options = ["--main", "README.rdoc", "--charset", "utf-8", "--line-numbers"]
|
212
212
|
s.require_paths = ["lib"]
|
213
|
-
s.rubygems_version =
|
214
|
-
s.summary =
|
213
|
+
s.rubygems_version = "1.8.24"
|
214
|
+
s.summary = "Some [hopefully] useful extensions to Ruby's String class"
|
215
215
|
s.test_files = ["test/acts_as_url_test.rb", "test/string_extensions_test.rb", "test/unidecoder_test.rb"]
|
216
216
|
|
217
217
|
if s.respond_to? :specification_version then
|
218
218
|
s.specification_version = 3
|
219
219
|
|
220
220
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
221
|
+
s.add_runtime_dependency(%q<stringex>, [">= 0"])
|
222
|
+
s.add_development_dependency(%q<activerecord>, [">= 0"])
|
223
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
224
|
+
s.add_development_dependency(%q<RedCloth>, [">= 0"])
|
225
|
+
s.add_development_dependency(%q<sqlite3>, [">= 0"])
|
226
|
+
s.add_development_dependency(%q<travis-lint>, [">= 0"])
|
221
227
|
else
|
228
|
+
s.add_dependency(%q<stringex>, [">= 0"])
|
229
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
230
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
231
|
+
s.add_dependency(%q<RedCloth>, [">= 0"])
|
232
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
233
|
+
s.add_dependency(%q<travis-lint>, [">= 0"])
|
222
234
|
end
|
223
235
|
else
|
236
|
+
s.add_dependency(%q<stringex>, [">= 0"])
|
237
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
238
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
239
|
+
s.add_dependency(%q<RedCloth>, [">= 0"])
|
240
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
241
|
+
s.add_dependency(%q<travis-lint>, [">= 0"])
|
224
242
|
end
|
225
243
|
end
|
226
244
|
|
data/test/acts_as_url_test.rb
CHANGED
@@ -49,7 +49,7 @@ 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
|
-
|
52
|
+
|
53
53
|
create_table :ununiquments, :force => true do |t|
|
54
54
|
t.string :title, :url, :other
|
55
55
|
end
|
@@ -57,6 +57,10 @@ ActiveRecord::Schema.define(:version => 1) do
|
|
57
57
|
create_table :limituments, :force => true do |t|
|
58
58
|
t.string :title, :url, :other
|
59
59
|
end
|
60
|
+
|
61
|
+
create_table :skipuments, :force => true do |t|
|
62
|
+
t.string :title, :url, :other
|
63
|
+
end
|
60
64
|
end
|
61
65
|
ActiveRecord::Migration.verbose = true
|
62
66
|
|
@@ -105,6 +109,10 @@ class Limitument < ActiveRecord::Base
|
|
105
109
|
acts_as_url :title, :limit => 13
|
106
110
|
end
|
107
111
|
|
112
|
+
class Skipument < ActiveRecord::Base
|
113
|
+
acts_as_url :title, :exclude => ["_So_Fucking_Special"]
|
114
|
+
end
|
115
|
+
|
108
116
|
class ActsAsUrlTest < Test::Unit::TestCase
|
109
117
|
def test_should_create_url
|
110
118
|
@doc = Document.create(:title => "Let's Make a Test Title, <em>Okay</em>?")
|
@@ -116,7 +124,7 @@ class ActsAsUrlTest < Test::Unit::TestCase
|
|
116
124
|
@other_doc = Document.create!(:title => "Unique")
|
117
125
|
assert_equal "unique-1", @other_doc.url
|
118
126
|
end
|
119
|
-
|
127
|
+
|
120
128
|
def test_should_not_create_unique_url
|
121
129
|
@doc = Ununiqument.create!(:title => "I am not a clone")
|
122
130
|
@other_doc = Ununiqument.create!(:title => "I am not a clone")
|
@@ -244,7 +252,21 @@ class ActsAsUrlTest < Test::Unit::TestCase
|
|
244
252
|
end
|
245
253
|
|
246
254
|
def test_should_allow_url_limit
|
247
|
-
@doc = Limitument.create(:title => "I am much too long")
|
255
|
+
@doc = Limitument.create!(:title => "I am much too long")
|
248
256
|
assert_equal "i-am-much-too", @doc.url
|
249
257
|
end
|
258
|
+
|
259
|
+
def test_should_handle_duplicate_urls_with_limits
|
260
|
+
@doc = Limitument.create!(:title => "I am long but not unique")
|
261
|
+
assert_equal "i-am-long-but", @doc.url
|
262
|
+
@doc_2 = Limitument.create!(:title => "I am long but not unique")
|
263
|
+
assert_equal "i-am-long-but-1", @doc_2.url
|
264
|
+
end
|
265
|
+
|
266
|
+
def test_should_allow_exclusions
|
267
|
+
@doc = Skipument.create!(:title => "_So_Fucking_Special")
|
268
|
+
assert_equal "_So_Fucking_Special", @doc.url
|
269
|
+
@doc_2 = Skipument.create!(:title => "But I'm a creep")
|
270
|
+
assert_equal "but-im-a-creep", @doc_2.url
|
271
|
+
end
|
250
272
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
+
$KCODE = "U"
|
2
3
|
|
3
4
|
require "test/unit"
|
4
5
|
|
@@ -53,27 +54,55 @@ class StringExtensionsTest < Test::Unit::TestCase
|
|
53
54
|
"period-dot-period",
|
54
55
|
"Will…This Work?" =>
|
55
56
|
"will-dot-dot-dot-this-work",
|
57
|
+
"We check the D.N.A for matches" =>
|
58
|
+
"we-check-the-dna-for-matches",
|
59
|
+
"We check the D.N.A. for matches" =>
|
60
|
+
"we-check-the-dna-for-matches",
|
61
|
+
"Go to the Y.M.C.America" =>
|
62
|
+
"go-to-the-ymcamerica",
|
63
|
+
"He shops at J.C. Penney" =>
|
64
|
+
"he-shops-at-jc-penney",
|
56
65
|
"¼ pound with cheese" =>
|
57
66
|
"one-fourth-pound-with-cheese",
|
58
67
|
"Will's Ferrel" =>
|
59
68
|
"wills-ferrel",
|
60
|
-
"Капитал" =>
|
69
|
+
"Капитал" =>
|
61
70
|
"kapital",
|
62
|
-
"Ελλάδα" =>
|
71
|
+
"Ελλάδα" =>
|
63
72
|
"ellada",
|
64
|
-
"中文" =>
|
73
|
+
"中文" =>
|
65
74
|
"zhong-wen",
|
66
|
-
"Paul Cézanne" =>
|
75
|
+
"Paul Cézanne" =>
|
67
76
|
"paul-cezanne",
|
68
|
-
"21'17ʼ51" =>
|
77
|
+
"21'17ʼ51" =>
|
69
78
|
"21-17-51",
|
70
|
-
"
|
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"
|
79
|
+
"ITCZ÷1 (21°17ʼ51.78”N / 89°35ʼ28.18”O / 26-04-08 / 09:00 am)" =>
|
80
|
+
"itcz-divide-1-21-degrees-17-51-dot-78-n-slash-89-degrees-35-28-dot-18-o-slash-26-04-08-slash-09-00-am",
|
81
|
+
"/" =>
|
82
|
+
"slash",
|
83
|
+
"私はガラスを食べられます。それは私を傷つけません。" =>
|
84
|
+
"si-hagarasuwoshi-beraremasu-sorehasi-woshang-tukemasen",
|
85
|
+
"ǝ is a magical string" =>
|
86
|
+
"at-is-a-magical-string",
|
87
|
+
"either | or" =>
|
88
|
+
"either-or",
|
89
|
+
"La Maison d`Uliva" =>
|
90
|
+
"la-maison-duliva",
|
91
|
+
"カッページ・テラスに日系カフェ&バー、店内にDJブースも - シンガポール経済新聞" =>
|
92
|
+
"katupeziterasuniri-xi-kahue-and-ba-dian-nei-nidjbusumo-singaporujing-ji-xin-wen",
|
93
|
+
"AVアンプ、ホームシアターシステム、スピーカーシステム等" =>
|
94
|
+
"avanpu-homusiatasisutemu-supikasisutemudeng",
|
95
|
+
"У лукоморья дуб зеленый" =>
|
96
|
+
"u-lukomoria-dub-zielienyi"
|
72
97
|
}.each do |html, plain|
|
73
98
|
assert_equal plain, html.to_url
|
74
99
|
end
|
75
100
|
end
|
76
101
|
|
102
|
+
def test_to_url_with_excludes
|
103
|
+
assert_equal "So Fucking Special", "So Fucking Special".to_url(:exclude => "So Fucking Special")
|
104
|
+
end
|
105
|
+
|
77
106
|
def test_remove_formatting
|
78
107
|
{
|
79
108
|
"<p>This has 100% too much <em>formatting</em></p>" =>
|
data/test/unidecoder_test.rb
CHANGED
@@ -48,7 +48,9 @@ class UnidecoderTest < Test::Unit::TestCase
|
|
48
48
|
"我能吞下玻璃而不伤身体。" => # Chinese
|
49
49
|
"Wo Neng Tun Xia Bo Li Er Bu Shang Shen Ti . ",
|
50
50
|
"私はガラスを食べられます。それは私を傷つけません。" => # Japanese
|
51
|
-
"Si hagarasuwoShi beraremasu. sorehaSi woShang tukemasen. "
|
51
|
+
"Si hagarasuwoShi beraremasu. sorehaSi woShang tukemasen. ",
|
52
|
+
"⠋⠗⠁⠝⠉⠑" => # Braille
|
53
|
+
"france"
|
52
54
|
}
|
53
55
|
|
54
56
|
def test_unidecoder_decode
|
@@ -80,6 +82,17 @@ class UnidecoderTest < Test::Unit::TestCase
|
|
80
82
|
end
|
81
83
|
end
|
82
84
|
|
85
|
+
def test_unidecoder_get_codepoint
|
86
|
+
{
|
87
|
+
# Strings
|
88
|
+
"A" => "0041",
|
89
|
+
"æ" => "00e6",
|
90
|
+
"Я" => "042f"
|
91
|
+
}.each do |unicode, codepoint|
|
92
|
+
assert_equal codepoint, Stringex::Unidecoder.get_codepoint(unicode)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
83
96
|
def test_unidecoder_in_yaml_file
|
84
97
|
{
|
85
98
|
"A" => "x00.yml (line 67)",
|
metadata
CHANGED
@@ -1,34 +1,123 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: stringex
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.5.0
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 4
|
9
|
-
- 0
|
10
|
-
version: 1.4.0
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Russell Norris
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
12
|
+
date: 2012-11-29 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: stringex
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: activerecord
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: jeweler
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: RedCloth
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: sqlite3
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: travis-lint
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
description: ! 'Some [hopefully] useful extensions to Ruby''s String class. Stringex
|
111
|
+
is made up of three libraries: ActsAsUrl [permalink solution with better character
|
112
|
+
translation], Unidecoder [Unicode to ASCII transliteration], and StringExtensions
|
113
|
+
[miscellaneous helper methods for the String class].'
|
23
114
|
email: rsl@luckysneaks.com
|
24
115
|
executables: []
|
25
|
-
|
26
116
|
extensions: []
|
27
|
-
|
28
|
-
extra_rdoc_files:
|
117
|
+
extra_rdoc_files:
|
29
118
|
- MIT-LICENSE
|
30
119
|
- README.rdoc
|
31
|
-
files:
|
120
|
+
files:
|
32
121
|
- MIT-LICENSE
|
33
122
|
- README.rdoc
|
34
123
|
- Rakefile
|
@@ -221,45 +310,39 @@ files:
|
|
221
310
|
- test/acts_as_url_test.rb
|
222
311
|
- test/string_extensions_test.rb
|
223
312
|
- test/unidecoder_test.rb
|
224
|
-
has_rdoc: true
|
225
313
|
homepage: http://github.com/rsl/stringex
|
226
314
|
licenses: []
|
227
|
-
|
228
315
|
post_install_message:
|
229
|
-
rdoc_options:
|
316
|
+
rdoc_options:
|
230
317
|
- --main
|
231
318
|
- README.rdoc
|
232
319
|
- --charset
|
233
320
|
- utf-8
|
234
321
|
- --line-numbers
|
235
|
-
require_paths:
|
322
|
+
require_paths:
|
236
323
|
- lib
|
237
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
324
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
238
325
|
none: false
|
239
|
-
requirements:
|
240
|
-
- -
|
241
|
-
- !ruby/object:Gem::Version
|
242
|
-
|
243
|
-
segments:
|
326
|
+
requirements:
|
327
|
+
- - ! '>='
|
328
|
+
- !ruby/object:Gem::Version
|
329
|
+
version: '0'
|
330
|
+
segments:
|
244
331
|
- 0
|
245
|
-
|
246
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
332
|
+
hash: -2172347514117989172
|
333
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
247
334
|
none: false
|
248
|
-
requirements:
|
249
|
-
- -
|
250
|
-
- !ruby/object:Gem::Version
|
251
|
-
|
252
|
-
segments:
|
253
|
-
- 0
|
254
|
-
version: "0"
|
335
|
+
requirements:
|
336
|
+
- - ! '>='
|
337
|
+
- !ruby/object:Gem::Version
|
338
|
+
version: '0'
|
255
339
|
requirements: []
|
256
|
-
|
257
340
|
rubyforge_project:
|
258
|
-
rubygems_version: 1.
|
341
|
+
rubygems_version: 1.8.24
|
259
342
|
signing_key:
|
260
343
|
specification_version: 3
|
261
344
|
summary: Some [hopefully] useful extensions to Ruby's String class
|
262
|
-
test_files:
|
345
|
+
test_files:
|
263
346
|
- test/acts_as_url_test.rb
|
264
347
|
- test/string_extensions_test.rb
|
265
348
|
- test/unidecoder_test.rb
|