stringex 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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