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