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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/Gemfile +19 -5
  4. data/Gemfile.lock +101 -55
  5. data/{readme.md → README.md} +128 -35
  6. data/Rakefile +33 -0
  7. data/lib/globalize/active_record/act_macro.rb +29 -3
  8. data/lib/globalize/active_record/adapter.rb +10 -11
  9. data/lib/globalize/active_record/adapter_dirty.rb +54 -0
  10. data/lib/globalize/active_record/class_methods.rb +19 -6
  11. data/lib/globalize/active_record/exceptions.rb +1 -7
  12. data/lib/globalize/active_record/instance_methods.rb +55 -35
  13. data/lib/globalize/active_record/migration.rb +57 -30
  14. data/lib/globalize/active_record/query_methods.rb +42 -17
  15. data/lib/globalize/active_record.rb +1 -0
  16. data/lib/globalize/version.rb +1 -1
  17. data/lib/globalize.rb +12 -6
  18. data/lib/patches/active_record/persistence.rb +6 -15
  19. data/lib/patches/active_record/query_method.rb +2 -34
  20. data/lib/patches/active_record/rails4/query_method.rb +35 -0
  21. data/lib/patches/active_record/rails4/serialization.rb +22 -0
  22. data/lib/patches/active_record/rails4/uniqueness_validator.rb +42 -0
  23. data/lib/patches/active_record/rails5/uniqueness_validator.rb +47 -0
  24. data/lib/patches/active_record/rails5_1/serialization.rb +22 -0
  25. data/lib/patches/active_record/rails5_1/uniqueness_validator.rb +45 -0
  26. data/lib/patches/active_record/relation.rb +12 -0
  27. data/lib/patches/active_record/serialization.rb +5 -24
  28. data/lib/patches/active_record/uniqueness_validator.rb +7 -39
  29. data/lib/patches/active_record/xml_attribute_serializer.rb +19 -9
  30. metadata +28 -22
  31. 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, :translations
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.reject {|locale, attrs| attrs.empty?}.each do |locale, attrs|
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
- return translation && translation.send(name)
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
- klass = self.const_get(:Translation) rescue nil
46
- if klass.nil? || klass.class_name != (self.class_name + "Translation")
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, :class_name => self.name, :foreign_key => translation_options[:foreign_key]
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, value)|
104
- write_attribute name, value, :locale => locale
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=(attributes, *args)
15
- with_given_locale(attributes) { super }
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(attributes, *args)
19
- with_given_locale(attributes) { super }
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, options = {})
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(options)
29
+ options = {:locale => Globalize.locale}.merge(args.first || {})
26
30
 
27
- # Dirty tracking, paraphrased from
28
- # ActiveRecord::AttributeMethods::Dirty#write_attribute.
29
- name_str = name.to_s
30
- if attribute_changed?(name_str)
31
- # If there's already a change, delete it if this undoes the change.
32
- old = changed_attributes[name_str]
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
- # If there's not a change yet, record it.
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
- super
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(attributes, &block)
188
- attributes.symbolize_keys! if attributes.respond_to?(:symbolize_keys!)
199
+ def with_given_locale(_attributes, &block)
200
+ attributes = _attributes.stringify_keys
189
201
 
190
- if locale = attributes.try(:delete, :locale)
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!, :drop_translation_table!,
13
- :translation_index_name, :translation_locale_index_name,
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, :fields
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
- connection.remove_columns(table_name, *fields.keys)
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
- "#{table_name.sub(/^#{table_name_prefix}/, "").singularize}_id",
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.remove_index(translations_table_name, :name => translation_index_name)
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.default_locale) || record.translations.build(:locale => I18n.default_locale)
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 = @model.all.collect{|m| m.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
- @model.where(:id => translated_record['id']).update_all(fields_to_update)
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
- index_name = "index_#{translations_table_name}_on_#{table_name.singularize}_id"
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
- index_name = "index_#{translations_table_name}_on_locale"
165
- index_name.size < connection.index_name_length ? index_name :
166
- "index_#{Digest::SHA1.hexdigest(index_name)}"[0, connection.index_name_length]
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
- 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
- }
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
- binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
60
+ binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
61
61
 
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
- }])
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
- klass = translated_column?(column) ? translation_class : self
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
- translated_column_name(opts) if translated_attribute_names.include?(opts)
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'
@@ -1,3 +1,3 @@
1
1
  module Globalize
2
- Version = '5.0.1'
2
+ Version = '5.1.0'
3
3
  end