globalize 3.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 +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +87 -0
- data/LICENSE +21 -0
- data/Rakefile +22 -0
- data/lib/globalize.rb +80 -0
- data/lib/globalize/active_record.rb +12 -0
- data/lib/globalize/active_record/act_macro.rb +96 -0
- data/lib/globalize/active_record/adapter.rb +111 -0
- data/lib/globalize/active_record/attributes.rb +27 -0
- data/lib/globalize/active_record/class_methods.rb +173 -0
- data/lib/globalize/active_record/exceptions.rb +19 -0
- data/lib/globalize/active_record/instance_methods.rb +224 -0
- data/lib/globalize/active_record/migration.rb +191 -0
- data/lib/globalize/active_record/translation.rb +46 -0
- data/lib/globalize/interpolation.rb +28 -0
- data/lib/globalize/version.rb +3 -0
- data/lib/globalize/versioning.rb +5 -0
- data/lib/globalize/versioning/paper_trail.rb +41 -0
- data/lib/i18n/missing_translations_log_handler.rb +41 -0
- data/lib/i18n/missing_translations_raise_handler.rb +25 -0
- data/lib/patches/active_record/query_method.rb +35 -0
- data/lib/patches/active_record/relation.rb +15 -0
- data/lib/patches/active_record/uniqueness_validator.rb +55 -0
- data/lib/patches/active_record/xml_attribute_serializer.rb +13 -0
- metadata +186 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7415d0b0223d5ace130c535aaee60e36929a4c7d
|
4
|
+
data.tar.gz: b4b6553f95f6f662aab9036c96b66ad0d7cac875
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 79141074b2c0943e9af5d02a1cb2c44bee1394f061398fc338d762891799b0d952ebe3e1a0ae25911759e00afb66c52227f20cac4907fe6af472f82d39a21307
|
7
|
+
data.tar.gz: e4d92686bcbd19736730327ef01f478d179fa0c60229dc4b17e038e274e66216f30e69845d6b29d409c091a36ebe4bb29eca08dfb2b00fc1672ae5adf36b34d6
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
globalize (3.0.0)
|
5
|
+
activemodel (>= 3.0.0)
|
6
|
+
activerecord (>= 3.0.0)
|
7
|
+
paper_trail (~> 2)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
actionpack (3.2.15)
|
13
|
+
activemodel (= 3.2.15)
|
14
|
+
activesupport (= 3.2.15)
|
15
|
+
builder (~> 3.0.0)
|
16
|
+
erubis (~> 2.7.0)
|
17
|
+
journey (~> 1.0.4)
|
18
|
+
rack (~> 1.4.5)
|
19
|
+
rack-cache (~> 1.2)
|
20
|
+
rack-test (~> 0.6.1)
|
21
|
+
sprockets (~> 2.2.1)
|
22
|
+
activemodel (3.2.15)
|
23
|
+
activesupport (= 3.2.15)
|
24
|
+
builder (~> 3.0.0)
|
25
|
+
activerecord (3.2.15)
|
26
|
+
activemodel (= 3.2.15)
|
27
|
+
activesupport (= 3.2.15)
|
28
|
+
arel (~> 3.0.2)
|
29
|
+
tzinfo (~> 0.3.29)
|
30
|
+
activesupport (3.2.15)
|
31
|
+
i18n (~> 0.6, >= 0.6.4)
|
32
|
+
multi_json (~> 1.0)
|
33
|
+
arel (3.0.2)
|
34
|
+
builder (3.0.4)
|
35
|
+
database_cleaner (0.6.7)
|
36
|
+
erubis (2.7.0)
|
37
|
+
hike (1.2.3)
|
38
|
+
i18n (0.6.5)
|
39
|
+
journey (1.0.4)
|
40
|
+
json (1.8.1)
|
41
|
+
metaclass (0.0.1)
|
42
|
+
mocha (0.14.0)
|
43
|
+
metaclass (~> 0.0.1)
|
44
|
+
multi_json (1.8.2)
|
45
|
+
paper_trail (2.7.2)
|
46
|
+
activerecord (~> 3.0)
|
47
|
+
railties (~> 3.0)
|
48
|
+
pathname_local (0.0.2)
|
49
|
+
rack (1.4.5)
|
50
|
+
rack-cache (1.2)
|
51
|
+
rack (>= 0.4)
|
52
|
+
rack-ssl (1.3.3)
|
53
|
+
rack
|
54
|
+
rack-test (0.6.2)
|
55
|
+
rack (>= 1.0)
|
56
|
+
railties (3.2.15)
|
57
|
+
actionpack (= 3.2.15)
|
58
|
+
activesupport (= 3.2.15)
|
59
|
+
rack-ssl (~> 1.3.2)
|
60
|
+
rake (>= 0.8.7)
|
61
|
+
rdoc (~> 3.4)
|
62
|
+
thor (>= 0.14.6, < 2.0)
|
63
|
+
rake (10.1.0)
|
64
|
+
rdoc (3.12.2)
|
65
|
+
json (~> 1.4)
|
66
|
+
sprockets (2.2.2)
|
67
|
+
hike (~> 1.2)
|
68
|
+
multi_json (~> 1.0)
|
69
|
+
rack (~> 1.0)
|
70
|
+
tilt (~> 1.1, != 1.3.0)
|
71
|
+
sqlite3 (1.3.7)
|
72
|
+
test_declarative (0.0.5)
|
73
|
+
thor (0.18.1)
|
74
|
+
tilt (1.4.1)
|
75
|
+
tzinfo (0.3.38)
|
76
|
+
|
77
|
+
PLATFORMS
|
78
|
+
java
|
79
|
+
ruby
|
80
|
+
|
81
|
+
DEPENDENCIES
|
82
|
+
database_cleaner (~> 0.6.0)
|
83
|
+
globalize!
|
84
|
+
mocha
|
85
|
+
pathname_local
|
86
|
+
sqlite3
|
87
|
+
test_declarative
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2008, 2009, 2010 Sven Fuchs, Joshua Harvey, Clemens Kofler, John-Paul Bader
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rdoc/task'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Run all tests.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Generate documentation.'
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'Globalize'
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
20
|
+
rdoc.rdoc_files.include('README')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
data/lib/globalize.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'patches/active_record/xml_attribute_serializer'
|
3
|
+
require 'patches/active_record/query_method'
|
4
|
+
require 'patches/active_record/uniqueness_validator'
|
5
|
+
require 'patches/active_record/relation'
|
6
|
+
|
7
|
+
module Globalize
|
8
|
+
autoload :ActiveRecord, 'globalize/active_record'
|
9
|
+
autoload :Versioning, 'globalize/versioning'
|
10
|
+
autoload :Interpolation, 'globalize/interpolation'
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def locale
|
14
|
+
read_locale || I18n.locale
|
15
|
+
end
|
16
|
+
|
17
|
+
def locale=(locale)
|
18
|
+
set_locale(locale)
|
19
|
+
end
|
20
|
+
|
21
|
+
def with_locale(locale, &block)
|
22
|
+
previous_locale = read_locale
|
23
|
+
begin
|
24
|
+
set_locale(locale)
|
25
|
+
result = yield(locale)
|
26
|
+
ensure
|
27
|
+
set_locale(previous_locale)
|
28
|
+
end
|
29
|
+
result
|
30
|
+
end
|
31
|
+
|
32
|
+
def with_locales(*locales, &block)
|
33
|
+
locales.flatten.map do |locale|
|
34
|
+
with_locale(locale, &block)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def fallbacks=(locales)
|
39
|
+
set_fallbacks(locales)
|
40
|
+
end
|
41
|
+
|
42
|
+
def i18n_fallbacks?
|
43
|
+
I18n.respond_to?(:fallbacks)
|
44
|
+
end
|
45
|
+
|
46
|
+
def fallbacks(for_locale = self.locale)
|
47
|
+
read_fallbacks[for_locale] || default_fallbacks(for_locale)
|
48
|
+
end
|
49
|
+
|
50
|
+
def default_fallbacks(for_locale = self.locale)
|
51
|
+
i18n_fallbacks? ? I18n.fallbacks[for_locale] : [for_locale.to_sym]
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
def read_locale
|
57
|
+
Thread.current[:globalize_locale]
|
58
|
+
end
|
59
|
+
|
60
|
+
def set_locale(locale)
|
61
|
+
Thread.current[:globalize_locale] = locale.try(:to_sym)
|
62
|
+
end
|
63
|
+
|
64
|
+
def read_fallbacks
|
65
|
+
Thread.current[:fallbacks] || HashWithIndifferentAccess.new
|
66
|
+
end
|
67
|
+
|
68
|
+
def set_fallbacks(locales)
|
69
|
+
fallback_hash = HashWithIndifferentAccess.new
|
70
|
+
|
71
|
+
locales.each do |key, value|
|
72
|
+
fallback_hash[key] = value.presence || [key]
|
73
|
+
end if locales.present?
|
74
|
+
|
75
|
+
Thread.current[:fallbacks] = fallback_hash
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
ActiveRecord::Base.extend(Globalize::ActiveRecord::ActMacro)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Globalize
|
2
|
+
module ActiveRecord
|
3
|
+
autoload :ActMacro, 'globalize/active_record/act_macro'
|
4
|
+
autoload :Adapter, 'globalize/active_record/adapter'
|
5
|
+
autoload :Attributes, 'globalize/active_record/attributes'
|
6
|
+
autoload :ClassMethods, 'globalize/active_record/class_methods'
|
7
|
+
autoload :Exceptions, 'globalize/active_record/exceptions'
|
8
|
+
autoload :InstanceMethods, 'globalize/active_record/instance_methods'
|
9
|
+
autoload :Migration, 'globalize/active_record/migration'
|
10
|
+
autoload :Translation, 'globalize/active_record/translation'
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Globalize
|
2
|
+
module ActiveRecord
|
3
|
+
module ActMacro
|
4
|
+
def translates(*attr_names)
|
5
|
+
|
6
|
+
options = attr_names.extract_options!
|
7
|
+
setup_translates!(options) unless translates?
|
8
|
+
|
9
|
+
attr_names = attr_names.map(&:to_sym)
|
10
|
+
attr_names -= translated_attribute_names if defined?(translated_attribute_names)
|
11
|
+
|
12
|
+
if attr_names.present?
|
13
|
+
translation_class.instance_eval %{
|
14
|
+
attr_accessible :#{attr_names.join(', :')}
|
15
|
+
}
|
16
|
+
|
17
|
+
attr_names.each do |attr_name|
|
18
|
+
# Detect and apply serialization.
|
19
|
+
serializer = self.serialized_attributes[attr_name.to_s]
|
20
|
+
if serializer.present?
|
21
|
+
if defined?(::ActiveRecord::Coders::YAMLColumn) &&
|
22
|
+
serializer.is_a?(::ActiveRecord::Coders::YAMLColumn)
|
23
|
+
|
24
|
+
serializer = serializer.object_class
|
25
|
+
end
|
26
|
+
|
27
|
+
translation_class.send :serialize, attr_name, serializer
|
28
|
+
end
|
29
|
+
|
30
|
+
# Create accessors for the attribute.
|
31
|
+
translated_attr_accessor(attr_name)
|
32
|
+
translations_accessor(attr_name)
|
33
|
+
|
34
|
+
# Add attribute to the list.
|
35
|
+
self.translated_attribute_names << attr_name
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def class_name
|
41
|
+
@class_name ||= begin
|
42
|
+
class_name = table_name[table_name_prefix.length..-(table_name_suffix.length + 1)].downcase.camelize
|
43
|
+
pluralize_table_names ? class_name.singularize : class_name
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def translates?
|
48
|
+
included_modules.include?(InstanceMethods)
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
def setup_translates!(options)
|
53
|
+
options[:table_name] ||= "#{table_name.singularize}_translations"
|
54
|
+
options[:foreign_key] ||= class_name.foreign_key
|
55
|
+
|
56
|
+
class_attribute :translated_attribute_names, :translation_options, :fallbacks_for_empty_translations
|
57
|
+
self.translated_attribute_names = []
|
58
|
+
self.translation_options = options
|
59
|
+
self.fallbacks_for_empty_translations = options[:fallbacks_for_empty_translations]
|
60
|
+
|
61
|
+
include InstanceMethods
|
62
|
+
extend ClassMethods, Migration
|
63
|
+
|
64
|
+
translation_class.table_name = options[:table_name]
|
65
|
+
|
66
|
+
has_many :translations, :class_name => translation_class.name,
|
67
|
+
:foreign_key => options[:foreign_key],
|
68
|
+
:dependent => :destroy,
|
69
|
+
:extend => HasManyExtensions,
|
70
|
+
:autosave => false
|
71
|
+
|
72
|
+
after_create :save_translations!
|
73
|
+
after_update :save_translations!
|
74
|
+
|
75
|
+
if options[:versioning]
|
76
|
+
if options[:versioning].is_a?(Hash)
|
77
|
+
translation_class.versioned options[:versioning]
|
78
|
+
else
|
79
|
+
::ActiveRecord::Base.extend(Globalize::Versioning::PaperTrail)
|
80
|
+
|
81
|
+
translation_class.has_paper_trail
|
82
|
+
delegate :version, :versions, :to => :translation
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
translation_class.instance_eval %{ attr_accessible :locale }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
module HasManyExtensions
|
91
|
+
def find_or_initialize_by_locale(locale)
|
92
|
+
with_locale(locale.to_s).first || build(:locale => locale.to_s)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Globalize
|
2
|
+
module ActiveRecord
|
3
|
+
class Adapter
|
4
|
+
# The cache caches attributes that already were looked up for read access.
|
5
|
+
# The stash keeps track of new or changed values that need to be saved.
|
6
|
+
attr_accessor :record, :stash, :translations
|
7
|
+
private :record=, :stash=
|
8
|
+
|
9
|
+
delegate :translation_class, :to => :'record.class'
|
10
|
+
|
11
|
+
def initialize(record)
|
12
|
+
self.record = record
|
13
|
+
self.stash = Attributes.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def fetch_stash(locale, name)
|
17
|
+
value = stash.read(locale, name)
|
18
|
+
return value if value
|
19
|
+
return nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def stash_contains?(locale, name)
|
23
|
+
stash.contains?(locale, name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def fetch(locale, name)
|
27
|
+
record.globalize_fallbacks(locale).each do |fallback|
|
28
|
+
value = stash.contains?(fallback, name) ? fetch_stash(fallback, name) : fetch_attribute(fallback, name)
|
29
|
+
|
30
|
+
unless fallbacks_for?(value)
|
31
|
+
set_metadata(value, :locale => fallback, :requested_locale => locale)
|
32
|
+
return value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
return nil
|
37
|
+
end
|
38
|
+
|
39
|
+
def write(locale, name, value)
|
40
|
+
stash.write(locale, name, value)
|
41
|
+
end
|
42
|
+
|
43
|
+
def save_translations!
|
44
|
+
existing_translations_by_locale = {}
|
45
|
+
record.translations.each do |t|
|
46
|
+
existing_translations_by_locale[t.locale.to_s] = t
|
47
|
+
end
|
48
|
+
|
49
|
+
stash.each do |locale, attrs|
|
50
|
+
if attrs.any?
|
51
|
+
locale_str = locale.to_s
|
52
|
+
translation = existing_translations_by_locale[locale_str] ||
|
53
|
+
record.translations.find_or_initialize_by_locale(locale_str)
|
54
|
+
attrs.each { |name, value| translation[name] = value }
|
55
|
+
translation.save!
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
reset
|
60
|
+
end
|
61
|
+
|
62
|
+
def reset
|
63
|
+
stash.clear
|
64
|
+
end
|
65
|
+
|
66
|
+
protected
|
67
|
+
|
68
|
+
def type_cast(name, value)
|
69
|
+
if value.nil?
|
70
|
+
nil
|
71
|
+
elsif column = column_for_attribute(name)
|
72
|
+
column.type_cast(value)
|
73
|
+
else
|
74
|
+
value
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def column_for_attribute(name)
|
79
|
+
translation_class.columns_hash[name.to_s]
|
80
|
+
end
|
81
|
+
|
82
|
+
def unserializable_attribute?(name, column)
|
83
|
+
column.text? && translation_class.serialized_attributes[name.to_s]
|
84
|
+
end
|
85
|
+
|
86
|
+
def fetch_attribute(locale, name)
|
87
|
+
translation = record.translation_for(locale, false)
|
88
|
+
return translation && translation.send(name)
|
89
|
+
end
|
90
|
+
|
91
|
+
def set_metadata(object, metadata)
|
92
|
+
object.translation_metadata.merge!(metadata) if object.respond_to?(:translation_metadata)
|
93
|
+
object
|
94
|
+
end
|
95
|
+
|
96
|
+
def translation_metadata_accessor(object)
|
97
|
+
return if obj.respond_to?(:translation_metadata)
|
98
|
+
class << object; attr_accessor :translation_metadata end
|
99
|
+
object.translation_metadata ||= {}
|
100
|
+
end
|
101
|
+
|
102
|
+
def fallbacks_for?(object)
|
103
|
+
object.nil? || (fallbacks_for_empty_translations? && object.blank?)
|
104
|
+
end
|
105
|
+
|
106
|
+
def fallbacks_for_empty_translations?
|
107
|
+
record.fallbacks_for_empty_translations
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|