mobility 0.1.20 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -0
  4. data/CHANGELOG.md +17 -0
  5. data/CONTRIBUTING.md +55 -0
  6. data/Gemfile +2 -0
  7. data/Gemfile.lock +2 -56
  8. data/README.md +64 -15
  9. data/Rakefile +17 -17
  10. data/lib/mobility.rb +43 -40
  11. data/lib/mobility/active_model.rb +0 -1
  12. data/lib/mobility/active_model/backend_resetter.rb +1 -1
  13. data/lib/mobility/active_record.rb +8 -15
  14. data/lib/mobility/active_record/backend_resetter.rb +2 -0
  15. data/lib/mobility/active_record/model_translation.rb +1 -1
  16. data/lib/mobility/active_record/string_translation.rb +2 -0
  17. data/lib/mobility/active_record/text_translation.rb +2 -0
  18. data/lib/mobility/attributes.rb +74 -71
  19. data/lib/mobility/backend.rb +64 -25
  20. data/lib/mobility/backend/orm_delegator.rb +17 -7
  21. data/lib/mobility/backend/stringify_locale.rb +18 -0
  22. data/lib/mobility/backend_resetter.rb +8 -5
  23. data/lib/mobility/backends.rb +4 -0
  24. data/lib/mobility/backends/active_record.rb +20 -0
  25. data/lib/mobility/{backend → backends}/active_record/column.rb +25 -13
  26. data/lib/mobility/{backend → backends}/active_record/column/query_methods.rb +5 -3
  27. data/lib/mobility/backends/active_record/hstore.rb +29 -0
  28. data/lib/mobility/{backend → backends}/active_record/hstore/query_methods.rb +2 -2
  29. data/lib/mobility/{backend → backends}/active_record/jsonb.rb +6 -6
  30. data/lib/mobility/{backend → backends}/active_record/jsonb/query_methods.rb +4 -2
  31. data/lib/mobility/{backend → backends}/active_record/key_value.rb +10 -44
  32. data/lib/mobility/{backend → backends}/active_record/key_value/query_methods.rb +4 -2
  33. data/lib/mobility/{backend/active_record/hash_valued.rb → backends/active_record/pg_hash.rb} +13 -21
  34. data/lib/mobility/{backend → backends}/active_record/query_methods.rb +2 -2
  35. data/lib/mobility/{backend → backends}/active_record/serialized.rb +12 -28
  36. data/lib/mobility/{backend → backends}/active_record/serialized/query_methods.rb +5 -8
  37. data/lib/mobility/{backend → backends}/active_record/table.rb +11 -60
  38. data/lib/mobility/{backend → backends}/active_record/table/query_methods.rb +5 -3
  39. data/lib/mobility/{backend → backends}/column.rb +8 -4
  40. data/lib/mobility/backends/hash_valued.rb +29 -0
  41. data/lib/mobility/{backend → backends}/hstore.rb +4 -4
  42. data/lib/mobility/{backend → backends}/jsonb.rb +4 -4
  43. data/lib/mobility/backends/key_value.rb +111 -0
  44. data/lib/mobility/{backend → backends}/null.rb +4 -4
  45. data/lib/mobility/backends/sequel.rb +20 -0
  46. data/lib/mobility/backends/sequel/column.rb +52 -0
  47. data/lib/mobility/{backend → backends}/sequel/column/query_methods.rb +5 -3
  48. data/lib/mobility/backends/sequel/hstore.rb +29 -0
  49. data/lib/mobility/{backend → backends}/sequel/hstore/query_methods.rb +4 -3
  50. data/lib/mobility/{backend → backends}/sequel/jsonb.rb +6 -6
  51. data/lib/mobility/{backend → backends}/sequel/jsonb/query_methods.rb +4 -3
  52. data/lib/mobility/{backend → backends}/sequel/key_value.rb +28 -39
  53. data/lib/mobility/{backend → backends}/sequel/key_value/query_methods.rb +4 -5
  54. data/lib/mobility/backends/sequel/pg_hash.rb +46 -0
  55. data/lib/mobility/{backend → backends}/sequel/postgres_query_methods.rb +1 -2
  56. data/lib/mobility/{backend → backends}/sequel/query_methods.rb +6 -4
  57. data/lib/mobility/{backend → backends}/sequel/serialized.rb +17 -38
  58. data/lib/mobility/backends/sequel/serialized/query_methods.rb +17 -0
  59. data/lib/mobility/{backend → backends}/sequel/table.rb +29 -60
  60. data/lib/mobility/{backend → backends}/sequel/table/query_methods.rb +5 -3
  61. data/lib/mobility/{backend → backends}/serialized.rb +27 -5
  62. data/lib/mobility/{backend → backends}/table.rb +69 -29
  63. data/lib/mobility/configuration.rb +40 -0
  64. data/lib/mobility/{orm.rb → loaded.rb} +0 -0
  65. data/lib/mobility/plugins.rb +35 -0
  66. data/lib/mobility/plugins/active_model.rb +6 -0
  67. data/lib/mobility/plugins/active_model/dirty.rb +81 -0
  68. data/lib/mobility/plugins/active_record.rb +6 -0
  69. data/lib/mobility/plugins/active_record/dirty.rb +59 -0
  70. data/lib/mobility/plugins/cache.rb +54 -0
  71. data/lib/mobility/plugins/cache/translation_cacher.rb +40 -0
  72. data/lib/mobility/plugins/default.rb +73 -0
  73. data/lib/mobility/plugins/dirty.rb +61 -0
  74. data/lib/mobility/{backend → plugins}/fallbacks.rb +36 -31
  75. data/lib/mobility/plugins/fallthrough_accessors.rb +66 -0
  76. data/lib/mobility/plugins/locale_accessors.rb +84 -0
  77. data/lib/mobility/{backend → plugins}/presence.rb +15 -6
  78. data/lib/mobility/plugins/sequel.rb +6 -0
  79. data/lib/mobility/plugins/sequel/dirty.rb +59 -0
  80. data/lib/mobility/sequel.rb +5 -14
  81. data/lib/mobility/sequel/backend_resetter.rb +4 -6
  82. data/lib/mobility/sequel/column_changes.rb +4 -4
  83. data/lib/mobility/sequel/model_translation.rb +1 -1
  84. data/lib/mobility/sequel/string_translation.rb +2 -0
  85. data/lib/mobility/sequel/text_translation.rb +2 -0
  86. data/lib/mobility/translates.rb +1 -5
  87. data/lib/mobility/util.rb +126 -0
  88. data/lib/mobility/version.rb +1 -1
  89. data/lib/mobility/wrapper.rb +1 -1
  90. data/lib/rails/generators/mobility/translations_generator.rb +7 -3
  91. metadata +85 -55
  92. metadata.gz.sig +0 -0
  93. data/lib/mobility/backend/active_model.rb +0 -7
  94. data/lib/mobility/backend/active_model/dirty.rb +0 -95
  95. data/lib/mobility/backend/active_record.rb +0 -29
  96. data/lib/mobility/backend/active_record/dirty.rb +0 -54
  97. data/lib/mobility/backend/active_record/hstore.rb +0 -29
  98. data/lib/mobility/backend/cache.rb +0 -117
  99. data/lib/mobility/backend/dirty.rb +0 -38
  100. data/lib/mobility/backend/key_value.rb +0 -85
  101. data/lib/mobility/backend/sequel.rb +0 -29
  102. data/lib/mobility/backend/sequel/column.rb +0 -39
  103. data/lib/mobility/backend/sequel/dirty.rb +0 -57
  104. data/lib/mobility/backend/sequel/hash_valued.rb +0 -51
  105. data/lib/mobility/backend/sequel/hstore.rb +0 -29
  106. data/lib/mobility/backend/sequel/serialized/query_methods.rb +0 -20
  107. data/lib/mobility/core_ext/object.rb +0 -30
  108. data/lib/mobility/core_ext/string.rb +0 -16
  109. data/lib/mobility/fallthrough_accessors.rb +0 -57
  110. data/lib/mobility/locale_accessors.rb +0 -55
@@ -1,5 +1,5 @@
1
1
  module Mobility
2
- module Backend
2
+ module Backends
3
3
 
4
4
  =begin
5
5
 
@@ -9,13 +9,13 @@ Stores translations as hash on Postgres hstore column.
9
9
 
10
10
  This backend has no options.
11
11
 
12
- @see Mobility::Backend::ActiveRecord::Hstore
13
- @see Mobility::Backend::Sequel::Hstore
12
+ @see Mobility::Backends::ActiveRecord::Hstore
13
+ @see Mobility::Backends::Sequel::Hstore
14
14
  @see https://www.postgresql.org/docs/current/static/hstore.html PostgreSQL Documentation for hstore
15
15
 
16
16
  =end
17
17
  module Hstore
18
- include OrmDelegator
18
+ extend Backend::OrmDelegator
19
19
  end
20
20
  end
21
21
  end
@@ -1,5 +1,5 @@
1
1
  module Mobility
2
- module Backend
2
+ module Backends
3
3
 
4
4
  =begin
5
5
 
@@ -9,13 +9,13 @@ Stores translations as hash on Postgres jsonb column.
9
9
 
10
10
  This backend has no options.
11
11
 
12
- @see Mobility::Backend::ActiveRecord::Jsonb
13
- @see Mobility::Backend::Sequel::Jsonb
12
+ @see Mobility::Backends::ActiveRecord::Jsonb
13
+ @see Mobility::Backends::Sequel::Jsonb
14
14
  @see https://www.postgresql.org/docs/current/static/datatype-json.html PostgreSQL Documentation for JSON Types
15
15
 
16
16
  =end
17
17
  module Jsonb
18
- include OrmDelegator
18
+ extend Backend::OrmDelegator
19
19
  end
20
20
  end
21
21
  end
@@ -0,0 +1,111 @@
1
+ require "mobility/plugins/cache"
2
+
3
+ module Mobility
4
+ module Backends
5
+ =begin
6
+
7
+ Stores attribute translation as attribute/value pair on a shared translations
8
+ table, using a polymorphic relationship between a translation class and models
9
+ using the backend. By default, two tables are assumed to be present supporting
10
+ string and text translations: a +mobility_text_translations+ table for
11
+ text-valued translations and a +string_translations+ table for
12
+ string-valued translations (the only difference being the column type of the
13
+ +value+ column on the table).
14
+
15
+ ==Backend Options
16
+
17
+ ===+association_name+
18
+
19
+ Name of association on model. Defaults to +text_translations+ (if +type+ is
20
+ +:text+) or +string_translations+ (if +type+ is +:string+). If specified,
21
+ ensure name does not overlap with other methods on model or with the
22
+ association name used by other backends on model (otherwise one will overwrite
23
+ the other).
24
+
25
+ ===+type+
26
+
27
+ Currently, either +:text+ or +:string+ is supported. Determines which class to
28
+ use for translations, which in turn determines which table to use to store
29
+ translations (by default +text_translations+ for text type,
30
+ +string_translations+ for string type).
31
+
32
+ ===+class_name+
33
+
34
+ Class to use for translations when defining association. By default,
35
+ {Mobility::ActiveRecord::TextTranslation} or
36
+ {Mobility::ActiveRecord::StringTranslation} for ActiveRecord models (similar
37
+ for Sequel models). If string is passed in, it will be constantized to get the
38
+ class.
39
+
40
+ @see Mobility::Backends::ActiveRecord::KeyValue
41
+ @see Mobility::Backends::Sequel::KeyValue
42
+
43
+ =end
44
+ module KeyValue
45
+ extend Backend::OrmDelegator
46
+
47
+ # @return [Symbol] name of the association
48
+ attr_reader :association_name
49
+
50
+ # @!macro backend_constructor
51
+ # @option options [Symbol] association_name Name of association
52
+ def initialize(model, attribute, options = {})
53
+ super
54
+ @association_name = options[:association_name]
55
+ end
56
+
57
+ # @!group Backend Accessors
58
+ # @!macro backend_reader
59
+ def read(locale, options = {})
60
+ translation_for(locale, options).value
61
+ end
62
+
63
+ # @!macro backend_reader
64
+ def write(locale, value, options = {})
65
+ translation_for(locale, options).tap { |t| t.value = value }.value
66
+ end
67
+ # @!endgroup
68
+
69
+ # @!macro backend_iterator
70
+ def each_locale
71
+ translations.each { |t| yield(t.locale.to_sym) if t.key == attribute }
72
+ end
73
+
74
+ private
75
+
76
+ def translations
77
+ model.send(association_name)
78
+ end
79
+
80
+ def self.included(backend)
81
+ backend.extend ClassMethods
82
+ end
83
+
84
+ module ClassMethods
85
+ # @!group Backend Configuration
86
+ # @option options [Symbol,String] type (:text) Column type to use
87
+ # @raise [ArgumentError] if type is not either :text or :string
88
+ def configure(options)
89
+ options[:type] = (options[:type] || :text).to_sym
90
+ raise ArgumentError, "type must be one of: [text, string]" unless [:text, :string].include?(options[:type])
91
+ end
92
+
93
+ # Apply custom processing for plugin
94
+ # @param (see Backend::Setup#apply_plugin)
95
+ # @return (see Backend::Setup#apply_plugin)
96
+ def apply_plugin(name)
97
+ if name == :cache
98
+ include self::Cache
99
+ true
100
+ else
101
+ super
102
+ end
103
+ end
104
+ end
105
+
106
+ module Cache
107
+ include Plugins::Cache::TranslationCacher.new(:translation_for)
108
+ end
109
+ end
110
+ end
111
+ end
@@ -1,5 +1,5 @@
1
1
  module Mobility
2
- module Backend
2
+ module Backends
3
3
  =begin
4
4
 
5
5
  Backend which does absolutely nothing. Mostly for testing purposes.
@@ -10,14 +10,14 @@ Backend which does absolutely nothing. Mostly for testing purposes.
10
10
 
11
11
  # @!group Backend Accessors
12
12
  # @return [NilClass]
13
- def read(*); end
13
+ def read(_, _ = {}); end
14
14
 
15
15
  # @return [NilClass]
16
- def write(*); end
16
+ def write(_, _, _ = {}); end
17
17
  # @!endgroup
18
18
 
19
19
  # @!group Backend Configuration
20
- def self.configure(*); end
20
+ def self.configure(_); end
21
21
  # @!endgroup
22
22
  end
23
23
  end
@@ -0,0 +1,20 @@
1
+ module Mobility
2
+ module Backends
3
+ module Sequel
4
+ def setup_query_methods(query_methods)
5
+ setup do |attributes, options|
6
+ extend(Module.new do
7
+ define_method ::Mobility.query_method do
8
+ super().with_extend(query_methods.new(attributes, options))
9
+ end
10
+ end)
11
+ end
12
+ end
13
+
14
+ def self.included(backend_class)
15
+ backend_class.include(Backend)
16
+ backend_class.extend(self)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,52 @@
1
+ require "mobility/backends/sequel"
2
+ require "mobility/backends/column"
3
+
4
+ module Mobility
5
+ module Backends
6
+ =begin
7
+
8
+ Implements the {Mobility::Backends::Column} backend for Sequel models.
9
+
10
+ =end
11
+ class Sequel::Column
12
+ include Sequel
13
+ include Column
14
+
15
+ require 'mobility/backends/sequel/column/query_methods'
16
+
17
+ # @!group Backend Accessors
18
+ # @!macro backend_reader
19
+ def read(locale, _ = {})
20
+ column = column(locale)
21
+ model[column] if model.columns.include?(column)
22
+ end
23
+
24
+ # @!group Backend Accessors
25
+ # @!macro backend_writer
26
+ def write(locale, value, _ = {})
27
+ column = column(locale)
28
+ model[column] = value if model.columns.include?(column)
29
+ end
30
+
31
+ # @!macro backend_iterator
32
+ def each_locale
33
+ available_locales.each { |l| yield(l) if present?(l) }
34
+ end
35
+
36
+ setup_query_methods(QueryMethods)
37
+
38
+ private
39
+
40
+ def available_locales
41
+ @available_locales ||= get_column_locales
42
+ end
43
+
44
+ def get_column_locales
45
+ column_name_regex = /\A#{attribute}_([a-z]{2}(_[a-z]{2})?)\z/.freeze
46
+ model.columns.map do |c|
47
+ (match = c.to_s.match(column_name_regex)) && match[1].to_sym
48
+ end.compact
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,7 +1,9 @@
1
+ require "mobility/backends/sequel/query_methods"
2
+
1
3
  module Mobility
2
- module Backend
3
- class Sequel::Column::QueryMethods < Backend::Sequel::QueryMethods
4
- def initialize(attributes, **)
4
+ module Backends
5
+ class Sequel::Column::QueryMethods < Sequel::QueryMethods
6
+ def initialize(attributes, _)
5
7
  super
6
8
  attributes_extractor = @attributes_extractor
7
9
 
@@ -0,0 +1,29 @@
1
+ require 'mobility/backends/sequel/pg_hash'
2
+
3
+ module Mobility
4
+ module Backends
5
+ =begin
6
+
7
+ Implements the {Mobility::Backends::Hstore} backend for Sequel models.
8
+
9
+ @see Mobility::Backends::Sequel::HashValued
10
+
11
+ =end
12
+ class Sequel::Hstore < Sequel::PgHash
13
+ require 'mobility/backends/sequel/hstore/query_methods'
14
+
15
+ # @!group Backend Accessors
16
+ # @!macro backend_reader
17
+ # @!method read(locale, **options)
18
+
19
+ # @!group Backend Accessors
20
+ # @!macro backend_writer
21
+ def write(locale, value, options = {})
22
+ super(locale, value && value.to_s, options)
23
+ end
24
+ # @!endgroup
25
+
26
+ setup_query_methods(QueryMethods)
27
+ end
28
+ end
29
+ end
@@ -1,13 +1,14 @@
1
- require 'mobility/backend/sequel/postgres_query_methods'
1
+ require 'mobility/backends/sequel/postgres_query_methods'
2
+ require "mobility/backends/sequel/query_methods"
2
3
 
3
4
  Sequel.extension :pg_hstore, :pg_hstore_ops
4
5
 
5
6
  module Mobility
6
- module Backend
7
+ module Backends
7
8
  class Sequel::Hstore::QueryMethods < Sequel::QueryMethods
8
9
  include PostgresQueryMethods
9
10
 
10
- def initialize(attributes, **)
11
+ def initialize(attributes, _)
11
12
  super
12
13
 
13
14
  define_query_methods("hstore")
@@ -1,16 +1,16 @@
1
- require 'mobility/backend/sequel/hash_valued'
1
+ require 'mobility/backends/sequel/pg_hash'
2
2
 
3
3
  module Mobility
4
- module Backend
4
+ module Backends
5
5
  =begin
6
6
 
7
- Implements the {Mobility::Backend::Jsonb} backend for Sequel models.
7
+ Implements the {Mobility::Backends::Jsonb} backend for Sequel models.
8
8
 
9
- @see Mobility::Backend::Sequel::HashValued
9
+ @see Mobility::Backends::Sequel::HashValued
10
10
 
11
11
  =end
12
- class Sequel::Jsonb < Sequel::HashValued
13
- require 'mobility/backend/sequel/jsonb/query_methods'
12
+ class Sequel::Jsonb < Sequel::PgHash
13
+ require 'mobility/backends/sequel/jsonb/query_methods'
14
14
 
15
15
  # @!group Backend Accessors
16
16
  #
@@ -1,13 +1,14 @@
1
- require 'mobility/backend/sequel/postgres_query_methods'
1
+ require 'mobility/backends/sequel/postgres_query_methods'
2
+ require "mobility/backends/sequel/query_methods"
2
3
 
3
4
  Sequel.extension :pg_json, :pg_json_ops
4
5
 
5
6
  module Mobility
6
- module Backend
7
+ module Backends
7
8
  class Sequel::Jsonb::QueryMethods < Sequel::QueryMethods
8
9
  include PostgresQueryMethods
9
10
 
10
- def initialize(attributes, **)
11
+ def initialize(attributes, _)
11
12
  super
12
13
 
13
14
  define_query_methods("pg_jsonb")
@@ -1,10 +1,16 @@
1
1
  # frozen-string-literal: true
2
+ require "mobility/util"
3
+ require "mobility/backends/sequel"
4
+ require "mobility/backends/key_value"
5
+ require "mobility/sequel/column_changes"
6
+ require "mobility/sequel/string_translation"
7
+ require "mobility/sequel/text_translation"
2
8
 
3
9
  module Mobility
4
- module Backend
10
+ module Backends
5
11
  =begin
6
12
 
7
- Implements the {Mobility::Backend::KeyValue} backend for Sequel models.
13
+ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
8
14
 
9
15
  @note This backend requires the cache to be enabled in order to track
10
16
  and store changed translations, since Sequel does not support +build+-type
@@ -14,11 +20,9 @@ Implements the {Mobility::Backend::KeyValue} backend for Sequel models.
14
20
  class Sequel::KeyValue
15
21
  include Sequel
16
22
  include KeyValue
23
+ include Util
17
24
 
18
- require 'mobility/backend/sequel/key_value/query_methods'
19
-
20
- # @return [Symbol] name of the association
21
- attr_reader :association_name
25
+ require 'mobility/backends/sequel/key_value/query_methods'
22
26
 
23
27
  # @return [Class] translation model class
24
28
  attr_reader :class_name
@@ -26,27 +30,14 @@ Implements the {Mobility::Backend::KeyValue} backend for Sequel models.
26
30
  # @!macro backend_constructor
27
31
  # @option options [Symbol] association_name Name of association
28
32
  # @option options [Class] class_name Translation model class
29
- def initialize(model, attribute, **options)
33
+ def initialize(model, attribute, options = {})
30
34
  super
31
- @association_name = options[:association_name]
32
35
  @class_name = options[:class_name]
33
36
  end
34
37
 
35
- # @!group Backend Accessors
36
- # @!macro backend_reader
37
- def read(locale, **_)
38
- translation_for(locale).value
39
- end
40
-
41
- # @!macro backend_writer
42
- def write(locale, value, **_)
43
- translation_for(locale).tap { |t| t.value = value }.value
44
- end
45
- # @!endgroup
46
-
47
38
  # @!group Backend Configuration
48
39
  # @option options [Symbol,String] type (:text) Column type to use
49
- # @option options [Symbol] associaiton_name (:mobility_text_translations) Name of association method
40
+ # @option options [Symbol] associaiton_name (:text_translations) Name of association method
50
41
  # @option options [Symbol] class_name ({Mobility::Sequel::TextTranslation}) Translation class
51
42
  # @raise [CacheRequired] if cache is disabled
52
43
  # @raise [ArgumentError] if type is not either :text or :string
@@ -56,7 +47,7 @@ Implements the {Mobility::Backend::KeyValue} backend for Sequel models.
56
47
  type = options[:type]
57
48
  options[:class_name] ||= Mobility::Sequel.const_get("#{type.capitalize}Translation".freeze)
58
49
  options[:class_name] = options[:class_name].constantize if options[:class_name].is_a?(String)
59
- options[:association_name] ||= options[:class_name].table_name.to_sym
50
+ options[:association_name] ||= :"#{options[:type]}_translations"
60
51
  %i[type association_name].each { |key| options[key] = options[key].to_sym }
61
52
  end
62
53
  # @!endgroup
@@ -82,7 +73,7 @@ Implements the {Mobility::Backend::KeyValue} backend for Sequel models.
82
73
  callback_methods = Module.new do
83
74
  define_method :before_save do
84
75
  super()
85
- send(association_name).select { |t| attributes.include?(t.key) && t.value.blank? }.each(&:destroy)
76
+ send(association_name).select { |t| attributes.include?(t.key) && Util.blank?(t.value) }.each(&:destroy)
86
77
  end
87
78
  define_method :after_save do
88
79
  super()
@@ -112,22 +103,10 @@ Implements the {Mobility::Backend::KeyValue} backend for Sequel models.
112
103
 
113
104
  setup_query_methods(QueryMethods)
114
105
 
115
- # @!group Cache Methods
116
- # @return [KeyValue::TranslationsCache]
117
- def new_cache
118
- KeyValue::TranslationsCache.new(self)
119
- end
120
-
121
- # @return [Boolean]
122
- def write_to_cache?
123
- true
124
- end
125
- # @!endgroup
126
-
127
106
  # Returns translation for a given locale, or initializes one if none is present.
128
107
  # @param [Symbol] locale
129
108
  # @return [Mobility::Sequel::TextTranslation,Mobility::Sequel::StringTranslation]
130
- def translation_for(locale)
109
+ def translation_for(locale, _)
131
110
  translation = model.send(association_name).find { |t| t.key == attribute && t.locale == locale.to_s }
132
111
  translation ||= class_name.new(locale: locale, key: attribute)
133
112
  translation
@@ -135,13 +114,23 @@ Implements the {Mobility::Backend::KeyValue} backend for Sequel models.
135
114
 
136
115
  # Saves translation which have been built and which have non-blank values.
137
116
  def save_translations
138
- cache.each_translation do |translation|
139
- next unless translation.value.present?
140
- translation.id ? translation.save : model.send("add_#{association_name.to_s.singularize}", translation)
117
+ cache.each_value do |translation|
118
+ next unless present?(translation.value)
119
+ translation.id ? translation.save : model.send("add_#{singularize(association_name)}", translation)
141
120
  end
142
121
  end
143
122
 
144
123
  class CacheRequired < ::StandardError; end
124
+
125
+ module Cache
126
+ include KeyValue::Cache
127
+
128
+ private
129
+
130
+ def translations
131
+ (model.send(association_name) + cache.values).uniq
132
+ end
133
+ end
145
134
  end
146
135
  end
147
136
  end