stringex 1.4.0 → 1.5.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.
- 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
|