globalize2 0.1.0 → 0.2.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.
- data/.gitignore +2 -1
- data/README.textile +27 -143
- data/VERSION +1 -1
- data/generators/templates/db_backend_migration.rb +3 -3
- data/globalize2.gemspec +30 -49
- data/init.rb +1 -8
- data/lib/globalize.rb +15 -0
- data/lib/globalize/active_record.rb +195 -0
- data/lib/globalize/active_record/adapter.rb +80 -0
- data/lib/globalize/active_record/attributes.rb +25 -0
- data/lib/globalize/active_record/migration.rb +40 -0
- data/lib/{globalize/i18n → i18n}/missing_translations_log_handler.rb +8 -8
- data/lib/{globalize/i18n → i18n}/missing_translations_raise_handler.rb +3 -5
- data/test/active_record/fallbacks_test.rb +102 -0
- data/test/{model/active_record → active_record}/migration_test.rb +21 -26
- data/test/{model/active_record → active_record}/sti_translated_test.rb +4 -30
- data/test/active_record/translates_test.rb +87 -0
- data/test/active_record/translation_class_test.rb +30 -0
- data/test/active_record/validation_tests.rb +75 -0
- data/test/active_record_test.rb +451 -0
- data/test/data/models.rb +16 -0
- data/test/data/schema.rb +23 -7
- data/test/i18n/missing_translations_test.rb +6 -6
- data/test/test_helper.rb +55 -15
- metadata +33 -46
- data/lib/globalize/backend/chain.rb +0 -102
- data/lib/globalize/backend/pluralizing.rb +0 -37
- data/lib/globalize/backend/static.rb +0 -61
- data/lib/globalize/load_path.rb +0 -63
- data/lib/globalize/locale/fallbacks.rb +0 -63
- data/lib/globalize/locale/language_tag.rb +0 -81
- data/lib/globalize/model/active_record.rb +0 -56
- data/lib/globalize/model/active_record/adapter.rb +0 -100
- data/lib/globalize/model/active_record/translated.rb +0 -174
- data/lib/globalize/translation.rb +0 -32
- data/lib/locale/root.yml +0 -3
- data/lib/rails_edge_load_path_patch.rb +0 -40
- data/notes.textile +0 -51
- data/test/backends/chained_test.rb +0 -175
- data/test/backends/pluralizing_test.rb +0 -63
- data/test/backends/static_test.rb +0 -147
- data/test/data/locale/all.yml +0 -2
- data/test/data/locale/de-DE.yml +0 -2
- data/test/data/locale/en-US.yml +0 -2
- data/test/data/locale/en-US/module.yml +0 -2
- data/test/data/locale/fi-FI/module.yml +0 -2
- data/test/data/locale/root.yml +0 -0
- data/test/load_path_test.rb +0 -49
- data/test/locale/fallbacks_test.rb +0 -154
- data/test/locale/language_tag_test.rb +0 -130
- data/test/model/active_record/translated_test.rb +0 -487
- data/test/translation_test.rb +0 -54
@@ -1,61 +0,0 @@
|
|
1
|
-
require 'globalize/backend/pluralizing'
|
2
|
-
require 'globalize/locale/fallbacks'
|
3
|
-
require 'globalize/translation'
|
4
|
-
|
5
|
-
module Globalize
|
6
|
-
module Backend
|
7
|
-
class Static < Pluralizing
|
8
|
-
def initialize(*args)
|
9
|
-
add(*args) unless args.empty?
|
10
|
-
end
|
11
|
-
|
12
|
-
def translate(locale, key, options = {})
|
13
|
-
result, default, fallback = nil, options.delete(:default), nil
|
14
|
-
I18n.fallbacks[locale].each do |fallback|
|
15
|
-
begin
|
16
|
-
result = super(fallback, key, options) and break
|
17
|
-
rescue I18n::MissingTranslationData
|
18
|
-
end
|
19
|
-
end
|
20
|
-
result ||= default locale, default, options
|
21
|
-
|
22
|
-
attrs = {:requested_locale => locale, :locale => fallback, :key => key, :options => options}
|
23
|
-
translation(result, attrs)
|
24
|
-
# translation(result, attrs) || raise(I18n::MissingTranslationData.new(locale, key, options))
|
25
|
-
end
|
26
|
-
|
27
|
-
protected
|
28
|
-
|
29
|
-
alias :orig_interpolate :interpolate unless method_defined? :orig_interpolate
|
30
|
-
def interpolate(locale, string, values = {})
|
31
|
-
result = orig_interpolate(locale, string, values)
|
32
|
-
translation = translation(string)
|
33
|
-
translation.nil? ? result : translation.replace(result)
|
34
|
-
end
|
35
|
-
|
36
|
-
def translation(result, meta = nil)
|
37
|
-
return unless result
|
38
|
-
|
39
|
-
case result
|
40
|
-
when Numeric
|
41
|
-
result
|
42
|
-
when String
|
43
|
-
result = Translation::Static.new(result) unless result.is_a? Translation::Static
|
44
|
-
result.set_meta meta
|
45
|
-
result
|
46
|
-
when Hash
|
47
|
-
Hash[*result.map do |key, value|
|
48
|
-
[key, translation(value, meta)]
|
49
|
-
end.flatten]
|
50
|
-
when Array
|
51
|
-
result.map do |value|
|
52
|
-
translation(value, meta)
|
53
|
-
end
|
54
|
-
else
|
55
|
-
result
|
56
|
-
# raise "unexpected translation type: #{result.inspect}"
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
data/lib/globalize/load_path.rb
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
# Locale load_path and Locale loading support.
|
2
|
-
#
|
3
|
-
# To use this include the Globalize::LoadPath::I18n module to I18n like this:
|
4
|
-
#
|
5
|
-
# I18n.send :include, Globalize::LoadPath::I18n
|
6
|
-
#
|
7
|
-
# Clients can add load_paths using:
|
8
|
-
#
|
9
|
-
# I18n.load_path.add load_path, 'rb', 'yml' # pass any number of extensions like this
|
10
|
-
# I18n.load_path << 'path/to/dir' # usage without an extension, defaults to 'yml'
|
11
|
-
#
|
12
|
-
# And load locale data using either of:
|
13
|
-
#
|
14
|
-
# I18n.load_locales 'en-US', 'de-DE'
|
15
|
-
# I18n.load_locale 'en-US'
|
16
|
-
#
|
17
|
-
# This will lookup all files named like:
|
18
|
-
#
|
19
|
-
# 'path/to/dir/all.yml'
|
20
|
-
# 'path/to/dir/en-US.yml'
|
21
|
-
# 'path/to/dir/en-US/*.yml'
|
22
|
-
#
|
23
|
-
# The filenames will be passed to I18n.load_translations which delegates to
|
24
|
-
# the backend. So the actual behaviour depends on the implementation of the
|
25
|
-
# backend. I18n::Backend::Simple will be able to read YAML and plain Ruby
|
26
|
-
# files. See the documentation for I18n.load_translations for details.
|
27
|
-
|
28
|
-
module Globalize
|
29
|
-
class LoadPath < Array
|
30
|
-
def extensions
|
31
|
-
@extensions ||= ['rb', 'yml']
|
32
|
-
end
|
33
|
-
attr_writer :extensions
|
34
|
-
|
35
|
-
def locales
|
36
|
-
@locales ||= ['*']
|
37
|
-
end
|
38
|
-
attr_writer :locales
|
39
|
-
|
40
|
-
def <<(path)
|
41
|
-
push path
|
42
|
-
end
|
43
|
-
|
44
|
-
def push(*paths)
|
45
|
-
super(*paths.map{|path| filenames(path) }.flatten.uniq.sort)
|
46
|
-
end
|
47
|
-
|
48
|
-
protected
|
49
|
-
|
50
|
-
def filenames(path)
|
51
|
-
return [path] if File.file? path
|
52
|
-
patterns(path).map{|pattern| Dir[pattern] }
|
53
|
-
end
|
54
|
-
|
55
|
-
def patterns(path)
|
56
|
-
locales.map do |locale|
|
57
|
-
extensions.map do |extension|
|
58
|
-
%W(#{path}/all.#{extension} #{path}/#{locale}.#{extension} #{path}/#{locale}/**/*.#{extension})
|
59
|
-
end
|
60
|
-
end.flatten.uniq
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
require 'globalize/locale/language_tag'
|
2
|
-
|
3
|
-
module I18n
|
4
|
-
@@fallbacks = nil
|
5
|
-
|
6
|
-
class << self
|
7
|
-
# Returns the current fallbacks. Defaults to +Globalize::Locale::Fallbacks+.
|
8
|
-
def fallbacks
|
9
|
-
@@fallbacks ||= Globalize::Locale::Fallbacks.new
|
10
|
-
end
|
11
|
-
|
12
|
-
# Sets the current fallbacks. Used to set a custom fallbacks instance.
|
13
|
-
def fallbacks=(fallbacks)
|
14
|
-
@@fallbacks = fallbacks
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
module Globalize
|
20
|
-
module Locale
|
21
|
-
class Fallbacks < Hash
|
22
|
-
def initialize(*defaults)
|
23
|
-
@map = {}
|
24
|
-
map defaults.pop if defaults.last.is_a?(Hash)
|
25
|
-
|
26
|
-
defaults = [I18n.default_locale.to_sym] if defaults.empty?
|
27
|
-
self.defaults = defaults
|
28
|
-
end
|
29
|
-
|
30
|
-
def defaults=(defaults)
|
31
|
-
@defaults = defaults.map{|default| compute(default, false) }.flatten << :root
|
32
|
-
end
|
33
|
-
attr_reader :defaults
|
34
|
-
|
35
|
-
def [](tag)
|
36
|
-
tag = tag.to_sym
|
37
|
-
has_key?(tag) ? fetch(tag) : store(tag, compute(tag))
|
38
|
-
end
|
39
|
-
|
40
|
-
def map(mappings)
|
41
|
-
mappings.each do |from, to|
|
42
|
-
from, to = from.to_sym, Array(to)
|
43
|
-
to.each do |to|
|
44
|
-
@map[from] ||= []
|
45
|
-
@map[from] << to.to_sym
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
protected
|
51
|
-
|
52
|
-
def compute(tags, include_defaults = true)
|
53
|
-
result = Array(tags).collect do |tag|
|
54
|
-
tags = LanguageTag::tag(tag.to_sym).parents(true).map! {|t| t.to_sym }
|
55
|
-
tags.each{|tag| tags += compute(@map[tag]) if @map[tag] }
|
56
|
-
tags
|
57
|
-
end.flatten
|
58
|
-
result.push *defaults if include_defaults
|
59
|
-
result.uniq
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
@@ -1,81 +0,0 @@
|
|
1
|
-
# for specifications see http://en.wikipedia.org/wiki/IETF_language_tag
|
2
|
-
#
|
3
|
-
# SimpleParser does not implement advanced usages such as grandfathered tags
|
4
|
-
|
5
|
-
module Globalize
|
6
|
-
module Locale
|
7
|
-
module Rfc4646
|
8
|
-
SUBTAGS = [:language, :script, :region, :variant, :extension, :privateuse, :grandfathered]
|
9
|
-
FORMATS = {:language => :downcase, :script => :capitalize, :region => :upcase, :variant => :downcase}
|
10
|
-
end
|
11
|
-
|
12
|
-
class LanguageTag < Struct.new(*Rfc4646::SUBTAGS)
|
13
|
-
class << self
|
14
|
-
def parser
|
15
|
-
@@parser ||= SimpleParser
|
16
|
-
end
|
17
|
-
|
18
|
-
def parser=(parser)
|
19
|
-
@@parser = parser
|
20
|
-
end
|
21
|
-
|
22
|
-
def tag(tag)
|
23
|
-
matches = parser.match(tag)
|
24
|
-
new *matches if matches
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
Rfc4646::FORMATS.each do |name, format|
|
29
|
-
define_method(name) { self[name].send(format) unless self[name].nil? }
|
30
|
-
end
|
31
|
-
|
32
|
-
def to_sym
|
33
|
-
to_s.to_sym
|
34
|
-
end
|
35
|
-
|
36
|
-
def to_s
|
37
|
-
@tag ||= to_a.compact.join("-")
|
38
|
-
end
|
39
|
-
|
40
|
-
def to_a
|
41
|
-
members.collect {|attr| self.send(attr) }
|
42
|
-
end
|
43
|
-
|
44
|
-
def parent
|
45
|
-
segs = to_a.compact
|
46
|
-
segs.length < 2 ? nil : LanguageTag.tag(segs[0..(segs.length-2)].join('-'))
|
47
|
-
end
|
48
|
-
|
49
|
-
def parents(include_self = true)
|
50
|
-
result, parent = [], self.dup
|
51
|
-
result << parent if include_self
|
52
|
-
while parent = parent.parent
|
53
|
-
result << parent
|
54
|
-
end
|
55
|
-
result
|
56
|
-
end
|
57
|
-
|
58
|
-
module SimpleParser
|
59
|
-
PATTERN = %r{\A(?:
|
60
|
-
([a-z]{2,3}(?:(?:-[a-z]{3}){0,3})?|[a-z]{4}|[a-z]{5,8}) # language
|
61
|
-
(?:-([a-z]{4}))? # script
|
62
|
-
(?:-([a-z]{2}|\d{3}))? # region
|
63
|
-
(?:-([0-9a-z]{5,8}|\d[0-9a-z]{3}))* # variant
|
64
|
-
(?:-([0-9a-wyz](?:-[0-9a-z]{2,8})+))* # extension
|
65
|
-
(?:-(x(?:-[0-9a-z]{1,8})+))?| # privateuse subtag
|
66
|
-
(x(?:-[0-9a-z]{1,8})+)| # privateuse tag
|
67
|
-
/* ([a-z]{1,3}(?:-[0-9a-z]{2,8}){1,2}) */ # grandfathered
|
68
|
-
)\z}xi
|
69
|
-
|
70
|
-
class << self
|
71
|
-
def match(tag)
|
72
|
-
c = PATTERN.match(tag.to_s).captures
|
73
|
-
c[0..4] << (c[5].nil? ? c[6] : c[5]) << c[7] # TODO c[7] is grandfathered, throw a NotImplemented exception here?
|
74
|
-
rescue
|
75
|
-
false
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
require 'globalize/translation'
|
2
|
-
require 'globalize/locale/fallbacks'
|
3
|
-
require 'globalize/model/active_record/adapter'
|
4
|
-
require 'globalize/model/active_record/translated'
|
5
|
-
|
6
|
-
module Globalize
|
7
|
-
module Model
|
8
|
-
module ActiveRecord
|
9
|
-
class << self
|
10
|
-
def create_proxy_class(klass)
|
11
|
-
module_names = klass.name.split('::')
|
12
|
-
klass_name = module_names.pop
|
13
|
-
target = module_names.empty? ? Object : module_names.join('::').constantize
|
14
|
-
|
15
|
-
proxy_class_name = "#{klass_name}Translation"
|
16
|
-
proxy_class = nil
|
17
|
-
begin
|
18
|
-
proxy_class = proxy_class_name.constantize
|
19
|
-
rescue NameError
|
20
|
-
proxy_class = target.const_set proxy_class_name, Class.new(::ActiveRecord::Base)
|
21
|
-
end
|
22
|
-
|
23
|
-
proxy_class.instance_eval do
|
24
|
-
belongs_to "#{klass.name.underscore.gsub('/', '_')}".intern
|
25
|
-
end
|
26
|
-
proxy_class.class_eval do
|
27
|
-
def locale
|
28
|
-
read_attribute(:locale).to_sym
|
29
|
-
end
|
30
|
-
|
31
|
-
def locale=(locale)
|
32
|
-
write_attribute(:locale, locale.to_s)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
return proxy_class
|
37
|
-
end
|
38
|
-
|
39
|
-
def define_accessors(klass, attr_names)
|
40
|
-
attr_names.each do |attr_name|
|
41
|
-
klass.send :define_method, attr_name, lambda {
|
42
|
-
globalize.fetch self.class.locale, attr_name
|
43
|
-
}
|
44
|
-
klass.send :define_method, "#{attr_name}_before_type_cast", lambda {
|
45
|
-
globalize.fetch self.class.locale, attr_name
|
46
|
-
}
|
47
|
-
klass.send :define_method, "#{attr_name}=", lambda {|val|
|
48
|
-
globalize.stash self.class.locale, attr_name, val
|
49
|
-
self[attr_name] = val
|
50
|
-
}
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,100 +0,0 @@
|
|
1
|
-
module Globalize
|
2
|
-
module Model
|
3
|
-
class AttributeStash < Hash
|
4
|
-
def contains?(locale, attr_name)
|
5
|
-
locale = locale.to_sym
|
6
|
-
self[locale] ||= {}
|
7
|
-
self[locale].has_key? attr_name
|
8
|
-
end
|
9
|
-
|
10
|
-
def read(locale, attr_name)
|
11
|
-
locale = locale.to_sym
|
12
|
-
self[locale] ||= {}
|
13
|
-
self[locale][attr_name]
|
14
|
-
end
|
15
|
-
|
16
|
-
def write(locale, attr_name, value)
|
17
|
-
locale = locale.to_sym
|
18
|
-
self[locale] ||= {}
|
19
|
-
self[locale][attr_name] = value
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
class Adapter
|
24
|
-
def initialize(record)
|
25
|
-
@record = record
|
26
|
-
|
27
|
-
# TODO what exactly are the roles of cache and stash
|
28
|
-
@cache = AttributeStash.new
|
29
|
-
@stash = AttributeStash.new
|
30
|
-
end
|
31
|
-
|
32
|
-
def fetch(locale, attr_name)
|
33
|
-
# locale = I18n.locale
|
34
|
-
is_cached = @cache.contains?(locale, attr_name)
|
35
|
-
is_cached ? @cache.read(locale, attr_name) : begin
|
36
|
-
value = fetch_attribute locale, attr_name
|
37
|
-
@cache.write locale, attr_name, value if value && value.locale == locale
|
38
|
-
value
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def stash(locale, attr_name, value)
|
43
|
-
@stash.write locale, attr_name, value
|
44
|
-
@cache.write locale, attr_name, value
|
45
|
-
end
|
46
|
-
|
47
|
-
def update_translations!
|
48
|
-
@stash.each do |locale, attrs|
|
49
|
-
translation = @record.globalize_translations.find_or_initialize_by_locale(locale.to_s)
|
50
|
-
attrs.each{|attr_name, value| translation[attr_name] = value }
|
51
|
-
translation.save!
|
52
|
-
end
|
53
|
-
@stash.clear
|
54
|
-
end
|
55
|
-
|
56
|
-
# Clears the cache
|
57
|
-
def clear
|
58
|
-
@cache.clear
|
59
|
-
@stash.clear
|
60
|
-
end
|
61
|
-
|
62
|
-
def clear_cache
|
63
|
-
@cache.clear
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
|
68
|
-
def fetch_attribute(locale, attr_name)
|
69
|
-
fallbacks = I18n.fallbacks[locale].map{|tag| tag.to_s}.map(&:to_sym)
|
70
|
-
|
71
|
-
# If the translations were included with
|
72
|
-
# :include => globalize_translations
|
73
|
-
# there is no need to query them again.
|
74
|
-
unless @record.globalize_translations.loaded?
|
75
|
-
translations = @record.globalize_translations.by_locales(fallbacks)
|
76
|
-
else
|
77
|
-
translations = @record.globalize_translations
|
78
|
-
end
|
79
|
-
result, requested_locale = nil, locale
|
80
|
-
|
81
|
-
# Walk through the fallbacks, starting with the current locale itself, and moving
|
82
|
-
# to the next best choice, until we find a match.
|
83
|
-
# Check the @globalize_set_translations cache first to see if we've just changed the
|
84
|
-
# attribute and not saved yet.
|
85
|
-
fallbacks.each do |fallback|
|
86
|
-
# TODO should we be checking stash or just cache?
|
87
|
-
result = @cache.read(fallback, attr_name) || begin
|
88
|
-
translation = translations.detect {|tr| tr.locale == fallback }
|
89
|
-
translation && translation.send(attr_name)
|
90
|
-
end
|
91
|
-
if result
|
92
|
-
locale = fallback
|
93
|
-
break
|
94
|
-
end
|
95
|
-
end
|
96
|
-
result && Translation::Attribute.new(result, :locale => locale, :requested_locale => requested_locale)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
@@ -1,174 +0,0 @@
|
|
1
|
-
require 'digest/sha1'
|
2
|
-
|
3
|
-
module Globalize
|
4
|
-
module Model
|
5
|
-
class MigrationError < StandardError; end
|
6
|
-
class MigrationMissingTranslatedField < MigrationError; end
|
7
|
-
class BadMigrationFieldType < MigrationError; end
|
8
|
-
|
9
|
-
module ActiveRecord
|
10
|
-
module Translated
|
11
|
-
def self.included(base)
|
12
|
-
base.extend ActMethods
|
13
|
-
end
|
14
|
-
|
15
|
-
module ActMethods
|
16
|
-
def translates(*attr_names)
|
17
|
-
options = attr_names.extract_options!
|
18
|
-
options[:translated_attributes] = attr_names
|
19
|
-
|
20
|
-
# Only set up once per class
|
21
|
-
unless included_modules.include? InstanceMethods
|
22
|
-
class_inheritable_accessor :globalize_options, :globalize_proxy
|
23
|
-
|
24
|
-
include InstanceMethods
|
25
|
-
extend ClassMethods
|
26
|
-
|
27
|
-
self.globalize_proxy = Globalize::Model::ActiveRecord.create_proxy_class(self)
|
28
|
-
has_many(
|
29
|
-
:globalize_translations,
|
30
|
-
:class_name => globalize_proxy.name,
|
31
|
-
:extend => Extensions,
|
32
|
-
:dependent => :delete_all,
|
33
|
-
:foreign_key => class_name.foreign_key
|
34
|
-
)
|
35
|
-
|
36
|
-
after_save :update_globalize_record
|
37
|
-
end
|
38
|
-
|
39
|
-
self.globalize_options = options
|
40
|
-
Globalize::Model::ActiveRecord.define_accessors(self, attr_names)
|
41
|
-
|
42
|
-
# Import any callbacks that have been defined by extensions to Globalize2
|
43
|
-
# and run them.
|
44
|
-
extend Callbacks
|
45
|
-
Callbacks.instance_methods.each { |callback| send(callback) }
|
46
|
-
end
|
47
|
-
|
48
|
-
def locale=(locale)
|
49
|
-
@@locale = locale
|
50
|
-
end
|
51
|
-
|
52
|
-
def locale
|
53
|
-
(defined?(@@locale) && @@locale) || I18n.locale
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
# Dummy Callbacks module. Extensions to Globalize2 can insert methods into here
|
58
|
-
# and they'll be called at the end of the translates class method.
|
59
|
-
module Callbacks
|
60
|
-
end
|
61
|
-
|
62
|
-
# Extension to the has_many :globalize_translations association
|
63
|
-
module Extensions
|
64
|
-
def by_locales(locales)
|
65
|
-
find :all, :conditions => { :locale => locales.map(&:to_s) }
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
module ClassMethods
|
70
|
-
def method_missing(method, *args)
|
71
|
-
if method.to_s =~ /^find_by_(\w+)$/ && globalize_options[:translated_attributes].include?($1.to_sym)
|
72
|
-
find(:first, :joins => :globalize_translations,
|
73
|
-
:conditions => [ "#{i18n_attr($1)} = ? AND #{i18n_attr('locale')} IN (?)",
|
74
|
-
args.first,I18n.fallbacks[I18n.locale].map{|tag| tag.to_s}])
|
75
|
-
else
|
76
|
-
super
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def create_translation_table!(fields)
|
81
|
-
translated_fields = self.globalize_options[:translated_attributes]
|
82
|
-
translated_fields.each do |f|
|
83
|
-
raise MigrationMissingTranslatedField, "Missing translated field #{f}" unless fields[f]
|
84
|
-
end
|
85
|
-
|
86
|
-
fields.each do |name, type|
|
87
|
-
if translated_fields.include?(name) && ![:string, :text].include?(type)
|
88
|
-
raise BadMigrationFieldType, "Bad field type for #{name}, should be :string or :text"
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
self.connection.create_table(translation_table_name) do |t|
|
93
|
-
t.references self.table_name.singularize
|
94
|
-
t.string :locale
|
95
|
-
fields.each do |name, type|
|
96
|
-
t.column name, type
|
97
|
-
end
|
98
|
-
t.timestamps
|
99
|
-
end
|
100
|
-
|
101
|
-
self.connection.add_index(translation_table_name, "#{self.table_name.singularize}_id", :name => translation_index_name)
|
102
|
-
end
|
103
|
-
|
104
|
-
def set_translation_table_name(table_name)
|
105
|
-
globalize_proxy.set_table_name(table_name)
|
106
|
-
end
|
107
|
-
|
108
|
-
def translation_table_name
|
109
|
-
globalize_proxy.table_name
|
110
|
-
end
|
111
|
-
|
112
|
-
def translation_index_name
|
113
|
-
# FIXME what's the max size of an index name?
|
114
|
-
index_name = "index_#{translation_table_name}_on_#{self.table_name.singularize}_id"
|
115
|
-
index_name.size < 50 ? index_name : "index_#{Digest::SHA1.hexdigest(index_name)}"
|
116
|
-
end
|
117
|
-
|
118
|
-
def drop_translation_table!
|
119
|
-
self.connection.remove_index(translation_table_name, :name => translation_index_name)
|
120
|
-
self.connection.drop_table translation_table_name
|
121
|
-
end
|
122
|
-
|
123
|
-
private
|
124
|
-
|
125
|
-
def i18n_attr(attribute_name)
|
126
|
-
self.base_class.name.underscore.gsub('/', '_') + "_translations.#{attribute_name}"
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
module InstanceMethods
|
131
|
-
def reload(options = nil)
|
132
|
-
globalize.clear_cache
|
133
|
-
|
134
|
-
# clear all globalized attributes
|
135
|
-
# TODO what's the best way to handle this?
|
136
|
-
self.class.globalize_options[:translated_attributes].each do |attr|
|
137
|
-
@attributes.delete(attr.to_s)
|
138
|
-
end
|
139
|
-
|
140
|
-
super(options)
|
141
|
-
end
|
142
|
-
|
143
|
-
def translated_attributes
|
144
|
-
self.class.globalize_options[:translated_attributes].inject({}) {|h, tf| h[tf] = send(tf); h }
|
145
|
-
end
|
146
|
-
|
147
|
-
def globalize
|
148
|
-
@globalize ||= Adapter.new self
|
149
|
-
end
|
150
|
-
|
151
|
-
def update_globalize_record
|
152
|
-
globalize.update_translations!
|
153
|
-
end
|
154
|
-
|
155
|
-
def translated_locales
|
156
|
-
globalize_translations.scoped(:select => 'DISTINCT locale').map do |translation|
|
157
|
-
translation.locale.to_sym
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
def set_translations(options)
|
162
|
-
options.keys.each do |key|
|
163
|
-
translation = globalize_translations.find_by_locale(key.to_s) ||
|
164
|
-
globalize_translations.build(:locale => key.to_s)
|
165
|
-
translation.update_attributes!(options[key])
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
ActiveRecord::Base.send(:include, Globalize::Model::ActiveRecord::Translated)
|