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
@@ -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