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.
@@ -1,4 +1,4 @@
1
- Copyright (c) 2008 Lucky Sneaks
1
+ Copyright (c) 2008-2012 Lucky Sneaks
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -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 'rake/rdoctask'
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 = "1.4.0"
216
+ gem.version = Stringex::Version::STRING
216
217
  end
217
218
 
218
- Jeweler::GemcutterTasks.new
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
@@ -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
- # act_as_url :title
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>:url_attribute</tt>:: The name of the attribute to use for storing the generated url string.
23
- # Default is <tt>:url</tt>
24
- # <tt>:scope</tt>:: The name of model attribute to scope unique urls to. There is no default here.
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 :attribute_to_urlify
31
- cattr_accessor :scope_for_url
32
- cattr_accessor :url_attribute # The attribute on the DB
33
- cattr_accessor :only_when_blank
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 options[:sync_url]
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
- find(:all, :conditions => {self.url_attribute => nil}).each do |instance|
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
- url_attribute = self.class.url_attribute
88
- separator = self.class.duplicate_count_separator
89
- base_url = self.send(url_attribute)
90
- base_url = self.send(self.class.attribute_to_urlify).to_s.to_url(:allow_slash => self.allow_slash, :limit => self.url_limit) if base_url.blank? || !self.only_when_blank
91
- conditions = ["#{url_attribute} LIKE ?", base_url+'%']
92
- unless new_record?
93
- conditions.first << " and id != ?"
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
- def limit(lim = nil)
43
- lim.nil? ? self : self[0...lim]
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.convert_smart_punctuation.convert_accented_entities.convert_vulgar_fractions.convert_misc_entities.convert_misc_characters(options).to_ascii.collapse
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
  # "&aacute;".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*(\\|\/)\s*/] = 'slash' unless options[:allow_slash]
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
@@ -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 = hash.is_a?(Hash) && hash.all?{|key, value|
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 locale=
139
- default_locale default_locale=
147
+ locale
148
+ locale=
149
+ default_locale
150
+ default_locale=
140
151
  local_codepoint
141
- with_locale with_default_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 #=> "braille"
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
- - E
139
+ - H
140
140
  - I
141
141
  - '[?]'
142
142
  - O
143
143
  - '[?]'
144
- - U
144
+ - Y
145
145
  - O
146
- - I
146
+ - i
147
147
  - A
148
148
  - B
149
149
  - G
150
150
  - D
151
151
  - E
152
152
  - Z
153
- - E
153
+ - H
154
154
  - Th
155
155
  - I
156
156
  - K
@@ -164,16 +164,16 @@
164
164
  - '[?]'
165
165
  - S
166
166
  - T
167
- - U
167
+ - Y
168
168
  - Ph
169
- - Kh
169
+ - Ks
170
170
  - Ps
171
171
  - O
172
172
  - I
173
- - U
173
+ - Y
174
174
  - a
175
175
  - e
176
- - e
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
- - e
185
+ - i
186
186
  - th
187
187
  - i
188
188
  - k
189
189
  - l
190
190
  - m
191
191
  - n
192
- - x
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
- - kh
201
+ - x
202
202
  - ps
203
203
  - o
204
204
  - i
@@ -209,9 +209,9 @@
209
209
  - '[?]'
210
210
  - b
211
211
  - th
212
- - U
213
- - U
214
- - U
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
+ - '[?]'
@@ -4,14 +4,14 @@
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = %q{stringex}
8
- s.version = "1.4.0"
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 = %q{2012-05-04}
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
- s.email = %q{rsl@luckysneaks.com}
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 = %q{http://github.com/rsl/stringex}
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 = %q{1.4.2}
214
- s.summary = %q{Some [hopefully] useful extensions to Ruby's String class}
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
 
@@ -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
- "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"
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>" =>
@@ -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
- hash: 7
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
- date: 2012-05-04 00:00:00 -04:00
19
- default_executable:
20
- dependencies: []
21
-
22
- 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]."
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
- hash: 3
243
- segments:
326
+ requirements:
327
+ - - ! '>='
328
+ - !ruby/object:Gem::Version
329
+ version: '0'
330
+ segments:
244
331
  - 0
245
- version: "0"
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
- hash: 3
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.4.2
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