mobility 0.8.8 → 1.0.0.alpha

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 (96) 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 +56 -0
  5. data/Gemfile +52 -16
  6. data/Gemfile.lock +113 -52
  7. data/Guardfile +23 -1
  8. data/README.md +184 -92
  9. data/Rakefile +6 -4
  10. data/lib/mobility.rb +40 -166
  11. data/lib/mobility/active_record/translation.rb +1 -1
  12. data/lib/mobility/arel/nodes/pg_ops.rb +1 -1
  13. data/lib/mobility/backend.rb +19 -41
  14. data/lib/mobility/backends.rb +20 -0
  15. data/lib/mobility/backends/active_record.rb +4 -0
  16. data/lib/mobility/backends/active_record/column.rb +2 -0
  17. data/lib/mobility/backends/active_record/container.rb +4 -2
  18. data/lib/mobility/backends/active_record/hstore.rb +2 -0
  19. data/lib/mobility/backends/active_record/json.rb +2 -0
  20. data/lib/mobility/backends/active_record/jsonb.rb +2 -0
  21. data/lib/mobility/backends/active_record/key_value.rb +5 -3
  22. data/lib/mobility/backends/active_record/pg_hash.rb +1 -1
  23. data/lib/mobility/backends/active_record/serialized.rb +2 -0
  24. data/lib/mobility/backends/active_record/table.rb +5 -3
  25. data/lib/mobility/backends/column.rb +0 -6
  26. data/lib/mobility/backends/container.rb +2 -1
  27. data/lib/mobility/backends/hash.rb +39 -0
  28. data/lib/mobility/backends/hstore.rb +0 -1
  29. data/lib/mobility/backends/json.rb +0 -1
  30. data/lib/mobility/backends/jsonb.rb +0 -1
  31. data/lib/mobility/backends/key_value.rb +22 -14
  32. data/lib/mobility/backends/null.rb +2 -0
  33. data/lib/mobility/backends/sequel.rb +3 -0
  34. data/lib/mobility/backends/sequel/column.rb +2 -0
  35. data/lib/mobility/backends/sequel/container.rb +3 -1
  36. data/lib/mobility/backends/sequel/hstore.rb +2 -0
  37. data/lib/mobility/backends/sequel/json.rb +2 -0
  38. data/lib/mobility/backends/sequel/jsonb.rb +3 -1
  39. data/lib/mobility/backends/sequel/key_value.rb +8 -6
  40. data/lib/mobility/backends/sequel/serialized.rb +2 -0
  41. data/lib/mobility/backends/sequel/table.rb +5 -2
  42. data/lib/mobility/backends/serialized.rb +1 -3
  43. data/lib/mobility/backends/table.rb +14 -6
  44. data/lib/mobility/pluggable.rb +36 -0
  45. data/lib/mobility/plugin.rb +260 -0
  46. data/lib/mobility/plugins.rb +26 -25
  47. data/lib/mobility/plugins/active_model.rb +17 -0
  48. data/lib/mobility/plugins/active_model/cache.rb +26 -0
  49. data/lib/mobility/plugins/active_model/dirty.rb +310 -54
  50. data/lib/mobility/plugins/active_record.rb +34 -0
  51. data/lib/mobility/plugins/active_record/backend.rb +25 -0
  52. data/lib/mobility/plugins/active_record/cache.rb +28 -0
  53. data/lib/mobility/plugins/active_record/dirty.rb +72 -101
  54. data/lib/mobility/plugins/active_record/query.rb +48 -34
  55. data/lib/mobility/plugins/active_record/uniqueness_validation.rb +60 -0
  56. data/lib/mobility/plugins/attribute_methods.rb +28 -20
  57. data/lib/mobility/plugins/attributes.rb +70 -0
  58. data/lib/mobility/plugins/backend.rb +138 -0
  59. data/lib/mobility/plugins/backend_reader.rb +34 -0
  60. data/lib/mobility/plugins/cache.rb +59 -24
  61. data/lib/mobility/plugins/default.rb +22 -17
  62. data/lib/mobility/plugins/dirty.rb +12 -33
  63. data/lib/mobility/plugins/fallbacks.rb +51 -43
  64. data/lib/mobility/plugins/fallthrough_accessors.rb +26 -25
  65. data/lib/mobility/plugins/locale_accessors.rb +25 -35
  66. data/lib/mobility/plugins/presence.rb +28 -21
  67. data/lib/mobility/plugins/query.rb +8 -17
  68. data/lib/mobility/plugins/reader.rb +50 -0
  69. data/lib/mobility/plugins/sequel.rb +34 -0
  70. data/lib/mobility/plugins/sequel/backend.rb +25 -0
  71. data/lib/mobility/plugins/sequel/cache.rb +24 -0
  72. data/lib/mobility/plugins/sequel/dirty.rb +45 -32
  73. data/lib/mobility/plugins/sequel/query.rb +21 -6
  74. data/lib/mobility/plugins/writer.rb +44 -0
  75. data/lib/mobility/translations.rb +95 -0
  76. data/lib/mobility/version.rb +12 -1
  77. data/lib/rails/generators/mobility/templates/initializer.rb +95 -77
  78. metadata +51 -51
  79. metadata.gz.sig +0 -0
  80. data/lib/mobility/active_model.rb +0 -4
  81. data/lib/mobility/active_model/backend_resetter.rb +0 -26
  82. data/lib/mobility/active_record.rb +0 -23
  83. data/lib/mobility/active_record/backend_resetter.rb +0 -26
  84. data/lib/mobility/active_record/uniqueness_validator.rb +0 -60
  85. data/lib/mobility/attributes.rb +0 -324
  86. data/lib/mobility/backend/orm_delegator.rb +0 -44
  87. data/lib/mobility/backend_resetter.rb +0 -50
  88. data/lib/mobility/configuration.rb +0 -138
  89. data/lib/mobility/fallbacks.rb +0 -28
  90. data/lib/mobility/interface.rb +0 -0
  91. data/lib/mobility/loaded.rb +0 -4
  92. data/lib/mobility/plugins/active_record/attribute_methods.rb +0 -38
  93. data/lib/mobility/plugins/cache/translation_cacher.rb +0 -40
  94. data/lib/mobility/sequel.rb +0 -9
  95. data/lib/mobility/sequel/backend_resetter.rb +0 -23
  96. data/lib/mobility/translates.rb +0 -73
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+ require "mobility/backend"
3
+ require "mobility/arel"
4
+
1
5
  module Mobility
2
6
  module Backends
3
7
  module ActiveRecord
@@ -73,5 +73,7 @@ can be run again to add new attributes or locales.)
73
73
  end.compact
74
74
  end
75
75
  end
76
+
77
+ register_backend(:active_record_column, ActiveRecord::Column)
76
78
  end
77
79
  end
@@ -71,7 +71,7 @@ Implements the {Mobility::Backends::Container} backend for ActiveRecord models.
71
71
  private
72
72
 
73
73
  def get_column_type
74
- options[:model_class].type_for_attribute(options[:column_name].to_s).try(:type).tap do |type|
74
+ model_class.type_for_attribute(options[:column_name].to_s).try(:type).tap do |type|
75
75
  unless %i[json jsonb].include? type
76
76
  raise InvalidColumnType, "#{options[:column_name]} must be a column of type json or jsonb"
77
77
  end
@@ -122,7 +122,7 @@ Implements the {Mobility::Backends::Container} backend for ActiveRecord models.
122
122
 
123
123
  class Coder
124
124
  def self.dump(obj)
125
- if obj.is_a? Hash
125
+ if obj.is_a? ::Hash
126
126
  obj.inject({}) do |translations, (locale, value)|
127
127
  value.each do |k, v|
128
128
  (translations[locale] ||= {})[k] = v if v.present?
@@ -141,5 +141,7 @@ Implements the {Mobility::Backends::Container} backend for ActiveRecord models.
141
141
 
142
142
  class InvalidColumnType < StandardError; end
143
143
  end
144
+
145
+ register_backend(:active_record_container, ActiveRecord::Container)
144
146
  end
145
147
  end
@@ -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