mobility 0.8.13 → 1.0.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 (114) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +2 -2
  3. data.tar.gz.sig +0 -0
  4. data/CHANGELOG.md +63 -0
  5. data/Gemfile +5 -2
  6. data/Gemfile.lock +39 -20
  7. data/README.md +183 -93
  8. data/lib/mobility.rb +101 -169
  9. data/lib/mobility/backend.rb +27 -51
  10. data/lib/mobility/backends.rb +20 -0
  11. data/lib/mobility/backends/active_record.rb +4 -0
  12. data/lib/mobility/backends/active_record/column.rb +3 -1
  13. data/lib/mobility/backends/active_record/container.rb +10 -11
  14. data/lib/mobility/backends/active_record/hstore.rb +6 -4
  15. data/lib/mobility/backends/active_record/json.rb +5 -3
  16. data/lib/mobility/backends/active_record/jsonb.rb +5 -3
  17. data/lib/mobility/backends/active_record/key_value.rb +31 -13
  18. data/lib/mobility/backends/active_record/pg_hash.rb +1 -1
  19. data/lib/mobility/backends/active_record/serialized.rb +6 -0
  20. data/lib/mobility/backends/active_record/table.rb +17 -10
  21. data/lib/mobility/backends/column.rb +0 -6
  22. data/lib/mobility/backends/container.rb +10 -1
  23. data/lib/mobility/backends/hash.rb +39 -0
  24. data/lib/mobility/backends/hash_valued.rb +4 -0
  25. data/lib/mobility/backends/hstore.rb +0 -1
  26. data/lib/mobility/backends/json.rb +0 -1
  27. data/lib/mobility/backends/jsonb.rb +1 -2
  28. data/lib/mobility/backends/key_value.rb +31 -26
  29. data/lib/mobility/backends/null.rb +2 -0
  30. data/lib/mobility/backends/sequel.rb +37 -2
  31. data/lib/mobility/backends/sequel/column.rb +2 -0
  32. data/lib/mobility/backends/sequel/container.rb +11 -9
  33. data/lib/mobility/backends/sequel/hstore.rb +3 -1
  34. data/lib/mobility/backends/sequel/json.rb +3 -0
  35. data/lib/mobility/backends/sequel/jsonb.rb +3 -1
  36. data/lib/mobility/backends/sequel/key_value.rb +87 -18
  37. data/lib/mobility/backends/sequel/pg_hash.rb +6 -6
  38. data/lib/mobility/backends/sequel/serialized.rb +6 -0
  39. data/lib/mobility/backends/sequel/table.rb +22 -9
  40. data/lib/mobility/backends/serialized.rb +1 -3
  41. data/lib/mobility/backends/table.rb +39 -31
  42. data/lib/mobility/pluggable.rb +56 -0
  43. data/lib/mobility/plugin.rb +260 -0
  44. data/lib/mobility/plugins.rb +27 -24
  45. data/lib/mobility/plugins/active_model.rb +17 -0
  46. data/lib/mobility/plugins/active_model/cache.rb +26 -0
  47. data/lib/mobility/plugins/active_model/dirty.rb +119 -78
  48. data/lib/mobility/plugins/active_record.rb +37 -0
  49. data/lib/mobility/plugins/active_record/backend.rb +27 -0
  50. data/lib/mobility/plugins/active_record/cache.rb +28 -0
  51. data/lib/mobility/plugins/active_record/dirty.rb +34 -17
  52. data/lib/mobility/plugins/active_record/query.rb +43 -31
  53. data/lib/mobility/plugins/active_record/uniqueness_validation.rb +64 -0
  54. data/lib/mobility/plugins/arel.rb +125 -0
  55. data/lib/mobility/plugins/arel/nodes.rb +15 -0
  56. data/lib/mobility/plugins/arel/nodes/pg_ops.rb +134 -0
  57. data/lib/mobility/plugins/attribute_methods.rb +29 -20
  58. data/lib/mobility/plugins/attributes.rb +72 -0
  59. data/lib/mobility/plugins/backend.rb +161 -0
  60. data/lib/mobility/plugins/backend_reader.rb +34 -0
  61. data/lib/mobility/plugins/cache.rb +68 -26
  62. data/lib/mobility/plugins/default.rb +22 -17
  63. data/lib/mobility/plugins/dirty.rb +12 -33
  64. data/lib/mobility/plugins/fallbacks.rb +52 -44
  65. data/lib/mobility/plugins/fallthrough_accessors.rb +19 -23
  66. data/lib/mobility/plugins/locale_accessors.rb +22 -35
  67. data/lib/mobility/plugins/presence.rb +28 -21
  68. data/lib/mobility/plugins/query.rb +8 -17
  69. data/lib/mobility/plugins/reader.rb +50 -0
  70. data/lib/mobility/plugins/sequel.rb +34 -0
  71. data/lib/mobility/plugins/sequel/backend.rb +25 -0
  72. data/lib/mobility/plugins/sequel/cache.rb +24 -0
  73. data/lib/mobility/plugins/sequel/dirty.rb +34 -23
  74. data/lib/mobility/plugins/sequel/query.rb +21 -6
  75. data/lib/mobility/plugins/writer.rb +44 -0
  76. data/lib/mobility/translations.rb +95 -0
  77. data/lib/mobility/version.rb +12 -1
  78. data/lib/rails/generators/mobility/templates/create_string_translations.rb +0 -1
  79. data/lib/rails/generators/mobility/templates/create_text_translations.rb +0 -1
  80. data/lib/rails/generators/mobility/templates/initializer.rb +104 -78
  81. metadata +35 -40
  82. metadata.gz.sig +0 -0
  83. data/lib/mobility/active_model.rb +0 -4
  84. data/lib/mobility/active_model/backend_resetter.rb +0 -26
  85. data/lib/mobility/active_record.rb +0 -23
  86. data/lib/mobility/active_record/backend_resetter.rb +0 -26
  87. data/lib/mobility/active_record/model_translation.rb +0 -14
  88. data/lib/mobility/active_record/string_translation.rb +0 -10
  89. data/lib/mobility/active_record/text_translation.rb +0 -10
  90. data/lib/mobility/active_record/translation.rb +0 -14
  91. data/lib/mobility/active_record/uniqueness_validator.rb +0 -60
  92. data/lib/mobility/arel.rb +0 -49
  93. data/lib/mobility/arel/nodes.rb +0 -13
  94. data/lib/mobility/arel/nodes/pg_ops.rb +0 -132
  95. data/lib/mobility/arel/visitor.rb +0 -61
  96. data/lib/mobility/attributes.rb +0 -324
  97. data/lib/mobility/backend/orm_delegator.rb +0 -44
  98. data/lib/mobility/backend_resetter.rb +0 -50
  99. data/lib/mobility/configuration.rb +0 -138
  100. data/lib/mobility/fallbacks.rb +0 -28
  101. data/lib/mobility/interface.rb +0 -0
  102. data/lib/mobility/loaded.rb +0 -4
  103. data/lib/mobility/plugins/active_record/attribute_methods.rb +0 -38
  104. data/lib/mobility/plugins/cache/translation_cacher.rb +0 -40
  105. data/lib/mobility/sequel.rb +0 -9
  106. data/lib/mobility/sequel/backend_resetter.rb +0 -23
  107. data/lib/mobility/sequel/column_changes.rb +0 -28
  108. data/lib/mobility/sequel/hash_initializer.rb +0 -21
  109. data/lib/mobility/sequel/model_translation.rb +0 -20
  110. data/lib/mobility/sequel/sql.rb +0 -16
  111. data/lib/mobility/sequel/string_translation.rb +0 -10
  112. data/lib/mobility/sequel/text_translation.rb +0 -10
  113. data/lib/mobility/sequel/translation.rb +0 -53
  114. data/lib/mobility/translates.rb +0 -73
@@ -0,0 +1,39 @@
1
+ module Mobility
2
+ module Backends
3
+ =begin
4
+
5
+ Backend which stores translations in an in-memory hash.
6
+
7
+ =end
8
+ class Hash
9
+ include Backend
10
+
11
+ # @!group Backend Accessors
12
+ # @!macro backend_reader
13
+ # @return [Object]
14
+ def read(locale, _ = {})
15
+ translations[locale]
16
+ end
17
+
18
+ # @!macro backend_writer
19
+ # @return [Object]
20
+ def write(locale, value, _ = {})
21
+ translations[locale] = value
22
+ end
23
+ # @!endgroup
24
+
25
+ # @!macro backend_iterator
26
+ def each_locale
27
+ translations.each { |l, _| yield l }
28
+ end
29
+
30
+ private
31
+
32
+ def translations
33
+ @translations ||= {}
34
+ end
35
+ end
36
+
37
+ register_backend(:hash, Hash)
38
+ end
39
+ end
@@ -36,6 +36,10 @@ Defines read and write methods that access the value at a key with value
36
36
  end
37
37
 
38
38
  module ClassMethods
39
+ def valid_keys
40
+ [:column_prefix, :column_suffix]
41
+ end
42
+
39
43
  def configure(options)
40
44
  options[:column_affix] = "#{options[:column_prefix]}%s#{options[:column_suffix]}"
41
45
  end
@@ -17,7 +17,6 @@ Prefix and suffix to add to attribute name to generate hstore column name.
17
17
 
18
18
  =end
19
19
  module Hstore
20
- extend Backend::OrmDelegator
21
20
  end
22
21
  end
23
22
  end
@@ -16,7 +16,6 @@ Prefix and suffix to add to attribute name to generate json column name.
16
16
 
17
17
  =end
18
18
  module Json
19
- extend Backend::OrmDelegator
20
19
  end
21
20
  end
22
21
  end
@@ -6,7 +6,7 @@ Stores translations as hash on Postgres jsonb column.
6
6
 
7
7
  ==Backend Options
8
8
 
9
- ===+prefix+ and +suffix+
9
+ ===+column_prefix+ and +column_suffix+
10
10
 
11
11
  Prefix and suffix to add to attribute name to generate jsonb column name.
12
12
 
@@ -16,7 +16,6 @@ Prefix and suffix to add to attribute name to generate jsonb column name.
16
16
 
17
17
  =end
18
18
  module Jsonb
19
- extend Backend::OrmDelegator
20
19
  end
21
20
  end
22
21
  end
@@ -44,8 +44,6 @@ other backends on model (otherwise one will overwrite the other).
44
44
 
45
45
  =end
46
46
  module KeyValue
47
- extend Backend::OrmDelegator
48
-
49
47
  # @!method association_name
50
48
  # Returns the name of the polymorphic association.
51
49
  # @return [Symbol] Name of the association
@@ -56,13 +54,13 @@ other backends on model (otherwise one will overwrite the other).
56
54
 
57
55
  # @!group Backend Accessors
58
56
  # @!macro backend_reader
59
- def read(locale, options = {})
60
- translation_for(locale, options).value
57
+ def read(locale, **options)
58
+ translation_for(locale, **options).value
61
59
  end
62
60
 
63
61
  # @!macro backend_writer
64
- def write(locale, value, options = {})
65
- translation_for(locale, options).value = value
62
+ def write(locale, value, **options)
63
+ translation_for(locale, **options).value = value
66
64
  end
67
65
  # @!endgroup
68
66
 
@@ -85,9 +83,13 @@ other backends on model (otherwise one will overwrite the other).
85
83
  end
86
84
 
87
85
  module ClassMethods
86
+ def valid_keys
87
+ [:type, :association_name, :class_name]
88
+ end
89
+
88
90
  # @!group Backend Configuration
89
91
  # @option options [Symbol,String] type Column type to use
90
- # @option options [Symbol] associaiton_name (:<type>_translations) Name
92
+ # @option options [Symbol] association_name (:<type>_translations) Name
91
93
  # of association method, defaults to +<type>_translations+
92
94
  # @option options [Symbol] class_name Translation class, defaults to
93
95
  # +Mobility::<ORM>::<type>Translation+
@@ -98,27 +100,13 @@ other backends on model (otherwise one will overwrite the other).
98
100
  options[:association_name] &&= options[:association_name].to_sym
99
101
  options[:class_name] &&= Util.constantize(options[:class_name])
100
102
  if !(options[:type] || (options[:class_name] && options[:association_name]))
101
- # TODO: Remove warning and raise ArgumentError in v1.0
102
- warn %{
103
- WARNING: In previous versions, the Mobility KeyValue backend defaulted to a
104
- text type column, but this behavior is now deprecated and will be removed in
105
- the next release. Either explicitly specify the type by passing type: :text in
106
- each translated model, or set a default option in your configuration.
107
- }
108
- options[:type] = :text
103
+ raise ArgumentError, "KeyValue backend requires an explicit type option, either text or string."
109
104
  end
110
105
  end
111
106
 
112
- # Apply custom processing for plugin
113
- # @param (see Backend::Setup#apply_plugin)
114
- # @return (see Backend::Setup#apply_plugin)
115
- def apply_plugin(name)
116
- if name == :cache
117
- include self::Cache
118
- true
119
- else
120
- super
121
- end
107
+ # Apply custom processing for cache plugin
108
+ def include_cache
109
+ include self::Cache
122
110
  end
123
111
 
124
112
  def table_alias(attr, locale)
@@ -127,7 +115,24 @@ each translated model, or set a default option in your configuration.
127
115
  end
128
116
 
129
117
  module Cache
130
- include Plugins::Cache::TranslationCacher.new(:translation_for)
118
+ def translation_for(locale, **options)
119
+ return super(locale, options) if options.delete(:cache) == false
120
+ if cache.has_key?(locale)
121
+ cache[locale]
122
+ else
123
+ cache[locale] = super(locale, **options)
124
+ end
125
+ end
126
+
127
+ def clear_cache
128
+ @cache = {}
129
+ end
130
+
131
+ private
132
+
133
+ def cache
134
+ @cache ||= {}
135
+ end
131
136
  end
132
137
  end
133
138
  end
@@ -20,5 +20,7 @@ Backend which does absolutely nothing. Mostly for testing purposes.
20
20
  def self.configure(_); end
21
21
  # @!endgroup
22
22
  end
23
+
24
+ register_backend(:null, Null)
23
25
  end
24
26
  end
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+ require "mobility/backend"
3
+
1
4
  module Mobility
2
5
  module Backends
3
6
  module Sequel
4
7
  def self.included(backend_class)
5
- backend_class.include(Backend)
6
- backend_class.extend(ClassMethods)
8
+ backend_class.include Backend
9
+ backend_class.extend ClassMethods
7
10
  end
8
11
 
9
12
  module ClassMethods
@@ -27,6 +30,38 @@ module Mobility
27
30
  def prepare_dataset(dataset, _predicate, _locale)
28
31
  dataset
29
32
  end
33
+
34
+ # Forces Sequel to notice changes when Mobility setter method is
35
+ # called.
36
+ # TODO: Find a better way to do this.
37
+ def define_column_changes(mod, attributes, column_affix: "%s")
38
+ mod.class_eval do
39
+ attributes.each do |attribute|
40
+ define_method "#{attribute}=" do |value, **options|
41
+ if !options[:super] && send(attribute) != value
42
+ locale = options[:locale] || Mobility.locale
43
+ column = (column_affix % attribute).to_sym
44
+ attribute_with_locale = :"#{attribute}_#{Mobility.normalize_locale(locale)}"
45
+ @changed_columns = changed_columns | [column, attribute.to_sym, attribute_with_locale]
46
+ end
47
+ super(value, **options)
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ # Initialize column value(s) by default to a hash.
54
+ # TODO: Find a better way to do this.
55
+ def define_hash_initializer(mod, columns)
56
+ mod.class_eval do
57
+ class_eval <<-EOM, __FILE__, __LINE__ + 1
58
+ def initialize_set(values)
59
+ #{columns.map { |c| "self[:#{c}] = {}" }.join(';')}
60
+ super
61
+ end
62
+ EOM
63
+ end
64
+ end
30
65
  end
31
66
  end
32
67
  end
@@ -50,5 +50,7 @@ Implements the {Mobility::Backends::Column} backend for Sequel models.
50
50
  end.compact
51
51
  end
52
52
  end
53
+
54
+ register_backend(:sequel_column, Sequel::Column)
53
55
  end
54
56
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
- require "mobility/backends/sequel/json"
2
+ require "mobility/backends/sequel"
3
3
  require "mobility/backends/sequel/jsonb"
4
+ require "mobility/backends/container"
4
5
 
5
6
  module Mobility
6
7
  module Backends
@@ -11,10 +12,7 @@ Implements the {Mobility::Backends::Container} backend for Sequel models.
11
12
  =end
12
13
  class Sequel::Container
13
14
  include Sequel
14
-
15
- # @!method column_name
16
- # @return [Symbol] (:translations) Name of translations column
17
- option_reader :column_name
15
+ include Container
18
16
 
19
17
  # @!group Backend Accessors
20
18
  #
@@ -44,7 +42,7 @@ Implements the {Mobility::Backends::Container} backend for Sequel models.
44
42
  def self.configure(options)
45
43
  options[:column_name] ||= :translations
46
44
  options[:column_name] = options[:column_name].to_sym
47
- column_name, db_schema = options[:column_name], options[:model_class].db_schema
45
+ column_name, db_schema = options[:column_name], model_class.db_schema
48
46
  options[:column_type] = db_schema[column_name] && (db_schema[column_name][:db_type]).to_sym
49
47
  unless %i[json jsonb].include?(options[:column_type])
50
48
  raise InvalidColumnType, "#{options[:column_name]} must be a column of type json or jsonb"
@@ -59,9 +57,11 @@ Implements the {Mobility::Backends::Container} backend for Sequel models.
59
57
  end
60
58
  end
61
59
 
60
+ backend = self
61
+
62
62
  setup do |attributes, options|
63
63
  column_name = options[:column_name]
64
- before_validation = Module.new do
64
+ mod = Module.new do
65
65
  define_method :before_validation do
66
66
  self[column_name].each do |k, v|
67
67
  v.delete_if { |_locale, translation| Util.blank?(translation) }
@@ -70,8 +70,8 @@ Implements the {Mobility::Backends::Container} backend for Sequel models.
70
70
  super()
71
71
  end
72
72
  end
73
- include before_validation
74
- include Mobility::Sequel::HashInitializer.new(column_name)
73
+ include mod
74
+ backend.define_hash_initializer(mod, [column_name])
75
75
 
76
76
  plugin :defaults_setter
77
77
  attributes.each { |attribute| default_values[attribute.to_sym] = {} }
@@ -114,5 +114,7 @@ Implements the {Mobility::Backends::Container} backend for Sequel models.
114
114
  end
115
115
  end
116
116
  end
117
+
118
+ register_backend(:sequel_container, Sequel::Container)
117
119
  end
118
120
  end
@@ -20,7 +20,7 @@ Implements the {Mobility::Backends::Hstore} backend for Sequel models.
20
20
  # @!group Backend Accessors
21
21
  # @!macro backend_writer
22
22
  def write(locale, value, options = {})
23
- super(locale, value && value.to_s, options)
23
+ super(locale, value && value.to_s, **options)
24
24
  end
25
25
  # @!endgroup
26
26
 
@@ -35,5 +35,7 @@ Implements the {Mobility::Backends::Hstore} backend for Sequel models.
35
35
  class HStoreOp < ::Sequel::Postgres::HStoreOp; end
36
36
  end
37
37
  end
38
+
39
+ register_backend(:sequel_hstore, Sequel::Hstore)
38
40
  end
39
41
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'mobility/backends/sequel/pg_hash'
2
3
 
3
4
  Sequel.extension :pg_json, :pg_json_ops
@@ -42,5 +43,7 @@ Implements the {Mobility::Backends::Json} backend for Sequel models.
42
43
  class JSONOp < ::Sequel::Postgres::JSONOp; end
43
44
  end
44
45
  end
46
+
47
+ register_backend(:sequel_json, Sequel::Json)
45
48
  end
46
49
  end
@@ -55,7 +55,7 @@ Implements the {Mobility::Backends::Jsonb} backend for Sequel models.
55
55
 
56
56
  def =~(other)
57
57
  case other
58
- when Integer, Hash
58
+ when Integer, ::Hash
59
59
  to_dash_arrow =~ other.to_json
60
60
  when NilClass
61
61
  ~to_question
@@ -66,5 +66,7 @@ Implements the {Mobility::Backends::Jsonb} backend for Sequel models.
66
66
  end
67
67
  end
68
68
  end
69
+
70
+ register_backend(:sequel_jsonb, Sequel::Jsonb)
69
71
  end
70
72
  end
@@ -2,11 +2,6 @@
2
2
  require "mobility/util"
3
3
  require "mobility/backends/sequel"
4
4
  require "mobility/backends/key_value"
5
- require "mobility/sequel/column_changes"
6
- require "mobility/sequel/hash_initializer"
7
- require "mobility/sequel/string_translation"
8
- require "mobility/sequel/text_translation"
9
- require "mobility/sequel/sql"
10
5
 
11
6
  module Mobility
12
7
  module Backends
@@ -34,16 +29,16 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
34
29
  super
35
30
  if type = options[:type]
36
31
  options[:association_name] ||= :"#{options[:type]}_translations"
37
- options[:class_name] ||= Mobility::Sequel.const_get("#{type.capitalize}Translation")
32
+ options[:class_name] ||= const_get("#{type.capitalize}Translation")
38
33
  end
39
- options[:table_alias_affix] = "#{options[:model_class]}_%s_#{options[:association_name]}"
34
+ options[:table_alias_affix] = "#{model_class}_%s_#{options[:association_name]}"
40
35
  rescue NameError
41
36
  raise ArgumentError, "You must define a Mobility::Sequel::#{type.capitalize}Translation class."
42
37
  end
43
38
  # @!endgroup
44
39
 
45
40
  def build_op(attr, locale)
46
- ::Mobility::Sequel::SQL::QualifiedIdentifier.new(table_alias(attr, locale), :value, locale, self, attribute_name: attr)
41
+ QualifiedIdentifier.new(table_alias(attr, locale), :value, locale, self, attr)
47
42
  end
48
43
 
49
44
  # @param [Sequel::Dataset] dataset Dataset to prepare
@@ -75,7 +70,7 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
75
70
  case predicate
76
71
  when Array
77
72
  visit_collection(predicate, locale)
78
- when ::Mobility::Sequel::SQL::QualifiedIdentifier
73
+ when QualifiedIdentifier
79
74
  visit_sql_identifier(predicate, locale)
80
75
  when ::Sequel::SQL::BooleanExpression
81
76
  visit_boolean(predicate, locale)
@@ -93,7 +88,7 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
93
88
  join_type = nils.empty? ? :inner : :left_outer
94
89
  # TODO: simplify to hash.transform_values { join_type } when
95
90
  # support for Ruby 2.3 is deprecated
96
- Hash[hash.keys.map { |key| [key, join_type] }]
91
+ ::Hash[hash.keys.map { |key| [key, join_type] }]
97
92
  else
98
93
  {}
99
94
  end
@@ -101,13 +96,13 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
101
96
  hash = visit(boolean.args, locale)
102
97
  # TODO: simplify to hash.transform_values { :inner } when
103
98
  # support for Ruby 2.3 is deprecated
104
- Hash[hash.keys.map { |key| [key, :inner] }]
99
+ ::Hash[hash.keys.map { |key| [key, :inner] }]
105
100
  elsif boolean.op == :OR
106
101
  hash = boolean.args.map { |op| visit(op, locale) }.
107
- compact.inject(&:merge)
102
+ compact.inject(:merge)
108
103
  # TODO: simplify to hash.transform_values { :left_outer } when
109
104
  # support for Ruby 2.3 is deprecated
110
- Hash[hash.keys.map { |key| [key, :left_outer] }]
105
+ ::Hash[hash.keys.map { |key| [key, :left_outer] }]
111
106
  else
112
107
  visit(boolean.args, locale)
113
108
  end
@@ -128,6 +123,8 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
128
123
  end
129
124
  end
130
125
 
126
+ backend = self
127
+
131
128
  setup do |attributes, options|
132
129
  association_name = options[:association_name]
133
130
  translations_class = options[:class_name]
@@ -153,19 +150,20 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
153
150
  end
154
151
  define_method :after_save do
155
152
  super()
156
- attributes.each { |attribute| public_send(Backend.method_name(attribute)).save_translations }
153
+ attributes.each { |attribute| mobility_backends[attribute].save_translations }
157
154
  end
158
155
  end
159
156
  include callback_methods
160
157
 
161
158
  include DestroyKeyValueTranslations
162
- include Mobility::Sequel::ColumnChanges.new(attributes)
159
+ include(mod = Module.new)
160
+ backend.define_column_changes(mod, attributes)
163
161
  end
164
162
 
165
163
  # Returns translation for a given locale, or initializes one if none is present.
166
164
  # @param [Symbol] locale
167
- # @return [Mobility::Sequel::TextTranslation,Mobility::Sequel::StringTranslation]
168
- def translation_for(locale, _)
165
+ # @return [Mobility::Backends::Sequel::KeyValue::TextTranslation,Mobility::Backends::Sequel::KeyValue::StringTranslation]
166
+ def translation_for(locale, **)
169
167
  translation = model.send(association_name).find { |t| t.key == attribute && t.locale == locale.to_s }
170
168
  translation ||= class_name.new(locale: locale, key: attribute)
171
169
  translation
@@ -184,7 +182,7 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
184
182
  def after_destroy
185
183
  super
186
184
  [:string, :text].freeze.each do |type|
187
- Mobility::Sequel.const_get("#{type.capitalize}Translation").
185
+ Mobility::Backends::Sequel::KeyValue.const_get("#{type.capitalize}Translation").
188
186
  where(translatable_id: id, translatable_type: self.class.name).destroy
189
187
  end
190
188
  end
@@ -201,6 +199,77 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
201
199
  (model.send(association_name) + cache.values).uniq
202
200
  end
203
201
  end
202
+
203
+ class QualifiedIdentifier < ::Sequel::SQL::QualifiedIdentifier
204
+ attr_reader :backend_class, :locale, :attribute_name
205
+
206
+ def initialize(table, column, locale, backend_class, attribute_name)
207
+ @backend_class = backend_class
208
+ @locale = locale
209
+ @attribute_name = attribute_name || column
210
+ super(table, column)
211
+ end
212
+ end
213
+
214
+ module Translation
215
+ def self.included(base)
216
+ base.class_eval do
217
+ plugin :validation_helpers
218
+
219
+ # Paraphased from sequel_polymorphic gem
220
+ #
221
+ model = underscore(self.to_s)
222
+ plural_model = pluralize(model)
223
+ many_to_one :translatable,
224
+ reciprocal: plural_model.to_sym,
225
+ reciprocal_type: :many_to_one,
226
+ setter: (proc do |able_instance|
227
+ self[:translatable_id] = (able_instance.pk if able_instance)
228
+ self[:translatable_type] = (able_instance.class.name if able_instance)
229
+ end),
230
+ dataset: (proc do
231
+ translatable_type = send :translatable_type
232
+ translatable_id = send :translatable_id
233
+ return if translatable_type.nil? || translatable_id.nil?
234
+ klass = self.class.send(:constantize, translatable_type)
235
+ klass.where(klass.primary_key => translatable_id)
236
+ end),
237
+ eager_loader: (proc do |eo|
238
+ id_map = {}
239
+ eo[:rows].each do |model|
240
+ model_able_type = model.send :translatable_type
241
+ model_able_id = model.send :translatable_id
242
+ model.associations[:translatable] = nil
243
+ ((id_map[model_able_type] ||= {})[model_able_id] ||= []) << model if !model_able_type.nil? && !model_able_id.nil?
244
+ end
245
+ id_map.each do |klass_name, id_map|
246
+ klass = constantize(camelize(klass_name))
247
+ klass.where(klass.primary_key=>id_map.keys).all do |related_obj|
248
+ id_map[related_obj.pk].each do |model|
249
+ model.associations[:translatable] = related_obj
250
+ end
251
+ end
252
+ end
253
+ end)
254
+
255
+ def validate
256
+ super
257
+ validates_presence [:locale, :key, :translatable_id, :translatable_type]
258
+ validates_unique [:locale, :key, :translatable_id, :translatable_type]
259
+ end
260
+ end
261
+ end
262
+ end
263
+
264
+ class TextTranslation < ::Sequel::Model(:mobility_text_translations)
265
+ include Translation
266
+ end
267
+
268
+ class StringTranslation < ::Sequel::Model(:mobility_string_translations)
269
+ include Translation
270
+ end
204
271
  end
272
+
273
+ register_backend(:sequel_key_value, Sequel::KeyValue)
205
274
  end
206
275
  end