globalize 5.0.1 → 5.1.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 +4 -4
- data/CHANGELOG.md +12 -0
- data/Gemfile +19 -5
- data/Gemfile.lock +101 -55
- data/{readme.md → README.md} +128 -35
- data/Rakefile +33 -0
- data/lib/globalize/active_record/act_macro.rb +29 -3
- data/lib/globalize/active_record/adapter.rb +10 -11
- data/lib/globalize/active_record/adapter_dirty.rb +54 -0
- data/lib/globalize/active_record/class_methods.rb +19 -6
- data/lib/globalize/active_record/exceptions.rb +1 -7
- data/lib/globalize/active_record/instance_methods.rb +55 -35
- data/lib/globalize/active_record/migration.rb +57 -30
- data/lib/globalize/active_record/query_methods.rb +42 -17
- data/lib/globalize/active_record.rb +1 -0
- data/lib/globalize/version.rb +1 -1
- data/lib/globalize.rb +12 -6
- data/lib/patches/active_record/persistence.rb +6 -15
- data/lib/patches/active_record/query_method.rb +2 -34
- data/lib/patches/active_record/rails4/query_method.rb +35 -0
- data/lib/patches/active_record/rails4/serialization.rb +22 -0
- data/lib/patches/active_record/rails4/uniqueness_validator.rb +42 -0
- data/lib/patches/active_record/rails5/uniqueness_validator.rb +47 -0
- data/lib/patches/active_record/rails5_1/serialization.rb +22 -0
- data/lib/patches/active_record/rails5_1/uniqueness_validator.rb +45 -0
- data/lib/patches/active_record/relation.rb +12 -0
- data/lib/patches/active_record/serialization.rb +5 -24
- data/lib/patches/active_record/uniqueness_validator.rb +7 -39
- data/lib/patches/active_record/xml_attribute_serializer.rb +19 -9
- metadata +28 -22
- data/globalize.gemspec +0 -29
|
@@ -3,7 +3,7 @@ module Globalize
|
|
|
3
3
|
class Adapter
|
|
4
4
|
# The cache caches attributes that already were looked up for read access.
|
|
5
5
|
# The stash keeps track of new or changed values that need to be saved.
|
|
6
|
-
attr_accessor :record, :stash
|
|
6
|
+
attr_accessor :record, :stash
|
|
7
7
|
private :record=, :stash=
|
|
8
8
|
|
|
9
9
|
delegate :translation_class, :to => :'record.class'
|
|
@@ -34,16 +34,15 @@ module Globalize
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def save_translations!
|
|
37
|
-
stash.
|
|
37
|
+
stash.each do |locale, attrs|
|
|
38
|
+
next if attrs.empty?
|
|
39
|
+
|
|
38
40
|
translation = record.translations_by_locale[locale] ||
|
|
39
41
|
record.translations.build(locale: locale.to_s)
|
|
40
|
-
|
|
41
42
|
attrs.each do |name, value|
|
|
42
43
|
value = value.val if value.is_a?(Arel::Nodes::Casted)
|
|
43
44
|
translation[name] = value
|
|
44
45
|
end
|
|
45
|
-
ensure_foreign_key_for(translation)
|
|
46
|
-
translation.save!
|
|
47
46
|
end
|
|
48
47
|
|
|
49
48
|
reset
|
|
@@ -55,11 +54,6 @@ module Globalize
|
|
|
55
54
|
|
|
56
55
|
protected
|
|
57
56
|
|
|
58
|
-
# Sometimes the translation is initialised before a foreign key can be set.
|
|
59
|
-
def ensure_foreign_key_for(translation)
|
|
60
|
-
translation[translation.class.reflections["globalized_model"].foreign_key] = record.id
|
|
61
|
-
end
|
|
62
|
-
|
|
63
57
|
def type_cast(name, value)
|
|
64
58
|
return value.presence unless column = column_for_attribute(name)
|
|
65
59
|
|
|
@@ -76,7 +70,11 @@ module Globalize
|
|
|
76
70
|
|
|
77
71
|
def fetch_attribute(locale, name)
|
|
78
72
|
translation = record.translation_for(locale, false)
|
|
79
|
-
|
|
73
|
+
if translation
|
|
74
|
+
translation.send(name)
|
|
75
|
+
else
|
|
76
|
+
record.class.translation_class.new.send(name)
|
|
77
|
+
end
|
|
80
78
|
end
|
|
81
79
|
|
|
82
80
|
def set_metadata(object, metadata)
|
|
@@ -95,6 +93,7 @@ module Globalize
|
|
|
95
93
|
end
|
|
96
94
|
|
|
97
95
|
delegate :fallbacks_for_empty_translations?, :to => :record, :prefix => false
|
|
96
|
+
prepend AdapterDirty
|
|
98
97
|
end
|
|
99
98
|
end
|
|
100
99
|
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module Globalize
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
module AdapterDirty
|
|
4
|
+
def write locale, name, value
|
|
5
|
+
# Dirty tracking, paraphrased from
|
|
6
|
+
# ActiveRecord::AttributeMethods::Dirty#write_attribute.
|
|
7
|
+
name = name.to_s
|
|
8
|
+
store_old_value name, locale
|
|
9
|
+
old_values = dirty[name]
|
|
10
|
+
old_value = old_values[locale]
|
|
11
|
+
is_changed = record.send :attribute_changed?, name
|
|
12
|
+
if is_changed && value == old_value
|
|
13
|
+
# If there's already a change, delete it if this undoes the change.
|
|
14
|
+
old_values.delete locale
|
|
15
|
+
if old_values.empty?
|
|
16
|
+
_reset_attribute name
|
|
17
|
+
end
|
|
18
|
+
elsif !is_changed
|
|
19
|
+
# If there's not a change yet, record it.
|
|
20
|
+
record.send(:attribute_will_change!, name) if old_value != value
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
super locale, name, value
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
attr_writer :dirty
|
|
27
|
+
def dirty
|
|
28
|
+
@dirty ||= {}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def store_old_value name, locale
|
|
32
|
+
dirty[name] ||= {}
|
|
33
|
+
unless dirty[name].key? locale
|
|
34
|
+
old = fetch(locale, name)
|
|
35
|
+
old = old.dup if old.duplicable?
|
|
36
|
+
dirty[name][locale] = old
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
def clear_dirty
|
|
40
|
+
self.dirty = {}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def _reset_attribute name
|
|
44
|
+
record.send("#{name}=", record.changed_attributes[name])
|
|
45
|
+
record.original_changed_attributes.except!(name)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def reset
|
|
49
|
+
clear_dirty
|
|
50
|
+
super
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -3,13 +3,21 @@ module Globalize
|
|
|
3
3
|
module ClassMethods
|
|
4
4
|
delegate :translated_locales, :set_translations_table_name, :to => :translation_class
|
|
5
5
|
|
|
6
|
+
if ::ActiveRecord::VERSION::STRING < "5.0.0"
|
|
7
|
+
def columns_hash
|
|
8
|
+
super.except(*translated_attribute_names.map(&:to_s))
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
6
12
|
def with_locales(*locales)
|
|
7
13
|
all.merge translation_class.with_locales(*locales)
|
|
8
14
|
end
|
|
9
15
|
|
|
10
16
|
def with_translations(*locales)
|
|
11
17
|
locales = translated_locales if locales.empty?
|
|
12
|
-
preload(:translations).joins(:translations).readonly(false).with_locales(locales)
|
|
18
|
+
preload(:translations).joins(:translations).readonly(false).with_locales(locales).tap do |query|
|
|
19
|
+
query.distinct! unless locales.flatten.one?
|
|
20
|
+
end
|
|
13
21
|
end
|
|
14
22
|
|
|
15
23
|
def with_required_attributes
|
|
@@ -42,12 +50,17 @@ module Globalize
|
|
|
42
50
|
|
|
43
51
|
def translation_class
|
|
44
52
|
@translation_class ||= begin
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
if self.const_defined?(:Translation, false)
|
|
54
|
+
klass = self.const_get(:Translation, false)
|
|
55
|
+
else
|
|
47
56
|
klass = self.const_set(:Translation, Class.new(Globalize::ActiveRecord::Translation))
|
|
48
57
|
end
|
|
49
58
|
|
|
50
|
-
klass.belongs_to :globalized_model,
|
|
59
|
+
klass.belongs_to :globalized_model,
|
|
60
|
+
class_name: self.name,
|
|
61
|
+
foreign_key: translation_options[:foreign_key],
|
|
62
|
+
inverse_of: :translations,
|
|
63
|
+
touch: translation_options.fetch(:touch, false)
|
|
51
64
|
klass
|
|
52
65
|
end
|
|
53
66
|
end
|
|
@@ -100,8 +113,8 @@ module Globalize
|
|
|
100
113
|
|
|
101
114
|
def define_translations_writer(name)
|
|
102
115
|
define_method(:"#{name}_translations=") do |value|
|
|
103
|
-
value.each do |(locale,
|
|
104
|
-
write_attribute name,
|
|
116
|
+
value.each do |(locale, _value)|
|
|
117
|
+
write_attribute name, _value, :locale => locale
|
|
105
118
|
end
|
|
106
119
|
end
|
|
107
120
|
end
|
|
@@ -8,12 +8,6 @@ module Globalize
|
|
|
8
8
|
super("Missing translated field #{field.inspect}")
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
|
-
|
|
12
|
-
class BadFieldType < MigrationError
|
|
13
|
-
def initialize(name, type)
|
|
14
|
-
super("Bad field type for field #{name.inspect} (#{type.inspect}), should be :string or :text")
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
11
|
end
|
|
18
12
|
end
|
|
19
|
-
end
|
|
13
|
+
end
|
|
@@ -11,48 +11,46 @@ module Globalize
|
|
|
11
11
|
super.merge(translated_attributes)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def attributes=(
|
|
15
|
-
|
|
14
|
+
def attributes=(new_attributes, *options)
|
|
15
|
+
super unless new_attributes.respond_to?(:stringify_keys) && new_attributes.present?
|
|
16
|
+
attributes = new_attributes.stringify_keys
|
|
17
|
+
with_given_locale(attributes) { super(attributes.except("locale"), *options) }
|
|
16
18
|
end
|
|
17
19
|
|
|
18
|
-
def assign_attributes(
|
|
19
|
-
|
|
20
|
+
def assign_attributes(new_attributes, *options)
|
|
21
|
+
super unless new_attributes.respond_to?(:stringify_keys) && new_attributes.present?
|
|
22
|
+
attributes = new_attributes.stringify_keys
|
|
23
|
+
with_given_locale(attributes) { super(attributes.except("locale"), *options) }
|
|
20
24
|
end
|
|
21
25
|
|
|
22
|
-
def write_attribute(name, value,
|
|
23
|
-
return super(name, value) unless translated?(name)
|
|
26
|
+
def write_attribute(name, value, *args, &block)
|
|
27
|
+
return super(name, value, *args, &block) unless translated?(name)
|
|
24
28
|
|
|
25
|
-
options = {:locale => Globalize.locale}.merge(
|
|
29
|
+
options = {:locale => Globalize.locale}.merge(args.first || {})
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@changed_attributes.delete(name_str) if value == old
|
|
31
|
+
globalize.write(options[:locale], name, value)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def [](attr_name)
|
|
35
|
+
if translated?(attr_name)
|
|
36
|
+
read_attribute(attr_name)
|
|
34
37
|
else
|
|
35
|
-
|
|
36
|
-
old = globalize.fetch(options[:locale], name)
|
|
37
|
-
old = old.dup if old.duplicable?
|
|
38
|
-
@changed_attributes[name_str] = old if value != old
|
|
38
|
+
read_attribute(attr_name) { |n| missing_attribute(n, caller) }
|
|
39
39
|
end
|
|
40
|
-
|
|
41
|
-
globalize.write(options[:locale], name, value)
|
|
42
40
|
end
|
|
43
41
|
|
|
44
|
-
def read_attribute(name, options = {})
|
|
42
|
+
def read_attribute(name, options = {}, &block)
|
|
45
43
|
options = {:translated => true, :locale => nil}.merge(options)
|
|
46
|
-
return super(name) unless options[:translated]
|
|
44
|
+
return super(name, &block) unless options[:translated]
|
|
47
45
|
|
|
48
46
|
if translated?(name)
|
|
49
|
-
if (value = globalize.fetch(options[:locale] || Globalize.locale, name))
|
|
47
|
+
if !(value = globalize.fetch(options[:locale] || Globalize.locale, name)).nil?
|
|
50
48
|
value
|
|
51
49
|
else
|
|
52
|
-
super(name)
|
|
50
|
+
super(name, &block)
|
|
53
51
|
end
|
|
54
52
|
else
|
|
55
|
-
super(name)
|
|
53
|
+
super(name, &block)
|
|
56
54
|
end
|
|
57
55
|
end
|
|
58
56
|
|
|
@@ -83,8 +81,9 @@ module Globalize
|
|
|
83
81
|
|
|
84
82
|
options[locale].each do |key, value|
|
|
85
83
|
translation.send :"#{key}=", value
|
|
84
|
+
translation.globalized_model.send :"#{key}=", value
|
|
86
85
|
end
|
|
87
|
-
translation.save
|
|
86
|
+
translation.save if persisted?
|
|
88
87
|
end
|
|
89
88
|
globalize.reset
|
|
90
89
|
end
|
|
@@ -143,14 +142,17 @@ module Globalize
|
|
|
143
142
|
Globalize.fallbacks(locale)
|
|
144
143
|
end
|
|
145
144
|
|
|
146
|
-
def rollback
|
|
147
|
-
translation_caches[::Globalize.locale] = translation.previous_version
|
|
148
|
-
end
|
|
149
|
-
|
|
150
145
|
def save(*)
|
|
151
|
-
Globalize.with_locale(translation.locale || I18n.default_locale) do
|
|
152
|
-
|
|
146
|
+
result = Globalize.with_locale(translation.locale || I18n.default_locale) do
|
|
147
|
+
without_fallbacks do
|
|
148
|
+
super
|
|
149
|
+
end
|
|
153
150
|
end
|
|
151
|
+
if result
|
|
152
|
+
globalize.clear_dirty
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
result
|
|
154
156
|
end
|
|
155
157
|
|
|
156
158
|
def column_for_attribute name
|
|
@@ -163,6 +165,16 @@ module Globalize
|
|
|
163
165
|
[super, translation.cache_key].join("/")
|
|
164
166
|
end
|
|
165
167
|
|
|
168
|
+
def changed?
|
|
169
|
+
changed_attributes.present? || translations.any?(&:changed?)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# need to access instance variable directly since changed_attributes
|
|
173
|
+
# is frozen as of Rails 4.2
|
|
174
|
+
def original_changed_attributes
|
|
175
|
+
@changed_attributes
|
|
176
|
+
end
|
|
177
|
+
|
|
166
178
|
protected
|
|
167
179
|
|
|
168
180
|
def each_locale_and_translated_attribute
|
|
@@ -184,15 +196,23 @@ module Globalize
|
|
|
184
196
|
translation_caches.clear
|
|
185
197
|
end
|
|
186
198
|
|
|
187
|
-
def with_given_locale(
|
|
188
|
-
attributes
|
|
199
|
+
def with_given_locale(_attributes, &block)
|
|
200
|
+
attributes = _attributes.stringify_keys
|
|
189
201
|
|
|
190
|
-
if locale = attributes.try(:delete,
|
|
202
|
+
if locale = attributes.try(:delete, "locale")
|
|
191
203
|
Globalize.with_locale(locale, &block)
|
|
192
204
|
else
|
|
193
205
|
yield
|
|
194
206
|
end
|
|
195
207
|
end
|
|
208
|
+
|
|
209
|
+
def without_fallbacks
|
|
210
|
+
before = self.fallbacks_for_empty_translations
|
|
211
|
+
self.fallbacks_for_empty_translations = false
|
|
212
|
+
yield
|
|
213
|
+
ensure
|
|
214
|
+
self.fallbacks_for_empty_translations = before
|
|
215
|
+
end
|
|
196
216
|
end
|
|
197
217
|
end
|
|
198
218
|
end
|
|
@@ -3,20 +3,18 @@ require 'digest/sha1'
|
|
|
3
3
|
module Globalize
|
|
4
4
|
module ActiveRecord
|
|
5
5
|
module Migration
|
|
6
|
-
attr_reader :globalize_migrator
|
|
7
|
-
|
|
8
6
|
def globalize_migrator
|
|
9
7
|
@globalize_migrator ||= Migrator.new(self)
|
|
10
8
|
end
|
|
11
9
|
|
|
12
|
-
delegate :create_translation_table!, :add_translation_fields!,
|
|
13
|
-
:
|
|
14
|
-
:to => :globalize_migrator
|
|
10
|
+
delegate :create_translation_table!, :add_translation_fields!,
|
|
11
|
+
:drop_translation_table!, :translation_index_name,
|
|
12
|
+
:translation_locale_index_name, :to => :globalize_migrator
|
|
15
13
|
|
|
16
14
|
class Migrator
|
|
17
15
|
include Globalize::ActiveRecord::Exceptions
|
|
18
16
|
|
|
19
|
-
attr_reader :model
|
|
17
|
+
attr_reader :model
|
|
20
18
|
delegate :translated_attribute_names, :connection, :table_name,
|
|
21
19
|
:table_name_prefix, :translations_table_name, :columns, :to => :model
|
|
22
20
|
|
|
@@ -24,7 +22,15 @@ module Globalize
|
|
|
24
22
|
@model = model
|
|
25
23
|
end
|
|
26
24
|
|
|
25
|
+
def fields
|
|
26
|
+
@fields ||= complete_translated_fields
|
|
27
|
+
end
|
|
28
|
+
|
|
27
29
|
def create_translation_table!(fields = {}, options = {})
|
|
30
|
+
extra = options.keys - [:migrate_data, :remove_source_columns, :unique_index]
|
|
31
|
+
if extra.any?
|
|
32
|
+
raise ArgumentError, "Unknown migration #{'option'.pluralize(extra.size)}: #{extra}"
|
|
33
|
+
end
|
|
28
34
|
@fields = fields
|
|
29
35
|
# If we have fields we only want to create the translation table with those fields
|
|
30
36
|
complete_translated_fields if fields.blank?
|
|
@@ -32,14 +38,13 @@ module Globalize
|
|
|
32
38
|
|
|
33
39
|
create_translation_table
|
|
34
40
|
add_translation_fields!(fields, options)
|
|
35
|
-
create_translations_index
|
|
41
|
+
create_translations_index(options)
|
|
36
42
|
clear_schema_cache!
|
|
37
43
|
end
|
|
38
44
|
|
|
39
45
|
def add_translation_fields!(fields, options = {})
|
|
40
46
|
@fields = fields
|
|
41
47
|
validate_translated_fields
|
|
42
|
-
|
|
43
48
|
add_translation_fields
|
|
44
49
|
clear_schema_cache!
|
|
45
50
|
move_data_to_translation_table if options[:migrate_data]
|
|
@@ -48,7 +53,12 @@ module Globalize
|
|
|
48
53
|
end
|
|
49
54
|
|
|
50
55
|
def remove_source_columns
|
|
51
|
-
|
|
56
|
+
column_names = *fields.keys
|
|
57
|
+
column_names.each do |column|
|
|
58
|
+
if connection.column_exists?(table_name, column)
|
|
59
|
+
connection.remove_column(table_name, column)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
52
62
|
end
|
|
53
63
|
|
|
54
64
|
def drop_translation_table!(options = {})
|
|
@@ -62,13 +72,13 @@ module Globalize
|
|
|
62
72
|
# It's a problem because in early migrations would add all the translated attributes
|
|
63
73
|
def complete_translated_fields
|
|
64
74
|
translated_attribute_names.each do |name|
|
|
65
|
-
fields[name] ||= column_type(name)
|
|
75
|
+
@fields[name] ||= column_type(name)
|
|
66
76
|
end
|
|
67
77
|
end
|
|
68
78
|
|
|
69
79
|
def create_translation_table
|
|
70
80
|
connection.create_table(translations_table_name) do |t|
|
|
71
|
-
t.references table_name.sub(/^#{table_name_prefix}/, '').singularize, :null => false
|
|
81
|
+
t.references table_name.sub(/^#{table_name_prefix}/, '').singularize, :null => false, :index => false, :type => column_type(model.primary_key).to_sym
|
|
72
82
|
t.string :locale, :null => false
|
|
73
83
|
t.timestamps :null => false
|
|
74
84
|
end
|
|
@@ -86,10 +96,11 @@ module Globalize
|
|
|
86
96
|
end
|
|
87
97
|
end
|
|
88
98
|
|
|
89
|
-
def create_translations_index
|
|
99
|
+
def create_translations_index(options)
|
|
100
|
+
foreign_key = "#{table_name.sub(/^#{table_name_prefix}/, "").singularize}_id".to_sym
|
|
90
101
|
connection.add_index(
|
|
91
102
|
translations_table_name,
|
|
92
|
-
|
|
103
|
+
foreign_key,
|
|
93
104
|
:name => translation_index_name
|
|
94
105
|
)
|
|
95
106
|
# index for select('DISTINCT locale') call in translation.rb
|
|
@@ -98,6 +109,15 @@ module Globalize
|
|
|
98
109
|
:locale,
|
|
99
110
|
:name => translation_locale_index_name
|
|
100
111
|
)
|
|
112
|
+
|
|
113
|
+
if options[:unique_index]
|
|
114
|
+
connection.add_index(
|
|
115
|
+
translations_table_name,
|
|
116
|
+
[foreign_key, :locale],
|
|
117
|
+
:name => translation_unique_index_name,
|
|
118
|
+
unique: true
|
|
119
|
+
)
|
|
120
|
+
end
|
|
101
121
|
end
|
|
102
122
|
|
|
103
123
|
def drop_translation_table
|
|
@@ -105,12 +125,17 @@ module Globalize
|
|
|
105
125
|
end
|
|
106
126
|
|
|
107
127
|
def drop_translations_index
|
|
108
|
-
connection.
|
|
128
|
+
if connection.indexes(translations_table_name).map(&:name).include?(translation_index_name)
|
|
129
|
+
connection.remove_index(translations_table_name, :name => translation_index_name)
|
|
130
|
+
end
|
|
131
|
+
if connection.indexes(translations_table_name).map(&:name).include?(translation_locale_index_name)
|
|
132
|
+
connection.remove_index(translations_table_name, :name => translation_locale_index_name)
|
|
133
|
+
end
|
|
109
134
|
end
|
|
110
135
|
|
|
111
136
|
def move_data_to_translation_table
|
|
112
137
|
model.find_each do |record|
|
|
113
|
-
translation = record.translation_for(I18n.
|
|
138
|
+
translation = record.translation_for(I18n.locale) || record.translations.build(:locale => I18n.locale)
|
|
114
139
|
fields.each do |attribute_name, attribute_type|
|
|
115
140
|
translation[attribute_name] = record.read_attribute(attribute_name, {:translated => false})
|
|
116
141
|
end
|
|
@@ -122,7 +147,7 @@ module Globalize
|
|
|
122
147
|
add_missing_columns
|
|
123
148
|
|
|
124
149
|
# Find all of the translated attributes for all records in the model.
|
|
125
|
-
all_translated_attributes =
|
|
150
|
+
all_translated_attributes = model.all.collect{|m| m.attributes}
|
|
126
151
|
all_translated_attributes.each do |translated_record|
|
|
127
152
|
# Create a hash containing the translated column names and their values.
|
|
128
153
|
translated_attribute_names.inject(fields_to_update={}) do |f, name|
|
|
@@ -130,15 +155,13 @@ module Globalize
|
|
|
130
155
|
end
|
|
131
156
|
|
|
132
157
|
# Now, update the actual model's record with the hash.
|
|
133
|
-
|
|
158
|
+
model.where(model.primary_key.to_sym => translated_record[model.primary_key]).update_all(fields_to_update)
|
|
134
159
|
end
|
|
135
160
|
end
|
|
136
161
|
|
|
137
162
|
def validate_translated_fields
|
|
138
163
|
fields.each do |name, options|
|
|
139
164
|
raise BadFieldName.new(name) unless valid_field_name?(name)
|
|
140
|
-
type = (options.is_a? Hash) ? options[:type] : options
|
|
141
|
-
raise BadFieldType.new(name, type) unless valid_field_type?(name, type)
|
|
142
165
|
end
|
|
143
166
|
end
|
|
144
167
|
|
|
@@ -150,20 +173,16 @@ module Globalize
|
|
|
150
173
|
translated_attribute_names.include?(name)
|
|
151
174
|
end
|
|
152
175
|
|
|
153
|
-
def valid_field_type?(name, type)
|
|
154
|
-
!translated_attribute_names.include?(name) || [:string, :text].include?(type)
|
|
155
|
-
end
|
|
156
|
-
|
|
157
176
|
def translation_index_name
|
|
158
|
-
|
|
159
|
-
index_name.size < connection.index_name_length ? index_name :
|
|
160
|
-
"index_#{Digest::SHA1.hexdigest(index_name)}"[0, connection.index_name_length]
|
|
177
|
+
truncate_index_name "index_#{translations_table_name}_on_#{table_name.singularize}_id"
|
|
161
178
|
end
|
|
162
179
|
|
|
163
180
|
def translation_locale_index_name
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
181
|
+
truncate_index_name "index_#{translations_table_name}_on_locale"
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def translation_unique_index_name
|
|
185
|
+
truncate_index_name "index_#{translations_table_name}_on_#{table_name.singularize}_id_and_locale"
|
|
167
186
|
end
|
|
168
187
|
|
|
169
188
|
def clear_schema_cache!
|
|
@@ -174,14 +193,22 @@ module Globalize
|
|
|
174
193
|
|
|
175
194
|
private
|
|
176
195
|
|
|
196
|
+
def truncate_index_name(index_name)
|
|
197
|
+
if index_name.size < connection.index_name_length
|
|
198
|
+
index_name
|
|
199
|
+
else
|
|
200
|
+
"index_#{Digest::SHA1.hexdigest(index_name)}"[0, connection.index_name_length]
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
177
204
|
def add_missing_columns
|
|
205
|
+
clear_schema_cache!
|
|
178
206
|
translated_attribute_names.map(&:to_s).each do |attribute|
|
|
179
207
|
unless model.column_names.include?(attribute)
|
|
180
208
|
connection.add_column(table_name, attribute, model::Translation.columns_hash[attribute].type)
|
|
181
209
|
end
|
|
182
210
|
end
|
|
183
211
|
end
|
|
184
|
-
|
|
185
212
|
end
|
|
186
213
|
end
|
|
187
214
|
end
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
module Globalize
|
|
2
2
|
module ActiveRecord
|
|
3
3
|
module QueryMethods
|
|
4
|
-
|
|
5
4
|
class WhereChain < ::ActiveRecord::QueryMethods::WhereChain
|
|
6
5
|
def not(opts, *rest)
|
|
7
|
-
if parsed = @scope.parse_translated_conditions(opts)
|
|
6
|
+
if parsed = @scope.clone.parse_translated_conditions(opts)
|
|
8
7
|
@scope.join_translations.where.not(parsed, *rest)
|
|
9
8
|
else
|
|
10
9
|
super
|
|
@@ -24,7 +23,7 @@ module Globalize
|
|
|
24
23
|
|
|
25
24
|
def order(opts, *rest)
|
|
26
25
|
if respond_to?(:translated_attribute_names) && parsed = parse_translated_order(opts)
|
|
27
|
-
super(parsed)
|
|
26
|
+
join_translations super(parsed)
|
|
28
27
|
else
|
|
29
28
|
super
|
|
30
29
|
end
|
|
@@ -50,19 +49,21 @@ module Globalize
|
|
|
50
49
|
end
|
|
51
50
|
end
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
if ::ActiveRecord::VERSION::STRING < "5.0.0"
|
|
53
|
+
def where_values_hash(*args)
|
|
54
|
+
return super unless respond_to?(:translations_table_name)
|
|
55
|
+
equalities = respond_to?(:with_default_scope) ? with_default_scope.where_values : where_values
|
|
56
|
+
equalities = equalities.grep(Arel::Nodes::Equality).find_all { |node|
|
|
57
|
+
node.left.relation.name == translations_table_name
|
|
58
|
+
}
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
super.merge(Hash[equalities.map { |where|
|
|
63
|
+
name = where.left.name
|
|
64
|
+
[name, binds.fetch(name.to_s) { right = where.right; right.is_a?(Arel::Nodes::Casted) ? right.val : right }]
|
|
65
|
+
}])
|
|
66
|
+
end
|
|
66
67
|
end
|
|
67
68
|
|
|
68
69
|
def join_translations(relation = self)
|
|
@@ -75,16 +76,40 @@ module Globalize
|
|
|
75
76
|
|
|
76
77
|
private
|
|
77
78
|
|
|
79
|
+
def arel_translated_order_node(column, direction)
|
|
80
|
+
unless translated_column?(column)
|
|
81
|
+
return self.arel_table[column].send(direction)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
full_column = translated_column_name(column)
|
|
85
|
+
|
|
86
|
+
# Inject `full_column` to the select values to avoid
|
|
87
|
+
# PG::InvalidColumnReference errors with distinct queries on Postgres
|
|
88
|
+
if select_values.empty?
|
|
89
|
+
self.select_values = [self.arel_table[Arel.star], full_column]
|
|
90
|
+
else
|
|
91
|
+
self.select_values << full_column
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
translation_class.arel_table[column].send(direction)
|
|
95
|
+
end
|
|
96
|
+
|
|
78
97
|
def parse_translated_order(opts)
|
|
79
98
|
case opts
|
|
80
99
|
when Hash
|
|
100
|
+
# Do not process nothing unless there is at least a translated column
|
|
101
|
+
# so that the `order` statement will be processed by the original
|
|
102
|
+
# ActiveRecord method
|
|
103
|
+
return nil unless opts.find { |col, dir| translated_column?(col) }
|
|
104
|
+
|
|
105
|
+
# Build order arel nodes for translateds and untranslateds statements
|
|
81
106
|
ordering = opts.map do |column, direction|
|
|
82
|
-
|
|
83
|
-
klass.arel_table[column].send(direction)
|
|
107
|
+
arel_translated_order_node(column, direction)
|
|
84
108
|
end
|
|
109
|
+
|
|
85
110
|
order(ordering).order_values
|
|
86
111
|
when Symbol
|
|
87
|
-
|
|
112
|
+
parse_translated_order({ opts => :asc })
|
|
88
113
|
else # failsafe returns nothing
|
|
89
114
|
nil
|
|
90
115
|
end
|
|
@@ -2,6 +2,7 @@ module Globalize
|
|
|
2
2
|
module ActiveRecord
|
|
3
3
|
autoload :ActMacro, 'globalize/active_record/act_macro'
|
|
4
4
|
autoload :Adapter, 'globalize/active_record/adapter'
|
|
5
|
+
autoload :AdapterDirty, 'globalize/active_record/adapter_dirty'
|
|
5
6
|
autoload :Attributes, 'globalize/active_record/attributes'
|
|
6
7
|
autoload :ClassMethods, 'globalize/active_record/class_methods'
|
|
7
8
|
autoload :Exceptions, 'globalize/active_record/exceptions'
|
data/lib/globalize/version.rb
CHANGED