stringex 1.5.1 → 2.0.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.
- checksums.yaml +15 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +74 -0
- data/README.rdoc +22 -1
- data/Rakefile +46 -223
- data/VERSION +1 -0
- data/init.rb +1 -0
- data/lib/stringex.rb +11 -3
- data/lib/stringex/acts_as_url.rb +49 -97
- data/lib/stringex/acts_as_url/adapter.rb +26 -0
- data/lib/stringex/acts_as_url/adapter/active_record.rb +23 -0
- data/lib/stringex/acts_as_url/adapter/base.rb +188 -0
- data/lib/stringex/acts_as_url/adapter/data_mapper.rb +67 -0
- data/lib/stringex/acts_as_url/adapter/mongoid.rb +36 -0
- data/lib/stringex/configuration.rb +4 -0
- data/lib/stringex/configuration/acts_as_url.rb +44 -0
- data/lib/stringex/configuration/base.rb +58 -0
- data/lib/stringex/configuration/configurator.rb +25 -0
- data/lib/stringex/configuration/string_extensions.rb +19 -0
- data/lib/stringex/localization.rb +98 -0
- data/lib/stringex/localization/backend/i18n.rb +53 -0
- data/lib/stringex/localization/backend/internal.rb +51 -0
- data/lib/stringex/localization/conversion_expressions.rb +148 -0
- data/lib/stringex/localization/converter.rb +121 -0
- data/lib/stringex/localization/default_conversions.rb +88 -0
- data/lib/stringex/rails/railtie.rb +10 -0
- data/lib/stringex/string_extensions.rb +153 -208
- data/lib/stringex/unidecoder.rb +6 -101
- data/lib/stringex/unidecoder_data/x00.yml +1 -1
- data/lib/stringex/unidecoder_data/x02.yml +5 -5
- data/lib/stringex/unidecoder_data/x05.yml +1 -1
- data/lib/stringex/unidecoder_data/x06.yml +1 -1
- data/lib/stringex/unidecoder_data/x07.yml +3 -3
- data/lib/stringex/unidecoder_data/x09.yml +1 -1
- data/lib/stringex/unidecoder_data/x0e.yml +2 -2
- data/lib/stringex/unidecoder_data/x1f.yml +2 -2
- data/lib/stringex/unidecoder_data/x20.yml +1 -1
- data/lib/stringex/unidecoder_data/xfb.yml +1 -1
- data/lib/stringex/unidecoder_data/xff.yml +1 -1
- data/lib/stringex/version.rb +8 -0
- data/locales/da.yml +73 -0
- data/locales/en.yml +66 -0
- data/stringex.gemspec +77 -18
- data/test/acts_as_url/adapter/active_record.rb +72 -0
- data/test/acts_as_url/adapter/data_mapper.rb +82 -0
- data/test/acts_as_url/adapter/mongoid.rb +73 -0
- data/test/acts_as_url_configuration_test.rb +51 -0
- data/test/acts_as_url_integration_test.rb +271 -0
- data/test/localization/da_test.rb +117 -0
- data/test/localization/default_test.rb +113 -0
- data/test/localization/en_test.rb +117 -0
- data/test/localization_test.rb +123 -0
- data/test/redcloth_to_html_test.rb +37 -0
- data/test/string_extensions_test.rb +59 -91
- data/test/test_helper.rb +2 -0
- data/test/unicode_point_suite/basic_greek_test.rb +113 -0
- data/test/unicode_point_suite/basic_latin_test.rb +142 -0
- data/test/unicode_point_suite/codepoint_test_helper.rb +32 -0
- data/test/unidecoder/bad_localization.yml +1 -0
- data/test/unidecoder/localization.yml +4 -0
- data/test/unidecoder_test.rb +3 -5
- metadata +145 -37
- data/test/acts_as_url_test.rb +0 -272
@@ -0,0 +1,67 @@
|
|
1
|
+
module Stringex
|
2
|
+
module ActsAsUrl
|
3
|
+
module Adapter
|
4
|
+
class DataMapper < Base
|
5
|
+
def self.load
|
6
|
+
ensure_loadable
|
7
|
+
orm_class.send :include, Stringex::ActsAsUrl::ActsAsUrlInstanceMethods
|
8
|
+
::DataMapper::Model.send :include, Stringex::ActsAsUrl::ActsAsUrlClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def create_callback
|
14
|
+
if settings.sync_url
|
15
|
+
klass.class_eval do
|
16
|
+
before :save, :ensure_unique_url
|
17
|
+
end
|
18
|
+
else
|
19
|
+
klass.class_eval do
|
20
|
+
before :create, :ensure_unique_url
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def instance_from_db
|
26
|
+
instance.class.get(instance.id)
|
27
|
+
end
|
28
|
+
|
29
|
+
def is_blank?(object)
|
30
|
+
object.nil? || object == '' || object == []
|
31
|
+
end
|
32
|
+
|
33
|
+
def is_new?(object)
|
34
|
+
object.new?
|
35
|
+
end
|
36
|
+
|
37
|
+
def is_present?(object)
|
38
|
+
!is_blank? object
|
39
|
+
end
|
40
|
+
|
41
|
+
def klass_previous_instances(&block)
|
42
|
+
klass.all(:conditions => {settings.url_attribute => nil}).each(&block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def read_attribute(instance, attribute)
|
46
|
+
instance.attributes[attribute]
|
47
|
+
end
|
48
|
+
|
49
|
+
def url_owners
|
50
|
+
@url_owners ||= url_owners_class.all(:conditions => url_owner_conditions)
|
51
|
+
end
|
52
|
+
|
53
|
+
def read_attribute(instance, name)
|
54
|
+
instance.attribute_get name
|
55
|
+
end
|
56
|
+
|
57
|
+
def write_attribute(instance, name, value)
|
58
|
+
instance.attribute_set name, value
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.orm_class
|
62
|
+
::DataMapper::Resource
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Stringex
|
2
|
+
module ActsAsUrl
|
3
|
+
module Adapter
|
4
|
+
class Mongoid < Base
|
5
|
+
def self.load
|
6
|
+
ensure_loadable
|
7
|
+
orm_class.send :extend, Stringex::ActsAsUrl::ActsAsUrlClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def add_new_record_url_owner_conditions
|
13
|
+
return if instance.new_record?
|
14
|
+
@url_owner_conditions.merge! :id => {'$ne' => instance.id}
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_scoped_url_owner_conditions
|
18
|
+
return unless settings.scope_for_url
|
19
|
+
@url_owner_conditions.merge! settings.scope_for_url => instance.send(settings.scope_for_url)
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_base_url_owner_conditions
|
23
|
+
@url_owner_conditions = {settings.url_attribute => /^#{Regexp.escape(base_url)}/}
|
24
|
+
end
|
25
|
+
|
26
|
+
def klass_previous_instances(&block)
|
27
|
+
klass.where(settings.url_attribute => nil).to_a.each(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.orm_class
|
31
|
+
::Mongoid::Document
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Stringex
|
2
|
+
module Configuration
|
3
|
+
class ActsAsUrl < Base
|
4
|
+
def initialize(options = {})
|
5
|
+
if options[:scope]
|
6
|
+
options[:scope_for_url] = options.delete(:scope)
|
7
|
+
end
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def string_extensions_settings
|
12
|
+
[
|
13
|
+
:allow_slash,
|
14
|
+
:exclude,
|
15
|
+
:force_downcase,
|
16
|
+
:limit,
|
17
|
+
:replace_whitespace_with
|
18
|
+
].inject(Hash.new){|m, x| m[x] = settings.send(x); m}
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.settings
|
22
|
+
@settings
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def default_settings
|
28
|
+
self.class.default_settings
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.default_settings
|
32
|
+
@default_settings ||= {
|
33
|
+
:allow_duplicates => false,
|
34
|
+
:duplicate_count_separator => "-",
|
35
|
+
:enforce_uniqueness_on_sti_class => false,
|
36
|
+
:only_when_blank => false,
|
37
|
+
:scope_for_url => nil,
|
38
|
+
:sync_url => false,
|
39
|
+
:url_attribute => "url",
|
40
|
+
}.merge(Stringex::Configuration::StringExtensions.new.default_settings)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Stringex
|
2
|
+
module Configuration
|
3
|
+
class Base
|
4
|
+
attr_accessor :settings
|
5
|
+
|
6
|
+
def initialize(local_options = {})
|
7
|
+
current_settings = default_settings.merge(system_wide_customizations)
|
8
|
+
current_settings.merge! local_options
|
9
|
+
|
10
|
+
@settings = OpenStruct.new(current_settings)
|
11
|
+
end
|
12
|
+
|
13
|
+
# NOTE: This does not cache itself so that instance and class can be cached on the adapter
|
14
|
+
# without worrying about thread safety or race conditions
|
15
|
+
def adapter
|
16
|
+
adapter_name = settings.adapter || Stringex::ActsAsUrl::Adapter.first_available
|
17
|
+
case adapter_name
|
18
|
+
when Class
|
19
|
+
adapter_name.send :new, self
|
20
|
+
when :active_record
|
21
|
+
Stringex::ActsAsUrl::Adapter::ActiveRecord.new self
|
22
|
+
when :mongoid
|
23
|
+
Stringex::ActsAsUrl::Adapter::Mongoid.new self
|
24
|
+
else
|
25
|
+
raise ArgumentError, "#{adapter_name} is not a defined ActsAsUrl adapter. Please feel free to implement your own and submit it back upstream."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.configure(&block)
|
30
|
+
configurator = Stringex::Configuration::Configurator.new(self)
|
31
|
+
yield configurator
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.system_wide_customizations
|
35
|
+
@system_wide_customizations ||= {}
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.unconfigure!
|
39
|
+
@system_wide_customizations = {}
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def default_settings
|
45
|
+
raise ArgumentError, "You shouldn't have hit default_settings on Stringex::Configuration::Base. Check your code."
|
46
|
+
end
|
47
|
+
|
48
|
+
def system_wide_customizations
|
49
|
+
self.class.system_wide_customizations
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.valid_configuration_details
|
53
|
+
default_settings.keys
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Stringex
|
2
|
+
module Configuration
|
3
|
+
class Configurator
|
4
|
+
attr_accessor :klass
|
5
|
+
|
6
|
+
def initialize(klass)
|
7
|
+
@klass = klass
|
8
|
+
|
9
|
+
self.klass.valid_configuration_details.each do |name|
|
10
|
+
define_instance_method_for_configuration_wrapper name
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def define_instance_method_for_configuration_wrapper(name)
|
15
|
+
name = name.respond_to?(:intern) ? name.intern : name
|
16
|
+
(class << self; self; end).instance_eval do
|
17
|
+
define_method("#{name}=") do |value|
|
18
|
+
customizations = klass.send(:system_wide_customizations)
|
19
|
+
customizations[name] = value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Stringex
|
2
|
+
module Configuration
|
3
|
+
class StringExtensions < Base
|
4
|
+
def default_settings
|
5
|
+
self.class.default_settings
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.default_settings
|
9
|
+
@default_settings ||= {
|
10
|
+
:allow_slash => false,
|
11
|
+
:exclude => [],
|
12
|
+
:force_downcase => true,
|
13
|
+
:limit => nil,
|
14
|
+
:replace_whitespace_with => "-"
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'stringex/localization/converter'
|
4
|
+
require 'stringex/localization/default_conversions'
|
5
|
+
require 'stringex/localization/backend/internal'
|
6
|
+
require 'stringex/localization/backend/i18n'
|
7
|
+
|
8
|
+
module Stringex
|
9
|
+
module Localization
|
10
|
+
include DefaultConversions
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def backend
|
14
|
+
@backend ||= defined?(I18n) ? Backend::I18n : Backend::Internal
|
15
|
+
end
|
16
|
+
|
17
|
+
def backend=(sym_or_class)
|
18
|
+
if sym_or_class.is_a?(Symbol)
|
19
|
+
@backend = case sym_or_class
|
20
|
+
when :internal then Backend::Internal
|
21
|
+
when :i18n then Backend::I18n
|
22
|
+
else raise "Invalid backend :#{sym_or_class}"
|
23
|
+
end
|
24
|
+
else
|
25
|
+
@backend = sym_or_class
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def store_translations(locale, scope, data)
|
30
|
+
backend.store_translations(locale, scope, data)
|
31
|
+
end
|
32
|
+
|
33
|
+
def translate(scope, key, options = {})
|
34
|
+
return if key == "." # I18n doesn't support dots as translation keys so we don't either
|
35
|
+
|
36
|
+
locale = options[:locale] || self.locale
|
37
|
+
|
38
|
+
translation = initial_translation(scope, key, locale)
|
39
|
+
|
40
|
+
return translation unless translation.nil?
|
41
|
+
|
42
|
+
if locale != default_locale
|
43
|
+
translate scope, key, options.merge(:locale => default_locale)
|
44
|
+
else
|
45
|
+
default_conversion(scope, key) || options[:default]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def locale
|
50
|
+
backend.locale
|
51
|
+
end
|
52
|
+
|
53
|
+
def locale=(new_locale)
|
54
|
+
backend.locale = new_locale
|
55
|
+
end
|
56
|
+
|
57
|
+
def default_locale
|
58
|
+
backend.default_locale
|
59
|
+
end
|
60
|
+
|
61
|
+
def default_locale=(new_locale)
|
62
|
+
backend.default_locale = new_locale
|
63
|
+
end
|
64
|
+
|
65
|
+
def with_locale(new_locale, &block)
|
66
|
+
new_locale = default_locale if new_locale == :default
|
67
|
+
backend.with_locale new_locale, &block
|
68
|
+
end
|
69
|
+
|
70
|
+
def with_default_locale(&block)
|
71
|
+
with_locale default_locale, &block
|
72
|
+
end
|
73
|
+
|
74
|
+
def reset!
|
75
|
+
backend.reset!
|
76
|
+
@backend = nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def convert(string, options = {}, &block)
|
80
|
+
converter = Converter.new(string)
|
81
|
+
converter.instance_exec &block
|
82
|
+
converter.smart_strip!
|
83
|
+
converter.string
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def initial_translation(scope, key, locale)
|
89
|
+
backend.initial_translation(scope, key, locale)
|
90
|
+
end
|
91
|
+
|
92
|
+
def default_conversion(scope, key)
|
93
|
+
return unless DefaultConversions.respond_to?(scope)
|
94
|
+
DefaultConversions.send(scope)[key]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Stringex
|
2
|
+
module Localization
|
3
|
+
module Backend
|
4
|
+
module I18n
|
5
|
+
LOAD_PATH_BASE = File.join(File.expand_path(File.dirname(__FILE__)), '..', '..', '..', '..', 'locales')
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def locale
|
9
|
+
::I18n.locale
|
10
|
+
end
|
11
|
+
|
12
|
+
def locale=(new_locale)
|
13
|
+
::I18n.locale = new_locale
|
14
|
+
end
|
15
|
+
|
16
|
+
def default_locale
|
17
|
+
::I18n.default_locale
|
18
|
+
end
|
19
|
+
|
20
|
+
def default_locale=(new_locale)
|
21
|
+
::I18n.default_locale = new_locale
|
22
|
+
end
|
23
|
+
|
24
|
+
def with_locale(new_locale, &block)
|
25
|
+
::I18n.with_locale new_locale, &block
|
26
|
+
end
|
27
|
+
|
28
|
+
def store_translations(locale, scope, data)
|
29
|
+
::I18n.backend.store_translations(locale, { :stringex => { scope => data } })
|
30
|
+
end
|
31
|
+
|
32
|
+
def initial_translation(scope, key, locale)
|
33
|
+
# I18n can't return a nil as default as this gets interpreted as if no default
|
34
|
+
# is specified, so we use a string instead.
|
35
|
+
translated = ::I18n.translate(key, :scope => [:stringex, scope], :locale => locale, :default => "__default__")
|
36
|
+
translated == "__default__" ? nil : translated
|
37
|
+
end
|
38
|
+
|
39
|
+
def load_translations(locale = nil)
|
40
|
+
locale ||= ::I18n.locale
|
41
|
+
path = Dir[File.join(LOAD_PATH_BASE, "#{locale}.yml")]
|
42
|
+
::I18n.load_path |= Dir[File.join(LOAD_PATH_BASE, "#{locale}.yml")]
|
43
|
+
::I18n.backend.load_translations
|
44
|
+
end
|
45
|
+
|
46
|
+
def reset!
|
47
|
+
# Can't reset I18n. Needed?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Stringex
|
2
|
+
module Localization
|
3
|
+
module Backend
|
4
|
+
module Internal
|
5
|
+
DEFAULT_LOCALE = :en
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def locale
|
9
|
+
@locale || default_locale
|
10
|
+
end
|
11
|
+
|
12
|
+
def locale=(new_locale)
|
13
|
+
@locale = new_locale.to_sym
|
14
|
+
end
|
15
|
+
|
16
|
+
def default_locale
|
17
|
+
@default_locale || DEFAULT_LOCALE
|
18
|
+
end
|
19
|
+
|
20
|
+
def default_locale=(new_locale)
|
21
|
+
@default_locale = @locale = new_locale.to_sym
|
22
|
+
end
|
23
|
+
|
24
|
+
def with_locale(new_locale, &block)
|
25
|
+
original_locale = locale
|
26
|
+
self.locale = new_locale
|
27
|
+
yield
|
28
|
+
self.locale = original_locale
|
29
|
+
end
|
30
|
+
|
31
|
+
def translations
|
32
|
+
# Set up hash like translations[:en][:transliterations]["é"]
|
33
|
+
@translations ||= Hash.new { |k, v| k[v] = Hash.new({}) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def store_translations(locale, scope, data)
|
37
|
+
self.translations[locale.to_sym][scope.to_sym] = Hash[data.map { |k, v| [k.to_sym, v] }] # Symbolize keys
|
38
|
+
end
|
39
|
+
|
40
|
+
def initial_translation(scope, key, locale)
|
41
|
+
translations[locale][scope][key.to_sym]
|
42
|
+
end
|
43
|
+
|
44
|
+
def reset!
|
45
|
+
@translations = @locale = @default_locale = nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|