mobility 0.8.11 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CHANGELOG.md +69 -1
  5. data/Gemfile +50 -18
  6. data/Gemfile.lock +32 -75
  7. data/README.md +184 -92
  8. data/Rakefile +6 -4
  9. data/lib/mobility.rb +100 -168
  10. data/lib/mobility/backend.rb +27 -51
  11. data/lib/mobility/backends.rb +20 -0
  12. data/lib/mobility/backends/active_record.rb +4 -0
  13. data/lib/mobility/backends/active_record/column.rb +3 -1
  14. data/lib/mobility/backends/active_record/container.rb +10 -11
  15. data/lib/mobility/backends/active_record/hstore.rb +6 -4
  16. data/lib/mobility/backends/active_record/json.rb +5 -3
  17. data/lib/mobility/backends/active_record/jsonb.rb +5 -3
  18. data/lib/mobility/backends/active_record/key_value.rb +31 -13
  19. data/lib/mobility/backends/active_record/pg_hash.rb +1 -1
  20. data/lib/mobility/backends/active_record/serialized.rb +6 -0
  21. data/lib/mobility/backends/active_record/table.rb +17 -10
  22. data/lib/mobility/backends/column.rb +0 -6
  23. data/lib/mobility/backends/container.rb +10 -1
  24. data/lib/mobility/backends/hash.rb +39 -0
  25. data/lib/mobility/backends/hash_valued.rb +4 -0
  26. data/lib/mobility/backends/hstore.rb +0 -1
  27. data/lib/mobility/backends/json.rb +0 -1
  28. data/lib/mobility/backends/jsonb.rb +1 -2
  29. data/lib/mobility/backends/key_value.rb +31 -26
  30. data/lib/mobility/backends/null.rb +2 -0
  31. data/lib/mobility/backends/sequel.rb +37 -2
  32. data/lib/mobility/backends/sequel/column.rb +2 -0
  33. data/lib/mobility/backends/sequel/container.rb +11 -9
  34. data/lib/mobility/backends/sequel/hstore.rb +3 -1
  35. data/lib/mobility/backends/sequel/json.rb +3 -0
  36. data/lib/mobility/backends/sequel/jsonb.rb +3 -1
  37. data/lib/mobility/backends/sequel/key_value.rb +87 -18
  38. data/lib/mobility/backends/sequel/pg_hash.rb +6 -6
  39. data/lib/mobility/backends/sequel/serialized.rb +6 -0
  40. data/lib/mobility/backends/sequel/table.rb +22 -9
  41. data/lib/mobility/backends/serialized.rb +1 -3
  42. data/lib/mobility/backends/table.rb +39 -31
  43. data/lib/mobility/pluggable.rb +56 -0
  44. data/lib/mobility/plugin.rb +260 -0
  45. data/lib/mobility/plugins.rb +27 -24
  46. data/lib/mobility/plugins/active_model.rb +17 -0
  47. data/lib/mobility/plugins/active_model/cache.rb +26 -0
  48. data/lib/mobility/plugins/active_model/dirty.rb +119 -78
  49. data/lib/mobility/plugins/active_record.rb +37 -0
  50. data/lib/mobility/plugins/active_record/backend.rb +27 -0
  51. data/lib/mobility/plugins/active_record/cache.rb +28 -0
  52. data/lib/mobility/plugins/active_record/dirty.rb +34 -17
  53. data/lib/mobility/plugins/active_record/query.rb +43 -31
  54. data/lib/mobility/plugins/active_record/uniqueness_validation.rb +60 -0
  55. data/lib/mobility/plugins/arel.rb +125 -0
  56. data/lib/mobility/plugins/arel/nodes.rb +15 -0
  57. data/lib/mobility/plugins/arel/nodes/pg_ops.rb +134 -0
  58. data/lib/mobility/plugins/attribute_methods.rb +29 -20
  59. data/lib/mobility/plugins/attributes.rb +72 -0
  60. data/lib/mobility/plugins/backend.rb +161 -0
  61. data/lib/mobility/plugins/backend_reader.rb +34 -0
  62. data/lib/mobility/plugins/cache.rb +68 -26
  63. data/lib/mobility/plugins/default.rb +22 -17
  64. data/lib/mobility/plugins/dirty.rb +12 -33
  65. data/lib/mobility/plugins/fallbacks.rb +52 -44
  66. data/lib/mobility/plugins/fallthrough_accessors.rb +25 -25
  67. data/lib/mobility/plugins/locale_accessors.rb +22 -35
  68. data/lib/mobility/plugins/presence.rb +28 -21
  69. data/lib/mobility/plugins/query.rb +8 -17
  70. data/lib/mobility/plugins/reader.rb +50 -0
  71. data/lib/mobility/plugins/sequel.rb +34 -0
  72. data/lib/mobility/plugins/sequel/backend.rb +25 -0
  73. data/lib/mobility/plugins/sequel/cache.rb +24 -0
  74. data/lib/mobility/plugins/sequel/dirty.rb +34 -23
  75. data/lib/mobility/plugins/sequel/query.rb +21 -6
  76. data/lib/mobility/plugins/writer.rb +44 -0
  77. data/lib/mobility/translations.rb +95 -0
  78. data/lib/mobility/version.rb +12 -1
  79. data/lib/rails/generators/mobility/templates/initializer.rb +96 -78
  80. metadata +31 -42
  81. metadata.gz.sig +0 -0
  82. data/lib/mobility/active_model.rb +0 -4
  83. data/lib/mobility/active_model/backend_resetter.rb +0 -26
  84. data/lib/mobility/active_record.rb +0 -23
  85. data/lib/mobility/active_record/backend_resetter.rb +0 -26
  86. data/lib/mobility/active_record/model_translation.rb +0 -14
  87. data/lib/mobility/active_record/string_translation.rb +0 -10
  88. data/lib/mobility/active_record/text_translation.rb +0 -10
  89. data/lib/mobility/active_record/translation.rb +0 -14
  90. data/lib/mobility/active_record/uniqueness_validator.rb +0 -60
  91. data/lib/mobility/arel.rb +0 -49
  92. data/lib/mobility/arel/nodes.rb +0 -13
  93. data/lib/mobility/arel/nodes/pg_ops.rb +0 -132
  94. data/lib/mobility/arel/visitor.rb +0 -61
  95. data/lib/mobility/attributes.rb +0 -324
  96. data/lib/mobility/backend/orm_delegator.rb +0 -44
  97. data/lib/mobility/backend_resetter.rb +0 -50
  98. data/lib/mobility/configuration.rb +0 -138
  99. data/lib/mobility/fallbacks.rb +0 -28
  100. data/lib/mobility/interface.rb +0 -0
  101. data/lib/mobility/loaded.rb +0 -4
  102. data/lib/mobility/plugins/active_record/attribute_methods.rb +0 -38
  103. data/lib/mobility/plugins/cache/translation_cacher.rb +0 -40
  104. data/lib/mobility/sequel.rb +0 -9
  105. data/lib/mobility/sequel/backend_resetter.rb +0 -23
  106. data/lib/mobility/sequel/column_changes.rb +0 -28
  107. data/lib/mobility/sequel/hash_initializer.rb +0 -21
  108. data/lib/mobility/sequel/model_translation.rb +0 -20
  109. data/lib/mobility/sequel/sql.rb +0 -16
  110. data/lib/mobility/sequel/string_translation.rb +0 -10
  111. data/lib/mobility/sequel/text_translation.rb +0 -10
  112. data/lib/mobility/sequel/translation.rb +0 -53
  113. data/lib/mobility/translates.rb +0 -73
@@ -1,4 +1,24 @@
1
1
  module Mobility
2
2
  module Backends
3
+ @backends = {}
4
+
5
+ class << self
6
+ # @param [Symbol, Object] backend Name of backend to load.
7
+ def load_backend(name)
8
+ return name if Module === name || name.nil?
9
+
10
+ unless (backend = @backends[name])
11
+ require "mobility/backends/#{name}"
12
+ raise LoadError, "backend #{name} did not register itself correctly in Mobility::Backends" unless (backend = @backends[name])
13
+ end
14
+ backend
15
+ end
16
+ end
17
+
18
+ def self.register_backend(name, mod)
19
+ @backends[name] = mod
20
+ end
21
+
22
+ class LoadError < Error; end
3
23
  end
4
24
  end
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+ require "mobility/backend"
3
+ require "mobility/plugins/arel"
4
+
1
5
  module Mobility
2
6
  module Backends
3
7
  module ActiveRecord
@@ -57,7 +57,7 @@ can be run again to add new attributes or locales.)
57
57
  # on model table
58
58
  def self.build_node(attr, locale)
59
59
  model_class.arel_table[Column.column_name_for(attr, locale)]
60
- .extend(::Mobility::Arel::MobilityExpressions)
60
+ .extend(Plugins::Arel::MobilityExpressions)
61
61
  end
62
62
 
63
63
  private
@@ -73,5 +73,7 @@ can be run again to add new attributes or locales.)
73
73
  end.compact
74
74
  end
75
75
  end
76
+
77
+ register_backend(:active_record_column, ActiveRecord::Column)
76
78
  end
77
79
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require "mobility/backends/active_record"
3
- require "mobility/arel/nodes/pg_ops"
3
+ require "mobility/backends/container"
4
+ require "mobility/plugins/arel/nodes/pg_ops"
4
5
 
5
6
  module Mobility
6
7
  module Backends
@@ -11,11 +12,7 @@ Implements the {Mobility::Backends::Container} backend for ActiveRecord models.
11
12
  =end
12
13
  class ActiveRecord::Container
13
14
  include ActiveRecord
14
-
15
- # @!method column_name
16
- # Returns name of json or jsonb column used to store translations
17
- # @return [Symbol] (:translations) Name of translations column
18
- option_reader :column_name
15
+ include Container
19
16
 
20
17
  # @!group Backend Accessors
21
18
  #
@@ -52,15 +49,15 @@ Implements the {Mobility::Backends::Container} backend for ActiveRecord models.
52
49
 
53
50
  # @param [String] attr Attribute name
54
51
  # @param [Symbol] locale Locale
55
- # @return [Mobility::Arel::Nodes::Json,Mobility::Arel::Nodes::Jsonb] Arel
52
+ # @return [Mobility::Plugins::Arel::Nodes::Json,Mobility::Arel::Nodes::Jsonb] Arel
56
53
  # node for attribute on json or jsonb column
57
54
  def build_node(attr, locale)
58
55
  column = model_class.arel_table[column_name]
59
56
  case column_type
60
57
  when :json
61
- Arel::Nodes::JsonContainer.new(column, build_quoted(locale), build_quoted(attr))
58
+ Plugins::Arel::Nodes::JsonContainer.new(column, build_quoted(locale), build_quoted(attr))
62
59
  when :jsonb
63
- Arel::Nodes::JsonbContainer.new(column, build_quoted(locale), build_quoted(attr))
60
+ Plugins::Arel::Nodes::JsonbContainer.new(column, build_quoted(locale), build_quoted(attr))
64
61
  end
65
62
  end
66
63
 
@@ -71,7 +68,7 @@ Implements the {Mobility::Backends::Container} backend for ActiveRecord models.
71
68
  private
72
69
 
73
70
  def get_column_type
74
- options[:model_class].type_for_attribute(options[:column_name].to_s).try(:type).tap do |type|
71
+ model_class.type_for_attribute(options[:column_name].to_s).try(:type).tap do |type|
75
72
  unless %i[json jsonb].include? type
76
73
  raise InvalidColumnType, "#{options[:column_name]} must be a column of type json or jsonb"
77
74
  end
@@ -122,7 +119,7 @@ Implements the {Mobility::Backends::Container} backend for ActiveRecord models.
122
119
 
123
120
  class Coder
124
121
  def self.dump(obj)
125
- if obj.is_a? Hash
122
+ if obj.is_a? ::Hash
126
123
  obj.inject({}) do |translations, (locale, value)|
127
124
  value.each do |k, v|
128
125
  (translations[locale] ||= {})[k] = v if v.present?
@@ -141,5 +138,7 @@ Implements the {Mobility::Backends::Container} backend for ActiveRecord models.
141
138
 
142
139
  class InvalidColumnType < StandardError; end
143
140
  end
141
+
142
+ register_backend(:active_record_container, ActiveRecord::Container)
144
143
  end
145
144
  end
@@ -1,5 +1,5 @@
1
1
  require 'mobility/backends/active_record/pg_hash'
2
- require 'mobility/arel/nodes/pg_ops'
2
+ require 'mobility/plugins/arel/nodes/pg_ops'
3
3
 
4
4
  module Mobility
5
5
  module Backends
@@ -18,19 +18,21 @@ Implements the {Mobility::Backends::Hstore} backend for ActiveRecord models.
18
18
 
19
19
  # @!macro backend_writer
20
20
  def write(locale, value, options = {})
21
- super(locale, value && value.to_s, options)
21
+ super(locale, value && value.to_s, **options)
22
22
  end
23
23
  # @!endgroup
24
24
 
25
25
  # @param [String] attr Attribute name
26
26
  # @param [Symbol] locale Locale
27
- # @return [Mobility::Arel::Nodes::Hstore] Arel node for value of
27
+ # @return [Mobility::Plugins::Arel::Nodes::Hstore] Arel node for value of
28
28
  # attribute key on hstore column
29
29
  def self.build_node(attr, locale)
30
30
  column_name = column_affix % attr
31
- Arel::Nodes::Hstore.new(model_class.arel_table[column_name], build_quoted(locale))
31
+ Plugins::Arel::Nodes::Hstore.new(model_class.arel_table[column_name], build_quoted(locale))
32
32
  end
33
33
  end
34
34
  end
35
+
36
+ register_backend(:active_record_hstore, ActiveRecord::Hstore)
35
37
  end
36
38
  end
@@ -1,5 +1,5 @@
1
1
  require 'mobility/backends/active_record/pg_hash'
2
- require 'mobility/arel/nodes/pg_ops'
2
+ require 'mobility/plugins/arel/nodes/pg_ops'
3
3
 
4
4
  module Mobility
5
5
  module Backends
@@ -32,13 +32,15 @@ Implements the {Mobility::Backends::Json} backend for ActiveRecord models.
32
32
 
33
33
  # @param [String] attr Attribute name
34
34
  # @param [Symbol] locale Locale
35
- # @return [Mobility::Arel::Nodes::Json] Arel node for value of
35
+ # @return [Mobility::Plugins::Arel::Nodes::Json] Arel node for value of
36
36
  # attribute key on jsonb column
37
37
  def self.build_node(attr, locale)
38
38
  column_name = column_affix % attr
39
- Arel::Nodes::Json.new(model_class.arel_table[column_name], build_quoted(locale))
39
+ Plugins::Arel::Nodes::Json.new(model_class.arel_table[column_name], build_quoted(locale))
40
40
  end
41
41
  end
42
42
  end
43
+
44
+ register_backend(:active_record_json, ActiveRecord::Json)
43
45
  end
44
46
  end
@@ -1,5 +1,5 @@
1
1
  require 'mobility/backends/active_record/pg_hash'
2
- require 'mobility/arel/nodes/pg_ops'
2
+ require 'mobility/plugins/arel/nodes/pg_ops'
3
3
 
4
4
  module Mobility
5
5
  module Backends
@@ -32,13 +32,15 @@ Implements the {Mobility::Backends::Jsonb} backend for ActiveRecord models.
32
32
 
33
33
  # @param [String] attr Attribute name
34
34
  # @param [Symbol] locale Locale
35
- # @return [Mobility::Arel::Nodes::Jsonb] Arel node for value of
35
+ # @return [Mobility::Plugins::Arel::Nodes::Jsonb] Arel node for value of
36
36
  # attribute key on jsonb column
37
37
  def self.build_node(attr, locale)
38
38
  column_name = column_affix % attr
39
- Arel::Nodes::Jsonb.new(model_class.arel_table[column_name], build_quoted(locale))
39
+ Plugins::Arel::Nodes::Jsonb.new(model_class.arel_table[column_name], build_quoted(locale))
40
40
  end
41
41
  end
42
42
  end
43
+
44
+ register_backend(:active_record_jsonb, ActiveRecord::Jsonb)
43
45
  end
44
46
  end
@@ -1,8 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
  require "mobility/backends/active_record"
3
3
  require "mobility/backends/key_value"
4
- require "mobility/active_record/string_translation"
5
- require "mobility/active_record/text_translation"
6
4
 
7
5
  module Mobility
8
6
  module Backends
@@ -37,21 +35,21 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
37
35
  super
38
36
  if type = options[:type]
39
37
  options[:association_name] ||= :"#{options[:type]}_translations"
40
- options[:class_name] ||= Mobility::ActiveRecord.const_get("#{type.capitalize}Translation")
38
+ options[:class_name] ||= const_get("#{type.capitalize}Translation")
41
39
  end
42
- options[:table_alias_affix] = "#{options[:model_class]}_%s_#{options[:association_name]}"
40
+ options[:table_alias_affix] = "#{model_class}_%s_#{options[:association_name]}"
43
41
  rescue NameError
44
- raise ArgumentError, "You must define a Mobility::ActiveRecord::#{type.capitalize}Translation class."
42
+ raise ArgumentError, "You must define a Mobility::Backends::ActiveRecord::KeyValue::#{type.capitalize}Translation class."
45
43
  end
46
44
  # @!endgroup
47
45
 
48
46
  # @param [String] attr Attribute name
49
47
  # @param [Symbol] _locale Locale
50
- # @return [Mobility::Arel::Attribute] Arel attribute for aliased
48
+ # @return [Mobility::Plugins::Arel::Attribute] Arel attribute for aliased
51
49
  # translation table value column
52
50
  def build_node(attr, locale)
53
51
  aliased_table = class_name.arel_table.alias(table_alias(attr, locale))
54
- Arel::Attribute.new(aliased_table, :value, locale, self, attribute_name: attr.to_sym)
52
+ Plugins::Arel::Attribute.new(aliased_table, :value, locale, self, attr.to_sym)
55
53
  end
56
54
 
57
55
  # Joins translations using either INNER/OUTER join appropriate to the query.
@@ -119,7 +117,7 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
119
117
  # The title predicate has a non-nil value, so we can use an INNER JOIN,
120
118
  # whereas we are searching for nil content, which requires an OUTER JOIN.
121
119
  #
122
- class Visitor < Arel::Visitor
120
+ class Visitor < Plugins::Arel::Visitor
123
121
  private
124
122
 
125
123
  def visit_Arel_Nodes_Equality(object)
@@ -137,11 +135,11 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
137
135
  alias :visit_Array :visit_collection
138
136
 
139
137
  def visit_Arel_Nodes_Or(object)
140
- [object.left, object.right].map(&method(:visit)).compact.inject(&:merge).
138
+ [object.left, object.right].map(&method(:visit)).compact.inject(:merge).
141
139
  transform_values { OUTER_JOIN }
142
140
  end
143
141
 
144
- def visit_Mobility_Arel_Attribute(object)
142
+ def visit_Mobility_Plugins_Arel_Attribute(object)
145
143
  if object.backend_class == backend_class && object.locale == locale
146
144
  { object.attribute_name => OUTER_JOIN }
147
145
  end
@@ -194,8 +192,8 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
194
192
 
195
193
  # Returns translation for a given locale, or builds one if none is present.
196
194
  # @param [Symbol] locale
197
- # @return [Mobility::ActiveRecord::TextTranslation,Mobility::ActiveRecord::StringTranslation]
198
- def translation_for(locale, _options = {})
195
+ # @return [Mobility::Backends::ActiveRecord::KeyValue::TextTranslation,Mobility::Backends::ActiveRecord::KeyValue::StringTranslation]
196
+ def translation_for(locale, **)
199
197
  translation = translations.find { |t| t.key == attribute && t.locale == locale.to_s }
200
198
  translation ||= translations.build(locale: locale, key: attribute)
201
199
  translation
@@ -205,12 +203,32 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
205
203
  def self.included(model_class)
206
204
  model_class.after_destroy do
207
205
  [:string, :text].each do |type|
208
- Mobility::ActiveRecord.const_get("#{type.capitalize}Translation").
206
+ Mobility::Backends::ActiveRecord::KeyValue.const_get("#{type.capitalize}Translation").
209
207
  where(translatable: self).destroy_all
210
208
  end
211
209
  end
212
210
  end
213
211
  end
212
+
213
+ class Translation < ::ActiveRecord::Base
214
+ self.abstract_class = true
215
+
216
+ belongs_to :translatable, polymorphic: true, touch: true
217
+
218
+ validates :key, presence: true, uniqueness: { scope: [:translatable_id, :translatable_type, :locale], case_sensitive: true }
219
+ validates :translatable, presence: true
220
+ validates :locale, presence: true
221
+ end
222
+
223
+ class TextTranslation < Translation
224
+ self.table_name = "mobility_text_translations"
225
+ end
226
+
227
+ class StringTranslation < Translation
228
+ self.table_name = "mobility_string_translations"
229
+ end
214
230
  end
231
+
232
+ register_backend(:active_record_key_value, ActiveRecord::KeyValue)
215
233
  end
216
234
  end
@@ -30,7 +30,7 @@ Internal class used by ActiveRecord backends backed by a Postgres data type
30
30
 
31
31
  class Coder
32
32
  def self.dump(obj)
33
- if obj.is_a? Hash
33
+ if obj.is_a? ::Hash
34
34
  obj.inject({}) do |translations, (locale, value)|
35
35
  translations[locale] = value if value.present?
36
36
  translations
@@ -30,6 +30,10 @@ Implements {Mobility::Backends::Serialized} backend for ActiveRecord models.
30
30
  include ActiveRecord
31
31
  include HashValued
32
32
 
33
+ def self.valid_keys
34
+ super + [:format]
35
+ end
36
+
33
37
  # @!group Backend Configuration
34
38
  # @param (see Backends::Serialized.configure)
35
39
  # @option (see Backends::Serialized.configure)
@@ -72,5 +76,7 @@ Implements {Mobility::Backends::Serialized} backend for ActiveRecord models.
72
76
  EOM
73
77
  end
74
78
  end
79
+
80
+ register_backend(:active_record_serialized, ActiveRecord::Serialized)
75
81
  end
76
82
  end
@@ -1,7 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
  require "mobility/backends/active_record"
3
3
  require "mobility/backends/table"
4
- require "mobility/active_record/model_translation"
5
4
 
6
5
  module Mobility
7
6
  module Backends
@@ -101,7 +100,7 @@ columns to that table.
101
100
  # @option options [Symbol] subclass_name (:Translation) Name of subclass
102
101
  # to append to model class to generate translation class
103
102
  def configure(options)
104
- table_name = options[:model_class].table_name
103
+ table_name = model_class.table_name
105
104
  options[:table_name] ||= "#{table_name.singularize}_translations"
106
105
  options[:foreign_key] ||= table_name.downcase.singularize.camelize.foreign_key
107
106
  if (association_name = options[:association_name]).present?
@@ -116,10 +115,10 @@ columns to that table.
116
115
 
117
116
  # @param [String] attr Attribute name
118
117
  # @param [Symbol] _locale Locale
119
- # @return [Mobility::Arel::Attribute] Arel node for column on translation table
118
+ # @return [Mobility::Plugins::Arel::Attribute] Arel node for column on translation table
120
119
  def build_node(attr, locale)
121
120
  aliased_table = model_class.const_get(subclass_name).arel_table.alias(table_alias(locale))
122
- Arel::Attribute.new(aliased_table, attr, locale, self)
121
+ Plugins::Arel::Attribute.new(aliased_table, attr, locale, self)
123
122
  end
124
123
 
125
124
  # Joins translations using either INNER/OUTER join appropriate to the
@@ -153,7 +152,7 @@ columns to that table.
153
152
  def already_joined?(relation, locale, join_type)
154
153
  if join = get_join(relation, locale)
155
154
  return true if (join_type == Visitor::OUTER_JOIN) || (Visitor::INNER_JOIN === join)
156
- relation.joins_values = relation.joins_values - [join]
155
+ relation.joins_values -= [join]
157
156
  end
158
157
  false
159
158
  end
@@ -190,7 +189,7 @@ columns to that table.
190
189
  # we need an OUTER JOIN. In the second case, one attribute is matched
191
190
  # against a non-nil value, so we can use an INNER JOIN.
192
191
  #
193
- class Visitor < Arel::Visitor
192
+ class Visitor < Plugins::Arel::Visitor
194
193
  private
195
194
 
196
195
  def visit_Arel_Nodes_Equality(object)
@@ -227,7 +226,7 @@ columns to that table.
227
226
  end
228
227
  end
229
228
 
230
- def visit_Mobility_Arel_Attribute(object)
229
+ def visit_Mobility_Plugins_Arel_Attribute(object)
231
230
  # We compare table names here to ensure that attributes defined on
232
231
  # different backends but the same table will correctly get an OUTER
233
232
  # join when required. Use options[:table_name] here since we don't
@@ -245,7 +244,7 @@ columns to that table.
245
244
  if self.const_defined?(subclass_name, false)
246
245
  const_get(subclass_name, false)
247
246
  else
248
- const_set(subclass_name, Class.new(Mobility::ActiveRecord::ModelTranslation))
247
+ const_set(subclass_name, Class.new(Translation))
249
248
  end
250
249
 
251
250
  translation_class.table_name = options[:table_name]
@@ -265,7 +264,7 @@ columns to that table.
265
264
  touch: true
266
265
 
267
266
  before_save do
268
- required_attributes = self.class.mobility_attributes & translation_class.attribute_names
267
+ required_attributes = translation_class.attribute_names.select { |name| self.class.mobility_attribute?(name) }
269
268
  send(association_name).destroy_empty_translations(required_attributes)
270
269
  end
271
270
 
@@ -283,7 +282,7 @@ columns to that table.
283
282
 
284
283
  # Returns translation for a given locale, or builds one if none is present.
285
284
  # @param [Symbol] locale
286
- def translation_for(locale, _)
285
+ def translation_for(locale, **)
287
286
  translation = translations.in_locale(locale)
288
287
  translation ||= translations.build(locale: locale)
289
288
  translation
@@ -303,6 +302,14 @@ columns to that table.
303
302
  destroy(empty_translations) if empty_translations.any?
304
303
  end
305
304
  end
305
+
306
+ # Subclassed dynamically to generate translation class.
307
+ class Translation < ::ActiveRecord::Base
308
+ self.abstract_class = true
309
+ validates :locale, presence: true
310
+ end
306
311
  end
312
+
313
+ register_backend(:active_record_table, ActiveRecord::Table)
307
314
  end
308
315
  end
@@ -27,8 +27,6 @@ be ignored if set, since it would cause a conflict with column accessors.
27
27
 
28
28
  =end
29
29
  module Column
30
- extend Backend::OrmDelegator
31
-
32
30
  # Returns name of column where translated attribute is stored
33
31
  # @param [Symbol] locale
34
32
  # @return [String]
@@ -44,10 +42,6 @@ be ignored if set, since it would cause a conflict with column accessors.
44
42
  normalized_locale = Mobility.normalize_locale(locale)
45
43
  "#{attribute}_#{normalized_locale}".to_sym
46
44
  end
47
-
48
- def self.included(base)
49
- base.extend Backend::OrmDelegator
50
- end
51
45
  end
52
46
  end
53
47
  end
@@ -19,7 +19,16 @@ stored).
19
19
 
20
20
  =end
21
21
  module Container
22
- extend Backend::OrmDelegator
22
+ def self.included(backend_class)
23
+ backend_class.extend ClassMethods
24
+ backend_class.option_reader :column_name
25
+ end
26
+
27
+ module ClassMethods
28
+ def valid_keys
29
+ [:column_name]
30
+ end
31
+ end
23
32
  end
24
33
  end
25
34
  end