mobility 0.8.13 → 1.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) 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 +26 -0
  5. data/Gemfile +5 -2
  6. data/Gemfile.lock +79 -8
  7. data/README.md +183 -91
  8. data/lib/mobility.rb +40 -166
  9. data/lib/mobility/arel/nodes/pg_ops.rb +1 -1
  10. data/lib/mobility/backend.rb +19 -41
  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 +2 -0
  14. data/lib/mobility/backends/active_record/container.rb +4 -2
  15. data/lib/mobility/backends/active_record/hstore.rb +2 -0
  16. data/lib/mobility/backends/active_record/json.rb +2 -0
  17. data/lib/mobility/backends/active_record/jsonb.rb +2 -0
  18. data/lib/mobility/backends/active_record/key_value.rb +5 -3
  19. data/lib/mobility/backends/active_record/pg_hash.rb +1 -1
  20. data/lib/mobility/backends/active_record/serialized.rb +2 -0
  21. data/lib/mobility/backends/active_record/table.rb +5 -3
  22. data/lib/mobility/backends/column.rb +0 -6
  23. data/lib/mobility/backends/container.rb +2 -1
  24. data/lib/mobility/backends/hash.rb +39 -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 +0 -1
  28. data/lib/mobility/backends/key_value.rb +22 -14
  29. data/lib/mobility/backends/null.rb +2 -0
  30. data/lib/mobility/backends/sequel.rb +3 -0
  31. data/lib/mobility/backends/sequel/column.rb +2 -0
  32. data/lib/mobility/backends/sequel/container.rb +3 -1
  33. data/lib/mobility/backends/sequel/hstore.rb +2 -0
  34. data/lib/mobility/backends/sequel/json.rb +2 -0
  35. data/lib/mobility/backends/sequel/jsonb.rb +3 -1
  36. data/lib/mobility/backends/sequel/key_value.rb +8 -6
  37. data/lib/mobility/backends/sequel/serialized.rb +2 -0
  38. data/lib/mobility/backends/sequel/table.rb +5 -2
  39. data/lib/mobility/backends/serialized.rb +1 -3
  40. data/lib/mobility/backends/table.rb +14 -6
  41. data/lib/mobility/pluggable.rb +36 -0
  42. data/lib/mobility/plugin.rb +260 -0
  43. data/lib/mobility/plugins.rb +26 -25
  44. data/lib/mobility/plugins/active_model.rb +17 -0
  45. data/lib/mobility/plugins/active_model/cache.rb +26 -0
  46. data/lib/mobility/plugins/active_model/dirty.rb +112 -77
  47. data/lib/mobility/plugins/active_record.rb +34 -0
  48. data/lib/mobility/plugins/active_record/backend.rb +25 -0
  49. data/lib/mobility/plugins/active_record/cache.rb +28 -0
  50. data/lib/mobility/plugins/active_record/dirty.rb +34 -17
  51. data/lib/mobility/plugins/active_record/query.rb +43 -31
  52. data/lib/mobility/plugins/active_record/uniqueness_validation.rb +60 -0
  53. data/lib/mobility/plugins/attribute_methods.rb +28 -20
  54. data/lib/mobility/plugins/attributes.rb +70 -0
  55. data/lib/mobility/plugins/backend.rb +138 -0
  56. data/lib/mobility/plugins/backend_reader.rb +34 -0
  57. data/lib/mobility/plugins/cache.rb +59 -24
  58. data/lib/mobility/plugins/default.rb +22 -17
  59. data/lib/mobility/plugins/dirty.rb +12 -33
  60. data/lib/mobility/plugins/fallbacks.rb +51 -43
  61. data/lib/mobility/plugins/fallthrough_accessors.rb +20 -23
  62. data/lib/mobility/plugins/locale_accessors.rb +25 -35
  63. data/lib/mobility/plugins/presence.rb +28 -21
  64. data/lib/mobility/plugins/query.rb +8 -17
  65. data/lib/mobility/plugins/reader.rb +50 -0
  66. data/lib/mobility/plugins/sequel.rb +34 -0
  67. data/lib/mobility/plugins/sequel/backend.rb +25 -0
  68. data/lib/mobility/plugins/sequel/cache.rb +24 -0
  69. data/lib/mobility/plugins/sequel/dirty.rb +32 -21
  70. data/lib/mobility/plugins/sequel/query.rb +21 -6
  71. data/lib/mobility/plugins/writer.rb +44 -0
  72. data/lib/mobility/translations.rb +95 -0
  73. data/lib/mobility/version.rb +12 -1
  74. data/lib/rails/generators/mobility/templates/initializer.rb +95 -77
  75. metadata +28 -27
  76. metadata.gz.sig +0 -0
  77. data/lib/mobility/active_model.rb +0 -4
  78. data/lib/mobility/active_model/backend_resetter.rb +0 -26
  79. data/lib/mobility/active_record.rb +0 -23
  80. data/lib/mobility/active_record/backend_resetter.rb +0 -26
  81. data/lib/mobility/active_record/uniqueness_validator.rb +0 -60
  82. data/lib/mobility/attributes.rb +0 -324
  83. data/lib/mobility/backend/orm_delegator.rb +0 -44
  84. data/lib/mobility/backend_resetter.rb +0 -50
  85. data/lib/mobility/configuration.rb +0 -138
  86. data/lib/mobility/fallbacks.rb +0 -28
  87. data/lib/mobility/interface.rb +0 -0
  88. data/lib/mobility/loaded.rb +0 -4
  89. data/lib/mobility/plugins/active_record/attribute_methods.rb +0 -38
  90. data/lib/mobility/plugins/cache/translation_cacher.rb +0 -40
  91. data/lib/mobility/sequel.rb +0 -9
  92. data/lib/mobility/sequel/backend_resetter.rb +0 -23
  93. data/lib/mobility/translates.rb +0 -73
@@ -32,5 +32,7 @@ Implements the {Mobility::Backends::Hstore} backend for ActiveRecord models.
32
32
  end
33
33
  end
34
34
  end
35
+
36
+ register_backend(:active_record_hstore, ActiveRecord::Hstore)
35
37
  end
36
38
  end
@@ -40,5 +40,7 @@ Implements the {Mobility::Backends::Json} backend for ActiveRecord models.
40
40
  end
41
41
  end
42
42
  end
43
+
44
+ register_backend(:active_record_json, ActiveRecord::Json)
43
45
  end
44
46
  end
@@ -40,5 +40,7 @@ Implements the {Mobility::Backends::Jsonb} backend for ActiveRecord models.
40
40
  end
41
41
  end
42
42
  end
43
+
44
+ register_backend(:active_record_jsonb, ActiveRecord::Jsonb)
43
45
  end
44
46
  end
@@ -39,7 +39,7 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
39
39
  options[:association_name] ||= :"#{options[:type]}_translations"
40
40
  options[:class_name] ||= Mobility::ActiveRecord.const_get("#{type.capitalize}Translation")
41
41
  end
42
- options[:table_alias_affix] = "#{options[:model_class]}_%s_#{options[:association_name]}"
42
+ options[:table_alias_affix] = "#{model_class}_%s_#{options[:association_name]}"
43
43
  rescue NameError
44
44
  raise ArgumentError, "You must define a Mobility::ActiveRecord::#{type.capitalize}Translation class."
45
45
  end
@@ -119,7 +119,7 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
119
119
  # The title predicate has a non-nil value, so we can use an INNER JOIN,
120
120
  # whereas we are searching for nil content, which requires an OUTER JOIN.
121
121
  #
122
- class Visitor < Arel::Visitor
122
+ class Visitor < ::Mobility::Arel::Visitor
123
123
  private
124
124
 
125
125
  def visit_Arel_Nodes_Equality(object)
@@ -137,7 +137,7 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
137
137
  alias :visit_Array :visit_collection
138
138
 
139
139
  def visit_Arel_Nodes_Or(object)
140
- [object.left, object.right].map(&method(:visit)).compact.inject(&:merge).
140
+ [object.left, object.right].map(&method(:visit)).compact.inject(:merge).
141
141
  transform_values { OUTER_JOIN }
142
142
  end
143
143
 
@@ -212,5 +212,7 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
212
212
  end
213
213
  end
214
214
  end
215
+
216
+ register_backend(:active_record_key_value, ActiveRecord::KeyValue)
215
217
  end
216
218
  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
@@ -72,5 +72,7 @@ Implements {Mobility::Backends::Serialized} backend for ActiveRecord models.
72
72
  EOM
73
73
  end
74
74
  end
75
+
76
+ register_backend(:active_record_serialized, ActiveRecord::Serialized)
75
77
  end
76
78
  end
@@ -101,7 +101,7 @@ columns to that table.
101
101
  # @option options [Symbol] subclass_name (:Translation) Name of subclass
102
102
  # to append to model class to generate translation class
103
103
  def configure(options)
104
- table_name = options[:model_class].table_name
104
+ table_name = model_class.table_name
105
105
  options[:table_name] ||= "#{table_name.singularize}_translations"
106
106
  options[:foreign_key] ||= table_name.downcase.singularize.camelize.foreign_key
107
107
  if (association_name = options[:association_name]).present?
@@ -153,7 +153,7 @@ columns to that table.
153
153
  def already_joined?(relation, locale, join_type)
154
154
  if join = get_join(relation, locale)
155
155
  return true if (join_type == Visitor::OUTER_JOIN) || (Visitor::INNER_JOIN === join)
156
- relation.joins_values = relation.joins_values - [join]
156
+ relation.joins_values -= [join]
157
157
  end
158
158
  false
159
159
  end
@@ -265,7 +265,7 @@ columns to that table.
265
265
  touch: true
266
266
 
267
267
  before_save do
268
- required_attributes = self.class.mobility_attributes & translation_class.attribute_names
268
+ required_attributes = translation_class.attribute_names.select { |name| self.class.mobility_attribute?(name) }
269
269
  send(association_name).destroy_empty_translations(required_attributes)
270
270
  end
271
271
 
@@ -304,5 +304,7 @@ columns to that table.
304
304
  end
305
305
  end
306
306
  end
307
+
308
+ register_backend(:active_record_table, ActiveRecord::Table)
307
309
  end
308
310
  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,8 @@ stored).
19
19
 
20
20
  =end
21
21
  module Container
22
- extend Backend::OrmDelegator
23
22
  end
23
+
24
+ register_backend(:container, Container)
24
25
  end
25
26
  end
@@ -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
@@ -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
@@ -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
@@ -87,7 +85,7 @@ other backends on model (otherwise one will overwrite the other).
87
85
  module ClassMethods
88
86
  # @!group Backend Configuration
89
87
  # @option options [Symbol,String] type Column type to use
90
- # @option options [Symbol] associaiton_name (:<type>_translations) Name
88
+ # @option options [Symbol] association_name (:<type>_translations) Name
91
89
  # of association method, defaults to +<type>_translations+
92
90
  # @option options [Symbol] class_name Translation class, defaults to
93
91
  # +Mobility::<ORM>::<type>Translation+
@@ -98,20 +96,13 @@ other backends on model (otherwise one will overwrite the other).
98
96
  options[:association_name] &&= options[:association_name].to_sym
99
97
  options[:class_name] &&= Util.constantize(options[:class_name])
100
98
  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
99
+ raise ArgumentError, "KeyValue backend requires an explicit type option, either text or string."
109
100
  end
110
101
  end
111
102
 
112
103
  # Apply custom processing for plugin
113
- # @param (see Backend::Setup#apply_plugin)
114
- # @return (see Backend::Setup#apply_plugin)
104
+ # @param (see Backend::ClassMethods#apply_plugin)
105
+ # @return (see Backend::ClassMethods#apply_plugin)
115
106
  def apply_plugin(name)
116
107
  if name == :cache
117
108
  include self::Cache
@@ -127,7 +118,24 @@ each translated model, or set a default option in your configuration.
127
118
  end
128
119
 
129
120
  module Cache
130
- include Plugins::Cache::TranslationCacher.new(:translation_for)
121
+ def translation_for(locale, **options)
122
+ return super(locale, options) if options.delete(:cache) == false
123
+ if cache.has_key?(locale)
124
+ cache[locale]
125
+ else
126
+ cache[locale] = super(locale, options)
127
+ end
128
+ end
129
+
130
+ def clear_cache
131
+ @cache = {}
132
+ end
133
+
134
+ private
135
+
136
+ def cache
137
+ @cache ||= {}
138
+ end
131
139
  end
132
140
  end
133
141
  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,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ require "mobility/backend"
3
+
1
4
  module Mobility
2
5
  module Backends
3
6
  module Sequel
@@ -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
@@ -44,7 +44,7 @@ Implements the {Mobility::Backends::Container} backend for Sequel models.
44
44
  def self.configure(options)
45
45
  options[:column_name] ||= :translations
46
46
  options[:column_name] = options[:column_name].to_sym
47
- column_name, db_schema = options[:column_name], options[:model_class].db_schema
47
+ column_name, db_schema = options[:column_name], model_class.db_schema
48
48
  options[:column_type] = db_schema[column_name] && (db_schema[column_name][:db_type]).to_sym
49
49
  unless %i[json jsonb].include?(options[:column_type])
50
50
  raise InvalidColumnType, "#{options[:column_name]} must be a column of type json or jsonb"
@@ -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
@@ -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
@@ -42,5 +42,7 @@ Implements the {Mobility::Backends::Json} backend for Sequel models.
42
42
  class JSONOp < ::Sequel::Postgres::JSONOp; end
43
43
  end
44
44
  end
45
+
46
+ register_backend(:sequel_json, Sequel::Json)
45
47
  end
46
48
  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
@@ -36,7 +36,7 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
36
36
  options[:association_name] ||= :"#{options[:type]}_translations"
37
37
  options[:class_name] ||= Mobility::Sequel.const_get("#{type.capitalize}Translation")
38
38
  end
39
- options[:table_alias_affix] = "#{options[:model_class]}_%s_#{options[:association_name]}"
39
+ options[:table_alias_affix] = "#{model_class}_%s_#{options[:association_name]}"
40
40
  rescue NameError
41
41
  raise ArgumentError, "You must define a Mobility::Sequel::#{type.capitalize}Translation class."
42
42
  end
@@ -93,7 +93,7 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
93
93
  join_type = nils.empty? ? :inner : :left_outer
94
94
  # TODO: simplify to hash.transform_values { join_type } when
95
95
  # support for Ruby 2.3 is deprecated
96
- Hash[hash.keys.map { |key| [key, join_type] }]
96
+ ::Hash[hash.keys.map { |key| [key, join_type] }]
97
97
  else
98
98
  {}
99
99
  end
@@ -101,13 +101,13 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
101
101
  hash = visit(boolean.args, locale)
102
102
  # TODO: simplify to hash.transform_values { :inner } when
103
103
  # support for Ruby 2.3 is deprecated
104
- Hash[hash.keys.map { |key| [key, :inner] }]
104
+ ::Hash[hash.keys.map { |key| [key, :inner] }]
105
105
  elsif boolean.op == :OR
106
106
  hash = boolean.args.map { |op| visit(op, locale) }.
107
- compact.inject(&:merge)
107
+ compact.inject(:merge)
108
108
  # TODO: simplify to hash.transform_values { :left_outer } when
109
109
  # support for Ruby 2.3 is deprecated
110
- Hash[hash.keys.map { |key| [key, :left_outer] }]
110
+ ::Hash[hash.keys.map { |key| [key, :left_outer] }]
111
111
  else
112
112
  visit(boolean.args, locale)
113
113
  end
@@ -153,7 +153,7 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
153
153
  end
154
154
  define_method :after_save do
155
155
  super()
156
- attributes.each { |attribute| public_send(Backend.method_name(attribute)).save_translations }
156
+ attributes.each { |attribute| mobility_backends[attribute].save_translations }
157
157
  end
158
158
  end
159
159
  include callback_methods
@@ -202,5 +202,7 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
202
202
  end
203
203
  end
204
204
  end
205
+
206
+ register_backend(:sequel_key_value, Sequel::KeyValue)
205
207
  end
206
208
  end
@@ -110,5 +110,7 @@ Sequel serialization plugin.
110
110
  super.to_sym
111
111
  end
112
112
  end
113
+
114
+ register_backend(:sequel_serialized, Sequel::Serialized)
113
115
  end
114
116
  end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
  require "mobility/util"
3
3
  require "mobility/backends/sequel"
4
- require "mobility/backends/key_value"
4
+ require "mobility/backends/table"
5
+ require "mobility/sequel/column_changes"
5
6
  require "mobility/sequel/model_translation"
6
7
  require "mobility/sequel/sql"
7
8
 
@@ -34,7 +35,7 @@ Implements the {Mobility::Backends::Table} backend for Sequel models.
34
35
  # @raise [CacheRequired] if cache option is false
35
36
  def configure(options)
36
37
  raise CacheRequired, "Cache required for Sequel::Table backend" if options[:cache] == false
37
- table_name = Util.singularize(options[:model_class].table_name)
38
+ table_name = Util.singularize(model_class.table_name)
38
39
  options[:table_name] ||= :"#{table_name}_translations"
39
40
  options[:foreign_key] ||= Util.foreign_key(Util.camelize(table_name.downcase))
40
41
  if association_name = options[:association_name]
@@ -175,5 +176,7 @@ Implements the {Mobility::Backends::Table} backend for Sequel models.
175
176
 
176
177
  class CacheRequired < ::StandardError; end
177
178
  end
179
+
180
+ register_backend(:sequel_table, Sequel::Table)
178
181
  end
179
182
  end
@@ -23,8 +23,6 @@ Format for serialization. Either +:yaml+ (default) or +:json+.
23
23
 
24
24
  =end
25
25
  module Serialized
26
- extend Backend::OrmDelegator
27
-
28
26
  class << self
29
27
 
30
28
  # @!group Backend Configuration
@@ -40,7 +38,7 @@ Format for serialization. Either +:yaml+ (default) or +:json+.
40
38
  def serializer_for(format)
41
39
  lambda do |obj|
42
40
  return if obj.nil?
43
- if obj.is_a? Hash
41
+ if obj.is_a? ::Hash
44
42
  obj = obj.inject({}) do |translations, (locale, value)|
45
43
  translations[locale] = value.to_s if Util.present?(value)
46
44
  translations